Проверка прав доступа к файлам в QThread с диалоговым окном «Повторить/Отмена» (в потоке графического интерфейса)

У меня есть процесс, работающий в QThread, который генерирует некоторые данные, которые я хочу сохранить в файл. Прежде чем сделать это, мне нужно убедиться, что у меня есть надлежащие разрешения для записи в файл (это в Windows), и я бы очень хотел использовать QtWidgets.QMessageBox с опциями «Повторить попытку» и «Отмена». Это легко сделать, например, с помощью (типичная проблема в моем случае заключается в том, что у пользователя есть файл xlsx, открытый в Excel)

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, отправленным в слот в потоке графического интерфейса, который открывает диалог. Но мне трудно сообщить QThread результат проверки разрешений, и логика быстро становится беспорядочной.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
32
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Из моего опыта подход к подклассифицировать QThread не годится для этих случаев.

Простое решение — использовать подход рабочий (QThread)-поток, поскольку у исполнителя может быть несколько задач, таких как выполнение тяжелых вычислений и сохранение файла. Таким образом, 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_())

Это похоже на решение, к которому я склонялся, но не смог разобраться в логике. Вы проделали хорошую работу, так что я попробую!

Filip S. 13.06.2019 11:28

«подклассификация QThread не подходит для этих случаев» - какой подход вы бы предложили вместо этого?

Filip S. 13.06.2019 11:28

@ФилипС. Как я указываю в своем ответе: используйте рабочий (QObject), который живет в другом потоке

eyllanesc 13.06.2019 11:29

О, теперь я вижу разницу. Спасибо!

Filip S. 13.06.2019 12:41

Другие вопросы по теме