PyQT events between multiple objects - python

I am creating a GUI program in Python/PyQT and would like to know how I can connect an event which happens in a child object to the parent?
For example, if someone clicks a 'Submit' button, how would i trigger something to happen in the parent object (lets say update a QLabel on the parent)
Any help would be greatly appreciated
L

It is done like in C++ Qt by connecting signals to slots, you will find all the information on this page (and here for the old way).

You must connect these methods every time you set new parent (and remove old connections!!)
http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qobject.html#connect
(If widget parent change and You still wont that new parent to recive signals)
But if your layout is static just give good names for your widgets. Then connect each signal to callable (any python function) and that function will change QLable.
In this case relation parent children change nothing since you refer to widgets by names not relations.

Related

How to use "on edit" events/signals with e.g. QLineEdit in pyside6 (Qt6)

Sorry for this post but I still don't get it after my research on the internet.
I am using Python PySide6 since I need to due to some display scaling issues I have with PyQt6 and other older versions.
I already have implemented the basics of a large program with dynamic .ui loading and now I want to implement the event actions for all my gui elements. Some I already managed to use by overloading the eventFilter method of the gui objects by using installEventFilter(self) function. That works well for hovering and other things and the used events are always of the type QtCore.QEvent. .... The problem is that I am missing some events here.
First an easy example and maybe the potential solution explains it for all other cases in my code:
How can I implement a function for the "on change" event of an QLineEdit object? I just want to get notified when the value of the control has been changed.
On the internet I find documentation for signals that should be connected to a slot that I can implement myself.
For example:
self.any_line_edit.textChanged.connect(self.any_slot)
The strange thing is, my QLineEdit object seems to have no reference for the "textChanged" signal. At least PyCharm is not able to resolve the name and I see the other methods of the QLineEdit class so the type of the object seems to be fine.
On other websites I find a similar example for QPushButton:
self.button.clicked.connect(self.the_button_was_clicked)
Same problem here. My PySide6 QPushButton seem to have no signal called "clicked".
Then I thought, no problem. Maybe I just continue using the eventFilter overload function and just install and replace the eventFilter for every object and manually filter the needed event myself.
But it seems that a simple QLineEdit throws no event that equals an "on edit" functionality.
I just see events like: QEvent::Paint, QEvent::DynamicPropertyChange, QEvent::WindowDeactivate, QEvent::Leave and many others but nothing like "on edit" or "edit end" or "on changed" or something.
So it seems that I don't see a respective event in the eventFilter function and due to any circumstance I also don't see the signals used to connect the Object to Slots.
I am sure I am missing something very basic but currently I am really confused.
I am using Python 3.9, PySide6 and PyCharm 2022.1 .
I just want to get notified if these input controls are getting changed in their values. I even have more complex signal-slot constructs and eventFilter reimplementations in my code and now I fail on such a basic thing. To my excuse: Normally I am implementing in Qt5 with C++ so I am a noob regarding Python and PySide6. xD
There is no such "on edit" event. The reason is that events are not meant for that. As the documentation explains:
In Qt, events are objects, derived from the abstract QEvent class, that represent things that have happened either within an application or as a result of outside activity that the application needs to know about. Events can be received and handled by any instance of a QObject subclass, but they are especially relevant to widgets.
So, events are "generic", not specialized for any widget or object type.
An "on edit" event would make sense only for editable widgets (which are just a few: QLineEdit, QPlainTextEdit, QTextEdit and their derived classes), and such an event would be also too generic and abstract to justify its existence.
Most importantly, events are normally created by Qt and then "sent" to the objects that could and possibly should handle them; again, from the above docs:
When an event occurs, Qt creates an event object to represent it by constructing an instance of the appropriate QEvent subclass, and delivers it to a particular instance of QObject (or one of its subclasses) by calling its event() function.
Note: some events are "synthetic", generated internally by Qt when required, sometimes even by widgets: in some cases, it's to work around some OS specific issues in complex focus situations, others due to the architecture of a particular framework (such as the Graphics View, which uses an internal "graphics event" system for items).
Qt cannot know (nor should) if the target object(s) is "editable". That's also because events are normally propagated from the target object up to the object tree, going up through all its parents, until the event is actually handled or (at least) accepted. That is of utmost importance for the event system: especially with input events, those events are intended for generic use by any object in the widget structure, so a widget (instance) specific "on edit" event wouldn't make any sense, mostly because it would be an event that would be only used by the widget itself.
In fact, some similar events do exist (such as QInputEvent, or the more specific QKeyEvent), but, as the name suggests, they are input events, they do not tell with certainty that the event actually results in an edit.
When the user presses a key, Qt creates a QKeyEvent and sends it to the currently focused widget. If that widget accepts that event, that event is (normally) not processed anymore by anything else. If the widget does not accept it, Qt will then propagate the event to its parent, which will decide if accept it in turn or not, and so on up to the top level window.
Note: events can be handled (or not) and accepted (or ignored). This means that a widget could handle (do "something" with that event) and still ignore it. A typical example is the Tab key: a QLineEdit would handle it in the sense that it will check that event, and then ignore it; then that event will be managed by its parent which possibly use it to switch the focus to the next widget that accepts it. A QTextEdit, instead, will both handle and accept the event, as it will normally write a tabulation character in the document contents.
In summary, an event is dispatched to a specific designated object and generally only handled by the single object that actually accepts it.
Signals, on the other hand, are a "communication system" that follows the observer pattern, similarly to the callback mechanism: you "subscribe" to a certain signal and whenever the object emits that signal, the receiver will do anything it wants. The eventual argument(s) of that signal is not changed (or, at least, it shouldn't), and the subscriber(s) can be anything. The sender of that signals doesn't need to care about the targets, nor the different "hierarchy" of those objects matters, as opposed to events.
In summary, don't use events for this, use signals, as that's what they've been made for. This is completely fine:
self.any_line_edit.textChanged.connect(self.any_slot)
Finally, the warnings you're getting from PyCharm are just that: warnings (which are not errors). When the UI is dynamically loaded on runtime, the IDE cannot know the object types of the new attributes, because they will be effective only when the program is running.
In fact, the object to which the attribute is referencing could also change during the lifespan of the program: imagine a class that sets an instance attribute for a "target" widget that is used as an argument in its __init__. How could the IDE know if that widget is a QLineEdit or a QPushButton and "accept" the attributes of that specific widget, such as textChanged or clicked? It couldn't.
So, dynamically created attributes will always show that warning; and you can safely ignore them, as annoying as they are (there should be some shortcut to do that, AFAIK).

PyQt: How to expand all children in a QTreeWidget

There is no real problem, I have just a cosmetic problem. Well, first I create a form with Qt Designer. On this form I place a QTreeWidget. We are still in Qt Designer. In QTreeWidget, I make a few entries (parents), and then I also take a few child entries. When I am done, I save the ui-file and then I load this file - using loadUi() dynamically. In this case I use PyQt4. Up to this point no problem.
When I load the form with placed QTreeWidget I want the program to expand all child entries. By default, when I run the program I only see the parent entries. When I want to see the child entries I have to click on the parent entries, but I don't want this.
Is there a way to solve this?
There is an expandAll() method, which QTreeWidget inherits from the QTreeView class. So, after calling loadUi(), just add something like this:
self.treewWidget.expandAll()

Python GUI Events

I'm sure this question has been asked many times before, so I apologize in advance. I simply cannot find the answer via google or searching stack overflow.
I'm working in python with the wx library. I simply need a wx.EVT_CHAR to be thrown anytime a user presses a key, no matter the focus. How can this be accomplished? Is there a way to bind all widgets? Or a way to always throw an event when application receives a key press?
I tried binding the application itself, the main frame, and the main panel. None of these have accomplished the job of always throwing a wx.EVT_CHAR when a key is pressed.
I was able to solve my problem by writing a recursive method that sets every widget to receive characters and binds every widget to my callback function. It's pretty simple, but took me a bit of googling to realize that not every widget can inherently receive text events (such as a button). Hopefully this will save someone some time in the future. It should be noted that only widgets that are children or subchildren of the parent window passed will be bound to the callback method.
def __RecursiveBinding(self, parent):
try:
parent.Bind(wx.EVT_CHAR, self.CharInputCallback)
parent.SetWindowStyleFlag(wx.WANTS_CHARS)
parent.Refresh()
children = parent.GetChildren()
if(children):
for child in children:
self.__RecursiveBinding(child)

Call a Function in Another Frame (WxPython)

In my wxpython app, I have a parent frame and a child frame. When I click on a button inside the child frame, I want it to send some data (list, string etc.) to the parent frame, close itself, show the parent and finally call a function in the parent frame. How can I do that? (especially function calling)
Thanks in advance.
SOLVED
You should have the button click generate an event containing the data. Then add an event handler to the parent frame that extracts the data from the event and calls the function. The wxPython wiki has a tutorial that covers events. The wx documentation also has an event handling overview.
You can use pubsub to do that too: http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/
Or you can just use a subclass of a Dialog instead and show it modally. Or you could use PostEvent to pass the event. There's an example here: http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
Another method that I just thought of would be to use a socket server or similar and post your data to that and have your program check the server for new data. That's pretty convoluted for this use case though.

PyGTK: Embed a window into another window

I want to embed a window into another window, kind of like this:
EDIT: Screenshots deleted, sorry!
That is a wingdows program and was not made with GTK tough.
I tried using plugs and sockets, but apparently I can't put a gtk.Window (a toplevel window) on a plug.
Is it possible? If so, how? If not, what do you think I should do instead?
gtk.Window is derived from gtk.Bin, so it can only contain one single child. This again can be used in the following way:
Load both windows (e.g. from Glade files)
Remove the child from the second window, but save a reference to the child
Add the child somewhere in the first window
The second step would look like this:
childWidget = secondWindow.get_child()
secondWindow.remove(childWidget)
I'm using this approach to add plugin windows as tabs in one of my PyGTK applications. That means main window and plugins can be designed separately in Glade, and also implemented independently. Of course you're free to add the child widget anywhere you want.

Categories