diff options
author | jblum | 2009-05-01 20:28:04 +0000 |
---|---|---|
committer | jblum | 2009-05-01 20:28:04 +0000 |
commit | a3ba8cf268816af51c4bb39ea7ecd7e85ea0807b (patch) | |
tree | 21dbd446e92672a56b323e005088d3c03edc238f /grc/src/gui | |
parent | 6ce881caaacdd60a8bea37584c7286e08bea97a7 (diff) | |
download | gnuradio-a3ba8cf268816af51c4bb39ea7ecd7e85ea0807b.tar.gz gnuradio-a3ba8cf268816af51c4bb39ea7ecd7e85ea0807b.tar.bz2 gnuradio-a3ba8cf268816af51c4bb39ea7ecd7e85ea0807b.zip |
Merged grc developer branch r10679:10938
Misc fixes and internal changes.
Added help menu for usage tips.
Added drag and drop for blocks.
Removed callback controls, adopted forms.
Any type can have enumerated options.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@10941 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'grc/src/gui')
-rw-r--r-- | grc/src/gui/ActionHandler.py | 77 | ||||
-rw-r--r-- | grc/src/gui/Actions.py | 140 | ||||
-rw-r--r-- | grc/src/gui/Bars.py | 12 | ||||
-rw-r--r-- | grc/src/gui/BlockTreeWindow.py | 42 | ||||
-rw-r--r-- | grc/src/gui/Constants.py | 23 | ||||
-rw-r--r-- | grc/src/gui/Dialogs.py | 20 | ||||
-rw-r--r-- | grc/src/gui/DrawingArea.py | 36 | ||||
-rw-r--r-- | grc/src/gui/ParamsDialog.py | 8 |
8 files changed, 203 insertions, 155 deletions
diff --git a/grc/src/gui/ActionHandler.py b/grc/src/gui/ActionHandler.py index 91dd0fb45..da0909a64 100644 --- a/grc/src/gui/ActionHandler.py +++ b/grc/src/gui/ActionHandler.py @@ -1,5 +1,5 @@ """ -Copyright 2007 Free Software Foundation, Inc. +Copyright 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import os import signal -from Constants import DIR_LEFT, DIR_RIGHT, IMAGE_FILE_EXTENSION +from Constants import IMAGE_FILE_EXTENSION import Actions import pygtk pygtk.require('2.0') @@ -32,7 +32,8 @@ from .. utils import ParseXML import random from .. platforms.gui.Platform import Platform from MainWindow import MainWindow -from Dialogs import AboutDialog +from ParamsDialog import ParamsDialog +import Dialogs from FileDialogs import OpenFlowGraphFileDialog, SaveFlowGraphFileDialog, SaveImageFileDialog gobject.threads_init() @@ -57,7 +58,7 @@ class ActionHandler: #setup the main window self.main_window = MainWindow(self.handle_states, platform) self.main_window.connect('delete_event', self._quit) - self.main_window.connect('key_press_event', self._handle_key_press) + self.main_window.connect('key-press-event', self._handle_key_press) self.get_page = self.main_window.get_page self.get_flow_graph = self.main_window.get_flow_graph self.get_focus_flag = self.main_window.drawing_area.get_focus_flag @@ -72,32 +73,25 @@ class ActionHandler: def _handle_key_press(self, widget, event): """ - Handle key presses from the keyboard and translate key combos into actions. - This key press handler is called before the gtk accelerators kick in. - This handler ensures that key presses without a mod mask, - only pass to the accelerators if the flow graph is in focus. - This function also handles keys that accelerators refuse to handle: left/right, - and keys that are not registered with an accelerator: +/-. - @return false to let the accelerators handle the key action + Handle key presses from the keyboard and translate key combinations into actions. + This key press handler is called prior to the gtk key press handler. + This handler bypasses built in accelerator key handling when in focus because + * some keys are ignored by the accelerators like the direction keys, + * some keys are not registered to any accelerators but are still used. + When not in focus, gtk and the accelerators handle the the key press. + @return false to let gtk handle the key action """ - if self.get_focus_flag(): - try: - self.handle_states({ - 'Left': Actions.BLOCK_ROTATE_LEFT, - 'Right': Actions.BLOCK_ROTATE_RIGHT, - 'Up': Actions.BLOCK_DEC_TYPE, - 'Down': Actions.BLOCK_INC_TYPE, - 'equal': Actions.PORT_CONTROLLER_INC, - 'plus': Actions.PORT_CONTROLLER_INC, - 'KP_Add': Actions.PORT_CONTROLLER_INC, - 'minus': Actions.PORT_CONTROLLER_DEC, - 'KP_Subtract': Actions.PORT_CONTROLLER_DEC, - }[gtk.gdk.keyval_name(event.keyval)]) - return True - #focus: always return false for accelerator to handle - except: return False - #no focus: only allow accelerator to handle when a mod is used - return not event.state + #dont allow key presses to queue up + if gtk.events_pending(): return True + #extract action name from this key press + key_name = gtk.gdk.keyval_name(event.keyval) + mod_mask = event.state + action_name = Actions.get_action_name_from_key_name(key_name, mod_mask) + #handle the action if flow graph is in focus + if action_name and self.get_focus_flag(): + self.handle_states(action_name) + return True #handled by this method + return False #let gtk handle the key press def _quit(self, window, event): """ @@ -138,7 +132,7 @@ class ActionHandler: Actions.APPLICATION_QUIT, Actions.FLOW_GRAPH_NEW, Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS, Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY, - Actions.FLOW_GRAPH_SCREEN_CAPTURE, + Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY, ): Actions.get_action_from_name(action).set_sensitive(True) if not self.init_file_paths: self.init_file_paths = Preferences.files_open() @@ -193,13 +187,13 @@ class ActionHandler: elif state == Actions.BLOCK_MOVE: self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) - elif state == Actions.BLOCK_ROTATE_LEFT: - if self.get_flow_graph().rotate_selected(DIR_LEFT): + elif state == Actions.BLOCK_ROTATE_CCW: + if self.get_flow_graph().rotate_selected(90): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) - elif state == Actions.BLOCK_ROTATE_RIGHT: - if self.get_flow_graph().rotate_selected(DIR_RIGHT): + elif state == Actions.BLOCK_ROTATE_CW: + if self.get_flow_graph().rotate_selected(-90): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) @@ -238,12 +232,15 @@ class ActionHandler: # Window stuff ################################################## elif state == Actions.ABOUT_WINDOW_DISPLAY: - AboutDialog() + Dialogs.AboutDialog() + elif state == Actions.HELP_WINDOW_DISPLAY: + Dialogs.HelpDialog() ################################################## # Param Modifications ################################################## elif state == Actions.BLOCK_PARAM_MODIFY: - if self.get_flow_graph().param_modify_selected(): + selected_block = self.get_flow_graph().get_selected_block() + if selected_block and ParamsDialog(selected_block).run(): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) @@ -296,7 +293,7 @@ class ActionHandler: elif state == Actions.FLOW_GRAPH_SCREEN_CAPTURE: file_path = SaveImageFileDialog(self.get_page().get_file_path()).run() if file_path is not None: - pixmap = self.get_flow_graph().get_drawing_area().pixmap + pixmap = self.get_flow_graph().get_pixmap() width, height = pixmap.get_size() pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 0, 8, width, height) pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, width, height) @@ -333,8 +330,8 @@ class ActionHandler: #update general buttons Actions.get_action_from_name(Actions.ELEMENT_DELETE).set_sensitive(bool(self.get_flow_graph().get_selected_elements())) Actions.get_action_from_name(Actions.BLOCK_PARAM_MODIFY).set_sensitive(bool(self.get_flow_graph().get_selected_block())) - Actions.get_action_from_name(Actions.BLOCK_ROTATE_RIGHT).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.get_action_from_name(Actions.BLOCK_ROTATE_LEFT).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.get_action_from_name(Actions.BLOCK_ROTATE_CCW).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.get_action_from_name(Actions.BLOCK_ROTATE_CW).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) #update cut/copy/paste Actions.get_action_from_name(Actions.BLOCK_CUT).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) Actions.get_action_from_name(Actions.BLOCK_COPY).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) @@ -348,7 +345,7 @@ class ActionHandler: Actions.get_action_from_name(Actions.FLOW_GRAPH_SAVE).set_sensitive(not self.get_page().get_saved()) self.main_window.update() #draw the flow graph - self.get_flow_graph().draw() + self.get_flow_graph().queue_draw() def update_exec_stop(self): """ diff --git a/grc/src/gui/Actions.py b/grc/src/gui/Actions.py index 16f12dd4c..9b687df7e 100644 --- a/grc/src/gui/Actions.py +++ b/grc/src/gui/Actions.py @@ -1,5 +1,5 @@ """ -Copyright 2007 Free Software Foundation, Inc. +Copyright 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -28,8 +28,8 @@ APPLICATION_INITIALIZE = 'app init' APPLICATION_QUIT = 'app quit' PARAM_MODIFY = 'param modify' BLOCK_MOVE = 'block move' -BLOCK_ROTATE_LEFT = 'block rotate left' -BLOCK_ROTATE_RIGHT = 'block rotate right' +BLOCK_ROTATE_CCW = 'block rotate ccw' +BLOCK_ROTATE_CW = 'block rotate cw' BLOCK_PARAM_MODIFY = 'block param modify' BLOCK_INC_TYPE = 'block increment type' BLOCK_DEC_TYPE = 'block decrement type' @@ -56,60 +56,85 @@ FLOW_GRAPH_EXEC = 'flow graph exec' FLOW_GRAPH_KILL = 'flow graph kill' FLOW_GRAPH_SCREEN_CAPTURE = 'flow graph screen capture' ABOUT_WINDOW_DISPLAY = 'about window display' +HELP_WINDOW_DISPLAY = 'help window display' ###################################################################################################### # Action Key Map ###################################################################################################### -_actions_key_map = { - #action name: (key name, mask) - FLOW_GRAPH_NEW: ('n', gtk.gdk.CONTROL_MASK), - FLOW_GRAPH_OPEN: ('o', gtk.gdk.CONTROL_MASK), - FLOW_GRAPH_SAVE: ('s', gtk.gdk.CONTROL_MASK), - FLOW_GRAPH_CLOSE: ('w', gtk.gdk.CONTROL_MASK), - APPLICATION_QUIT: ('q', gtk.gdk.CONTROL_MASK), - FLOW_GRAPH_UNDO: ('z', gtk.gdk.CONTROL_MASK), - FLOW_GRAPH_REDO: ('y', gtk.gdk.CONTROL_MASK), - ELEMENT_DELETE: ('Delete', 0), - BLOCK_ROTATE_LEFT: ('Left', 0), - BLOCK_ROTATE_RIGHT: ('Right', 0), - BLOCK_PARAM_MODIFY: ('Return', 0), - BLOCK_ENABLE: ('e', 0), - BLOCK_DISABLE: ('d', 0), - BLOCK_CUT: ('x', gtk.gdk.CONTROL_MASK), - BLOCK_COPY: ('c', gtk.gdk.CONTROL_MASK), - BLOCK_PASTE: ('v', gtk.gdk.CONTROL_MASK), - FLOW_GRAPH_GEN: ('F5', 0), - FLOW_GRAPH_EXEC: ('F6', 0), - FLOW_GRAPH_KILL: ('F7', 0), - FLOW_GRAPH_SCREEN_CAPTURE: ('Print', 0), -} +_actions_key_list = ( + #action name, key name, mod mask + (FLOW_GRAPH_NEW, 'n', gtk.gdk.CONTROL_MASK), + (FLOW_GRAPH_OPEN, 'o', gtk.gdk.CONTROL_MASK), + (FLOW_GRAPH_SAVE, 's', gtk.gdk.CONTROL_MASK), + (FLOW_GRAPH_SAVE_AS, 's', gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK), + (FLOW_GRAPH_CLOSE, 'w', gtk.gdk.CONTROL_MASK), + (APPLICATION_QUIT, 'q', gtk.gdk.CONTROL_MASK), + (FLOW_GRAPH_UNDO, 'z', gtk.gdk.CONTROL_MASK), + (FLOW_GRAPH_REDO, 'y', gtk.gdk.CONTROL_MASK), + (ELEMENT_DELETE, 'Delete', 0), + (BLOCK_ROTATE_CCW, 'Left', 0), + (BLOCK_ROTATE_CW, 'Right', 0), + (BLOCK_DEC_TYPE, 'Up', 0), + (BLOCK_INC_TYPE, 'Down', 0), + (BLOCK_PARAM_MODIFY, 'Return', 0), + (BLOCK_ENABLE, 'e', 0), + (BLOCK_DISABLE, 'd', 0), + (BLOCK_CUT, 'x', gtk.gdk.CONTROL_MASK), + (BLOCK_COPY, 'c', gtk.gdk.CONTROL_MASK), + (BLOCK_PASTE, 'v', gtk.gdk.CONTROL_MASK), + (FLOW_GRAPH_GEN, 'F5', 0), + (FLOW_GRAPH_EXEC, 'F6', 0), + (FLOW_GRAPH_KILL, 'F7', 0), + (FLOW_GRAPH_SCREEN_CAPTURE, 'Print', 0), + (HELP_WINDOW_DISPLAY, 'F1', 0), + #the following have no associated gtk.Action + (PORT_CONTROLLER_INC, 'equal', 0), + (PORT_CONTROLLER_INC, 'plus', 0), + (PORT_CONTROLLER_INC, 'KP_Add', 0), + (PORT_CONTROLLER_DEC, 'minus', 0), + (PORT_CONTROLLER_DEC, 'KP_Subtract', 0), +) + +_actions_key_dict = dict(((key_name, mod_mask), action_name) for action_name, key_name, mod_mask in _actions_key_list) +def get_action_name_from_key_name(key_name, mod_mask=0): + """ + Get the action name associated with the key name and mask. + Both keyname and mask have to match. + @param key_name the name of the key + @param mod_mask the key press mask (shift, ctrl) 0 for none + @return the action name or blank string + """ + key_name_mod_mask = (key_name, mod_mask) + if key_name_mod_mask in _actions_key_dict: return _actions_key_dict[key_name_mod_mask] + return '' ###################################################################################################### # Actions ###################################################################################################### _actions_list = ( - gtk.Action(FLOW_GRAPH_NEW, '_New', 'Create a new flow graph', 'gtk-new'), - gtk.Action(FLOW_GRAPH_OPEN, '_Open', 'Open an existing flow graph', 'gtk-open'), - gtk.Action(FLOW_GRAPH_SAVE, '_Save', 'Save the current flow graph', 'gtk-save'), - gtk.Action(FLOW_GRAPH_SAVE_AS, 'Save _As', 'Save the current flow graph as...', 'gtk-save-as'), - gtk.Action(FLOW_GRAPH_CLOSE, '_Close', 'Close the current flow graph', 'gtk-close'), - gtk.Action(APPLICATION_QUIT, '_Quit', 'Quit program', 'gtk-quit'), - gtk.Action(FLOW_GRAPH_UNDO, '_Undo', 'Undo a change to the flow graph', 'gtk-undo'), - gtk.Action(FLOW_GRAPH_REDO, '_Redo', 'Redo a change to the flow graph', 'gtk-redo'), - gtk.Action(ELEMENT_DELETE, '_Delete', 'Delete the selected blocks', 'gtk-delete'), - gtk.Action(BLOCK_ROTATE_LEFT, 'Rotate _Left', 'Rotate the selected blocks 90 degrees', 'gtk-go-back'), - gtk.Action(BLOCK_ROTATE_RIGHT, 'Rotate _Right', 'Rotate the selected blocks -90 degrees', 'gtk-go-forward'), - gtk.Action(BLOCK_PARAM_MODIFY, '_Properties', 'Modify params for the selected block', 'gtk-properties'), - gtk.Action(BLOCK_ENABLE, 'E_nable', 'Enable the selected blocks', 'gtk-connect'), - gtk.Action(BLOCK_DISABLE, 'D_isable', 'Disable the selected blocks', 'gtk-disconnect'), - gtk.Action(BLOCK_CUT, 'Cu_t', 'Cut', 'gtk-cut'), - gtk.Action(BLOCK_COPY, '_Copy', 'Copy', 'gtk-copy'), - gtk.Action(BLOCK_PASTE, '_Paste', 'Paste', 'gtk-paste'), - gtk.Action(ABOUT_WINDOW_DISPLAY, '_About', 'About this program', 'gtk-about'), - gtk.Action(FLOW_GRAPH_GEN, '_Generate', 'Generate the flow graph', 'gtk-convert'), - gtk.Action(FLOW_GRAPH_EXEC, '_Execute', 'Execute the flow graph', 'gtk-execute'), - gtk.Action(FLOW_GRAPH_KILL, '_Kill', 'Kill the flow graph', 'gtk-stop'), - gtk.Action(FLOW_GRAPH_SCREEN_CAPTURE, 'S_creen Capture', 'Create a screen capture of the flow graph', 'gtk-print'), + gtk.Action(FLOW_GRAPH_NEW, '_New', 'Create a new flow graph', gtk.STOCK_NEW), + gtk.Action(FLOW_GRAPH_OPEN, '_Open', 'Open an existing flow graph', gtk.STOCK_OPEN), + gtk.Action(FLOW_GRAPH_SAVE, '_Save', 'Save the current flow graph', gtk.STOCK_SAVE), + gtk.Action(FLOW_GRAPH_SAVE_AS, 'Save _As', 'Save the current flow graph as...', gtk.STOCK_SAVE_AS), + gtk.Action(FLOW_GRAPH_CLOSE, '_Close', 'Close the current flow graph', gtk.STOCK_CLOSE), + gtk.Action(APPLICATION_QUIT, '_Quit', 'Quit program', gtk.STOCK_QUIT), + gtk.Action(FLOW_GRAPH_UNDO, '_Undo', 'Undo a change to the flow graph', gtk.STOCK_UNDO), + gtk.Action(FLOW_GRAPH_REDO, '_Redo', 'Redo a change to the flow graph', gtk.STOCK_REDO), + gtk.Action(ELEMENT_DELETE, '_Delete', 'Delete the selected blocks', gtk.STOCK_DELETE), + gtk.Action(BLOCK_ROTATE_CCW, 'Rotate Counterclockwise', 'Rotate the selected blocks 90 degrees to the left', gtk.STOCK_GO_BACK), + gtk.Action(BLOCK_ROTATE_CW, 'Rotate Clockwise', 'Rotate the selected blocks 90 degrees to the right', gtk.STOCK_GO_FORWARD), + gtk.Action(BLOCK_PARAM_MODIFY, '_Properties', 'Modify params for the selected block', gtk.STOCK_PROPERTIES), + gtk.Action(BLOCK_ENABLE, 'E_nable', 'Enable the selected blocks', gtk.STOCK_CONNECT), + gtk.Action(BLOCK_DISABLE, 'D_isable', 'Disable the selected blocks', gtk.STOCK_DISCONNECT), + gtk.Action(BLOCK_CUT, 'Cu_t', 'Cut', gtk.STOCK_CUT), + gtk.Action(BLOCK_COPY, '_Copy', 'Copy', gtk.STOCK_COPY), + gtk.Action(BLOCK_PASTE, '_Paste', 'Paste', gtk.STOCK_PASTE), + gtk.Action(ABOUT_WINDOW_DISPLAY, '_About', 'About this program', gtk.STOCK_ABOUT), + gtk.Action(HELP_WINDOW_DISPLAY, '_Help', 'Usage Tips', gtk.STOCK_HELP), + gtk.Action(FLOW_GRAPH_GEN, '_Generate', 'Generate the flow graph', gtk.STOCK_CONVERT), + gtk.Action(FLOW_GRAPH_EXEC, '_Execute', 'Execute the flow graph', gtk.STOCK_EXECUTE), + gtk.Action(FLOW_GRAPH_KILL, '_Kill', 'Kill the flow graph', gtk.STOCK_STOP), + gtk.Action(FLOW_GRAPH_SCREEN_CAPTURE, 'S_creen Capture', 'Create a screen capture of the flow graph', gtk.STOCK_PRINT), ) def get_all_actions(): return _actions_list @@ -122,16 +147,21 @@ def get_action_from_name(action_name): @throw KeyError bad action name @return a gtk action object """ - if _actions_dict.has_key(action_name): return _actions_dict[action_name] + if action_name in _actions_dict: return _actions_dict[action_name] raise KeyError('Action Name: "%s" does not exist'%action_name) +###################################################################################################### +# Accelerators +###################################################################################################### _accel_group = gtk.AccelGroup() def get_accel_group(): return _accel_group -#load the actions key map #set the accelerator group, and accelerator path -#register the key and mod with the accelerator path -for action_name, (key_name, mod) in _actions_key_map.iteritems(): - get_action_from_name(action_name).set_accel_group(get_accel_group()) - get_action_from_name(action_name).set_accel_path('<main>/'+action_name) - gtk.accel_map_add_entry('<main>/'+action_name, gtk.gdk.keyval_from_name(key_name),mod) +#register the key name and mod mask with the accelerator path +for action_name, key_name, mod_mask in _actions_key_list: + try: + accel_path = '<main>/'+action_name + get_action_from_name(action_name).set_accel_group(get_accel_group()) + get_action_from_name(action_name).set_accel_path(accel_path) + gtk.accel_map_add_entry(accel_path, gtk.gdk.keyval_from_name(key_name), mod_mask) + except KeyError: pass #no action was created for this action name diff --git a/grc/src/gui/Bars.py b/grc/src/gui/Bars.py index c89aea580..52e7ba1f8 100644 --- a/grc/src/gui/Bars.py +++ b/grc/src/gui/Bars.py @@ -1,5 +1,5 @@ """ -Copyright 2007 Free Software Foundation, Inc. +Copyright 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -43,8 +43,8 @@ TOOLBAR_LIST = ( Actions.FLOW_GRAPH_EXEC, Actions.FLOW_GRAPH_KILL, None, - Actions.BLOCK_ROTATE_LEFT, - Actions.BLOCK_ROTATE_RIGHT, + Actions.BLOCK_ROTATE_CCW, + Actions.BLOCK_ROTATE_CW, None, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, @@ -73,8 +73,8 @@ MENU_BAR_LIST = ( Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE, None, - Actions.BLOCK_ROTATE_LEFT, - Actions.BLOCK_ROTATE_RIGHT, + Actions.BLOCK_ROTATE_CCW, + Actions.BLOCK_ROTATE_CW, None, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, @@ -87,6 +87,8 @@ MENU_BAR_LIST = ( Actions.FLOW_GRAPH_KILL, ]), (gtk.Action('Help', '_Help', None, None), [ + Actions.HELP_WINDOW_DISPLAY, + None, Actions.ABOUT_WINDOW_DISPLAY, ]), ) diff --git a/grc/src/gui/BlockTreeWindow.py b/grc/src/gui/BlockTreeWindow.py index 94b9476c5..9e4bec3f9 100644 --- a/grc/src/gui/BlockTreeWindow.py +++ b/grc/src/gui/BlockTreeWindow.py @@ -1,5 +1,5 @@ """ -Copyright 2007 Free Software Foundation, Inc. +Copyright 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -from Constants import DEFAULT_BLOCKS_WINDOW_WIDTH +from Constants import DEFAULT_BLOCKS_WINDOW_WIDTH, DND_TARGETS import pygtk pygtk.require('2.0') import gtk @@ -53,6 +53,9 @@ class BlockTreeWindow(gtk.VBox): renderer = gtk.CellRendererText() column = gtk.TreeViewColumn('Blocks', renderer, text=NAME_INDEX) self.treeview.append_column(column) + #setup drag and drop + self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, DND_TARGETS, gtk.gdk.ACTION_COPY) + self.treeview.connect('drag-data-get', self._handle_drag_get_data) #make the scrolled window to hold the tree view scrolled_window = gtk.ScrolledWindow() scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) @@ -60,12 +63,11 @@ class BlockTreeWindow(gtk.VBox): scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1) self.pack_start(scrolled_window) #add button - self.add_button = gtk.Button(None, 'gtk-add') + self.add_button = gtk.Button(None, gtk.STOCK_ADD) self.add_button.connect('clicked', self._handle_add_button) self.pack_start(self.add_button, False) - #map categories to iters - self.categories = dict() - self.categories[tuple()] = None + #map categories to iters, automatic mapping for root + self._categories = {tuple(): None} #add blocks and categories self.platform.load_block_tree(self) #initialize @@ -78,22 +80,21 @@ class BlockTreeWindow(gtk.VBox): """ Add a block with category to this selection window. Add only the category when block is None. - @param category the category string + @param category the category list @param block the block object or None """ - #rectify category - category = filter(lambda x: x, category.split('/')) + category = tuple(category)[1:] #tuple is hashable #add category and all sub categories - for i in range(len(category)): - sub_category = tuple(category[:i+1]) - if sub_category not in self.categories.keys(): - iter = self.treestore.insert_before(self.categories[tuple(category[:i])], None) - self.treestore.set_value(iter, NAME_INDEX, '[ %s ]'%category[i]) + for i, cat_name in enumerate(category): + sub_category = category[:i+1] + if sub_category not in self._categories: + iter = self.treestore.insert_before(self._categories[sub_category[:-1]], None) + self.treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name) self.treestore.set_value(iter, KEY_INDEX, '') - self.categories[sub_category] = iter + self._categories[sub_category] = iter #add block if block is None: return - iter = self.treestore.insert_before(self.categories[tuple(category)], None) + iter = self.treestore.insert_before(self._categories[category], None) self.treestore.set_value(iter, NAME_INDEX, block.get_name()) self.treestore.set_value(iter, KEY_INDEX, block.get_key()) @@ -127,6 +128,15 @@ class BlockTreeWindow(gtk.VBox): ############################################################ ## Event Handlers ############################################################ + def _handle_drag_get_data(self, widget, drag_context, selection_data, info, time): + """ + Handle a drag and drop by setting the key to the selection object. + This will call the destination handler for drag and drop. + Only call set when the key is valid to ignore DND from categories. + """ + key = self._get_selected_block_key() + if key: selection_data.set(selection_data.target, 8, key) + def _handle_mouse_button_press(self, widget, event): """ Handle the mouse button press. diff --git a/grc/src/gui/Constants.py b/grc/src/gui/Constants.py index 70e6b6b6e..f23ab8b13 100644 --- a/grc/src/gui/Constants.py +++ b/grc/src/gui/Constants.py @@ -1,5 +1,5 @@ """ -Copyright 2008 Free Software Foundation, Inc. +Copyright 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -17,6 +17,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ +import pygtk +pygtk.require('2.0') +import gtk import os ##default path for the open/save dialogs @@ -28,10 +31,6 @@ IMAGE_FILE_EXTENSION = '.png' ##name for new/unsaved flow graphs NEW_FLOGRAPH_TITLE = 'untitled' -##rotation constants -DIR_LEFT = 'left' -DIR_RIGHT = 'right' - ##main window constraints MIN_WINDOW_WIDTH = 600 MIN_WINDOW_HEIGHT = 400 @@ -42,16 +41,8 @@ MIN_DIALOG_HEIGHT = 500 DEFAULT_BLOCKS_WINDOW_WIDTH = 100 DEFAULT_REPORTS_WINDOW_WIDTH = 100 -##How close can the mouse get to the window border before mouse events are ignored. -BORDER_PROXIMITY_SENSITIVITY = 50 -##How close the mouse can get to the edge of the visible window before scrolling is invoked. -SCROLL_PROXIMITY_SENSITIVITY = 30 -##When the window has to be scrolled, move it this distance in the required direction. -SCROLL_DISTANCE = 15 -##The redrawing sensitivity, how many seconds must pass between motion events before a redraw? -MOTION_DETECT_REDRAWING_SENSITIVITY = .02 -##How close the mouse click can be to a connection and register a connection select. -CONNECTION_SELECT_SENSITIVITY = 5 - ##The size of the state saving cache in the flow graph (for undo/redo functionality) STATE_CACHE_SIZE = 42 + +##Shared targets for drag and drop of blocks +DND_TARGETS = [('STRING', gtk.TARGET_SAME_APP, 0)] diff --git a/grc/src/gui/Dialogs.py b/grc/src/gui/Dialogs.py index e15f8c574..d526b97b4 100644 --- a/grc/src/gui/Dialogs.py +++ b/grc/src/gui/Dialogs.py @@ -1,5 +1,5 @@ """ -Copyright 2008 Free Software Foundation, Inc. +Copyright 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -20,7 +20,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import pygtk pygtk.require('2.0') import gtk -from Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT from .. platforms.base.Constants import PACKAGE, VERSION import Preferences @@ -82,3 +81,20 @@ Achilleas Anastasopoulos -> trellis support -----""") self.run() self.destroy() + +def HelpDialog(): + MessageDialogHelper( + type=gtk.MESSAGE_INFO, + buttons=gtk.BUTTONS_CLOSE, + title='Help', + markup="""\ +<b>Usage Tips</b> + +<u>Add block</u>: drag and drop or double click a block in the block selection window. +<u>Rotate block</u>: Select a block, press left/right on the keyboard. +<u>Change type</u>: Select a block, press up/down on the keyboard. +<u>Edit parameters</u>: double click on a block in the flow graph. +<u>Make connection</u>: click on the source port of one block, then click on the sink port of another block. +<u>Remove connection</u>: select the connection and press delete, or drag the connection. + +* See the menu for other keyboard shortcuts.""") diff --git a/grc/src/gui/DrawingArea.py b/grc/src/gui/DrawingArea.py index 3588e122d..5258979d8 100644 --- a/grc/src/gui/DrawingArea.py +++ b/grc/src/gui/DrawingArea.py @@ -1,5 +1,5 @@ """ -Copyright 2007 Free Software Foundation, Inc. +Copyright 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import pygtk pygtk.require('2.0') import gtk -from Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT +from Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, DND_TARGETS class DrawingArea(gtk.DrawingArea): """ @@ -51,24 +51,26 @@ class DrawingArea(gtk.DrawingArea): gtk.gdk.LEAVE_NOTIFY_MASK | \ gtk.gdk.ENTER_NOTIFY_MASK ) + #setup drag and drop + self.drag_dest_set(gtk.DEST_DEFAULT_ALL, DND_TARGETS, gtk.gdk.ACTION_COPY) + self.connect('drag-data-received', self._handle_drag_data_received) #setup the focus flag self._focus_flag = False self.get_focus_flag = lambda: self._focus_flag - self.connect("leave-notify-event", self._handle_focus_event, False) - self.connect("enter-notify-event", self._handle_focus_event, True) + self.connect('leave-notify-event', self._handle_focus_event, False) + self.connect('enter-notify-event', self._handle_focus_event, True) #pixmap for drawing self.pixmap = None - self.gc = None - - def draw(self): - """ - Draw the pixmap onto this drawing area. - """ - self.window.draw_drawable(self.gc, self.pixmap, 0, 0, 0, 0, -1, -1) ########################################################################## ## Handlers ########################################################################## + def _handle_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time): + """ + Handle a drag and drop by adding a block at the given coordinate. + """ + self._main_window.get_flow_graph().add_new_block(selection_data.data, (x, y)) + def _handle_focus_event(self, widget, event, focus_flag): """Record the focus state of the flow graph window.""" self._focus_flag = focus_flag @@ -83,7 +85,6 @@ class DrawingArea(gtk.DrawingArea): double_click=(event.type == gtk.gdk._2BUTTON_PRESS), coordinate=(event.x, event.y), ) - return True def _handle_mouse_button_release(self, widget, event): """ @@ -94,7 +95,6 @@ class DrawingArea(gtk.DrawingArea): left_click=(event.button == 1), coordinate=(event.x, event.y), ) - return True def _handle_mouse_motion(self, widget, event): """ @@ -104,15 +104,15 @@ class DrawingArea(gtk.DrawingArea): self._main_window.get_flow_graph().handle_mouse_motion( coordinate=(event.x, event.y), ) - return True def _handle_window_expose(self, widget, event): """ - Called when the window initially appears or is resized: create a new pixmap, draw the flow graph. + Called when window is exposed, resized, or queue_draw is called. """ - self.gc = self.window.new_gc() + gc = self.window.new_gc() width, height = self.get_size_request() if not self.pixmap or (width, height) != self.pixmap.get_size(): self.pixmap = gtk.gdk.Pixmap(self.window, width, height, -1) - self._main_window.get_flow_graph().draw() - return True + #double buffering: draw to pixmap, then draw pixmap + self._main_window.get_flow_graph().draw(gc, self.pixmap) + self.window.draw_drawable(gc, self.pixmap, 0, 0, 0, 0, -1, -1) diff --git a/grc/src/gui/ParamsDialog.py b/grc/src/gui/ParamsDialog.py index 5837ab7c4..6cc42e8fc 100644 --- a/grc/src/gui/ParamsDialog.py +++ b/grc/src/gui/ParamsDialog.py @@ -1,5 +1,5 @@ """ -Copyright 2007 Free Software Foundation, Inc. +Copyright 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -45,9 +45,11 @@ class ParamsDialog(gtk.Dialog): SignalBlockParamsDialog contructor. @param block the signal block """ - gtk.Dialog.__init__(self, buttons=('gtk-close', gtk.RESPONSE_CLOSE)) + gtk.Dialog.__init__(self, + title='Properties: %s'%block.get_name(), + buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE), + ) self.block = block - self.set_title('Properties: %s'%block.get_name()) self.set_size_request(MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT) vbox = gtk.VBox() #Add the title label |