Wie schnelle Graphen zeichnen?

Plattformunabhängige GUIs mit wxWidgets.
Antworten
Hand
User
Beiträge: 65
Registriert: Sonntag 28. Januar 2007, 14:28

Ich arbeite mich gerade in Grafikausgabe ein, dazu habe ich mal ein kleines
Testprogramm geschrieben was Messwerte als Graphen aus einer List ausgibt.

Code: Alles auswählen


import wx
import threading
import time
import random

class myPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.bmp = wx.EmptyBitmap(300,300)
        dc = wx.MemoryDC(self.bmp)
        dc.SetPen(wx.RED_PEN)
        dc.DrawLine(0,0,300,300)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.val1 = [0] * 500
        self.val2 = [0] * 500
        self.x1 = 0
        self.x2 = 100
        self.x1dir = False
        self.x2dir = True
        self.t1 = threading.Thread(target=self.thread1)
        self.t1.start()

    def OnPaint(self, evt):
        wx.BufferedPaintDC(self, self.bmp, style = wx.BUFFER_VIRTUAL_AREA)

    def thread1(self):
        while 1:
            time.sleep(0.005)
            dc = wx.MemoryDC(self.bmp)
            dc.SetBackground(wx.Brush("black"))
            dc.Clear()
            if self.x1dir == False: self.x1 += random.randint(1,5)
            if self.x1dir == True:  self.x1 -= random.randint(1,5)
            if self.x2dir == False: self.x2 += random.randint(1,10)
            if self.x2dir == True:  self.x2 -= random.randint(1,10)
            if self.x1 > random.randint(50,100): self.x1dir = True
            if self.x1 <= 0: self.x1dir = False
            if self.x2 > random.randint(50,100): self.x2dir = True
            if self.x2 <= 0: self.x2dir = False
            self.val1.pop()
            self.val2.pop()
            self.val1.insert(0,self.x1)
            self.val2.insert(0,self.x2)
            for i in range(1,500):
                dc.SetPen(wx.RED_PEN)
                dc.DrawLine(i-1, self.val1[i-1], i, self.val1[i])
                dc.SetPen(wx.GREEN_PEN)
                dc.DrawLine(i-1, self.val2[i-1], i, self.val2[i])
            dc.SelectObject(wx.NullBitmap)
            self.Refresh(False)

class myFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self,None)
        panel = myPanel(self)

class myApp(wx.App):
    def OnInit(self):
        frame = myFrame(None)
        frame.Show()
        return True
        
def main():
    app = myApp(0)
    app.MainLoop()

if( __name__ == '__main__'):
    main()
Das ganze erzeugt leider zwischen 30-50% CPU Last, ist mein Ansatz total falsch bzw. wo könnte man das noch optimieren?
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi,

na ja, so etwas
Hand hat geschrieben: if self.x1dir == False: self.x1 += random.randint(1,5)
if self.x1dir == True: self.x1 -= random.randint(1,5)
kann man natürlich auch vereinfachen:

Code: Alles auswählen

if not x: do_something() # oder 'if x == False', weil deutlicher
else: do_something_else()
Du kannst auch if-elif nutzen ;-).

Warum Dein Programm nur 30 - 50 % CPU-Auslastung erzeugt, weiß ich so nicht, aber vielleicht liegt es an dem sleep() währenddessen ja nichts weiter geschieht. Wenn ein Programm nämlich "durchläuft" nimmt es immer 100 % der CPU-Power in Anspruch. Du bittest es aber eine best. Zeit zu warten und die Hände in den Schoß zu legen.

Viel interessanter finde ich die Frage: Was ist Dein Ziel? Sonst lohnt es sich nämlich eigentlich nicht am Code rumzudocktern. Willst Du realtime-Plots erzeugen? Oder nur rel. schnell komplexe Graphen erzeugen? Welche Qualität sollen die haben?

Gruß,
Christian
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo Hand!

Ohne genauer auf deinen Code einzugehen. Ich muss gleich los...

``time.sleep(0.005)`` würde ich auv ``time.sleep(0.01)`` verlängern. 0.005 schafft nur ein Realtime-Betriebssystem. Und ein normales Windows oder Linux braucht für die Abarbeitung des sleep-Befehls länger als es wirklich wartet.

BufferedPaintDC bringt nichts, wenn sich das Bild ständig ändert. Besser direkt in den DC zeichnen und das PAINT-Ereignis ignorieren. Es wird ja sowiso ständig neu gezeichnet.

Besser größere Schritte machen und länger warten. Um richtig timen zu können, könntest du z.b. alle 2 ms von einem Timer ein Event auslösen lassen. Im Event prüfen ob 10 ms vergangen sind und nur dann den Befehl zum Zeichnen geben.

10 ms -- das ruckelt zwar ein bischen, aber stört kaum. Flüssig wird es bei 5 ms. Aber das muss ein Computer erst mal schaffen. -- Müsst man ausprobieren.

Du beendest den Thread nicht. Das zeigt eine Fehlermeldung nach dem Klicken auf das X zum Schließen des Fensters.

Du greifst vom Thread aus ohne ``wx.CallAfter()`` auf den Hauptthread zu. --> Das macht dein Programm instabil

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten