PyQt4 nested classes - "RuntimeError: underlying C/C++ object has been deleted" - python

I'm trying to build a cool app, but it seems I lack some knowledge. Read lots of infos and examples in internet, but it doesn't help:
Understanding the "underlying C/C++ object has been deleted" error
Ok, here what I do:
I create central widget from my main.py, which works fine and I don't post it here fully:
self.rw = ReportWidget()
self.setCentralWidget(self.rw)
And here is my central widget - report.py:
#! /usr/bin/env python
# -*- coding: utf-8 -*-
from PyQt4 import QtGui, QtCore
class ReportWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(ReportWidget, self).__init__(parent)
self.setup_ui()
def setup_ui(self):
report = QtGui.QVBoxLayout(self)
report.setAlignment(QtCore.Qt.AlignTop)
head = QtGui.QHBoxLayout()
add_label = QtGui.QLabel(u"Add")
head.addWidget(add_label)
report.addLayout(head)
area = QtGui.QScrollArea()
area.setWidgetResizable(True)
area.setEnabled(True)
layout = QtGui.QVBoxLayout()
layout.setAlignment(QtCore.Qt.AlignTop)
widget = QtGui.QWidget()
widget.setLayout(layout)
area.setWidget(widget)
report.addWidget(area)
self.s = layout
# trying to create first line:
first_line = Line(self)
self.s.addWidget(first_line)
first_line.set_controls(True, False)
head = QtGui.QHBoxLayout()
ok = QtGui.QPushButton(u"Calculate")
head.addWidget(ok)
report.addLayout(head)
Continued from the same file report.py:
class Line(QtGui.QWidget):
def __init__(self, parent=None):
super(Line, self).__init__(parent)
self.setup_ui(parent)
def setup_ui(self, parent):
add_button = QtGui.QPushButton()
add_button.setObjectName("add_button")
self.add_button = add_button
self.layout = QtGui.QHBoxLayout(line)
self.layout.addWidget(add_button)
def set_controls(self, add_button=True, remove_button=True):
self.add_button.setEnabled(add_button)
Thus, running main.py raises RuntimeError: underlying C/C++ object has been deleted error on the last piece of code where I try to setEnabled parameter to new button, as if it was never created or bound anywhere.
It seems I have some design flaw. Maybe it's wrong idea to have different classes in one file or else? Or maybe I don't quite control which widget has which parent and how layouts work.
Thank you for reading. Have a nice day!

Thanks to everyone who tried to answer! Unfortunately no one said what a bunch of crap I wrote! *smile*
My line is already a widget and I don't need to create itself inside itself. All I had to do is to create layout inside setup_ui and add widgets to it. Finally it looks like:
class Line(QtGui.QWidget):
def __init__(self, parent=None):
super(Line, self).__init__(parent)
self.setup_ui(parent)
def setup_ui(self, parent):
line = QtGui.QHBoxLayout(self)
add_button = QtGui.QPushButton()
add_button.setObjectName("add_button")
line.addWidget(add_button)
# to get reference from outside
self.add_button = add_button
def set_controls(self, add_button=True, remove_button=True):
self.add_button.setEnabled(add_button)
Special thanks to nymk and Avaris!

I could not reproduce an error with the code you showed us (apart from an error about the variable line not being defined in Line.setup_ui). If I replaced line with self, I got no error.
However, I could get a crash if I set line to a QWidget that I created and didn't keep a reference to. In other words, I added
line = QtGui.QWidget()
to Line.setup_ui, and found that this crashed on the same line of code you reported, complaining that the wrapped C/C++ object had been deleted.

Related

PyQt5 can't create Tabbars or Tabs

I have been struggling to learn PyQt5 (and object oriented programming). In my current script I need to create a tabbed interface but can't seem to manage it. I suspect the problem is related to OOP (I am a novice). "self" seems to be the problem, and I kind of know what that means but not enough to be able to fix it. Below is my latest attempt. It seems like I am using the "wrong self", from elsewhere in the script. I want very much to understand object oriented programming - thanks in advance to anyone kind enough to help!
Some of the code/errors are:
code:
tabbar = QTabBar()
tab1 = QTabWidget()
tabbar.addTab(tab1, 'tab1')
error:
TypeError: arguments did not match any overloaded call:
addTab(self, str): argument 1 has unexpected type 'QTabWidget'
addTab(self, QIcon, str): argument 1 has unexpected type 'QTabWidget'
And here's the code:
class App(QMainWindow):
def launch(self, filepath):
subprocess.run(filepath)
def newLauncher(self, matrix):
pass # cut for brevity
def __init__(self):
super(App, self).__init__()
tabbar = QTabBar()
tab1 = QTabWidget()
index = tabbar.addTab(tab1, 'tab1')
self.initUI()
def initUI(self):
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
It is good that you want to learn about OOP, but that is not the main problem in this case, but it seems that you do not read the documentation. If we check that it is a QTabBar it would see that it refers to the top part of the QTabWidget, it is that part with buttons.
You do not have to use QTabBar but QTabWidget, QTabWidget has the addTab method that requires as a first parameter the widget that will be displayed on a page, and as a second parameter a title that will appear on the buttons.
Another mistake that I see in your code is that you create the widget but not setting it as part of another widget are just local variables that we know are deleted when the function is finished.
Since you are using QMainWindow you must set QTabWidget as part of a central widget, for this we can use the layouts.
import sys
from PyQt5.QtWidgets import *
class App(QMainWindow):
def __init__(self):
super(App, self).__init__()
centralWidget = QWidget()
lay = QVBoxLayout(centralWidget)
tab = QTabWidget()
lay.addWidget(tab)
for i in range(5):
page = QWidget()
tab.addTab(page, 'tab{}'.format(i))
self.setCentralWidget(centralWidget)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())

Setting up QStackedWidget properly, signal cannot see function/slot

I am working on developing a GUI with PyQt5. This is my first step into OOP, and I'm trying to teach myself as I go. I'm struggling with understanding when classes inherit methods/attributes etc and what methods they have available -- I guess it is a scope-related question? I have produced a MWE to my GUI below. In total, there will be many more pages and signals/slots.
What I want:
The stack should initialize with the "MainMenu" widget/object showing (left image below). Clicking on "Next Page" button should switch the stack order to put the "OtherPage" widget/object on top (right image below). I am creating each page as a class, thinking this would be a good way to organize my project. Is this good or bad practice?
What happens now:
The GUI works (initializes) if the line nextPg.clicked.connect(self.drawOtherPage()) is commented out, but of course then clicking on the button does nothing. I can switch the initial stack order so that "other" widget is on top of the stack and it shows up fine, so I think that class is also working. When the above line is included in the code, the following error is thrown:
in __init__
nextPg.clicked.connect(self.drawOtherPage())
AttributeError: 'MainMenu' object has no attribute 'drawOtherPage'
What I've tried
I thought that the call to super() was supposed to allow the child class (in this case MainMenu) to inherit the methods from the parent class (RootInit). Therefore, I would think this should make the drawOtherPage method available to the button connect signal. Obviously, the error isa result of the method not being available.
What am I doing wrong? Should I be creating these "page" widgets in methods instead? Do they need to be under the RootInit class or can they live in the top level of the .py file? I'm trying to follow best practices as the project will become pretty large in the end. Fortunately, most of it should be pages with variations based on what buttons were clicked to get there -- I therefore thought classes would be helpful. Please be harsh on the code and my python/PyQt vernacular, trying to learn -- thanks!
import sys, os
from PyQt5.QtWidgets import *
from PyQt5 import QtGui, QtCore
class RootInit(QMainWindow):
# root window
def __init__(self, parent=None):
QMainWindow.__init__(self)
self.root = QWidget()
self.stack = QStackedWidget()
rootLayout = QVBoxLayout()
rootLayout.addWidget(self.stack)
self.root.setLayout(rootLayout)
self.setCentralWidget(self.root)
self.initializeGUI()
def initializeGUI(self):
self.main = MainMenu(self) # build MainMenu (class)
self.other = OtherPage(self) # build OtherPage (class)
self.stack.addWidget(self.main)
self.stack.addWidget(self.other)
def drawMain(self):
self.stack.setCurrentIndex(0)
def drawOtherPage(self):
self.stack.setCurrentIndex(1)
class MainMenu(QWidget):
# class for main menu
def __init__(self, parent=None):
QWidget.__init__(self, parent)
super().__init__()
mainLayout = QGridLayout() # layout for entire main menu
quitBtn = QPushButton("Quit")
quitBtn.clicked.connect(QtCore.QCoreApplication.instance().quit)
nextPg = QPushButton("Next page")
nextPg.clicked.connect(self.drawOtherPage())
mainLayout.addWidget(quitBtn, 0, 0)
mainLayout.addWidget(nextPg, 0, 1)
self.setLayout(mainLayout)
class OtherPage(QWidget):
# class for another menu
def __init__(self, parent=None):
QWidget.__init__(self, parent)
label = QLabel("test label")
layout = QGridLayout() #
layout.addWidget(label, 0, 0)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
root = RootInit()
root.setWindowTitle("Title")
root.show()
sys.exit(app.exec_())
Your code has the following errors:
The variable self refers to the same instance of the class, in your case self refers to an instance of MainMenu, and if we observe MainMenu it does not have any drawOtherPage() method.
Another mistake in your case is to call the parent's constructor twice:
class MainMenu(QWidget):
# class for main menu
def __init__(self, parent=None):
QWidget.__init__(self, parent)
super().__init__()
In the first constructor you are assigning a parent, and in the second, you are not. To clarify in python there are several ways to call the parent's constructor:
QWidget.__init__(self, parent)
super(MainMenu, self).__init__(parent)
super().__init__(parent)
so you should only use one of them.
Another error is that a signal is connected through the name of a function, the function must not be evaluated using parentheses
and for the last use of functions or methods that involve several objects should be done in a place where both objects can access, in your case you can take advantage of what you are going to RootInit as parent of MainMenu: self.main = MainMenu(self), and access the connection to that element through the method parent().
All of the above entails modifying the MainMenu class to the following:
class MainMenu(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
mainLayout = QGridLayout() # layout for entire main menu
quitBtn = QPushButton("Quit")
quitBtn.clicked.connect(QtCore.QCoreApplication.instance().quit)
nextPg = QPushButton("Next page")
nextPg.clicked.connect(self.parent().drawOtherPage)
mainLayout.addWidget(quitBtn, 0, 0)
mainLayout.addWidget(nextPg, 0, 1)
self.setLayout(mainLayout)

Python program works, but it shows an error. Why?

I am new in programming and I have done my research on this website and others, but I can't find anything helpful for my problem. I am writing a Python program with several PyQt windows opening when different buttons are pressed. This is my program:
import sys, os,
from PyQt4 import QtCore, QtGui, uic
Ui_IntroWindow = uic.loadUiType('introduction.ui')[0]
Ui_ElmWindow = uic.loadUiType('elm.ui')[0]
Ui_ClueWindow = uic.loadUiType('pistaelm.ui') [0]
Ui_ButtonWindow = uic.loadUiType('firtsguibutton.ui')[0]
class IntroWindow(QtGui.QMainWindow, Ui_IntroWindow):
def __init__ (self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.continuar.clicked.connect(self.continuar_clicked)
def continuar_clicked(self):
window = ElmWindow(self)
window.show()
window.exec_()
self.close()
class ElmWindow(QtGui.QMainWindow, Ui_ElmWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.bpista.clicked.connect(self.pista)
self.bcontinuar.clicked.connect(self.continuar)
def pista(self):
pistaelm = ClueWindow(self)
pistaelm.show()
pistaelm.exec_()
def continuar(self):
elemento = str(self.elemento.text())
main = ButtonWindow(self)
if elemento == 'cobalto':
main.show()
main.exec_()
self.close()
class ClueWindow(QtGui.QMainWindow, Ui_ClueWindow):
def __init__ (self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
class ButtonWindow(QtGui.QMainWindow, Ui_ButtonWindow):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
app = QtGui.QApplication(sys.argv)
myWindow = IntroWindow()
myWindow.show()
app.exec_()
I have several sub windows created, for example in:
def continuar_clicked(self):
window = ElmWindow(self)
window.show()
window.exec_()
self.close()
If i don't write
window.exec_()
the window will open, but the buttons won't work. But when i write it I get an error:
Traceback (most recent call last):
File "C:\Users\Work\Desktop\Project\project.py", line 19, in continuar_clicked
window.exec_()
AttributeError: 'ElmWindow' object has no attribute 'exec_'
How can I stop the error?
Objects derived from QMainWindow do not have a method exec_(). This is why you see the exception.
The reason this exception is modifying the behaviour of your program is because when an exception is raised, the rest of the slot currently being executed is not run. So your continuar_clicked method runs until it hits the line with the missing method, and stops.
This points to the fact that not calling self.close() in continuar_clicked keeps your GUI working. Calling self.close() is apparently breaking the program.
SO what does this mean? Well it points to a bad object hierarchy. You are spawning new windows, that are children of an existing window, and then closing the parent window. Quite possibly the parent window is being deleted, depending on whether the Qt.WA_DeleteOnClose attribute is set to true for your windows.
So I would suggest redesigning your program. Perhaps have a parent window that is always open, or write a window managing class which handles the creation/closing of all windows (eg a window object calls a method from your window managing object to close the current window and open a new window).
Ultimately how you structure your code will be up to you as it is difficult to gauge the details of your program from a minimal example

PyQt4 custom widget (uic loaded) added to layout is invisible

I've created a custom widget in pyqt4 that I've worked on and tested and now am ready to load it into my main window. Since it doesn't show up in designer, I need to manually add it to my main window manually.
My widget uses uic to load the ui file instead of converting it to a py file (it's been quicker less hassle so far) so it looks something like this:
class widgetWindow(QtGui.QWidget):
def __init__(self, parent = None):
super(widgetWindow, self).__init__(parent)
self.ui = uic.loadUi("widget.ui")
#everything else
now in my main class (example for brevity) I create the layout, add the widget to the layout and then add it to the main widget
class main(QtGui.QMainWindow):
def __init__(self, parent = None):
super(main, self).__init__(parent)
self.ui = uic.loadUi("testWindow.ui")
mainlayout = QtGui.QVBoxLayout()
window = widgetWindow(self)
mainlayout.addWidget(window)
centerWidget = QtGui.QWidget()
centerWidget.setLayout(mainlayout)
self.ui.setCentralWidget(centerWidget)
There are no errors thrown, and it will make space for the widget, but it simply won't show anything.
adding in the line window.ui.show() will just pop open a new window overtop the space that it should be occupying on the main window. What am I missing?
Doing some more research into the uic loader, there are two ways to load a ui file. The way I'm using it in the question is one way, the other way is with the uic.loadUiType(). This creates both the base class and the form class to be inherited by the class object instead of just the QtGui.QWidget class object.
widgetForm, baseClass= uic.loadUiType("addTilesWidget.ui")
class windowTest(baseClass, widgetForm):
def __init__(self, parent = None):
super(windowTest, self).__init__(parent)
self.setupUi(self)
This way, the widget can be loaded into another form as expected. As for exactly why, I haven't found that answer yet.
Some more info on the different setup types: http://bitesofcode.blogspot.com/2011/10/comparison-of-loading-techniques.html
Try to add the parent argument into the loadUi statements:
self.ui = uic.loadUi("widget.ui",parent)
self.ui = uic.loadUi("testWindow.ui",self)
And try the following line at the end of your main class.
self.setCentralWidget(centerWidget)
You need to specify that 'centerWidget' is the central widget of the main window.
i.e your code for class main should be have a line like:
self.setCentralWidget(centerWidget)
class main(QMainWindow):
def __init__(self, parent = None):
super(main, self).__init__(parent)
....
self.setCentralWidget(centerWidget)

Make qwidget in new window in PyQt4

I'm trying to make a class that extends qwidget, that pops up a new window, I must be missing something fundamental,
class NewQuery(QtGui.QWidget):
def __init__(self, parent):
QtGui.QMainWindow.__init__(self,parent)
self.setWindowTitle('Add New Query')
grid = QtGui.QGridLayout()
label = QtGui.QLabel('blah')
grid.addWidget(label,0,0)
self.setLayout(grid)
self.resize(300,200)
when a new instance of this is made in main window's class, and show() called, the content is overlaid on the main window, how can I make it display in a new window?
follow the advice that #ChristopheD gave you and try this instead
from PyQt4 import QtGui
class NewQuery(QtGui.QWidget):
def __init__(self, parent=None):
super(NewQuery, self).__init__(parent)
self.setWindowTitle('Add New Query')
grid = QtGui.QGridLayout()
label = QtGui.QLabel('blah')
grid.addWidget(label,0,0)
self.setLayout(grid)
self.resize(300,200)
app = QtGui.QApplication([])
mainform = NewQuery()
mainform.show()
newchildform = NewQuery()
newchildform.show()
app.exec_()
Your superclass initialiser is wrong, you probably meant:
class NewQuery(QtGui.QWidget):
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent)
(a reason to use super):
class NewQuery(QtGui.QWidget):
def __init__(self, parent):
super(NewQuery, self).__init__(parent)
But maybe you want inherit from QtGui.QDialog instead (that could be appropriate - hard to tell with the current context).
Also note that the indentation in your code example is wrong (a single space will work but 4 spaces or a single tab are considered nicer).

Categories