Я пытался сделать самообновляющуюся программу с pyqt5, поэтому подумал, что было бы здорово
показать окно загрузки, пока идет обновление.
Я следил за this ответ, и все было хорошо, пока я не добавил поток, который обновляет программу.
вот мой код.
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from packaging import version
import os
import sys
import time
import threading
updateFlag = False
updateDoneFlag = False
CUR_VERSION = "1.0.1"
LATEST_VERSION = "1.0.2"
class MainWidget(QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
try:
self.qle_id = QLineEdit(self)
self.qle_id.setPlaceholderText("ID")
self.qle_id.setFocus()
self.qle_pw = QLineEdit(self)
self.qle_pw.setPlaceholderText("PW")
vbox = QVBoxLayout()
vbox.addStretch(1)
vbox.addWidget(self.qle_id)
vbox.addWidget(self.qle_pw)
vbox.addStretch(1)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addLayout(vbox)
hbox.addStretch(1)
self.setLayout(hbox)
except Exception as e:
print(e)
pass
class UpdateWidget(QWidget):
def __init__(self, parent=None):
super(UpdateWidget, self).__init__(parent)
self.gif = QMovie("loading.gif", QByteArray(), self)
self.loading = QLabel()
self.loading.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.loading.setAlignment(Qt.AlignCenter)
self.lbl = QLabel("Update in progress...")
self.lbl.setAlignment(Qt.AlignCenter)
self.btn = QPushButton("Cancel", self)
vbox = QVBoxLayout()
vbox.addStretch(1)
vbox.addWidget(self.loading)
vbox.addWidget(self.lbl)
vbox.addWidget(self.btn)
vbox.addStretch(1)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addLayout(vbox)
hbox.addStretch(1)
self.setLayout(hbox)
self.gif.setCacheMode(QMovie.CacheAll)
self.loading.setMovie(self.gif)
self.gif.start()
self.gif.loopCount()
self.updateThr = (threading.Thread(target=self.startUpdate)).start()
def startUpdate(self):
global updateFlag, updateDoneFlag
try:
while True:
time.sleep(5)
if not updateFlag:
updateDoneFlag = True
break
time.sleep(5)
if not updateFlag:
updateDoneFlag = True
break
time.sleep(5)
if not updateFlag:
updateDoneFlag = True
break
time.sleep(5)
updateDoneFlag = True
global CUR_VERSION
CUR_VERSION = "1.0.2"
break
except Exception as e:
print(e)
pass
class WindowManager(QMainWindow):
def __init__(self, parent=None):
super(WindowManager, self).__init__(parent)
infoMenu = QAction("&Info", self)
infoMenu.setShortcut("Ctrl+I")
infoMenu.triggered.connect(self.showProgInfo)
infoMenu.setStatusTip("About the program")
updateMenu = QAction("&Update", self)
updateMenu.setShortcut("Ctrl+T")
updateMenu.setStatusTip("Check for update")
updateMenu.triggered.connect(self.checkUpdate)
exitMenu = QAction("&Exit", self)
exitMenu.setShortcut("Ctrl+Q")
exitMenu.setStatusTip("Exit program")
exitMenu.triggered.connect(self.closeEventForShortcut)
self.menu = self.menuBar()
fileMenu = self.menu.addMenu("&Menu")
fileMenu.addAction(infoMenu)
fileMenu.addAction(updateMenu)
fileMenu.addAction(exitMenu)
self.checkUpdate()
def showMainWindow(self):
try:
self.mainWindow = MainWidget(self)
self.menu.setVisible(True)
self.setWindowFlag(Qt.WindowCloseButtonHint, True)
self.setFixedSize(600, 400)
self.setWindowTitle("Main")
self.setCentralWidget(self.mainWindow)
self.show()
except Exception as e:
print(e)
pass
def showUpdateWindow(self):
global updateFlag
try:
updateFlag = True
self.updateWindow = UpdateWidget(self)
self.menu.setVisible(False)
self.setWindowFlag(Qt.WindowCloseButtonHint, False)
self.setFixedSize(500, 500)
self.setWindowTitle("Update")
self.setCentralWidget(self.updateWindow)
self.updateWindow.btn.clicked.connect(self.cancelUpdate)
self.show()
except Exception as e:
print(e)
pass
def checkUpdate(self):
global CUR_VERSION, LATEST_VERSION, updateFlag, updateDoneFlag
try:
msg = QMessageBox()
if version.parse(CUR_VERSION) < version.parse(LATEST_VERSION):
msg.setWindowTitle("Update alert")
msg.setText("Update is available (%s to %s)" % (CUR_VERSION, LATEST_VERSION))
msg.addButton(QPushButton("&Update"), QMessageBox.YesRole)
msg.setStandardButtons(QMessageBox.Cancel)
update = msg.exec_()
if update == QMessageBox.Cancel:
self.showMainWindow()
else:
updateFlag = True
updateDoneFlag = False
self.showUpdateWindow()
except Exception as e:
print(e)
pass
def cancelUpdate(self):
try:
self.updateWindow.lbl.setText("Cancelling update...")
global updateFlag
updateFlag = False
self.updateDoneThr = (threading.Thread(target=self.waitUntilUpdateThrDie)).start()
except Exception as e:
print(e)
pass
def waitUntilUpdateThrDie(self):
global updateDoneFlag
try:
while True:
if updateDoneFlag:
self.showMainWindow()
break
except Exception as e:
print(e)
pass
def showProgInfo(self):
infoBox = QMessageBox()
infoBox.information(self, "Information", "Hey what's up guys it's scarce here")
def closeEvent(self, event):
global exitFlag, updateFlag
if not updateFlag:
close = QMessageBox.question(self, "Exit", "Exit the program?", QMessageBox.Yes | QMessageBox.No)
if close == QMessageBox.Yes:
exitFlag = True
updateFlag = False
event.accept()
else:
event.ignore()
else:
close = QMessageBox.question(self, "Update", "Cancel the update?", QMessageBox.Yes | QMessageBox.No)
if close == QMessageBox.Yes:
self.cancelUpdate()
event.ignore()
else:
event.ignore()
def closeEventForShortcut(self):
close = QMessageBox.question(self, "Exit", "Exit the program?", QMessageBox.Yes | QMessageBox.No)
if close == QMessageBox.Yes:
global exitFlag, updateFlag
exitFlag = True
updateFlag = False
QCoreApplication.instance().quit()
else:
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
main = WindowManager()
sys.exit(app.exec_())
После того, как обновление было отменено или завершено, вместо этого появилось это сообщение об ошибке главного окна.
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
Я погуглил, и он сказал, что я пытался изменить GUI из другого потока.
Но разве не все GUI изменились в классе WindowManager? Классы QWidget просто меняют флаги.
Итак, класс WindowManager знает, что он должен отображать.
Я действительно сбиваю с толку .. Будет здорово, если вы скажете мне, что я делаю неправильно .