Анимированная Gif-статика в QStyleSheet

Я хочу настроить внешний вид QProgressBar с помощью таблицы стилей. Я бы хотел придать этому анимированному "неопределенному" виду. Так что я сделал анимированный GIF, чтобы прикрепить на фоне QProgressBar::chunk,

Он хорошо отображается, но изображение статично, без анимации. Есть предложения или обходные пути?

Я использую PyQt 4.9.4 на OS X 10.8.

1 ответ

Решение

Итак, это немного более сложная проблема, с которой вы сталкиваетесь.

QProgressBar полагается на информацию из цикла или некоторую определенную сумму, чтобы представить процент выполнения. Когда вы устанавливаете значение в своем коде, Qt устанавливает значение индикатора выполнения, перерисовывает его и, таким образом, отображает обновленное значение. На самом деле это занимает несколько миллисекунд обработки в вашем цикле. Если вы этого не сделаете, тогда оптимизация Qt никогда не будет перерисована. Это синхронно.

"Неопределенный" внешний вид, к которому вы стремитесь (например, GJ-вращатель AJAX или полоса), используется в Интернете, потому что процесс фактически переместился с клиентской стороны и обрабатывается на сервере и ожидает ответа. Процесс клиента вообще не блокируется, поэтому он может обновить интерфейс с фильмом.

Вы можете добиться нужного вам вида, используя QMovie в QLabel:

movie = QMovie()
movie.setFileName('/path/to/ajax_loader.gif')
movie.start()

label = QLabel(parent)
label.setMovie(movie)

Это сделает ваш неопределенный счетчик. Однако он будет воспроизводиться только до тех пор, пока цикл обработки событий обрабатывает события. Как только вы запускаете фактический рабочий процесс, он блокирует цикл событий, поэтому ваш фильм начнет воспроизводиться.

Вам нужно будет на самом деле "прокачать" события игроку в вашем цикле, чтобы он обновился. Вы можете сделать это:

app = QApplication.instance()

# show my label
label.show()

for obj in objs:
    # process stuff on my obj
    app.processEvents()

# hide the label
label.hide()

Конечно, в зависимости от того, сколько времени занимает каждое отдельное действие, которое вы выполняете в цикле, ваш ролик / загрузчик будет "зависать", пока события не будут обработаны снова.

Действительно, лучший способ добиться желаемого внешнего вида - это многопоточное приложение. Вы можете использовать QThread для выполнения всех ваших действий и отображения изображения загрузчика пользователю во время его обработки. Это больше похоже на работу ajax - основной цикл событий Qt будет продолжать работать, пока ваш рабочий поток обрабатывает все - в течение неизвестного промежутка времени. Это асинхронно.

Что-то вроде:

class MyThread(QThread):
    def run( self ):
       # perform some actions
       ...

class MyDialog(QDialog):
    def __init__( self, parent ):
        # initialize the dialog
        ...
        self._thread = MyThread(self)
        self._thread.finished.connect(self.refreshResults)

        self.refresh()

    def refresh( self ):
        # show the ajax spinner
        self._ajaxLabel.show()

        # start the thread and wait for the results
        self._thread.start()

    def refreshResults( self ):
        # hide the ajax spinner
        self._ajaxLabel.hide()

        # process the thread results
        ...

У меня есть виджет загрузчика, который я использую каждый раз, когда делаю подобные вещи в своей библиотеке GUI. Если вы хотите увидеть код для него / использовать его, он находится по адресу http://dev.projexsoftware.com/projects/projexui и класс XLoaderWidget (projexui.widgets.xloaderwidget)

Настройка такая же, как указано выше, но будет просто:

from projexui.widgets.xloaderwidget import XLoaderWidget

class MyThread(QThread):
    def run( self ):
       # perform some actions
       ...

class MyDialog(QDialog):
    def __init__( self, parent ):
        # initialize the dialog
        ...
        self._thread = MyThread(self)
        self._thread.finished.connect(self.refreshResults)

        self.refresh()

    def refresh( self ):
        # show the ajax spinner
        XLoaderWidget.start(self)

        # start the thread and wait for the results
        self._thread.start()

    def refreshResults( self ):
        # hide the ajax spinner
        XLoaderWidget.stop(self)

        # process the thread results
        ...
Другие вопросы по тегам