好吧,我想我有一些工作,但我不是 100% 确定,所以更博学的答案将不胜感激。首先,请注意:
- The
wx.Slider
小部件(我认为)是用 C 实现的;所以Python“知道”有关其执行的唯一时间是当小部件广播事件时
- The
wx.lib.agw.knobctrl.KnobCtrl
用Python实现;因此,Python 解释器“知道”(因为它必须运行它)小部件执行的每一行代码。
所以,我所做的就是尝试跟踪执行情况:
python -m trace --trace --timing test.py > test.pylog
我能注意到的是:OnSliderScroll
出现在日志中显然不是由鼠标事件触发的,而多个OnMouseEvents
会出现在knubctrl.py(鼠标悬停)中,并且只有一些会触发SetTrackPosition()
最终调用OnAngleChanged()
。但更重要的是,有一个ton of _gdi_.DC_DrawLine
在日志中!然后,看着knobctrl.py
源码中,我意识到渐变实际上是用Python绘制的for
逐行循环:
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)
...
……所以我想,这一定是很浪费时间吧!因此,下面的代码中所做的就是派生一个子类KnobCtrl
, where DrawDiagonalGradient()
因此它使用普通填充而不是渐变,这样工作速度要快得多。
因此,下面的代码使用相同的事件处理程序和相同的文本字段来比较原始变体和派生变体;你可以在以下网址观看视频https://vid.me/kM8V https://vid.me/kM8V,看起来像这样:
您会注意到,当转动原始旋钮时,textctrl 几乎没有变化(即使打印输出以预期的速度发出);但当具有“普通”背景的派生旋钮转动时(几乎与滑块一样快),更新得相当不错。我认为,当重载方法中没有任何类型的绘制时,“普通”速度会更快,但旋钮点之前的位置不会被删除。我的猜测是,原始旋钮渐变的绘制在分配的绘制时间范围内占用了太多时间,以至于 Python 需要删除该帧的其他排队更新,这里特别是文本控件的更新。
请注意,旋钮会发出两种信号:KC.EVT_KC_ANGLE_CHANGING
(这不会刷新抽奖除非e.Skip()
存在于处理程序中),并且KC.EVT_KC_ANGLE_CHANGED
;然而,据我所知,它们总是相互跟随,所以下面*CHANGED
用于两者。
当然,如果我误解了问题和解决方案,我很想知道......
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()