Hi all,
I'd like to ask about the standard way to handle the wx.EVT_MOUSEWHEEL
on a TextCtrl widget.
This is a part of the problem described in
http://lists.wxwidgets.org/pipermail/wxpython-users/2009-January/083217.html
It seems, that, the function to determine the scrolled position (using
HitTest) can be called directly for wx.EVT_LEFT_UP and wx.EVT_KEY_UP;
for wx.EVT_SCROLLWIN it must be called via wx.CallAfter(),
but for wx.EVT_MOUSEWHEEL neither of these approaches works well. -
the mouswheel event is probably sent at the beginning of scrolling or
with some lower frequency, hence the reported position values don't
always correspond with the definitive position after the scrolling.
The following way seems to work, but it involves setting a custom
timeout, the position is periodically compared to the previous state
and finally the checking function is called using wx.CallLater (which
actually corrects the delayed results).
The part I am most concerned about, is setting the intervalls, the
needed values probably depend on the hardware parameters and while it
seems to work on computers I tested, I'm not sure, how tu make it work
generaly.
I'd be very interested in ways, how to achieve this in a better or
more general way.
(now using win XP, Python 2.54, wxPython 2.8.9.1, but the solution
should work cross platform).
Is the Timer the right thing to use here?
thanks in advance,
Vlasta
···
###########################
#! Python
# -*- coding: utf-8 -*-
import wx
class TextFrame(wx.Frame):
def __init__(self, parent, id=-1, *args, **kwargs):
wx.Frame.__init__(self, parent, id, *args, **kwargs)
self.txtField = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE
wx.TE_RICH2)
self.txtField.SetValue("\n".join((str(x).zfill(9) for x in
range(0,1001))))
self.szrTxt = wx.BoxSizer(wx.HORIZONTAL)
self.szrTxt.Add(self.txtField, 1, wx.EXPAND)
self.SetSizer(self.szrTxt)
self.txtField.Bind(wx.EVT_MOUSEWHEEL, self.on_mousewheel)
def on_mousewheel(self, evt):
evt.Skip()
self.handle_mousewheel(evt, self.show_pos_txt)
def handle_mousewheel(self, evt, function_to_call):
"""
Should ensure, that the bound function is also called after
the scrolling was done.
"""
evt.Skip()
self.saved_position = None
self.timer = None
self.rate = 100 # <-- finetune??
self.function_to_call = function_to_call
self.scrolling_widget = evt.GetEventObject()
self.start_scroll_check()
def show_pos_txt(self, evt=None):
self.SetTitle("mousewheel - scrolling position - " +
str(self.scrolling_widget.HitTest((5, 5))))
def scroll_pos_check(self, widget, evt=None):
if self.saved_position == self.show_pos_txt():
self.stop_scroll_check()
wx.CallLater(self.rate * 5, self.function_to_call) # <--
approx. intervall ??
else:
self.saved_position = self.show_pos_txt()
self.function_to_call()
def start_scroll_check(self):
if not self.timer:
self.saved_position = self.show_pos_txt()
self.timer = wx.Timer(self.scrolling_widget)
self.scrolling_widget.Bind(wx.EVT_TIMER,
self.scroll_pos_check, id=self.timer.GetId())
self.timer.Start(self.rate)
def stop_scroll_check(self):
if self.timer:
self.timer.Stop()
self.scrolling_widget.Disconnect(self.timer.GetId())
self.timer.Destroy()
self.timer = None
self.saved_position = None
self.function_to_call()
if __name__ == '__main__':
appl = wx.App(redirect=False)
frm = TextFrame(None, -1, "mousewheel - scrolling position - ??")
frm.Show()
appl.MainLoop()
###########################