PyQt - how to read documentation - python

I need some advise about how to read PyQt's documentation. Because on my own I can hardly figure anything out. I'm a programming newbie so sorry if my question is confusing. I'll try to explain the best I can :)
This is an example of where I got stuck. I was experimenting around with QListView. Basically just trying to print out data of what I have selected in the view. I got stuck until Justin, a very patient Python tutor, showed me this bit of code.
listView.clicked.connect(B)
def B(index):
record = sqlmodel.record(index.row())
It connects a clicked signal from QListView to function B. I was very surprised that right away the clicked event sends index to B by itself. I tried to look through QListView's documentation but can't find anything that explains this.
http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qlistview.html
In this case, where in the docs should I look at to learn about this clicked event, and the index it sends out?
Would really appreciate any advise. :)

Unfortunately PyQt documentation is not full. Better source for knowledge is Qt documentation.
In your case QTableView inherits QAbstractItemView, and there is no clicked signal in QTableView docs, you can find it in List of all members, including inherited members page. And you can see, that this signal comes from QAbstractItemView and it's defined as:
void QAbstractItemView::clicked ( const QModelIndex & index )
Here you can see type of function arguments (clickable).
So, index passed to you function will be instance of QModelIndex and it has row method.
If you are confused with C++ syntax, another option is to use PySide documentation, which is more Python friendly.
QAbstractItemView.clicked in PySide docs

The following code connects QTableView's clicked signal to your function. QTableView emits clicked whenever someone clicks an item, which means that your function will be called automatically, since it's connected to that signal.
listView.clicked.connect(viewItemClicked)
Or am I missing something in your question? Read up on signal-slots in Qt if the above is unclear. (PyQt allows any function (i.e. python callable) to be connected to a signal, not just a slot (as it is in C++).

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).

PyQt5 connect textChanged signal to QPlainTextEdit [duplicate]

This question already has an answer here:
Passing extra arguments through connect
(1 answer)
Closed 2 years ago.
so I'm currently working on a PyQt5 GUI, and as always need to connect some signals to method calls.
Naturally I've looked up a standard syntax to do so and used it throughout my entire project (it's been working so far with more then 20 different signals)
That syntax is: self.widget.signal.connect(lambda x: whatever)
So I recently got to the point of connecting the QPlainTextEdit signal "textChanged()" to one of my methods and it just didn't work. I've tried replacing my method with a simple print(text) but that didn't help. My next step was testing wether another signal of the same widget worked and it did!
So now I have the following code:
self.plainTextEdit.textChanged.connect(lambda x: print("testTextChanged"))
self.plainTextEdit.blockCountChanged.connect(lambda x: print("blockCountChanged"))
and the upper signal doesn't trigger, but the lower one does.
I've already read the documentation of QPlainTextEdit, textChanged() should be a valid signal of this class. I've also used the same signal on several QLineEdits within my project.
Does anyone have any suspicion as to why this behaviour is occuring? Maybe I did make an error that I just can't recognize. (I'm trying to trigger the signal by simply typing into the textBox on the GUI, whereas blockCountChanged get's triggered whenever I'm pressing enter while editing it)
So, the comment of musicamente (comment on the question) did answer it. The reason it did not work is, because the textChanged signal of QPlainTextEdit does not have any parameters (QLineEdit textChanged does f.e.). That's why the lambda should not have and parameters -> the correct code should be:
self.plainTextEdit.textChanged.connect(lambda: print("testTextChanged"))
PS: just answering this if someone searches for the same stuff.

Access the built-in shortcuts of PyQt5 components?

I'm very keen on being able to use the keyboard to do everything with a GUI and am currently exploring QTreeView and QTableView among other things.
I'm adding a lot of my own hotkeys (shortcuts) and am devising a method to automate a user list or guide to these available hotkeys.
But something like QTreeView also comes with its own built-in hotkeys, e.g. arrow keys for navigation, F2 to start editing, Ctrl-A for "select all", etc.. I want to get a comprehensive list of these and include them in the automatically generated user guide.
I've got to this page, for example, but I haven't really got a clue how to dig down into PyQt5 components to unearth this kind of information programmatically.
There's some interesting functionality, probably unknown to many users, with QTreeView: e.g. if, in column 0, you have a tree structure you can skip from label (text) to label by pressing the first letter of each one's label. But if you enter 2 (or more) keys quickly enough this also works: entering "ra" will skip over "Roma" and "Rimini" to "Ravenna" even if "Roma" and "Rimini" come first. It turns out that this is implemented by QTreeView.keyboardSearch ... but what I want to know is whether it's possible to find details of the "mapping" functionality for this and other keyboard enablements, often implemented by keyPressEvent, programmatically. Having looked a little at the PyQt5 files it seems that a lot of PyQt5 functionality may ultimately be contained in .dll files (this is a W10 machine), so I'm not particularly optimistic.
Each widget has a certain behavior depending on the hotkeys pressed, so there is no documentation that indicates all the cases, so you will have to review the documentation of each class and the parent class. So for example to understand the behavior of QTableView you should also review the documentation of QAbstractItemView, QAbstractScrollArea and QFrame (the same is for QTreeView), considering the above we can collect information:
void QAbstractScrollArea::keyPressEvent(QKeyEvent *e)
This function is called with key event e when key presses occur. It
handles PageUp, PageDown, Up, Down, Left, and Right, and ignores all
other key presses.
QAbstractItemView:
void QAbstractItemView::keyPressEvent(QKeyEvent *event).
This function is called with the given event when a key event is sent
to the widget. The default implementation handles basic cursor
movement, e.g. Up, Down, Left, Right, Home, PageUp, and PageDown; the
activated() signal is emitted if the current index is valid and the
activation key is pressed (e.g. Enter or Return, depending on the
platform). This function is where editing is initiated by key press,
e.g. if F2 is pressed.
(emphasis mine)
QTableView and QTreeView when inheriting from QAbstractItemView have the same hotkeys.

Segmentation fault using Python gtk3 with pyOpenGL

I use Python in combination with the gtk3 and the GTKGLExt fork from https://github.com/tdz/gtkglext so I can use the gobject introspection feature to use Gtk3 from python.
I created a Gtk.DrawingArea to draw my OpenGL stuff. Everything works fine as long as I have just one instance of this widget.
However, I use a Gtk Notebook to have multiple instances of this widget present in different pages of the notebook (one widget per page).
Sometimes (meaning in a non deterministic way) the program crashes with a segmentation fault. I ran a stacktrace using gdb and located the problem to be the call to "gtk_widget_end_gl" which is placed at the end of my drawing, realize and configure handler methods (of course there is a gtk_widget_begin_gl at the beginning of each of those as well).
Here is the relevant excerpt from the stacktrace:
0 0xb1170b58 in _gdk_x11_gl_context_impl_get_current () at gdkglcontext-x11.c:514
1 0xb116c094 in gdk_gl_context_get_current () at gdkglcontext.c:244
2 0xb116c0b4 in gdk_gl_context_release_current () at gdkglcontext.c:215
3 0xb4d04592 in gtk_widget_end_gl (widget=0xa175608, do_swap=0) at gtkglwidget.c:549
and below is a minimal example of my realize method where the problem occurs where "widget" is an instance of Gtk.DrawingArea:
def on_realize(self, widget, *user_data):
if not GtkGLExt.widget_begin_gl(widget):
return False
gl.glClearColor(BACKGROUND_COLOR[0],
BACKGROUND_COLOR[1],
BACKGROUND_COLOR[2],
BACKGROUND_COLOR[3])
GtkGLExt.widget_end_gl(widget, False)
Since I am pretty much clueless why this problem occurs sometimes (around every 5th time a new widget is created) I wonder if anyone ever has experienced the same or can reproduce the problem or help me to find a solution.
I need to say that I don't manually create an OpenGL context here since in the provided examples this never seemed necessary and I figured the widget would do this on it's own. The stacktrace implies that there seems to be a problem getting the context. Whats startles me is the fact that this only happens sometimes. So if someone could even hint me how to tackle this problem I would be very glad as I am not an experienced C programmer.

getting keyboard events with pyqt

i converted recently from wxpython to pyqt and im still facing alot of problems since im still noob in pyqt
so is it possible to detected if user pressed (CTRL+key ) in pyqt ? and how ?
i've been trying to find an answer for this for 3 days . if you know website or a good place to learn pyqt, it will be highly appreciated
thanx in advance
Add a QShortcut and listen to its activated() signal, then perform the action in the slot.
Or you could reimplement QWidget and define keyPressEvent to what you like. Check for the event parameter's modifiers() and key() to see if they match with what you want. This listens for shortcut keys when the QWidget has focus.
As for websites to learn, this is the official documentation - it takes some getting used to, but is quite helpful once you get the lay of the land (so to speak). This tutorial is what I walked through to get the initial idea, before discovering the documentation.
Good luck!
P.S. You might also look at QAction, if you are trying to map your Ctrl+X to an action that may also be performed by a menu or toolbar button... It incorporates a shortcut along with icons and/or text in a very convenient package. Just FYI.

Categories