PyQt SimpleHTTPServer: GUI freezes on starting server - python

I am trying to create a simple desktop app using PyQt that runs a SimpleHTTPServer on clicking a start server button. I have tried using threads(both python threads and Qthread) and understand that this is not possible as it runs into issues with the GIL. Here's the code
def btn_startserver_clicked(self):
server_thread=threading.Thread(target=start_server())
server_thread.start()
def start_server():
#to get server's IP
host=([(s.connect(('8.8.8.8', 80)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1])
start=8000
end=56999
PORT = random.randint(start,end)
print host,":",PORT
httpd=ThreadedServer(("",PORT), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd.handle_request()`
I tried creating another process but the same thing happened. Also, if I create another process a new window pops up each time a request is served.
def btn_startserver_clicked(self):
if __name__=='__main__':
server_process=Process(target=start_server())
server_process.start()
Is there any way around this? I feel using multiprocessing is the right approach but I am new to this and can't figure out why it still freezes.
Thanks

The issue with your examples that lock the GUI is that rather than passing a reference to the function when creating the thread, you are actually running the function immediately and the thread is never created. For example, you should be doing:
server_thread=threading.Thread(target=start_server)
Note that I drop the brackets on start_server otherwise the code waits for start_server() to finish executing before creating the threading.Thread object, and uses the return value from start_server() as the value for the target attribute.
A final suggestion, you should really store the created thread as self.server_thread to prevent it from being garbage collected.

Not exactly sure what you're trying to do, but this might help you get started:
import sys
from urllib.request import urlopen
from http.server import HTTPServer, SimpleHTTPRequestHandler
from PyQt4 import QtCore, QtGui
HOST, PORT = '127.0.0.1', 12345
class HttpDaemon(QtCore.QThread):
def run(self):
self._server = HTTPServer((HOST, PORT), SimpleHTTPRequestHandler)
self._server.serve_forever()
def stop(self):
self._server.shutdown()
self._server.socket.close()
self.wait()
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtGui.QPushButton('Start', self)
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
self.httpd = HttpDaemon(self)
def handleButton(self):
if self.button.text() == 'Start':
self.httpd.start()
self.button.setText('Test')
else:
urlopen('http://%s:%s/index.html' % (HOST, PORT))
def closeEvent(self, event):
self.httpd.stop()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Related

Update a label while inside a loop. PyQT

So I have a TCP server, which just echo's back whatever I send to it. And I have a GUI client which sends the stuff. But since I have to maintain the connection I can't seem to get the label I want to change once in a certain amount of time, I was trying to use signals but I seem to be doing something terribly wrong since the application freezes as soon as I connect to the server on button click. Here's what I got so far. Also, eventually I need to get 2 servers to echo to the client the information, and I guess that will pose even more of a problem and I will have to use multithreading.
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import *
import sys
import socket
import time
class MyClient(QMainWindow):
updateText = QtCore.pyqtSignal(str)
def __init__(self):
super(MyClient, self).__init__()
self.setGeometry(200, 200, 300, 300)
self.setWindowTitle("Krs")
self.initSocket()
self.initUI()
def initSocket(self):
self.ClientSocket = socket.socket()
self.s1 = ['127.0.0.1', 1233]
def initUI(self):
self.label = QLabel(self)
self.label.setText("nuthin")
self.label.move(50,50)
self.updateText.connect(self.label.setText)
self.b1 = QtWidgets.QPushButton(self)
self.b1.setText("S1")
self.b1.clicked.connect(lambda: self.conntoS(self.s1))
def conntoS(self, addrs):
self.ClientSocket.connect((addrs[0], addrs[1]))
while True:
time.sleep(1)
self.ClientSocket.send(str.encode("Anything"))
Response = self.ClientSocket.recv(1024)
self.upd(Response.decode('utf-8'))
#QtCore.pyqtSlot()
def upd(self, txt):
self.updateText.emit(txt)
def window():
app = QApplication(sys.argv)
win = MyClient()
win.show()
sys.exit(app.exec_())
window()
You should not run while True loop with time.sleep() inside gui thread, because it freezes the eventloop: while it run, widgets have no chance to recieve any events, and no chance to handle them - no repaints, no signals can be emited, no handlers invoked. All long operations should be performed outside of the gui thread.
You need to create worker, move your blocking function to it and run it separate QThread, or in your case you can use QTcpSocket asyncronous signal-slot api.
Example of the right way to use QThread in PyQt?
Python QTcpSocket and QTcpServer receiving message

QThread: Threading with GUI python

I am making mini chat application to improve my socket and GUI skills in Python. But my QThread is dumping every time when I'm launching application.
I need to run Thread with GUI
Here it is client.py
import socket
import sys
from colorama import Fore, Back, Style
from os import system, name
import sys
from chat import Ui_Form as Ui_Form
from login import Ui_Form as Ui_Form1
from PyQt5 import QtCore, QtGui, QtWidgets
from threading import Thread
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
app = QtWidgets.QApplication(sys.argv)
Form_login = QtWidgets.QWidget()
ui_login = Ui_Form1()
ui_login.setupUi(Form_login)
Form_login.show()
Form_chat = QtWidgets.QWidget()
ui_chat = Ui_Form()
ui_chat.setupUi(Form_chat)
history = ''
class listen_thread(QtCore.QObject):
running = False
listen_var = QtCore.pyqtSignal(str)
def run():
print('checkpoint')
while True:
listen_var.emit('message')
QtCore.QThread.msleep(1000)
def connect_pressed():
username = ui_login.lineEdit.text()
Form_login.hide()
Form_chat.show()
sock.connect(('127.0.0.1', 10000))
thread = QtCore.QThread()
listen1 = listen_thread()
listen1.moveToThread(thread)
listen1.listen_var.connect(update_chat_history)
thread.started.connect(listen1.run)
thread.start()
#QtCore.pyqtSlot(str)
def update_chat_history(message):
print(message)
ui_chat.textEdit_2.append(message)
def send_pressed():
message = ui_login.lineEdit.text() + ' > ' + ui_chat.lineEdit.text()
sock.send(bytes(str(message),'utf-8'))
# update_chat_history(message)
ui_chat.lineEdit.setText('')
def listen(some):
while True:
try:
data = sock.recv(1024)
except:
pass
else:
update_chat_history(str(data))
ui_login.pushButton.clicked.connect(connect_pressed)
ui_chat.pushButton.clicked.connect(send_pressed)
sys.exit(app.exec_())
When I'm launching it the output is:
QThread: Destroyed while thread is still running
Aborted (core dumped)
Can somebody help???
The explanation of your problem is simple: the thread created in connect_pressed has no reference outside that function, so it gets immediately garbage collected as soon as the function returns.
The "simple" solution would be to create the thread outside of that function and run the thread only there, or add the thread to a container (for instance, a list):
threads = []
def connect_pressed():
# ...
threads.append(thread)
But, the reality, is that your code has other serious issues, which would probably create other problems as soon as you try to expand your code even minimally, and that's also because Qt is easier to deal with when implementing the program using subclasses; even the pyqtSlot decorator (which is normally unnecessary) can only correctly work if it's set on a method of a QObject subclass.
Using only anonymous functions is usually discouraged, as they cannot have a direct reference to instances they must work with. Also, your run() function is wrong, as it doesn't have the self argument, which will result in a crash.
A better version of your code could be similar to this:
class LoginWindow(QtWidgets.QWidget, Ui_Form1):
def __init__(self, listenThread):
super().__init__()
self.setupUi(self)
self.pushButton.clicked.connect(listenThread.start)
class ChatWindow(QtWidgets.QWidget, Ui_Form):
def __init__(self, listenThread, loginWindow):
super().__init__()
self.listenThread = listenThread
self.loginWindow = loginWindow
self.setupUi(self)
self.listenThread.listen_var.connect(self.update_chat_history)
def update_chat_history(self, message):
print(message)
self.textEdit_2.append(message)
def send_pressed(self):
message = self.loginWindow.lineEdit.text() + ' > ' + self.lineEdit.text()
sock.send(bytes(str(message),'utf-8'))
self.lineEdit.setText('')
class ListenThread(QtCore.QThread):
listen_var = QtCore.pyqtSignal(str)
def run(self):
print('checkpoint')
while True:
listen_var.emit('message')
QtCore.QThread.msleep(1000)
listenThread = ListenThread()
loginWindow = LoginWindow(listenThread)
chatWindow = ChatWindow(listenThread, loginWindow)
sys.exit(app.exec_())

Continuously Check SQLAlchemy Database Connection on Pyside2

First of all, I want to figure out how to check database status every second. so that the user will able to tell if the database is up or not without even clicking or triggering anything. I've read that this will create a problem as mentioned in the comments here
so here's my minimal reproducible example:
import sys
import os
import shiboken2
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtWidgets import QMainWindow, QFileDialog, QMessageBox, QWidget, QDialog, QProxyStyle
from sqlalchemy import create_engine, inspect
class MyWidget(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.resize(200, 200)
self.path = os.path.abspath(os.path.dirname(sys.argv[0]))
self.button = QtWidgets.QPushButton("Open File")
self.labelFile = QtWidgets.QLabel("empty")
self.labelData = QtWidgets.QLabel("None")
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.button)
self.layout.addWidget(self.labelFile)
self.layout.addWidget(self.labelData)
self.setLayout(self.layout)
self.button.clicked.connect(self.open_file)
self.process = None
self.CreateEngine = CreateEngine(self)
self.CreateEngine.result.connect(self.start_timer)
self.CreateEngine.start()
def open_file(self):
x = QFileDialog.getOpenFileName(self,"Just To Spice This Code",self.path,"CSV Files (*.csv)")
self.labelFile.setText(x[0]) #just to check that GUI doesn't freeze
def start_timer(self,engine): #callback from CreateEngine
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(lambda: self.continuously_check(engine))
self.timer.start(1000) #check connetion every second, as real-time as possible
def continuously_check(self,engine): #this gonna get called every second, yes it isn't effective i know
self.process = CheckConnection(self,engine)
self.process.result.connect(self.update_connection_label)
self.process.start()
def update_connection_label(self,x): #update connection status on GUI
self.labelData.setText("DB Status: "+str(x))
def closeEvent(self,event): #to handle QThread: Destroyed while thread is still running
print("begin close event")
if(self.process is not None):
if(shiboken2.isValid(self.process)): #to check whether the object is deleted. ->
self.process.wait() #-> this will get messy when the DB connection is down
self.process.quit() #-> (IMO):since i stack so many CheckConnection objects maybe?
print("end close event")
class CreateEngine(QtCore.QThread): #creating engine on seperate thread so that it wont block GUI
result = QtCore.Signal(object)
def __init__(self, parent):
QtCore.QThread.__init__(self, parent)
self.engine = None
def run(self):
self.engine = create_engine('mysql+pymysql://{}:{}#{}:{}/{}'.format("root","","localhost","3306","adex_admin"))
self.result.emit(self.engine)
class CheckConnection(QtCore.QThread): #constantly called every second, yes its not a good approach ->
result = QtCore.Signal(str) #-> i wonder how to replace all this with something appropriate
def __init__(self, parent,engine):
QtCore.QThread.__init__(self, parent)
self.engine = engine
def run(self):
try:
self.engine.execute('SELECT 1').fetchall()
self.result.emit("Connected")
except:
self.result.emit("Not Connected")
self.deleteLater() #somehow this doesn't do it job very well. maybe blocked?
#-> especially when the connection is busted. this thread gets stuck quite long to finish
if __name__ == "__main__":
#idk why when you start this without connection it's running really slow on showing the status of DB
#you must wait like 4 seconds until the connection status is showed up, which is really bad
#but once it's live. it could read database status really fast
app = QtWidgets.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
I've created this example just to reproduce the same problem I'm facing in my real app. so the problem is that closeEvent takes too long to terminate the checking process and also blocking the GUI. The reason why I create 'closeEvent' is that I had this problem which produce [QThread: Destroyed while thread is still running] when the app is closed.
also, whenever the database isn't reachable it makes the QThread finishes way longer than it should unlike when the database is reachable. but we can retrieve the status pretty much like we want (every second of live DB Status). I also tried a silly approach like this
...
def continuously_check(self,engine):
self.process = CheckConnection(self,engine)
self.process.result.connect(self.update_connection_label)
self.process.finished.connect(lambda: QtCore.QTimer.singleShot(1000,self.continuously_check))
self.process.start()
...
hoping that it won't keep creating objects before the thread even finished (ps: obviously this won't work). so what's the best approach when it comes to this? sorry for multiple problems at a time.

Update widget with data from another Python process, without blocking it

I'm running a Python script that extracts and localizes some files. I'd like to use a QDialog to show the progress status through a QProgressBar, and the list of files that are being copied.
Let me start by saying that the localization script cannot be integrated in the PyQt script - otherwise I'm aware that the solution would be quite straightforward. I need to keep the localization script separated from the UI and have them running at the same time.
I thought about running the UI from the localization script through a thread, to avoid it blocking the localization process. But at that point I have no idea how to update the UI elements as I don't have an instance I can call and update since I've launched it with the thread.
This is a simplified example of the dialog code:
from PyQt5 import QtCore, QtWidgets
import sys
class Ui_dialog_main(object):
def setupUi(self, dialog_main):
dialog_main.setWindowTitle("Test")
dialog_main.resize(390, 120)
self.progress_bar = QtWidgets.QProgressBar(dialog_main)
self.progress_bar.setGeometry(QtCore.QRect(10, 60, 371, 30))
self.label_localizing = QtWidgets.QLabel(dialog_main)
self.label_localizing.setGeometry(QtCore.QRect(10, 10, 81, 25))
self.label_localizing.setText("Package:")
QtCore.QMetaObject.connectSlotsByName(dialog_main)
def start():
app = QtWidgets.QApplication(sys.argv)
dialog_main = QtWidgets.QDialog()
ui = Ui_dialog_main()
ui.setupUi(dialog_main)
dialog_main.show()
sys.exit(app.exec_())
And this is how I'm starting the thread in the localizer file:
thread = Thread(target=LocManager.start)
thread.start()
where LocManager is the name of the ui .py file.
Of course this way the main script doesn't get stuck by the ui, which is one of my goals - but I have no idea how to update the progress bar and label in this situation. I've found several threads discussing similar problems, but nothing that would exactly help with mine.
I hope my description was clear enough.
UPDATE:
I found a solution for this here, using pipes. Even if I'm not sure this is the appropriate way to tackle this issue, it definitely did the trick. Instead of working with a "Bi-directional Communication" between two PyQt GUIs, as described in the link, I modified the code to have a bi-directional communication between my GUI and my localization script.
One way to solve this is to run the dialog in a separate process, and then use some form of IPC to send the updates. The solution below uses Qt's QLocalServer and QLocalSocket classes to pass a dict encoded with json to the dialog process. The server emits a signal whenever new data is received, which the dialog connects to in order to process updates. When the sending process exits, the server process is automatically shut down.
test.py:
import time
from dlg_server import send_data
for message in 'One Two Three Four Five'.split():
send_data(message=message)
time.sleep(2)
dlg_server.py:
import sys, os, json, atexit
from PyQt5 import QtCore, QtWidgets, QtNetwork
SERVER = 'dlg_server'
_tries = 0
def send_data(**data):
socket = QtNetwork.QLocalSocket()
socket.connectToServer(SERVER, QtCore.QIODevice.WriteOnly)
if socket.waitForConnected(500):
socket.write(json.dumps(data).encode('utf-8'))
if not socket.waitForBytesWritten(2000):
raise RuntimeError('could not write to socket: %s' %
socket.errorString())
socket.disconnectFromServer()
elif socket.error() == QtNetwork.QAbstractSocket.HostNotFoundError:
global _tries
if _tries < 10:
if not _tries:
if QtCore.QProcess.startDetached(
'python', [os.path.abspath(__file__)]):
atexit.register(lambda: send_data(shutdown=True))
else:
raise RuntimeError('could not start dialog server')
_tries += 1
QtCore.QThread.msleep(100)
send_data(**data)
else:
raise RuntimeError('could not connect to server: %s' %
socket.errorString())
else:
raise RuntimeError('could not send data: %s' % socket.errorString())
class Server(QtNetwork.QLocalServer):
dataReceived = QtCore.pyqtSignal(object)
def __init__(self):
super().__init__()
self.newConnection.connect(self.handleConnection)
if not self.listen(SERVER):
raise RuntimeError(self.errorString())
def handleConnection(self):
data = {}
socket = self.nextPendingConnection()
if socket is not None:
if socket.waitForReadyRead(2000):
data = json.loads(str(socket.readAll().data(), 'utf-8'))
socket.disconnectFromServer()
socket.deleteLater()
if 'shutdown' in data:
self.close()
self.removeServer(self.fullServerName())
QtWidgets.qApp.quit()
else:
self.dataReceived.emit(data)
class Dialog(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setGeometry(50, 50, 200, 30)
layout = QtWidgets.QVBoxLayout(self)
self.label = QtWidgets.QLabel()
layout.addWidget(self.label)
def handleDataReceived(self, data):
self.show()
self.label.setText(data.get('message', ''))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dialog = Dialog()
server = Server()
server.dataReceived.connect(dialog.handleDataReceived)
app.exec_()

Using a QThread in PyQT for serial communication (w. pyserial)

I am pretty much a beginner when it comes to GUI programming.
I am using QT in combination with python bindings (PyQT4).
What I am trying to do:
Setting up a QThread to read from & write to a Serial Port with
pyserial.
The main application should be able to emit new serial data via a
signal to the running QThread. and receive serial data from the
QThread with a signal.
I started my own test implementation based on this code (Link).
Prior to this I read the basics about QThreads and tried to understand how they are intended to be used.
The following test code is what I have come up with. I m sorry, I tried to keep it minmal,
but it is still 75 lines of code:
from PyQt4 import QtCore, QtGui
import time
import sys
class SerialData(QtCore.QObject):
def __init__(self, message):
super(SerialData, self).__init__()
self.__m = message
def getMsg(self):
return self.__m
class SerialCon(QtCore.QObject):
finished = QtCore.pyqtSignal()
received = QtCore.pyqtSignal(SerialData)
def init(self):
super(SerialCon, self).__init__()
# TODO setup serial connection:
# setting up a timer to check periodically for new received serial data
self.timer = QtCore.QTimer()
self.timer.setInterval(400)
self.timer.timeout.connect(self.readData)
self.timer.start(200)
# self.finished.emit()
def readData(self):
self.received.emit(SerialData("New serial data!"))
print "-> serial.readLine() ..."
#QtCore.pyqtSlot(SerialData)
def writeData(self, data):
print "-> serial.write(), ", data.getMsg()
class MyGui(QtGui.QWidget):
serialWrite = QtCore.pyqtSignal(SerialData)
def __init__(self):
super(MyGui, self).__init__()
self.initUI()
def initUI(self):
bSend = QtGui.QPushButton("Send",self)
bSend.clicked.connect(self.sendData)
self.show()
#QtCore.pyqtSlot(SerialData)
def updateData(self, data):
print "Gui:", data.getMsg()
def sendData(self, pressed):
data = SerialData("Send me!")
self.serialWrite.emit(data)
def usingMoveToThread():
app = QtGui.QApplication(sys.argv)
guui = MyGui()
thread = QtCore.QThread()
serialc = SerialCon()
serialc.moveToThread(thread)
# connecting signals to slots
serialc.finished.connect(thread.quit)
guui.serialWrite.connect(serialc.writeData)
serialc.received.connect(guui.updateData)
thread.started.connect(serialc.init)
thread.finished.connect(app.exit)
thread.start()
sys.exit(app.exec_())
if __name__ == "__main__":
usingMoveToThread()
My Problems:
In test code the signal emitted from the SerialCon object (which has
been moved to the QThread) seems to be not received by the
corresponding slot (in MyGui, updateData)
Sooner or later the running test code always causes a Segmentation
fault (core dumped). Which makes me believe that I missed some
important bits.
What could cause this?
Maybe I m taking a completely wrong approach? -
so if you have a better idea how to achieve this, I d be very grateful to hear about it!
Thanks a lot!
At first I was only focussing on the new way, how QThreads should be used since QT4
(Link),
by creating a QObject, and then invoking moveToThread(), pretty much like in my first code sample (at least thats how I understood it).
However I just could not figure out, why I was not able to pass signals from the
QThread to the main application.
As I really needed a fast solution to my problem, I desperately tried varius things.
Here is some second code, that does seem to work the way I wanted:
from PyQt4 import QtCore, QtGui
import time
import sys
import math
class SerialCon(QtCore.QThread):
received = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
QtCore.QThread.__init__(self)
# specify thread context for signals and slots:
# test: comment following line, and run again
self.moveToThread(self)
# timer:
self.timer = QtCore.QTimer()
self.timer.moveToThread(self)
self.timer.setInterval(800)
self.timer.timeout.connect(self.readData)
def run(self):
self.timer.start()
#start eventloop
self.exec_()
def readData(self):
# keeping the thread busy
# figure out if the GUI remains responsive (should be running on a different thread)
result = []
for i in range(1,1000000):
result.append(math.pow(i,0.2)*math.pow(i,0.1)*math.pow(i,0.3))
#
self.received.emit("New serial data!")
#QtCore.pyqtSlot(object)
def writeData(self, data):
#print(self.currentThreadId())
print(data)
class MyGui(QtGui.QWidget):
serialWrite = QtCore.pyqtSignal(object)
def __init__(self, app, parent=None):
self.app = app
super(MyGui, self).__init__(parent)
self.initUI()
def initUI(self):
self.bSend = QtGui.QPushButton("Send",self)
self.bSend.clicked.connect(self.sendData)
self.show()
def closeEvent(self, event):
print("Close.")
self.serialc.quit();
#QtCore.pyqtSlot(object)
def updateData(self, data):
print(data)
def sendData(self, pressed):
self.serialWrite.emit("Send Me! Please?")
def usingMoveToThread(self):
self.serialc = SerialCon()
# binding signals:
self.serialc.received.connect(self.updateData)
self.serialWrite.connect(self.serialc.writeData)
# start thread
self.serialc.start()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
guui = MyGui(app)
guui.usingMoveToThread()
sys.exit(app.exec_())
I consider it a workaround for now, but it does not really answer the question for me.
Plus, as mentioned in the previously linked QT blog entry,
it is not really the intended way to use the QThread.
So I am still wondering how to get the first code in my question to work as expected.
If you have some ideas about what is wrong with my first code, please let my know!

Categories