Call a PyQt class from inside Watchdog event handler class - python-3.x

I have watchdog set up to monitor a folder for new files and I want to try to get back to the main PyQt class after it runs the on_created function.
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class CreatedEvent(FileSystemEventHandler):
def on_created(self, event):
#some code
I need to be able to call someFunction
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class someClass(QMainWindow):
def someFunction(self):
#some code
I have tried using signals but I always get this error
QPixmap: It is not safe to use pixmaps outside the GUI thread
QPixmap: It is not safe to use pixmaps outside the GUI thread

Related

Is it possible to inherit setup() and tearDown() methods?

I work with automation primarily in C# / Java and have been looking into Python for its speed.
In C#, I can write a class that implements a WebDriver instance, along with [SetUp] and [TearDown] methods. Then, every class containing test cases can inherit from this class, so I do not need to write my SetUp and TearDown for every test class I write.
The other benefit of SetUp / TearDown fixture is that I can use same WebDriver instance throughout all tests. SetUp will create the WebDriver instance, and pass it into test case class, where test case can use to initialize PageObjects & perform clicks etc. When test is finished, WebDriver instance gets passed back into TearDown for cleanup. This is highly efficient and easy to use.
My issue: I do not understand the Python best-practice on how to implement this functionality.
I have read through Python unittest docs here and read up on Python multiple inheritance here with little luck. I have also read this SO discussion here, but it is 10+ years old and contains many polarizing opinions. I did use discussion as guidance to get started, but I do not want to blindly write code without understanding it.
I am hung up on the part about how to actually inherit setUp(), tearDown(), and my webdriver instance. I don't want to declare a new webdriver instance, and re-write setUp() and tearDown() methods for every single test class, as this seems inefficient.
Here's what I've tried:
This is SetUp / TearDown fixture which is meant to handle setup and teardown for ALL test cases, and also keep track of singleton WebDriver instance.
Project Directory Structure:
base_test_fixture.py
from selenium import webdriver
import unittest
class BaseTestFixture(unittest.TestCase):
driver = None
def setUp(self):
print("Running SetUp")
self.driver = webdriver.Chrome()
def tearDown(self):
print("Running Teardown")
self.driver.close()
self.driver.quit()
Here is test_webdriver.py:
import unittest
import BaseTestFixture
class TestWebDriver(BaseTestFixture.SetUpTearDownFixture):
def test_should_start_webdriver(self):
super.setUp()
print("Running test 1")
super.driver.get("https://www.google.com")
assert "Google" in self.driver.title
super.tearDown()
def test_should_navigate_to_stackoverflow(self):
super.setUp()
print("Running test 2")
super.driver.get("https://www.stackoverflow.com")
assert "Stack Overflow" in self.driver.title
super.teardown()
if __name__ == '__main__':
unittest.main()
Here's the error my class declaration is showing: AttributeError: module 'BaseTestFixture' has no attribute 'SetUpTearDownFixture'
Is it possible to implement a single WebDriver, setUp(), and tearDown() for all Python test cases?
You are very close. the Python convention is that your module should be named with underscores, so I would rename BaseTestFixture.py to base_test_fixture.py, and the class in the module would be the camelcase version of the module name.
that would give us, base_test_fixture.py:
from selenium import webdriver
from unittest import TestCase
class BaseTestFixture(TestCase):
and test_web_driver.py:
import unittest
from base_test_fixture import BaseTestFixture
class TestWebDriver(BaseTestFixture):
If you're still having trouble, the problem may be in the directory structure of your package, so share that with us by editing your question above to indicate the structure of your directory and files.
Also, within your test, since the test class inherits self.driver, you just have to refer to it as self.driver (no super.).
Also, setUp() and tearDown() are automatically called by unittest, so you don't have to call them explicitly.

Is it possible to create pyqtSignals on instances at runtime without using class variables?

Is there any possibility to create signals at runtime when needed?
I'm doing something like this in a function:
class WSBaseConnector(QObject)
def __init__(self) -> None:
super(QObject, self).__init__()
self._orderBookListeners: Dict[str, pyqtSignal[OrderBookData]] = {}
def registerOrderBookListener(self, market: str, listener: Callable[[OrderBookData], None], loop: AbstractEventLoop) -> None:
try:
signal = self._orderBookListeners[market]
except KeyError:
signal = pyqtSignal(OrderBookData)
signal.connect(listener)
self._orderBookListeners[market] = signal
else:
signal.connect(listener)
As you can see, I have a dict that stores str, pyqtSignal pairs. When I try to connect the signal to the listener I get the error:
'PyQt5.QtCore.pyqtSignal' object has no attribute 'connect'
Is it not possible to create pyqtSignals at runtime without been class vars?
Cheers.
No, it is not possible. The pyqtSignal object is a factory function that returns a descriptor, so it must be created when the class statement is executed. To quote from the docs:
New signals should only be defined in sub-classes of QObject. They
must be part of the class definition and cannot be dynamically added
as class attributes after the class has been defined.
New signals defined in this way will be automatically added to the
class’s QMetaObject. This means that they will appear in Qt Designer
and can be introspected using the QMetaObject API. [emphasis added]
Your code is creating unbound signal objects, which is why you get the attribute error. The distinction between bound and unbound signals is exactly the same as with the methods of classes. To quote again from the docs:
A signal (specifically an unbound signal) is a class attribute. When a
signal is referenced as an attribute of an instance of the class then
PyQt5 automatically binds the instance to the signal in order to
create a bound signal. This is the same mechanism that Python itself
uses to create bound methods from class functions.
#ekhumoro's accepted answer is, as far as I know, entirely accurate (specifically the statement: "They must be part of the class definition and cannot be dynamically added as class attributes after the class has been defined.").
Unfortunately I've seen this answer mis-interpreted as "It is not possible to generate Qt signals programmatically", which of course, when it comes to Python, is an entirely different proposition.
So I'm afraid this answer does not address the original question, which genuinely wanted to add signals at runtime and instead I thought I'd set the record straight and provide an example of creating signals programmatically, without contradicting the above statement. The solution is to create a dynamically generated class along with some dynamically generated signals. There are several good ways of generating classes dynamically in Python:
Option 1 - using the type function
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtCore import QMetaObject
DynamicSignal = type("DynamicSignal", (QObject, ), {
"my_custom_signal": pyqtSignal([str]),
})
if __name__ == '__main__':
dynamic = DynamicSignal()
dynamic.my_custom_signal.connect(print)
dynamic.my_custom_signal.emit("Hello world")
This prints "Hello world" when executed.
Option 2 - using metaclasses
We can also achieve the above with a metaclass:
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtCore import QMetaObject
class DynamicSignalMeta(type(QObject)):
def __new__(cls, name, bases, dct):
dct['my_custom_signal'] = pyqtSignal([str])
return super().__new__(cls, name, bases, dct)
class DynamicSignal(QObject, metaclass=DynamicSignalMeta):
pass
if __name__ == '__main__':
dynamic = DynamicSignal()
dynamic.my_custom_signal.connect(print)
dynamic.my_custom_signal.emit("Hello world")
In my other answer I focused on the question "Is it possible to programmatically add signals" as opposed to what the OP asked "Is it possible to dynamically add signals at runtime (i.e. after the class has been instantiated)".
Contrary to #ekhumoro's accepted answer, I would claim that it is actually possible to add signals at runtime, despite the PyQT documentation's very clear statement:
They must be part of the class definition and cannot be dynamically added as class attributes after the class has been defined
Whilst I don't doubt the accuracy of the statement, Python is a wonderfully dynamic language and it is in-fact reasonably easy to achieve the desired result. The problem we have to overcome is that in order to add signals at runtime, we must create a new class definition and modify an instance's underlying class. In Python this can be achieved by setting an object's __class__ attribute (which in general has a number of issues to be aware of).
from PyQt5.QtCore import QObject, pyqtSignal
class FunkyDynamicSignals(QObject):
def add_signal(self, name, *args):
# Get the class of this instance.
cls = self.__class__
# Create a new class which is identical to this one,
# but which has a new pyqtSignal class attribute called of the given name.
new_cls = type(
cls.__name__, cls.__bases__,
{**cls.__dict__, name: pyqtSignal(*args)},
)
# Update this instance's class with the newly created one.
self.__class__ = new_cls # noqa
With this class we can create signals after the object has been instantiated:
>>> dynamic = FunkyDynamicSignals()
>>> dynamic.add_signal('example', [str])
>>> dynamic.example.connect(print)
>>> dynamic.add_signal('another_example', [str])
>>> dynamic.another_example.connect(print)
>>> dynamic.example.emit("Hello world")
Hello world
This approach uses modern Python syntax (but could equally have been written for Py2), is careful to expose a sensible class hierarchy and preserves existing connections when new signals are added.

How do I connect a PyQt5 slot to a signal function in a class?

I'm trying to set up a pyqt signal between a block of UI code and a separate python class that is just for handling event responses. I don't want to give the UI code access to the handler (classic MVC style). Unfortunately, I am having difficulty connecting the slot to the signal. Here is the code:
from PyQt5 import QtCore
from PyQt5.QtCore import QObject
class UiClass(QObject):
mySignal = QtCore.pyqtSignal( str )
def __init__(self):
QObject.__init__(self)
def send_signal(self):
self.mySignal.emit("Hello world!")
class HandlerClass():
currentMessage = "none"
def register(self, mySignal):
mySignal.connect(self.receive_signal)
#QtCore.pyqtSlot(str)
def receive_signal(self, message):
self.currentMessage = message
print(message)
ui = UiClass()
handler = HandlerClass()
handler.register(ui.mySignal)
ui.send_signal()
When I run this code it fails at the handler.register line. Here is the error:
Traceback (most recent call last):
File "C:\git\IonControl\testpyqtslot.py", line 25, in
handler.register(ui.mySignal)
File "C:\git\IonControl\testpyqtslot.py", line 17, in register
mySignal.connect(self.receive_signal)
TypeError: connect() failed between UiClass.mySignal[str] and receive_signal()
I'd like this code to successfully register the signal to the slot and have the handler print "hello world" at the end. What did I do wrong here?
My basic question is this: how do I connect a signal to a slot function that is part of a class?
The error happens becuase you are using pyqtSlot decorator in a class which doesn't inherit from QObject. You can fix the problem by either getting rid of the decorator, or making HandlerClass a subclass of QObject.
The main purpose of pyqtSlot is to allow several different overloads of a slot to be defined, each with a different signature. It may also be needed sometimes when making cross-thread connections. However, these use-cases are relatively rare, and in most PyQt applications it is not necessary to use pyqtSlot at all. Signals can be connected to any python callable object, whether it is decorated as a slot or not.

RobotFramework Monitoring Thread to Trigger Teardown

I am trying to have a monitoring thread in robotframework to continuously monitor a signal and force a teardown if a certain signal is read. I have the following code.
import signal
from datetime import datetime
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
import logging
import thread
import os
import sys
import time
def do_error_log_monitoring_new():
def _monitor_log():
time.sleep(5)
# Monitor_Some_Signal
builtin_lib.fatal_error(msg='Force Teardown')
thread_logger = logging.getLogger('root')
thread_logger.info("Started Fault Listener")
builtin_lib = BuiltIn().get_library_instance('BuiltIn', all=True)
thread.start_new_thread(_monitor_log, ())
I have builtin_lib = BuiltIn().get_library_instance('BuiltIn', all=True) with the all=True argument to return dictionary mapping of all library names to instances. However I see the following error:
AttributeError: 'dict' object has no attribute 'fatal_error'
Furthermore, removing the all=True argument allows the function to go through and fatal_error to trigger, however I notice that it does not trigger a teardown in the main thread, which is what I intend to do. Any advice on how I can trigger teardown in the main thread from the fatal_error() function in the secondary thread?
You're setting builtin_lib to a dictionary (the result of BuiltIn().get_library_instances(..., all=True). As the error message states, this dictionary does not have a fatal_error method.
Change this:
builtin_lib.fatal_error(msg='Force Teardown')
... to this:
BuiltIn().fatal_error(msg='Force Teardown')
If you only need a reference to the BuiltIn library (versus a dictionary of all libraries), you don't need to call get_library_instance at all since BuiltIn() already returns an instance of the library.

PyQt signal with Twisted: Cannot catch custom signal

Problem Description:
A server process which keeps a database (Cacheable)
A client which reads and displays the data in UI (RemoteCache)
They talk to each other through Twisted PB
I'd like to refresh my UI when the server database changes.
My client has a method, _mutation_handler, which is notified by the server
To notify my UI, I created a singleton Notifier class which emits a signal.
Then in my Qt widget, I wire the signal to the widget's slot.
# inside the RemoteCache subclass on my client
# notified by the PB server when something happens
def __mutation_handler(self):
notifier = Notifier()
notifier.notify()
# inside notify.py
class Notifier(QObject):
def __new__(cls):
# Make it a singleton
def notify(self):
self.emit(SIGNAL('SomethingChanged'))
# inside the RemoteCache subclass on my client
def __mutation_handler(self):
# singleton notifier
notifier = Notifier()
notifier.notify()
# inside my widget.py
class MyWidget(QWidget):
def __init__(self):
... # code
self.notifier = Notifier()
self._create_signal_slot_connections()
... # more code
def _create_signal_slot_connections(self):
self.connect(self.notifier, SIGNAL('SomethingChanged'),self.shout)
def shout(self):
print 'Server's database changed!!!'
Problem:
When I change something in my server's database, _mutation_handler gets called
correctly, then the signal is emitted alright.
However, MyWidget is NOT able to catch the signal.
Note: I am using qt4reactor to adapt Twisted's event loop to suit qt's
I really appreciate your help!!!
There seems to be a new API for signals in PyQt4. See http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/new_style_signals_slots.html for examples on its use. Perhaps this will work better.

Resources