SFML community forums

Help => General => Topic started by: sb on July 15, 2016, 03:52:46 pm

Title: [SOLVED] SFML inside QT: sf::RenderWindow and QScrollArea
Post by: sb on July 15, 2016, 03:52:46 pm
Hey there.

Last year, I asked for help as I wanted to integrate SFML into QT. The post can be found here: http://en.sfml-dev.org/forums/index.php?topic=19163.msg138596#msg138596
We kind of concluded a way to do this and someone wrote it down as a tutorial here: https://github.com/SFML/SFML/wiki/Tutorial%3A-Integrating-SFML-into-Qt

My QWidget, which also is a sf::RenderWindow at the same time, is inside of a QScrollArea. If the QWidget/sf::RenderWindow is bigger than the QScrollArea, scrollbars appear and behave as expected.

However, I want to draw a really big object (multiple displays in size). I figure that the solution is not to set the QWidget/sf::RenderWindow that big (you usually don't have windows bigger than your screen can display).

I think I know how to implement a RenderWindow that only shows a portion of the object and scrolling by translating the object relatively. However, as the QWidget/sf::RenderWindow now is only as big as the actually seen portion, the QScrollArea around it doesn't need to provide any scrollbars as the object insinde fits into it.

So I wonder if there is a way to tell the QScrollArea that the size of the QObject/sf::RenderWindow inside is actually bigger (as big as the object I want to draw) without having the RenderWindow explode in size. If I found a way to do this, I guess I could connect the scrollBar to a function that perfoms the relative translation of the drawn object inside the RenderWindow.

Does this make any sense to you? It seems to be rather a Qt than a SFML question, but in my opinion it is of interest to everyone who wants to integrate SFML into Qt. If someone found a comprehensive way to achieve this, it could be added to the tutorial I mentioned above. I think that without this, integration is not fully achieved yet.
Title: Re: SFML inside QT: sf::RenderWindow and QScrollArea
Post by: Laurent on July 15, 2016, 04:14:31 pm
Can't you just add and manage the scroll bars yourself, rather than trying to trick a QScrollArea?
Title: Re: SFML inside QT: sf::RenderWindow and QScrollArea
Post by: sb on July 15, 2016, 04:15:36 pm
Probably. In fact, this is what I am trying right now.
Title: Re: [SOLVED] SFML inside QT: sf::RenderWindow and QScrollArea
Post by: Yohdu on July 21, 2016, 10:18:07 am

Since you marked this topic as "solved", can you provide us with a solution please ?

I'm very curious about how you did it, because I was thinking about the same problematic like 2 weeks ago (without going into implementation, just having thoughts about this exact same feature I'd like to provide).

Thank you
Title: Re: [SOLVED] SFML inside QT: sf::RenderWindow and QScrollArea
Post by: sb on July 21, 2016, 03:42:56 pm
Hey, Yohdu.

My implementation works but I recently realized that it's not compatible with sf::View. I will try to solve this and then provide the code. Until then, I'll just explain my solution informally and in short:

1. ScrollCanvas inherits from QSFMLCanvas and adds a pointer to a sf::Drawable object named content, it also has two QScrollbars and a member function scroll().
2. Set the Scrollbars' ranges from 0 to the contents width/height.
3. connect(...) the scrollbars value-changed-signal to scroll(), which then stores the new scroll values. This is a Qt mechanism that looks like
connect(this->horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(scroll()));
4. Consider the scroll values when drawing the content in the ScrollCanvas.
Title: Re: [SOLVED] SFML inside QT: sf::RenderWindow and QScrollArea
Post by: sb on July 22, 2016, 04:44:03 pm
I think I did it.

You will find my solution in this post's attachments.

The QSFML class is almost the same as in the tutorial mentioned in the original post, I think. As for the class ScrollCanvas: The header file explains all the functions and members, so I will restrict myself to outlining how to use it:

Basically, you must assign the ScrollCanvas a content (sf::Drawable*) and the content's dimensions. The latter is important for adjusting the QScrollBars. Whenever the content's dimensions are changed (via the setter method), the QScrollBars adjust (if you take a look at the implementation file, you will see the not-so-clear formulas). By changing the QScrollBars' values, a signal is sent to the ScrollCanvas scroll() method, which changes the contentOffset members and updates its View. The View shall not be changed manually as all the methods rely on the member variables.

You can also zoom via setContentScale() (it will always zoom into the widgets center), however, I don't provide a GUI element for this.

To integrate into your Qt window, you need to create a ScrollCanvas object and add both itself and its two scrollBar members to where you want it to appear (they are all QWidgets). Deleting the ScrollCanvas object should also delete the QScrollBars.

For drawing the assigned content, you can consider the ScrollCanvas' View as it should be usual in SFML.

If there is anything that's unclear, please ask.
Title: Re: [SOLVED] SFML inside QT: sf::RenderWindow and QScrollArea
Post by: Yohdu on July 25, 2016, 11:13:26 am
Thank you very much sb :)

Coming back to your initial post, I also did try to put a QSFMLCanvas directly in a QScrollArea, but there was 2 problems :
 - as you said, creating a very large sf::RenderWindow did not seem to be very elegant ;
 - when I used the scroll bars, the scrolling process did not occur very smoothly ; there was like a white trail on the widget side (which would stop in the same time I would stop scrolling).

For my part, I was considering a solution using a class, named like QSFMLScollArea, which would inherits the astract class QAbstractScrollArea. Every QAbstractScrollArea has a viewport as central widget, which contents is the widget to be scrolled. In this case, the widget to be scrolled would be the QSFMLCanvas. Then I would follow the implementation details explained here : http://doc.qt.io/qt-5/qabstractscrollarea.html#details , in order to provide the custom scrollbar features.

I will do some testing to compare your solution and the one I just briefly described above, as soon as I will have the time to do so (meaning : I don't know when ^^). Hopefully I will come back to you if you're still interested :)
Title: Re: [SOLVED] SFML inside QT: sf::RenderWindow and QScrollArea
Post by: sb on July 25, 2016, 07:08:08 pm
I also experienced the same problems as you did (I'm especially referring to the white trail). RenderWindows should really be only as big as what you are supposed to see "through" them (like an actual window ;) ), it seems.

Using a QAbstractScrollArea seems to be "semantically more correct" than my solution, however, I guess the actual implementation will need the same or similar steps.
Title: Re: [SOLVED] SFML inside QT: sf::RenderWindow and QScrollArea
Post by: Yohdu on July 26, 2016, 08:48:06 am
Well I'm glad you experienced those same problems! And yeah, when you think about it, it does not really make any sense to have a window which can grow indefinitely (depending on the map size for example).

I'll keep you posted (hopefully by the end of the week) :)
Title: Re: [SOLVED] SFML inside QT: sf::RenderWindow and QScrollArea
Post by: Yohdu on August 25, 2016, 10:09:09 am
OK, so I know it's a bit late, but here is my implementation.

class ScrollingMapCanvas : public QAbstractScrollArea
    ScrollingMapCanvas(MapCanvas* mapCanvas, QWidget *parent = Q_NULLPTR);

    MapCanvas* mapCanvas() const;

    void resizeEvent(QResizeEvent* event);
    void scrollContentsBy(int dx, int dy);

    MapCanvas* m_mapCanvas;

ScrollingMapCanvas::ScrollingMapCanvas(MapCanvas* mapCanvas, QWidget* parent) :
    m_mapCanvas = mapCanvas;


void ScrollingMapCanvas::resizeEvent(QResizeEvent* /*event*/)
    QSize areaSize = viewport()->size();

    //add 1.f to width and height to see the end of the grid
    int width = (m_mapCanvas->map()->size().x * m_mapCanvas->map()->tileSize().x + 1) / m_mapCanvas->scale();
    int height = (m_mapCanvas->map()->size().y * m_mapCanvas->map()->tileSize().y + 1) / m_mapCanvas->scale();
    QSize mapSize = QSize(width, height);

    horizontalScrollBar()->setRange(0, mapSize.width() - areaSize.width());
    verticalScrollBar()->setRange(0, mapSize.height() - areaSize.height());

void ScrollingMapCanvas::scrollContentsBy(int dx, int dy)
    m_mapCanvas->translate(sf::Vector2f(-dx * m_mapCanvas->scale(), -dy * m_mapCanvas->scale()));

MapCanvas* ScrollingMapCanvas::mapCanvas() const
    return m_mapCanvas;

MapCanvas is the class which inherits from QSFMLCanvas, it has a Map object (which owns in particular the size of the map and the tile size for the map), a translation (sf::Vector2f) and a scaling factor (for zoom). These 2 last attributes are used by an internal sf::View to update the camera at each frame.

Here you go, if you have any suggestion to improve it, I will gladly receive them !