Вращающаяся анимация во время работы другого процесса в pyqt - PullRequest
0 голосов
/ 11 ноября 2019

У меня есть простое приложение pyqt5, которое читает большой файл * .json и создает вид дерева ключей. Хотелось бы узнать, как сделать одновременную работу над анимацией и сравнительно долгой задачей чтения json. Поэтому мне нужно, чтобы колесо вращалось во время чтения файла.

Похоже, что оно должно быть многопоточным, но я еще не знаком с ним.

Вот код:

import sys 
import os 
import json
from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication, QWidget,  QGridLayout, QLabel, QListView, QTreeWidget, QTreeWidgetItem
from PyQt5.QtCore import *

current_folder = os.path.dirname(os.path.realpath(__file__))
load_icon = os.path.join(current_folder, 'icon_load.png')

class loaderDialog(QWidget):
    def __init__(self, parent=None):
        super(ren_loaderDialog, self).__init__(parent)
        self.initUI()


    def get_values_dict(self):
        """Getting data of unique values using in tables
        and making a dict of mappings for specific table
        Operation can take a long time
        """

        script_folder = current_folder
        file_local = os.path.join(script_folder, 'attributes.json')

        with open(file_local, 'r', encoding='utf-8') as fp:
            data = json.load(fp)

        return data


    def initUI(self):
        """Set GUI and get all data for launching
        """

        self.setWindowTitle('Layer loader')
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
        self.grid = QGridLayout()
        self.grid.setSpacing(10)
        self.setGeometry(500, 500, 400, 520)

        self.listView = QTreeWidget()
        self.listView.setHeaderLabel('Layers')

        self.setLayout(self.grid)
        self.grid.addWidget(self.listView, 0, 1, 1, 2)

        self.case_strings = self.get_values_dict()
        self.load_data_to_tree(self.case_strings)

        self.show()


    def loading_fig(self):
        """Animation of rotating wheel
        """

        self.spin_wheel_init = QLabel()
        self.spin_wheel_init.setAlignment(QtCore.Qt.AlignCenter)
        self.spin_wheel_init.setPixmap(QPixmap(load_icon))
        self.grid.addWidget(self.spin_wheel_init, 0, 1, 1, 2)
        angle = 0

        while True:
            tr = QTransform().rotate(angle)
            angle = angle + 1 if angle<360 else 0
            self.spin_wheel_init.setPixmap(QPixmap(load_icon).transformed(tr))
            time.sleep(0.001)
            QtCore.QCoreApplication.processEvents()


    def load_data_to_tree(self, data):
        """Giving keys to treeview
        """
        for name in data:
            child = QTreeWidgetItem(self.listView)
            child.setFlags(child.flags() | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable)
            child.setText(0, name)
            child.setCheckState(0, Qt.Unchecked)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = loaderDialog()
    w.show()
    sys.exit(app.exec_())   

Здесь мне нужно, чтобы функция loading_fig работала, пока файл читает в функции, называемой get_values_dict.

1 Ответ

1 голос
/ 11 ноября 2019

Вам следует избегать использования циклов и sleep в вашем потоке пользовательского интерфейса.

Распространенным способом запуска функции длительного времени является создание другого потока. Вы можете сделать это с QThread классом Qt (или в Python, если хотите).

Чтобы использовать QThread, создайте новый класс Worker, наследующий от QObject. Он будет содержать ваш процесс. Затем создайте экземпляр и переместите в другой поток:

class Worker(QObject):
    done = pyqtSignal(list)

    def __init__(self, parent=None):
        super().__init__(parent)

    def doWork(self):
        print("Start")
        time.sleep(10)
        self.done.emit(['one', 'two', 'three'])
        print("done")
class loaderDialog(QWidget):
    def __init__(self, parent=None):
        super(loaderDialog, self).__init__(parent)
        self.initUI()
        self.thread = QThread(self)
        self.worker = Worker()
        self.worker.moveToThread(self.thread) # worker will be runned in another thread
        self.worker.done.connect(self.load_data_to_tree) # Call load_data_to_tree when worker.done is emitted
        self.thread.started.connect(self.worker.doWork) # Call worker.doWork when the thread starts
        self.thread.start() # Start the thread (and run doWork)

Для счетчика вы должны использовать анимацию, например QPropertyAnimation вместо цикла. Например:

class Spinner(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAlignment(QtCore.Qt.AlignCenter)
        self.pixmap = QPixmap(load_icon)

        self.setFixedSize(30, 30)
        self._angle = 0

        self.animation = QPropertyAnimation(self, b"angle", self)
        self.animation.setStartValue(0)
        self.animation.setEndValue(360)
        self.animation.setLoopCount(-1)
        self.animation.setDuration(2000)
        self.animation.start()


    @pyqtProperty(int)
    def angle(self):
        return self._angle

    @angle.setter
    def angle(self, value):
        self._angle = value
        self.update()


    def paintEvent(self, ev=None):
        painter = QPainter(self)
        painter.translate(15, 15)
        painter.rotate(self._angle)
        painter.translate(-15, -15)
        painter.drawPixmap(5, 5, self.pixmap)
...