Анимированный статический Gif в QStyleSheet

Я хочу настроить внешний вид QProgressBar с помощью таблицы стилей. Я хотел бы придать ему этот оживленный, «неопределенный» вид. Поэтому я сделал анимированный GIF, чтобы вставить его на задний план QProgressBar::chunk.

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

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


person Murphy Randle    schedule 08.08.2012    source источник


Ответы (1)


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

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

«Неопределенный» вид, к которому вы стремитесь (например, счетчик GIF 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
        ...

У меня есть виджет-загрузчик, который я использую всякий раз, когда делаю что-то подобное в своей библиотеке графического интерфейса. Если вы хотите увидеть код для него/использовать его, он находится по адресу 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
        ...
person Eric Hulser    schedule 10.08.2012
comment
Какой отличный ответ, @Eric! Спасибо, что так подробно рассказали. Я отмечу это как правильный ответ, потому что я думаю, что это поможет большинству людей. В моих конкретных обстоятельствах у меня уже есть асинхронный процесс, обновляющий прогресс на моей панели. Я хотел применить анимированный GIF в качестве фона готовой части индикатора выполнения, чтобы он создавал визуальное впечатление непрерывной работы. Тем не менее, возможно, небольшой зацикленный QMovie сбоку от панели был бы столь же эффективным. Благодарю вас! - person Murphy Randle; 12.08.2012
comment
О, я вижу... тогда я неправильно понял вопрос... в этом случае я действительно не знаю ответа, если только вы не сделали это на заказ, где у вас был зацикленный QMovie и метка, на ширину которой вы влияли на основе ваш завершенный процент. Был бы полезный виджет. - person Eric Hulser; 13.08.2012