- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有一个根据MVC模型设计的程序。我的 View 有一个 open
按钮,我可以在其中打开文件。文件被解析,并根据文件内容完成大量计算。
现在我想显示一个加载程序来指示用户应该等待。我对 Python 和 Qt 完全陌生。我正在使用 PyQt5 (Qt 5.6.2) 和 Python 3.6.2。
我将 showLoader()
方法添加到 View 的 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);
这将显示加载程序,但它将在文件加载后出现。甚至不是在文件加载后立即完成,而是在所有操作完成后(包括窗口的一些重画)还需要 1-2 秒
我读了很多关于线程的内容,所以我认为文件解析正在阻止进度加载器,这是有道理的。我读到我应该将 QProgressDialog 添加到插槽(我真的不知道那是什么),但这对我没有帮助,因为我希望 QProgressDialog 成为显示在QFileDialog
之后。
我还阅读了一些有关添加 QtWidgets.QApplication.processEvents()
来重新绘制窗口的内容,但这对我不起作用(或者我用错了)。
所以我的问题是:
showLoader()
方法时如何显示 QProgressDialog
?QProgressDialog
中显示更多信息,例如更新文本和进度,我该怎么做?进一步问题
@ekhumoro 指出的解决方案效果很好。我看到加载程序和文件已正确解析。我现在的问题是更新现有的 MainWindow
不起作用。
执行代码后,我看到弹出一个小窗口,但它立即消失。 (我遇到了这样的问题,它与 Qt 后台的 C++ 垃圾收集器有关。但根据我的理解,布局应该保留对 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
方法起作用?
编辑
我正在尝试对代码进行一些更改。如果我用 QLabel
替换 ParsedDataWidget
,我会得到以下结果:
如果我关闭窗口 python 就会崩溃。
解决方案
经过一些进一步的研究,我发现了我的问题:你不应该在 PyQt 中使用线程,你应该使用 SIGNALS
( written here )
所以我改变了worker的代码,我添加了另一个名为finishedParsing
的SIGNAL
,如果加载完成,它就会发出。这个信号
保存DataParser
。可能看起来像这样:
class Worker(QtCore.QObject):
finishedParsing = QtCore.pyqtSignal(DataParser)
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)
# emit a signal to let the window know that this data is
# ready to use
self.finishedParsing.emit(data)
self.loaded.emit(count, file)
if self._stop:
break
self.finished.emit()
class Window(QtWidgets.QWidget):
def showOpenDialog(self):
if files and not self.thread.isRunning():
# do the opening stuff like written before
self.worker = Worker(files)
#...
self.worker.finishedParsing.connect(self.addParsedData)
现在可以了!
最佳答案
下面是一个实现您所要求的示例。在实际使用中,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_())
关于python - QProgressDialog 仅在长时间运行的代码完成后显示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47156183/
我有一个独立的 Thread 应用程序。这是一个等待消息的监听器,当消息到达时执行一些操作,其中我必须将消息保存在数据库中。但我遇到了问题,因为如果我运行应用程序并“手动发送消息”,一切都会正常工作,
我有以下php代码: sleep(65); $query = "UPDATE database.table SET XXXXXXX = XXXXXXX - ".$YYYYYY." WHERE
我正在开发一个业余爱好应用程序。它在主布局中使用 webview。单击 webview 内的链接会使用户保持在 webview 内。启动后一切正常,但仍在应用程序内。但是,在手机休眠一段时间后,我重新
我目前运行的应用程序需要最大堆大小为 16GB。 目前我使用以下标志来处理垃圾回收。 -XX\:+UseParNewGC, -XX\:+UseConcMarkSweepGC, -XX:CMSIniti
$ uname -a Darwin Wheelie-Cyberman 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun 7 16:33:36 PDT 2011
在 while 循环仍在休眠时退出它的最简单方法是什么?是否有某种函数可以在 sleep 时检测某个值是否为真? 或者我是否在循环中设置一个小 sleep 并检查如果不再睡一会儿就退出?如果可以,我该
我正在 Ubunu 的 Jetty 6 上运行 Java Web 服务器,用于基于反向 ajax 的 Web。而且我在向浏览器重新发送数据的线程滞后方面遇到了严重的问题。很多时候,一些线程开始 hib
当我运行长时间操作时,我遇到来自 IIS 的请求超时。我的 ASP.NET 应用程序正在后台处理数据,但处理的记录数量很大,因此操作需要很长时间。 但是,我认为 IIS 使 session 超时。这是
我不确定从哪里开始解决这个问题,但如果我有一个 AJAX 网络应用程序向服务器发送请求并在数据库(在我的例子中是 postgresql)上运行长查询,有没有办法停止或如果仍在运行时用户刷新页面或关闭
我是一名优秀的程序员,十分优秀!