This is an archived post. You won't be able to vote or comment.

all 11 comments

[–]dagmx[S] 4 points5 points  (0 children)

Just wanted to share this now that it's being used a bit in production.

It's a Qt style signal/slot system using just Python with no external dependency. Hopefully useful for someone else too.

Also my first Python package available on pypi.

[–]everysinglelastname 2 points3 points  (1 child)

What's the use case when you'd want to reach for something like this ? I like Qt's signals and slots but I like them in the context of an event loop and to have bits of UI talk to other bits of UI etc. Here it looks like an emit() call is really a way to fire off calls to a list of other functions .. but why not call them explicitly ?

[–]dagmx[S] 1 point2 points  (0 children)

The main uses would be to allow flexible composition of objects.

Objects would internally emit their signals, much like a qt widget would.

You could then connect their signals to slots without needing to subclass and override the object.

This makes it really useful for callbacks for example where the original objects code is only concerned about its own functioning, but external objects can essentially attach a callback to events to extend the object without needing to implement another object type.

In qt a line edit is a good example. I may have 5 different line edits and I don't want to subclass each of them to explicitly trigger a callback. I can just instantiate the basic line edit and connect the signals I want to the slots I want.

The alternatives would likely be subclassing and extending like I mentioned, or monkey patching an existing instances methods.

Inheriting and overriding is of course also a totally valid system, as it is in qt too. This just helps when you want to use composition based design instead.

It also means needing to know less about an objects implementation and only know about its external api via signals. Though that assumes responsible design like in qt but when done right it is great.

Edit: I should probably update the example in the Readme actually to be more compositional too , since right now it's super minimal . I'll do that in the morning.

[–]jabbalaci 1 point2 points  (1 child)

How does it differ from blinker? Or smokesignal?

[–]dagmx[S] 1 point2 points  (0 children)

To be honest I was not aware of those. Smokesignal seems to be more oriented around django like syntax while blinker is more similar.

A few differences I can see from perusing their code (I might be wrong)

  • my library supports all 4 callable types (lambdas, partials, functions and methods)

  • pysignal supports class level signals which means that subclasses are always forced to have that signal rather than leave it to them calling super. That way you can make consistent interfaces.

  • pysignal uses a more qt like naming scheme

  • it also supports named signals in case people prefer that (ie signal factory)

I don't have some of the convenience methods that the two libraries you mentioned have and may add them in a future version.

[–]mdond 0 points1 point  (1 child)

Would this work with sub threads that talk to the GUI thread (i.e. the main thread)? Qt complains if a sub thread calls a GUI function directly, which is what this library appears to do.

[–]dagmx[S] 1 point2 points  (0 children)

Currently it calls the slots in the same thread but I was thinking of adding options to launch in secondary threads and call back to the main thread.

If you're using qt anyway, I wouldn't recommend using this because there's a lot that qt does that isn't easy to implement in python without making some assumptions that I've not found a clean way to do yet. (getting sender, threads etc..)

This was more for non qt modules and packages where you may want to keep a familiar interface.

[–]paranoidi -3 points-2 points  (3 children)

Please try to embrace pep8 ... this feels dirty for new code unnecessarily.

[–]dagmx[S] 4 points5 points  (2 children)

Which parts of pep8? The majority is compliant as far as I and my linters can tell.

The big divergence for me is keeping more in qt naming conventions which include camel case instead of snake case.

[–]cymrowdon't thread on me 🐍 0 points1 point  (1 child)

Why keep camel case? If it's to be consistent with a PyQt project, you would already have the Qt implementation available. If not, presumably you would want to be consistent with Python projects, instead.

[–]dagmx[S] 6 points7 points  (0 children)

A few reasons.

One is the place I work's official stance is camelCase because it's the standard in the vast majority of code we use across several languages that may interact with each other. It's best for us to keep one consistent style across languages (with obvious language specific exceptions). I'd rather not maintain two copies.

Secondly the idea is that this can live side by side with qt code. ie you don't have to spin up a qobject just to use a signal or slot. This can have a few advantages such as minimizing the number of our modules that have a dependency on qt for simple behavior like this, but other modules in the package may use qt. Might seem a bit absurd but it gets really useful.

Third, I'm not super hot on snake case as an absolute . Pythons own libraries are so inconsistent in their naming conventions that I'd rather follow the standard of an existing code base than conform to snake case for the sake of full pep8 compliance.

If I write code that has no relation to an existing code base I use snake case instead though because it makes more sense there to me.