From 50e411b9f37f8caee4ba391a47a1e2644fcdf1bd Mon Sep 17 00:00:00 2001 From: Yu-Jie Lin Date: Wed, 20 Jul 2011 11:46:39 +0800 Subject: [PATCH 01/15] Force mouse to work for PuDB. --- pudb/debugger.py | 78 ++++++++++++++++++++++++++++++++++-------------- pudb/ui_tools.py | 63 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 23 deletions(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index dd80b181..73a67f3f 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -4,6 +4,7 @@ from __future__ import division import urwid import bdb +import time @@ -272,9 +273,9 @@ def _runscript(self, filename): # UI stuff -------------------------------------------------------------------- -from pudb.ui_tools import make_hotkey_markup, labelled_value, \ - SelectableText, SignalWrap, StackFrame, BreakpointFrame, \ - SearchBox +from pudb.ui_tools import double_press_input_filter, labelled_value, \ + make_hotkey_markup, SelectableText, SignalWrap, StackFrame, \ + BreakpointFrame, SearchBox from pudb.var_view import FrameVarInfoKeeper @@ -340,23 +341,34 @@ def __init__(self, dbg): header)) # variable listeners -------------------------------------------------- - def change_var_state(w, size, key): + def change_var_state(w, size, *args): var, pos = self.var_list._w.get_focus() iinfo = self.get_frame_var_info(read_only=False) \ .get_inspect_info(var.id_path, read_only=False) - if key == "\\": iinfo.show_detail = not iinfo.show_detail + key = "" if len(args) != 1 else args[0] + event, button, x, y, focus = args if len(args) !=1 else (None,)*5 + display_types = ("type", "repr", "str") + + if key == "\\" or (event == 'mouse press' and button == 3): + iinfo.show_detail = not iinfo.show_detail elif key == "t": iinfo.display_type = "type" elif key == "r": iinfo.display_type = "repr" elif key == "s": iinfo.display_type = "str" - elif key == "h": iinfo.highlighted = not iinfo.highlighted - elif key == "@": iinfo.repeated_at_top = not iinfo.repeated_at_top + elif key == "h" or (event == 'ctrl mouse press' and button == 1): + iinfo.highlighted = not iinfo.highlighted + elif key == "@" or (event == 'ctrl mouse double press' and button == 1): + iinfo.repeated_at_top = not iinfo.repeated_at_top elif key == "*": iinfo.show_private_members = not iinfo.show_private_members + elif event == 'mouse press' and button == 5: + iinfo.display_type = display_types[(display_types.index(iinfo.display_type) + 1) % len(display_types)] + elif event == 'mouse press' and button == 4: + iinfo.display_type = display_types[(display_types.index(iinfo.display_type) - 1 + len(display_types)) % len(display_types)] self.update_var_view() - def edit_inspector_detail(w, size, key): + def edit_inspector_detail(w, size, *args): var, pos = self.var_list._w.get_focus() if var is None: @@ -456,30 +468,37 @@ def insert_watch(w, size, key): self.var_list.listen("t", change_var_state) self.var_list.listen("r", change_var_state) self.var_list.listen("s", change_var_state) + self.var_list.listen_mouse_event("mouse press", None, change_var_state) self.var_list.listen("h", change_var_state) + self.var_list.listen_mouse_event("ctrl mouse press", 1, change_var_state) self.var_list.listen("@", change_var_state) + self.var_list.listen_mouse_event("ctrl mouse double press", 1, change_var_state) self.var_list.listen("enter", edit_inspector_detail) + self.var_list.listen_mouse_event("mouse double press", 1, edit_inspector_detail) self.var_list.listen("n", insert_watch) self.var_list.listen("insert", insert_watch) # stack listeners ----------------------------------------------------- - def examine_frame(w, size, key): + def examine_frame(w, size, *args): _, pos = self.stack_list._w.get_focus() self.debugger.set_frame_index(len(self.debugger.stack)-1-pos) self.stack_list.listen("enter", examine_frame) + self.stack_list.listen_mouse_event("mouse double press", 1, examine_frame) - def move_stack_up(w, size, key): + def move_stack_up(w, size, *args): self.debugger.move_up_frame() - def move_stack_down(w, size, key): + def move_stack_down(w, size, *args): self.debugger.move_down_frame() self.stack_list.listen("u", move_stack_up) + self.stack_list.listen_mouse_event("mouse press", 4, move_stack_up) self.stack_list.listen("d", move_stack_down) + self.stack_list.listen_mouse_event("mouse press", 5, move_stack_down) self.source_sigwrap.listen("u", move_stack_up) self.source_sigwrap.listen("d", move_stack_down) - # stack listeners ----------------------------------------------------- + # breakpoints listeners ----------------------------------------------- def save_breakpoints(w, size, key): self.debugger.save_breakpoints() @@ -495,7 +514,7 @@ def delete_breakpoint(w, size, key): else: self.update_breakpoints() - def examine_breakpoint(w, size, key): + def examine_breakpoint(w, size, *args): bp_entry, pos = self.bp_list._w.get_focus() if bp_entry is None: @@ -557,6 +576,7 @@ def examine_breakpoint(w, size, key): self.update_breakpoints() self.bp_list.listen("enter", examine_breakpoint) + self.bp_list.listen_mouse_event("mouse double press", 1, examine_breakpoint) self.bp_list.listen("d", delete_breakpoint) self.bp_list.listen("s", save_breakpoints) @@ -565,14 +585,14 @@ def end(): self.debugger.save_breakpoints() self.quit_event_loop = True - def next(w, size, key): + def next(w, size, *args): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: self.debugger.set_next(self.debugger.curframe) end() - def step(w, size, key): + def step(w, size, *args): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: @@ -594,7 +614,7 @@ def cont(w, size, key): self.debugger.set_continue() end() - def run_to_cursor(w, size, key): + def run_to_cursor(w, size, *args): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: @@ -643,20 +663,18 @@ def go_to_line(w, size, key): lineno = min(max(0, int(lineno_edit.value())-1), len(self.source)-1) self.source.set_focus(lineno) - def move_down(w, size, key): + def move_down(w, size, *args): w.keypress(size, "down") - def move_up(w, size, key): + def move_up(w, size, *args): w.keypress(size, "up") - def page_down(w, size, key): + def page_down(w, size, *args): w.keypress(size, "page down") - def page_up(w, size, key): + def page_up(w, size, *args): w.keypress(size, "page up") - def move_up(w, size, key): - w.keypress(size, "up") def scroll_left(w, size, key): self.source_hscroll_start = max( 0, @@ -700,7 +718,7 @@ def search_previous(w, size, key): else: self.message("No previous search term.") - def toggle_breakpoint(w, size, key): + def toggle_breakpoint(w, size, *args): if self.shown_file: sline, pos = self.source.get_focus() lineno = pos+1 @@ -854,16 +872,23 @@ def keypress(self, size, key): break self.source_sigwrap.listen("n", next) + self.source_sigwrap.listen_mouse_event("meta mouse press", 5, next) self.source_sigwrap.listen("s", step) + self.source_sigwrap.listen_mouse_event("meta mouse press", 1, step) self.source_sigwrap.listen("f", finish) self.source_sigwrap.listen("r", finish) self.source_sigwrap.listen("c", cont) self.source_sigwrap.listen("t", run_to_cursor) + self.source_sigwrap.listen_mouse_event("mouse double press", 1, run_to_cursor) self.source_sigwrap.listen("j", move_down) + self.source_sigwrap.listen_mouse_event("mouse press", 5, move_down) self.source_sigwrap.listen("k", move_up) + self.source_sigwrap.listen_mouse_event("mouse press", 4, move_up) self.source_sigwrap.listen("ctrl d", page_down) + self.source_sigwrap.listen_mouse_event("ctrl mouse press", 5, page_down) self.source_sigwrap.listen("ctrl u", page_up) + self.source_sigwrap.listen_mouse_event("ctrl mouse press", 4, page_up) self.source_sigwrap.listen("ctrl f", page_down) self.source_sigwrap.listen("ctrl b", page_up) self.source_sigwrap.listen("h", scroll_left) @@ -880,6 +905,7 @@ def keypress(self, size, key): self.source_sigwrap.listen("L", go_to_line) self.source_sigwrap.listen("b", toggle_breakpoint) + self.source_sigwrap.listen_mouse_event("mouse press", 3, toggle_breakpoint) self.source_sigwrap.listen("m", pick_module) # top-level listeners ------------------------------------------------- @@ -1014,6 +1040,8 @@ def help(w, size, key): self.current_line = None + self.double_press_input_filter = double_press_input_filter() + self.quit_event_loop = False def message(self, msg, title="Message", **kwargs): @@ -1097,6 +1125,7 @@ def setup_palette(screen): def show(self): if self.show_count == 0: self.screen.start() + self.screen.set_mouse_tracking() self.show_count += 1 def hide(self): @@ -1165,10 +1194,13 @@ def event_loop(self, toplevel=None): canvas = toplevel.render(self.size, focus=True) self.screen.draw_screen(self.size, canvas) keys = self.screen.get_input() + keys = self.double_press_input_filter(keys, None) for k in keys: if k == "window resize": self.size = self.screen.get_cols_rows() + elif urwid.is_mouse_event(k): + toplevel.mouse_event(self.size, *k, focus=True) else: toplevel.keypress(self.size, k) diff --git a/pudb/ui_tools.py b/pudb/ui_tools.py index 2201ace5..917d51c1 100644 --- a/pudb/ui_tools.py +++ b/pudb/ui_tools.py @@ -1,3 +1,5 @@ +import time + import urwid @@ -74,10 +76,14 @@ class SignalWrap(urwid.WidgetWrap): def __init__(self, w): urwid.WidgetWrap.__init__(self, w) self.event_listeners = [] + self.mouse_event_listeners = [] def listen(self, mask, handler): self.event_listeners.append((mask, handler)) + def listen_mouse_event(self, event, button, handler): + self.mouse_event_listeners.append((event, button, handler)) + def keypress(self, size, key): result = self._w.keypress(size, key) @@ -88,6 +94,15 @@ def keypress(self, size, key): return result + def mouse_event(self, size, event, button, x, y, focus=True): + result = self._w.mouse_event(size, event, button, x, y, focus) + + if result is False: + for m_event, m_button, handler in self.mouse_event_listeners: + if (m_event is None or m_event == event) and (m_button is None or m_button == button): + return handler(self, size, event, button, x, y, focus) + + return result @@ -257,3 +272,51 @@ def cancel_search(self): del self.ui.lhs_col.item_types[0] del self.ui.lhs_col.widget_list[0] self.ui.lhs_col.set_focus(self.ui.lhs_col.widget_list[0]) + + +class double_press_input_filter: + ''' + A filter generates new mouse event, double press. + + Usage: + + loop = urwid.MainLoop(..., input_filter=double_press_input_filter(), ...) + + When double-press the mouse buttons (1, 2, and 3. Wheels are ignored), the + handler shall receive events as follow, in order: + + ('mouse press', 1, 21, 14) + ('mouse release', 0, 21, 14) + ('mouse press', 1, 21, 14) + ('mouse double press', 1, 21, 14) + ('mouse release', 0, 21, 14) + ''' + last_press = None + last_press_time = -1 + double_press_timing = 0.25 + + @classmethod + def __call__(cls, events, raw): + + i = 0 + while i < len(events): + e = events[i] + i += 1 + if not urwid.is_mouse_event(e) or not urwid.is_mouse_press(e[0]): + continue + + if cls.last_press and \ + time.time() > cls.last_press_time + cls.double_press_timing: + cls.last_press = None + + if cls.last_press: + if cls.last_press[1] == e[1]: + events.insert(i, (e[0].replace('press', 'double press'),) + e[1:]) + i += 1 + elif urwid.is_mouse_press(e[0]) and e[1] not in (4, 5): + cls.last_press = e + cls.last_press_time = time.time() + continue + cls.last_press = None + return events + From 3d6fc9132130fd9a8c765e5c827f675832e322d8 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 17:49:06 -0400 Subject: [PATCH 02/15] Fix assumedly misspelled function name is_mouse_press to is_mouse_event --- pudb/ui_tools.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pudb/ui_tools.py b/pudb/ui_tools.py index 917d51c1..9fb8a80d 100644 --- a/pudb/ui_tools.py +++ b/pudb/ui_tools.py @@ -294,7 +294,7 @@ class double_press_input_filter: last_press = None last_press_time = -1 double_press_timing = 0.25 - + @classmethod def __call__(cls, events, raw): @@ -302,7 +302,7 @@ def __call__(cls, events, raw): while i < len(events): e = events[i] i += 1 - if not urwid.is_mouse_event(e) or not urwid.is_mouse_press(e[0]): + if not urwid.is_mouse_event(e) or not urwid.is_mouse_event(e[0]): continue if cls.last_press and \ @@ -313,10 +313,9 @@ def __call__(cls, events, raw): if cls.last_press[1] == e[1]: events.insert(i, (e[0].replace('press', 'double press'),) + e[1:]) i += 1 - elif urwid.is_mouse_press(e[0]) and e[1] not in (4, 5): + elif urwid.is_mouse_event(e[0]) and e[1] not in (4, 5): cls.last_press = e cls.last_press_time = time.time() continue cls.last_press = None return events - From c81058c3ee6d79acb7a793a0660434076736d3c6 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 18:20:03 -0400 Subject: [PATCH 03/15] Remove some unused imports --- pudb/debugger.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index 288940a5..7bc76b93 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -6,7 +6,6 @@ import bdb import sys import os -import time from pudb.settings import load_config, save_config CONFIG = load_config() @@ -425,7 +424,7 @@ def _runscript(self, filename): # UI stuff -------------------------------------------------------------------- from pudb.ui_tools import double_press_input_filter, labelled_value, \ make_hotkey_markup, SelectableText, SignalWrap, StackFrame, \ - BreakpointFrame, SearchBox, make_hotkey_markup + BreakpointFrame from pudb.var_view import FrameVarInfoKeeper From 127879738621f46304714a1ae5b6fb4f83549917 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 18:22:59 -0400 Subject: [PATCH 04/15] Use key, *args instead of *args in UI callbacks The mouse events can still be considered a "key", and this makes the API easier to understand. --- pudb/debugger.py | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index 7bc76b93..da5607e4 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -746,17 +746,16 @@ def change_rhs_box(name, index, direction, w, size, key): # {{{ variables listeners - def change_var_state(w, size, *args): + def change_var_state(w, size, key, *args): var, pos = self.var_list._w.get_focus() iinfo = self.get_frame_var_info(read_only=False) \ .get_inspect_info(var.id_path, read_only=False) - key = "" if len(args) != 1 else args[0] - event, button, x, y, focus = args if len(args) !=1 else (None,)*5 + button, x, y, focus = args or (None,)*4 display_types = ("type", "repr", "str") - if key == "\\" or (event == 'mouse press' and button == 3): + if key == "\\" or (key == 'mouse press' and button == 3): iinfo.show_detail = not iinfo.show_detail elif key == "t": iinfo.display_type = "type" @@ -766,9 +765,9 @@ def change_var_state(w, size, *args): iinfo.display_type = "str" elif key == "c": iinfo.display_type = CONFIG["custom_stringifier"] - elif key == "h" or (event == 'ctrl mouse press' and button == 1): + elif key == "h" or (key == 'ctrl mouse press' and button == 1): iinfo.highlighted = not iinfo.highlighted - elif key == "@" or (event == 'ctrl mouse double press' and button == 1): + elif key == "@" or (key == 'ctrl mouse double press' and button == 1): iinfo.repeated_at_top = not iinfo.repeated_at_top elif key == "*": levels = ["public", "private", "all", "public"] @@ -777,14 +776,14 @@ def change_var_state(w, size, *args): iinfo.wrap = not iinfo.wrap elif key == "m": iinfo.show_methods = not iinfo.show_methods - elif event == 'mouse press' and button == 5: + elif key == 'mouse press' and button == 5: iinfo.display_type = display_types[(display_types.index(iinfo.display_type) + 1) % len(display_types)] - elif event == 'mouse press' and button == 4: + elif key == 'mouse press' and button == 4: iinfo.display_type = display_types[(display_types.index(iinfo.display_type) - 1 + len(display_types)) % len(display_types)] self.update_var_view() - def edit_inspector_detail(w, size, *args): + def edit_inspector_detail(w, size, key, *args): var, pos = self.var_list._w.get_focus() if var is None: @@ -935,20 +934,20 @@ def insert_watch(w, size, key): # }}} # {{{ stack listeners - def examine_frame(w, size, *args): + def examine_frame(w, size, key, *args): _, pos = self.stack_list._w.get_focus() self.debugger.set_frame_index(self.translate_ui_stack_index(pos)) self.stack_list.listen("enter", examine_frame) self.stack_list.listen_mouse_event("mouse double press", 1, examine_frame) - def move_stack_top(w, size, *args): + def move_stack_top(w, size, key, *args): self.debugger.set_frame_index(len(self.debugger.stack)-1) - def move_stack_up(w, size, *args): + def move_stack_up(w, size, key, *args): self.debugger.move_up_frame() - def move_stack_down(w, size, *args): + def move_stack_down(w, size, key, *args): self.debugger.move_down_frame() self.stack_list.listen("H", move_stack_top) @@ -1002,7 +1001,7 @@ def enable_disable_breakpoint(w, size, key): self.update_breakpoints() - def examine_breakpoint(w, size, *args): + def examine_breakpoint(w, size, key, *args): bp_entry, pos = self.bp_list._w.get_focus() if bp_entry is None: @@ -1090,14 +1089,14 @@ def end(): self.debugger.save_breakpoints() self.quit_event_loop = True - def next(w, size, *args): + def next(w, size, key, *args): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: self.debugger.set_next(self.debugger.curframe) end() - def step(w, size, *args): + def step(w, size, key, *args): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: @@ -1118,7 +1117,7 @@ def cont(w, size, key): self.debugger.set_continue() end() - def run_to_cursor(w, size, *args): + def run_to_cursor(w, size, key, *args): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: @@ -1178,16 +1177,16 @@ def go_to_line(w, size, key): lineno = min(max(0, int(lineno_edit.value())-1), len(self.source)-1) self.source.set_focus(lineno) - def move_down(w, size, *args): + def move_down(w, size, key, *args): w.keypress(size, "down") - def move_up(w, size, *args): + def move_up(w, size, key, *args): w.keypress(size, "up") - def page_down(w, size, *args): + def page_down(w, size, key, *args): w.keypress(size, "page down") - def page_up(w, size, *args): + def page_up(w, size, key, *args): w.keypress(size, "page up") def scroll_left(w, size, key): @@ -1211,7 +1210,7 @@ def search_next(w, size, key): def search_previous(w, size, key): self.search_controller.perform_search(dir=-1, update_search_start=True) - def toggle_breakpoint(w, size, *args): + def toggle_breakpoint(w, size, key, *args): bp_source_identifier = \ self.source_code_provider.get_breakpoint_source_identifier() From b58404d53b6937310ea70730edfe172fbea4d096 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 18:25:13 -0400 Subject: [PATCH 05/15] Use explicit keyword arguments for all UI callback functions --- pudb/debugger.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index da5607e4..8dac94b5 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -746,13 +746,12 @@ def change_rhs_box(name, index, direction, w, size, key): # {{{ variables listeners - def change_var_state(w, size, key, *args): + def change_var_state(w, size, key, button=None, x=None, y=None, focus=None): var, pos = self.var_list._w.get_focus() iinfo = self.get_frame_var_info(read_only=False) \ .get_inspect_info(var.id_path, read_only=False) - button, x, y, focus = args or (None,)*4 display_types = ("type", "repr", "str") if key == "\\" or (key == 'mouse press' and button == 3): @@ -783,7 +782,7 @@ def change_var_state(w, size, key, *args): self.update_var_view() - def edit_inspector_detail(w, size, key, *args): + def edit_inspector_detail(w, size, key, button=None, x=None, y=None, focus=None): var, pos = self.var_list._w.get_focus() if var is None: @@ -934,20 +933,20 @@ def insert_watch(w, size, key): # }}} # {{{ stack listeners - def examine_frame(w, size, key, *args): + def examine_frame(w, size, key, button=None, x=None, y=None, focus=None): _, pos = self.stack_list._w.get_focus() self.debugger.set_frame_index(self.translate_ui_stack_index(pos)) self.stack_list.listen("enter", examine_frame) self.stack_list.listen_mouse_event("mouse double press", 1, examine_frame) - def move_stack_top(w, size, key, *args): + def move_stack_top(w, size, key, button=None, x=None, y=None, focus=None): self.debugger.set_frame_index(len(self.debugger.stack)-1) - def move_stack_up(w, size, key, *args): + def move_stack_up(w, size, key, button=None, x=None, y=None, focus=None): self.debugger.move_up_frame() - def move_stack_down(w, size, key, *args): + def move_stack_down(w, size, key, button=None, x=None, y=None, focus=None): self.debugger.move_down_frame() self.stack_list.listen("H", move_stack_top) @@ -1001,7 +1000,7 @@ def enable_disable_breakpoint(w, size, key): self.update_breakpoints() - def examine_breakpoint(w, size, key, *args): + def examine_breakpoint(w, size, key, button=None, x=None, y=None, focus=None): bp_entry, pos = self.bp_list._w.get_focus() if bp_entry is None: @@ -1089,14 +1088,14 @@ def end(): self.debugger.save_breakpoints() self.quit_event_loop = True - def next(w, size, key, *args): + def next(w, size, key, button=None, x=None, y=None, focus=None): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: self.debugger.set_next(self.debugger.curframe) end() - def step(w, size, key, *args): + def step(w, size, key, button=None, x=None, y=None, focus=None): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: @@ -1117,7 +1116,7 @@ def cont(w, size, key): self.debugger.set_continue() end() - def run_to_cursor(w, size, key, *args): + def run_to_cursor(w, size, key, button=None, x=None, y=None, focus=None): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: @@ -1177,16 +1176,16 @@ def go_to_line(w, size, key): lineno = min(max(0, int(lineno_edit.value())-1), len(self.source)-1) self.source.set_focus(lineno) - def move_down(w, size, key, *args): + def move_down(w, size, key, button=None, x=None, y=None, focus=None): w.keypress(size, "down") - def move_up(w, size, key, *args): + def move_up(w, size, key, button=None, x=None, y=None, focus=None): w.keypress(size, "up") - def page_down(w, size, key, *args): + def page_down(w, size, key, button=None, x=None, y=None, focus=None): w.keypress(size, "page down") - def page_up(w, size, key, *args): + def page_up(w, size, key, button=None, x=None, y=None, focus=None): w.keypress(size, "page up") def scroll_left(w, size, key): @@ -1210,7 +1209,7 @@ def search_next(w, size, key): def search_previous(w, size, key): self.search_controller.perform_search(dir=-1, update_search_start=True) - def toggle_breakpoint(w, size, key, *args): + def toggle_breakpoint(w, size, key, button=None, x=None, y=None, focus=None): bp_source_identifier = \ self.source_code_provider.get_breakpoint_source_identifier() From 787e4bf45e90a315c9903873a171fd82d7de615b Mon Sep 17 00:00:00 2001 From: Yu-Jie Lin Date: Sat, 30 Jul 2011 00:54:27 +0800 Subject: [PATCH 06/15] add CONFIG["custom_stringifier"] into display types for mouse --- pudb/debugger.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pudb/debugger.py b/pudb/debugger.py index 8dac94b5..15cbec83 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -753,6 +753,8 @@ def change_var_state(w, size, key, button=None, x=None, y=None, focus=None): .get_inspect_info(var.id_path, read_only=False) display_types = ("type", "repr", "str") + if CONFIG["custom_stringifier"]: + display_types.append(CONFIG["custom_stringifier"]) if key == "\\" or (key == 'mouse press' and button == 3): iinfo.show_detail = not iinfo.show_detail From 2892277a8851903f041b2b782db421bc900c7ade Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 18:27:21 -0400 Subject: [PATCH 07/15] Use list instead of tuple for append --- pudb/debugger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index 15cbec83..336943bf 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -752,7 +752,7 @@ def change_var_state(w, size, key, button=None, x=None, y=None, focus=None): iinfo = self.get_frame_var_info(read_only=False) \ .get_inspect_info(var.id_path, read_only=False) - display_types = ("type", "repr", "str") + display_types = ["type", "repr", "str"] if CONFIG["custom_stringifier"]: display_types.append(CONFIG["custom_stringifier"]) From 5ca165dcf8a35cc43c841fb8e29e0dcdd8220dd3 Mon Sep 17 00:00:00 2001 From: Yu-Jie Lin Date: Wed, 20 Jul 2011 22:47:52 +0800 Subject: [PATCH 08/15] Fix exception when key press on empty variables list --- pudb/debugger.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pudb/debugger.py b/pudb/debugger.py index 336943bf..94d0f268 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -748,6 +748,8 @@ def change_rhs_box(name, index, direction, w, size, key): def change_var_state(w, size, key, button=None, x=None, y=None, focus=None): var, pos = self.var_list._w.get_focus() + if not var: + return iinfo = self.get_frame_var_info(read_only=False) \ .get_inspect_info(var.id_path, read_only=False) From 189bf5710a483692657055224c5ff113fa0e7f66 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 20:18:05 -0500 Subject: [PATCH 09/15] Add setting for mouse support (with auto-updating) The default is enabled. --- pudb/debugger.py | 3 ++- pudb/settings.py | 27 +++++++++++++++++++++++++++ pudb/ui_tools.py | 4 ++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index 94d0f268..80d20761 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -2075,7 +2075,8 @@ def show_exception_dialog(self, exc_tuple): def show(self): if self.show_count == 0: self.screen.start() - self.screen.set_mouse_tracking() + if CONFIG["mouse_support"]: + self.screen.set_mouse_tracking() self.show_count += 1 def hide(self): diff --git a/pudb/settings.py b/pudb/settings.py index e47ac754..3f50a002 100644 --- a/pudb/settings.py +++ b/pudb/settings.py @@ -79,6 +79,8 @@ def load_config(): conf_dict.setdefault("display", "auto") + conf_dict.setdefault("mouse_support", True) + conf_dict.setdefault("prompt_on_quit", True) def normalize_bool_inplace(name): @@ -92,6 +94,7 @@ def normalize_bool_inplace(name): normalize_bool_inplace("line_numbers") normalize_bool_inplace("wrap_variables") + normalize_bool_inplace("mouse_support") normalize_bool_inplace("prompt_on_quit") return conf_dict @@ -146,6 +149,9 @@ def _update_stringifier(): def _update_wrap_variables(): ui.update_var_view() + def _update_mouse_support(mouse_support): + ui.screen.set_mouse_tracking(mouse_support) + def _update_config(check_box, new_state, option_newvalue): option, newvalue = option_newvalue new_conf_dict = {option: newvalue} @@ -190,6 +196,11 @@ def _update_config(check_box, new_state, option_newvalue): conf_dict.update(new_conf_dict) _update_wrap_variables() + elif option == "mouse_support": + mouse_support = new_conf_dict["mouse_support"] = not check_box.get_state() + conf_dict.update(new_conf_dict) + _update_mouse_support(mouse_support) + heading = urwid.Text("This is the preferences screen for PuDB. " "Hit Ctrl-P at any time to get back to it.\n\n" "Configuration settings are saved in " @@ -331,6 +342,18 @@ def _update_config(check_box, new_state, option_newvalue): # }}} + + # {{{ mouse support + + cb_mouse_support = urwid.CheckBox("Mouse support", + bool(conf_dict["mouse_support"]), on_state_change=_update_config, + user_data=("mouse_support", None)) + + mouse_support_info = urwid.Text("Enable mouse support (for terminals that " + "support it)? See the help (hit '?') for information about what " + "different mouse buttons do.") + # }}} + lb_contents = ( [heading] + [urwid.AttrMap(urwid.Text("Line Numbers:\n"), "group head")] @@ -361,6 +384,10 @@ def _update_config(check_box, new_state, option_newvalue): + [urwid.AttrMap(urwid.Text("\nDisplay driver:\n"), "group head")] + [display_info] + display_rbs + + + [urwid.AttrMap(urwid.Text("\nMouse support:\n"), "group head")] + + [mouse_support_info] + + [cb_mouse_support] ) lb = urwid.ListBox(urwid.SimpleListWalker(lb_contents)) diff --git a/pudb/ui_tools.py b/pudb/ui_tools.py index 193e418d..d2fa9665 100644 --- a/pudb/ui_tools.py +++ b/pudb/ui_tools.py @@ -108,6 +108,10 @@ def keypress(self, size, key): return result def mouse_event(self, size, event, button, x, y, focus=True): + from pudb.debugger import CONFIG + if not CONFIG["mouse_support"]: + return False + result = self._w.mouse_event(size, event, button, x, y, focus) if result is False: From 9979644b96276d209460a177160906b72dcbf168 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 19:15:45 -0700 Subject: [PATCH 10/15] Use a variable that's been defined --- pudb/debugger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index 80d20761..ec390938 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -1236,7 +1236,7 @@ def toggle_breakpoint(w, size, key, button=None, x=None, y=None, focus=None): from pudb.lowlevel import get_breakpoint_invalid_reason invalid_reason = get_breakpoint_invalid_reason( - bp_source_identifier, pos+1) + bp_source_identifier, lineno) if invalid_reason is not None: do_set = not self.dialog( From a4ba8630fd062edd82537583a10be8b5cce5e101 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 19:15:58 -0700 Subject: [PATCH 11/15] Always select elements with mouse events This makes, for instance, right clicking on a line set the break point on that line, not the currently selected line. --- pudb/ui_tools.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pudb/ui_tools.py b/pudb/ui_tools.py index d2fa9665..047aa510 100644 --- a/pudb/ui_tools.py +++ b/pudb/ui_tools.py @@ -112,6 +112,9 @@ def mouse_event(self, size, event, button, x, y, focus=True): if not CONFIG["mouse_support"]: return False + # Always select the element first + super(SignalWrap, self).mouse_event(size, 'mouse press', 1, x, y, focus) + result = self._w.mouse_event(size, event, button, x, y, focus) if result is False: From 5d30fb6cca3d8fe37df8be797a49770a651aa769 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 19:26:19 -0700 Subject: [PATCH 12/15] Use four-space indents --- pudb/ui_tools.py | 87 ++++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/pudb/ui_tools.py b/pudb/ui_tools.py index 047aa510..b34bf885 100644 --- a/pudb/ui_tools.py +++ b/pudb/ui_tools.py @@ -339,47 +339,46 @@ def keypress(self, size, key): # }}} class double_press_input_filter: - ''' - A filter generates new mouse event, double press. - - Usage: - - loop = urwid.MainLoop(..., input_filter=double_press_input_filter(), ...) - - When double-press the mouse buttons (1, 2, and 3. Wheels are ignored), the - handler shall receive events as follow, in order: - - ('mouse press', 1, 21, 14) - ('mouse release', 0, 21, 14) - ('mouse press', 1, 21, 14) - ('mouse double press', 1, 21, 14) - ('mouse release', 0, 21, 14) - ''' - last_press = None - last_press_time = -1 - double_press_timing = 0.25 - - @classmethod - def __call__(cls, events, raw): - - i = 0 - while i < len(events): - e = events[i] - i += 1 - if not urwid.is_mouse_event(e) or not urwid.is_mouse_event(e[0]): - continue - - if cls.last_press and \ - time.time() > cls.last_press_time + cls.double_press_timing: - cls.last_press = None - - if cls.last_press: - if cls.last_press[1] == e[1]: - events.insert(i, (e[0].replace('press', 'double press'),) + e[1:]) - i += 1 - elif urwid.is_mouse_event(e[0]) and e[1] not in (4, 5): - cls.last_press = e - cls.last_press_time = time.time() - continue - cls.last_press = None - return events + ''' + A filter generates new mouse event, double press. + + Usage: + + loop = urwid.MainLoop(..., input_filter=double_press_input_filter(), ...) + + When double-press the mouse buttons (1, 2, and 3. Wheels are ignored), the + handler shall receive events as follow, in order: + + ('mouse press', 1, 21, 14) + ('mouse release', 0, 21, 14) + ('mouse press', 1, 21, 14) + ('mouse double press', 1, 21, 14) + ('mouse release', 0, 21, 14) + ''' + last_press = None + last_press_time = -1 + double_press_timing = 0.25 + + @classmethod + def __call__(cls, events, raw): + i = 0 + while i < len(events): + e = events[i] + i += 1 + if not urwid.is_mouse_event(e) or not urwid.is_mouse_event(e[0]): + continue + + if cls.last_press and \ + time.time() > cls.last_press_time + cls.double_press_timing: + cls.last_press = None + + if cls.last_press: + if cls.last_press[1] == e[1]: + events.insert(i, (e[0].replace('press', 'double press'),) + e[1:]) + i += 1 + elif urwid.is_mouse_event(e[0]) and e[1] not in (4, 5): + cls.last_press = e + cls.last_press_time = time.time() + continue + cls.last_press = None + return events From fff79b1b06097bd47d4d5d65312db90999f603a1 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 20:19:22 -0700 Subject: [PATCH 13/15] Don't auto-select for scroll events Except ones that don't "scroll", like the var stringifier cycling. --- pudb/debugger.py | 6 ++++++ pudb/ui_tools.py | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index ec390938..199dcc98 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -780,8 +780,14 @@ def change_var_state(w, size, key, button=None, x=None, y=None, focus=None): elif key == "m": iinfo.show_methods = not iinfo.show_methods elif key == 'mouse press' and button == 5: + # SignalWrap.mouse_event by doesn't select on scroll events, + # but we want to do that here. + super(SignalWrap, self.var_list).mouse_event(size, + 'mouse press', 1, x, y, focus) iinfo.display_type = display_types[(display_types.index(iinfo.display_type) + 1) % len(display_types)] elif key == 'mouse press' and button == 4: + super(SignalWrap, self.var_list).mouse_event(size, + 'mouse press', 1, x, y, focus) iinfo.display_type = display_types[(display_types.index(iinfo.display_type) - 1 + len(display_types)) % len(display_types)] self.update_var_view() diff --git a/pudb/ui_tools.py b/pudb/ui_tools.py index b34bf885..19a0a50b 100644 --- a/pudb/ui_tools.py +++ b/pudb/ui_tools.py @@ -112,8 +112,9 @@ def mouse_event(self, size, event, button, x, y, focus=True): if not CONFIG["mouse_support"]: return False - # Always select the element first - super(SignalWrap, self).mouse_event(size, 'mouse press', 1, x, y, focus) + # Always select the element first, except for scroll events + if button not in [4, 5]: + super(SignalWrap, self).mouse_event(size, 'mouse press', 1, x, y, focus) result = self._w.mouse_event(size, event, button, x, y, focus) From 27f4f170463471b813a7f6b5949e5ba81a4afc47 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 20:21:54 -0700 Subject: [PATCH 14/15] Use single mouse click to examine stack frames Double click doesn't actually work (it will likely be removed, as it requires a real event loop). There's no reason to select a stack frame without entering it, since no other actions exist on stack frames. --- pudb/debugger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index 199dcc98..ad704b5f 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -948,7 +948,7 @@ def examine_frame(w, size, key, button=None, x=None, y=None, focus=None): self.debugger.set_frame_index(self.translate_ui_stack_index(pos)) self.stack_list.listen("enter", examine_frame) - self.stack_list.listen_mouse_event("mouse double press", 1, examine_frame) + self.stack_list.listen_mouse_event("mouse press", 1, examine_frame) def move_stack_top(w, size, key, button=None, x=None, y=None, focus=None): self.debugger.set_frame_index(len(self.debugger.stack)-1) From b1a71cbbe63c55a40a8f58f422eaa3cba9eebd4b Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sun, 29 May 2016 20:31:38 -0700 Subject: [PATCH 15/15] Right-clicking on a breakpoint enables/disables it Unfortunately, there's no UI for this yet, unless you examine the breakpoint. --- pudb/debugger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index ad704b5f..6673b0e9 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -999,7 +999,7 @@ def delete_breakpoint(w, size, key): else: self.update_breakpoints() - def enable_disable_breakpoint(w, size, key): + def enable_disable_breakpoint(w, size, key, button=None, x=None, y=None, focus=None): bp_entry, pos = self.bp_list._w.get_focus() if bp_entry is None: @@ -1086,6 +1086,7 @@ def examine_breakpoint(w, size, key, button=None, x=None, y=None, focus=None): self.bp_list.listen("d", delete_breakpoint) self.bp_list.listen("s", save_breakpoints) self.bp_list.listen("e", enable_disable_breakpoint) + self.bp_list.listen_mouse_event("mouse press", 3, enable_disable_breakpoint) self.bp_list.listen("[", partial(change_rhs_box, 'breakpoints', 2, -1)) self.bp_list.listen("]", partial(change_rhs_box, 'breakpoints', 2, 1))