pyqt5 QThread with SOAP Zeep Client - multithreading

I am working on a simple application with GUI which connect to a server via SOAP and request some data, this data is returned in XML format
I was able to run in my application successfully but due to threading, GUI is freezes till the complete SOAP request is finished and returned its value
now I am trying to run my application into threading, I created a Thread which to first check and verify the SOAP connection if it is successful or not
Connection Verification
class WorkerThread(QThread):
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
def run(self):
self.session = Session()
self.session.verify = False
self.cucmurl = 'https://URL'
self.session.auth = HTTPBasicAuth(user, pass)
self.cache = SqliteCache(path='/tmp/sqlite.db', timeout=10)
self.trself.clientansport = Transport(session=self.session, cache=self.cache)
self.client = Client(wsdl, transport=self.transport, strict=False)
the above work fine to verify the connection, but I want to use the self.client later in my code in order to start initiating SOAP connection to severs
class MainAPP(QTabWidget, UI_File):
def __init__(self, parent=None):
def getinfor(self):
output_2 = self.client.getinfor('')
the function getinfor should be able to use the self.client from the WorkerThread.
any idea how to accomplish this

You can emit the client through a signal from the worker thread and set it in the main window.
class WorkerThread(QThread):
client_signal = pyqtSignal(object)
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
def run(self):
# code here
self.client = Client(wsdl, transport=self.transport, strict=False)
self.client_signal.emit(self.client)
class MainApp(QTabWidget, UI_File):
def __init__(self):
self.client = None
def set_client(self, client):
self.client = client
def setup_thread(self):
self.worker_thread = WorkerThread()
self.worker_thread.client_signal.connect(self.set_client)

Related

Locust how to set the timeout for request of the custom client. Example : Exception should be caught if the response time is greater than 1 ms

class XmlRpcClient(ServerProxy):
"""
XmlRpcClient is a wrapper around the standard library's ServerProxy.
It proxies any function calls and fires the request event when they finish,
so that the calls get recorded in Locust.
"""
def __init__(self, host, request_event):
super().__init__(host)
self._request_event = request_event
def __getattr__(self, name):
func = ServerProxy.__getattr__(self, name)
def wrapper(*args, **kwargs):
request_meta = {
"request_type": "xmlrpc",
"name": name,
"start_time": time.time(),
"response_length": 0, # calculating this for an xmlrpc.client response would be too hard
"response": None,
"context": {}, # see HttpUser if you actually want to implement contexts
"exception": None,
}
start_perf_counter = time.perf_counter()
try:
request_meta["response"] = func(*args, **kwargs)
except Fault as e:
request_meta["exception"] = e
request_meta["response_time"] = (time.perf_counter() - start_perf_counter) * 1000
self._request_event.fire(**request_meta) # This is what makes the request actually get logged in Locust
return request_meta["response"]
return wrapper
class XmlRpcUser(User):
"""
A minimal Locust user class that provides an XmlRpcClient to its subclasses
"""
abstract = True # dont instantiate this as an actual user when running Locust
def __init__(self, environment):
super().__init__(environment)
self.client = XmlRpcClient(self.host, request_event=environment.events.request)
The real user class that will be instantiated and run by Locust
This is the only thing that is actually specific to the service that we are testing.
class MyUser(XmlRpcUser):
host = "http://127.0.0.1:8877/"
#task
def get_time(self):
self.client.get_time()
#task
def get_random_number(self):
self.client.get_random_number(0, 100)

Can i subscribe to a topic after i started loop_forever() in paho_mqtt?

Do i need to subscribe to all topics i am interested in before i execute the loop_forever() function? E.g. to somehow dynamically add more subscriptions during the life time of the MQTT client.
I found out that it is possible. I implemented a class which hides the mqtt stuff. In the constructor i connect to the broker and start the loop_forever() with a seperate thread. And afterwards that class subscribes to some topics and register a callback for each topic, which are called from on_message.
c = comm("localhost")
c.register_handler("topic1", first_handler)
c.register_handler("topic2", second_handler)
class comm:
def __init__(self, broker_address):
self.client = mqtt.Client("")
self.client.on_message = self.on_message
self.callbacks = dict()
self.client.connect(broker_address)
threading.Thread(target=self.__run, daemon=True).start()
def __run(self):
self.client.loop_forever()
def on_message(self, client, userdata, msg):
self.callbacks[msg.topic](jsonpickle.decode(msg.payload))
def on_connect(self, client, userdata, flags, rc):
for topic in self.callbacks:
self.client.subscribe(topic)
def register_handler(self, topic: str, handler: Callable[[Dict], None]):
self.callbacks[topic] = handler
self.client.subscribe(topic)

Send data from an other class through ws after client connected

I want to send data through websockets as soon as a client is connected.
The Data is at an other place then the Websocket Handler. How can i get the data to the client ?
The server should hold the loop and the Handler. In the connector i connect to a tcp socket to get the data out of some hardware. I expect to have not more then 6 Websockets open once a time. The Data comes as a stream out of the TCP socket.
server.py
import os
from tornado import web, websocket
import asyncio
import connector
class StaticFileHandler(web.RequestHandler):
def set_default_headers(self):
self.set_header("Access-Control-Allow-Origin", "*")
def get(self):
self.render('index.html')
class WSHandler(websocket.WebSocketHandler):
def open(self):
print('new connection')
self.write_message("connected")
def on_message(self, message):
print('message received %s' % message)
self.write_message("pong")
def on_close(self):
print('connection closed')
public_root = 'web_src'
handlers = [
(r'/', StaticFileHandler),
(r'/ws', WSHandler),
]
settings = dict(
template_path = os.path.join(os.path.dirname(__file__), public_root),
static_path = os.path.join(os.path.dirname(__file__), public_root),
debug = True
)
app = web.Application(handlers, **settings)
sensorIP = "xxx.xxx.xxx.xxx"
if __name__ == "__main__":
app.listen(8888)
asyncio.ensure_future(connector.main_task(sensorIP))
asyncio.get_event_loop().run_forever()
connector.py
import yaml
import asyncio
class RAMReceiver:
def __init__(self, reader):
self.reader = reader
self.remote_data = None
self.initParams = None
async def work(self):
i = 0
while True:
data = await self.reader.readuntil(b"\0")
self.remote_data = yaml.load(data[:-1].decode("utf-8",
"backslashreplace"))
# here i want to emit some data
# send self.remote_data to websockets
if i == 0:
i += 1
self.initParams = self.remote_data
# here i want to emit some data after open event is
# triggered
# send self.initParams as soon as a client has connected
async def main_task(host):
tasks = []
(ram_reader,) = await asyncio.gather(asyncio.open_connection(host,
51000))
receiver = RAMReceiver(ram_reader[0])
tasks.append(receiver.work())
while True:
await asyncio.gather(*tasks)
You can use Tornado's add_callback function to call a method on your websocket handler to send the messages.
Here's an example:
1. Create an additional method on your websocket handler which will receive a message from connector.py and will send to connected clients:
# server.py
class WSHandler(websocket.WebSocketHandler):
# make it a classmethod so that
# it can be accessed directly
# from class without `self`
#classmethod
async def send_data(cls, data):
# write your code for sending data to client
2. Pass the currently running IOLoop and WSHandler.send_data to your connector.py:
# server.py
from tornado import ioloop
...
if __name__ == "__main__":
...
io_loop = ioloop.IOLoop.current() # current IOLoop
callback = WSHandler.send_data
# pass io_loop and callback to main_task
asyncio.ensure_future(connector.main_task(sensorIP, io_loop, callback))
...
3. Then modify main_task function in connector.py to receive io_loop and callback. Then pass io_loop and callback to RAMReceiver.
4. Finally, use io_loop.add_callback to call WSHandler.send_data:
class RAMReceiver:
def __init__(self, reader, io_loop, callback):
...
self.io_loop = io_loop
self.callback = callback
async def work(self):
...
data = "Some data"
self.io_loop.add_callback(self.callback, data)
...

How i get a dynamic url in my websocket tornado?

How do I get the dynamic url passed by js in my websocket, do I need it to pass by parameter to the pc.connect() class that today does not receive anything but in the future will it?
webosocket:
class MyWebSocketHandler(websocket.WebSocketHandler):
def allow_draft76(self):
# for iOS 5.0 Safari
return True
def check_origin(self, origin):
return True
def open(self, *args, **kwargs):
self.application.pc.add_event_listener(self)
print ("WebSocket opened")
def on_close(self):
print ("WebSocket closed")
self.application.pc.remove_event_listener(self)
def main():
parse_command_line()
aplication = tornado.web.Application([
(r'.*', MyWebSocketHandler)
])
pc = PikaClient(io_loop)
aplication.pc = pc
aplication.pc.connect()
aplication.listen(8081)
io_loop.start()
if __name__ == '__main__':
main()
html:
const websocket = new WebSocket('ws://192.168.99.100:8081/${this.$route.params.uri}')
In the application, define the route like this:
(r'/(.*)', MyWebSocketHandler),
The parentheses define a capturing group which will be passed to open() as args[0].
Also note that the allow_draft76 method was removed in Tornado 4.0, so there is no longer any reason to include this method in your handler.

Error when passing argument to QThread subclass in PyQt5

I'm new in PyQt5, there is something wrong when I tried to pass arguments to a QThread subclass. Here is the code:
class DrClientThread(QtCore.QThread):
def __int__(self, server, username, password):
QtCore.QThread.__init__(self)
self.server = server
self.username = username
self.password = password
def run(self):
print(self.username, self.password, self.server)
class DrCOM_UI(Ui_Login_Dialog):
def __init__(self, dialog):
Ui_Login_Dialog.__init__(self)
self.setupUi(dialog)
self.login_btn.clicked.connect(self.login_and_keep_alive)
...
def login_and_keep_alive(self):
server, username, password = self.write_data_before_login()
self.drclient = DrClientThread(server, username, password)
self.drclient.start()
When I run the code, it gave me the exception:
in login_and_keep_alive
self.mydrclient = DrClientThread(server, username, password)
builtins.TypeError: QThread(QObject parent=None): argument 1 has unexpected type 'str'
I did some google work, and read almost every question on stackoverflow related to this problem, but I can't find out a solution. Actually, I want to do like this: How can I pass arguments to QThread Worker class?
Anyone can help me?
class DrClientThread(QtCore.QThread):
def __int__(self, server, username, password):
QtCore.QThread.__init__(self)
self.server = server
self.username = username
self.password = password
def run(self):
print(self.username, self.password, self.server)
class DrCOM_UI(Ui_Login_Dialog):
def __init__(self, dialog):
Ui_Login_Dialog.__init__(self)
self.setupUi(dialog)
self.login_btn.clicked.connect(self.login_and_keep_alive)
...
def login_and_keep_alive(self):
server, username, password = self.write_data_before_login()
self.drclient = DrClientThread(self, server, username, password) # <<-----
self.drclient.start()

Resources