PySide and PyQt employ Qt signal-slot mechanism with which we can connect any/multiple signals to any/multiple slots as far as the trasmistted data types match.
The signalling object has some knowledge of the receiving slots, e.g. knows their number via method receivers or the signal can disconnect from the receiving slots via its disconnect method.
My problem relates to the oposite direction - e.g. does a slot know to which signals it is connected? Can a slot disconnect from the signals?
UPDATE: So why I am asking this - I have an object that performs some calculation. The calculation is defined by a user editable Python expression. The expression is parsed and necessary data sources are identified from this... The calculation object (acts as a slot) then connects to these data sources (they act as signals) and once the data sources produce/update a value, this fact is signalled to the slot and the expression is reevaluated. And when the expression is changed by a user, it needs to be parsed again and disconnected from the existing signals (i.e. data sources) and connect to new data sources. You can imagine it is something like a formula in Excel that is connected to other cells.
I know there are a few ways to work around this, e.g. keeping track of connections manually (well, this is extra work) or deleting the expression object and creating a new one everytime it is changed (seems not good enough, because user might want to trace back the calculation data sources and this will not help). But I was curious if this can be solved purely using simple signal-slot mechanism. In other words, I am not interested in any workarounds... I know of them and will use them I signals-slots will not help here. :)
The approach you propose forces a very close relationship between the concrete data widgets and the calculation engine. You mingle UI with the calculations. This makes it much harder than it needs to be.
What you could try instead is the model-view approach. The model would be a simple implementation of QAbstractTableModel. The view would be the individual data-entry widgets mapped to the model's cells using QDataWidgetMapper. The calculation engine would access only the model, completely unaware of how the model is modified by the widgets. This make life easier.
The calculation object can connect to the model using just one dataChanged signal and it will be notified of changes to any of the variables. You can easily pass both the value and the variable name by having two columns in the table.
The implementation of the model can be very simple, you can have a list of strings for the variable names in the first column, and a list of variants for the second column. The model must correctly emit the dataChanged signal whenever setData is called, of course.
Related
As I learn from Qt docs, in Qt Model/View framework, models and their attached views should live in the same (GUI) thread. This might lead to some undesirable effects like the following. I have subclassed QAbstractTableModel and implemented the required virtual functions. Internally, the model makes queries into sqlite database which have lots of records, and provides the data accordingly to attached views via reimplemented data() function.
Now, in GUI I have a QTableView, which I attached to that model. Also I have a QLineEdit input field. Printing text in this field emits a textChanged() signal, which is connected to a (custom) query() slot of the model. In this way, typing a new character in the input field should update the table with the records matching the typed phrase.
Well, as my database is large, I do not expect the table updating to follow immediately after typing another letter - updating waits for the query to complete which might take a second or two.
But what bothers me is that since I am obliged to have model and table in the same GUI thread, the input field also friezes after each letter untill the table is updated. I would like to make it such that I might type the phrase without freezing effect, and let wait the table to update. Notifying the model only when the entire phrase is typed with pressing Enter is not an option for me - I need the textChanged() signal to work.
So then I thought - would it be of a big offend to Qt if I ignore the docs and put the model into a non-GUI thread? To my surprise, it worked! Now typing does not freeze, and the program does not crash (at least for now).
So my question is - is it nonetheless unsafe to use model in a non-GUI thread, and my program might suddenly crash any other day? I also should mention that I want to use the model in a read-only way. If I need to change data underlying the model, I will not do it using the view/delegates, I will just send appropriate signals to the model's thread, and all changes will be performed within that thread.
Imagine this example of removing the last row:
Synchronous (same thread)
emit beginRemoveRows(int r = last row)
view reacts and removes references to r
remove r from model
endRemoveRows()
view knows it may repaint
Asynchronous (different threads)
emit beginRemoveRows(r)
remove r from model
endRemoveRows()
Both signals are in the event queue of the GUI thread.
If the GUI event queue contains a repaint event before the beginRemoveRows(), the view will call model->data(r) and your program will likely crash*.
(*) Or at least run into the safeties of your data() implementation, but there are other things under the hood like QPersistentModelIndex which you don't control...
With Qt, I can filter all the QEvent events my application receives or produces with:
qApp.installEventFilter(my_filter_object)
Is there any way to filter Qt signals (of signals and slots) in a similar manner I can filter QEvent events?
With QtCore.QStateMachine.SignalEvent extending QEvent, and StateMachineSignal QEvent.Type being there, everything seems to be in place, but my event filter can't seem to catch one of these.
IOW, is there any way to get the signal's name (index), the signal emitting object, and the passed arguments, for each signal of every QObject in my application without explicitly connecting to it?
Thanks!
No. The only generic API for monitoring signals is QSignalSpy, but that requires an explicit connection to a specific object and signal. The only other thing is the TestLib Framework, which has a command-line option (-vs) that can output all the emitted signals during a test run. But that seems pretty far removed from what you're asking for.
Of course, you can use QMetaObject to list all the signals of a QObject and try to explicitly monitor everything. But that presupposes you can get a reference to all the objects you might be interested in (and that you can actually know in advance what they will be).
The only exception to the above, is for signals that are emitted via a queued connection. These signals, which are most commonly emitted across threads, are wrapped in an event and posted to the event-queue of the receiver's thread. It is possible to detect these kinds of signals by filtering on events of type QEvent.MetaCall.
However, the associated event objects are of type QMetaCallEvent, which is an internal Qt class which is not wrapped by PyQt. Normally, these objects would have id(), sender(), signalId() and args() methods. But in PyQt, all you'll get is a plain QEvent, and thus no information about the emitted signal will be available.
Even if QMetaCallEvent was available, though, the default connection type is not queued - and there is no way to globally reset the default for all signals.
My guess is that the signal events you described are only used for queued connections. In this case events are sent to ensure slots are called after control is passed to the eventloop. Direct connections (wich you would use normally) are essentially just function calls and I doubt there is a way to intercept them without having access to the specific objects.
It is known that QWidget::paintEvent is triggered automatically the moment widget becomes visible or any event from the basic window happens. What should I do if I only want the paintEvent to be issued in response to update()?
It'd make no sense for a paintEvent not to be issued whenever the widget needs to be painted. Your reason to demand such an option means that you're trying to use the Qt API in a way it wasn't meant to be used. I can't quite imagine yet a particular design that would lead you to such use, sorry about that.
So, the only thing I can tell you is how you'd use update(). The idiomatic way of handling widget updates is as follows:
The source of data that the widget uses is updated. For example, the text or some variable affecting the visible contents is changed. Often this data is a Q_PROPERTY of the widget. The setter function is updating the member variable that stores the value and calls update(). The setter should not perform any expensive computations - they should be deferred until the paint event.
If the properties are changed multiple times before the event loop has a chance to run, the update events get coalesced. Internally, a call to update() results in posting an event to the event queue of the GUI thread. If there already is an update event in the queue for the given widget, the events get coalesced. The following invariant is preserved: at any given time, there can only be one update event for any particular widget in an event queue.
When the control returns to the event loop, the update event gets dispatched to the widget, ending up in calling your reimplementation of QWidget::paintEvent(...). This implementation should do the calculations necessary to paint the widget, and do the actual painting. If the calculations are extensive, they should be relegated to a worker thread.
Example
Let's say an application is receiving data from a serial port, modeled as a QIODevice (a QSerialPort is one). You could do as follows:
Connect QIODevice's readyRead signal to a slot in a parser QObject.
The slot receives and parses the data. It then emits a signal with processed data (for example, a vector of floating point values). This signal is connected to a newData slot in the widget.
The newData slot adds the data as-is to a queue, and schedules an update(). This is very fast if you're using Qt's data structures, or if your data class is modeled after them and uses implicit sharing with copy-on-write.
The update() dequeues all data sets and plots them. The QWidget::scroll() method comes handy for scrolling plots.
In QWidget, rather than being signals, mouse and paint and other events are virtual functions to be overloaded. Why is this the case, rather than being consistent?
Although I can't find anything within Qt's documentation that states their motivations for using virtual functions for events, I can guess:
Signals and slots can be nice to work with, but there's overhead associated with them (see, the answer to this question). Since events are triggered quite frequently, the use of signals and slots could carry quite a performance penalty.
Some event handlers need to return values. Returning a value from a slot back to the signal emitter is not officially supported (though see this question), which means that they wouldn't be appropriate.
Signals emitted from QWidget may not make sense from a design point-of-view. If I create my own widget derived from QWidget, why have signals emitted from my widget that just connect back to slots within the widget itself? This also has the danger of breaking encapsulation since signals are always "public".
Even if none of the above were true, there would still be quite a few architectural questions raised by trying to use signals for events. For example, a single event is often passed to multiple different objects (for example, in the case where some objects don't wish to process the event). In this scenario, which object emits the signal indicating that the event has taken place? It's certainly not the target of the event (i.e., the widget, as you've implied above). It would have to be some other global object. But if that were true, we would likely just implement virtual slots on every widget to handle the signal. This essentially just gets us back to where we started with virtual functions!
In Qt, I have a model subclassing QAbstractItemModel - it's a tree displayed in a QTreeView.
The model supports various forms of change which all work OK. The two of relevance are:
1) Some data in a small number of related rows changes
2) A visualisation change means that the majority of rows should change their formatting - in particular they have a change of background highlighting. Their DisplayRole data does not change.
The current design deals with both of these in the same way: for every row that has any change the model emits dataChanged(start_of_row_index,end_of_row_index). I emit the signal for both parent rows that change and for any of their children that have changed.
However, this performs badly in case 2 as the model gets big: a very large number of dataChanged signals are emitted.
I have changed the code so that in case 2 the model emits dataChanged only for the (single) row that is the parent of the entire tree.
This still appears to work correctly but does not accord with my understanding of the responsibilities of the model. But I suspect I may be wrong.
Perhaps I am misunderstanding the dataChanged signal? Does it actually cause the view to update all children as well as the specified range? Or can I avoid emitting dataChanged when it is not the DisplayRole that is changing?
Edited with my progress so far
As Jan points out, I ought to emit dataChanged either for most or all of the rows in case 2.
My code originally did this by emitting dataChanged for every changed row but this is too expensive - the view takes too long to process all these signals.
A possible solution could be to aggregate the dataChanged signal for any contiguous blocks of changed rows but this will still not perform well when, for example, every other row has changed - it would still emit too many signals.
Ideally I would like to just tell the view to consider all data as potentially changed (but all indexes still valid - the layout unchanged). This does not seem to be possible with a single signal.
Because of a quirk of the QTreeView class, it is possible (though incorrect according to the spec) to emit only one dataChanged(tl,br) as long as tl != br. I had this working and it passed our testing but left me nervous.
I have settled for now on a version which traverses the tree and emits a single dataChanged(tl,br) for every parent (with tl,br spanning all the children of that parent). This conforms to the model/view protocol and for our models it typically reduces the number of signals by about a factor of 10.
It does not seem ideal however. Any other suggestions anyone?
You are expected to let your views know whenever any data gets changed. This "letting know" can happen through multiple ways; emitting dataChanged is the most common one when the structure of the indexes has not changed; others are the "serious" ones like modelReset or layoutChanged. By a coincidence, some of the Qt's views are able to pick up changes even without dataChanged on e.g. a mouseover, but you aren't supposed to rely on that. It's an implementation detail and a subject to change.
To answer the final bit of your question, yes, dataChanged must be emitted whenever any data returned from the QAIM::data() changes, even if it's "just" some other role than Qt::DisplayRole.
You're citing performance problems. What are the hard numbers -- are you actually getting any measurable slowdown, or are you just prematurely worried that this might be a problem later on? Are you aware of the fact that you can use both arguments to the dataChanged to signal a change over a big matrix of indexes?
EDIT:
A couple more things to try:
Make sure that your view does not request extra data. For example, unless you set the QTreeView's uniformRowHeights (IIRC), the view will have to execute O(n) calls for each dataChanged signal, leading to O(n^2) complexity. That's bad.
If you are really sure that there's no way around this, you might get away by combining the layoutAboutToBeChanged, updatePersistentIndexes and layoutChanged. As you are not actually changing the structure of your indexes, this might be rather cheap. However, the optimization opportunity in the previous point is still worthwhile taking.