Я запускаю скрипт Python, который извлекает и локализует некоторые файлы. Я хотел бы использовать QDialog
чтобы показать статус выполнения через QProgressBar
, и список файлов, которые копируются.
Позвольте мне начать с того, что сценарий локализации не может быть интегрирован в скрипт PyQt - иначе я знаю, что решение будет довольно простым. Мне нужно держать скрипт локализации отделенным от пользовательского интерфейса и запускать их одновременно.
Я думал о запуске пользовательского интерфейса из сценария локализации через поток, чтобы избежать блокирования процесса локализации. Но в этот момент я понятия не имею, как обновить элементы пользовательского интерфейса, поскольку у меня нет экземпляра, который я могу вызвать и обновить, так как я запустил его с потоком.
Это упрощенный пример диалогового кода:
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_())
И вот как я запускаю поток в файле локализатора:
thread = Thread(target=LocManager.start)
thread.start()
где LocManager
- это имя файла .py пользовательского интерфейса.
Конечно, таким образом основной скрипт не застревает в интерфейсе, что является одной из моих целей - но я не знаю, как обновить индикатор выполнения и метку в этой ситуации. Я нашел несколько тем, обсуждающих подобные проблемы, но ничего, что точно помогло бы мне.
Надеюсь, мое описание было достаточно ясным.
ОБНОВИТЬ:
Я нашел решение для этого здесь, используя трубы. Даже если я не уверен, что это правильный способ решения этой проблемы, это определенно помогло. Вместо того, чтобы работать с "Двунаправленной связью" между двумя PyQt GUI, как описано в ссылке, я изменил код, чтобы иметь двунаправленную связь между моим GUI и моим сценарием локализации.
Одним из способов решения этой проблемы является запуск диалога в отдельном процессе, а затем использование какой-либо формы IPC для отправки обновлений. Решение ниже использует Qt QLocalServer
и QLocalSocket
классов, чтобы передать dict
, закодированный с JSON в диалоговом процесс. Сервер выдает сигнал при получении новых данных, к которому подключается диалоговое окно для обработки обновлений. Когда процесс отправки завершается, процесс сервера автоматически отключается.
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_()
PyQt указывает, что GUI должен выполняться в главном потоке, а не во вторичном потоке, как вы собираетесь. Вместо того, чтобы выполнять GUI в другом потоке, вы должны выполнить тяжелую задачу (в вашем случае задачу копирования файлов) в другом потоке.
Для получения дополнительной информации читайте: https://doc.qt.io/qt-5/thread-basics.html#gui-thread-and-worker-thread
thread = Thread(target=LocManager.start)
target должен быть именем функции, а не файлом.py