I'm trying to write a map editor in Qt, using QGraphicsView and QGraphicsScene for both the map and tile sheets.
The problem I'm having right now is with making a good widget for importing tiles. For this, I'm using a QTabWidget (for different tile sheets), and TileWidget as the widget for each tab, which contains the QGraphicsScene and QGraphicsView.
It's working to a rough degree, but not all the tiles (or TileObjects, which are implementations of QGraphicsItem) are visible. I'm even calling view->ensureVisible(scene->sceneRect()), but still not all of the QGraphicsScene is not visible, even with scroll bars.
I understand this is due to limiting the maximum size of my QTabWidget, but that is necessary.
This happens mainly when I import a larger tile sheet.
I have a TileWidget as the QWidget for the QTabWidget, which has both the QGraphicsScene and the QGraphicsView.
TileWidget::TileWidget(QWidget *parent)
: QWidget(parent)
{
scene = new QGraphicsScene;
view = new TileView(scene, this);
connect(view, SIGNAL(newBrushSelected(TileObject *b)), this, SLOT(selectNewBrush(TileObject *b)));
}
TileView is simply a QGraphicsView re-implemented to handle mouse release events.
To add tiles, I simply call scene->addItem().
I have no other code for TileView. When I use
void TileWidget::showEvent(QShowEvent *event)
{
view->fitInView(scene->itemsBoundingRect(), Qt::KeepAspectRatio);
}
I get something like this.
It's okay for smaller tile sheets, but not for larger ones. What should I add to keep the size of the tiles normal, and navigate TileView using scroll bars?
Nevermind, figured it out. Just me being stupid.
You need something like:
p_myGraphicsView->fitInView(
myGraphicsView->scene()->itemsBoundingRect(),
Qt::KeepAspectRatio);
Related
Context: I'm making Risk (the popular board game) in C++/Qt, and I've run into a problem. I decided to make the map interactive by placing buttons on every country, which could then be clicked on. After some experimenting, I've subclassed QGraphicsPixmapItem for the buttons, and stuck them inside a QGraphicsScene and a QGraphicsView. I've made the world map a background image via CSS, so that buttons could be overlaid without much hassle.
My problem: I want to place those buttons at specific coordinates. (If it matters, those coordinates would be absolute.) All of the interfaces that I've made so far, I've done in code - I'm not familiar with the Qt Designer, so I'm looking for a function or set of functions that'd let me place my buttons (more or less) where I want them.
What I've tried: I looked in the documentation, but couldn't find a function that let me control where items were placed, just ones that organized items in various ways - columns, horizontal boxes, vertical boxes, etc.
When I designed QWidgets before, I'd done so by placing buttons, other widgets, etc. in QLayouts, but there don't seem to be layouts that allow me the control I'd like. The only one I can see that'd do something similar, is QGridLayout, and experiments with that so far haven't worked the way I wanted them to - the buttons don't get anywhere near the edges of the map, no matter how many columns or rows I add.
The easiest solution would be giving up and placing the buttons beside the map, of course, but that's a last-ditch solution.
Thanks in advance.
EDIT: Added example source code, for clarity.
QHBoxLayout* layout = new QHBoxLayout;
TerritoryButton* test = new TerritoryButton(QPixmap("img.png"));
TerritoryButton* test2 = new TerritoryButton(QPixmap("img.png"));
QGraphicsScene* scene = new QGraphicsScene;
QGraphicsScene* scene2 = new QGraphicsScene;
scene->addItem(test);
scene2->addItem(test2);
QGraphicsView* view = new QGraphicsView(scene);
QGraphicsView* view2 = new QGraphicsView(scene2);
layout->addWidget(view);
layout->addWidget(view2);
setLayout(layout);
setFixedSize(1000, 512);
QGraphicsPixmapItem inherits QGraphicsItem, so you can call setPos(x, y) (after inserting the pixmap item into the scene with addItem).
void QGraphicsItem::setPos(const QPointF &pos)
Sets the position of the item to pos, which is in parent coordinates. For
items with no parent, pos is in scene coordinates.
The position of the item describes its origin (local coordinate (0, 0)) in parent coordinates.
I have found a few fixes for this issue, but none of seemed to be sufficient. I have a QGraphicsView displaying a QGraphicsScene. What I want to do is to scale the scene in order to fill the whole view. Also I want it to scale dynamically when the user will be resizing the window displaying the view. Is such a thing possible? If so I'd be glad if you could give me a short example on how should it be implemented. Thanks in advance.
You can make your custom class which inherits from QGraphicsView. You should reimplement resizeEvent( QResizeEvent *event ) in your custom QGraphicsView like:
void MyView::resizeEvent(QResizeEvent *event)
{
fitInView(0, 0, 500, 500,Qt::KeepAspectRatio);
QGraphicsView::resizeEvent(event);
}
This way the view will always display the whole scene. I.e. if the window size is changed and the graphicsView is resized, The scene gets scaled and you can see everything appropriately.
I have problem with scrolling in QGraphicsView.
I've set scene rect in my QGraphicsView subclass:
MyQGraphicsView::MyQGraphicsView{
setSceneRect(0,0,2000,2000)
}
And overloaded paintEvent:
void MyQGraphicsView::paintEvent(QPaintEvent *event){
qDebug()<<"Paint event";
QPainter painter(viewport());
painter.setRenderHint(QPainter::Antialiasing);
paint(painter);
}
void MyQGraphicsView::paint(QPainter &painter){
painter.setPen(Qt::NoPen);
painter.fillRect(QRect(0,0,200,200),Qt::gray);
painter.fillRect(QRect(500,500,1000,100),Qt::green);
painter.setPen(QPen(Qt::white,4,Qt::DashLine));
painter.drawLine(QLine(0,35,200,35));
painter.drawLine(QLine(0,165,200,165));
}
When I scroll the second rectangle is not visible. When I resize window it is. Also when scrolling rectangle is extending in wired way.
How should scrolling be implemened in this case? I've found several topics about scrolling in QGraphicsView but none solves my problem.
QGraphicsView inherits QAbstractScrollArea. So its content is displayed in its internal widget that can be obtained using viewport(). If you want to paint something and be able to scroll it, you need to attach an event filter to viewport widget and process its paintEvent, not view's event.
But you should not do this for QGraphicsView. You're trying to do something terribly wrong. You should not reimplement QGraphicsView::paintEvent just to paint something! It totally devalues its advantages. You need to use QGraphicsScene to add something to the view.
I have a QMainWindow whose central widget has been set to a QGraphicsView viewing a black scene (for test purposes). Note that in the code below, I use my class derived from QGraphicsView, called CQtGlView, which reimplements only the resizeEvent function.
Regardless of whether I add the view directly,
CQtMainWindow::CQtMainWindow() {
m_glView = new CQtGlView();
setCentralWidget(m_glView);
}
or stick it in a layout with margins of 0 in a dummy widget,
CQtMainWindow::CQtMainWindow() {
m_glView = new CQtGlView();
QWidget* dummy = new QWidget();
QHBoxLayout* l = new QHBoxLayout();
l->setContentsMargins(0,0,0,0);
l->addWidget(m_glView);
dummy->setLayout(l);
setCentralWidget(dummy);
}
I get an unwanted grey border around the widget.
The screenshot below illustrates the problem, visible between my scene and the windows aero border.
This would not be a problem if my application did not allow switching to full screen. The border is very obvious once the rest of the screen is black.
It's possible this area represents the DockWidgetAreas around the outside of the central widget.
Is there anything I can do to solve this other than not use QMainWindow? (Undesirable due to my use of menuBar, tool bars, and statusBar.)
It turns out that QGraphicsView derives from QFrame, where I assumed it was only a QWidget.
The solution to this problem was to call setFrameStyle(QFrame::NoFrame); in the constructor of my QGraphicsView subclass. Or if it was not a subclass,
m_glView->setFrameStyle(QFrame::NoFrame);
Have you tried setFrameShape(QFrame::NoFrame) on the QGraphicsView?
I'm creating some graphic data displaying widget in Qt4 and I was tempted to use the QGraphicsScene for it, create QGraphicsItems for the data items etc.
However, I wanted to add some layer of controls (eg. scrollbars, zoom+other buttons - I want to make it in a similar style as eg. Google Maps, that is, the data would be displayed all over the widget, and the buttons would be shown atop of them) to the widget. So I thought it might be feasible to add them to the scene (perhaps as a child of a QGraphicsGroupItem that would be shown over the data). But I want them to move & resize when I resize the whole widget, so I should use a QGraphicsLayout for managing them. But at this point, I discovered things are pretty complicated.
The problem is that when using QGraphicsLayout, the following constraints hold:
Only a QGraphicsWidget can be managed by a layout
QGraphicsLayout can only be used to manage children of a QGraphicsWidget
Which means that I would have to create my controls as QGraphicsWidgets, add a top level QGraphicsWidget to the data widget, and manage the size of this top level widget myself.
So I want to ask:
Wouldn't a classic approach (ie. use plain old widgets for all controls, and use QGraphicsScene only for displaying the data) be more reasonable?
Is there any advantage in using QGraphicsScene in this case (performance or simplicity...)?
How should I use QGraphicsScene to exploit its strengths?
Since Qt 4.4 you can embed classic widgets in a QGraphicsScene by using QGraphicsProxyWidget :
QWidget *widget = new QWidget;
QGraphicsScene scene;
QGraphicsProxyWidget *proxy = scene.addWidget(widget);
If you think that QGraphicsScene (or whatever other widget you have) is appropriate for most of your display, use that. What we have done in the past for somewhat similar things is to make a custom widget that inherits (one way or another) from QWidget, and put the control widgets in a layout on top of that widget. This means that the whole widget is drawing whatever it is you want drawn, and the control widgets are on top of that, resizing as the whole widget is resized.
Alternatively, a couple of times we've had layouts that were just a bit too complicated for the layout widgets to easily handle. Rather than create a custom layout, we just positioned them with no layout, and moved them in code on the resize event. It works just as well.