[PyQt] pyqtsignal() and None value
J Barchan
jnbarchan at gmail.com
Thu Sep 6 08:29:32 BST 2018
On 5 September 2018 at 18:38, Maurizio Berti <maurizio.berti at gmail.com>
wrote:
> First of all, if anyone here has access to the documentation pages of
> PyQt, please read this (expecially the third point).
>
>
> I do not know your exact user case, so it's hard to give you specific
> suggestions, but here's what I'd do.
>
> 1. do not give any signature to the signal at all:
>
> notifyProgress = QtCore.pyqtSignal(object, object)
>
> This allows you to use None for both arguments, but I'm not familiar with
> typing in methods for python 3 (to be fair, I didn't even know about it
> until yesterday, as I mainly use Python2). I did a quick test and it seems
> to work fine, though.
>
> 2. use a single parameter that will be sent as a tuple, and unpack it
> within the method:
>
> notifyProgress = QtCore.pyqtSignal(object)
> def updateProgress(self, *args):
> val, text = args
>
> [...]
>
> self.notifyProgress.emit((None, "Some text"))
> self.notifyProgress.emit((i / 2, None))
>
> 3. use an overloaded signal and two separate slots. This is something I
> was struggling for a lot of time and (thanks to you! :-D) I finally found
> out how it works, as it is *not* explained exhaustively in the
> documentation. Also, I think that's what you were referring to.
> As we know, signals can be connected "in two ways" (I'm referring to
> new-style connections). Normally, you can go by simply connecting the slot
> and eventually decorating that slot with the signature you are interested
> in; decoration is usually not strictly required, as slot that are
> processing invalid data are somewhat "ignored": I always thought it as some
> kind of try/except mechanism, if at some point within the method something
> wrong happens, the error is ignored but no exception is thrown; of course,
> using a decorated slot avoids unnecessary computation for wrong signature
> arguments.
> Sometimes, though, it's better if not necessary to connect the slot to the
> specific signature: self.somesignal[signature].connect(slot).
> What the documentation forgets to explain is that the same _has_ to be
> done when emitting custom overloaded signals if you are not using the
> default signature (the first argument).
>
> #in this case, "str" is the default signature
> notifyProgress = QtCore.pyqtSignal([str], [int])
>
> [...]
> #the default signature can be omitted
> self.notifyProgress.connect(self.updateProgressString)
> self.notifyProgress[int].connect(self.updateProgressInt)
> [...]
>
> @QtCore.pyqtSlot(str)
> def updateProgressString(self, text):
> [...]
>
> @QtCore.pyqtSlot(int)
> def updateProgressInt(self, val):
> [...]
>
> [...]
>
> #again, the default signature can be omitted
> self.notifyProgress.emit("Some text")
> #now, *THIS*!
> self.notifyProgress[int].emit(5)
>
> I really think that this is something the documentation should *not* miss.
> I understand that from the C++ point of view it's pretty obvious that a
> signal is emitted according to its argument type signature, but this is not
> that obvious for a common Python programmer.
>
> Obviously, in the last case there's a small drawback: you can't pass None
> as argument, as it will be converted to the typed signature, giving you an
> empty string or that infamous random 32bit integer. I don't think it would
> be an issue in your case, but it is something to keep in mind anyway.
>
>
> I hope this helps... it helped me :-)
>
> Maurizio
>
> 2018-09-05 12:33 GMT+02:00 J Barchan <jnbarchan at gmail.com>:
>
>>
>>
>> On 5 September 2018 at 09:11, J Barchan <jnbarchan at gmail.com> wrote:
>>
>>>
>>>
>>> On 4 September 2018 at 18:08, Maurizio Berti <maurizio.berti at gmail.com>
>>> wrote:
>>>
>>>> You are defining a specific signature in the signal:
>>>>
>>>> QtCore.pyqtSignal(int, str)
>>>>
>>>> this means that, despite the types you set in the method (which Qt
>>>> doesn't know anything of also, as you didn't use the Slot decorator),
>>>> whenever you emit a None (which is a NoneType, not int, nor str) Qt will
>>>> try to "translate" it to the valid signature you assigned.
>>>>
>>>> I don't know exactly why the int is that a high number (always high and
>>>> always random), but this probably makes sense for some C++ type signature,
>>>> as it seems to me that the number is always 32bit long and, in my case,
>>>> always negative.
>>>>
>>>> Anyway, if you really need to send None, you can use the generic
>>>> "object" signature in the signal definition, or, in your case, just go with
>>>> this, assuming the progress will never use negative numbers.
>>>>
>>>> def updateProgress(self, val: int=-1, text: str=''):
>>>> if val is >= 0:
>>>> self.progressBar.pb.setValue(val)
>>>> if text:
>>>> self.progressBar.label.setText(text)
>>>>
>>>> and then emit the text signal using -1 for val.
>>>>
>>>> Maurizio
>>>>
>>>>
>>>> 2018-09-04 18:16 GMT+02:00 J Barchan <jnbarchan at gmail.com>:
>>>>
>>>>> PyQt5.7. I am having trouble `emit()`ing a signal and receiving its
>>>>> arguments correctly. I have read http://pyqt.sourceforge.net/Do
>>>>> cs/PyQt5/signals_slots.html carefully.
>>>>>
>>>>> *Declaration*:
>>>>>
>>>>> # class variable for "notifyProgress" signal, for displaying a
>>>>> progressbar
>>>>> notifyProgress = QtCore.pyqtSignal(int, str)
>>>>>
>>>>> *Initialisation*:
>>>>>
>>>>> self.notifyProgress.connect(self.updateProgress)
>>>>>
>>>>> *Slot*:
>>>>>
>>>>> def updateProgress(self, val: int, text: str):
>>>>> # slot for self.notifyProgress
>>>>> # eprpayrequestfunctions.runEpr() calls this to indicate
>>>>> progress
>>>>> # if it passes an integer it's the desired value for the
>>>>> progressbar
>>>>> # if it passes a string it's the desired value for the label
>>>>> if val is not None:
>>>>> self.progressBar.pb.setValue(val)
>>>>> if text is not None:
>>>>> self.progressBar.label.setText(text)
>>>>>
>>>>> *Signals*:
>>>>>
>>>>> 1. notifyProgress.emit(None, "Some text")
>>>>>
>>>>> 2. notifyProgress.emit(i / 2, None)
>>>>>
>>>>> *Behaviour in slot*:
>>>>>
>>>>> The problem is the passing of None from emit():
>>>>>
>>>>> 1. val arrives in slot as 1261196128.
>>>>>
>>>>> 2. text arrives in slot as '' (empty string).
>>>>>
>>>>> *Questions*:
>>>>>
>>>>> - Where is this behaviour for None as an emit() parameter
>>>>> documented?
>>>>> - What is the correct/best way for handling this correctly/easily?
>>>>>
>>>>>
>>>>> --
>>>>> Kindest,
>>>>> Jonathan
>>>>>
>>>>> _______________________________________________
>>>>> PyQt mailing list PyQt at riverbankcomputing.com
>>>>> https://www.riverbankcomputing.com/mailman/listinfo/pyqt
>>>>>
>>>>
>>>>
>>>>
>>>> --
>>>> È difficile avere una convinzione precisa quando si parla delle ragioni
>>>> del cuore. - "Sostiene Pereira", Antonio Tabucchi
>>>> http://www.jidesk.net
>>>>
>>>
>>> I'm OK with the val: int=-1, but not with the text: str=''. An int of
>>> -1 is happily an invalid value for me, butt the trouble is a str of ''
>>> is perfectly legal, and will be used :(
>>>
>>> (which Qt doesn't know anything of also, as you didn't use the Slot
>>>> decorator),
>>>>
>>>
>>> I don't mind adding a slot decorator if that would help. But presumably
>>> it would not here, as you're saying my None value does not match the
>>> type correctly anyway, right?
>>>
>>> I did consider making it so there is only one parameter, and the type (
>>> int or str) indicates which of the two paths to follow in the slot. I
>>> followed http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html
>>> carefully where it describes"The following code demonstrates the connection
>>> of overloaded signals:" but I couldn't get it to work: it *always*
>>> called the first/default overload, regardless of the parameter type ... :(
>>>
>>> I'm now thinking: could I cut my losses and pass (and receive?) a single
>>> parameter as an explicit QVariant (which we don't usually use from
>>> PyQt), so that a single receiver slot can correctly see the original type
>>> --- would that work/be advisable?
>>>
>>>
>>> --
>>> Kindest,
>>> Jonathan
>>>
>>
>> OK, I gave QVariant (single parameter) a try. *All* I had to was:
>>
>> 1. Declare signal as:
>>
>> QtCore.pyqtSignal(QVariant)
>>
>> 2. Write slot like:
>>
>> def updateProgress(self, val):
>> if type(val) is int or type(val) is float:
>> self.progressBar.pb.setValue(val)
>> elif type(val) is str:
>> self.progressBar.label.setText(val)
>>
>> 3. And then it works correctly with both emitter types (without caller
>> bothering with QVariant), like:
>>
>> notifyProgress.emit(50)
>> notifyProgress.emit("Finishing...")
>>
>> Is there any reason I should *not* be doing this, because it seems
>> perfect to me?
>>
>>
>>
>> --
>> Kindest,
>> Jonathan
>>
>
>
>
> --
> È difficile avere una convinzione precisa quando si parla delle ragioni
> del cuore. - "Sostiene Pereira", Antonio Tabucchi
> http://www.jidesk.net
>
Hi Maurizio,
First, thank you for your time on this matter.
I think the bit you identified which I would not have tried is the
(non-documented) need to invoke the non-default overload via
#now, *THIS*!
self.notifyProgress[int].emit(5)
But I would still welcome your comment on my last post? There I said I
have solved the whole thing very simply, just by using
QtCore.pyqtSignal(QVariant)
as my signal declaration. To be clear: although I originally asked for two
separate parameters or overloads for the string versus the int, in my case
I am quite content with one at a time (calling code tends *either* to pass
the int *or* the string, it doesn't need both in one call).
My problem then was the type for the parameter, to cater for either str or
int. I chose QVariant, and it works fine for me. Your suggestion seems to
be object (I don't know what that maps to in C++). Do you think it makes
any difference if I stick with my choice?
Thanks.
--
Kindest,
Jonathan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20180906/cd669846/attachment-0001.html>
More information about the PyQt
mailing list