我有一个根据MVC模型设计的程序。我的视图有一个open
按钮,我可以在其中打开文件。解析文件并对文件内容进行大量计算。
现在我想显示一个加载器来指示用户应该等待。我对Python和Qt完全不熟悉。我在Python 3.6.2中使用PyQt5(Qt 5.6.2)。
我将showLoader()
方法添加到我视图的openFiles()
方法中:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, controller, parent = None):
# initializing the window
def showOpenDialog(self):
files, filters = QtWidgets.QFileDialog.getOpenFileNames(self, 'Open file(s)', '',
"Raw files (*.rw.dat);;Data files (*.dat)" +
";;Text files (*.txt);;All files (*)")
self.showLoader("Loading file(s)")
self.doSomeStuffWithTheFiles(files)
self.hideLoader()
def showLoader(self, text):
self._progress = QtWidgets.QProgressDialog(text, "Abort", 0, 0, self);
self._progress.setWindowModality(QtCore.Qt.WindowModal);
这将显示Loader,但在文件加载后会出现。文件加载后甚至没有立即加载,但一切都完成后还需要1-2秒(包括重新绘制窗口)
我读了很多关于线程的内容,所以我假设文件解析阻塞了有意义的进度加载器。我读到我应该将QProgressDialog
添加到一个插槽中(我真的不知道那是什么)但这对我没有帮助,因为我希望{strong}之后显示<{1}} / strong> QProgressDialog
。
我还阅读了一些关于添加QFileDialog
以重新绘制窗口的内容,但这对我不起作用(或者我用错了)。
所以我的问题是:
QtWidgets.QApplication.processEvents()
方法时,如何显示QProgressDialog
?showLoader()
中显示更多信息,例如更新文字和进度,我该怎么做?进一步的问题
@ekhumoro指出的解决方案很好。我看到加载器和文件被正确解析。我现在的问题是更新现有的QProgressDialog
不起作用。
执行代码后,我看到一个小窗口弹出,但它立即消失。 (我有这样的问题,它是关于Qt背景中的C ++垃圾收集器。但是在我的理解中,布局应该保留对MainWindow
的引用,所以这对我来说没有意义。)另外ParsedDataWidget
是一个小部件,应添加到ParsedDataWidget
“内联”中,而不是显示为“窗口”。
layout
那么我需要做些什么才能使# a class that handles the data parsing of each file and creates an
# object that contains all the data with some methods...
class DataParser
def __init__(self, data):
# handle the data
# displaying the parsed data in a fancy way
class ParsedDataWidget(QtWidgets.QWidget)
def __init__(self, data):
# create some UI
# the worker class just like @ekhumoro wrote it (stripped down to
# relevant code)
class Worker(QtCore.QObject):
def run(self):
self._stop = False
for count, file in enumerate(self._files, 1):
# parse the data in the DataParser and create an object
# of the files data
data = DataParser(file)
# works fine, the data is parsed correctly
print(data)
# does not work
window.addParsedData(data)
self.loaded.emit(count, file)
if self._stop:
break
self.finished.emit()
# the window class like mentioned before (or like @ekhumoro wrote it)
class Window(QtWidgets.QWidget):
def __init__(self):
self._data_container = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
self._data_container.setLayout(layout)
def addParsedData(data):
data_widget = ParsedDataWidget(data)
layout = self._data_container.layout()
layout.addWidget(data_widget)
方法起作用?
修改
我正在尝试一些代码的变化。如果我用addParsedData
替换ParsedDataWidget
,我会得到以下结果:
如果我关闭窗口python崩溃。
解决方案
通过一些进一步的研究,我发现了我的问题:你不应该使用PyQt的线程,你应该使用QLabel
代替(written here)
所以我更改了worker的代码,我添加了另一个名为SIGNALS
的{{1}},如果加载完成则会发出该SIGNAL
。这finishedParsing
持有SIGNAL
。可能会是这样的:
DataParser
现在可以使用了!
答案 0 :(得分:1)
下面是一个实现您要求的示例。在实际使用中,QThread.sleep
行应替换为处理每个文件的函数调用。这可以定义为Worker
类的方法,也可以作为参数传递给__init__
。
import sys, os
from PyQt5 import QtCore, QtWidgets
class Worker(QtCore.QObject):
loaded = QtCore.pyqtSignal(int, str)
finished = QtCore.pyqtSignal()
def __init__(self, files):
super().__init__()
self._files = files
def run(self):
self._stop = False
for count, file in enumerate(self._files, 1):
QtCore.QThread.sleep(2) # process file...
self.loaded.emit(count, file)
if self._stop:
break
self.finished.emit()
def stop(self):
self._stop = True
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtWidgets.QPushButton('Choose Files')
self.button.clicked.connect(self.showOpenDialog)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
self.thread = QtCore.QThread()
def showOpenDialog(self):
files, filters = QtWidgets.QFileDialog.getOpenFileNames(
self, 'Open file(s)', '',
'Raw files (*.rw.dat);;Data files (*.dat)'
';;Text files (*.txt);;All files (*)',
'All files (*)')
if files and not self.thread.isRunning():
self.worker = Worker(files)
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit)
self.thread.started.connect(self.worker.run)
self.thread.finished.connect(self.worker.deleteLater)
self.showProgress(
'Loading file(s)...', len(files), self.worker.stop)
self.worker.loaded.connect(self.updateProgress)
self.thread.start()
def updateProgress(self, count, file):
if not self.progress.wasCanceled():
self.progress.setLabelText(
'Loaded: %s' % os.path.basename(file))
self.progress.setValue(count)
else:
QtWidgets.QMessageBox.warning(
self, 'Load Files', 'Loading Aborted!')
def showProgress(self, text, length, handler):
self.progress = QtWidgets.QProgressDialog(
text, "Abort", 0, length, self)
self.progress.setWindowModality(QtCore.Qt.WindowModal)
self.progress.canceled.connect(
handler, type=QtCore.Qt.DirectConnection)
self.progress.forceShow()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 100, 50)
window.show()
sys.exit(app.exec_())