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.
Related
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)
I am trying to create a GUI (see attached screenshot) where it has about 6 sets of widgets where each set comprises of QLabel, QLineEdit and QPushButton.
Instead of writing the widgets and the layout again and again, I thought of writing up a wrapper function such that I will only need to write up 6 lines for the creation.
However, while this works efficiently for me in terms of the widgets creation, I realized that I am not able to set the Signal for the button(s) as I do not know the widget's names.
What will be the best way to set the signals if I decided to use this wrapper methodology?
The following is a screenshot of what I am trying to achieve as well as my code:
class MyDevTool(QtGui.QWidget):
def __init__(self, parent=None):
super(MyDevTool, self).__init__(parent)
self._create_ui()
def _create_hbox_layout(self, label):
h_layout = QtGui.QHBoxLayout()
label = QtGui.QLabel(label)
label.setFixedWidth(80)
line_edit = QtGui.QLineEdit()
push_btn = QtGui.QPushButton("<<<")
h_layout.addWidget(label)
h_layout.addWidget(line_edit)
h_layout.addWidget(push_btn)
return h_layout
def _create_ui(self):
main_layout = QtGui.QVBoxLayout()
main_layout.addLayout(
self._create_hbox_layout("Input Directory")
)
main_layout.addLayout(
self._create_hbox_layout("Output Directory")
)
self.setLayout(main_layout)
I have a problem to set a new layout in my QWidget object. I start to set one type of layout when the app exec, and I want to change it when the button is pressed with a new layout. In the documentation of PySide I read this:
Sets the layout manager for this widget to layout.
If there already is a layout manager installed on this widget,
PySide.QtGui.QWidget won’t let you install another. You must first
delete the existing layout manager (returned by
PySide.QtGui.QWidget.layout() ) before you can call
PySide.QtGui.QWidget.setLayout() with the new layout.
But how can I delete the existing layout manager? What are the methods which I must apply on my QWidget object?
If you're new to PySide/PyQt, see the Layout Management article in the documentation for an overview of Qt's layout system.
For your specific example, you will need a method to recursively remove and delete all the objects from a layout (i.e. all its child widgets, spacer-items and other layouts). And also a method to build and add the new layout.
Here's a simple demo:
from PySide import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
layout = QtGui.QVBoxLayout(self)
self.changeLayout(QtCore.Qt.Vertical)
self.button = QtGui.QPushButton('Horizontal', self)
self.button.clicked.connect(self.handleButton)
layout.addStretch()
layout.addWidget(self.button)
def handleButton(self):
if self.button.text() == 'Horizontal':
self.changeLayout(QtCore.Qt.Horizontal)
self.button.setText('Vertical')
else:
self.changeLayout(QtCore.Qt.Vertical)
self.button.setText('Horizontal')
def changeLayout(self, direction):
if self.layout().count():
layout = self.layout().takeAt(0)
self.clearLayout(layout)
layout.deleteLater()
if direction == QtCore.Qt.Vertical:
layout = QtGui.QVBoxLayout()
else:
layout = QtGui.QHBoxLayout()
for index in range(3):
layout.addWidget(QtGui.QLineEdit(self))
self.layout().insertLayout(0, layout)
def clearLayout(self, layout):
if layout is not None:
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
else:
self.clearLayout(item.layout())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 100)
window.show()
sys.exit(app.exec_())
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.
I have an extended main window with a QtGui.QTabWidget added to it. I am creating several widgets extended from QtGui.QWidget which I can add and remove to the tab widget.
What I would like to do is have a "pop-out" button that causes the child widget to be removed from the tab widget and come up as it's own independent window (and a "pop-in" button to put it back into the main window). The same sort of idea as Gtalk-in-Gmail has.
Note that if I close the main window, the other "tabs" or "windows" should also close, and I should be able to put all the windows side-by-side and have them all visible and updating at the same time. (I will be displaying near-realtime data).
I am new to Qt, but if I'm not mistaken, if a Widget has no parent it comes up independently. This works, but I then have no idea how I could "pop" the window back in.
class TCWindow(QtGui.QMainWindow):
.
.
.
def popOutWidget(self, child):
i = self.tabHolder.indexOf(child)
if not i == -1:
self.tabCloseRequested(i)
self.widgets[i].setParent(None)
self.widgets[i].show()
My gut says that there should still be a parent/child relationship between the two.
Is there a way to keep the parent but still have the window come up independently, or am I misunderstanding Qt's style?
Otherwise, would creating a variable in the child to hold a link to the main window (like self.parentalUnit = self.parent()) be a good idea or a hackish/kludgy idea?
Leave the parent as is. If you remove the parent, then closing main window won't close 'floating' tabs, since they are now top-level windows. windowFlags defines if a widget is window or a child widget. Basically, you need to alternate between QtCore.Qt.Window and QtCore.Qt.Widget
Below is a small but complete example:
#!/usr/bin/env python
# -.- coding: utf-8 -.-
import sys
from PySide import QtGui, QtCore
class Tab(QtGui.QWidget):
popOut = QtCore.Signal(QtGui.QWidget)
popIn = QtCore.Signal(QtGui.QWidget)
def __init__(self, parent=None):
super(Tab, self).__init__(parent)
popOutButton = QtGui.QPushButton('Pop Out')
popOutButton.clicked.connect(lambda: self.popOut.emit(self))
popInButton = QtGui.QPushButton('Pop In')
popInButton.clicked.connect(lambda: self.popIn.emit(self))
layout = QtGui.QHBoxLayout(self)
layout.addWidget(popOutButton)
layout.addWidget(popInButton)
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__()
self.button = QtGui.QPushButton('Add Tab')
self.button.clicked.connect(self.createTab)
self._count = 0
self.tab = QtGui.QTabWidget()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.tab)
def createTab(self):
tab = Tab()
tab.setWindowTitle('%d' % self._count)
tab.popIn.connect(self.addTab)
tab.popOut.connect(self.removeTab)
self.tab.addTab(tab, '%d' % self._count)
self._count += 1
def addTab(self, widget):
if self.tab.indexOf(widget) == -1:
widget.setWindowFlags(QtCore.Qt.Widget)
self.tab.addTab(widget, widget.windowTitle())
def removeTab(self, widget):
index = self.tab.indexOf(widget)
if index != -1:
self.tab.removeTab(index)
widget.setWindowFlags(QtCore.Qt.Window)
widget.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
In Qt, layout takes ownership over the widgets that are added to layout, so let it handle parentship.
You can create another widget (with no parent) which will be hidden until you press pop-out button and when it is pressed, you remove "pop-out widget" from its original layout and add it to layout of the hidden widget. And when pop-in button pressed - return widget to it's original layout.
To close this hidden window, when closing main window, you will need to redefine closeEvent(QCloseEvent* ev) to something like this (sorry for c++, but i bet, in python it's all the same):
void MainWindow::closeEvent(QCloseEvent* ev)
{
dw->setVisible(false); // independent of mainwindow widget
sw->setVisible(false); // independent of mainwindow widget
QWidget::closeEvent(ev); //invoking close event after all the other windows are hidden
}