How to move a set of buttons in Qt? - qt

Currently I have a set of buttons in a QVBoxLayout which is inside a QHBoxLayout.
Is there a way to move these according to where my mouse is?
My current code is like this:
self.button1 = QPushButton()
self.button1.setText("button A")
self.button2 = QPushButton()
self.button2.setText("button B")
vbox = QVBoxLayout(self.canvas)
vbox.addStretch(1)
vbox.addWidget(self.button1)
vbox.addWidget(self.button2)
hbox = QHBoxLayout(self.window)
hbox.addStretch(1)
hbox.addLayout(vbox)"

The prototype for moving some button widget would be:
vbox.removeWidget(self.button) // that only needs the reference to your button item
vbox.insertWidget(newPos, self.button) // mind the difference in target position when you remove one item
That does not cover event processing. With two buttons you can either do that twice for each or maybe altogether, mind the target positions.
Or you can move an entire layout within another layout for the group of buttons in a very similar fashion. That'd be moving layout item. As long as you don't imply those buttons cannot be moved separately the example is for individual buttons. Oh, there is some Qt C++ discussion about that.

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

Qt-designer: enlarging the window does not replace my items

To keep the question as simple as possible, I prepared a simple Qt designer form below
There is a Tab Widget on the left side and empty QWidget on the right side, the QWidget as a GroupBox. The Groupbox has a radio and pusbuttons (you can see them on Object inspector window on the photo as well ). The tab widget has a line edit. The central widget has a gridlayout and horizontal qsplitter is used.
My issue is that when I enlarge the window, all items (lineedit, radiobutton, pushbutton) are on the fix position. Here is an example what I mean:
What I want is that when the window is enlarged the items should be placed on the bottom of the window, or if they are in the middle, then they should stay in the middle. ( I don't want size of the buttons/lineedits to be changed).
How can I do it?
The items you want to move dynamically, with window resizing must be in a layout.
So, in the example you've posted, you need two layouts; one inside the tab widget, for the QLineEdit and at least one in the GroupBox for the radio button and push button.
If you want the radio and push button to be aligned horizontally, you can start by placing them in a horizontal layout, before placing that layout in another, which all reside in the group box.
When you start to add items to layouts, such as push buttons, you'll start to notice that they can get stretched, so you may need to set the size policies of the widgets.
If you want the line edit to be centered horizontally, you will have to place two horizontal spacers on each side of the line edit and select the three together and set "Lay out Horizontally". This can found at the top toolbar in Qt Designer.
To always have it at the bottom of the tab widget, put a vertical spacer above the line edit in your tab widget. Then select the option "Lay out vertically" for the tab widget.
The same goes for your radio button and push button. Keep them in a horizontal layout, with horizontal spacers if required and put a vertical spacer into the group box and set the layout property for the group box as "Lay out Vertically".
Most important of all, I suggest you go through some basic tutorials before you continue. Here is a link to a good channel on youtube.
https://www.youtube.com/playlist?list=PL2D1942A4688E9D63
If you don't have a layout in your tabWidget or GroupBox:
You must set a layout (for example QVBoxLayout) inside your tab widget and a group box.
It can be done using QtDesigner. It also can be done in code like this:
QWidget *window = new QWidget;
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two");
QPushButton *button3 = new QPushButton("Three");
QPushButton *button4 = new QPushButton("Four");
QPushButton *button5 = new QPushButton("Five");
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);
window->setLayout(layout);
But if you do, and then you want your buttons to stay at the bottom:
Then you have to try setRowStretch method http://qt-project.org/doc/qt-4.8/qgridlayout.html#setRowStretch or take a look at QSpacerItem.

How can I exchange an item and a spacer?

In qt, I have a form that contains among other things, a group with
A combo box
a checkbox
a spacer
a button
Based on some logic, I want sometimes to show another combo box... Where the spacer is, but smaller.
When I add it though, everything resizes automatically
I don't see a way to make it invisible, and yet keep items of the same size when I make it visible again.
I tried making it fixed size... But unless I use fixed sizes and positioning for everything, which I think is a bad idea, the items still move around when I change visibility.
It seems silly... But how can I make my little combo box show up instead of the spacer not next to it ? Spacers don't seem to have a name...
I would do
combo.setVisible(condition);
Spacer.setVisible(!condition);
Very easy... Except how do I access the spacer from code ?
My suggestion is to use a container QWidget instead of the spacer. Here is how it will look:
A combo box
a checkbox
a widget-container
a button
Widget-container is a QWidget with fixed size. Put your combo-box there and it will maintain it's size when you show/hide the combo-box.
Regarding your question (You will not need it but just to know in the future):
how do I access the spacer from code
You can create a spacer from code like this:
QSpacerItem* spacer = new QSpacerItem(0, 15, QSizePolicy::Fixed, QSizePolicy::Fixed);
layout->addItem(spacer);
...
Also you can get it from a layout if you know its index:
QLayoutItem* item = layout->itemAt(index);
But there is no such method as show/hide for layout items.

(Qt) two rows of widgets in a toolbar

I can't figure out how creating a toolbar with two rows of widgets. I'm working on a Python3/Qt4 project.
My guess :
add a widget to the toolbar widget
add a vertical layout (or a QGridLayout, it doesn't matter) to the widget
add the toolbar's buttons to the layout
But all I get is a tiny and empty toolbar : do I need to somehow 'stretch' the widget ?
My Python3 code, trying to insert vertically two buttons in the toolbar :
self.main_toolbar = self.addToolBar('MyToolBar')
self.toolbar_widget = QtGui.QWidget(self.main_toolbar)
self.toolbar_layout = QtGui.QVBoxLayout()
self.toolbar_widget.setLayout(self.toolbar_layout)
button1 = QtGui.QPushButton(self)
button1.setText("button1")
self.toolbar_layout.addWidget( button1 )
button2 = QtGui.QPushButton(self)
button2.setText("button2")
self.toolbar_layout.addWidget( button2 )
The code you posted is fine, it's just that you forgot to add your widget to the toolbar.
You can do this using QToolBar.addWidget:
self.main_toolbar.addWidget(self.toolbar_widget)
It's simple: you really need two toolbars, one under another.
You can't merely add child widgets to a toolbar. Your main_toolbar is presumably of the QToolBar class. You can't simply add a toolbar_widget to it!
Neither you can change the layout of a QToolBar. The toolbar manages the layout of its children itself, you're not supposed to mess with it - it's not designed that way.
The only way of adding widgets to a toolbar is via the addWidget method of the toolbar, not of the layout!
Your code is wrong. What you can do, and all that you can do with a toolbar is:
self.main_toolbar = self.addToolBar('MyToolBar')
button1 = QtGui.QPushButton(self)
button1.setText("button1")
self.main_toolbar.addWidget( button1 )
button2 = QtGui.QPushButton(self)
button2.setText("button2")
self.main_toolbar.addWidget( button2 )
What you want to do is simply not supported by the toolbar. The toolbar may, perhaps, arrange its items in more than one row if they don't all fit. It probably depends on the style. You're free to insert "tall" toolbar widgets that have an internal layout and two sub-buttons inside, but it'll look very ugly.

How do i resize the contents of a QScrollArea as more widgets are placed inside

I have a QScrollArea Widget, which starts empty;
It has a vertical layout, with a QGridLayout, and a vertical spacer to keep it at the top, and prevent it from stretching over the whole scroll area;
Elsewhere in the program, there is a QTextEdit, which when changed, has its contents scanned for "species" elements, and then they are added to the QGridLayout. Any species elements which have been removed are removed too. This bit works;
I have turned the vertical scrollbar on all the time, so that when it appears it does not sit on top of the other stuff in there. Note that the scroll bar is larger than the scroll box already though, despite not needing to be.
This is the problem. The scroll area seems to be preset, and i cannot change it. If i add more rows to the QGridLayout, the scroll area doesn't increase in size.
Instead, it stays the same size, and squeezes the QGridLayout, making it look ugly (at first);
And then after adding even more it becomes unusable;
Note that again, the scroll bar is still the same size as in previous images. The first two images are from Qt Designer, the subsequent 3 are from the program running.
If I resize the window so that the QScrollArea grows, then I see this:
Indicating that there's some layout inside the scroll area that is not resizing properly.
My question is; what do I need to do to make the scrollable area of the widget resize dynamically as I add and remove from the QGridLayout?
If you're coming here from Google and not having luck with the accepted answer, that's because you're missing the other secret invocation: QScrollArea::setWidget. You must create and explicitly identify a single widget which is to be scrolled. It's not enough to just add the item as a child! Adding multiple items directly to the ScrollArea will also not work.
This script demonstrates a simple working example of QScrollArea:
from PySide.QtGui import *
app = QApplication([])
scroll = QScrollArea()
scroll.setWidgetResizable(True) # CRITICAL
inner = QFrame(scroll)
inner.setLayout(QVBoxLayout())
scroll.setWidget(inner) # CRITICAL
for i in range(40):
b = QPushButton(inner)
b.setText(str(i))
inner.layout().addWidget(b)
scroll.show()
app.exec_()
The documentation provide an answer :
widgetResizable : bool
This property holds whether the scroll area should resize the view widget.
If this property is set to false (the default), the scroll area honors the size of its widget.
Set it to true.
Why don't you use a QListView for your rows, it will manage all the issues for you? Just make sure that after you add it you click on the Class (top right window of designer) and assign a layout or it wont expand properly.
I use a QLIstWidget inside a QScrollArea to make a scrollable image list
Try this for adding other objects to the list, this is how I add an image to the list.
QImage& qim = myclass.getQTImage();
QImage iconImage = copyImageToSquareRegion(qim, ui->display_image->palette().color(QWidget::backgroundRole()));
QListWidgetItem* pItem = new QListWidgetItem(QIcon(QPixmap::fromImage(iconImage)), NULL);
pItem->setData(Qt::UserRole, "thumb" + QString::number(ui->ImageThumbList->count())); // probably not necessary for you
QString strTooltip = "a tooltip"
pItem->setToolTip(strTooltip);
ui->ImageThumbList->addItem(pItem);
Update on Artfunkel's answer:
Here's a PySide6 demo that uses a "Populate" button to run the for loop adding items to the scroll area. Each button will also delete itself when clicked.
from PySide6.QtWidgets import *
app = QApplication([])
scroll = QScrollArea()
scroll.setWidgetResizable(True) # CRITICAL
inner = QFrame(scroll)
inner.setLayout(QVBoxLayout())
scroll.setWidget(inner) # CRITICAL
def on_remove_widget(button):
button.deleteLater()
def populate():
for i in range(40):
b = QPushButton(inner)
b.setText(str(i))
b.clicked.connect(b.deleteLater)
inner.layout().addWidget(b)
b = QPushButton(inner)
b.setText("Populate")
b.clicked.connect(populate)
inner.layout().addWidget(b)
scroll.show()
app.exec()

Resources