Langsame GUI-Aktualisierung eines wx (Python) Widget?

Betrachte dieses Beispiel (versucht auf python2.7, Ubuntu 11.04):

import wx import wx.lib.agw.knobctrl as KC # started from: http://wxpython.org/Phoenix/docs/html/lib.agw.knobctrl.html class MyFrame(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, -1, "KnobCtrl Demo") self.panel = wx.Panel(self) self.knob1 = KC.KnobCtrl(self, -1, size=(100, 100)) self.knob1.SetTags(range(0, 151, 10)) self.knob1.SetAngularRange(-45, 225) self.knob1.SetValue(45) # explicit sizes here - cannot figure out the expands ATM self.text_ctrl_1 = wx.TextCtrl(self, -1, "0", size=(50, -1)) self.slider_1 = wx.Slider(self, -1, 0, -12, 12, style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS|wx.SL_INVERSE, size=(150, -1)) self.text_ctrl_2 = wx.TextCtrl(self, -1, "0", size=(50, -1)) main_sizer = wx.BoxSizer(wx.VERTICAL) main_sizer.Add(self.knob1, 0, wx.EXPAND | wx.ALL, 20) main_sizer.Add(self.text_ctrl_1, 0, wx.EXPAND, 20) main_sizer.Add(self.slider_1, 0, wx.EXPAND , 20) main_sizer.Add(self.text_ctrl_2, 0, wx.EXPAND, 20) self.panel.SetSizer(main_sizer) main_sizer.Layout() self.knob1.Bind(KC.EVT_KC_ANGLE_CHANGED, self.OnAngleChanged) self.slider_1.Bind(wx.EVT_SCROLL, self.OnSliderScroll) def OnAngleChanged(self, e): theknob = e.EventObject x = theknob._mousePosition.x y = theknob._mousePosition.y ang = theknob.GetAngleFromCoord(x, y) self.text_ctrl_1.SetValue("%.2f" % (ang)) self.text_ctrl_1.Refresh() # no dice def OnSliderScroll(self, e): obj = e.GetEventObject() val = obj.GetValue() self.text_ctrl_2.SetValue(str(val)) # our normal wxApp-derived class, as usual app = wx.App(0) frame = MyFrame(None) app.SetTopWindow(frame) frame.Show() app.MainLoop() 

Es ergibt sich so etwas wie folgt:

Test.png

Die Sache ist: Wenn du den Schieberegler sehr schnell verschiebst, wirst du den sechsten Textfeld aktualisieren auch ziemlich schnell. Aber wenn du den Drehknopf sehr schnell bewegst, scheint es sein Textfeld (unten) mit viel reduzierter Frequenz zu ändern!

Warum ist das; Und wäre es möglich, die Antwortgeschwindigkeit des Textfeldes des Drehknopfes so schnell wie der Schieberegler zu bekommen?

One Solution collect form web for “Langsame GUI-Aktualisierung eines wx (Python) Widget?”

Ok, ich glaube, ich habe etwas zu arbeiten, aber ich bin nicht 100% sicher, so dass eine gelehrtere Antwort geschätzt werden würde. Zuerst beachten Sie, dass:

  • Das wx.Slider Widget ist (ich glaube) in C implementiert; So dass die einzige Zeit Python "weiß" etwas über seine Ausführung ist, wenn das Widget ein Ereignis sendet
  • Die wx.lib.agw.knobctrl.KnobCtrl ist in Python implementiert; Also der Python-Interpreter "weiß" (wie er es ausführen muss) jede Zeile des Codes der Widget-Ausführung.

Also, was ich getan habe, habe ich versucht, die Hinrichtung zu verfolgen, mit:

 python -m trace --trace --timing test.py > test.pylog 

Was ich bemerken konnte, ist: OnSliderScroll erschien im Protokoll anscheinend, ohne von einem Mausereignis ausgelöst zu werden, während mehrere OnMouseEvents von knobctrl.py (die Mouseovers) erscheinen würden, und nur einige würden SetTrackPosition() auslösen, die schließlich OnAngleChanged() anruft. Aber noch wichtiger, da war eine Tonne _gdi_.DC_DrawLine im Log! Dann, mit Blick auf die knobctrl.py Quelle, erkannte ich, dass die Steigung tatsächlich in einem Python for Loop gemalt ist, line-by-line:

 def DrawDiagonalGradient(self, dc, size): ... for ii in xrange(0, maxsize, 2): currCol = (r1 + rf, g1 + gf, b1 + bf) dc.SetPen(wx.Pen(currCol, 2)) dc.DrawLine(0, ii+2, ii+2, 0) ... 

… also dachte ich, das muss die Zeit hüpfen! Also, was im Code unten getan wird, ist, dass eine Unterklasse von KnobCtrl abgeleitet KnobCtrl , wo DrawDiagonalGradient() so verwendet es eine einfache Füllung anstelle eines Gradienten, die viel schneller funktioniert.

So vergleicht der untenstehende Code das Original und die abgeleitete Variante mit demselben Event-Handler und demselben Textfeld. Sie können das Video unter https://vid.me/kM8V ausprobieren , was so aussieht:

Test2.png

Sie werden feststellen, dass sich der Textctrl kaum ändert, wenn der ursprüngliche Knopf gedreht wird (auch wenn vor allem die Ausdrucke mit der erwarteten Geschwindigkeit ausgegeben werden); Aber Updates ziemlich anständig, wenn der abgeleitete Knopf mit "plain" Hintergrund gedreht wird (fast so schnell wie der Schieberegler). Ich denke, die "Ebene" geht noch schneller, wenn es keine Auslosung irgendwelcher Art in der überladenen Methode gibt, aber dann werden die vorherigen Positionen des Knopfpunktes nicht gelöscht. Meine Vermutung ist, die Ziehungen des ursprünglichen Knopfes Steigung nehmen so viel Zeit in der zugeteilten Zeichnung Zeitrahmen, dass Python braucht, um andere in der Warteschlange Updates für diesen Rahmen fallen, hier vor allem die Aktualisierung der Text-Steuerung.

Beachten Sie, dass der Knopf sowohl KC.EVT_KC_ANGLE_CHANGING aussendet (was die Zeichnung nicht erfrischt, wenn nicht e.Skip() im Handler vorhanden ist) und KC.EVT_KC_ANGLE_CHANGED ; Aber so weit ich sehen kann, folgen sie immer einander, also unten * CHANGED wird für beide verwendet.

Natürlich, wenn ich das Problem und die Lösung falsch verstanden habe, würde ich gerne wissen …

 import wx import wx.lib.agw.knobctrl as KC # started from: http://wxpython.org/Phoenix/docs/html/lib.agw.knobctrl.html class KnobCtrlPlain(KC.KnobCtrl): def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, agwStyle=KC.KC_BUFFERED_DC): super(KnobCtrlPlain, self).__init__(parent, id, pos, size, agwStyle) def DrawDiagonalGradient(self, dc, size): col1 = self._startcolour r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue()) sizex, sizey = size # must have a filled draw here, to erase previous draws: dc.SetPen(wx.TRANSPARENT_PEN) dc.SetBrush(wx.Brush(col1, wx.SOLID)) #~ dc.DrawCircle(self.Width/2, self.Height/2, sizex) dc.DrawRectangle(0, 0, sizex, sizey) # same effect as circle; prob. faster? class MyFrame(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, -1, "KnobCtrl DemoB") self.panel = wx.Panel(self) self.knob1 = KC.KnobCtrl(self.panel, -1, size=(100, 100)) self.knob1.SetTags(range(0, 151, 10)) self.knob1.SetAngularRange(-45, 225) self.knob1.SetValue(45) self.knob2 = KnobCtrlPlain(self.panel, -1, size=(100, 100)) self.knob2.SetTags(range(0, 151, 10)) self.knob2.SetAngularRange(-45, 225) self.knob2.SetValue(45) self.text_ctrl_1 = wx.TextCtrl(self.panel, -1, "0", size=(50, -1)) self.slider_1 = wx.Slider(self.panel, -1, 0, -12, 12, style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS|wx.SL_INVERSE, size=(150, -1)) self.text_ctrl_2 = wx.TextCtrl(self.panel, -1, "0", size=(50, -1)) main_sizer = wx.BoxSizer(wx.VERTICAL) main_sizer.Add(self.knob1, 0, wx.EXPAND | wx.ALL, 20) main_sizer.Add(self.text_ctrl_1, 0, wx.EXPAND, 20) main_sizer.Add(self.knob2, 0, wx.EXPAND | wx.ALL, 20) main_sizer.Add(self.slider_1, 0, wx.EXPAND , 20) main_sizer.Add(self.text_ctrl_2, 0, wx.EXPAND, 20) self.panel.SetSizer(main_sizer) main_sizer.Layout() self.knob1.Bind(KC.EVT_KC_ANGLE_CHANGED, self.OnAngleChanged) self.knob2.Bind(KC.EVT_KC_ANGLE_CHANGED, self.OnAngleChanged) self.slider_1.Bind(wx.EVT_SCROLL, self.OnSliderScroll) def OnAngleChanged(self, e): theknob = e.EventObject x = theknob._mousePosition.x y = theknob._mousePosition.y ang = theknob.GetAngleFromCoord(x, y) strval = str("%.2f" % (ang)) print("ac: " + strval) self.text_ctrl_1.SetValue(strval) def OnSliderScroll(self, e): obj = e.GetEventObject() val = obj.GetValue() strval = str(val) print("ss: " + strval) self.text_ctrl_2.SetValue(strval) # our normal wxApp-derived class, as usual app = wx.App(0) frame = MyFrame(None) app.SetTopWindow(frame) frame.Show() app.MainLoop() 
  • Aktualisieren eines Listenfeldes in wxpython?
  • Wie man wxpython 2.8 auf Mac OSX 10.6 deinstalliert
  • WxPython app: Kein Fehler aber noch gefriert
  • Halten von GUIs, die während langwieriger Aufgaben reagieren
  • Einfrieren wegen des schnellen Eingangsstroms zu einem GNU Funkblock
  • Ziehen Sie den Knopf zwischen den Panels in wxPython
  • Führen Sie __init__ aus, nachdem das Panel erstellt wurde?
  • Wx.TextCtrl.LoadFile ()
  • Verwenden Sie Klassenvariable in einer anderen Klasse
  • WrapSize und Scrollbar: Widgets, die beim Start nicht gezeichnet werden, werden nicht später gezeichnet
  • WxPython: Wie die Optionsfelder sich an meine Wahl erinnern können, wenn ich den Rahmen schließe
  • Python ist die beste Programmiersprache der Welt.