Can you display a QDialog as the central widget of a QMainWindow in Python? If so, how would you do it? I am trying to find the easiest way to add a menu bar which is only available with QMainWindow to my understanding. Is it possible to connect the two together?
tl;dr
No, but you can add a menubar to a layout set for the QDialog, using setMenuBar() or even by adding the menubar as you would do for any other widget, just by doing that on "top" of that layout.
Is it possible?
The technical answer is "yes": since QDialog is a QWidget, you can just use setCentralWidget() as you would do with any other QWidget subclass.
The real answer is "NO: don't do it!".
QDialog, just like QMainWindow, is intended to be a top level widget (aka, a "window"), so it should never be added as a child of a widget or in its layout.
There are very few exceptions to that:
when using systems that are intended as possible "containers" of windows, which are:
QMdiArea;
the Graphics View framework;
in very special cases (such as attempting custom layouts of dock widgets), for which you should really know what you're doing, be really aware of the limitations and aspects related to the UX;
Most importantly, QDialog has specific flags and event filters that might be problematic.
For instance, take this simple example:
mainWindow = QMainWindow()
dialog = QDialog()
layout = QVBoxLayout(dialog)
layout.addWidget(QPushButton())
mainWindow.setCentralWidget(dialog)
mainWindow.show()
Now, just press Esc, and you'll see that the dialog disappears.
The same happens by adding a QDialog as a child of any widget, clearly meaning that it should never be done (unless when using the "container" systems listed above).
This is one of the many reasons for which some tutorials on YouTube should be completely disregarded (since they provide terrible suggestions, like adding a QMainWindow or QDialog to a QStackedWidget).
The solution
Actually, it's very simple: just add the menubar to the top level layout of the dialog, just like you would do for any other widget.
Besides, consider that all Qt layout managers inherit from QLayout, which has a very basic and important function that is often ignored: setMenuBar().
Tells the geometry manager to place the menu bar widget at the top of parentWidget(), outside QWidget::contentsMargins(). All child widgets are placed below the bottom edge of the menu bar.
Note that this is actually a "convenience" feature, and it only works for the "top level" layout: if you add the menubar to a nested layout, it won't be considered in the whole size hint/policy computation, and it will be probably shown above (in the z stacking level) any other widget near it.
Also note that, for obvious reasons, this cannot be done from Designer. If you have a QDialog created in Designer and you want to add a menubar, you have to do it by code.
Assuming that you properly set a top level layout (as you should always do) for your dialog in Designer:
from myDialog import Ui_MyDialog
from PyQt5.QWidgets import *
class MyDialog(QDialog, Ui_MyDialog):
def __init__(self):
super().__init__()
self.setupUi(self)
self.menuBar = QMenuBar()
self.layout().setMenuBar(self.menuBar)
self.fileMenu = self.menuBar.addMenu('File')
self.someAction = self.fileMenu.addAction('Some action')
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
dialog = MyDialog()
dialog.exec()
Related
I'm researching refactoring a tkinter app that currently uses tkinter.Canvas. The Canvas reacts to user input by creating windows over the canvas, which are made of regular tkinter widgets.
I haven't seen the equivalent method in QGraphicsView to tkinter.Canvas.create_window, wihch creates a canvas bound item that can then be used for building a regular interface.
Basically I'm looking for the right way to, say, right click on the View and get a popup window on top that I can deal with. Hopefully not bound to the scene, either, so that if I have multiple views on the scene the window only appears over the original View.
Because I'm new to Qt I'm out of my depth. Perhaps the widget that contains the View can receive the clicking event and proceeds to layout another widget in absolute terms over the QGraphicsView?
Any help is appreciated. Thanks!
I Found how to do it:
import sys
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QApplication,
QGraphicsScene,
QGraphicsView,
QGraphicsWidget,
)
app = QApplication(sys.argv)
scene = QGraphicsScene()
view = QGraphicsView(scene)
view.resize(400, 300)
window = QGraphicsWidget(None, Qt.Window)
window.resize(200, 200)
scene.addItem(window)
view.setScene(scene)
view.show()
sys.exit(app.exec())
Notice that unlike tkinter although you can add a regular widget to the QGraphicsScene directly with scene.addWidget, there are some limitations.
The method scene.addWidget returns a QGraphicsProxyWidget which is what can be positioned on the scene. And the layout of these widgets are different to the ones of regular widgets.
I want to have a small QFormLayout that grows to fill its parent widget.
I created a new .ui file using the QWidget template in Qt Designer. I put a QFormLayout inside that 'window', then put some controls inside that QFormLayout.
This all works reasonably well, but the QFormLayout always stays at the size I set in Qt Designer. I would like the QFormLayout to fill its parent widget and grow/shrink with it.
How can I accomplish that?
In Designer, activate the centralWidget and assign a layout, e.g. horizontal or vertical layout.
Then your QFormLayout will automatically resize.
Always make sure, that all widgets have a layout! Otherwise, automatic resizing will break with that widget!
See also
Controls insist on being too large, and won't resize, in QtDesigner
I found it was impossible to assign a layout to the centralwidget until I had added at least one child beneath it. Then I could highlight the tiny icon with the red 'disabled' mark and then click on a layout in the Designer toolbar at top.
The accepted answer (its image) is wrong, at least now in QT5. Instead you should assign a layout to the root object/widget (pointing to the aforementioned image, it should be the MainWindow instead of centralWidget). Also note that you must have at least one QObject created beneath it for this to work. Do this and your ui will become responsive to window resizing.
You need to change the default layout type of top level QWidget object from Break layout type to other layout types (Vertical Layout, Horizontal Layout, Grid Layout, Form Layout).
For example:
To something like this:
Everytime you create new project from Qt Creator there is on top QWidget which you can delete.
You can of course write the code manually as this:
app = QApplication(sys.argv)
ex = Main()
sys.exit(app.exec_())
where Main class is inherited from QTabWidget and everything works.
class Main(QTabWidget):
def __init__(self):
super().__init__()
Is there any way how to achieve this from Qt Designer?
How can i delete QWidget and put QTabWidget on top of hierarchy?
When you create a GUI with Qt Designer you can choose the widget that you take as base as shown in the following image:
Generally we choose from the first option templates/forms, but we can also choose from Widgets (Second option)
In this second option we can choose as a basis for QTabWidget.
I'm quite new to Python, and I'm developing an app with the PySide library.
I have a QTabWidget in which I will later define buttons/labels/... with some layouts. The question is simple : I'd like to have my active tab filling the window horizontally and vertically, even when I resize the window. For now its size is determined by the widgets I put in. Even with addStretch(1) in a QHBoxLayout or QVBoxLayout I can't get it to expand.
Is there an easy way to do this (I'm not a Python expert yet) ?
Any help will be highly appreciated.
If your main window is QMainWindow then it is enough to insert QTabWidget with mainwindow->setCentralWidget() - by default it will use all available space.
In other case, to have QTabWidget to follow the resizing/geometry change of parent widget, it is enough to make some layout on the parent widget and just insert QTabWidget there.
Code is approx (in Qt):
QWidget * dialog = ...
QTabWidget * tabs = new QTabWidget;
QVBoxLayout * vbox = new QVBoxLayout;
vbox->addWidget(tabs);
dialog->setLayout(vbox);
dialog->show();
I'm using python 2.7.5 on OSX 10.8. I'm learning PySide and trying to build a simple GUI.
I managed to use buttons (WOAAA!) used to chose a path or execute functions :
pathBtn = QtGui.QPushButton("FITS file path", self)
pathBtn.setToolTip('Choose the <b>path</b> to your FITS file')
pathBtn.clicked.connect(essai)
pathBtn.resize(pathBtn.sizeHint())
pathBtn.move(200, 100)
My problem is, when the program is running and I change the size of the window with the mouse cursor, the buttons don't move, don't adapt to the size variation.
I tried to find some answer (hell yeah google) and I understand that "QVBoxLayout" should do what I want (some kind of "dynamic" positionning, don't know if there's a specific name for that), but I didn't understand its syntax nor how to use it...
Any help appreciated!
In Qt widgets, layouts and the widget's size hints determine how things resize. The general procedure to layout a widget would be (for example):
dialog = QDialog()
layout = QVBoxLayout()
label = QLabel('This is a label')
edit = QLineEdit('This is a line edit box')
layout.addWidget(label)
layout.addWidget(edit)
dialog.setLayout(layout)
(*I cannot test this code here at work (no Qt/PySide), so consider this "pseudo code" :-)
This results in a dialog widget with a label and an edit box. If you resize the dialog widget, the layout and the resize properties of the widgets ensure that the label and edit box resize appropriately: horizontally both expand maximally, but vertically the edit will keep the same size while the label takes all the remaining space. This is because the resize hint of the edit box says it wants to keep its height (namely, one line of text).
If you do not specify a layout, your widgets (buttons, labels) don't do anything whenr resizing their parent widget, which is what you are observing. Hence, the solution is indeed the QVBoxLayout, use it as I described above.
By the way: for more complicated layouts, you probably want to use the Designer tool provided with Qt: this tool lets you see and test your GUI a priori.