2 Copyright 2007, 2008, 2009 Free Software Foundation, Inc.
3 This file is part of GNU Radio
5 GNU Radio Companion is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 GNU Radio Companion is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 from Constants import IMAGE_FILE_EXTENSION
29 from threading import Thread
31 from .. base import ParseXML
32 from MainWindow import MainWindow
33 from PropsDialog import PropsDialog
35 from FileDialogs import OpenFlowGraphFileDialog, SaveFlowGraphFileDialog, SaveImageFileDialog
37 gobject.threads_init()
41 The action handler will setup all the major window components,
42 and handle button presses and flow graph operations from the GUI.
45 def __init__(self, file_paths, platform):
47 ActionHandler constructor.
48 Create the main window, setup the message handler, import the preferences,
49 and connect all of the action handlers. Finally, enter the gtk main loop and block.
50 @param file_paths a list of flow graph file passed from command line
51 @param platform platform module
54 for action in Actions.get_all_actions(): action.connect('activate', self._handle_action)
55 #setup the main window
56 self.main_window = MainWindow(platform)
57 self.main_window.connect('delete-event', self._quit)
58 self.main_window.connect('key-press-event', self._handle_key_press)
59 self.get_page = self.main_window.get_page
60 self.get_flow_graph = self.main_window.get_flow_graph
61 self.get_focus_flag = self.main_window.get_focus_flag
63 Messages.register_messenger(self.main_window.add_report_line)
64 Messages.send_init(platform)
66 self.init_file_paths = file_paths
67 Actions.APPLICATION_INITIALIZE()
71 def _handle_key_press(self, widget, event):
73 Handle key presses from the keyboard and translate key combinations into actions.
74 This key press handler is called prior to the gtk key press handler.
75 This handler bypasses built in accelerator key handling when in focus because
76 * some keys are ignored by the accelerators like the direction keys,
77 * some keys are not registered to any accelerators but are still used.
78 When not in focus, gtk and the accelerators handle the the key press.
79 @return false to let gtk handle the key action
81 try: assert self.get_focus_flag()
82 except AssertionError: return False
83 return Actions.handle_key_press(event)
85 def _quit(self, window, event):
87 Handle the delete event from the main window.
88 Generated by pressing X to close, alt+f4, or right click+close.
89 This method in turns calls the state handler to quit.
92 Actions.APPLICATION_QUIT()
95 def _handle_action(self, action):
97 ##################################################
99 ##################################################
100 if action == Actions.APPLICATION_INITIALIZE:
101 for action in Actions.get_all_actions(): action.set_sensitive(False) #set all actions disabled
102 #enable a select few actions
104 Actions.APPLICATION_QUIT, Actions.FLOW_GRAPH_NEW,
105 Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS,
106 Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY,
107 Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY,
108 Actions.TYPES_WINDOW_DISPLAY,
109 ): action.set_sensitive(True)
110 if not self.init_file_paths:
111 self.init_file_paths = Preferences.files_open()
112 if not self.init_file_paths: self.init_file_paths = ['']
113 for file_path in self.init_file_paths:
114 if file_path: self.main_window.new_page(file_path) #load pages from file paths
115 if Preferences.file_open() in self.init_file_paths:
116 self.main_window.new_page(Preferences.file_open(), show=True)
117 if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists
118 elif action == Actions.APPLICATION_QUIT:
119 if self.main_window.close_pages():
122 ##################################################
124 ##################################################
125 elif action == Actions.ELEMENT_SELECT:
126 pass #do nothing, update routines below
127 elif action == Actions.NOTHING_SELECT:
128 self.get_flow_graph().unselect()
129 ##################################################
131 ##################################################
132 elif action == Actions.BLOCK_ENABLE:
133 if self.get_flow_graph().enable_selected(True):
134 self.get_flow_graph().update()
135 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
136 self.get_page().set_saved(False)
137 elif action == Actions.BLOCK_DISABLE:
138 if self.get_flow_graph().enable_selected(False):
139 self.get_flow_graph().update()
140 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
141 self.get_page().set_saved(False)
142 ##################################################
144 ##################################################
145 elif action == Actions.BLOCK_CUT:
147 Actions.ELEMENT_DELETE()
148 elif action == Actions.BLOCK_COPY:
149 self.clipboard = self.get_flow_graph().copy_to_clipboard()
150 elif action == Actions.BLOCK_PASTE:
152 self.get_flow_graph().paste_from_clipboard(self.clipboard)
153 self.get_flow_graph().update()
154 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
155 self.get_page().set_saved(False)
156 ##################################################
157 # Move/Rotate/Delete/Create
158 ##################################################
159 elif action == Actions.BLOCK_MOVE:
160 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
161 self.get_page().set_saved(False)
162 elif action == Actions.BLOCK_ROTATE_CCW:
163 if self.get_flow_graph().rotate_selected(90):
164 self.get_flow_graph().update()
165 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
166 self.get_page().set_saved(False)
167 elif action == Actions.BLOCK_ROTATE_CW:
168 if self.get_flow_graph().rotate_selected(-90):
169 self.get_flow_graph().update()
170 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
171 self.get_page().set_saved(False)
172 elif action == Actions.ELEMENT_DELETE:
173 if self.get_flow_graph().remove_selected():
174 self.get_flow_graph().update()
175 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
176 Actions.NOTHING_SELECT()
177 self.get_page().set_saved(False)
178 elif action == Actions.ELEMENT_CREATE:
179 self.get_flow_graph().update()
180 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
181 Actions.NOTHING_SELECT()
182 self.get_page().set_saved(False)
183 elif action == Actions.BLOCK_INC_TYPE:
184 if self.get_flow_graph().type_controller_modify_selected(1):
185 self.get_flow_graph().update()
186 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
187 self.get_page().set_saved(False)
188 elif action == Actions.BLOCK_DEC_TYPE:
189 if self.get_flow_graph().type_controller_modify_selected(-1):
190 self.get_flow_graph().update()
191 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
192 self.get_page().set_saved(False)
193 elif action == Actions.PORT_CONTROLLER_INC:
194 if self.get_flow_graph().port_controller_modify_selected(1):
195 self.get_flow_graph().update()
196 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
197 self.get_page().set_saved(False)
198 elif action == Actions.PORT_CONTROLLER_DEC:
199 if self.get_flow_graph().port_controller_modify_selected(-1):
200 self.get_flow_graph().update()
201 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
202 self.get_page().set_saved(False)
203 ##################################################
205 ##################################################
206 elif action == Actions.ABOUT_WINDOW_DISPLAY:
207 Dialogs.AboutDialog(self.get_flow_graph().get_parent())
208 elif action == Actions.HELP_WINDOW_DISPLAY:
210 elif action == Actions.TYPES_WINDOW_DISPLAY:
211 Dialogs.TypesDialog(self.get_flow_graph().get_parent())
212 elif action == Actions.ERRORS_WINDOW_DISPLAY:
213 Dialogs.ErrorsDialog(self.get_flow_graph())
214 ##################################################
215 # Param Modifications
216 ##################################################
217 elif action == Actions.BLOCK_PARAM_MODIFY:
218 selected_block = self.get_flow_graph().get_selected_block()
220 if PropsDialog(selected_block).run():
222 self.get_flow_graph().update()
223 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
224 self.get_page().set_saved(False)
226 #restore the current state
227 n = self.get_page().get_state_cache().get_current_state()
228 self.get_flow_graph().import_data(n)
229 self.get_flow_graph().update()
230 ##################################################
232 ##################################################
233 elif action == Actions.FLOW_GRAPH_UNDO:
234 n = self.get_page().get_state_cache().get_prev_state()
236 self.get_flow_graph().unselect()
237 self.get_flow_graph().import_data(n)
238 self.get_flow_graph().update()
239 self.get_page().set_saved(False)
240 elif action == Actions.FLOW_GRAPH_REDO:
241 n = self.get_page().get_state_cache().get_next_state()
243 self.get_flow_graph().unselect()
244 self.get_flow_graph().import_data(n)
245 self.get_flow_graph().update()
246 self.get_page().set_saved(False)
247 ##################################################
248 # New/Open/Save/Close
249 ##################################################
250 elif action == Actions.FLOW_GRAPH_NEW:
251 self.main_window.new_page()
252 elif action == Actions.FLOW_GRAPH_OPEN:
253 file_paths = OpenFlowGraphFileDialog(self.get_page().get_file_path()).run()
254 if file_paths: #open a new page for each file, show only the first
255 for i,file_path in enumerate(file_paths):
256 self.main_window.new_page(file_path, show=(i==0))
257 elif action == Actions.FLOW_GRAPH_CLOSE:
258 self.main_window.close_page()
259 elif action == Actions.FLOW_GRAPH_SAVE:
260 #read-only or undefined file path, do save-as
261 if self.get_page().get_read_only() or not self.get_page().get_file_path():
262 Actions.FLOW_GRAPH_SAVE_AS()
263 #otherwise try to save
266 ParseXML.to_file(self.get_flow_graph().export_data(), self.get_page().get_file_path())
267 self.get_page().set_saved(True)
269 Messages.send_fail_save(self.get_page().get_file_path())
270 self.get_page().set_saved(False)
271 elif action == Actions.FLOW_GRAPH_SAVE_AS:
272 file_path = SaveFlowGraphFileDialog(self.get_page().get_file_path()).run()
273 if file_path is not None:
274 self.get_page().set_file_path(file_path)
275 Actions.FLOW_GRAPH_SAVE()
276 elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
277 file_path = SaveImageFileDialog(self.get_page().get_file_path()).run()
278 if file_path is not None:
279 pixbuf = self.get_flow_graph().get_drawing_area().get_pixbuf()
280 pixbuf.save(file_path, IMAGE_FILE_EXTENSION[1:])
281 ##################################################
283 ##################################################
284 elif action == Actions.FLOW_GRAPH_GEN:
285 if not self.get_page().get_pid():
286 if not self.get_page().get_saved() or not self.get_page().get_file_path():
287 Actions.FLOW_GRAPH_SAVE() #only save if file path missing or not saved
288 if self.get_page().get_saved() and self.get_page().get_file_path():
289 generator = self.get_page().get_generator()
291 Messages.send_start_gen(generator.get_file_path())
293 except Exception,e: Messages.send_fail_gen(e)
294 else: self.generator = None
295 elif action == Actions.FLOW_GRAPH_EXEC:
296 if not self.get_page().get_pid():
297 Actions.FLOW_GRAPH_GEN()
298 if self.get_page().get_saved() and self.get_page().get_file_path():
299 ExecFlowGraphThread(self)
300 elif action == Actions.FLOW_GRAPH_KILL:
301 if self.get_page().get_pid():
302 try: os.kill(self.get_page().get_pid(), signal.SIGKILL)
303 except: print "could not kill pid: %s"%self.get_page().get_pid()
304 elif action == Actions.PAGE_CHANGE: #pass and run the global actions
306 else: print '!!! Action "%s" not handled !!!'%action
307 ##################################################
308 # Global Actions for all States
309 ##################################################
310 #update general buttons
311 Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid())
312 Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements()))
313 Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(self.get_flow_graph().get_selected_block()))
314 Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
315 Actions.BLOCK_ROTATE_CW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
316 #update cut/copy/paste
317 Actions.BLOCK_CUT.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
318 Actions.BLOCK_COPY.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
319 Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard))
320 #update enable/disable
321 Actions.BLOCK_ENABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
322 Actions.BLOCK_DISABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
323 #set the exec and stop buttons
324 self.update_exec_stop()
326 Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved())
327 self.main_window.update()
328 try: #set the size of the flow graph area (if changed)
329 new_size = self.get_flow_graph().get_option('window_size')
330 if self.get_flow_graph().get_size() != tuple(new_size):
331 self.get_flow_graph().set_size(*new_size)
334 self.get_flow_graph().update_selected()
335 self.get_flow_graph().queue_draw()
336 return True #action was handled
338 def update_exec_stop(self):
340 Update the exec and stop buttons.
341 Lock and unlock the mutex for race conditions with exec flow graph threads.
343 sensitive = self.get_flow_graph().is_valid() and not self.get_page().get_pid()
344 Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive)
345 Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive)
346 Actions.FLOW_GRAPH_KILL.set_sensitive(self.get_page().get_pid() != None)
348 class ExecFlowGraphThread(Thread):
349 """Execute the flow graph as a new process and wait on it to finish."""
351 def __init__ (self, action_handler):
353 ExecFlowGraphThread constructor.
354 @param action_handler an instance of an ActionHandler
356 Thread.__init__(self)
357 self.update_exec_stop = action_handler.update_exec_stop
358 self.flow_graph = action_handler.get_flow_graph()
359 #store page and dont use main window calls in run
360 self.page = action_handler.get_page()
361 Messages.send_start_exec(self.page.get_generator().get_file_path())
364 self.p = self.page.get_generator().get_popen()
365 self.page.set_pid(self.p.pid)
367 self.update_exec_stop()
370 Messages.send_verbose_exec(str(e))
371 Messages.send_end_exec()
375 Wait on the executing process by reading from its stdout.
376 Use gobject.idle_add when calling functions that modify gtk objects.
381 gobject.idle_add(Messages.send_verbose_exec, r)
382 r = os.read(self.p.stdout.fileno(), 1024)
383 gobject.idle_add(self.done)
386 """Perform end of execution tasks."""
387 Messages.send_end_exec()
388 self.page.set_pid(None)
389 self.update_exec_stop()