[PyQt] pyqtsignal() and None value

Maurizio Berti maurizio.berti at gmail.com
Wed Sep 5 18:38:25 BST 2018


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/Docs/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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20180905/073bddf6/attachment-0001.html>


More information about the PyQt mailing list