I have a program that I'm trying to develop but I guess I don't know the proper way to make subframes. I've looked at several examples and can't seem to get it to destroy the frame when it's closed. I get the following error readout (using pycharm):
Exception wx._core.PyDeadObjectError: PyDeadObjectError('The C++ part of the
Choice object has been deleted, attribute access no longer allowed.',) in
<bound method pvFileINdialog.__del__ of <fileIO.pvFileIOGUIFileIOdialog.pvFileINdialog;
proxy of <Swig Object of type 'wxDialog *' at 0x340cc70> >> ignored
Although the interpreter takes care of the problem, I would like to learn the proper way to take care of it.
If I move the destroy and close commands around no combination changes the result.
The code of interest is:
#in MainFrame.py:
def fileimport(self, event):
importprompt = pvFileIOGUIFileIOdialog.pvFileINdialog(self)
importprompt.ShowModal()
importprompt.Destroy()
#referring to pvFileIOGUIFileIOdialog.pvFileINdialog :
class pvFileINdialog(pvFileIOGUI.FileINdialog):
def __init__(self, child):
pvFileIOGUI.FileINdialog.__init__(self, child)
#which refers to pvFileIOGUI.FileINdialog :
class FileINdialog(wx.Dialog):
def __init__(self, child):
wx.Dialog.__init__(self, child, id=wx.ID_ANY, title=u"Select Import Method...", pos=wx.DefaultPosition,
size=wx.Size(800, 600), style=wx.DEFAULT_DIALOG_STYLE)
#and is closed by this routine in pvFileIOGUIFileIOdialog.py:
def cancelclk(self, event):
self.Close()
return 0
It's difficult to understand what goes on because your error message refers to a Choice, but your code doesn't show any choices at all, so something is probably missing, i.e. the problem is probably in the part which you don't show. But in any case, calling Destroy() is not necessary and you shouldn't do it unless you have some real reason (do you?). So I'd start by removing all calls to Destroy() from your code.
Related
I try to create a proper container Class for Gtk in Python (MyBin in the code below). There seems to be no authoritative documentation on how to do that, only bits and pieces, which I glued together with trial and error. – There are many open questions, but let me focus on size_allocate() here:
What is the the original size_allocate() function doing? And what is my overlaid version in my container class supposed to do?
I already do 95% know that I have to replace the original method with my own by prepending “do_” to form the do_size_allocate() method (by trial and error – see code below; I could not find anything talking about all those “do_*” functions).
I found so far, that calling self.set_allocation(allocation) seems to be a good idea as is self.get_child().size_allocate(...) to give the child some working space (see code). – Any other obligations I have to fulfill? What is the original function doing additionally?
Regarding the allocation I pass down to the child: I need to adapt it (not in the code below yet). I successfully(?) used a fresh Gdk.Rectangle() on which I set x, y, width and height, but I somehow feel that it’s wrong and something with (do_)adjust_size_allocation() should be used instead. But how?
Lets look into the documentation of do_size_allocate():
This function is only used by Gtk.Container subclasses, to assign a
size and position to their child widgets. [“Only used”? Only called from? Only implemented at? Only overwritten by? – Well, I’m doing it already, but the doc wasn’t helpful in finding out how.]
In this function, the allocation may be adjusted. [How?] It will be forced to
a 1x1 minimum size [Can confirm only that get_allocation() will return a 1x1 if I don’t set anything.],and the adjust_size_allocation virtual method on
the child will be used to adjust the allocation. [What? For my own allocation? Or for the one I set on the child via ...get_child().size_allocate()? Who calls that adjust method? In which conditions is it not called before size_allocate()?] Standard adjustments
include removing the widget’s margins, and applying the widget’s
Gtk.Widget :halign and Gtk.Widget :valign properties.
#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class MyBin(Gtk.Bin):
def __init__(self):
super().__init__()
def do_size_allocate(self, allocation):
print('PARENT METHOD do_size_allocate', allocation.width)
# if not called, `get_allocation()` in `do_draw()` will return a 1x1 rectangle
self.set_allocation(allocation)
# strangely required so that `do_draw()` will be called even on this class!
self.get_child().size_allocate(allocation)
def do_draw(self, c):
allocation = self.get_allocation()
print('PARENT do_draw()', allocation.width)
self.propagate_draw(self.get_child(), c)
class MyChild(Gtk.Button):
def __init__(self):
super().__init__()
self.connect('size-allocate', self.size_allocate_handler)
self.connect('draw', self.draw_handler)
def size_allocate_handler(self, self2, allocation):
print('CHILD signal size-allocate', allocation.width)
def draw_handler(self, self2, c):
allocation = self.get_allocation()
print('CHILD signal draw', allocation.width)
class MainWindow(Gtk.Window):
def __init__(self):
super().__init__(title='d2see test pattern')
the_child = MyChild()
my_container = MyBin()
my_container.add(the_child)
self.add(my_container)
self.show_all()
if __name__ == '__main__':
MainWindow()
Gtk.main()
Please note: this is on W10. This may well be significant.
Python: 3.9.4
pytest: 6.2.5
pytest-qt: 4.0.2
I've been using pytest-qt for about a week now to start developing a PyQt5 app. There have been a few baffling problems but none as baffling as this one.
My app code:
class LogTableView(QtWidgets.QTableView):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
def resizeEvent(self, resize_event):
super().resizeEvent(resize_event)
# self.resizeRowsToContents()
The last line above needs to be added. Using a TDD approach I therefore start writing the test:
def test_resize_event_should_result_in_resize_rows(request, qtbot):
t_logger.info(f'\n>>>>>> test name: {request.node.originalname}')
table_view = logger_table.LogTableView(QtWidgets.QSplitter())
# with unittest.mock.patch.object(table_view, 'resizeRowsToContents') as mock_resize:
# with unittest.mock.patch('logger_table.LogTableView.resizeRowsToContents') as mock_resize:
table_view.resizeEvent(QtGui.QResizeEvent(QtCore.QSize(10, 10), QtCore.QSize(20, 20)))
NB the commented-out lines show the kind of things I have been trying. But you can see that even just creating an object of the type LogTableView, and then calling the method, with no mocks around at all, causes the error.
On running this:
>pytest -s -v -k test_logger_table.py
I get this:
...
self = <logger_table.LogTableView object at 0x000002B672697670>
resize_event = <PyQt5.QtGui.QResizeEvent object at 0x000002B672743940>
def resizeEvent(self, resize_event):
> super().resizeEvent(resize_event)
E RuntimeError: wrapped C/C++ object of type LogTableView has been deleted
...
Has anyone got any idea what this is about?
PS FWIW, out of despair, I even tried this:
super(LogTableView, self).resizeEvent(resize_event)
... same error.
Creating a parent in the child constructor is not a very good idea.
Remember that PyQt is a binding, every reference used in Python is a wrapper for the Qt object: if the object is deleted on the C++ side, the python reference still exists, but any attempt to use its functions results in the RuntimeError above.
In your case, there's no persistent reference for the parent on the python side, only the pointer on the Qt side, which is not enough to avoid garbage collection: only parent objects take ownership in Qt (that's why you can avoid persistent references for a child Qt object), it's not the other way around. The problem is that the child believes that it has a parent (as it had one when it was created), but in the meantime that parent has been deleted, as soon as the child constructor is returned.
Just create a local variable for the parent.
def test_resize_event_should_result_in_resize_rows(request, qtbot):
t_logger.info(f'\n>>>>>> test name: {request.node.originalname}')
parent = QtWidgets.QSplitter()
table_view = logger_table.LogTableView(parent)
# ...
Besides the problem of the subject, technically speaking there's no point in using a very specific widget such as QSplitter as a parent (especially considering that in order to be properly used, the widget should be added with addWidget(), as the parenthood alone is pointless for a splitter); if you need a parent, just use a basic QWidget.
Whilst working on a tkinter application (Tcl/Tk 8.6 and Python 3.9.2) I recently encountered an error that I was able to resolve, but I think the existence of the error highlights some gaps in my knowledge and potential weaknesses in my approach.
A reproducible example of the error is below - this code will not work and returns the error AttributeError: 'parent_class' object has no attribute 'first'.
from tkinter import *
from tkinter import ttk
class child_one(ttk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.x = 10
def print_x(self):
print(self.x)
class child_two(ttk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.b = ttk.Button(self, text='Button 1',
command=parent.first.print_x).grid()
class parent_class(ttk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.grid()
self.second = child_two(self)
self.first = child_one(self)
self.first.grid()
self.second.grid()
if __name__ == '__main__':
root = Tk()
w = parent_class(root)
root.mainloop()
However the code will work if I reverse the order in which the instances of child_one and child_two are created i.e. replacing
self.second = child_two(self)
self.first = child_one(self)
with
self.first = child_one(self)
self.second = child_two(self)
in the definition of parent_class.
I'd really appreciate any explanations or link to resources to help me understand the program flow which causes this to happen - it appears to me that when I create w and get to the line self.second = child_two(self) Python is just looking at the part of the instance of parent_class which has already been created, and not the whole definition of the class.
Would this happen if this was not the first instance of parent_class to be created? Is it specific to tkinter? (I was only able to create a simple reproducible example with tkinter widgets, not with classes more generally.)
I suppose another solution would be to make print_x a (static?) method of parent_class? I also assume there's not enough detail here to definitively state if that (or alternative structures) would be preferable to facilitate interface between the components of my application, but would be interested to hear any suggestions for good practices in this space.
There's really no mystery here. If you do self.first = child_one(self) first, it defines self.first. When you then call child_two(self), it is able to access parent.first since it was previously created.
However, if you call child_two(self) first, at that point in time parent.first doesn't exist. Thus, when you do command=parent.first.print_x, parent.first doesn't exist.
it appears to me that when I create w and get to the line self.second = child_two(self) Python is just looking at the part of the instance of parent_class which has already been created
That is correct. You can't reference things that haven't been created yet.
Would this happen if this was not the first instance of parent_class to be created? Is it specific to tkinter?
I'm not quite sure what you're asking in the first part of that question. It will always happen if you try to reference any object attribute before that attribute has been created. And no, this isn't specific to tkinter. It's a fundamental aspect of the way that python works.
This is a good example of why it's generally best to create proper functions rather than using lambda. lambda is good when you need to pass arguments, but you don't need to do that in this case. Even then, a proper function is better than directly referencing some other object at the time the button is defined. An arguably better way would be to use a function so that self.parent.first doesn't need to be resolved until you actually click the button.
For example:
class child_two(ttk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.parent = parent
self.b = ttk.Button(self, text='Button 1', command=self.print_x)
self.b.grid()
def print_x(self):
self.parent.first.print_x()
When you say "Python is just looking at the part of the instance of parent_class which has already been created, and not the whole definition of the class", you seem to be expecting python to have built a static description of your class before the program starts running.
Python does not work that way, it's a dynamic language. As Bryan just said, the first variable is created only when you assign to it for the first.
I have a problem using Qt signal/slot mechanism in PySide when I want to send None.
Suppose I have a class named Calculator (a subclass of QObject) that can perform certain calculations the result of which can be anything, i.e. any type of Python object or even None. I need the instances of Calculator class to be able to signal the result to some consumer objects (subclasses of QObject).
What I had:
class Calculator(QObject):
finished = PySide.QtCore.Signal(object) # object means it can transmit anything
def calc(self):
... # calculation of the result
self.finished.emit(result)
class Consumer(QObject):
def __init__(self, calculator):
...
calculator.finished.connect(self.consume)
def consume(self, result): # do something with the result
...
This works fine except the situation when result is None. Then the program crashes when emitting the signal. It seems as if None is not a Python object (which may be true, I am not that proficient in the language standards).
Now I have a workaround, using two signals:
finished = PySide.QtCore.Signal((),(object,))
and:
def calc(self):
... # calculation of the result
if result is None:
self.finished.emit()
else:
self.finished[object].emit(result)
This solution works but it is complicated because it requires the consumer objects to link the signal twice. Either to two slots or to one slot with one default argument equal to None.
class Consumer(QObject):
def __init__(self, calculator):
...
calculator.finished.connect(self.consume)
calculator.finished[object].connect(self.consume)
def consume(self, result=None): # do something with the result
...
This is complicated and prone to errors when creating many different consumer classes.
I think this problem is the same with PyQt, with the difference that alternative signals are defined using lists [], [object] rather than tuples (),(object,).
My question is: is there any simple solution to this problem? Ideally with just one signal?
As an aside, this bug still exists in PySide where it segfaults on the transmission of a None signal as of version 1.2.1 (has existed since 1.1.0). There is a bug report at:
https://bugreports.qt-project.org/browse/PYSIDE-17
Obviously V.K.'s solution of encapsulating the None within a separate class still works, as does choosing another type to transmit instead (I switched my None to float('nan')). I just wanted to update this as I ran into the same issue and found this, and later the bug report explaining the segfault.
Just after posting my problem I found an answer. So I appologize for answering my own question - I know it is not how it should be on StackOverflow. I created a class named ResultHolder which encapsulates the real result (result is a member variable of this class). Then I am transmitting instances of this ResultHolder class. The rest of the solution is then just straightforward.
Update 2013-02-08
I have an idea now why I haven't been able to reproduce this problem in a small piece of test code. In a small program, Python's garbage collector isn't very active. I believe the problem is that Python is collecting some objects that are only referenced in GObject. I think it's a regression involving this bug, or a new similar bug.
I figured this out because I encountered the same problem again, but with my own class (which has references only from GObject objects) -- this time the entire dict is getting wiped out on the object. Uf I use the code here to monitor one of the attributes that dissappears, it doesn't disappear! It seems the extra reference keeps the attributes around. That smells like a garbage collector problem. I confirmed this by having the object add itself to a global list during initialization... that also fixes the problem as it occurs now.
Original Problem
I am experiencing some bizarre behavior with a PyGTK GUI. I have an object which is consistently losing a large number of attributes. I am trying to determine if this is a bug in my code, the Python interpreter, or PyGTK.
I make no calls to delattr(). I have tried detecting if anything is calling the __delattr__() method of my object by overriding __delattr__() with code that always raises an exception. I am able to reproduce the event which causes the object to lose its attributes but the exception is never raised. I'm not sure of another way to find out what function calls (if any) are causing the object to lose attributes.
The object in question is working perfectly at all times up until it suddenly loses attributes I'm trying to access.
The attribute loss occurs consistently after performing some actions in the GUI that have nothing to do with the object that is losing attributes. I discovered it by accident; there may be other actions that cause the object to lose its attributes.
I have added print id(self) to the method which access the disappearing attribute. The id that is printed is the same before and after the attribute disappears.
Any suggestions on how to track down the source of this problem?
Reference code below: (Note: I will update this code if/when I come up with a simplified test case that reproduces the problem. Right now the total code required to reproduce the bug is too big to post here.)
Here is the class for my object which loses its attributes. This is obviously a minimized version of the real functional code, but I am using this for debugging and the problem still occurs.
It is a subclass of my custom MenuBar class.
Note that the on_file_import__activate() method is connected to the signal for the menu item by one of the parent classes.
class FluidClassManagerWindowMenu(MenuBar):
menu_items = [("File",("Import",))]
def __init__(self, parent):
# XXX: different name than self.parent to see if it stops disappearing
self._xxx_my_parent = parent
MenuBar.__init__(self, parent)
def __delattr__(self,attr):
# XXX: trying to find the cause for lost attributes
traceback.print_stack()
def on_file_import__activate(self, widget=None, data=None):
# XXX: this is the same before and after the attributes disappear
print id(self)
# XXX: print the list of attributes to see what disappears
print dir(self)
# XXX: this works until the _xxx_my_parent attribute disappears
print self._xxx_my_parent
If you're curious, here is the complete source for my MenuBar class. It is a pygtkhelpers SlaveView, which inherits from GObject. The pygtkhelpers delegate does the automagic signal connection to the on_file_import__activate method above.
class MenuBar(pygtkhelpers.delegates.SlaveView):
def __init__(self, parent):
SlaveView.__init__(self)
self.parent = parent
def create_ui(self):
menu_bar = gtk.MenuBar()
menu_bar.set_pack_direction(gtk.PACK_DIRECTION_LTR)
for menu_name, items in self.menu_items:
menu = gtk.Menu()
submenu = gtk.MenuItem(menu_name)
submenu.set_submenu(menu)
for item_name in items:
if not item_name:
menu.append(gtk.MenuItem())
continue
menuitem = gtk.MenuItem(item_name)
fixed_item_name = item_name.lower().replace(' ','_')
fixed_menu_name = menu_name.lower().replace(' ','_')
attr_name = '%s_%s' % (fixed_menu_name,fixed_item_name)
# set an attribute like self.edit_vial_layout
# so pygtkhelpers can find the widget to connect the signal from
setattr(self,attr_name,menuitem)
menu.append(menuitem)
menu_bar.append(submenu)
self.vbox = gtk.VBox()
self.vbox.pack_start(menu_bar)
self.vbox.show_all()
self.widget.add(self.vbox)
List of attributes which disappear from the object:
'_model', '_props', '_toplevel', '_xxx_my_parent', 'file_import', 'parent', 'slaves', 'testtesttest', 'vbox', 'widget'
The attribute parent is what originally was disappearing; I tried assigning its value to _xxx_my_parent in
ManagerWindowMenu.__init__() but it disappears as well. I also added a new attribute in MenuBar.__init__ that I never access, called testtesttest, and it disappears too.
Keep in mind that objects in PyGTK often inherit from GObject. There is likely activity occuring within the GObject framework that is causing you to lose the attributes.
I had a very similar problem. I had a class (SearchWorker) that built a widget for adding to the GUI at runtime. In that widget, there was a button whose "clicked" signal was connected to one of the SearchWorker functions. Whenever the "clicked" signal was fired, many of the attributes of the SearchWorker self object were gone.
I was creating the SearchWorker object in another handler of a different class like this:
worker = SearchWorker()
I presume that once that handler exited something odd happened to the object behind the worker reference. Changing the creation of SearchWorker to:
self.worker = SearchWorker()
solved my problem.
How strange. BTW, using delattr and __delattr__ call is not very common, so I suspect if you are not dealing with two different objects by themselves, getting one while expecting another. Also it may not be a problem with the interpreter, it would crash at much lower level if there was a problem.