[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