Хорошо, я думаю, что у меня что-то работает, но я не уверен на 100%, поэтому более эрудированный ответ будет оценен. Во-первых, обратите внимание, что:
- Виджет
wx.Slider
реализован в C; поэтому единственный раз, когда Python «знает» что-либо о его выполнении, - это когда виджет передает событие
wx.lib.agw.knobctrl.KnobCtrl
реализован в Python; поэтому интерпретатор Python «знает» (как он должен его запускать) каждую строку кода выполнения виджета.
Итак, что я сделал, это я попытался проследить исполнение, с:
python -m trace --trace --timing test.py > test.pylog
Что я мог заметить, что: OnSliderScroll
появился в журнале, по-видимому, не будучи вызваны событиями мыши, в то время как с помощью knobctrl.py (накладки) появится несколько OnMouseEvents
, и только некоторые вызовут SetTrackPosition()
, который в конечном итоге вызывает OnAngleChanged()
. Но что еще более важно, в журнале был т из _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
, где DrawDiagonalGradient()
, поэтому он использует обычную заливку вместо градиента, которая работает намного быстрее.
Итак, приведенный ниже код сравнивает исходный и производный варианты, используя один и тот же обработчик событий и одно и то же текстовое поле; Вы можете проверить видео на https://vid.me/kM8V, который выглядит примерно так:
![test2.png](https://i.stack.imgur.com/qn96m.png)
Вы заметите 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()
Прежде всего, вы создали панель и назначен Sizer к нему, но виджеты, которыми управляет sizer, имеют кадр как родительский. Мне нужно было исправить это, прежде чем я смогу взаимодействовать с виджетами OSX, так что это может быть частью вашей основной проблемы. После этого изменения в OSX (по крайней мере) текстовый виджет обновляется со скоростью, которую я ожидал бы, пока ручка будет скручена. Какую версию wxPython вы используете? – RobinDunn
Привет @RobinDunn, спасибо за комментарий! 'print (wx.VERSION_STRING)' говорит '2.8.11.0'; что касается панели/sizer, я в основном копировал ее из исходной ссылки (в коде), я не очень эффективен в 'wx' в противном случае. Должны ли виджеты иметь панель в качестве родителя? Я попытаюсь захватить видео о поведении, которое я получаю, и отправить его где-нибудь ... Приветствия! – sdaau
@RobinDunn - удалось захватить короткое видео, но ниже в ответе лучше - я добавил распечатки из обработчика событий и (если я использую 'EVT_KC_ANGLE_CHANGED' и' e.Skip() '), то он будет видимым что распечатки на приличной скорости - но обновление текстового поля не является; как будто добавляется куча событий, забирая очередь событий GUI ... Еще раз спасибо - приветствия! – sdaau