Найти - Пользователи
Полная версия: Торможения таймера при зарисовки цветом
Начало » GUI » Торможения таймера при зарисовки цветом
1 2 3 4
agalen
zeze
Объясните пожалуйста эти торможения, так как в Qt их нету?
В последнем примере на моем компе отрисовка выполняется 0.08 сек., а ведь там всего 270x40.
Получается, то 8% времени программа тратит впустую и тормозит питоновкий код в других потоках из-за GIL.
Простая оптимизация:
        p = QtGui.QPainter( im )
        p.fillRect( 0, 0, phi, 40, QtGui.QColor('red') )
        p.fillRect( phi, 0, 270-phi, 40, QtGui.QColor('gray') )
        del p
ускоряет выполнение примерно в 800 раз и не занимает GIL.
zeze
Loki
Окно немного “подтармаживает” в момент установки нового рисунка, сделал потокоНЕбезопасным способом, только в качестве примера:

# -*- coding: utf-8 -*-
from PyQt4 import QtCore, QtGui
import sys, math
class MyThread(QtCore.QThread):
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.running = False        
        self.phi=0.0
    def run(self):       
        self.running = True
        while self.running:
            self.emit(QtCore.SIGNAL("mysignal(QString)"),
                      "%s" % self.phi)
            if int(self.MyRadianToGradus(self.phi)+0.5)*280/360 < 280:                
                self.phi += self.MyGradusToRadian(20.0)
            else:
                self.phi = self.MyGradusToRadian(0.0)            
            self.sleep(1)
    def MyRadianToGradus(self, phiedit):
        gradus = phiedit * 360/(2*math.pi)
        return gradus
    def MyGradusToRadian(self, phiedit):
        radian = phiedit * 2*math.pi / (360)
        return radian                
class MyLabel(QtGui.QLabel):    
    def __init__(self, title=None, parent=None):
        super(MyLabel, self).__init__(title, parent)
        self.setFrameShape(QtGui.QFrame.Box)
        self.setMinimumSize(280,50)        
        self.setText("<font color='red'>Закрашивание полосы</font>")
        self.phi = 0.0    
    def MyDraw(self, phi):        
        phi = 270/6.2*phi
        im = QtGui.QImage(270, 40, QtGui.QImage.Format_ARGB32)
        for j in range(0, 270, 1):
            for i in range(39, -1, -1):                
                if j<phi:
                    im.setPixel(j, i, QtGui.QColor('gray').rgba())
                else:
                    im.setPixel(j, i, QtGui.QColor('red').rgba())
        self.setPixmap(QtGui.QPixmap(im))
    
class MyWindow(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.label = MyLabel()
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.btnStart = QtGui.QPushButton("Запустить зарисовку")
        self.btnStop = QtGui.QPushButton("Остановить зарисовку")
        self.btnStop.setEnabled(False)
        self.vbox = QtGui.QVBoxLayout()
        self.vbox.addWidget(self.label)
        self.vbox.addWidget(self.btnStart)
        self.vbox.addWidget(self.btnStop)
        self.setLayout(self.vbox)
        self.mythread = MyThread()
        self.connect(self.btnStart, QtCore.SIGNAL("clicked()"),
                     self.on_start)
        self.connect(self.btnStop, QtCore.SIGNAL("clicked()"),
                     self.on_stop)
        self.connect(self.mythread, QtCore.SIGNAL("mysignal(QString)"),
                     self.on_change, QtCore.Qt.QueuedConnection)
    def on_start(self):
        if not self.mythread.isRunning():
            self.mythread.start()
            self.btnStart.setEnabled(False)
            self.btnStop.setEnabled(True)
    def on_stop(self):
        self.mythread.running = False
        self.btnStart.setEnabled(True)
        self.btnStop.setEnabled(False)
    def on_change(self, phi):
        self.label.MyDraw(float(phi))
    def closeEvent(self, event):      
        self.hide()                    
        self.mythread.running = False  
        self.mythread.wait(5000)       
        event.accept()                 
if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    window = MyWindow()
    window.setWindowTitle("Запуск и остановка закрашивания")
    window.resize(300, 100)
    window.show()
    sys.exit(app.exec_())

А почему компилятор выдаёт 3 ошибки?:
1. File “DPythonProject/l/ld.py”, line 82, in <module>
window = MyWindow()
2. File “DPythonProject/l/ld.py”, line 46, in __init__
self.label = MyLabel()
3. File “DPythonProject/l/ld.py”, line 27, in __init__
super(MyLabel, self).__init__(title, parent)
TypeError: arguments did not match any overloaded call:
QLabel(QWidget parent=None, Qt.WindowFlags flags=0): argument 2 has unexpected type ‘NoneType’
QLabel(QString, QWidget parent=None, Qt.WindowFlags flags=0): argument 1 has unexpected type ‘NoneType’
Loki
zeze, незнаю, у меня все работает…
Попробуй все скопировать и запустить, а не кусками!
zeze
Loki
Попробуй все скопировать и запустить, а не кусками!

Я полностью копировал, может дело в том, что у меня Python 2.6?
agalen
class MyLabel(QtGui.QLabel):    
    def __init__(self, title="", parent=None):
zeze
agalen
class MyLabel(QtGui.QLabel): def __init__(self, title="", parent=None):

Большое спасибо! Помогло.

agalen
p = QtGui.QPainter(im)
 p.fillRect(0, 0, phi, 40, QtGui.QColor('red'))
p.fillRect(phi, 0, 270-phi, 40, QtGui.QColor('gray'))
del p

А этот код надо написать в место этого кода?

im = QtGui.QImage(270, 40, QtGui.QImage.Format_ARGB32)
for j in range(0, 270, 1):
       for i in range(39, -1, -1):
            if j<phi:
                 im.setPixel(j, i, QtGui.QColor('gray').rgba())
            else:
                 im.setPixel(j, i, QtGui.QColor('red').rgba())
  self.setPixmap(QtGui.QPixmap(im))

И почему когда вроде стоит зарисовка 5 секунд, зарисовывается, через каждые 0,5 секунды?
agalen
Код от Loki для python 2.6:
# -*- coding: utf-8 -*-
from PyQt4 import QtCore, QtGui
import sys, math, time
class MyThread(QtCore.QThread):
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.running = False        
        self.phi=0.0
    def run(self):       
        self.running = True
        while self.running:
            self.emit(QtCore.SIGNAL("mysignal(QString)"),
                      "%s" % self.phi)
            if int(self.MyRadianToGradus(self.phi)+0.5)*280/360 < 280:                
                self.phi += self.MyGradusToRadian(20.0)
            else:
                self.phi = self.MyGradusToRadian(0.0)            
            self.sleep(1)
    def MyRadianToGradus(self, phiedit):
        gradus = phiedit * 360/(2*math.pi)
        return gradus
    def MyGradusToRadian(self, phiedit):
        radian = phiedit * 2*math.pi / (360)
        return radian                
class MyLabel(QtGui.QLabel):    
    def __init__(self, title="", parent=None):
        super(MyLabel, self).__init__(title, parent)
        self.setFrameShape(QtGui.QFrame.Box)
        self.setMinimumSize(280,50)        
        self.setText(u"<font color='red'>Закрашивание полосы</font>")
        self.phi = 0.0    
    def MyDraw(self, phi):        
        phi = 270/6.2*phi
        im = QtGui.QImage(270, 40, QtGui.QImage.Format_ARGB32)
        #c = time.clock()
        #for j in range(0, 270, 1):
        #    for i in range(39, -1, -1):                
        #        if j<phi:
        #            im.setPixel(j, i, QtGui.QColor('gray').rgba())
        #        else:
        #            im.setPixel(j, i, QtGui.QColor('red').rgba())
        #print time.clock() - c
        c = time.clock()
        p = QtGui.QPainter( im )
        p.fillRect( 0, 0, phi, 40, QtGui.QColor('red') )
        p.fillRect( phi, 0, 270-phi, 40, QtGui.QColor('gray') )
        print time.clock() - c
        del p
        self.setPixmap(QtGui.QPixmap(im))
    
class MyWindow(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.label = MyLabel()
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.btnStart = QtGui.QPushButton(u"Запустить зарисовку")
        self.btnStop = QtGui.QPushButton(u"Остановить зарисовку")
        self.btnStop.setEnabled(False)
        self.vbox = QtGui.QVBoxLayout()
        self.vbox.addWidget(self.label)
        self.vbox.addWidget(self.btnStart)
        self.vbox.addWidget(self.btnStop)
        self.setLayout(self.vbox)
        self.mythread = MyThread()
        self.connect(self.btnStart, QtCore.SIGNAL("clicked()"),
                     self.on_start)
        self.connect(self.btnStop, QtCore.SIGNAL("clicked()"),
                     self.on_stop)
        self.connect(self.mythread, QtCore.SIGNAL("mysignal(QString)"),
                     self.on_change, QtCore.Qt.QueuedConnection)
    def on_start(self):
        if not self.mythread.isRunning():
            self.mythread.start()
            self.btnStart.setEnabled(False)
            self.btnStop.setEnabled(True)
    def on_stop(self):
        self.mythread.running = False
        self.btnStart.setEnabled(True)
        self.btnStop.setEnabled(False)
    def on_change(self, phi):
        self.label.MyDraw(float(phi))
    def closeEvent(self, event):      
        self.hide()                    
        self.mythread.running = False  
        self.mythread.wait(5000)       
        event.accept()                 
if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    window = MyWindow()
    window.setWindowTitle(u"Запуск и остановка закрашивания")
    window.resize(300, 100)
    window.show()
    sys.exit(app.exec_())
reclosedev
Причина тормозов, как правильно отметил agalen, - это попиксельная отрисовка. Она даже на С++ скорее всего бы тормозила.

По поводу организации анимации.
Все-таки, можно обойтись без потоков.

В Qt можно рисовать прямо в виджетах (без промежуточной картинки), но делать это можно только в paintEvent().
Поэтому обычно создают таймер, по сигналу которого обновляется анимация (точнее ее параметры, в данном случае длина закраски phi), и вызывается метод виджета update(), который вызовет событие перерисовки.

Почему нельзя обновлять параметры (модель, phi в данном случае) в paintEvent? Потому что paintEvent вызывается при разных событиях, например, изменение размера виджета.

В виде кода (на базе agalen и Loki):
# -*- coding: utf-8 -*-
import sys
import math
from PyQt4 import QtCore, QtGui
 
 
def phi_generator(width=720, step=10):
    phi = 0.0
    while True:
        if (math.degrees(phi) + 0.5) * width / 360 < width:
            phi += math.radians(step)
        else:
            phi = 0
        yield phi
 
 
class MyLabel(QtGui.QLabel):
    def __init__(self, title="", parent=None):
        super(MyLabel, self).__init__(title, parent)
        self.setMinimumSize(280, 50)
        self.setText(u"<font color='red'>Закрашивание полосы</font>")
        self.phi = 0.0
        self.phi_gen = phi_generator()
 
    def paintEvent(self, event):
        width, height = self.width(), self.height()
        phi = width / 6.2 * self.phi
        p = QtGui.QPainter(self)
        p.fillRect(0, 0, phi, height, QtGui.QColor('red'))
        p.fillRect(phi, 0, width - phi, height, QtGui.QColor('gray'))
 
    def animation_step(self):
        self.phi = self.phi_gen.next()
        self.update()
 
 
class MyWindow(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.label = MyLabel()
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.btnStart = QtGui.QPushButton(u"Запустить зарисовку")
        self.btnStop = QtGui.QPushButton(u"Остановить зарисовку")
        self.vbox = QtGui.QVBoxLayout()
        self.vbox.addWidget(self.label)
        self.vbox.addWidget(self.btnStart)
        self.vbox.addWidget(self.btnStop)
        self.setLayout(self.vbox)
 
        self.paint_timer = QtCore.QTimer(
            interval=50,
            timeout=self.label.animation_step,
        )
        self.btnStart.clicked.connect(self.paint_timer.start)
        self.btnStop.clicked.connect(self.paint_timer.stop)
 
 
if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    window = MyWindow()
    window.setWindowTitle(u"Запуск и остановка закрашивания")
    window.resize(300, 100)
    window.show()
    sys.exit(app.exec_())
Здесь phi_generator() упрощен, но это может быть и класс, который будет содержать различные параметры и состояние, который знает как себя обновить. Также это можно было написать внутри MyLabel.animation_step. Тут уже все зависит от логики.
Например, можно делегировать widget.paintEvent() классу анимации, а widget.update() и animation.one_step() присоединять к timer.timeout по отдельности.

P.S.
Где-то я находил хорошие примеры, но чуть поискал и сейчас не попадаются.
zeze
reclosedev
В виде кода (на базе agalen и Loki):

На основании выше изложенного кода, я написал свой пример, закрашивается всё прекрасно.

Вот код:

# -*- coding: utf-8 -*-
from PyQt4 import QtCore, QtGui, uic
import math
phi = 0.0
class ld(QtGui.QDialog):
    def __init__(self, parent=None):
        super(ld, self).__init__(parent)
        self.ui = uic.loadUi("ld.ui", self)
        self.timer = QtCore.QTimer()
        self.connect(self.timer, QtCore.SIGNAL("timeout()"), self.MyTimer)
        self.timer.setInterval(3)
        self.timer.start()
    def paintEvent(self, QPaintEvent):
        if self.ui.radioButton.isChecked():
            painterRect = QtGui.QPainter(self)
            painterRect.setBrush(QtGui.QColor('black'))
            painterRect.drawRect(50, 50, 720, 492)
            self.MyDraw()
        self.update()
    def MyRadianToGradus(self, phiedit):
        gradus = phiedit * 360 / (2 * math.pi)
        return gradus
    def MyGradusToRadian(self, phiedit):
        radian = phiedit * 2 * math.pi / (360)
        return radian
    def MyTimer(self):
        global phi
        self.update()
        if int(self.MyRadianToGradus(phi) + 0.5) * 720 / 360 < 720:
            phi += self.MyGradusToRadian(6.0)
        else:
            phi = self.MyGradusToRadian(6.0)
    def MyDraw(self):
        global phi
        p = QtGui.QPainter(self)
        p.translate(50, 50)
        p.fillRect(0, 0, int(self.MyRadianToGradus(phi)+0.5)*720/360, 492, QtGui.QColor('red'))
if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    window = ld()
    window.show()
    sys.exit(app.exec_())

reclosedev
Причина тормозов, как правильно отметил agalen, - это попиксельная отрисовка. Она даже на С++ скорее всего бы тормозила.

Как я понял всё заработало из-за того, что циклы исчезли.
А какже быть, если в место цвета я постоянно вывожу картинки из файлов. Картинки рисуются по значениям из двоичного файла.

Схема вывода картинок:

_______________________________
|—————————|—————————|
|Первая картинка| Фон ——————–|
|—————————|—————————-|
_______________________________|

________________________________
|—————————|——————————|
|Вторая картинка| Первая картинка–|
|—————————|——————————|
________________________________|

_______________________________
|————————–|——————————|
|Третья картинка| Вторая картинка–|
|————————–|—————————–|
_______________________________|

и так далее.

То есть мне надо избавится от циклов в двух моментах:
1. Вывод изображений на экран.
2. Заполнения объекта QImage значениями из файла.

Если я в пункте номер 1 избавляюсь от циклов с помощью drawImage, то от циклов во втором пункте я не смог изавится.

Может кто знает, как избавится от циклов в обоих пунктах?
reclosedev
Покажите код загрузки и отображения картинок.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB