summaryrefslogtreecommitdiff
path: root/grc/gui
diff options
context:
space:
mode:
Diffstat (limited to 'grc/gui')
-rw-r--r--grc/gui/ActionHandler.py190
-rw-r--r--grc/gui/Actions.py379
-rw-r--r--grc/gui/Bars.py18
-rw-r--r--grc/gui/Block.py37
-rw-r--r--grc/gui/BlockTreeWindow.py15
-rw-r--r--grc/gui/Connection.py11
-rw-r--r--grc/gui/Dialogs.py18
-rw-r--r--grc/gui/Element.py53
-rw-r--r--grc/gui/FlowGraph.py26
-rw-r--r--grc/gui/MainWindow.py23
-rw-r--r--grc/gui/Makefile.am2
-rw-r--r--grc/gui/NotebookPage.py7
-rw-r--r--grc/gui/Param.py150
-rw-r--r--grc/gui/Platform.py33
-rw-r--r--grc/gui/Port.py13
-rw-r--r--grc/gui/PropsDialog.py (renamed from grc/gui/ParamsDialog.py)134
-rw-r--r--grc/gui/StateCache.py6
-rw-r--r--grc/gui/Utils.py20
18 files changed, 653 insertions, 482 deletions
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index ff137f669..ee3e19a6c 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -30,9 +30,8 @@ from threading import Thread
import Messages
from .. base import ParseXML
import random
-from Platform import Platform
from MainWindow import MainWindow
-from ParamsDialog import ParamsDialog
+from PropsDialog import PropsDialog
import Dialogs
from FileDialogs import OpenFlowGraphFileDialog, SaveFlowGraphFileDialog, SaveImageFileDialog
@@ -53,11 +52,10 @@ class ActionHandler:
@param platform platform module
"""
self.clipboard = None
- platform = Platform(platform)
- for action in Actions.get_all_actions(): action.connect('activate', self._handle_actions)
+ for action in Actions.get_all_actions(): action.connect('activate', self._handle_action)
#setup the main window
- self.main_window = MainWindow(self.handle_states, platform)
- self.main_window.connect('delete_event', self._quit)
+ self.main_window = MainWindow(platform)
+ self.main_window.connect('delete-event', self._quit)
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
@@ -67,7 +65,7 @@ class ActionHandler:
Messages.send_init(platform)
#initialize
self.init_file_paths = file_paths
- self.handle_states(Actions.APPLICATION_INITIALIZE)
+ Actions.APPLICATION_INITIALIZE()
#enter the mainloop
gtk.main()
@@ -81,17 +79,9 @@ class ActionHandler:
When not in focus, gtk and the accelerators handle the the key press.
@return false to let gtk handle the key action
"""
- #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
+ try: assert self.get_focus_flag()
+ except AssertionError: return False
+ return Actions.handle_key_press(event)
def _quit(self, window, event):
"""
@@ -100,41 +90,24 @@ class ActionHandler:
This method in turns calls the state handler to quit.
@return true
"""
- self.handle_states(Actions.APPLICATION_QUIT)
+ Actions.APPLICATION_QUIT()
return True
- def _handle_actions(self, event):
- """
- Handle all of the activate signals from the gtk actions.
- The action signals derive from clicking on a toolbar or menu bar button.
- Forward the action to the state handler.
- """
- self.handle_states(event.get_name())
-
- def handle_states(self, state=''):
- """
- Handle the state changes in the GUI.
- Handle all of the state changes that arise from the action handler or other gui and
- inputs in the application. The state passed to the handle_states method is a string descriping
- the change. A series of if/elif statements handle the state by greying out action buttons, causing
- changes in the flow graph, saving/opening files... The handle_states method is passed to the
- contructors of many of the classes used in this application enabling them to report any state change.
- @param state a string describing the state change
- """
- #print state
+ def _handle_action(self, action):
+ #print action
##################################################
# Initalize/Quit
##################################################
- if state == Actions.APPLICATION_INITIALIZE:
+ if action == Actions.APPLICATION_INITIALIZE:
for action in Actions.get_all_actions(): action.set_sensitive(False) #set all actions disabled
- # enable a select few actions
+ #enable a select few actions
for action in (
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.HELP_WINDOW_DISPLAY,
- Actions.COLORS_WINDOW_DISPLAY,
- ): Actions.get_action_from_name(action).set_sensitive(True)
+ Actions.TYPES_WINDOW_DISPLAY,
+ ): action.set_sensitive(True)
if not self.init_file_paths:
self.init_file_paths = Preferences.files_open()
if not self.init_file_paths: self.init_file_paths = ['']
@@ -143,26 +116,26 @@ class ActionHandler:
if Preferences.file_open() in self.init_file_paths:
self.main_window.new_page(Preferences.file_open(), show=True)
if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists
- elif state == Actions.APPLICATION_QUIT:
+ elif action == Actions.APPLICATION_QUIT:
if self.main_window.close_pages():
gtk.main_quit()
exit(0)
##################################################
# Selections
##################################################
- elif state == Actions.ELEMENT_SELECT:
+ elif action == Actions.ELEMENT_SELECT:
pass #do nothing, update routines below
- elif state == Actions.NOTHING_SELECT:
+ elif action == Actions.NOTHING_SELECT:
self.get_flow_graph().unselect()
##################################################
# Enable/Disable
##################################################
- elif state == Actions.BLOCK_ENABLE:
+ elif action == Actions.BLOCK_ENABLE:
if self.get_flow_graph().enable_selected(True):
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_DISABLE:
+ elif action == Actions.BLOCK_DISABLE:
if self.get_flow_graph().enable_selected(False):
self.get_flow_graph().update()
self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
@@ -170,12 +143,12 @@ class ActionHandler:
##################################################
# Cut/Copy/Paste
##################################################
- elif state == Actions.BLOCK_CUT:
- self.handle_states(Actions.BLOCK_COPY)
- self.handle_states(Actions.ELEMENT_DELETE)
- elif state == Actions.BLOCK_COPY:
+ elif action == Actions.BLOCK_CUT:
+ Actions.BLOCK_COPY()
+ Actions.ELEMENT_DELETE()
+ elif action == Actions.BLOCK_COPY:
self.clipboard = self.get_flow_graph().copy_to_clipboard()
- elif state == Actions.BLOCK_PASTE:
+ elif action == Actions.BLOCK_PASTE:
if self.clipboard:
self.get_flow_graph().paste_from_clipboard(self.clipboard)
self.get_flow_graph().update()
@@ -184,81 +157,88 @@ class ActionHandler:
##################################################
# Move/Rotate/Delete/Create
##################################################
- elif state == Actions.BLOCK_MOVE:
+ elif action == 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_CCW:
+ elif action == 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_CW:
+ elif action == 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)
- elif state == Actions.ELEMENT_DELETE:
+ elif action == Actions.ELEMENT_DELETE:
if self.get_flow_graph().remove_selected():
self.get_flow_graph().update()
self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.handle_states(Actions.NOTHING_SELECT)
+ Actions.NOTHING_SELECT()
self.get_page().set_saved(False)
- elif state == Actions.ELEMENT_CREATE:
+ elif action == Actions.ELEMENT_CREATE:
self.get_flow_graph().update()
self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
- self.handle_states(Actions.NOTHING_SELECT)
+ Actions.NOTHING_SELECT()
self.get_page().set_saved(False)
- elif state == Actions.BLOCK_INC_TYPE:
+ elif action == Actions.BLOCK_INC_TYPE:
if self.get_flow_graph().type_controller_modify_selected(1):
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_DEC_TYPE:
+ elif action == Actions.BLOCK_DEC_TYPE:
if self.get_flow_graph().type_controller_modify_selected(-1):
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.PORT_CONTROLLER_INC:
+ elif action == Actions.PORT_CONTROLLER_INC:
if self.get_flow_graph().port_controller_modify_selected(1):
self.get_flow_graph().update()
- self.get_flow_graph().update() #2 times
self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
self.get_page().set_saved(False)
- elif state == Actions.PORT_CONTROLLER_DEC:
+ elif action == Actions.PORT_CONTROLLER_DEC:
if self.get_flow_graph().port_controller_modify_selected(-1):
self.get_flow_graph().update()
- self.get_flow_graph().update() #2 times
self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
self.get_page().set_saved(False)
##################################################
# Window stuff
##################################################
- elif state == Actions.ABOUT_WINDOW_DISPLAY:
+ elif action == Actions.ABOUT_WINDOW_DISPLAY:
Dialogs.AboutDialog(self.get_flow_graph().get_parent())
- elif state == Actions.HELP_WINDOW_DISPLAY:
+ elif action == Actions.HELP_WINDOW_DISPLAY:
Dialogs.HelpDialog()
- elif state == Actions.COLORS_WINDOW_DISPLAY:
- Dialogs.ColorsDialog(self.get_flow_graph().get_parent())
+ elif action == Actions.TYPES_WINDOW_DISPLAY:
+ Dialogs.TypesDialog(self.get_flow_graph().get_parent())
+ elif action == Actions.ERRORS_WINDOW_DISPLAY:
+ Dialogs.ErrorsDialog(self.get_flow_graph())
##################################################
# Param Modifications
##################################################
- elif state == Actions.BLOCK_PARAM_MODIFY:
+ elif action == Actions.BLOCK_PARAM_MODIFY:
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)
+ if selected_block:
+ if PropsDialog(selected_block).run():
+ #save the new state
+ 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)
+ else:
+ #restore the current state
+ n = self.get_page().get_state_cache().get_current_state()
+ self.get_flow_graph().import_data(n)
+ self.get_flow_graph().update()
##################################################
# Undo/Redo
##################################################
- elif state == Actions.FLOW_GRAPH_UNDO:
+ elif action == Actions.FLOW_GRAPH_UNDO:
n = self.get_page().get_state_cache().get_prev_state()
if n:
self.get_flow_graph().unselect()
self.get_flow_graph().import_data(n)
self.get_flow_graph().update()
self.get_page().set_saved(False)
- elif state == Actions.FLOW_GRAPH_REDO:
+ elif action == Actions.FLOW_GRAPH_REDO:
n = self.get_page().get_state_cache().get_next_state()
if n:
self.get_flow_graph().unselect()
@@ -268,19 +248,19 @@ class ActionHandler:
##################################################
# New/Open/Save/Close
##################################################
- elif state == Actions.FLOW_GRAPH_NEW:
+ elif action == Actions.FLOW_GRAPH_NEW:
self.main_window.new_page()
- elif state == Actions.FLOW_GRAPH_OPEN:
+ elif action == Actions.FLOW_GRAPH_OPEN:
file_paths = OpenFlowGraphFileDialog(self.get_page().get_file_path()).run()
if file_paths: #open a new page for each file, show only the first
for i,file_path in enumerate(file_paths):
self.main_window.new_page(file_path, show=(i==0))
- elif state == Actions.FLOW_GRAPH_CLOSE:
+ elif action == Actions.FLOW_GRAPH_CLOSE:
self.main_window.close_page()
- elif state == Actions.FLOW_GRAPH_SAVE:
+ elif action == Actions.FLOW_GRAPH_SAVE:
#read-only or undefined file path, do save-as
if self.get_page().get_read_only() or not self.get_page().get_file_path():
- self.handle_states(Actions.FLOW_GRAPH_SAVE_AS)
+ Actions.FLOW_GRAPH_SAVE_AS()
#otherwise try to save
else:
try:
@@ -289,12 +269,12 @@ class ActionHandler:
except IOError:
Messages.send_fail_save(self.get_page().get_file_path())
self.get_page().set_saved(False)
- elif state == Actions.FLOW_GRAPH_SAVE_AS:
+ elif action == Actions.FLOW_GRAPH_SAVE_AS:
file_path = SaveFlowGraphFileDialog(self.get_page().get_file_path()).run()
if file_path is not None:
self.get_page().set_file_path(file_path)
- self.handle_states(Actions.FLOW_GRAPH_SAVE)
- elif state == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
+ Actions.FLOW_GRAPH_SAVE()
+ elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
file_path = SaveImageFileDialog(self.get_page().get_file_path()).run()
if file_path is not None:
pixbuf = self.get_flow_graph().get_drawing_area().get_pixbuf()
@@ -302,10 +282,10 @@ class ActionHandler:
##################################################
# Gen/Exec/Stop
##################################################
- elif state == Actions.FLOW_GRAPH_GEN:
+ elif action == Actions.FLOW_GRAPH_GEN:
if not self.get_page().get_pid():
if not self.get_page().get_saved() or not self.get_page().get_file_path():
- self.handle_states(Actions.FLOW_GRAPH_SAVE) #only save if file path missing or not saved
+ Actions.FLOW_GRAPH_SAVE() #only save if file path missing or not saved
if self.get_page().get_saved() and self.get_page().get_file_path():
generator = self.get_page().get_generator()
try:
@@ -313,37 +293,38 @@ class ActionHandler:
generator.write()
except Exception,e: Messages.send_fail_gen(e)
else: self.generator = None
- elif state == Actions.FLOW_GRAPH_EXEC:
+ elif action == Actions.FLOW_GRAPH_EXEC:
if not self.get_page().get_pid():
- self.handle_states(Actions.FLOW_GRAPH_GEN)
+ Actions.FLOW_GRAPH_GEN()
if self.get_page().get_saved() and self.get_page().get_file_path():
ExecFlowGraphThread(self)
- elif state == Actions.FLOW_GRAPH_KILL:
+ elif action == Actions.FLOW_GRAPH_KILL:
if self.get_page().get_pid():
try: os.kill(self.get_page().get_pid(), signal.SIGKILL)
except: print "could not kill pid: %s"%self.get_page().get_pid()
- elif state == '': #pass and run the global actions
+ elif action == Actions.PAGE_CHANGE: #pass and run the global actions
pass
- else: print '!!! State "%s" not handled !!!'%state
+ else: print '!!! Action "%s" not handled !!!'%action
##################################################
# Global Actions for all States
##################################################
#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_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()))
+ Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid())
+ Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements()))
+ Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(self.get_flow_graph().get_selected_block()))
+ Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
+ 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()))
- Actions.get_action_from_name(Actions.BLOCK_PASTE).set_sensitive(bool(self.clipboard))
+ Actions.BLOCK_CUT.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
+ Actions.BLOCK_COPY.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
+ Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard))
#update enable/disable
- Actions.get_action_from_name(Actions.BLOCK_ENABLE).set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
- Actions.get_action_from_name(Actions.BLOCK_DISABLE).set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
+ Actions.BLOCK_ENABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
+ Actions.BLOCK_DISABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
#set the exec and stop buttons
self.update_exec_stop()
#saved status
- Actions.get_action_from_name(Actions.FLOW_GRAPH_SAVE).set_sensitive(not self.get_page().get_saved())
+ Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved())
self.main_window.update()
try: #set the size of the flow graph area (if changed)
new_size = self.get_flow_graph().get_option('window_size')
@@ -353,6 +334,7 @@ class ActionHandler:
#draw the flow graph
self.get_flow_graph().update_selected()
self.get_flow_graph().queue_draw()
+ return True #action was handled
def update_exec_stop(self):
"""
@@ -360,9 +342,9 @@ class ActionHandler:
Lock and unlock the mutex for race conditions with exec flow graph threads.
"""
sensitive = self.get_flow_graph().is_valid() and not self.get_page().get_pid()
- Actions.get_action_from_name(Actions.FLOW_GRAPH_GEN).set_sensitive(sensitive)
- Actions.get_action_from_name(Actions.FLOW_GRAPH_EXEC).set_sensitive(sensitive)
- Actions.get_action_from_name(Actions.FLOW_GRAPH_KILL).set_sensitive(self.get_page().get_pid() != None)
+ Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive)
+ Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive)
+ Actions.FLOW_GRAPH_KILL.set_sensitive(self.get_page().get_pid() != None)
class ExecFlowGraphThread(Thread):
"""Execute the flow graph as a new process and wait on it to finish."""
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index 3695e09ef..f374efde1 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -21,149 +21,254 @@ import pygtk
pygtk.require('2.0')
import gtk
-######################################################################################################
-# Action Names
-######################################################################################################
-APPLICATION_INITIALIZE = 'app init'
-APPLICATION_QUIT = 'app quit'
-PARAM_MODIFY = 'param modify'
-BLOCK_MOVE = 'block move'
-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'
-BLOCK_ENABLE = 'block enable'
-BLOCK_DISABLE = 'block disable'
-BLOCK_CUT = 'block cut'
-BLOCK_COPY = 'block copy'
-BLOCK_PASTE = 'block paste'
-PORT_CONTROLLER_INC = 'port controller increment'
-PORT_CONTROLLER_DEC = 'port controller decrement'
-ELEMENT_CREATE = 'element create'
-ELEMENT_DELETE = 'element delete'
-ELEMENT_SELECT = 'element select'
-NOTHING_SELECT = 'nothing select'
-FLOW_GRAPH_OPEN = 'flow graph open'
-FLOW_GRAPH_UNDO = 'flow graph undo'
-FLOW_GRAPH_REDO = 'flow graph redo'
-FLOW_GRAPH_SAVE = 'flow graph save'
-FLOW_GRAPH_SAVE_AS = 'flow graph save as'
-FLOW_GRAPH_CLOSE = 'flow graph close'
-FLOW_GRAPH_NEW = 'flow graph new'
-FLOW_GRAPH_GEN = 'flow graph gen'
-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'
-COLORS_WINDOW_DISPLAY = 'colors window display'
+NO_MODS_MASK = 0
-######################################################################################################
-# Action Key Map
-######################################################################################################
-_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):
+########################################################################
+# Actions API
+########################################################################
+_actions_keypress_dict = dict()
+_keymap = gtk.gdk.keymap_get_default()
+_used_mods_mask = NO_MODS_MASK
+def handle_key_press(event):
"""
- 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
+ Call the action associated with the key press event.
+ Both the key value and the mask must have a match.
+ @param event a gtk key press event
+ @return true if handled
"""
- 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 ''
+ _used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for keyval, mod_mask in _actions_keypress_dict], NO_MODS_MASK)
+ #extract the key value and the consumed modifiers
+ keyval, egroup, level, consumed = _keymap.translate_keyboard_state(
+ event.hardware_keycode, event.state, event.group)
+ #get the modifier mask and ignore irrelevant modifiers
+ mod_mask = event.state & ~consumed & _used_mods_mask
+ #look up the keypress and call the action
+ try: _actions_keypress_dict[(keyval, mod_mask)]()
+ except KeyError: return False #not handled
+ return True #handled here
-######################################################################################################
-# Actions
-######################################################################################################
-_actions_list = (
- 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(COLORS_WINDOW_DISPLAY, '_Colors', 'Color Mapping', gtk.STOCK_DIALOG_INFO),
- 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
+_all_actions_list = list()
+def get_all_actions(): return _all_actions_list
+
+_accel_group = gtk.AccelGroup()
+def get_accel_group(): return _accel_group
-_actions_dict = dict((action.get_name(), action) for action in _actions_list)
-def get_action_from_name(action_name):
+class Action(gtk.Action):
"""
- Retrieve the action from the action list.
- Search the list and find an action with said name.
- @param action_name the action name(string)
- @throw KeyError bad action name
- @return a gtk action object
+ A custom Action class based on gtk.Action.
+ Pass additional arguments such as keypresses.
+ Register actions and keypresses with this module.
"""
- 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
+ def __init__(self, keypresses=(), name=None, label=None, tooltip=None, stock_id=None):
+ """
+ Create a new Action instance.
+ @param key_presses a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...)
+ @param the regular gtk.Action parameters (defaults to None)
+ """
+ if name is None: name = label
+ gtk.Action.__init__(self,
+ name=name, label=label,
+ tooltip=tooltip, stock_id=stock_id,
+ )
+ #register this action
+ _all_actions_list.append(self)
+ for i in range(len(keypresses)/2):
+ keyval, mod_mask = keypresses[i*2:(i+1)*2]
+ #register this keypress
+ assert not _actions_keypress_dict.has_key((keyval, mod_mask))
+ _actions_keypress_dict[(keyval, mod_mask)] = self
+ #set the accelerator group, and accelerator path
+ #register the key name and mod mask with the accelerator path
+ if label is None: continue #dont register accel
+ accel_path = '<main>/'+self.get_name()
+ self.set_accel_group(get_accel_group())
+ self.set_accel_path(accel_path)
+ gtk.accel_map_add_entry(accel_path, keyval, mod_mask)
+
+ def __str__(self):
+ """
+ The string representation should be the name of the action id.
+ Try to find the action id for this action by searching this module.
+ """
+ try:
+ import Actions
+ return filter(lambda attr: getattr(Actions, attr) == self, dir(Actions))[0]
+ except: return self.get_name()
+
+ def __repr__(self): return str(self)
+
+ def __call__(self):
+ """
+ Emit the activate signal when called with ().
+ """
+ self.emit('activate')
-#set the accelerator group, and accelerator path
-#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
+########################################################################
+# Actions
+########################################################################
+PAGE_CHANGE = Action()
+FLOW_GRAPH_NEW = Action(
+ label='_New',
+ tooltip='Create a new flow graph',
+ stock_id=gtk.STOCK_NEW,
+ keypresses=(gtk.keysyms.n, gtk.gdk.CONTROL_MASK),
+)
+FLOW_GRAPH_OPEN = Action(
+ label='_Open',
+ tooltip='Open an existing flow graph',
+ stock_id=gtk.STOCK_OPEN,
+ keypresses=(gtk.keysyms.o, gtk.gdk.CONTROL_MASK),
+)
+FLOW_GRAPH_SAVE = Action(
+ label='_Save',
+ tooltip='Save the current flow graph',
+ stock_id=gtk.STOCK_SAVE,
+ keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK),
+)
+FLOW_GRAPH_SAVE_AS = Action(
+ label='Save _As',
+ tooltip='Save the current flow graph as...',
+ stock_id=gtk.STOCK_SAVE_AS,
+ keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK),
+)
+FLOW_GRAPH_CLOSE = Action(
+ label='_Close',
+ tooltip='Close the current flow graph',
+ stock_id=gtk.STOCK_CLOSE,
+ keypresses=(gtk.keysyms.w, gtk.gdk.CONTROL_MASK),
+)
+APPLICATION_INITIALIZE = Action()
+APPLICATION_QUIT = Action(
+ label='_Quit',
+ tooltip='Quit program',
+ stock_id=gtk.STOCK_QUIT,
+ keypresses=(gtk.keysyms.q, gtk.gdk.CONTROL_MASK),
+)
+FLOW_GRAPH_UNDO = Action(
+ label='_Undo',
+ tooltip='Undo a change to the flow graph',
+ stock_id=gtk.STOCK_UNDO,
+ keypresses=(gtk.keysyms.z, gtk.gdk.CONTROL_MASK),
+)
+FLOW_GRAPH_REDO = Action(
+ label='_Redo',
+ tooltip='Redo a change to the flow graph',
+ stock_id=gtk.STOCK_REDO,
+ keypresses=(gtk.keysyms.y, gtk.gdk.CONTROL_MASK),
+)
+NOTHING_SELECT = Action()
+ELEMENT_SELECT = Action()
+ELEMENT_CREATE = Action()
+ELEMENT_DELETE = Action(
+ label='_Delete',
+ tooltip='Delete the selected blocks',
+ stock_id=gtk.STOCK_DELETE,
+ keypresses=(gtk.keysyms.Delete, NO_MODS_MASK),
+)
+BLOCK_MOVE = Action()
+BLOCK_ROTATE_CCW = Action(
+ label='Rotate Counterclockwise',
+ tooltip='Rotate the selected blocks 90 degrees to the left',
+ stock_id=gtk.STOCK_GO_BACK,
+ keypresses=(gtk.keysyms.Left, NO_MODS_MASK),
+)
+BLOCK_ROTATE_CW = Action(
+ label='Rotate Clockwise',
+ tooltip='Rotate the selected blocks 90 degrees to the right',
+ stock_id=gtk.STOCK_GO_FORWARD,
+ keypresses=(gtk.keysyms.Right, NO_MODS_MASK),
+)
+BLOCK_PARAM_MODIFY = Action(
+ label='_Properties',
+ tooltip='Modify params for the selected block',
+ stock_id=gtk.STOCK_PROPERTIES,
+ keypresses=(gtk.keysyms.Return, NO_MODS_MASK),
+)
+BLOCK_ENABLE = Action(
+ label='E_nable',
+ tooltip='Enable the selected blocks',
+ stock_id=gtk.STOCK_CONNECT,
+ keypresses=(gtk.keysyms.e, NO_MODS_MASK),
+)
+BLOCK_DISABLE = Action(
+ label='D_isable',
+ tooltip='Disable the selected blocks',
+ stock_id=gtk.STOCK_DISCONNECT,
+ keypresses=(gtk.keysyms.d, NO_MODS_MASK),
+)
+BLOCK_CUT = Action(
+ label='Cu_t',
+ tooltip='Cut',
+ stock_id=gtk.STOCK_CUT,
+ keypresses=(gtk.keysyms.x, gtk.gdk.CONTROL_MASK),
+)
+BLOCK_COPY = Action(
+ label='_Copy',
+ tooltip='Copy',
+ stock_id=gtk.STOCK_COPY,
+ keypresses=(gtk.keysyms.c, gtk.gdk.CONTROL_MASK),
+)
+BLOCK_PASTE = Action(
+ label='_Paste',
+ tooltip='Paste',
+ stock_id=gtk.STOCK_PASTE,
+ keypresses=(gtk.keysyms.v, gtk.gdk.CONTROL_MASK),
+)
+ERRORS_WINDOW_DISPLAY = Action(
+ label='_Errors',
+ tooltip='View flow graph errors',
+ stock_id=gtk.STOCK_DIALOG_ERROR,
+)
+ABOUT_WINDOW_DISPLAY = Action(
+ label='_About',
+ tooltip='About this program',
+ stock_id=gtk.STOCK_ABOUT,
+)
+HELP_WINDOW_DISPLAY = Action(
+ label='_Help',
+ tooltip='Usage tips',
+ stock_id=gtk.STOCK_HELP,
+ keypresses=(gtk.keysyms.F1, NO_MODS_MASK),
+)
+TYPES_WINDOW_DISPLAY = Action(
+ label='_Types',
+ tooltip='Types color mapping',
+ stock_id=gtk.STOCK_DIALOG_INFO,
+)
+FLOW_GRAPH_GEN = Action(
+ label='_Generate',
+ tooltip='Generate the flow graph',
+ stock_id=gtk.STOCK_CONVERT,
+ keypresses=(gtk.keysyms.F5, NO_MODS_MASK),
+)
+FLOW_GRAPH_EXEC = Action(
+ label='_Execute',
+ tooltip='Execute the flow graph',
+ stock_id=gtk.STOCK_EXECUTE,
+ keypresses=(gtk.keysyms.F6, NO_MODS_MASK),
+)
+FLOW_GRAPH_KILL = Action(
+ label='_Kill',
+ tooltip='Kill the flow graph',
+ stock_id=gtk.STOCK_STOP,
+ keypresses=(gtk.keysyms.F7, NO_MODS_MASK),
+)
+FLOW_GRAPH_SCREEN_CAPTURE = Action(
+ label='S_creen Capture',
+ tooltip='Create a screen capture of the flow graph',
+ stock_id=gtk.STOCK_PRINT,
+ keypresses=(gtk.keysyms.Print, NO_MODS_MASK),
+)
+PORT_CONTROLLER_DEC = Action(
+ keypresses=(gtk.keysyms.minus, NO_MODS_MASK, gtk.keysyms.KP_Subtract, NO_MODS_MASK),
+)
+PORT_CONTROLLER_INC = Action(
+ keypresses=(gtk.keysyms.plus, NO_MODS_MASK, gtk.keysyms.KP_Add, NO_MODS_MASK),
+)
+BLOCK_INC_TYPE = Action(
+ keypresses=(gtk.keysyms.Down, NO_MODS_MASK),
+)
+BLOCK_DEC_TYPE = Action(
+ keypresses=(gtk.keysyms.Up, NO_MODS_MASK),
+)
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index e0c547eba..8fd167869 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -39,6 +39,7 @@ TOOLBAR_LIST = (
Actions.FLOW_GRAPH_UNDO,
Actions.FLOW_GRAPH_REDO,
None,
+ Actions.ERRORS_WINDOW_DISPLAY,
Actions.FLOW_GRAPH_GEN,
Actions.FLOW_GRAPH_EXEC,
Actions.FLOW_GRAPH_KILL,
@@ -81,6 +82,9 @@ MENU_BAR_LIST = (
None,
Actions.BLOCK_PARAM_MODIFY,
]),
+ (gtk.Action('View', '_View', None, None), [
+ Actions.ERRORS_WINDOW_DISPLAY,
+ ]),
(gtk.Action('Build', '_Build', None, None), [
Actions.FLOW_GRAPH_GEN,
Actions.FLOW_GRAPH_EXEC,
@@ -88,7 +92,7 @@ MENU_BAR_LIST = (
]),
(gtk.Action('Help', '_Help', None, None), [
Actions.HELP_WINDOW_DISPLAY,
- Actions.COLORS_WINDOW_DISPLAY,
+ Actions.TYPES_WINDOW_DISPLAY,
None,
Actions.ABOUT_WINDOW_DISPLAY,
]),
@@ -104,9 +108,8 @@ class Toolbar(gtk.Toolbar):
"""
gtk.Toolbar.__init__(self)
self.set_style(gtk.TOOLBAR_ICONS)
- for action_name in TOOLBAR_LIST:
- if action_name: #add a tool item
- action = Actions.get_action_from_name(action_name)
+ for action in TOOLBAR_LIST:
+ if action: #add a tool item
self.add(action.create_tool_item())
#this reset of the tooltip property is required (after creating the tool item) for the tooltip to show
action.set_property('tooltip', action.get_property('tooltip'))
@@ -123,16 +126,15 @@ class MenuBar(gtk.MenuBar):
Add the submenu to the menu bar.
"""
gtk.MenuBar.__init__(self)
- for main_action,action_names in MENU_BAR_LIST:
+ for main_action, actions in MENU_BAR_LIST:
#create the main menu item
main_menu_item = main_action.create_menu_item()
self.append(main_menu_item)
#create the menu
main_menu = gtk.Menu()
main_menu_item.set_submenu(main_menu)
- for action_name in action_names:
- if action_name: #append a menu item
- action = Actions.get_action_from_name(action_name)
+ for action in actions:
+ if action: #append a menu item
main_menu.append(action.create_menu_item())
else: main_menu.append(gtk.SeparatorMenuItem())
main_menu.show_all() #this show all is required for the separators to show
diff --git a/grc/gui/Block.py b/grc/gui/Block.py
index 4add3aa19..8c65bf06f 100644
--- a/grc/gui/Block.py
+++ b/grc/gui/Block.py
@@ -37,15 +37,15 @@ BLOCK_MARKUP_TMPL="""\
class Block(Element):
"""The graphical signal block."""
- def __init__(self, *args, **kwargs):
+ def __init__(self):
"""
Block contructor.
Add graphics related params to the block.
"""
#add the position param
self.get_params().append(self.get_parent().get_parent().Param(
- self,
- odict({
+ block=self,
+ n=odict({
'name': 'GUI Coordinate',
'key': '_coordinate',
'type': 'raw',
@@ -54,8 +54,8 @@ class Block(Element):
})
))
self.get_params().append(self.get_parent().get_parent().Param(
- self,
- odict({
+ block=self,
+ n=odict({
'name': 'GUI Rotation',
'key': '_rotation',
'type': 'raw',
@@ -113,23 +113,16 @@ class Block(Element):
"""
self.get_param('_rotation').set_value(str(rot))
- def update(self):
+ def create_shapes(self):
"""Update the block, parameters, and ports when a change occurs."""
- self._bg_color = self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR
- self.clear()
- self._create_labels()
- self.W = self.label_width + 2*BLOCK_LABEL_PADDING
- self.H = max(*(
- [self.label_height+2*BLOCK_LABEL_PADDING] + [2*PORT_BORDER_SEPARATION + \
- sum([port.H + PORT_SEPARATION for port in ports]) - PORT_SEPARATION
- for ports in (self.get_sources(), self.get_sinks())]
- ))
+ Element.create_shapes(self)
if self.is_horizontal(): self.add_area((0, 0), (self.W, self.H))
elif self.is_vertical(): self.add_area((0, 0), (self.H, self.W))
- map(lambda p: p.update(), self.get_ports())
- def _create_labels(self):
+ def create_labels(self):
"""Create the labels for the signal block."""
+ Element.create_labels(self)
+ self._bg_color = self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR
layouts = list()
#create the main layout
layout = gtk.DrawingArea().create_pango_layout('')
@@ -142,7 +135,7 @@ class Block(Element):
layouts.append(layout)
w,h = layout.get_pixel_size()
self.label_width = max(w, self.label_width)
- self.label_height = self.label_height + h + LABEL_SEPARATION
+ self.label_height += h + LABEL_SEPARATION
width = self.label_width
height = self.label_height
#setup the pixmap
@@ -164,7 +157,13 @@ class Block(Element):
self.vertical_label = vimage = gtk.gdk.Image(gtk.gdk.IMAGE_NORMAL, pixmap.get_visual(), height, width)
for i in range(width):
for j in range(height): vimage.put_pixel(j, width-i-1, image.get_pixel(i, j))
- map(lambda p: p._create_labels(), self.get_ports())
+ #calculate width and height needed
+ self.W = self.label_width + 2*BLOCK_LABEL_PADDING
+ self.H = max(*(
+ [self.label_height+2*BLOCK_LABEL_PADDING] + [2*PORT_BORDER_SEPARATION + \
+ sum([port.H + PORT_SEPARATION for port in ports]) - PORT_SEPARATION
+ for ports in (self.get_sources(), self.get_sinks())]
+ ))
def draw(self, gc, window):
"""
diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py
index 379c4a6a2..07b8ea7e0 100644
--- a/grc/gui/BlockTreeWindow.py
+++ b/grc/gui/BlockTreeWindow.py
@@ -28,6 +28,15 @@ NAME_INDEX = 0
KEY_INDEX = 1
DOC_INDEX = 2
+DOC_MARKUP_TMPL="""\
+#if $doc
+$encode($doc)#slurp
+#else
+undocumented#slurp
+#end if"""
+
+CAT_MARKUP_TMPL="""Category: $cat"""
+
class BlockTreeWindow(gtk.VBox):
"""The block selection panel."""
@@ -48,7 +57,7 @@ class BlockTreeWindow(gtk.VBox):
self.treeview = gtk.TreeView(self.treestore)
self.treeview.set_enable_search(False) #disable pop up search box
self.treeview.add_events(gtk.gdk.BUTTON_PRESS_MASK)
- self.treeview.connect('button_press_event', self._handle_mouse_button_press)
+ self.treeview.connect('button-press-event', self._handle_mouse_button_press)
selection = self.treeview.get_selection()
selection.set_mode('single')
selection.connect('changed', self._handle_selection_change)
@@ -97,14 +106,14 @@ class BlockTreeWindow(gtk.VBox):
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.treestore.set_value(iter, DOC_INDEX, Utils.xml_encode('Category: %s'%cat_name))
+ self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name))
self._categories[sub_category] = iter
#add block
if block is None: return
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())
- self.treestore.set_value(iter, DOC_INDEX, Utils.xml_encode(block.get_doc() or 'undocumented'))
+ self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc()))
############################################################
## Helper Methods
diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py
index 013bcb00f..fabf34ee7 100644
--- a/grc/gui/Connection.py
+++ b/grc/gui/Connection.py
@@ -1,5 +1,5 @@
"""
-Copyright 2007, 2008 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
@@ -32,6 +32,8 @@ class Connection(Element):
The arrow coloring exposes the enabled and valid states.
"""
+ def __init__(self): Element.__init__(self)
+
def get_coordinate(self):
"""
Get the 0,0 coordinate.
@@ -48,8 +50,9 @@ class Connection(Element):
"""
return 0
- def update(self):
+ def create_shapes(self):
"""Precalculate relative coordinates."""
+ Element.create_shapes(self)
self._sink_rot = None
self._source_rot = None
self._sink_coor = None
@@ -72,7 +75,7 @@ class Connection(Element):
def _update_after_move(self):
"""Calculate coordinates."""
- self.clear()
+ self.clear() #FIXME do i want this here?
#source connector
source = self.get_source()
X, Y = source.get_connector_coordinate()
@@ -123,7 +126,7 @@ class Connection(Element):
sink = self.get_sink()
source = self.get_source()
#check for changes
- if self._sink_rot != sink.get_rotation() or self._source_rot != source.get_rotation(): self.update()
+ if self._sink_rot != sink.get_rotation() or self._source_rot != source.get_rotation(): self.create_shapes()
elif self._sink_coor != sink.get_coordinate() or self._source_coor != source.get_coordinate(): self._update_after_move()
#cache values
self._sink_rot = sink.get_rotation()
diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py
index 8d764e28e..af40f47c0 100644
--- a/grc/gui/Dialogs.py
+++ b/grc/gui/Dialogs.py
@@ -57,6 +57,20 @@ def MessageDialogHelper(type, buttons, title=None, markup=None):
message_dialog.destroy()
return response
+
+ERRORS_MARKUP_TMPL="""\
+#for $i, $err_msg in enumerate($errors)
+<b>Error $i:</b>
+$encode($err_msg.replace('\t', ' '))
+
+#end for"""
+def ErrorsDialog(flowgraph): MessageDialogHelper(
+ type=gtk.MESSAGE_ERROR,
+ buttons=gtk.BUTTONS_CLOSE,
+ title='Flow Graph Errors',
+ markup=Utils.parse_template(ERRORS_MARKUP_TMPL, errors=flowgraph.get_error_messages()),
+)
+
class AboutDialog(gtk.AboutDialog):
"""A cute little about dialog."""
@@ -98,8 +112,8 @@ COLORS_DIALOG_MARKUP_TMPL = """\
#end if
"""
-def ColorsDialog(platform): MessageDialogHelper(
+def TypesDialog(platform): MessageDialogHelper(
type=gtk.MESSAGE_INFO,
buttons=gtk.BUTTONS_CLOSE,
- title='Colors',
+ title='Types',
markup=Utils.parse_template(COLORS_DIALOG_MARKUP_TMPL, colors=platform.get_colors()))
diff --git a/grc/gui/Element.py b/grc/gui/Element.py
index 315191723..f0518ee12 100644
--- a/grc/gui/Element.py
+++ b/grc/gui/Element.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,11 +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 Colors
import pygtk
pygtk.require('2.0')
import gtk
-import pango
from Constants import LINE_SELECT_SENSITIVITY
from Constants import POSSIBLE_ROTATIONS
@@ -32,7 +30,7 @@ class Element(object):
and methods to detect selection of those areas.
"""
- def __init__(self, *args, **kwargs):
+ def __init__(self):
"""
Make a new list of rectangular areas and lines, and set the coordinate and the rotation.
"""
@@ -61,6 +59,21 @@ class Element(object):
rotation = rotation or self.get_rotation()
return rotation in (90, 270)
+ def create_labels(self):
+ """
+ Create labels (if applicable) and call on all children.
+ Call this base method before creating labels in the element.
+ """
+ for child in self.get_children(): child.create_labels()
+
+ def create_shapes(self):
+ """
+ Create shapes (if applicable) and call on all children.
+ Call this base method before creating shapes in the element.
+ """
+ self.clear()
+ for child in self.get_children(): child.create_shapes()
+
def draw(self, gc, window, border_color, bg_color):
"""
Draw in the given window.
@@ -70,14 +83,14 @@ class Element(object):
@param bg_color the color for the inside of the rectangle
"""
X,Y = self.get_coordinate()
- for (rX,rY),(W,H) in self.areas_dict[self.get_rotation()]:
+ for (rX,rY),(W,H) in self._areas_list:
aX = X + rX
aY = Y + rY
gc.set_foreground(bg_color)
window.draw_rectangle(gc, True, aX, aY, W, H)
gc.set_foreground(border_color)
window.draw_rectangle(gc, False, aX, aY, W, H)
- for (x1, y1),(x2, y2) in self.lines_dict[self.get_rotation()]:
+ for (x1, y1),(x2, y2) in self._lines_list:
gc.set_foreground(border_color)
window.draw_line(gc, X+x1, Y+y1, X+x2, Y+y2)
@@ -90,8 +103,8 @@ class Element(object):
def clear(self):
"""Empty the lines and areas."""
- self.areas_dict = dict((rotation, list()) for rotation in POSSIBLE_ROTATIONS)
- self.lines_dict = dict((rotation, list()) for rotation in POSSIBLE_ROTATIONS)
+ self._areas_list = list()
+ self._lines_list = list()
def set_coordinate(self, coor):
"""
@@ -136,7 +149,7 @@ class Element(object):
X, Y = self.get_coordinate()
self.set_coordinate((X+deltaX, Y+deltaY))
- def add_area(self, rel_coor, area, rotation=None):
+ def add_area(self, rel_coor, area):
"""
Add an area to the area list.
An area is actually a coordinate relative to the main coordinate
@@ -144,25 +157,21 @@ class Element(object):
A positive width is to the right of the coordinate.
A positive height is above the coordinate.
The area is associated with a rotation.
- If rotation is not specified, the element's current rotation is used.
@param rel_coor (x,y) offset from this element's coordinate
@param area (width,height) tuple
- @param rotation rotation in degrees
"""
- self.areas_dict[rotation or self.get_rotation()].append((rel_coor, area))
+ self._areas_list.append((rel_coor, area))
- def add_line(self, rel_coor1, rel_coor2, rotation=None):
+ def add_line(self, rel_coor1, rel_coor2):
"""
Add a line to the line list.
A line is defined by 2 relative coordinates.
Lines must be horizontal or vertical.
The line is associated with a rotation.
- If rotation is not specified, the element's current rotation is used.
@param rel_coor1 relative (x1,y1) tuple
@param rel_coor2 relative (x2,y2) tuple
- @param rotation rotation in degrees
"""
- self.lines_dict[rotation or self.get_rotation()].append((rel_coor1, rel_coor2))
+ self._lines_list.append((rel_coor1, rel_coor2))
def what_is_selected(self, coor, coor_m=None):
"""
@@ -183,24 +192,24 @@ class Element(object):
if coor_m:
x_m, y_m = [a-b for a,b in zip(coor_m, self.get_coordinate())]
#handle rectangular areas
- for (x1,y1), (w,h) in self.areas_dict[self.get_rotation()]:
+ for (x1,y1), (w,h) in self._areas_list:
if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \
in_between(x1+w, x, x_m) and in_between(y1, y, y_m) or \
in_between(x1, x, x_m) and in_between(y1+h, y, y_m) or \
in_between(x1+w, x, x_m) and in_between(y1+h, y, y_m):
return self
#handle horizontal or vertical lines
- for (x1, y1), (x2, y2) in self.lines_dict[self.get_rotation()]:
+ for (x1, y1), (x2, y2) in self._lines_list:
if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \
in_between(x2, x, x_m) and in_between(y2, y, y_m):
return self
return None
else:
#handle rectangular areas
- for (x1,y1), (w,h) in self.areas_dict[self.get_rotation()]:
+ for (x1,y1), (w,h) in self._areas_list:
if in_between(x, x1, x1+w) and in_between(y, y1, y1+h): return self
#handle horizontal or vertical lines
- for (x1, y1), (x2, y2) in self.lines_dict[self.get_rotation()]:
+ for (x1, y1), (x2, y2) in self._lines_list:
if x1 == x2: x1, x2 = x1-LINE_SELECT_SENSITIVITY, x2+LINE_SELECT_SENSITIVITY
if y1 == y2: y1, y2 = y1-LINE_SELECT_SENSITIVITY, y2+LINE_SELECT_SENSITIVITY
if in_between(x, x1, x2) and in_between(y, y1, y2): return self
@@ -220,7 +229,3 @@ class Element(object):
if rotation not in POSSIBLE_ROTATIONS:
raise Exception('"%s" is not one of the possible rotations: (%s)'%(rotation, POSSIBLE_ROTATIONS))
self.rotation = rotation
-
- def update(self):
- """Do nothing for the update. Dummy method."""
- pass
diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py
index f8028f199..8feb171f1 100644
--- a/grc/gui/FlowGraph.py
+++ b/grc/gui/FlowGraph.py
@@ -18,10 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
from Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE
-from Actions import \
- ELEMENT_CREATE, ELEMENT_SELECT, \
- BLOCK_PARAM_MODIFY, BLOCK_MOVE, \
- ELEMENT_DELETE
+import Actions
import Colors
import Utils
from Element import Element
@@ -39,7 +36,7 @@ class FlowGraph(Element):
and the connections between inputs and outputs.
"""
- def __init__(self, *args, **kwargs):
+ def __init__(self):
"""
FlowGraph contructor.
Create a list for signal blocks and connections. Connect mouse handlers.
@@ -86,7 +83,7 @@ class FlowGraph(Element):
block.set_coordinate(coor)
block.set_rotation(0)
block.get_param('id').set_value(id)
- self.handle_states(ELEMENT_CREATE)
+ Actions.ELEMENT_CREATE()
###########################################################################
# Copy Paste
@@ -291,10 +288,13 @@ class FlowGraph(Element):
def update(self):
"""
- Call update on all elements.
+ Call the top level rewrite and validate.
+ Call the top level create labels and shapes.
"""
+ self.rewrite()
self.validate()
- for element in self.get_elements(): element.update()
+ self.create_labels()
+ self.create_shapes()
##########################################################################
## Get Selected
@@ -406,7 +406,7 @@ class FlowGraph(Element):
self._old_selected_port is not self._new_selected_port:
try:
self.connect(self._old_selected_port, self._new_selected_port)
- self.handle_states(ELEMENT_CREATE)
+ Actions.ELEMENT_CREATE()
except: Messages.send_fail_connection()
self._old_selected_port = None
self._new_selected_port = None
@@ -421,7 +421,7 @@ class FlowGraph(Element):
self._selected_elements = list(
set.union(old_elements, new_elements) - set.intersection(old_elements, new_elements)
)
- self.handle_states(ELEMENT_SELECT)
+ Actions.ELEMENT_SELECT()
##########################################################################
## Event Handlers
@@ -443,7 +443,7 @@ class FlowGraph(Element):
#double click detected, bring up params dialog if possible
if double_click and self.get_selected_block():
self.mouse_pressed = False
- self.handle_states(BLOCK_PARAM_MODIFY)
+ Actions.BLOCK_PARAM_MODIFY()
def handle_mouse_button_release(self, left_click, coordinate):
"""
@@ -454,7 +454,7 @@ class FlowGraph(Element):
self.time = 0
self.mouse_pressed = False
if self.element_moved:
- self.handle_states(BLOCK_MOVE)
+ Actions.BLOCK_MOVE()
self.element_moved = False
self.update_selected_elements()
@@ -484,7 +484,7 @@ class FlowGraph(Element):
adj.emit('changed')
#remove the connection if selected in drag event
if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection():
- self.handle_states(ELEMENT_DELETE)
+ Actions.ELEMENT_DELETE()
#move the selected elements and record the new coordinate
X, Y = self.get_coordinate()
if not self.get_ctrl_mask(): self.move_selected((int(x - X), int(y - Y)))
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index 6d36f4cf7..9fcbe2a6c 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.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
@@ -19,9 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
from Constants import \
NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH
-from Actions import \
- APPLICATION_QUIT, FLOW_GRAPH_KILL, \
- FLOW_GRAPH_SAVE, get_accel_group
+import Actions
import pygtk
pygtk.require('2.0')
import gtk
@@ -67,20 +65,19 @@ PAGE_TITLE_MARKUP_TMPL = """\
class MainWindow(gtk.Window):
"""The topmost window with menus, the tool bar, and other major windows."""
- def __init__(self, handle_states, platform):
+ def __init__(self, platform):
"""
- MainWindow contructor.
- @param handle_states the callback function
+ MainWindow contructor
+ Setup the menu, toolbar, flowgraph editor notebook, block selection window...
"""
self._platform = platform
#setup window
- self.handle_states = handle_states
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
vbox = gtk.VBox()
self.hpaned = gtk.HPaned()
self.add(vbox)
#create the menu bar and toolbar
- self.add_accel_group(get_accel_group())
+ self.add_accel_group(Actions.get_accel_group())
vbox.pack_start(Bars.MenuBar(), False)
vbox.pack_start(Bars.Toolbar(), False)
vbox.pack_start(self.hpaned)
@@ -123,7 +120,7 @@ class MainWindow(gtk.Window):
This method in turns calls the state handler to quit.
@return true
"""
- self.handle_states(APPLICATION_QUIT)
+ Actions.APPLICATION_QUIT()
return True
def _handle_page_change(self, notebook, page, page_num):
@@ -137,7 +134,7 @@ class MainWindow(gtk.Window):
"""
self.current_page = self.notebook.get_nth_page(page_num)
Messages.send_page_switch(self.current_page.get_file_path())
- self.handle_states()
+ Actions.PAGE_CHANGE()
############################################################
# Report Window
@@ -223,12 +220,12 @@ class MainWindow(gtk.Window):
self._set_page(self.page_to_be_closed)
#unsaved? ask the user
if not self.page_to_be_closed.get_saved() and self._save_changes():
- self.handle_states(FLOW_GRAPH_SAVE) #try to save
+ Actions.FLOW_GRAPH_SAVE() #try to save
if not self.page_to_be_closed.get_saved(): #still unsaved?
self.page_to_be_closed = None #set the page to be closed back to None
return
#stop the flow graph if executing
- if self.page_to_be_closed.get_pid(): self.handle_states(FLOW_GRAPH_KILL)
+ if self.page_to_be_closed.get_pid(): Actions.FLOW_GRAPH_KILL()
#remove the page
self.notebook.remove_page(self.notebook.page_num(self.page_to_be_closed))
if ensure and self.notebook.get_n_pages() == 0: self.new_page() #no pages, make a new one
diff --git a/grc/gui/Makefile.am b/grc/gui/Makefile.am
index cb45d5359..b14817d04 100644
--- a/grc/gui/Makefile.am
+++ b/grc/gui/Makefile.am
@@ -43,7 +43,7 @@ ourpython_PYTHON = \
MainWindow.py \
Messages.py \
NotebookPage.py \
- ParamsDialog.py \
+ PropsDialog.py \
Preferences.py \
StateCache.py \
__init__.py
diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py
index cb6b7ed30..fddfeaf5f 100644
--- a/grc/gui/NotebookPage.py
+++ b/grc/gui/NotebookPage.py
@@ -17,10 +17,10 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-from Actions import FLOW_GRAPH_CLOSE
import pygtk
pygtk.require('2.0')
import gtk
+import Actions
from StateCache import StateCache
from Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT
from DrawingArea import DrawingArea
@@ -80,9 +80,8 @@ class NotebookPage(gtk.HBox):
self.drawing_area = DrawingArea(self.get_flow_graph())
self.scrolled_window.add_with_viewport(self.get_drawing_area())
self.pack_start(self.scrolled_window)
- #inject drawing area and handle states into flow graph
+ #inject drawing area into flow graph
self.get_flow_graph().drawing_area = self.get_drawing_area()
- self.get_flow_graph().handle_states = main_window.handle_states
self.show_all()
def get_drawing_area(self): return self.drawing_area
@@ -104,7 +103,7 @@ class NotebookPage(gtk.HBox):
@param the button
"""
self.main_window.page_to_be_closed = self
- self.main_window.handle_states(FLOW_GRAPH_CLOSE)
+ Actions.FLOW_GRAPH_CLOSE()
def set_markup(self, markup):
"""
diff --git a/grc/gui/Param.py b/grc/gui/Param.py
index a11fd9065..4464a57ab 100644
--- a/grc/gui/Param.py
+++ b/grc/gui/Param.py
@@ -23,6 +23,106 @@ import pygtk
pygtk.require('2.0')
import gtk
+class InputParam(gtk.HBox):
+ """The base class for an input parameter inside the input parameters dialog."""
+
+ def __init__(self, param, callback=None):
+ gtk.HBox.__init__(self)
+ self.param = param
+ self._callback = callback
+ self.label = gtk.Label() #no label, markup is added by set_markup
+ self.label.set_size_request(150, -1)
+ self.pack_start(self.label, False)
+ self.set_markup = lambda m: self.label.set_markup(m)
+ self.tp = None
+ #connect events
+ self.connect('show', self._update_gui)
+ def set_color(self, color): pass
+
+ def _update_gui(self, *args):
+ """
+ Set the markup, color, tooltip, show/hide.
+ """
+ #set the markup
+ has_cb = \
+ hasattr(self.param.get_parent(), 'get_callbacks') and \
+ filter(lambda c: self.param.get_key() in c, self.param.get_parent()._callbacks)
+ self.set_markup(Utils.parse_template(PARAM_LABEL_MARKUP_TMPL, param=self.param, has_cb=has_cb))
+ #set the color
+ self.set_color(self.param.get_color())
+ #set the tooltip
+ if self.tp: self.tp.set_tip(
+ self.entry,
+ Utils.parse_template(TIP_MARKUP_TMPL, param=self.param).strip(),
+ )
+ #show/hide
+ if self.param.get_hide() == 'all': self.hide_all()
+ else: self.show_all()
+
+ def _handle_changed(self, *args):
+ """
+ Handle a gui change by setting the new param value,
+ calling the callback (if applicable), and updating.
+ """
+ #set the new value
+ self.param.set_value(self.get_text())
+ #call the callback
+ if self._callback: self._callback(*args)
+ else: self.param.validate()
+ #gui update
+ self._update_gui()
+
+class EntryParam(InputParam):
+ """Provide an entry box for strings and numbers."""
+
+ def __init__(self, *args, **kwargs):
+ InputParam.__init__(self, *args, **kwargs)
+ self.entry = input = gtk.Entry()
+ input.set_text(self.param.get_value())
+ input.connect('changed', self._handle_changed)
+ self.pack_start(input, True)
+ self.get_text = input.get_text
+ #tool tip
+ self.tp = gtk.Tooltips()
+ self.tp.set_tip(self.entry, '')
+ self.tp.enable()
+ def set_color(self, color): self.entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
+
+class EnumParam(InputParam):
+ """Provide an entry box for Enum types with a drop down menu."""
+
+ def __init__(self, *args, **kwargs):
+ InputParam.__init__(self, *args, **kwargs)
+ self._input = gtk.combo_box_new_text()
+ for option in self.param.get_options(): self._input.append_text(option.get_name())
+ self._input.set_active(self.param.get_option_keys().index(self.param.get_value()))
+ self._input.connect('changed', self._handle_changed)
+ self.pack_start(self._input, False)
+ def get_text(self): return self.param.get_option_keys()[self._input.get_active()]
+
+class EnumEntryParam(InputParam):
+ """Provide an entry box and drop down menu for Raw Enum types."""
+
+ def __init__(self, *args, **kwargs):
+ InputParam.__init__(self, *args, **kwargs)
+ self._input = gtk.combo_box_entry_new_text()
+ for option in self.param.get_options(): self._input.append_text(option.get_name())
+ try: self._input.set_active(self.param.get_option_keys().index(self.param.get_value()))
+ except:
+ self._input.set_active(-1)
+ self._input.get_child().set_text(self.param.get_value())
+ self._input.connect('changed', self._handle_changed)
+ self._input.get_child().connect('changed', self._handle_changed)
+ self.pack_start(self._input, False)
+ def get_text(self):
+ if self._input.get_active() == -1: return self._input.get_child().get_text()
+ return self.param.get_option_keys()[self._input.get_active()]
+ def set_color(self, color):
+ if self._input.get_active() == -1: #custom entry, use color
+ self._input.get_child().modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
+ else: #from enum, make white background
+ self._input.get_child().modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse('#ffffff'))
+
PARAM_MARKUP_TMPL="""\
#set $foreground = $param.is_valid() and 'black' or 'red'
<span foreground="$foreground" font_desc="Sans 7.5"><b>$encode($param.get_name()): </b>$encode(repr($param))</span>"""
@@ -49,49 +149,19 @@ Error:
class Param(Element):
"""The graphical parameter."""
- def update(self):
- """
- Called when an external change occurs.
- Update the graphical input by calling the change handler.
- """
- if hasattr(self, '_input'): self._handle_changed()
-
- def get_input_object(self, callback=None):
- """
- Get the graphical gtk object to represent this parameter.
- Create the input object with this data type and the handle changed method.
- @param callback a function of one argument(this param) to be called from the change handler
- @return gtk input object
- """
- self._callback = callback
- self._input = self.get_input_class()(self, self._handle_changed)
- if not self._callback: self.update()
- return self._input
+ def __init__(self): Element.__init__(self)
- def _handle_changed(self, widget=None):
+ def get_input(self, *args, **kwargs):
"""
- When the input changes, write the inputs to the data type.
- Finish by calling the exteral callback.
+ Get the graphical gtk class to represent this parameter.
+ An enum requires and combo parameter.
+ A non-enum with options gets a combined entry/combo parameter.
+ All others get a standard entry parameter.
+ @return gtk input class
"""
- self.set_value(self._input.get_text())
- self.validate()
- #is param is involved in a callback? #FIXME: messy
- has_cb = \
- hasattr(self.get_parent(), 'get_callbacks') and \
- filter(lambda c: self.get_key() in c, self.get_parent()._callbacks)
- self._input.set_markup(Utils.parse_template(PARAM_LABEL_MARKUP_TMPL, param=self, has_cb=has_cb))
- #hide/show
- if self.get_hide() == 'all': self._input.hide_all()
- else: self._input.show_all()
- #set the color
- self._input.set_color(self.get_color())
- #set the tooltip
- if self._input.tp: self._input.tp.set_tip(
- self._input.entry,
- Utils.parse_template(TIP_MARKUP_TMPL, param=self).strip(),
- )
- #execute the external callback
- if self._callback: self._callback(self)
+ if self.is_enum(): return EnumParam(self, *args, **kwargs)
+ if self.get_options(): return EnumEntryParam(self, *args, **kwargs)
+ return EntryParam(self, *args, **kwargs)
def get_layout(self):
"""
diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py
index a32b0209f..8bbfaca23 100644
--- a/grc/gui/Platform.py
+++ b/grc/gui/Platform.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,32 +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 FlowGraph import FlowGraph
-from Connection import Connection
-from Block import Block
-from Port import Port
-from Param import Param
+from Element import Element
-def conjoin_classes(name, c1, c2):
- exec("""
-class %s(c1, c2):
- def __init__(self, *args, **kwargs):
- c1.__init__(self, *args, **kwargs)
- c2.__init__(self, *args, **kwargs)
-"""%name, locals())
- return locals()[name]
-
-def Platform(platform):
- #combine with gui class
- for attr, value in (
- ('FlowGraph', FlowGraph),
- ('Connection', Connection),
- ('Block', Block),
- ('Source', Port),
- ('Sink', Port),
- ('Param', Param),
- ):
- old_value = getattr(platform, attr)
- c = conjoin_classes(attr, old_value, value)
- setattr(platform, attr, c)
- return platform
+class Platform(Element):
+ def __init__(self): Element.__init__(self)
diff --git a/grc/gui/Port.py b/grc/gui/Port.py
index d1f36f8b9..6763f6cbd 100644
--- a/grc/gui/Port.py
+++ b/grc/gui/Port.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
@@ -34,7 +34,7 @@ PORT_MARKUP_TMPL="""\
class Port(Element):
"""The graphical port."""
- def __init__(self, *args, **kwargs):
+ def __init__(self):
"""
Port contructor.
Create list of connector coordinates.
@@ -42,9 +42,9 @@ class Port(Element):
Element.__init__(self)
self.connector_coordinates = dict()
- def update(self):
+ def create_shapes(self):
"""Create new areas and labels for the port."""
- self.clear()
+ Element.create_shapes(self)
#get current rotation
rotation = self.get_rotation()
#get all sibling ports
@@ -82,8 +82,9 @@ class Port(Element):
#the connector length
self._connector_length = CONNECTOR_EXTENSION_MINIMAL + CONNECTOR_EXTENSION_INCREMENT*index
- def _create_labels(self):
+ def create_labels(self):
"""Create the labels for the socket."""
+ Element.create_labels(self)
self._bg_color = Colors.get_color(self.get_color())
#create the layout
layout = gtk.DrawingArea().create_pango_layout('')
@@ -114,7 +115,7 @@ class Port(Element):
border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or Colors.BORDER_COLOR,
)
X,Y = self.get_coordinate()
- (x,y),(w,h) = self.areas_dict[self.get_rotation()][0] #use the first area's sizes to place the labels
+ (x,y),(w,h) = self._areas_list[0] #use the first area's sizes to place the labels
if self.is_horizontal():
window.draw_image(gc, self.horizontal_label, 0, 0, x+X+(self.W-self.w)/2, y+Y+(self.H-self.h)/2, -1, -1)
elif self.is_vertical():
diff --git a/grc/gui/ParamsDialog.py b/grc/gui/PropsDialog.py
index ccf19d1a2..a7822b228 100644
--- a/grc/gui/ParamsDialog.py
+++ b/grc/gui/PropsDialog.py
@@ -37,70 +37,118 @@ def get_title_label(title):
hbox.pack_start(label, False, False, padding=11)
return hbox
-class ParamsDialog(gtk.Dialog):
- """A dialog box to set block parameters."""
+class PropsDialog(gtk.Dialog):
+ """
+ A dialog to set block parameters, view errors, and view documentation.
+ """
def __init__(self, block):
"""
- SignalBlockParamsDialog contructor.
- @param block the signal block
+ Properties dialog contructor.
+ @param block a block instance
"""
+ self._hash = 0
+ LABEL_SPACING = 7
gtk.Dialog.__init__(self,
title='Properties: %s'%block.get_name(),
- buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE),
+ buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT),
)
- self.block = block
+ self._block = block
self.set_size_request(MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT)
vbox = gtk.VBox()
- #Add the title label
- vbox.pack_start(get_title_label('Parameters'), False)
#Create the scrolled window to hold all the parameters
scrolled_window = gtk.ScrolledWindow()
scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolled_window.add_with_viewport(vbox)
self.vbox.pack_start(scrolled_window, True)
+ #Params box for block parameters
+ self._params_box = gtk.VBox()
+ self._params_box.pack_start(get_title_label('Parameters'), False)
+ self._input_object_params = list()
#Error Messages for the block
self._error_box = gtk.VBox()
self._error_messages_text_display = TextDisplay()
- self._error_box.pack_start(gtk.Label(), False, False, 7) #spacing
+ self._error_box.pack_start(gtk.Label(), False, False, LABEL_SPACING)
self._error_box.pack_start(get_title_label('Error Messages'), False)
self._error_box.pack_start(self._error_messages_text_display, False)
#Docs for the block
self._docs_box = err_box = gtk.VBox()
self._docs_text_display = TextDisplay()
- self._docs_box.pack_start(gtk.Label(), False, False, 7) #spacing
+ self._docs_box.pack_start(gtk.Label(), False, False, LABEL_SPACING)
self._docs_box.pack_start(get_title_label('Documentation'), False)
self._docs_box.pack_start(self._docs_text_display, False)
- #Add all the parameters
- for param in self.block.get_params():
- vbox.pack_start(param.get_input_object(self._handle_changed), False)
- #Add the error and docs box
+ #Add the boxes
+ vbox.pack_start(self._params_box, False)
vbox.pack_start(self._error_box, False)
vbox.pack_start(self._docs_box, False)
- #connect and show
- self.connect('key_press_event', self._handle_key_press)
+ #connect events
+ self.connect('key-press-event', self._handle_key_press)
+ self.connect('show', self._update_gui)
+ #show all (performs initial gui update)
self.show_all()
- #initial update
- for param in self.block.get_params(): param.update()
- self._update()
- def _update(self):
+ def _params_changed(self):
+ """
+ Have the params in this dialog changed?
+ Ex: Added, removed, type change, hide change...
+ To the props dialog, the hide setting of 'none' and 'part' are identical.
+ Therfore, the props dialog only cares if the hide setting is/not 'all'.
+ Make a hash that uniquely represents the params state.
+ @return true if changed
+ """
+ old_hash = self._hash
+ self._hash = 0
+ for param in self._block.get_params():
+ self._hash ^= hash(param)
+ self._hash ^= hash(param.get_type())
+ self._hash ^= hash(param.get_hide() == 'all')
+ return self._hash != old_hash
+
+ def _handle_changed(self, *args):
+ """
+ A change occured within a param:
+ Rewrite/validate the block and update the gui.
"""
+ #update for the block
+ self._block.rewrite()
+ self._block.validate()
+ self._update_gui()
+
+ def _update_gui(self, *args):
+ """
+ Repopulate the parameters box (if changed).
+ Update all the input parameters.
Update the error messages box.
Hide the box if there are no errors.
Update the documentation block.
Hide the box if there are no docs.
"""
- self.block.validate()
+ #update the params box
+ if self._params_changed():
+ #hide params box before changing
+ self._params_box.hide_all()
+ #empty the params box
+ for io_param in list(self._input_object_params):
+ self._params_box.remove(io_param)
+ self._input_object_params.remove(io_param)
+ io_param.destroy()
+ #repopulate the params box
+ for param in self._block.get_params():
+ if param.get_hide() == 'all': continue
+ io_param = param.get_input(self._handle_changed)
+ self._input_object_params.append(io_param)
+ self._params_box.pack_start(io_param, False)
+ #show params box with new params
+ self._params_box.show_all()
#update the errors box
- if self.block.is_valid(): self._error_box.hide()
+ if self._block.is_valid(): self._error_box.hide()
else: self._error_box.show()
- messages = '\n\n'.join(self.block.get_error_messages())
+ messages = '\n\n'.join(self._block.get_error_messages())
self._error_messages_text_display.set_text(messages)
#update the docs box
- if self.block.get_doc(): self._docs_box.show()
+ if self._block.get_doc(): self._docs_box.show()
else: self._docs_box.hide()
- self._docs_text_display.set_text(self.block.get_doc())
+ self._docs_text_display.set_text(self._block.get_doc())
def _handle_key_press(self, widget, event):
"""
@@ -108,38 +156,16 @@ class ParamsDialog(gtk.Dialog):
Call the ok response when enter is pressed.
@return false to forward the keypress
"""
- keyname = gtk.gdk.keyval_name(event.keyval)
- if keyname == 'Return': self.response(gtk.RESPONSE_OK)
+ if event.keyval == gtk.keysyms.Return:
+ self.response(gtk.RESPONSE_ACCEPT)
+ return True #handled here
return False #forward the keypress
- def _handle_changed(self, param):
- """
- A change occured, update any dependent parameters:
- The enum inside the variable type may have changed and,
- the variable param will need an external update.
- @param param the graphical parameter that initiated the callback
- """
- #update dependent params
- if param.is_enum():
- for other_param in param.get_parent().get_params():
- if param.get_key() is not other_param.get_key() and (
- param.get_key() in other_param._type or \
- param.get_key() in other_param._hide): other_param.update()
- #update
- self._update()
- return True
-
def run(self):
"""
- Call run().
- @return true if a change occured.
+ Run the dialog and get its response.
+ @return true if the response was accept
"""
- original_data = list()
- for param in self.block.get_params():
- original_data.append(param.get_value())
- gtk.Dialog.run(self)
+ response = gtk.Dialog.run(self)
self.destroy()
- new_data = list()
- for param in self.block.get_params():
- new_data.append(param.get_value())
- return original_data != new_data
+ return response == gtk.RESPONSE_ACCEPT
diff --git a/grc/gui/StateCache.py b/grc/gui/StateCache.py
index 04b18b18a..3f6b79224 100644
--- a/grc/gui/StateCache.py
+++ b/grc/gui/StateCache.py
@@ -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 Actions import FLOW_GRAPH_UNDO, FLOW_GRAPH_REDO, get_action_from_name
+import Actions
from Constants import STATE_CACHE_SIZE
class StateCache(object):
@@ -88,5 +88,5 @@ class StateCache(object):
"""
Update the undo and redo actions based on the number of next and prev states.
"""
- get_action_from_name(FLOW_GRAPH_REDO).set_sensitive(self.num_next_states != 0)
- get_action_from_name(FLOW_GRAPH_UNDO).set_sensitive(self.num_prev_states != 0)
+ Actions.FLOW_GRAPH_REDO.set_sensitive(self.num_next_states != 0)
+ Actions.FLOW_GRAPH_UNDO.set_sensitive(self.num_prev_states != 0)
diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py
index ee6dc6cdc..83036a4b8 100644
--- a/grc/gui/Utils.py
+++ b/grc/gui/Utils.py
@@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
from Constants import POSSIBLE_ROTATIONS
from Cheetah.Template import Template
+import gobject
def get_rotated_coordinate(coor, rotation):
"""
@@ -54,23 +55,6 @@ def get_angle_from_coordinates((x1,y1), (x2,y2)):
if y2 > y1: return 270
else: return 90
-def xml_encode(string):
- """
- Encode a string into an xml safe string by replacing special characters.
- Needed for gtk pango markup in labels.
- @param string the input string
- @return output string with safe characters
- """
- string = str(string)
- for char, safe in (
- ('&', '&amp;'),
- ('<', '&lt;'),
- ('>', '&gt;'),
- ('"', '&quot;'),
- ("'", '&apos;'),
- ): string = string.replace(char, safe)
- return string
-
def parse_template(tmpl_str, **kwargs):
"""
Parse the template string with the given args.
@@ -78,5 +62,5 @@ def parse_template(tmpl_str, **kwargs):
@param tmpl_str the template as a string
@return a string of the parsed template
"""
- kwargs['encode'] = xml_encode
+ kwargs['encode'] = gobject.markup_escape_text
return str(Template(tmpl_str, kwargs))