How I can run while loop with PyQt5 - python

I work on one Project : Program download but I have a problem with while loop for check the connection with the internet and if true doesn't setText('') to lable and if Flase setText('anyText') to lable
Method for check the connection
def checkInternetConnection(self,host="8.8.8.8", port=53, timeout=3):
while self.conection==False:
try:
socket.setdefaulttimeout(timeout)
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
self.conection = True
return self.conection
except Exception as e:
print(e)
self.label_9.setText('Please Check Internect Connection')
self.conection = False
return self.conection
self.finished.emit()
I have tired with QThread . Please How I can do it :) ? And when app is running if connection is lost=False setText('check internet') and when the connection become true setText('')
CONSTRUCTOR
From_Class,_=loadUiType(os.path.join(os.path.dirname(__file__),'designe.ui'))
class mainApp(QMainWindow,From_Class):
finished = pyqtSignal()
def __init__(self,parent=None):
super(mainApp, self).__init__(parent)
QMainWindow.__init__(self)
super().setupUi(self)
self.handleGui()
self.handleButton()
self.setWindowIcon(QIcon('mainIcon.png'))
self.menuBarW()
self.conection = False
MainCode
def main():
app = QApplication(sys.argv)
window = mainApp()
window.checkInternetConnection()
window.show()
app.exec()
if __name__=='__main__':
main()

Do not get complicated with QThread, use the threading library:
def main():
app = QtWidgets.QApplication(sys.argv)
window = mainApp()
threading.Thread(target=window.checkInternetConnection, daemon=True).start()
window.show()
app.exec()
On the other hand, since you are using a thread, you should not update the GUI from another thread, for this you can use QMetaObject::invokeMethod:
def checkInternetConnection(self,host="8.8.8.8", port=53, timeout=3):
while True:
try:
socket.setdefaulttimeout(timeout)
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
self.conection = True
except Exception as e:
self.conection = False
print(e)
msg = "" if self.conection else 'Please Check Internect Connection'
print("msg", msg)
QtCore.QMetaObject.invokeMethod(self.label_9, "setText",
QtCore.Qt.QueuedConnection,
QtCore.Q_ARG(str, msg))
self.finished.emit()

Related

PyQt5 : Destroyed while thread is still running

Tried to add exceptions, what should the program (client) do when the server is disconnected.
The idea is simple. If the server is turned off - the client should try to connect to the server until the server is turned on.
When server is turn off - program try to connect. But when I turned server on, client is connected, and after that connection drops, and in client app I saw that - QThread: Destroyed while thread is still running
This is simple example of what I want (minimal reproducible example for client):
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import socket
class ListeningThread(QtCore.QThread):
mysignal = QtCore.pyqtSignal(str)
def __init__(self, server_socket, parent=None):
QtCore.QThread.__init__(self, parent)
self.server_socket = server_socket
self.message = None
def run(self):
try:
while True:
self.message = self.server_socket.recv(4096)
self.mysignal.emit(self.message.decode('utf-8'))
except:
Push()
class Push(QtWidgets.QMainWindow):
def __init__(self):
super(Push, self).__init__()
print('now connecting...')
self.connect_server()
def connect_server(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.server_socket.connect(('127.0.0.1', 5555))
self.message_monitor = ListeningThread(self.server_socket)
self.message_monitor.mysignal.connect(self.init_UI)
self.message_monitor.start()
except:
Push()
def init_UI(self, message):
print(message)
app =QtWidgets.QApplication([])
application = Push()
sys.exit(app.exec_())
I tried that in ListeningThread:
class ListeningThread(QtCore.QThread):
mysignal = QtCore.pyqtSignal(str)
def __init__(self, server_socket, parent=None):
QtCore.QThread.__init__(self, parent)
self.server_socket = server_socket
self.message = None
def run(self):
try:
while not self.isInterruptionRequested():
self.message = self.server_socket.recv(4096)
self.mysignal.emit(self.message.decode('utf-8'))
except:
print('error in thread')
self.requestInterruption()
self.wait()
Push()
But the problem is still the same.
I think I should close the thread before it starts in Push.connect_server but idk how.
For minimal reproducible example u can use that server:
import socket
import threading
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('127.0.0.1', 5555))
server_socket.listen()
clients = []
def accept_socket():
while True:
client_socket, addr = server_socket.accept()
print(f'connection from {addr}')
print(client_socket)
if client_socket not in clients:
clients.append(client_socket)
def sending_message(clients, data):
for client_socket in clients:
try:
client_socket.send(data.encode("utf-8"))
except:
pass
accept_thread = threading.Thread(target= accept_socket)
accept_thread.start()
while True:
data = input('Enter_message:\n')
sending_message(clients, data)
The problem is that when an exception occurs you are creating a new "Push" object that has a limited scope since it is a local variable so it will be destroyed, and objects such as the thread will also be destroyed, and that is what it indicates. the error message.
Instead of complicating your life with the handling of threads (IMO they are the last option) you can use a QTcpSocket that allows to handle the sockets in a simple way through signals and that uses the Qt eventloop.
from functools import cached_property
import sys
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork
class Client(QtCore.QObject):
messageChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.socket.stateChanged.connect(self.handle_state_changed)
self.socket.errorOccurred.connect(self.handle_error_occurred)
self.socket.readyRead.connect(self.handle_ready_read)
#cached_property
def socket(self):
return QtNetwork.QTcpSocket()
def try_connect(self):
self.socket.connectToHost("127.0.0.1", 5555)
def handle_state_changed(self, state):
print(f"state: {state}")
if state == QtNetwork.QAbstractSocket.UnconnectedState:
print("disconnected")
QtCore.QTimer.singleShot(1000, self.try_connect)
elif state == QtNetwork.QAbstractSocket.ConnectedState:
print("connected")
def handle_error_occurred(self, error):
print(f"error code {error}, message: {self.socket.errorString()}")
def handle_ready_read(self):
codec = QtCore.QTextCodec.codecForName("UTF-8")
message = codec.toUnicode(self.socket.readAll())
self.messageChanged.emit(message)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.client.messageChanged.connect(self.handle_message_changed)
self.client.try_connect()
#cached_property
def client(self):
return Client()
def handle_message_changed(self, message):
print(f"client message: {message}")
def main():
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

How can give info only once with QMessageBox while internet connection is OFF?

this code opens web site with Qt browser. If internet connection is lost I want to give Information message with QMessageBox:
import sys
from PyQt5.QtCore import *
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QLabel
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtGui import QGuiApplication as App
from PyQt5.QtGui import QPixmap, QWindow
from PyQt5 import QtNetwork, QtCore, QtWidgets
import urllib
from urllib.request import urlopen
import threading
import time
class WebApp(QMainWindow):
def __init__(self):
self.is_connected = None
self.is_msgshow = True
self.msg = QtWidgets.QMessageBox()
super().__init__()
self.title = "OZI"
self.t_internet = threading.Thread(target=self.is_internet)
self.t_internet.start()
self.t_refreshpage = threading.Thread(target=self.refresh_page)
self.t_refreshpage.start()
self.web = QWebEngineView()
self.web.window().setWindowTitle(self.title)
self.web.load(QUrl("http://www.google.com"))
self.web.showFullScreen()
def is_internet(self):
"""
Query internet using python
:return:
"""
while True:
time.sleep(5)
try:
urlopen("http://www.google.com", timeout=1)
self.is_connected = True
except urllib.error.URLError as Error:
print(Error)
self.is_connected = False
print(self.is_connected)
def refresh_page(self):
while True:
time.sleep(.1)
if self.is_connected == False:
time.sleep(5)
if self.is_connected == True:
self.web.page().action(QWebEnginePage.Reload).trigger()
else:
if self.is_msgshow == True:
print('testtt')
self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!')
self.is_msgshow = False
self.msg.close()
else:
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = WebApp()
sys.exit(app.exec_())
However, I couldn't succeed at
if self.is_msgshow == True:
print('testtt')
this part while internet connection is lost my code opens many MessageBox. I guess my fault is controlling self.is_msgshow flag.
Far beyond the problem you are pointing out, you have other errors and it is more dangerous than the one indicated: You should not access the GUI from another thread, and you should also not access the same variable from 2 threads without protecting it through a mutex, semaphore or Similary. For example, you are calling self.web.page().action(QWebEnginePage.Reload).trigger() and self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!') from the secondary thread, and the variable "is_connected" is accessed by several threads.
Another error is that method QMessage::information() is static and generates a new object that is not directly accessible, instead you must use the object "self.msg" through its own methods to display the necessary information.
Considering the above I have created a class that is only responsible for analyzing the status of the connection and if it changes to emit a signal. To send information between threads in Qt you can use signals or QMetaObject::invokedMethod() in addition to the classic mutex and semaphores.
Then it is only necessary to analyze the value of the status sent to the GUI to implement the logic:
import sys
import threading
import time
import urllib
from urllib.request import urlopen
from PyQt5 import QtCore, QtWidgets, QtNetwork, QtWebEngineWidgets
class ConnectivityManager(QtCore.QObject):
statusChanged = QtCore.pyqtSignal(bool)
def __init__(self, *, timeout=4000, parent=None):
super().__init__(parent)
self._status = False
self._timeout = timeout
def start(self):
threading.Thread(target=self._check, daemon=True).start()
#QtCore.pyqtProperty(bool, notify=statusChanged)
def status(self):
return self._status
#QtCore.pyqtSlot(bool)
def _update_status(self, status):
if self._status != status:
self._status = status
self.statusChanged.emit(self.status)
def _check(self):
while True:
try:
urlopen("http://www.google.com", timeout=1)
status = True
except urllib.error.URLError as Error:
status = False
QtCore.QMetaObject.invokeMethod(
self,
"_update_status",
QtCore.Qt.QueuedConnection,
QtCore.Q_ARG(bool, status),
)
time.sleep(5)
class WebApp(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("OZI")
self.web = QtWebEngineWidgets.QWebEngineView()
self.setCentralWidget(self.web)
self.web.load(QtCore.QUrl("http://www.google.com"))
self.connectivity_manager = ConnectivityManager()
self.connectivity_manager.statusChanged.connect(self.on_status_changed)
self.connectivity_manager.start()
self.msg = QtWidgets.QMessageBox()
self.msg.setWindowTitle("INFO")
self.msg.setText("PLEASE CHECK YOUR INTERNET CONNECTION!!!")
#QtCore.pyqtSlot(bool)
def on_status_changed(self, status):
if status:
self.msg.hide()
self.statusBar().showMessage("Connected")
self.web.page().action(QtWebEngineWidgets.QWebEnginePage.Reload).trigger()
else:
self.statusBar().showMessage("Disconnected")
self.msg.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = WebApp()
ex.showFullScreen()
sys.exit(app.exec_())
On the other hand, the same logic can be implemented using QtNetwork without the need for threads:
import sys
from PyQt5 import QtCore, QtWidgets, QtNetwork, QtWebEngineWidgets
class ConnectivityManager(QtCore.QObject):
statusChanged = QtCore.pyqtSignal(bool)
def __init__(self, *, timeout=4000, parent=None):
super().__init__(parent)
self._status = False
self._timeout = timeout
self.manager = QtNetwork.QNetworkAccessManager()
self._timer = QtCore.QTimer(
singleShot=True, interval=self._timeout, timeout=self.verify_status
)
def start(self):
QtCore.QTimer.singleShot(0, self._check)
#QtCore.pyqtProperty(bool, notify=statusChanged)
def status(self):
return self._status
#QtCore.pyqtSlot(bool)
def _update_status(self, status):
if self._status != status:
self._status = status
self.statusChanged.emit(self.status)
def _check(self):
url = QtCore.QUrl("https://www.google.com/")
req = QtNetwork.QNetworkRequest(url)
self._reply = self.manager.get(req)
self._reply.finished.connect(self.verify_status)
self._timer.start()
#QtCore.pyqtSlot()
def verify_status(self):
if self._timer.isActive():
self._timer.stop()
if self._reply.error() == QtNetwork.QNetworkReply.NoError:
v = self._reply.attribute(
QtNetwork.QNetworkRequest.HttpStatusCodeAttribute
)
if 200 <= v < 300:
self._update_status(True)
else:
print("error", "code error: {}".format(v))
self._update_status(False)
else:
print("error", self._reply.errorString())
self._update_status(False)
else:
self._reply.finished.disconnect(self.verify_status)
self._reply.abort()
print("Timeout")
self._update_status(False)
QtCore.QTimer.singleShot(5000, self._check)
self._reply.deleteLater()
class WebApp(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("OZI")
self.web = QtWebEngineWidgets.QWebEngineView()
self.setCentralWidget(self.web)
self.web.load(QtCore.QUrl("http://www.google.com"))
self.connectivity_manager = ConnectivityManager()
self.connectivity_manager.statusChanged.connect(self.on_status_changed)
self.connectivity_manager.start()
self.msg = QtWidgets.QMessageBox()
self.msg.setWindowTitle("INFO")
self.msg.setText("PLEASE CHECK YOUR INTERNET CONNECTION!!!")
#QtCore.pyqtSlot(bool)
def on_status_changed(self, status):
if status:
self.msg.hide()
self.statusBar().showMessage("Connected")
self.web.page().action(QtWebEngineWidgets.QWebEnginePage.Reload).trigger()
else:
self.statusBar().showMessage("Disconnected")
self.msg.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = WebApp()
ex.showFullScreen()
sys.exit(app.exec_())
I do not know the pyqt5 at all but guessing by flow of your code, here:
if self.is_msgshow == True:
print('testtt')
self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!')
self.is_msgshow = False
self.msg.close()
It seems the self.msg.information() call is expected to be synchronous. If that is so, then as long as it remains open, the is_msgshow is still True because you changing it once you got dialog closed. And this is your bug as if new even occurs then nothing blocks new dialog from being shown. The fix is pretty simple - just move self.is_msgshow = False to be very first thing done in that code block, you should be good:
if self.is_msgshow:
self.is_msgshow = False
print('testtt')
self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!')
self.msg.close()
Additional note, you may want to reset it back to True once connectivity is restored otherwise you won't show a thing next time network is down.
The QMessageBox class provides a modal dialog for informing the user or for asking the user a question and receiving an answer.
https://doc.qt.io/qtforpython/PySide2/QtWidgets/QMessageBox.html

Handle COM Ports in another class

I am creating a Python project with PyQT5 which combines a class written by me and one written by someone else. The second class is opened in a new window to display data read from serial communication. I now have the problem, that when I close my program before the second window appears, the COM Port opened by the second class while instantiation is not closed properly, so that I can't run my program again. How can I handle this COM port so that it is not opened too soon or that it is closed properly?
My code looks like this:
class SecondWindow(SecondWindowClass):
def closeThis(self):
self.ser.close()
self.close()
self.first.show()
def __init__(self, first):
#SecondWindowClass.__init__(self)
super(SecondWindow, self).__init__()
self.first = first
self.ui = WindowTemplate()
self.ui.setupUi(self)
self.show()
print("Now opening COM port")
self.ser = serial.Serial('COM1', 9600, timeout=1)
print("COM1 opened")
#line = self.ser.readline() # first line is trash
self.ser.flush()
print("flushed COM11")
self.ui.pushButton.clicked.connect(lambda x:self.closeThis())
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def showSecond(self):
self.second.show()
self.hide()
self.ui.end_button.clicked.connect(lambda x:self.nextPicture())
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setWindowTitle('pyqtgraph example: Qt Designer')
self.ui=uic.loadUi(uiFile, self)
self.setupUi(self)
self.show()
self.second = SecondWindow(self)
self.second.hide()
self.ui.end_button.clicked.connect(lambda x:self.showSecond())
win = MainWindow()
if __name__ == '__main__':
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
I found a solution using closeEvent:
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def closeEvent(self, evnt): # Use the closing event
if isinstance(self.second, SecondWindow): # Check if instance already exists
try: # Close port if possible
self.second.ser.close()
print("Port closed")
except(RuntimeError, TypeError, NameError):
print("no serial port")
[...]

'QThread: Destroyed while thread is still running' on quit

I'm trying to find a way to quit my app properly. When I exit, I get an error saying QThread: Destroyed while thread is still running. I have a thread for feeding output to a QTextBrowser. What should be the proper way to exit? Here's what I've got:
class LogReceiver(QtCore.QObject):
mysignal = QtCore.Signal(str)
def __init__(self, queue, *args, **kwargs):
QtCore.QObject.__init__(self, *args, **kwargs)
self.queue = queue
def run(self):
while True:
text = self.queue.get()
self.mysignal.emit(text)
if __name__ == '__main__':
queue = Queue()
thread = QtCore.QThread()
my_receiver = MyReceiver(queue)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
my_receiver.mysignal.connect(window.append_text)
my_receiver.moveToThread(thread)
thread.started.connect(my_receiver.run)
thread.start()
sys.exit(app.exec_())
Should thread somehow be terminated upon exit? Note that self.queue.get() blocks and waits for text.
Thanks
You need to re-structure the while loop so that it doesn't block uncondtionally.
You can do this with a simple flag and a timeout:
def run(self):
self.active = True
while self.active:
try:
text = self.queue.get(timeout=1.0)
self.mysignal.emit(text)
except Empty:
continue
So now the queue won't block indefinitely, and the flag will be checked once a second to see if the loop should be exited.
EDIT:
Here's a working example based on your code:
import sys
from queue import Queue, Empty
from PySide import QtCore, QtGui
class LogReceiver(QtCore.QObject):
mysignal = QtCore.Signal(str)
def __init__(self, queue, *args, **kwargs):
QtCore.QObject.__init__(self, *args, **kwargs)
self.queue = queue
def run(self):
self.active = True
while self.active:
try:
text = self.queue.get(timeout=1.0)
self.mysignal.emit('text')
except Empty:
continue
print('finished')
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.queue = Queue()
self.thread = QtCore.QThread(self)
self.receiver = LogReceiver(self.queue)
self.receiver.moveToThread(self.thread)
self.thread.started.connect(self.receiver.run)
self.thread.start()
def closeEvent(self, event):
print('close')
self.receiver.active = False
self.thread.quit()
self.thread.wait()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Try:
# previous code here
thread.start()
app.exec_()
thread.terminate()
thread.wait()
sys.exit(0)
Basically when exec_() finishes (QApplication is closed ex. by closing the window) you force the thread to terminate and wait() for it to cleanup. If your thread has an event loop you can call quit() instead of terminate(). terminate() is generally not a good idea see: here.
The more desirable approach would be to put a flag in run() method ex.
while !flag:
do stuff
and change main to:
app.exec_()
flag = True
thread.wait()
sys.exit(0)
Where flag is a global variable. QThread terminates itself when run() method finishes.

PyQt - how to detect and close UI if it's already running?

I'm starting the UI from within Maya. If the UI hasn't been closed, running the UI again will completely freeze Maya (with the error "Event Loop is already running")
Manually closing the UI before re-running the script will prevent it from freezing up. But I guess that's not really practical.
Is there a way to detect if the UI I'm trying to run already exists? And possible force close it?
Here is a very simple PyQt5 solution using QLockFile:
from PyQt5 import QtCore, QtWidgets
lockfile = QtCore.QLockFile(QtCore.QDir.tempPath() + '/my_app_name.lock')
if lockfile.tryLock(100):
app = QtWidgets.QApplication([])
win = QtWidgets.QWidget()
win.setGeometry(50, 50, 100, 100)
win.show()
app.exec()
else:
print('app is already running')
There were a couple of fairly straightforward C++ solutions given on the Qt Wiki which no longer seem to exist. I ported one of them to PyQt, and have provided a sample script below. The original C++ solution has been split into two classes, because the messaging facility may not be needed.
PyQt5:
from PyQt5 import QtWidgets, QtCore, QtNetwork
class SingleApplication(QtWidgets.QApplication):
messageAvailable = QtCore.pyqtSignal(object)
def __init__(self, argv, key):
super().__init__(argv)
# cleanup (only needed for unix)
QtCore.QSharedMemory(key).attach()
self._memory = QtCore.QSharedMemory(self)
self._memory.setKey(key)
if self._memory.attach():
self._running = True
else:
self._running = False
if not self._memory.create(1):
raise RuntimeError(self._memory.errorString())
def isRunning(self):
return self._running
class SingleApplicationWithMessaging(SingleApplication):
def __init__(self, argv, key):
super().__init__(argv, key)
self._key = key
self._timeout = 1000
self._server = QtNetwork.QLocalServer(self)
if not self.isRunning():
self._server.newConnection.connect(self.handleMessage)
self._server.listen(self._key)
def handleMessage(self):
socket = self._server.nextPendingConnection()
if socket.waitForReadyRead(self._timeout):
self.messageAvailable.emit(
socket.readAll().data().decode('utf-8'))
socket.disconnectFromServer()
else:
QtCore.qDebug(socket.errorString())
def sendMessage(self, message):
if self.isRunning():
socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
if not socket.waitForConnected(self._timeout):
print(socket.errorString())
return False
if not isinstance(message, bytes):
message = message.encode('utf-8')
socket.write(message)
if not socket.waitForBytesWritten(self._timeout):
print(socket.errorString())
return False
socket.disconnectFromServer()
return True
return False
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.edit = QtWidgets.QLineEdit(self)
self.edit.setMinimumWidth(300)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.edit)
def handleMessage(self, message):
self.edit.setText(message)
if __name__ == '__main__':
import sys
key = 'app-name'
# send commandline args as message
if len(sys.argv) > 1:
app = SingleApplicationWithMessaging(sys.argv, key)
if app.isRunning():
print('app is already running')
app.sendMessage(' '.join(sys.argv[1:]))
sys.exit(1)
else:
app = SingleApplication(sys.argv, key)
if app.isRunning():
print('app is already running')
sys.exit(1)
window = Window()
app.messageAvailable.connect(window.handleMessage)
window.show()
sys.exit(app.exec_())
PyQt4:
# only needed for python2
import sip
sip.setapi('QString', 2)
from PyQt4 import QtGui, QtCore, QtNetwork
class SingleApplication(QtGui.QApplication):
messageAvailable = QtCore.pyqtSignal(object)
def __init__(self, argv, key):
QtGui.QApplication.__init__(self, argv)
# cleanup (only needed for unix)
QtCore.QSharedMemory(key).attach()
self._memory = QtCore.QSharedMemory(self)
self._memory.setKey(key)
if self._memory.attach():
self._running = True
else:
self._running = False
if not self._memory.create(1):
raise RuntimeError(self._memory.errorString())
def isRunning(self):
return self._running
class SingleApplicationWithMessaging(SingleApplication):
def __init__(self, argv, key):
SingleApplication.__init__(self, argv, key)
self._key = key
self._timeout = 1000
self._server = QtNetwork.QLocalServer(self)
if not self.isRunning():
self._server.newConnection.connect(self.handleMessage)
self._server.listen(self._key)
def handleMessage(self):
socket = self._server.nextPendingConnection()
if socket.waitForReadyRead(self._timeout):
self.messageAvailable.emit(
socket.readAll().data().decode('utf-8'))
socket.disconnectFromServer()
else:
QtCore.qDebug(socket.errorString())
def sendMessage(self, message):
if self.isRunning():
socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
if not socket.waitForConnected(self._timeout):
print(socket.errorString())
return False
if not isinstance(message, bytes):
message = message.encode('utf-8')
socket.write(message)
if not socket.waitForBytesWritten(self._timeout):
print(socket.errorString())
return False
socket.disconnectFromServer()
return True
return False
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.edit = QtGui.QLineEdit(self)
self.edit.setMinimumWidth(300)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.edit)
def handleMessage(self, message):
self.edit.setText(message)
if __name__ == '__main__':
import sys
key = 'app-name'
# send commandline args as message
if len(sys.argv) > 1:
app = SingleApplicationWithMessaging(sys.argv, key)
if app.isRunning():
print('app is already running')
app.sendMessage(' '.join(sys.argv[1:]))
sys.exit(1)
else:
app = SingleApplication(sys.argv, key)
if app.isRunning():
print('app is already running')
sys.exit(1)
window = Window()
app.messageAvailable.connect(window.handleMessage)
window.show()
sys.exit(app.exec_())
In case if someone want to run #ekhumoro solution with python3 there's need to make few adjustments to string operations, i'll share my copy where it was working python 3.
import sys
from PyQt4 import QtGui, QtCore, QtNetwork
class SingleApplication(QtGui.QApplication):
def __init__(self, argv, key):
QtGui.QApplication.__init__(self, argv)
self._memory = QtCore.QSharedMemory(self)
self._memory.setKey(key)
if self._memory.attach():
self._running = True
else:
self._running = False
if not self._memory.create(1):
raise RuntimeError( self._memory.errorString() )
def isRunning(self):
return self._running
class SingleApplicationWithMessaging(SingleApplication):
def __init__(self, argv, key):
SingleApplication.__init__(self, argv, key)
self._key = key
self._timeout = 1000
self._server = QtNetwork.QLocalServer(self)
if not self.isRunning():
self._server.newConnection.connect(self.handleMessage)
self._server.listen(self._key)
def handleMessage(self):
socket = self._server.nextPendingConnection()
if socket.waitForReadyRead(self._timeout):
self.emit(QtCore.SIGNAL('messageAvailable'), bytes(socket.readAll().data()).decode('utf-8') )
socket.disconnectFromServer()
else:
QtCore.qDebug(socket.errorString())
def sendMessage(self, message):
if self.isRunning():
socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
if not socket.waitForConnected(self._timeout):
print(socket.errorString())
return False
socket.write(str(message).encode('utf-8'))
if not socket.waitForBytesWritten(self._timeout):
print(socket.errorString())
return False
socket.disconnectFromServer()
return True
return False
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.edit = QtGui.QLineEdit(self)
self.edit.setMinimumWidth(300)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.edit)
def handleMessage(self, message):
self.edit.setText(message)
if __name__ == '__main__':
key = 'foobar'
# if parameter no. 1 was set then we'll use messaging between app instances
if len(sys.argv) > 1:
app = SingleApplicationWithMessaging(sys.argv, key)
if app.isRunning():
msg = ''
# checking if custom message was passed as cli argument
if len(sys.argv) > 2:
msg = sys.argv[2]
else:
msg = 'APP ALREADY RUNNING'
app.sendMessage( msg )
print( "app is already running, sent following message: \n\"{0}\"".format( msg ) )
sys.exit(1)
else:
app = SingleApplication(sys.argv, key)
if app.isRunning():
print('app is already running, no message has been sent')
sys.exit(1)
window = Window()
app.connect(app, QtCore.SIGNAL('messageAvailable'), window.handleMessage)
window.show()
sys.exit(app.exec_())
Example cli calls, assuming that your script name is "SingleInstanceApp.py":
python SingleInstanceApp.py 1
python SingleInstanceApp.py 1 "test"
python SingleInstanceApp.py 1 "foo bar baz"
python SingleInstanceApp.py 1 "utf8 test FOO ßÄÖÜ ßäöü łąćźżóń ŁĄĆŹŻÓŃ etc"
(and here is call wihout first parameter, so message simply will not be sent)
python SingleInstanceApp.py
Hope that it will help someone.
My solution is this:
import sys
from PyQt5.QtCore import QLockFile
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMessageBox
from window import MainWindow
if __name__ == "__main__":
try:
app_object = QApplication(sys.argv)
lock_file = QLockFile("app.lock")
if lock_file.tryLock():
window = MainWindow()
window.show()
app_object.exec()
else:
error_message = QMessageBox()
error_message.setIcon(QMessageBox.Warning)
error_message.setWindowTitle("Error")
error_message.setText("The application is already running!")
error_message.setStandardButtons(QMessageBox.Ok)
error_message.exec()
finally:
lock_file.unlock()

Categories