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
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)
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)
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 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.
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()