Add mouse events, event demo.
--- a/demos/03_application.py Mon Feb 16 21:17:43 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-#!/usr/bin/env python3
-
-from tuikit.core.application import Application
-from tuikit.widgets.label import Label
-from tuikit.widgets.button import Button
-from tuikit.widgets.textfield import TextField
-
-label = Label('Hello there!')
-button1 = Button()
-button2 = Button()
-field = TextField('text field')
-
-app = Application()
-app.root_window.add(label, 20, 10)
-app.root_window.add(button1, 20, 20)
-app.root_window.add(button2, 30, 20)
-app.root_window.add(field, 20, 30)
-app.root_window.focus_widget = field
-
-def on_keypress(ev):
- if ev.keyname == 'escape':
- app.stop()
-
-app.window_manager.sig_keypress.connect(on_keypress)
-
-app.start()
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/03_event.py Sat Feb 21 12:01:57 2015 +0100
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+
+from tuikit.core.buffer import Buffer
+from tuikit.driver.cursesw import CursesWDriver
+
+buffer = Buffer()
+line = 0
+with CursesWDriver() as driver:
+ buffer.resize(*driver.size)
+ buffer.puts(str(driver.size), 0, 10)
+ while True:
+ for event in driver.getevents():
+ buffer.puts(str(event), 0, line)
+ line += 1
+ driver.draw(buffer)
+ driver.flush()
+ if line >= buffer.size.h:
+ buffer.fill()
+ line = 0
--- a/demos/04_texteditor.py Mon Feb 16 21:17:43 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-#!/usr/bin/env python3
-
-import sys
-
-from tuikit.core.application import Application
-#from tuikit.scrollview import ScrollView
-from tuikit.widgets.textbox import TextBox
-
-
-class MyApplication(Application):
-
- def __init__(self):
- Application.__init__(self)
- self.window_manager.sig_keypress.connect(self.on_wm_keypress)
- #self.top.add_handler('keypress', self.on_top_keypress)
-
- t = open(sys.argv[0]).read()
- editbox = TextBox(t)
-
- #scroll = ScrollView()
- #scroll.add(editbox)
-
- self.root_window.add(editbox)
- #self.root_window.add(scroll, halign='fill', valign='fill')
-
- def on_wm_keypress(self, ev):
- if ev.keyname == 'escape':
- self.stop()
- return True
-
-if __name__ == '__main__':
- app = MyApplication()
- app.start()
-
--- a/demos/05_fixedlayout.py Mon Feb 16 21:17:43 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-#!/usr/bin/env python3
-
-from tuikit.core.application import Application
-from tuikit.widgets.button import Button
-
-app = Application()
-app.add(Button('left=0'), left=0)
-app.add(Button('left=5'), left=5, top=2)
-app.add(Button('right=0'), right=0)
-app.add(Button('right=5'), right=5, top=2)
-app.add(Button('center=x'), center='x', top=3)
-app.add(Button('center=x, left=5'), center='x', left=5, top=5)
-app.add(Button('center=x, right=5'), center='x', right=5, top=7)
-app.add(Button('center=xy'), center='xy')
-app.add(Button('fill=x'), fill='x', top=9)
-app.add(Button('fill=x, left=5'), fill='x', left=5, top=11)
-app.add(Button('fill=x, right=5'), fill='x', right=5, top=13)
-app.add(Button('fill=x, left=5, right=5'), fill='x', left=5, right=5, top=15)
-
-app.window_manager.sig_keypress.connect(lambda ev: app.stop())
-app.start()
--- a/demos/06_gridlayout.py Mon Feb 16 21:17:43 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-#!/usr/bin/env python3
-
-from tuikit.layouts.grid import GridLayout
-from tuikit.widgets.label import Label
-
-l1 = Label('Hello')
-l1.sizemin.update(10, 1)
-
-grid = GridLayout()
-grid.add(l1, 1, 1)
-grid.update(10, 10)
-
-print(grid._grid_size)
-print(grid._grid)
-
-for row in range(grid.row_count):
- for col in range(grid.column_count):
- w = grid.get_widget_at(row, col)
- name = w.name if w else '--'
- print(name.center(16), end='')
- print()
-
-print(l1.pos)
-print(l1.size)
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/10_application.py Sat Feb 21 12:01:57 2015 +0100
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+
+from tuikit.core.application import Application
+from tuikit.widgets.label import Label
+from tuikit.widgets.button import Button
+from tuikit.widgets.textfield import TextField
+
+label = Label('Hello there!')
+button1 = Button()
+button2 = Button()
+field = TextField('text field')
+
+app = Application()
+app.root_window.add(label, 20, 10)
+app.root_window.add(button1, 20, 20)
+app.root_window.add(button2, 30, 20)
+app.root_window.add(field, 20, 30)
+app.root_window.focus_widget = field
+
+def on_keypress(ev):
+ if ev.keyname == 'escape':
+ app.stop()
+
+app.window_manager.sig_keypress.connect(on_keypress)
+
+app.start()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/11_fixedlayout.py Sat Feb 21 12:01:57 2015 +0100
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+
+from tuikit.core.application import Application
+from tuikit.widgets.button import Button
+
+app = Application()
+app.add(Button('left=0'), left=0)
+app.add(Button('left=5'), left=5, top=2)
+app.add(Button('right=0'), right=0)
+app.add(Button('right=5'), right=5, top=2)
+app.add(Button('center=x'), center='x', top=3)
+app.add(Button('center=x, left=5'), center='x', left=5, top=5)
+app.add(Button('center=x, right=5'), center='x', right=5, top=7)
+app.add(Button('center=xy'), center='xy')
+app.add(Button('fill=x'), fill='x', top=9)
+app.add(Button('fill=x, left=5'), fill='x', left=5, top=11)
+app.add(Button('fill=x, right=5'), fill='x', right=5, top=13)
+app.add(Button('fill=x, left=5, right=5'), fill='x', left=5, right=5, top=15)
+
+app.window_manager.sig_keypress.connect(lambda ev: app.stop())
+app.start()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/12_gridlayout.py Sat Feb 21 12:01:57 2015 +0100
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+
+from tuikit.layouts.grid import GridLayout
+from tuikit.widgets.label import Label
+
+l1 = Label('Hello')
+l1.sizemin.update(10, 1)
+
+grid = GridLayout()
+grid.add(l1, 1, 1)
+grid.update(10, 10)
+
+print(grid._grid_size)
+print(grid._grid)
+
+for row in range(grid.row_count):
+ for col in range(grid.column_count):
+ w = grid.get_widget_at(row, col)
+ name = w.name if w else '--'
+ print(name.center(16), end='')
+ print()
+
+print(l1.pos)
+print(l1.size)
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/20_texteditor.py Sat Feb 21 12:01:57 2015 +0100
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+
+import sys
+
+from tuikit.core.application import Application
+#from tuikit.scrollview import ScrollView
+from tuikit.widgets.textbox import TextBox
+
+
+class MyApplication(Application):
+
+ def __init__(self):
+ Application.__init__(self)
+ self.window_manager.sig_keypress.connect(self.on_wm_keypress)
+ #self.top.add_handler('keypress', self.on_top_keypress)
+
+ t = open(sys.argv[0]).read()
+ editbox = TextBox(t)
+
+ #scroll = ScrollView()
+ #scroll.add(editbox)
+
+ self.root_window.add(editbox)
+ #self.root_window.add(scroll, halign='fill', valign='fill')
+
+ def on_wm_keypress(self, ev):
+ if ev.keyname == 'escape':
+ self.stop()
+ return True
+
+if __name__ == '__main__':
+ app = MyApplication()
+ app.start()
+
--- a/tuikit/core/container.py Mon Feb 16 21:17:43 2015 +0100
+++ b/tuikit/core/container.py Sat Feb 21 12:01:57 2015 +0100
@@ -91,21 +91,20 @@
if Widget.keypress_event(self, ev):
return True
- def mousedown(self, button, pos):
- self.mouse_child = None
- for child in reversed(self.children):
- if pos in child.boundaries:
- child.mousedown(button, pos - child.pos)
- self.mouse_child = child
+ def mousedown_event(self, ev):
+ self.mouse_widget = None
+ for child in reversed(self._widgets):
+ if ev.pos in child.boundaries:
+ child.mousedown_event(ev.rebase(child.pos))
+ self.mouse_widget = child
- def mouseup(self, button, pos):
- if self.mouse_child:
- self.mouse_child.mouseup(button, pos - self.mouse_child.pos)
+ def mouseup_event(self, ev):
+ if self.mouse_widget:
+ self.mouse_widget.mouseup_event(ev.rebase(self.mouse_widget.pos))
- def mousemove(self, button, pos, relpos):
- if self.mouse_child:
- self.mouse_child.mousemove(button,
- pos - self.mouse_child.pos, relpos)
+ def mousemove_event(self, ev):
+ if self.mouse_widget:
+ self.mouse_widget.mousemove_event(ev.rebase(self.mouse_widget.pos))
## focus ##
--- a/tuikit/core/events.py Mon Feb 16 21:17:43 2015 +0100
+++ b/tuikit/core/events.py Sat Feb 21 12:01:57 2015 +0100
@@ -51,3 +51,16 @@
res.append(mod)
res.append(self.keyname or self.char)
return sep.join(res)
+
+
+class MouseEvent(Event):
+
+ def __init__(self, name, button, pos, relpos=None):
+ Event.__init__(self, name,
+ ('name', 'button', 'pos', 'relpos'),
+ (name, button, pos, relpos))
+
+ def rebase(self, zero_pos):
+ """Return new MouseEvent with position rebased to zero_pos."""
+ pos = self.pos - zero_pos
+ return MouseEvent(self.name, self.button, pos, self.relpos)
--- a/tuikit/core/widget.py Mon Feb 16 21:17:43 2015 +0100
+++ b/tuikit/core/widget.py Sat Feb 21 12:01:57 2015 +0100
@@ -130,27 +130,24 @@
Override to accept keyboard input.
- Returns True if event was consumed.
+ Return True if event was consumed.
- Call this implementation from inherited classes
- if it does not consume the event.
+ Call parent implementation from inherited classes
+ when not consuming the event.
"""
if self.sig_keypress(ev):
return True
self.log.debug('Not consumed: %s', ev)
- def mousedown(self, button, pos):
- self._log.debug('mousedown(btn=%r, pos=%r)',
- button, pos)
+ def mousedown_event(self, ev):
+ self.log.debug('Not consumed: %s', ev)
- def mouseup(self, button, pos):
- self._log.debug('mouseup(btn=%r, pos=%r)',
- button, pos)
+ def mouseup_event(self, ev):
+ self.log.debug('Not consumed: %s', ev)
- def mousemove(self, button, pos, relpos):
- self._log.debug('mousemove(btn=%r, pos=%r, relpos=%r)',
- button, pos, relpos)
+ def mousemove_event(self, ev):
+ self.log.debug('Not consumed: %s', ev)
## timeouts ##
--- a/tuikit/driver/cursesw.py Mon Feb 16 21:17:43 2015 +0100
+++ b/tuikit/driver/cursesw.py Sat Feb 21 12:01:57 2015 +0100
@@ -3,7 +3,7 @@
import logging
from tuikit.driver.driver import Driver
-from tuikit.core.events import ResizeEvent, KeypressEvent
+from tuikit.core.events import ResizeEvent, KeypressEvent, MouseEvent
from tuikit.core.coords import Point
@@ -106,7 +106,12 @@
self.stdscr.addstr(y, x, ch)
else:
raise TypeError('Integer or one-char string is required.')
- except curses.error as e:
+ except curses.error:
+ if x == self.size.w - 1 and y == self.size.h - 1:
+ # Curses putch to lower-right corner gives error because
+ # scrolling is disabled and cursor cannot move to next char.
+ # Let's just ignore that for now.
+ return
self._log.exception('putch(%r, %s, %s) error:' % (ch, x, y))
def draw(self, buffer, x=0, y=0):
@@ -162,7 +167,7 @@
timeout -- float, in seconds (None=infinite)
Returns:
- [('event', param1, ...), ...]
+ List of Event objects.
"""
# Set timeout
@@ -200,21 +205,21 @@
return res
def _process_mouse(self):
- out = []
+ res = []
try:
_id, x, y, _z, bstate = curses.getmouse()
except curses.error:
- return out
+ return res
pos = Point(x, y)
if bstate & curses.REPORT_MOUSE_POSITION:
if self._mouse_last_pos != pos:
if self._mouse_last_pos:
relpos = pos - self._mouse_last_pos
- out += [('mousemove', 0, pos, relpos)]
+ res.append(MouseEvent('mousemove', 0, pos, relpos))
self._mouse_last_pos = pos
- # we are interested only in changes, not buttons already pressed before event
+ # We are interested only in changes, not buttons already pressed before event
if self._mouse_last_bstate is not None:
old = self._mouse_last_bstate
new = bstate
@@ -224,23 +229,23 @@
self._mouse_last_bstate = bstate
if bstate & curses.BUTTON1_PRESSED:
- out += [('mousedown', 1, pos)]
+ res.append(MouseEvent('mousedown', 1, pos))
if bstate & curses.BUTTON2_PRESSED:
- out += [('mousedown', 2, pos)]
+ res.append(MouseEvent('mousedown', 2, pos))
if bstate & curses.BUTTON3_PRESSED:
- out += [('mousedown', 3, pos)]
+ res.append(MouseEvent('mousedown', 3, pos))
if bstate & curses.BUTTON1_RELEASED:
- out += [('mouseup', 1, pos)]
+ res.append(MouseEvent('mouseup', 1, pos))
if bstate & curses.BUTTON2_RELEASED:
- out += [('mouseup', 2, pos)]
+ res.append(MouseEvent('mouseup', 2, pos))
if bstate & curses.BUTTON3_RELEASED:
- out += [('mouseup', 3, pos)]
+ res.append(MouseEvent('mouseup', 3, pos))
- # reset last pos when pressed/released
- if len(out) > 0 and out[-1][0] in ('mousedown', 'mouseup'):
+ # Reset last pos when pressed/released
+ if len(res) > 0 and res[-1].name in ('mousedown', 'mouseup'):
self._mouse_last_pos = None
- return out
+ return res
def _split_keyname_mods(self, keyname):
"""Parse keynames in form "shift+tab", return (keyname, mod)."""
--- a/tuikit/driver/driver.py Mon Feb 16 21:17:43 2015 +0100
+++ b/tuikit/driver/driver.py Sat Feb 21 12:01:57 2015 +0100
@@ -86,6 +86,7 @@
def __enter__(self):
self.init()
+ return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
--- a/tuikit/scrollwindow.py Mon Feb 16 21:17:43 2015 +0100
+++ b/tuikit/scrollwindow.py Sat Feb 21 12:01:57 2015 +0100
@@ -14,4 +14,3 @@
def add(self, widget, **kwargs):
Window.add(self, widget, **kwargs)
self._connect_child(widget)
-
--- a/tuikit/widgets/button.py Mon Feb 16 21:17:43 2015 +0100
+++ b/tuikit/widgets/button.py Sat Feb 21 12:01:57 2015 +0100
@@ -24,6 +24,7 @@
self.color = 'default'
self.color_active = 'default on red'
+ self.color_highlight = 'default on yellow'
self.highlight = False
self.sig_clicked = Signal()
@@ -47,6 +48,8 @@
self.color_active = theme.button_active
def _get_color(self):
+ if self.highlight:
+ return self.color_highlight
if self.has_focus():
return self.color_active
return self.color
@@ -71,20 +74,18 @@
# suffix
buffer.puts(self.suffix, pos)
- def on_mousedown(self, ev):
+ def mousedown_event(self, ev):
self.highlight = True
- self.redraw()
+ #self.redraw()
- def on_mouseup(self, ev):
+ def mouseup_event(self, ev):
self.highlight = False
- self.redraw()
+ #self.redraw()
+ self.sig_clicked()
- if self.enclose(ev.px, ev.py):
- self.emit('click')
-
- def on_keypress(self, ev):
+ def keypress_event(self, ev):
if ev.keyname == 'enter':
- self.emit('click')
+ self.sig_clicked()
def _divide_padding(self, pad):
# default is 'left'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tuikit/widgets/scrollbar.py Sat Feb 21 12:01:57 2015 +0100
@@ -0,0 +1,212 @@
+# -*- coding: utf-8 -*-
+
+from tuikit.core.widget import Widget
+from tuikit.core.signal import Signal
+
+
+class Scrollbar(Widget):
+
+ """Abstract base class for scrollbars."""
+
+ def __init__(self):
+ Widget.__init__(self)
+
+ # Scrolling range is 0 .. _scroll_max
+ self._scroll_max = self._get_length() - 3
+ # Current position of scrollbar in scrolling range.
+ self._scroll_pos = 0
+ # Current position of thumb on scrollbar - used for draw.
+ self._thumb_pos = 0
+ # Auxilliary variable, True when user holds mouse on thumb.
+ self._dragging = False
+ # Auxilliary variable, 'up' or 'down' depending on last move direction.
+ self._move = None
+
+ #: delay before continuous scrolling when user holds mouse on scrollbar arrow
+ self.scroll_delay = 0.150
+ #: interval for continuous scrolling
+ self.scroll_interval = 0.030
+
+ # change event is emitted when user moves scrollbar (even programmatically)
+ self.sig_changed = Signal()
+
+ @property
+ def scroll_max(self):
+ """Maximum for scrolling position."""
+ return self._scroll_max
+
+ @scroll_max.setter
+ def scroll_max(self, value):
+ if value < 0:
+ value = 0
+ self._scroll_max = value
+ if self._scroll_pos > value:
+ self._scroll_pos = value
+ self.sig_changed()
+ self._update_thumb_pos()
+
+ @property
+ def scroll_pos(self):
+ """Scrolling position.
+
+ Integer number between 0 and 'max'.
+
+ """
+ return self._scroll_pos
+
+ @scroll_pos.setter
+ def scroll_pos(self, value):
+ if self._scroll_pos != value:
+ self._scroll_pos = value
+ self._update_thumb_pos()
+ self.sig_changed()
+
+ def move_up(self):
+ """Move scrolling position up/left."""
+ if self._scroll_pos > 0:
+ self.scroll_pos = self._scroll_pos - 1
+ self._move = 'up'
+
+ def move_down(self):
+ """Move scrolling position down/right."""
+ if self._scroll_pos < self._scroll_max:
+ self.scroll_pos = self._scroll_pos + 1
+ self._move = 'down'
+
+ def drag(self, mouse_pos):
+ """Scroll using mouse drag on thumb.
+
+ Args:
+ mouse_pos: new position of mouse, in range 0 .. self._get_length()
+
+ """
+ new_pos = int(round((mouse_pos - 1) / (self._get_length() - 3) * self._scroll_max))
+ if new_pos < 0:
+ new_pos = 0
+ if new_pos > self._scroll_max:
+ new_pos = self._scroll_max
+ if self._scroll_pos != new_pos:
+ self.scroll_pos = new_pos
+
+ def _get_length(self):
+ """Return length of scrollbar.
+
+ This will be widget height for vertical scrollbar,
+ width for horizontal scrollbar.
+
+ """
+ raise NotImplementedError()
+
+ def _update_thumb_pos(self):
+ """Update value of internal variable _thumb_pos."""
+ self._thumb_pos = 0
+ if self._scroll_max and self._scroll_pos <= self._scroll_max:
+ self._thumb_pos = int(round(self._scroll_pos / self._scroll_max * (self._get_length() - 3)))
+ #self.redraw()
+
+ def _continuous_scroll(self):
+ if self._move == 'up':
+ self.move_up()
+ if self._move == 'down':
+ self.move_down()
+ self.add_timeout(self.scroll_interval, self._continuous_scroll)
+
+
+class VScrollbar(Scrollbar):
+ def __init__(self):
+ Scrollbar.__init__(self)
+ self.sizereq.update(1, 20)
+
+ def draw(self, buffer):
+ Widget.draw(self, buffer)
+ ug = ev.driver.unigraph
+ ev.driver.pushcolor('normal')
+ ev.driver.putch(ev.x, ev.y, ug.get_char('sb_up'))
+ for i in range(1, self.height - 1):
+ ev.driver.putch(ev.x, ev.y + i, ug.get_char('sb_vtrack'))
+ ev.driver.putch(ev.x, ev.y + 1 + self._thumb_pos, ug.get_char('sb_thumb'))
+ ev.driver.putch(ev.x, ev.y + self.height - 1, ug.get_char('sb_down'))
+ ev.driver.popcolor()
+
+ def on_mousedown(self, ev):
+ self._dragging = False
+ self._move = None
+ # arrow buttons
+ if ev.wy == 0 or ev.wy == self.height - 1:
+ if ev.wy == 0:
+ self.move_up()
+ else:
+ self.move_down()
+ self.add_timeout(self.scroll_delay, self._continuous_scroll)
+ return
+ # thumb bar
+ if ev.wy == 1 + self._thumb_pos:
+ self._dragging = True
+ return
+
+ def on_mousemove(self, ev):
+ if self._dragging:
+ self.drag(ev.wy)
+
+ def on_mouseup(self, ev):
+ if self._dragging:
+ self.drag(ev.wy)
+ self._dragging = False
+ return
+ if self._move:
+ self.remove_timeout(self._continuous_scroll)
+ self._move = None
+ return
+
+ def _get_length(self):
+ return self.height
+
+
+class HScrollbar(Scrollbar):
+ def __init__(self):
+ Scrollbar.__init__(self)
+ self.sizereq.update(20, 1)
+
+ def on_draw(self, ev):
+ ug = ev.driver.unigraph
+ ev.driver.pushcolor('normal')
+ ev.driver.putch(ev.x, ev.y, ug.get_char('sb_left'))
+ for i in range(1, self.width - 1):
+ ev.driver.putch(ev.x + i, ev.y, ug.get_char('sb_htrack'))
+ ev.driver.putch(ev.x + 1 + self._thumb_pos, ev.y, ug.get_char('sb_thumb'))
+ ev.driver.putch(ev.x + self.width - 1, ev.y, ug.get_char('sb_right'))
+ ev.driver.popcolor()
+
+ def on_mousedown(self, ev):
+ self._dragging = False
+ self._move = None
+ # arrow buttons
+ if ev.wx == 0 or ev.wx == self.width - 1:
+ if ev.wx == 0:
+ self.move_up()
+ else:
+ self.move_down()
+ self.add_timeout(self.scroll_delay, self._continuous_scroll)
+ return
+ # thumb bar
+ if ev.wx == 1 + self._thumb_pos:
+ self._dragging = True
+ return
+
+ def on_mousemove(self, ev):
+ if self._dragging:
+ self.drag(ev.wx)
+
+ def on_mouseup(self, ev):
+ if self._dragging:
+ self.drag(ev.wx)
+ self._dragging = False
+ return
+ if self._move:
+ self.remove_timeout(self._continuous_scroll)
+ self._move = None
+ return
+
+ def _get_length(self):
+ return self.width
+
--- a/tuikit/widgets/textbox.py Mon Feb 16 21:17:43 2015 +0100
+++ b/tuikit/widgets/textbox.py Sat Feb 21 12:01:57 2015 +0100
@@ -84,11 +84,11 @@
self.move_right()
return True
- def on_mousedown(self, ev):
- y = ev.wy
- x = min(ev.wx, len(self._lines[y]))
+ def mousedown_event(self, ev):
+ y = ev.pos.y
+ x = min(ev.pos.x, len(self._lines[y]))
self._cursor.update(x=x, y=y)
- self.redraw()
+ #self.redraw()
def on_mousewheel(self, ev):
if ev.button == 4: