Placing QWidgets at specified coordinates? - c++

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.

Related

How to enable mouse popup in QtCharts?

I need a small popup be shown when the mouse hovers over series in my QtChart.
Highcharts (Javascript) has some really nice examples like this one:
How can I implement this QtCharts?
I cannot find any documentation on implementing popups.
As far as I know you will have to do it by yourself. I needed exactly the same and used a simple QWidget embedded in a QGraphicsProxyWidget that I added to the QGraphicsScene of the chart view.
QWidget *popup = new MyPopUpWidget;
QChartView v;
QGraphicsProxyWidget *proxy = v.scene()->addWidget(popup);
// if you want a drop shadow you can use QGraphicsDropShadowEffect
QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect();
shadow->setOffset(0, 4);
shadow->setBlurRadius(8);
proxy->setGraphicsEffect(shadow);
While this is nice and simple, the positioning of the popup is the actual work. All the simple solutions weren't good enough for me. For example there are signals if your mouse hits a QGraphicsItem (all line items of your charts are QGraphicsItems) but they are usually to small and you only want to react on the actual data points, not on the line segments.
You can override your mouseMove(QMouseMoveEvent *) function and always check your mouse position against all data points and adjust your popup (show/hide, positioning). If you have many points that will be slow, so I used a spatial grid and assigned data points to grid cells initially. Then you only need to check against points within the grid cells around your mouse position.
I didn't find a better solution.

Nothing showing up in QScrollArea

I have a nice widget that basically looks like a dialog box with a bunch of QSliders on it. The number of sliders varies depending on the situation when the dialog (not an actual QDialog; just a QWidget) is invoked.
Since the varying number of sliders causes the box to be different sizes at different times, I now want to clean things up a bit by confining the sliders to a QScrollArea. If I understand things correctly, such a scroll area would display however many sliders fit within its height, and one could scroll down to see the rest if there were more.
Anyway, I tried a (somewhat complicated) procedure like this:
In constructor of custom QWidget class (m_variableName = member variable):
CustomScrollBox::CustomScrollBox(QWidget* _parent){
setWindowTitle(...);
...
m_scrollArea = new QScrollArea(this);
m_scrollAreaBox = new QGroupBox(m_scrollArea);
m_layout = new QGridLayout();
m_scrollAreaBox->setLayout(m_layout);
m_scrollArea->setWidget(m_scrollAreaBox);
m_scrollArea->setFixedHeight(250);
m_bottomButton = new QPushButton(this); //probably irrelevant
...
[connect calls, etc.]
}
After the constructor, the real, situation-dependent set-up of sliders occurs:
void
CustomScrollBox::SetUpWidgets(){
for([however many sliders the situation calls for]){
CustomSlider* s = new CustomSlider(this, label); //just a QWidget consisting of a
//QSlider and a QLabel to
//the left of it
..
m_layout->addWidget(s, [grid dimensions as needed]);
}
...
[set text on bottom button, etc., and add it as well]
}
This process causes nothing to show up on the overall dialog, except for an immobile scroll bar on the left. What, if possible, is the proper order of initialization steps to make this work? My guess is that I might have given something the wrong parent or set a layout at the wrong time, but the rearrganements I've tried so far haven't worked...
First of all you do not need to create explicit members for child widgets and layout to your CustomScrollBox unless you need to access them later (even then you might track them down through their child relationship to your CustomScrollBox). In particular, having set the layout of a widget you can use QWidget::layout to get QLayout* and downcast it to QGridLayout* or QVBoxLayout*. Secondly you are supplying parents to most of the child widgets ctors. Normally you should not do that as e.g. the layout to which the widget is added will take the ownership i.e. the layout will become parent to the added widget. Below is in principle what I would do. It will point you in a better direction at least.
CustomScrollBox::CustomScrollBox(QWidget* parent)
: QWidget(parent)
{
setWindowTitle(...);
...
QVBoxLayout* vBoxLayout(new QVBoxLayout);
QScrollArea* scrollArea(new QScrollArea);
vBoxLayout->addWidget(scrollArea);
QGroupBox* groupBox(new QGroupBox);
QGridLayout* gridLayout(new QGridLayout);
gridLayout->addWidget(.../*whatever buttons etc*/)
groupBox->setLayout(gridLayout);
scrollArea->setWidget(groupBox);
setLayout(vBoxLayout);
...
[connect calls, etc.]
}

Qt - how to make QPushButton change between two QVBoxLayouts

So i have a main layout called the 'vboxmain'. And the program has two states: blackjack and poker. For both I have a button. In this vboxmain I have an upper part, which covers most of the program, and is the same for both games, but I also have a bottom part which should display different parts for both games. For example, poker game should hold five QCheckBoxes and one button. As for the blackjack game i need simply two buttons. I created both of these bottom layouts as QVBoxLayouts. So now i have:
BlackjackiValikud = new QVBoxLayout; //for blackjack
Pokkerivalikud = new QVBoxLayout; //for poker
And I tried creating two button actions like this:
void mainwindow::BlackJack_clicked(){
vboxmain->removeItem(Pokkerivalikud);
vboxmain->addItem(BlackjackiValikud);
}
void mainwindow::Poker_clicked(){
vboxmain->removeItem(BlackjackiValikud);
vboxmain->addItem(Pokkerivalikud);
}
Buttons are connected like this:
connect(BlackjackButton, SIGNAL(clicked()), this, SLOT(BlackJack_clicked()));
connect(PokerButton, SIGNAL(clicked()), this, SLOT(Poker_clicked()));
But currently it's not working and I can't figure out a way to do this, so I'm asking for help. This is probably not the best way to do this either but I don't know any other ways. So I could use some help on how to make this work with whatever solution - so that with both buttons I can change the bottom part of my vboxmain as needed.
I'm open to solutions.
What do you mean by it is not working?
You have to make sure that the layout are enabled when you add them (via QLayout::setEnabled ( bool enable)) or that widget are visible (via QWidget::show()). In general you have to manuable make visible items which are added to a widget which is already visible...
An alternative would be to use a QStackedLayout to display either. You have a widget poker for the poker view and a widget blackjack for the black jack view. On button push you use either
void QStackedLayout::setCurrentIndex ( int index )
void QStackedLayout::setCurrentWidget ( QWidget * widget )
You may want to keep the layouts and change what's presented in the bottom layout. To do so, create classes for each game(say blakjackWidget and pokerWidget) derived from QWidget. and show only one of them in the bottom layout.

Viewing entire QGraphicsScene

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);

qt - widget - positioning

I want to place some widgets in a parent widget in some random places, like one button at Point (10,10) and another at (15,40), etc. How to achieve this?. QGridLayout is pushing everything into row column style. But I want to put the widgets whereever I want,Can anybody help me?
If you really want to set absolute positions, I would ignore using a layout altogether. You can manually set the positions of elements by using the move() function or the setGeometry() function.
QWidget *parent = new QWidget();
parent->resize(400, 400);
QPushButton *buttonA = new QPushButton(parent);
buttonA->setText("First Button");
buttonA->move(10, 10);
QPushButton *buttonB = new QPushButton(parent);
buttonB->setText("Second Button");
buttonB->move(15, 40);
Side note: I would avoid setting absolute positions of elements in Qt. Why? Well, Qt tries to be a platform-independent GUI library. On different platforms, a lot of display things can change (i.e. font size of text in push buttons) so the size of your actual push buttons can vary to accommodate large or smaller font sizes. This can throw off your meticulously spaced push buttons is you use absolute positions as in the example above.
If you use layouts, overlapping buttons or buttons falling off the edge of your window can be avoided.
You can see my answer for overlay button in QT: Qt Widget Overlays. This may help you to achieve what you want.