I am trying to intercept the text being typed into the Cell Editor of a wx.grid object by creating a custom cell editor and setting it as the default cell editor of my grid.
As a test I did the following:
class CellEditor(wx.grid.GridCellTextEditor):
def __init__(self):
wx.MessageBox("Init")
super(CellEditor, self).__init__()
def BeginEdit(self, *args, **kwargs):
wx.MessageBox("Begin Edit")
super(CellEditor, self).BeginEdit(*args, **kwargs)
While the "Init" message box shows up, the "begin edit" message box never appears. Even if I explicitly call the BeginEdit() method from the debugger, the message box is not displayed. This makes me think somehow the BeginEdit method is being re-written over by the original method during initialization. Come someone help me understand what's happening?
I do not completely understand the magic behind the custom cell editors, but this example may be a good starting point. It seems that you have to override more methods in order to make it work.
Also I would be careful with overriding methods, your method signature BeginEdit(self, *args, **kwargs) does not match the overriden one which is BeginEdit(self, row, col, grid). I did some quick tests to see if you can override methods like this and it seems so, but it is confusing and may lead to mistakes.
Related
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.
Can someone help me understand how MRO works in python?
Suppose I have four classes - Character, Thief, Agile, Sneaky. Character is the super class to Thief, Agile and Sneaky are siblings. Please see my code and question below
class Character:
def __init__(self, name="", **kwargs):
if not name:
raise ValueError("'name' is required")
self.name = name
for key, value in kwargs.items():
setattr(self, key, value)
class Agile:
agile = True
def __init__(self, agile=True, *args, **kwargs):
super().__init__(*args, **kwargs)
self.agile = agile
class Sneaky:
sneaky = True
def __init__(self, sneaky=True, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sneaky = sneaky
class Thief(Agile, Sneaky, Character):
def pickpocket(self):
return self.sneaky and bool(random.randint(0, 1))
parker = Thief(name="Parker", sneaky=False)
So, here is what I think is going on, please let me know if I'm understanding it correctly.
Since Agile is first on the list, all arguments are first sent to Agile where the arguments will be cross-referenced with the Agile parameters. If there is a match the value will be assigned, then everything that doesn't have a matching keyword will be packed up in *kwargs and sent to the Sneaky class (via super), where the same thing will happen - all arguments get unpacked, cross-referenced with the Sneaky parameters (this is when sneaky = False is set), then packed up in kwargs and sent to Character. Then everything within the Character inint method will run and all values will be set (like the name = "Parker").
HOW I THINK MRO WORKS ON THE WAY BACK
Now that everything made it to the Character class and everything in the Character init method has run, now it has to go back to the Agile and Sneaky classes and finishing running everything in their init methods(or everything under their super). So, it will first go back to the Sneaky class and finish it's init method, then go back to the Agile class and finish the rest of its init method (respectively).
Do I have it confused anywhere? Phew. I'm sorry, I know this is a lot, but I'm really stuck here and I'm trying to get a clear understanding of how MRO works.
Thank you, everyone.
Your code as posted doesn't even compile, much less run. But, guessing at how it's supposed to work…
Yes, you've got things basically right.
But you should be able to verify this yourself, in two ways. And knowing how to verify it may be even more important than knowing the answer.
First, just print out Thief.mro(). It should look something like this:
[Thief, Agile, Sneaky, Character, object]
And then you can see which classes provide an __init__ method, and therefore how they'll be chained up if everyone just calls super:
>>> [cls for cls in Thief.mro() if '__init__' in cls.__dict__]
[Agile, Sneaky, Character, object]
And, just to make sure Agile really does get called first:
>>> Thief.__init__
<function Agile.__init__>
Second, you can run your code in the debugger and step through the calls.
Or you can just add print statements at the top and bottom of each one, like this:
def __init__(self, agile=True, *args, **kwargs):
print(f'>Agile.__init__(agile={agile}, args={args}, kwargs={kwargs})')
super().__init__(*args, **kwargs)
self.agile = agile
print(f'<Agile.__init__: agile={agile}')
(You could even write a decorator that does this automatically, with a bit of inspect magic.)
If you do that, it'll print out something like:
> Agile.__init__(agile=True, args=(), kwargs={'name': 'Parker', 'sneaky':False})
> Sneaky.__init__(sneaky=False, args=(), kwargs={'name': 'Parker'})
> Character.__init__(name='Parker', args=(), kwargs={})
< Character.__init__: name: 'Parker'
< Sneaky.__init__: sneaky: False
< Agile.__init__: agile: True
So, you're right about the order things get called via super, and the order the stack gets popped on the way back is obviously the exact opposite.
But, meanwhile, you've got one detail wrong:
sent to the Sneaky class (via super), where the same thing will happen - all arguments get unpacked, cross-referenced with the Sneaky parameters (this is when sneaky = False is set)
This is where the parameter/local variable sneaky gets set, but self.sneaky doesn't get set until after the super returns. Until then (including during Character.__init__, and similarly for any other mixins that you choose to throw in after Sneaky), there is no sneaky in self.__dict__, so if anyone were to try to look up self.sneaky, they'd only be able to find the class attribute—which has the wrong value.
Which raises another point: What are those class attributes for? If you wanted them to provide default values, you've already got default values on the initializer parameters for that, so they're useless.
If you wanted them to provide values during initialization, then they're potentially wrong, so they're worse than useless. If you need to have a self.sneaky before calling Character.__init__, the way to do that is simple: just move self.sneaky = sneaky up before the super() call.
In fact, that's one of the strengths of Python's "explicit super" model. In some languages, like C++, constructors are always called automatically, whether from inside out or outside in. Python forcing you to do it explicitly is less convenient, and harder to get wrong—but it means you can choose to do your setup either before or after the base class gets its chance (or, of course, a little of each), which is sometimes useful.
I am writing a GUI in wxPython, and am creating a custom control for displaying a terminal window, as I have not been able to find one currently in existence.
My control TerminalCtrl extends upon wx.Control, and my init definition starts as follows:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
I would like to enforce the following style:
style=wx.BORDER_NONE
That is, no borders will ever be allowed on this window. However, I would still like to allow other styles to be applied, at programmers discretion.
For reference, the __init__ function for wx.Control is defined as follows
__init__ (self, parent, id=ID_ANY, pos=DefaultPosition, size=DefaultSize, style=0, validator=DefaultValidator, name=ControlNameStr)
What I would like to achieve is that I may filter the style parameter to enforce the wx.BORDER_NONE style. It is my understanding that this could be in either *args or **kwargs, depending on whether the parameters are passed by position or by specifically referencing the parameter name such as (style=wx.BORDER_NONE).
Is there a standard/recommended/pythonic way that I may enforce such a filter upon a parameter before passing it on to wx.Control.__init__ and if so how may I achieve that?
The cleanest way is probably to just copy the base class's signature:
def __init__(self, parent, id=ID_ANY, pos=DefaultPosition,
size=DefaultSize, style=0, validator=DefaultValidator,
name=ControlNameStr):
style |= wx.BORDER_NONE
super().__init__(parent, id, pos, size, style, validator, name)
This can get a bit ugly if you're doing this for a whole bunch of classes whose construction signatures all have a whole bunch of positional-or-keyword parameters. Or if you're doing it for an API that changes regularly.
For those cases, you can always do it dynamically, with inspect:
_wxControlSig = inspect.signature(wx.Control)
class TerminalCtrl(wx.Control)
def __init__(self, *args, **kwargs):
bound = _wxControlSig.bind(*args, **kwargs)
bound.apply_defaults()
bound.arguments['style'] |= wx.BORDER_NONE
super().__init__(*bound.args, **bound.kwargs)
If you were doing dozens of these, you'd probably want to write a decorator to help out. And you might also want to apply functools.wraps or do the equivalent manually to make your signature introspectable. (And if you weren't doing dozens of these, you'd probably want to just be explicit, as in the example at the top of the answer.)
If you have something which is just a bit too repetitive and annoying to do explicitly, but not worth going crazy with the introspection, the only thing in between is something decidedly hacky, like this:
def __init__(self, *args, **kwargs):
if len(args) > 3:
args = list(args)
args[3] |= WX_BORDER_NONE
elif 'style' in kwargs:
kwargs['style'] |= wx.BORDER_NONE
else:
kwargs['style'] = wx.BORDER_NONE
super().__init__(*args, **kwargs)
For Python 2.x (or 3.0-3.2), where you don't have signature, only getargspec and friends, this might be tempting. But for 3.3+, the only reason to avoid signature would optimizing out a few nanoseconds. and when the function in question is the constructor for a widget that involves talking to the system window manager, that would be pretty silly to worry about.
I am working on a simple QWizard that displays some radio buttons on its pages. The buttons on a given page are all part of the same QButtonGroup. The page is registered as a custom field in itself, based on the selection in the button group:
class Page1(QWizardPage):
selectionChanged = pyqtSignal('QString')
def __init__(self, name):
self.group = QButtonGroup()
self.group.addButton(QRadioButton("a"))
self.group.addButton(QRadioButton("b"))
self.group.addButton(QRadioButton("c"))
self.registerField(name, self, 'selection', self.selectionChanged)
#pyqtProperty('QString')
def selection(self):
checkedButton = self.group.checkedButton()
return checkedButton.text() if checkedButton else None
def nextId(self): return -1
I end up registering self as the widget containing the field property simply because QButtonGroup is not a QWidget. All of the other pages look pretty much exactly like this (I am actually using base class to do all the common work, and this is just a minimal example).
I would like to be able to get a list of all the registered fields in the QWizard. I have not found any methods provided by Qt to allow me to do this so I made a workaround by overriding the behavior of each page's registerField method as well as the wizard's addPage:
def registerField(self, name, *args, **kwargs):
self.field_names.add(name)
if self.wizard() is not None:
self.wizard().field_names.add(name)
super().registerField(name, *args, **kwargs)
def addPage(self, page, *args, **kwargs):
self.field_names.union(page.field_names)
return super().addPage(page, *args, **kwargs)
I can then use the field_set attribute of the parent QWizard combined with QWizard.field to access all the values. This seems a bit redundant and therefore unnecessary. Is there a method in Qt to access the complete collection of fields? The relevant section in the documentation does not mention anything, but there are a lot of other details it omits, so that's not very telling.
My assumption is that the functionality, if it exists, would be the same for PyQt4 as for PyQt5. If it is not, I would prefer an answer for PyQt5 since that is what I am using at the moment.
You said that if the answer is negative it would have to be "pretty convincing." You admit that the documentation contains no mention of the function you want, and I will point out that no such function appears in the list of public functions for QWizard. Therefore the desired function, if it exists at all, is undocumented. To me, that consideration alone would be a "pretty convincing" reason not to use it. The next release of Qt might not have that function, or it might not work the same way.
Meanwhile you have an acceptable solution with eight lines of straightforward python code. Given the choice between that and calling an undocumented function (if you can find it), the python solution is vastly superior in all practical respects.
There is a potential problem with your Python code, however. You override the function QWizard.addPage, but there is another function QWizard.removePage that should probably be overridden as well. An alternative approach, which I would prefer, is not to store the field_names in QWizard at all but only in the individual pages. Add a method to QWizard to dynamically build a set of all the current field_names:
def all_field_names(self):
return {s for page_id in self.pageIds() for s in self.page(page_id).field_names}
[I didn't have a good way of testing this function, but I think you get the idea.] Now you remove the overridden method QWizard.addPage, remove the variable field_names from QWizard, and remove the middle two lines of register_field. Now you have only five lines of Python code, which will work regardless of how pages are added or removed. And you no longer store the field names in two places.
For what it's worth, whenever I'm confronted with a choice between using Qt's functionality or basic Python functionality, I always lean toward Python. I use threads instead of QThreads, threading locks and timers instead of Qt equivalents, Python method objects and callbacks instead of custom Slots and Signals. Qt was written for C++ programmers and that often means that it's not as "pythonic" as I would like.
I want to define several plugins.
They all inherit from the superclass Plugin.
Each plugin consists on a wx.Panel that have a more specific method called "draw".
How can I define a class as a Panel and afterwards call that class in my frame?
I've tried like this:
class Panel(wx.Panel):
def __init__(self, parent):
wx.Panel(self, parent)
but it gives me this error:
in __init__
_windows_.Panel_swiginit(self,_windows_.new_Panel(*args, **kwargs))
TypeError: in method 'new_Panel', expected argument 1 of type 'wxWindow *'
Thanks in advance!
class MyPanel(wx.Panel):
def __init__(self, *args):
wx.Panel.__init__(self, *args)
def draw(self):
# Your code here
There is a class wx.PyPanel that is a version of Panel intended to be subclassed from Python and allows you to override C++ virtual methods.
There are PyXxxx versions of a number of other wx classes as well.
How can I define a class as a Panel and afterwards call that class in my frame?
What you tried is close, but you're not properly calling the super class __init__. When subclassing wxPython classes, however, it's generally best to use the following pattern so that you don't have to worry about which specific arguments you are passing to it. (This wouldn't have solved your problem, which was outside of the code in question, but it maybe makes it clearer what's happening.)
class Panel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
# ... code specific to your subclass goes here
This ensures that anything passed in is handed on to the super class method with no additions or removals. That means the signature for your subclass exactly matches the super class signature, which is also what someone else using your subclass would probably expect.
If, however, you are not actually doing anything in your own __init__() method other than calling the super class __init__(), you don't need to provide the method at all!
As for your original issue:
but it gives me this error: in __init__ windows.Panel_swiginit(self,windows.new_Panel(*args, **kwargs)) TypeError: in method 'new_Panel', expected argument 1 of type 'wxWindow *'
(Edited) You were actually instantiating a wx.Panel() inside the __init__ rather than calling the super class __init__, as Javier (and Bryan Oakley, correcting me) pointed out. (Javier's change of the "parent" arg to "*args" confused me... sorry to confuse you.)