使用 Retry/Cancel 对话框(在 GUI 线程中)检查 QThread 中的文件权限
Checking file permissions in a QThread, with Retry/Cancel dialog box (in the GUI thread)
我在 QThread 中有一个进程 运行,它生成一些我想保存到文件中的数据。在这样做之前,我需要检查我是否具有写入文件的适当权限(这是在 Windows 上),我真的很想使用 QtWidgets.QMessageBox
和 "Retry" 和"Cancel" 选项。例如,这很容易完成(我的典型问题是用户在 Excel 中打开了 xlsx 文件)
reply = QtWidgets.QMessageBox.warning(
self, "PermissionError",
"Could not open file \"%s\". Try closing the file if it is open." % filename,
QtWidgets.QMessageBox.StandardButton.Retry,
QtWidgets.QMessageBox.StandardButton.Cancel
)
但这只适用于主 (GUI) 线程,不适用于单独的 QThread。从 QThread 执行此操作的好方法是什么?
我用 QThread 的信号发送到 GUI 线程中打开对话框的插槽进行了一些测试。但是我发现很难将权限检查的结果传回给QThread,逻辑很快变得混乱。
根据我的经验,子分类 QThread 的方法不适用于这些情况。
一个简单的解决方案是使用 worker(QThread)-thread 方法,因为 worker 可以执行多项任务,例如进行繁重的计算和保存文件。所以GUI可以启动第一个任务,然后住在另一个线程的worker通过信号向GUI请求确认,然后GUI根据你的结果调用另一个任务。
上面的例子如下:
from PySide2 import QtCore, QtGui, QtWidgets
class Worker(QtCore.QObject):
requestSignal = QtCore.Signal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
self.data = None
@QtCore.Slot()
def process(self):
print("start process")
# emulate heavy task
QtCore.QThread.sleep(5)
self.data = "Foo"
print("end process")
self.requestSignal.emit("filename.xlsx")
@QtCore.Slot(bool)
def save_data(self, result):
print(result)
print(self.data)
class Widget(QtWidgets.QWidget):
sendResult = QtCore.Signal(bool)
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.m_button = QtWidgets.QPushButton(
"Press me", clicked=self.start_process
)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.m_button)
self.m_thread = QtCore.QThread(self)
self.m_thread.start()
self.m_worker = Worker()
self.m_worker.moveToThread(self.m_thread)
self.m_worker.requestSignal.connect(self.on_request)
self.sendResult.connect(self.m_worker.save_data)
@QtCore.Slot()
def start_process(self):
# launch process
QtCore.QTimer.singleShot(0, self.m_worker.process)
self.m_button.setDisabled(True)
@QtCore.Slot(str)
def on_request(self, filename):
reply = QtWidgets.QMessageBox.warning(
self,
"PermissionError",
'Could not open file "%s". Try closing the file if it is open.'
% filename,
QtWidgets.QMessageBox.Retry,
QtWidgets.QMessageBox.Cancel,
)
result = reply == QtWidgets.QMessageBox.Retry
print(result)
self.sendResult.emit(result)
self.m_button.setDisabled(False)
def closeEvent(self, event):
super(Widget, self).closeEvent(event)
self.m_thread.quit()
self.m_thread.wait()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
我在 QThread 中有一个进程 运行,它生成一些我想保存到文件中的数据。在这样做之前,我需要检查我是否具有写入文件的适当权限(这是在 Windows 上),我真的很想使用 QtWidgets.QMessageBox
和 "Retry" 和"Cancel" 选项。例如,这很容易完成(我的典型问题是用户在 Excel 中打开了 xlsx 文件)
reply = QtWidgets.QMessageBox.warning(
self, "PermissionError",
"Could not open file \"%s\". Try closing the file if it is open." % filename,
QtWidgets.QMessageBox.StandardButton.Retry,
QtWidgets.QMessageBox.StandardButton.Cancel
)
但这只适用于主 (GUI) 线程,不适用于单独的 QThread。从 QThread 执行此操作的好方法是什么?
我用 QThread 的信号发送到 GUI 线程中打开对话框的插槽进行了一些测试。但是我发现很难将权限检查的结果传回给QThread,逻辑很快变得混乱。
根据我的经验,子分类 QThread 的方法不适用于这些情况。
一个简单的解决方案是使用 worker(QThread)-thread 方法,因为 worker 可以执行多项任务,例如进行繁重的计算和保存文件。所以GUI可以启动第一个任务,然后住在另一个线程的worker通过信号向GUI请求确认,然后GUI根据你的结果调用另一个任务。
上面的例子如下:
from PySide2 import QtCore, QtGui, QtWidgets
class Worker(QtCore.QObject):
requestSignal = QtCore.Signal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
self.data = None
@QtCore.Slot()
def process(self):
print("start process")
# emulate heavy task
QtCore.QThread.sleep(5)
self.data = "Foo"
print("end process")
self.requestSignal.emit("filename.xlsx")
@QtCore.Slot(bool)
def save_data(self, result):
print(result)
print(self.data)
class Widget(QtWidgets.QWidget):
sendResult = QtCore.Signal(bool)
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.m_button = QtWidgets.QPushButton(
"Press me", clicked=self.start_process
)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.m_button)
self.m_thread = QtCore.QThread(self)
self.m_thread.start()
self.m_worker = Worker()
self.m_worker.moveToThread(self.m_thread)
self.m_worker.requestSignal.connect(self.on_request)
self.sendResult.connect(self.m_worker.save_data)
@QtCore.Slot()
def start_process(self):
# launch process
QtCore.QTimer.singleShot(0, self.m_worker.process)
self.m_button.setDisabled(True)
@QtCore.Slot(str)
def on_request(self, filename):
reply = QtWidgets.QMessageBox.warning(
self,
"PermissionError",
'Could not open file "%s". Try closing the file if it is open.'
% filename,
QtWidgets.QMessageBox.Retry,
QtWidgets.QMessageBox.Cancel,
)
result = reply == QtWidgets.QMessageBox.Retry
print(result)
self.sendResult.emit(result)
self.m_button.setDisabled(False)
def closeEvent(self, event):
super(Widget, self).closeEvent(event)
self.m_thread.quit()
self.m_thread.wait()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())