In PyQt5, what does self keyword do as a parameter regarding creating Widgets? I don't see any difference between those two and both work totally fine.
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
###############This Part#############
#QLCDNumber() and QSLider() also works fine below
lcd = QLCDNumber(self)
sld = QSlider(Qt.Horizontal, self)
#####################################
vbox = QVBoxLayout()
vbox.addWidget(lcd)
vbox.addWidget(sld)
sld.valueChanged.connect(lcd.display)
self.setLayout(vbox)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Signal and slot')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
There's no difference.
TL;DR;
QWidget inherit from QObject, and QObjects have a tree of hierarchy between parents and children, in C ++ it serves so that when the parent is deleted it eliminates its children so that memory can be handled easily, in the case of PyQt it happens same since the memory handle is not handled directly by python but by C++.
The previous reason is that it serves to pass the parent parameter to the QObjects, on the other hand in the QWidgets the position of the children is always relative to the parent, so if you pass to self as parent the widget will be drawn on the parent.
Going to your specific code there is no difference because the layouts establish the parent of the widgets that handle the widget where it is established so you can eliminate the initial relationship of kinship since the layout will establish it.
We can see the difference if we do not use the layout since when removing the self nobody will establish where the widget will be drawn and therefore the widgets will not be shown.
Without self:
def initUI(self):
lcd = QLCDNumber()
sld = QSlider(Qt.Horizontal)
With self:
def initUI(self):
lcd = QLCDNumber(self)
sld = QSlider(Qt.Horizontal, self)
Related
I want the child widget to just appear in the center of the parent widget with a horizontal pink stripe. But the widget of the parent becomes very small.
from PySide6 import QtWidgets, QtGui
import sys
class WidgetA(QtWidgets.QWidget):
def __init__(self):
super(WidgetA, self).__init__()
self.wb = WidgetB()
vbox = QtWidgets.QVBoxLayout()
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self.wb)
vbox.addLayout(hbox)
self.setLayout(vbox)
class WidgetB(QtWidgets.QWidget):
def __init__(self):
super(WidgetB, self).__init__()
palette = self.palette()
palette.setColor(QtGui.QPalette.Window, QtGui.QColor("#ff00ff"))
self.setPalette(palette)
app = QtWidgets.QApplication()
window = WidgetA()
window.show()
sys.exit(app.exec())
If I have not written something, or something is not clear in my question, then ask, I will supplement it.
The window appears very small, because your example script never sets an explicit size for either the parent or the child. Directly setting the geometry of the child won't help, because it's in a layout, and the layout automatically manages the geometry of the widgets it contains. If you don't want to give the child widget a minimum size, you can use spacers and stretch factors to control the proportion of the space the child widget takes up. See the Layout Management article in the Qt docs for an exellent overview of all the possibilities.
Below is a rewrite of your example that should do what you asked for (i.e. make "the child widget ... appear in the centre of the parent widget with a horizontal pink stripe"). But note that the height of the child widget is proportional, so the layout will adjust its size accordingly whenever the parent widget is manually resized. If you want different proportions, change the stretch factors to suit. I have also reimplemented sizeHint. This provides a sensible initial size for the child, whilst allowing it to remain freely resizeable - but you could also just explicitly set the geometry of the parent window to achieve much the same thing.
from PySide6 import QtCore, QtWidgets, QtGui
class WidgetA(QtWidgets.QWidget):
def __init__(self):
super(WidgetA, self).__init__()
self.wb = WidgetB()
vbox = QtWidgets.QVBoxLayout()
vbox.addStretch(1)
vbox.addWidget(self.wb, 2)
vbox.addStretch(1)
self.setLayout(vbox)
class WidgetB(QtWidgets.QWidget):
def __init__(self):
super(WidgetB, self).__init__()
palette = self.palette()
palette.setColor(QtGui.QPalette.Window, QtGui.QColor("#ff00ff"))
self.setAutoFillBackground(True)
self.setPalette(palette)
def sizeHint(self):
return QtCore.QSize(300, 200)
app = QtWidgets.QApplication(['Test'])
window = WidgetA()
window.show()
app.exec()
I created a class 'commonlessonItem' that is parent class
after creation of parent , I have inherited it by a child class 'commonlessonpiskelStepItem'
and I added a widget to the layout
after runing program, i got this issue.
QLayout: Attempting to add QLayout "" to CommonLessonPiskelStepItem "", which already has a layout
QWidget::setLayout: Attempting to set QLayout "" on CommonLessonPiskelStepItem "", which already has a layout
what is problem in here?
Here comes my code
class CommonLessonItem(MyFrame):
def __init__(self,parent):
super(CommonLessonItem,self).__init__(parent)
self.lbl_title = CommonHeaderLabel(self)
self.lbl_description = CommonDescriptionLabel(self)
self.lbl_icon = CommonHeaderIcon(self)
self.initUI()
self.isChild = False
def initUI(self):
#set layout
self.layout = MyGridLayout(self)
self.layout.addWidget(self.lbl_title,0,0,1,19)
self.layout.addWidget(self.lbl_icon,0,19,1,1)
self.layout.addWidget(self.lbl_description,1,0,1,20)
#initialize info
self.setInfo("Title","Description",None)
self.setLayout(self.layout)
def setInfo(self,title,description,iconPath):
self.lbl_title.setText(title)
self.lbl_description.setText(description)
if(iconPath is not None):
self.lbl_icon.setPixmap(QPixmap(iconPath))
class CommonLessonPiskelStepItem(CommonLessonItem):
def __init__(self,parent):
super(CommonLessonPiskelStepItem,self).__init__(parent)
# self.lbl_referUrl = CommonDescriptionLabel('wwww.piskelapp.com')
self.initUI()
self.lbl_icon.setPixmap(QPixmap('icons/lookstep'))
def initUI(self):
super().initUI()
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
mw = CommonLessonPiskelStepItem(None)
mw.show()
sys.exit(app.exec_())
The error comes from the fact that you're trying to set the layout more than once.
the first time when you create the layout: self.layout = MyGridLayout(self) (using the self argument results in automatically setting the layout for the widget)
the second time when you try to set the layout: self.setLayout(self.layout); the layout is already set;
the third time when you call the super().initUI(): it was already and implicitly called when you used super().__init__;
While the second point is not an issue (if you use setLayout with the same layout, it will just be ignored), the third is the one causing the error, because you're trying to create a new layout for a widget that already has one, and later you also try to set that new layout for the widget.
Just remove the super().initUI() from the initUI method of the subclass.
I have a parent and child window. When you push the button from the parent class the child is shown and the parent is hidden. Is there anyway to keep the taskbar icon visible when doing this?
from PyQt5.QtCore import Qt, QDateTime
from PyQt5.QtWidgets import *
from PyQt5 import QtGui
class ParentWindow(QDialog):
def __init__(self):
super(ParentWindow, self).__init__()
self.cw = None
self.button = QPushButton('Go to child')
self.button.clicked.connect(self.child)
layout = QHBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.show()
def child(self):
self.cw = ChildWindow(self)
self.hide()
def parent(self):
self.cw.close()
self.show()
class ChildWindow(QDialog):
def __init__(self, parent):
super(ChildWindow, self).__init__(parent)
self.button = QPushButton('Go to parent')
self.button.clicked.connect(self.parent().parent)
layout = QHBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = ParentWindow()
sys.exit(app.exec_())
The problem is that a child QDialog "shares" the same taskbar entry as its parent, and when the parent is hidden, so does the taskbar entry, no matter if a child is visible.
A possible solution is to not hide the parent, but minimize it. Keep in mind that this has only been tested as working on Windows, on Linux you can have unexpected behavior (depending on how the window manager relates to modal windows) and I don't know how it will work out on MacOs.
The idea is to use showMinimized() on the parent, then show the child, activate it to make it visible on top and, most importantly, use exec_() to ensure it has full control and make it behave as a dialog should.
Then we connect both the accepted and rejected signals of the dialog to restore the main window, and connect the clicked signal to accept.
class ParentWindow(QDialog):
def __init__(self):
super(ParentWindow, self).__init__()
self.cw = None
self.button = QPushButton('Go to child')
self.button.clicked.connect(self.child)
layout = QHBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.show()
def child(self):
self.cw = ChildWindow(self)
self.showMinimized()
self.cw.show()
self.cw.activateWindow()
self.cw.exec_()
class ChildWindow(QDialog):
def __init__(self, parent):
super(ChildWindow, self).__init__(parent)
self.button = QPushButton('Go to parent')
self.button.clicked.connect(self.accept)
layout = QHBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.accepted.connect(self.parent().showNormal)
self.rejected.connect(self.parent().showNormal)
PS: I ended up not using a custom function to call back the parent at all, but remember that you shouldn't overwrite function/property names like parent.
I'm new to pyQt and am a bit stuck of how the layouts and widgets work. My understanding is that I need to create a new widget, then add a layout (e.g. GridLayout) to this widget and then set this layout onto the main window. The below is not working as I had hoped and I'm not sure where to go from here:
self.grid_widget = QtGui.QWidget(self)
self.grid_widget.setGeometry(120,10,500,235)
gridPalette = self.grid_widget.palette()
gridPalette.setColor(self.grid_widget.backgroundRole(), QtGui.QColor('white'))
self.grid_widget.setPalette(gridPalette)
grid = QtGui.QGridLayout()
self.grid_widget.setLayout(grid)
self.setLayout(self.grid_widget)
I basically need the widget to be in a certain place (where I've set geometry). I could just add the grid to the window but then the grid covers the entire window and not where I want it to be placed.
If anyone could explain the pipeline for this that would be extremely handy!
In PyQT you usually start with a "main window". I usually make a subclass of QtGui.QMainWindow for that purpose. The object instantiated from that class is my "main window".
Next I make a subclass of QtGui.QWidget. I make an object from that class, call it my "main widget", and put it central in the "main window". Assign a layout to that "main widget" and you can start adding child widgets to it. The layout makes sure that the child widgets are well aligned.
So it is somewhat like this:
from PyQt4 import QtGui
from PyQt4 import QtCore
import sys
class CustomMainWindow(QtGui.QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
self.setGeometry(300, 300, 800, 800)
self.setWindowTitle("my first window")
self.mainWidget = CustomMainWidget(self)
self.setCentralWidget(self.mainWidget)
...
self.show()
''' End Class '''
class CustomMainWidget(QtGui.QWidget):
def __init__(self, parent):
super(CustomMainWidget, self).__init__(parent)
self.mainLayout = None
self.initLayout()
self.putSomeWidgetInTheLayout()
...
def initLayout(self):
# When you use a layout, you do not need to pass a parent when
# constructing the child widgets. The layout will automatically
# reparent the widgets (using QWidget::setParent()) so that they
# are children of the widget on which the layout is installed.
# Widgets can only have other widgets as parent, not layouts.
self.mainLayout = QtGui.QGridLayout()
self.setLayout(self.mainLayout)
def putSomeWidgetInTheLayout(self):
# Notice that I don't assign a parent to 'lbl1'!
lbl1 = QtGui.QLabel("My label")
setCustomSize(lbl1, 160, 40)
self.mainLayout.addWidget(lbl1, *(0,0))
# -> I just added the label widget to the layout
# of the main (central) widget. The label widget
# had no parent widget to begin with. But by adding
# it to the layout, the layout will 'reparent' the
# label widget. So from now on, the label widget is
# a child of the 'main (central) widget'.
lbl2 = QtGui.QLabel("My second label")
setCustomSize(lbl2, 160, 40)
self.mainLayout.addWidget(lbl2, *(0,1))
''' End Class '''
def setCustomSize(x, width, height):
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
x.setSizePolicy(sizePolicy)
x.setMinimumSize(QtCore.QSize(width, height))
x.setMaximumSize(QtCore.QSize(width, height))
if __name__== '__main__':
app = QtGui.QApplication(sys.argv)
myWindow = CustomMainWindow()
sys.exit(app.exec_())
I hope this was helpful.
If you still have any questions, I would be happy to help you out.
So I used QT Designer to create a simple grid. This code is in C++ but you can easily translate it to Python. It's pretty good as it explains how the QT pipeline works, so if anyone like me has the same problems then this code should help you understand it a bit more.
The idea is to open a child window from parent window menu and when I minimize the parent window, the child window must be minimized also and only one child window can be opened.
I have the solution for minimizing the child when parent is minimized, but I can open child window multiple-times (although the child is already opened) and I would like to disable opening of multiple child windows.
The parent window is MainWindow.py:
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle('Parent window')
self.flags = QtCore.Qt.Window
self.ControlPanel = Control_panel_window()
self.createActions()
self.createMenus()
def createActions(self):
# window - menu
self.windowShowControlPanelAction = QtGui.QAction(self.tr("&Control panel"), self, statusTip='Control panel')
self.connect(self.windowShowControlPanelAction, QtCore.SIGNAL("triggered()"), self.ShowControlPanel)
def createMenus(self):
# window
self.windowMenu = QtGui.QMenu(self.tr("&Window"), self)
self.windowMenu.addAction(self.windowShowControlPanelAction)
self.menuBar().addMenu(self.windowMenu)
def ShowControlPanel(self):
self.ControlPanel = Control_panel_window(self)
self.ControlPanel.setWindowFlags(QtCore.Qt.Window)
self.ControlPanel.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
win.show()
sys.exit(app.exec_())
The child window is ChildWindow.py:
class Control_panel_window(QWidget):
def __init__(self, parent = None):
super(Control_panel_window, self).__init__(parent)
self.setFixedSize(200, 300)
def setWindowFlags(self, flags):
print "flags value in setWindowFlags"
print flags
super(Control_panel_window, self).setWindowFlags(flags)
The problem is: how can I set that only one child window is opened?
In your ShowControlPanel function you are creating a new control panel each time the signal is triggered. Since you already have an instance available why don't you use that instead?
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle('Parent window')
self.flags = QtCore.Qt.Window
self.control_panel = ControlPanelWindow(self)
self.control_panel.setWindowFlags(self.flags)
#...
def create_actions(self):
self.show_control_panel_action = QtGui.QAction(
self.tr("&Control panel"),
self,
statusTip='Control panel'
)
self.show_control_panel_action.triggered.connect(self.show_control_panel)
#...
def show_control_panel(self):
self.control_panel.show()
Some stylistic notes:
Try to follow PEP8 official python coding-style guide. This include using CamelCase for classes, lowercase_with_underscore for almost everything else. In this case, since Qt uses halfCamelCase for methods etc you may use it too for consistency.
Use the new-style signal syntax:
the_object.signal_name.connect(function)
instead of:
self.connect(the_object, QtCore.SIGNAL('signal_name'), function)
not only it reads nicer, but it also provides better debugging information. Using QtCore.SIGNAL you will not receive an error if the signal doesn't exist (e.g. you wrote a typo like trigered() instead of triggered()). The new-style syntax does raise an exception in that case you will be able to correct the mistake earlier, without having to guess why something is not working right and searching the whole codebase for the typo.