How to create a websocket client by using QWebSocket in PyQt5 - python

I want create a websocket client by using QWebSocket in PyQt5.For convenience, assume I have a websocket server, source code is like this ,
from PyQt5 import QtCore, QtWebSockets, QtNetwork, QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QAction
from PyQt5.QtCore import QUrl
class MyServer(QtCore.QObject):
def __init__(self, parent):
super(QtCore.QObject, self).__init__(parent)
self.clients = []
self.server = QtWebSockets.QWebSocketServer(parent.serverName(), parent.secureMode(), parent)
if self.server.listen(QtNetwork.QHostAddress.LocalHost, 1302):
print('Connected: '+self.server.serverName()+' : '
+self.server.serverAddress().toString()+':'+str(self.server.serverPort()))
else:
print('error')
self.server.newConnection.connect(self.onNewConnection)
self.clientConnection = None
print(self.server.isListening())
def onNewConnection(self):
self.clientConnection = self.server.nextPendingConnection()
self.clientConnection.textMessageReceived.connect(self.processTextMessage)
self.clientConnection.binaryMessageReceived.connect(self.processBinaryMessage)
self.clientConnection.disconnected.connect(self.socketDisconnected)
print("newClient")
self.clients.append(self.clientConnection)
def processTextMessage(self, message):
print(message)
if self.clientConnection:
for client in self.clients:
# if client!= self.clientConnection:
client.sendTextMessage(message)
# self.clientConnection.sendTextMessage(message)
def processBinaryMessage(self, message):
print("b:",message)
if self.clientConnection:
self.clientConnection.sendBinaryMessage(message)
def socketDisconnected(self):
if self.clientConnection:
self.clients.remove(self.clientConnection)
self.clientConnection.deleteLater()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
serverObject = QtWebSockets.QWebSocketServer('My Socket', QtWebSockets.QWebSocketServer.NonSecureMode)
server = MyServer(serverObject)
serverObject.closed.connect(app.quit)
app.exec_()
It can create a websocket server,and I test it by using JavaScript,it works fine .But I can find a way to create a client by using Qwebsocket.My client code is like this:
client = QtWebSockets.QWebSocket("",QtWebSockets.QWebSocketProtocol.Version13,None)
client.open(QUrl("ws://127.0.0.1:1302"))
client.sendTextMessage("asd")
client.close()
It seems the server did not receive the message sent by client,how to create a websocket client and send message by using Qwebsocket?

This is a typical problem with qt console programs, you need to call your client methods outside of the python constructor (__init__).
I modified your server a little bit, adding some error tests (nothing really new):
from PyQt5 import QtCore, QtWebSockets, QtNetwork, QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QAction
from PyQt5.QtCore import QUrl
class MyServer(QtCore.QObject):
def __init__(self, parent):
super(QtCore.QObject, self).__init__(parent)
self.clients = []
print("server name: {}".format(parent.serverName()))
self.server = QtWebSockets.QWebSocketServer(parent.serverName(), parent.secureMode(), parent)
if self.server.listen(QtNetwork.QHostAddress.LocalHost, 1302):
print('Listening: {}:{}:{}'.format(
self.server.serverName(), self.server.serverAddress().toString(),
str(self.server.serverPort())))
else:
print('error')
self.server.acceptError.connect(self.onAcceptError)
self.server.newConnection.connect(self.onNewConnection)
self.clientConnection = None
print(self.server.isListening())
def onAcceptError(accept_error):
print("Accept Error: {}".format(accept_error))
def onNewConnection(self):
print("onNewConnection")
self.clientConnection = self.server.nextPendingConnection()
self.clientConnection.textMessageReceived.connect(self.processTextMessage)
self.clientConnection.textFrameReceived.connect(self.processTextFrame)
self.clientConnection.binaryMessageReceived.connect(self.processBinaryMessage)
self.clientConnection.disconnected.connect(self.socketDisconnected)
print("newClient")
self.clients.append(self.clientConnection)
def processTextFrame(self, frame, is_last_frame):
print("in processTextFrame")
print("\tFrame: {} ; is_last_frame: {}".format(frame, is_last_frame))
def processTextMessage(self, message):
print("processTextMessage - message: {}".format(message))
if self.clientConnection:
for client in self.clients:
# if client!= self.clientConnection:
client.sendTextMessage(message)
# self.clientConnection.sendTextMessage(message)
def processBinaryMessage(self, message):
print("b:",message)
if self.clientConnection:
self.clientConnection.sendBinaryMessage(message)
def socketDisconnected(self):
print("socketDisconnected")
if self.clientConnection:
self.clients.remove(self.clientConnection)
self.clientConnection.deleteLater()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
serverObject = QtWebSockets.QWebSocketServer('My Socket', QtWebSockets.QWebSocketServer.NonSecureMode)
server = MyServer(serverObject)
serverObject.closed.connect(app.quit)
app.exec_()
The client uses some QTimer to call the required methods outside of the __init__method. I also added ping / pong methods to check the connection :
import sys
from PyQt5 import QtCore, QtWebSockets, QtNetwork
from PyQt5.QtCore import QUrl, QCoreApplication, QTimer
from PyQt5.QtWidgets import QApplication
class Client(QtCore.QObject):
def __init__(self, parent):
super().__init__(parent)
self.client = QtWebSockets.QWebSocket("",QtWebSockets.QWebSocketProtocol.Version13,None)
self.client.error.connect(self.error)
self.client.open(QUrl("ws://127.0.0.1:1302"))
self.client.pong.connect(self.onPong)
def do_ping(self):
print("client: do_ping")
self.client.ping(b"foo")
def send_message(self):
print("client: send_message")
self.client.sendTextMessage("asd")
def onPong(self, elapsedTime, payload):
print("onPong - time: {} ; payload: {}".format(elapsedTime, payload))
def error(self, error_code):
print("error code: {}".format(error_code))
print(self.client.errorString())
def close(self):
self.client.close()
def quit_app():
print("timer timeout - exiting")
QCoreApplication.quit()
def ping():
client.do_ping()
def send_message():
client.send_message()
if __name__ == '__main__':
global client
app = QApplication(sys.argv)
QTimer.singleShot(2000, ping)
QTimer.singleShot(3000, send_message)
QTimer.singleShot(5000, quit_app)
client = Client(app)
app.exec_()
Server output:
G:\Qt\QtTests>python so_qwebsocket_server.py
server name: My Socket
Listening: My Socket:127.0.0.1:1302
True
onNewConnection
newClient
in processTextFrame
Frame: asd ; is_last_frame: True
processTextMessage - message: asd
socketDisconnected
client output:
G:\Qt\QtTests>python so_qwebsocket_client.py
client: do_ping
onPong - time: 0 ; payload: b'foo'
client: send_message
timer timeout
All in all, if you use your client in a simple GUI (e.g. moving the client.sendTextMessage() outside __init__ and connecting a button click to actually send the message), due to its asynchronous nature, it should work without any problems!

Related

How to integrate python Socket.IO with Qt

import sys
import socketio
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QApplication
class ServerConnection(QThread):
def __init__(self):
QThread.__init__(self)
self.sio = socketio.AsyncClient(reconnection=True, reconnection_attempts=3,
reconnection_delay=5, reconnection_delay_max=5, logger=True)
'thread run function'
def run(self) -> None:
self.sio.connect(url="...",socketio_path="/...", transports="websocket")
self.sio.on('connect', self.connect, namespace=None)
self.sio.on('socket_connected', self.socket_connected, namespace=None)
self.sio.on('connect_error', self.connect_error, namespace=None)
self.sio.on('/client_Unlock', self.client_unlock_ack, namespace=None)
# #sio.on('/client_unlock')
'custom event from server, on receiving, this socketio thread needs to inform main GUI'
async def client_unlock_ack(self, data):
print("")
'from here i want to call pyqt GUI main class function'
# #sio.event
'connection established status'
def connect(self):
print('Server Connection established!')
# #sio.on("socket_connected")
'socket connection status check'
async def socket_connected(self, message):
print("Socket Connected!", message)
# #sio.event
def connect_error(self, data):
print('Connection error!', data)
# #sio.on('disconnect' or 'socket_disconnected')
def disconnect(self):
print('Disconnected!')
'main function call'
def main():
app = QApplication(sys.argv)
con = ServerConnection()
con.start()
sys.exit(app.exec())
if __name__ == "__main__":
main()
'GUI class is a QMainWindow application which processes further other functionalities of my application. Socketio thread connection is triggered from GUI main thread. Basically, GUI main started->Socketio thread called from GUI thread-> once we receive socketio client-unlock event inside socketio thread class-> from client_unlock_ack, i need to trigger a signal to GUI main thread to call a member function'. Please guide.
It is not necessary to use threads since you can use libraries such as asyncqt so that Qt works together with asyncio avoiding adding unnecessary complexity with QThreads
import asyncio
import sys
from functools import cached_property
from PyQt5.QtCore import pyqtSignal, QObject, Qt
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow
from asyncqt import QEventLoop
import socketio
class Client(QObject):
connected = pyqtSignal()
disconnected = pyqtSignal()
error_ocurred = pyqtSignal(object, name="errorOcurred")
data_changed = pyqtSignal(str, name="dataChanged")
def __init__(self, parent=None):
super().__init__(parent)
self.sio.on("connect", self._handle_connect, namespace=None)
self.sio.on("connect_error", self._handle_connect_error, namespace=None)
self.sio.on("disconnect", self._handle_disconnect, namespace=None)
self.sio.on("/client_Unlock", self.client_unlock_ack, namespace=None)
#cached_property
def sio(self):
return socketio.AsyncClient(
reconnection=True,
reconnection_attempts=3,
reconnection_delay=5,
reconnection_delay_max=5,
logger=True,
)
async def start(self):
await self.sio.connect(url="...",socketio_path="/...", transports="websocket")
def _handle_connect(self):
self.connected.emit()
def _handle_disconnect(self):
self.disconnect.emit()
def _handle_connect_error(self, data):
self.error_ocurred.emit(data)
def client_unlock_ack(self, data):
self.data_changed.emit(data)
class View(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.label = QLabel(alignment=Qt.AlignCenter)
self.setCentralWidget(self.label)
self.resize(640, 480)
def update_data(self, message):
self.label.setText(message)
def main():
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
view = View()
view.show()
client = Client()
client.data_changed.connect(view.update_data)
with loop:
asyncio.ensure_future(client.start(), loop=loop)
loop.run_forever()
if __name__ == "__main__":
main()

Pass data to thread from GUI on runtime in python

I'm trying to build a little python application, where a simple webserver runs in the background, and you can the use GUI to send different messages.
I'm using PyQt5 and Python3.6, and I've managed to pass data from the working thread to the GUI, but I don't know how to do that the other way around.
Here's a skeleton of my code:
MainWindow:
class Ui_MainWindow(object):
def __init__(self):
super().__init__()
self.input = True
# 1 - create Worker and Thread inside the Form
self.obj = WebServer.WebServer(self.input) # no parent!
self.thread = QtCore.QThread() # no parent!
# 2 - Connect Worker`s Signals to Form method slots to post data.
self.obj.dataReady.connect(self.onDataReady)
# 3 - Move the Worker object to the Thread object
self.obj.moveToThread(self.thread)
# 4 - Connect Worker Signals to the Thread slots
self.obj.finished.connect(self.thread.quit)
# 5 - Connect Thread started signal to Worker operational slot method
self.thread.started.connect(self.obj.startServer)
# 6 - Start the thread
self.thread.start()
# 7 - Start the form
self.setupUi()
def setupUi(self):
# Set up the GUI
#...
self.MainWindow.show()
def onDataReady(self, data):
# Data received from working thread
# Do stuff...
def on_click_set2(self):
self.input = not self.input
print(self.input)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = Ui_MainWindow()
sys.exit(app.exec_())
WebServer:
class WebServer(QObject):
finished = pyqtSignal()
dataReady = pyqtSignal(dict)
def __init__(self, input):
super().__init__()
self.input = input
#pyqtSlot()
def startServer(self): # A slot takes no params
# self.loop = asyncio.get_event_loop()
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
coro = asyncio.start_server(self.handle_update, '192.168.2.1', 8888, loop=self.loop)
self.server = self.loop.run_until_complete(coro)
# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(self.server.sockets[0].getsockname()))
try:
self.loop.run_forever()
except KeyboardInterrupt:
pass
self.finished.emit()
async def handle_update(self, reader, writer):
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
print(f'Received: {message} from {addr}')
if self.input:
print("Input is True")
else:
print("Input is False")
reply = self.input
print(f'Send: {reply}')
writer.write(str(reply).encode())
await writer.drain()
print("Close the client socket")
writer.close()
self.dataReady.emit(reply)
So for example I want to pass input to the thread, and if I do like above (obviously) input always stays the initial value, and won't change in the thread when I hit the button on the GUI.
How can I do it, that the value of the input is updated whenever I hit the button (so passing a value to the thread from GUI during runtime)? I assume is similar to passing from the thread to the GUI, so emitting a signal from GUI and connecting to it on the tread, but I don't know how to find a reference to the GUI from the working thread.
So any advice on how to do that? And of course any other input regarding the code or approach to the application/background server solution is welcomed! Thanks for the help in advance!
UPDATE:
Maybe it wasn't clear what my question was, so here it is:
How can I send a value from the GUI thread to the worker thread while both of them are running parallel? (If the above code makes any sense to you use that as an example, otherwise a general example would be appreciated)
It is not necessary that the WebServer live in another thread, it is only necessary that the eventloop be executed in another thread. In this case, the WebServer exchanges the data with the server thread through a queue. Although the QObjects are not thread-safe, the signals are so, there is no problem in emitting the signal from a thread other than the one that the QObject lives
import asyncio
import threading
import queue
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
class WebServer(QtCore.QObject):
dataReady = QtCore.pyqtSignal(object)
def startServer(self):
self.m_loop = asyncio.new_event_loop()
self.m_queue = queue.Queue()
asyncio.set_event_loop(self.m_loop)
coro = asyncio.start_server(
self.handle_update, "127.0.0.1", 10000, loop=self.m_loop
)
self.server = self.m_loop.run_until_complete(coro)
print("Serving on {}".format(self.server.sockets[0].getsockname()))
threading.Thread(target=self.m_loop.run_forever, daemon=True).start()
#QtCore.pyqtSlot(object)
def setData(self, data):
if hasattr(self, "m_queue"):
self.m_queue.put(data)
def stop(self):
if hasattr(self, "m_loop"):
self.m_loop.stop()
async def handle_update(self, reader, writer):
reply = ""
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info("peername")
print(f"Received: {message} from {addr}")
if not self.m_queue.empty():
data = self.m_queue.get(block=False)
reply = data
print(f"Send: {reply}")
writer.write(str(reply).encode())
await writer.drain()
print("Close the client socket")
writer.close()
self.dataReady.emit(reply)
class Widget(QtWidgets.QWidget):
dataChanged = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super().__init__(parent)
self.m_lineedit = QtWidgets.QLineEdit()
button = QtWidgets.QPushButton("Send", clicked=self.onClicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.m_lineedit)
lay.addWidget(button)
self.m_web = WebServer()
self.m_web.startServer()
self.dataChanged.connect(self.m_web.setData)
#QtCore.pyqtSlot()
def onClicked(self):
text = self.m_lineedit.text()
self.dataChanged.emit(text)
#QtCore.pyqtSlot(object)
def onDataReady(self, data):
print(data)
def closeEvent(self, event):
self.m_web.stop()
super().closeEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Python QtNetwork.QTcpSocket.readAll() and QtNetwork.QTcpSocket.write() not working

I'm doing a simple socket connection and the read.All() and write() are not giving me anything back.
import PySide.QtNetwork as Network
import PySide.QtCore as Core
proxyAddress = '127.0.0.1'
proxyPort = 1025
tcpSocket = Network.QTcpSocket()
tcpSocket.connectToHost(proxyAddress, proxyPort)
tcpSocket.state() gives
"PySide.QtNetwork.QAbstractSocket.SocketState.ConnectingState"
When I then try to send something, I get this back:
tcpSocket.write("Hello")
5L
And for readAll():
tcpSocket.readAll()
PySide.QtCore.QByteArray('')
Oh and to disconnect I try:
tcpSocket.disconnectFromHost()
and it comes back with the next line to write as if it was successful in disconnecting but when I check the state:
tcpSocket.state()
"PySide.QtNetwork.QAbstractSocket.SocketState.ConnectingState"
Any help is appreciated.
First of all the state that signals is ConnectingState that indicates that the connection has not been made so it is not correct to perform any task at that moment, you must wait for the status to be ConnectedState. On the other hand, the port you point out is probably being used by another application since it is a small number that are usually reserved for another task. In the next part I show an example of a server and a client (first launch the server and then you can launch the number of clients you want.). Finally in Qt the tasks should not be performed synchronously but through signals since the Qt event-loop needs to update internal and external variables states.
server.py
import uuid
from PySide import QtCore, QtNetwork
class ServerManager(QtCore.QObject):
def __init__(self, parent=None):
super(ServerManager, self).__init__(parent)
self._server = QtNetwork.QTcpServer(self)
self._server.newConnection.connect(self.on_newConnection)
self._clients = {}
def launch(self, address=QtNetwork.QHostAddress.Any, port=9999):
return self._server.listen(QtNetwork.QHostAddress(address), port)
#QtCore.Slot()
def on_newConnection(self):
socket = self._server.nextPendingConnection()
socket.readyRead.connect(self.on_readyRead)
if socket not in self._clients:
self._clients[socket] = uuid.uuid4()
#QtCore.Slot()
def on_readyRead(self):
socket = self.sender()
resp = socket.readAll()
code = self._clients[socket]
print("From[{}]- message: {}".format(code, resp))
socket.write("Server: " + str(resp)[::-1])
if __name__ == '__main__':
import sys
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtCore.QCoreApplication(sys.argv)
address = '127.0.0.1'
port = 9000
server = ServerManager()
if not server.launch(address, port):
sys.exit(-1)
sys.exit(app.exec_())
client.py
from PySide import QtCore, QtNetwork
class ClientManager(QtCore.QObject):
def __init__(self, parent=None):
super(ClientManager, self).__init__(parent)
self._socket = QtNetwork.QTcpSocket(self)
self._socket.stateChanged.connect(self.on_stateChanged)
self._socket.readyRead.connect(self.on_readyRead)
self._timer = QtCore.QTimer(self, interval=1000)
self._timer.timeout.connect(self.sendMessage)
def launch(self, address=QtNetwork.QHostAddress.Any, port=9999):
return self._socket.connectToHost(QtNetwork.QHostAddress(address), port)
#QtCore.Slot(QtNetwork.QAbstractSocket.SocketState)
def on_stateChanged(self, state):
if state == QtNetwork.QAbstractSocket.ConnectedState:
self._timer.start()
print("connected")
elif state == QtNetwork.QAbstractSocket.UnconnectedState:
print("disconnected")
QtCore.QCoreApplication.quit()
#QtCore.Slot()
def sendMessage(self):
if self._socket.state() == QtNetwork.QAbstractSocket.ConnectedState:
msg = QtCore.QDateTime.currentDateTime().toString()
self._socket.write(msg)
#QtCore.Slot()
def on_readyRead(self):
print("Response: ", self._socket.readAll())
if __name__ == '__main__':
import sys
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtCore.QCoreApplication(sys.argv)
address = '127.0.0.1'
port = 9000
server = ClientManager()
server.launch(address, port)
sys.exit(app.exec_())

Non-blocking client FTP retrieval and write

I am building an FTP test server and client with Twisted. The server runs great. It is basically the same from the Twisted ftpserver.py example. The client is where I am having some blocking issues during my file retrieval and writing. I have tried to resolve it through some quick Twisted threading utilities, but to no avail.
Here is my server:
#!/usr/bin/env python2
from __future__ import print_function, division, absolute_import
# Import twisted things
from twisted.protocols.ftp import FTPFactory
from twisted.protocols.ftp import FTPRealm
from twisted.internet import reactor
from twisted.cred.portal import Portal
from twisted.cred.checkers import AllowAnonymousAccess
p = Portal(FTPRealm("test/"), [AllowAnonymousAccess()])
f = FTPFactory(p)
f.timeOut = None
reactor.listenTCP(5504, f)
reactor.run()
The client side, paired with this, is a simple wxPython GUI that presents a text box to write the name of the file you want to retrieve in. Within this GUI there is a wx.Timer that executes a method every 50 milliseconds. This is what is blocking my FTP file retrieval. I find that because the main thread is being used up, the protocol that receives the data is having hiccups. If you are wondering why I have this setup I am simulating the use case for a much larger project.
My attempt to solve this has been to use deferToThread on the specific point when I need to retrieve a file. However, by printing the current thread I find that the protocol that is receiving the data is running in the main thread. This is the problem I am trying to solve. Any help is much appreciated.
My client code:
#!/usr/bin/env python2
from __future__ import print_function, division, absolute_import
import wx
import sys
import threading
from twisted.internet import wxreactor
wxreactor.install()
from twisted.internet import reactor
from twisted.protocols.ftp import FTPClient
from twisted.internet import protocol
from twisted.internet import threads
from twisted.python import log
# This is the GUI
class TextSend(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Request Files", size=(200, 75))
self.protocol = None # ftp client protocol
self.factory = None
panel = wx.Panel(self)
vertSizer = wx.BoxSizer(wx.VERTICAL)
horzSizer = wx.BoxSizer(wx.HORIZONTAL)
self.fileName = None
self.textbox = wx.TextCtrl(parent=panel, id=100, size=(100,-1))
self.btn = wx.Button(panel, label="Retr.")
# timer and checkbox for timer
self.timer = wx.Timer(self, id=wx.ID_ANY)
self.check = wx.CheckBox(parent=panel, label="Start blocking")
#Bind
self.textbox.Bind(wx.EVT_TEXT, self.getText)
self.btn.Bind(wx.EVT_BUTTON, self.press)
self.check.Bind(wx.EVT_CHECKBOX, self.onCheck)
self.Bind(wx.EVT_TIMER, self.onTimer, self.timer)
horzSizer.Add(self.textbox, flag=wx.ALIGN_CENTER)
horzSizer.Add(self.btn, flag=wx.ALIGN_CENTER)
vertSizer.Add(horzSizer, flag=wx.ALIGN_CENTER)
vertSizer.Add(self.check, flag=wx.ALIGN_CENTER)
panel.SetSizer(vertSizer)
panel.Layout()
def getText(self, evt):
self.fileName = str(self.textbox.GetValue())
def onCheck(self, evt):
yes = self.check.GetValue()
if yes:
print("Starting timer")
self.timer.Start(50)
else: # no
self.timer.Stop()
def onTimer(self, evt):
#print("Triggered timer")
pass
def press(self, evt):
print("Send:", self.fileName)
d = threads.deferToThread(self.retrieve)
d.addCallback(self.done)
def retrieve(self):
print(threading.current_thread())
# This is what does the retrieving. Pass in FileWriter and
# FileWriter's dataReceived method is called by main thread
self.protocol.retrieveFile(self.fileName, FileWriter(self.fileName), offset=0).addCallbacks(self.done, self.fail)
return "Done with deferToThread"
def done(self, msg):
print(threading.current_thread())
print("DONE Retrieving:", msg)
def fail(self, error):
print('Failed. Error was:')
print(error)
# This writes to the file of a same name as the one retrieved.
class FileWriter(protocol.Protocol):
def __init__(self, fileName):
self.f = open(fileName, 'wb')
print("FROM FileWriter __init__:", threading.current_thread())
def dataReceived(self, data):
print("Byte size", len(data))
print("FROM FileWriter dataReceived:", threading.current_thread())
self.f.write(data)
def connectionLost(self, reason):
print("Writing closed and done")
print("FROM FileWriter connectionLost:", threading.current_thread())
self.f.close()
# Client FTP Protocol
class TestClient(FTPClient, object):
def __init__(self, factory, username, password, passive):
super(TestClient, self).__init__(username=username, password=password, passive=passive)
self.factory = factory
def connectionMade(self):
print("hello")
gui = self.factory.gui
gui.protocol = self
# Twisted Client Factory
class FileClientFactory(protocol.ClientFactory):
def __init__(self, gui):
self.gui = gui
self.protocol = None
def buildProtocol(self, addr):
user = 'anonymous'
passwd = 'twisted#matrix.com'
self.protocol = TestClient(self, username=user, password=passwd, passive=1)
return self.protocol
def clientConnectionLost(self, transport, reason):
print("Connectiong lost normally:", reason)
def clientConnectionFailed(self, transport, reason):
print("Connection failed:", reason)
if __name__ == "__main__":
# Initialize and show GUI
logger = log.startLogging(sys.stdout)
app = wx.App(False)
app.frame = TextSend()
app.frame.Show()
reactor.registerWxApp(app)
# Build Factory
f = FileClientFactory(app.frame)
# Connect to FTP server
reactor.connectTCP("localhost", 5504, f)
reactor.run()
wxPython main loop.
app.MainLoop()
You cannot deferToThread(function_that_uses_twisted_apis). Twisted APIs are almost all non-thread-safe. You must use them in the reactor thread only (the exceptions are a couple thread-scheduling-related APIs).
Instead, get rid of your blocking code. Put it into another thread, another process, or rewrite it to be non-blocking.

twisted transport.write in a non-reactor thread

I am designing a gui application as a client for a server where i have 3 classes
a class for twisted protocol , another for the factory and the third for the pyqt thread it self. how can i send data from the pyqt thread , as example if a button was clicked how can i send a command using the current twisted connection, here is a copy of my codes
from OpenSSL import SSL;import sys
from twisted.internet.protocol import ClientFactory
from twisted.internet import ssl, protocol
from PyQt4 import QtCore, QtGui
from gui import Ui_clientgui
class clientgui(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_clientgui()
self.ui.setupUi(self)
def closeEvent(self, e):
try:reactor.callFromThread(reactor.stop)
except:pass
class Client(protocol.Protocol):
def connectionMade(self):
global server_options
server_options['connection'] = True
def send(self,data):
self.transport.write(data)
def connectionLost(self, reason):
server_options['connection'] = False
def dataReceived(self, line):
print "receive:", line
class ClientFactory(ClientFactory):
protocol = Client
def clientConnectionFailed(self, connector, reason):
print 'connection failed'
try:reactor.stop()
except:pass
if __name__ == '__main__':
app = QtGui.QApplication([])
import qt4reactor
qt4reactor.install()
from twisted.internet import reactor
factory = ClientFactory()
reactor.connectSSL('localhost', 8080, factory, ssl.ClientContextFactory())
application = clientgui(reactor)
application.show()
reactor.runReturn()
sys.exit(app.exec_())
If you use qt4reactor, you don't need to mess around with multiple threads at all; Twisted will run in the QT thread and they can freely touch each others' data structures.
If you want to do this with threads though, your example already contains the solution: reactor.callFromThread. (I would recommend doing callFromThread(client.send) rather than callFromThread(transport.write) because the Client is a Protocol and therefore lives in Twisted's universe more than in Qt's.

Categories

Resources