How do containers/layouts reposition their children? - qt

I'm working on a simple widget system, and I'm implementing some containers right now.
Here's the situation I find myself in:
I have a Widget base class, a Container class, which is a widget that can contain other widgets, and several widget sub classes like Button.
I have two types of container: Container itself, which positions children absolutely, and Box, which will stack widgets next to each other, either horizontally or vertically.
Each widget draws itself at x=0, y=0. Therefore, containers need to add an offset to the drawing context before the widgets are told to draw themselves.
Each widget does its own hit testing based on its x/y position.
So far, it works fine. But it falls apart now that I'm implementing Box: What I do is that I overwrite the drawfunction inherited from Container to draw them all in next to each other, instead of based on their x/y position. Quite simple.
But event handling is totally off now, as the widget's x/y position has become meaningless.
I think I have two options:
Have the widget do hit testing at position x=0, y=0, like drawing. Then recalculate the mouse position to match that in Container.
Make each layout set x/y for its children, and make children draw themselves at their x/y position again. No more offsets for the drawing context
The first one is a bit ugly, I think. The second one is pretty complicated to implement, since I need to react to position changes in widgets.
How to other widget systems like Qt, Gtk and wxWidgets generally tackle this? I've looked at the source of some of these, but can't quite figure that out, it's too sophisticated. I don't have any resizing or packing issues to consider.

You are trying to implement your own layout system. You should expect it to be difficult.
I would advise against the first method. The x,y coordinates of a widget are not only used by the widgets themselves, but by anyone outside of the container who wants to do something with the widget.
The second solution is what I've chosen to implement custom widgets made of several smaller widgets and it's not that hard to put together if you don't want too many features.
Just get the widgets when they are added to your container, set their position to the current free spot, and move on to the next.

Related

How can I create multiple custom widgets and display them with their absolute position

So I currently have got a custom widget, and I want to add them to the main window after clicking a button. I would like to add them all to one fixed position first and then I will be able to drag them wherever I like. I am able to create and display these custom widgets with help of QHBoxLayout or QVBoxLayout, but in this case they will not be in the same position after I create them. Any help will be appreciated!
As the names suggest, the QLayout classes manage the position and geometry of the items added to them. You cannot move (eg. drag) an item out of a layout w/out first removing it from the layout (QLayout::removeItem() and derivatives). For example when you drag a toolbar or dock widget out of a QMainWindow it goes through all sorts of machinations to remove it from the MW layout, change the widget's window flags, remember the old position in the layout, and so on. And the reverse to dock it again.
To do what you describe (drag widgets arbitrarily around a window) you would need to not use a QLayout and position the widgets manually by specifying a QWidget::setGeometry() for example. After initial position, and assuming the user has some way to grab the widget (title bar or drag handle you made, etc), you'll probably still need to manage their positions, for example if the main window is resized (if you care about keeping them contained). Essentially you'd have a bunch of separate widgets acting as individual windows and probably need some way to keep track of them.
I don't know what kind of widgets you're talking about, but one option may be a QMdiArea which lets the user drag windowed widgets around, tabify them, save/restore state, and so on.
For more flexibility you could also look into the Qt Graphics Framework. The graphics scene has a lot of features for user-movable items/widgets, keeping track of them, and so on. It is probably the most flexible method overall, and you can also use regular QWidgets inside a graphics scene.
A couple other Q/A about arbitrarily positioning widgets (I'm sure there are more to be found):
QPushButton alignment on top another widget
How to keep Push Buttons constant in relative to change of Label Size in PyQt4

Resizing Layout equal to MainWindow

When I run my program it will display all content properly, and when I resizing the main window, the layout along with all associated widgets remain fixed, rather than resizing with the main window. I used to increase my all widget and listWidget respect to window computer resolution size but still this not one work properly.
I used this one code finding the system height and width.
QWidget widget;
widget.resize(widget.width(), widget.minimumHeight());
QRect rec = QApplication::desktop()->screenGeometry();
int h = rec.height();
int w = rec.width();
// Increasing the listwidget size
ui->listWidget->setFixedHeight(h);
ui->listWidget->setFixedWidth(w);
//increasing the button size
ui->pushButton->setFixedHeight(h0.2);
ui->pushButton->setFixedWidth(w0.2);
At this link you will find two screenshots that illustrate my problem.
Please resolve to solve my problem. Thanks very much in advance.
When defining the layout of your windows and forms in Qt Designer you have to define each element of your form in advance, in order to have a working layout.
This solution is based on the screenshots provided in the comments to the question. Follow these steps:
Add an empty widget to the central area of your form, if there is nothing there. It will be used as a placeholder for the controls you will add later, and of course you can replace it with whatever widget you want. But you need it there to define a proper layout.
In the property panel, set the horizontal QSizePolicy of this widget to MinimumExpanding.
Add an horizontal spacer to the left side of your progress bar.
Define a minimum/maximum width for the white widget on the left (I guess it's a text area). As an example set the maximum width to 200
pixels.
Make the same for the QTabWidget on the right.
Give a minimum height to the Groupbox on top.
Then give the grid layout to the MainWindow.
You should get something similar in the designer view (I use a dark theme, yours will have different colors of course):
If you complete all steps you should have a nicely resizing window.
For the future: remember to integrally define your layouts, also using placeholder widgets when needed, read carefully the documentation about the widgets size policies (there are several, you need to play with them to fully understand each one) and keep in mind that Qt uses a container based approach which is different, as an example from those used by the .Net framework that relies on the concept of anchors.
EDIT : to answer questions in the comments
You will need to add a layout to any widget that contains other widgets, e.g. adding controls to your groupbox will require to give it a grid, horizontal or vertical layout in order to scale nicely on resize. Again use spacers and size policies to make it look the way you want. If you need to add or remove controls, or change their positions, you may need to brake the layout, rearrange and then set it again.
You can also select groups of widgets and give them a layout e.g. vertical, than another group and set them horizontal and so on... then give a grid layout to the container widget to build a compound layout.
There are endless possibilities, you just need to practice and go through trial and error as for everything else...
You can also do it all programmatically, check the Qt widgets documentation for this. But for complex layouts I would not go that way: it's a lot of code... and you have to compile and run to test every modification.
Using the QtCreator, within the designer you can simply right-click on the parent-widget and add a Grid-Layout.
This one resizes it's children to it's dimensions.

User resize able widgets like 3D modeling programs

I am using qt designer and python. I want to make a layout where a user can drag widgets to control the size while at the same time scaling properly with the window. Basically all 3d modeling programs have this function, usually it has a top left right and perspective view. The user can drag the edges to resize each view and it will maintain that aspect ratio when the main window is resized. My question is similar to this: active resizing of widgets inside mainwindow but I would like to do it in qt designer.
So what I have found is the layout horizontal/vertical splitter. However I am only able to layout two widgets horizontally, and another two widgets horizontally, then grid layout. This makes it look similar to what I want except I want the top and bottom horizontal layouts to also be re-size able.
Thanks
Edit:
I have been able to do this with putting both horizontal split views in a group box and then splitting the two group boxes, however that makes each widget independent and does not let the user grab the middle axis to control all 4 views.
You are essentially asking for a splitter than works across two axes - no such standard widget exists. So you will have to create your own.

drawForeground on QGraphicsRectItem

I need a way for a QGraphicsRectItem to draw on top of its children. I have a item that contains several children items. At a specific height I need to draw a line over the child items.
Is there a way to implement a drawForeground in a QGraphicsItems similar to the drawForeground in QGraphicsScene?
If at all possible I would prefer to not have to draw the line for each child item.
I'm not entirely sure what you are trying to do but I suspect you will encounter a great deal of grief trying to fight the framework by trying to make a part of the parent draw over its child items. Perhaps you can add a top level child that is over the other child items covering a limited area, and renders the same content as the parent and includes the special overlay lines you want?
It might also suit your purpose to draw right over the scene from the graphics view.

Qt QHboxLayout cell size ssues

For those of you who haven't been reading my Qt questoins, I am learning Qt for a project. I have only limited experience with GUI design at all, and not in Qt.
I've got a horizontal layout that I want to populate with some buttons. I can feed these buttons in just fine, but my formerly-square buttons are stretched horizontally to take up more space.
I want to let the layout manager determine the best way to size these buttons, but I also want their original proportions to remain intact. For instance, if I start would with 32X32 buttons that need to shrink to fit all of them in the layout, I want them to shrink proportionally so that the width to height scale is maintained. 20X20, 16X16, 12X12 would all be just fine, but 24X16 would be an example of dimensions that are unacceptable.
I've tinkered with size policies on the buttons and stretch options. I'm not seeing, even after reading the QPushButton and QHboxLayout classes how to do this. How is it accomplished?
Thanks.
As long as I understand the question correctly, I think what you want is QBoxLayout::addStretch(). This will add a spacer object that fills the unused space. So the buttons will have their ideal size and the spacer will fill the rest. You can try experimenting with this in Designer, it's easier than the write/compile/run cycle.
You should take a look at the answers to this question. This is a recap of my answer there.
You need to create a custom derivative of QLayoutItem, which overrides bool hasHeightForWidth() and int heightForWidth( int width ) to preserve the aspect ratio. You could either pass the button in and query it, or you could just set the ratio directly. You'll also need to make sure the widget() function returns a pointer to the proper button.
Once that is done, you can add a layout item to a layout in the same manner you would a widget. So when your button gets added, change it to use your custom layout item class.
I haven't actually tested any of this, so it is a theoretical solution at this point. I don't know of any way to do this solution through designer, if that was desired.

Resources