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
33 from MainWindow import MainWindow
34 from PropsDialog import PropsDialog
36 from FileDialogs import OpenFlowGraphFileDialog, SaveFlowGraphFileDialog, SaveImageFileDialog
38 gobject.threads_init()
42 The action handler will setup all the major window components,
43 and handle button presses and flow graph operations from the GUI.
46 def __init__(self, file_paths, platform):
48 ActionHandler constructor.
49 Create the main window, setup the message handler, import the preferences,
50 and connect all of the action handlers. Finally, enter the gtk main loop and block.
51 @param file_paths a list of flow graph file passed from command line
52 @param platform platform module
55 for action in Actions.get_all_actions(): action.connect('activate', self._handle_action)
56 #setup the main window
57 self.main_window = MainWindow(platform)
58 self.main_window.connect('delete-event', self._quit)
59 self.main_window.connect('key-press-event', self._handle_key_press)
60 self.get_page = self.main_window.get_page
61 self.get_flow_graph = self.main_window.get_flow_graph
62 self.get_focus_flag = self.main_window.get_focus_flag
64 Messages.register_messenger(self.main_window.add_report_line)
65 Messages.send_init(platform)
67 self.init_file_paths = file_paths
68 Actions.APPLICATION_INITIALIZE()
72 def _handle_key_press(self, widget, event):
74 Handle key presses from the keyboard and translate key combinations into actions.
75 This key press handler is called prior to the gtk key press handler.
76 This handler bypasses built in accelerator key handling when in focus because
77 * some keys are ignored by the accelerators like the direction keys,
78 * some keys are not registered to any accelerators but are still used.
79 When not in focus, gtk and the accelerators handle the the key press.
80 @return false to let gtk handle the key action
82 try: assert self.get_focus_flag()
83 except AssertionError: return False
84 try: Actions.get_action_from_key_press(event)()
85 except KeyError: return False
86 return True #handled by this method
88 def _quit(self, window, event):
90 Handle the delete event from the main window.
91 Generated by pressing X to close, alt+f4, or right click+close.
92 This method in turns calls the state handler to quit.
95 Actions.APPLICATION_QUIT()
98 def _handle_action(self, action):
100 ##################################################
102 ##################################################
103 if action == Actions.APPLICATION_INITIALIZE:
104 for action in Actions.get_all_actions(): action.set_sensitive(False) #set all actions disabled
105 #enable a select few actions
107 Actions.APPLICATION_QUIT, Actions.FLOW_GRAPH_NEW,
108 Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS,
109 Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY,
110 Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY,
111 Actions.TYPES_WINDOW_DISPLAY,
112 ): action.set_sensitive(True)
113 if not self.init_file_paths:
114 self.init_file_paths = Preferences.files_open()
115 if not self.init_file_paths: self.init_file_paths = ['']
116 for file_path in self.init_file_paths:
117 if file_path: self.main_window.new_page(file_path) #load pages from file paths
118 if Preferences.file_open() in self.init_file_paths:
119 self.main_window.new_page(Preferences.file_open(), show=True)
120 if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists
121 elif action == Actions.APPLICATION_QUIT:
122 if self.main_window.close_pages():
125 ##################################################
127 ##################################################
128 elif action == Actions.ELEMENT_SELECT:
129 pass #do nothing, update routines below
130 elif action == Actions.NOTHING_SELECT:
131 self.get_flow_graph().unselect()
132 ##################################################
134 ##################################################
135 elif action == Actions.BLOCK_ENABLE:
136 if self.get_flow_graph().enable_selected(True):
137 self.get_flow_graph().update()
138 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
139 self.get_page().set_saved(False)
140 elif action == Actions.BLOCK_DISABLE:
141 if self.get_flow_graph().enable_selected(False):
142 self.get_flow_graph().update()
143 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
144 self.get_page().set_saved(False)
145 ##################################################
147 ##################################################
148 elif action == Actions.BLOCK_CUT:
150 Actions.ELEMENT_DELETE()
151 elif action == Actions.BLOCK_COPY:
152 self.clipboard = self.get_flow_graph().copy_to_clipboard()
153 elif action == Actions.BLOCK_PASTE:
155 self.get_flow_graph().paste_from_clipboard(self.clipboard)
156 self.get_flow_graph().update()
157 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
158 self.get_page().set_saved(False)
159 ##################################################
160 # Move/Rotate/Delete/Create
161 ##################################################
162 elif action == Actions.BLOCK_MOVE:
163 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
164 self.get_page().set_saved(False)
165 elif action == Actions.BLOCK_ROTATE_CCW:
166 if self.get_flow_graph().rotate_selected(90):
167 self.get_flow_graph().update()
168 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
169 self.get_page().set_saved(False)
170 elif action == Actions.BLOCK_ROTATE_CW:
171 if self.get_flow_graph().rotate_selected(-90):
172 self.get_flow_graph().update()
173 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
174 self.get_page().set_saved(False)
175 elif action == Actions.ELEMENT_DELETE:
176 if self.get_flow_graph().remove_selected():
177 self.get_flow_graph().update()
178 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
179 Actions.NOTHING_SELECT()
180 self.get_page().set_saved(False)
181 elif action == Actions.ELEMENT_CREATE:
182 self.get_flow_graph().update()
183 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
184 Actions.NOTHING_SELECT()
185 self.get_page().set_saved(False)
186 elif action == Actions.BLOCK_INC_TYPE:
187 if self.get_flow_graph().type_controller_modify_selected(1):
188 self.get_flow_graph().update()
189 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
190 self.get_page().set_saved(False)
191 elif action == Actions.BLOCK_DEC_TYPE:
192 if self.get_flow_graph().type_controller_modify_selected(-1):
193 self.get_flow_graph().update()
194 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
195 self.get_page().set_saved(False)
196 elif action == Actions.PORT_CONTROLLER_INC:
197 if self.get_flow_graph().port_controller_modify_selected(1):
198 self.get_flow_graph().update()
199 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
200 self.get_page().set_saved(False)
201 elif action == Actions.PORT_CONTROLLER_DEC:
202 if self.get_flow_graph().port_controller_modify_selected(-1):
203 self.get_flow_graph().update()
204 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
205 self.get_page().set_saved(False)
206 ##################################################
208 ##################################################
209 elif action == Actions.ABOUT_WINDOW_DISPLAY:
210 Dialogs.AboutDialog(self.get_flow_graph().get_parent())
211 elif action == Actions.HELP_WINDOW_DISPLAY:
213 elif action == Actions.TYPES_WINDOW_DISPLAY:
214 Dialogs.TypesDialog(self.get_flow_graph().get_parent())
215 ##################################################
216 # Param Modifications
217 ##################################################
218 elif action == Actions.BLOCK_PARAM_MODIFY:
219 selected_block = self.get_flow_graph().get_selected_block()
221 if PropsDialog(selected_block).run():
223 self.get_flow_graph().update()
224 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
225 self.get_page().set_saved(False)
227 #restore the current state
228 n = self.get_page().get_state_cache().get_current_state()
229 self.get_flow_graph().import_data(n)
230 self.get_flow_graph().update()
231 ##################################################
233 ##################################################
234 elif action == Actions.FLOW_GRAPH_UNDO:
235 n = self.get_page().get_state_cache().get_prev_state()
237 self.get_flow_graph().unselect()
238 self.get_flow_graph().import_data(n)
239 self.get_flow_graph().update()
240 self.get_page().set_saved(False)
241 elif action == Actions.FLOW_GRAPH_REDO:
242 n = self.get_page().get_state_cache().get_next_state()
244 self.get_flow_graph().unselect()
245 self.get_flow_graph().import_data(n)
246 self.get_flow_graph().update()
247 self.get_page().set_saved(False)
248 ##################################################
249 # New/Open/Save/Close
250 ##################################################
251 elif action == Actions.FLOW_GRAPH_NEW:
252 self.main_window.new_page()
253 elif action == Actions.FLOW_GRAPH_OPEN:
254 file_paths = OpenFlowGraphFileDialog(self.get_page().get_file_path()).run()
255 if file_paths: #open a new page for each file, show only the first
256 for i,file_path in enumerate(file_paths):
257 self.main_window.new_page(file_path, show=(i==0))
258 elif action == Actions.FLOW_GRAPH_CLOSE:
259 self.main_window.close_page()
260 elif action == Actions.FLOW_GRAPH_SAVE:
261 #read-only or undefined file path, do save-as
262 if self.get_page().get_read_only() or not self.get_page().get_file_path():
263 Actions.FLOW_GRAPH_SAVE_AS()
264 #otherwise try to save
267 ParseXML.to_file(self.get_flow_graph().export_data(), self.get_page().get_file_path())
268 self.get_page().set_saved(True)
270 Messages.send_fail_save(self.get_page().get_file_path())
271 self.get_page().set_saved(False)
272 elif action == Actions.FLOW_GRAPH_SAVE_AS:
273 file_path = SaveFlowGraphFileDialog(self.get_page().get_file_path()).run()
274 if file_path is not None:
275 self.get_page().set_file_path(file_path)
276 Actions.FLOW_GRAPH_SAVE()
277 elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
278 file_path = SaveImageFileDialog(self.get_page().get_file_path()).run()
279 if file_path is not None:
280 pixbuf = self.get_flow_graph().get_drawing_area().get_pixbuf()
281 pixbuf.save(file_path, IMAGE_FILE_EXTENSION[1:])
282 ##################################################
284 ##################################################
285 elif action == Actions.FLOW_GRAPH_GEN:
286 if not self.get_page().get_pid():
287 if not self.get_page().get_saved() or not self.get_page().get_file_path():
288 Actions.FLOW_GRAPH_SAVE() #only save if file path missing or not saved
289 if self.get_page().get_saved() and self.get_page().get_file_path():
290 generator = self.get_page().get_generator()
292 Messages.send_start_gen(generator.get_file_path())
294 except Exception,e: Messages.send_fail_gen(e)
295 else: self.generator = None
296 elif action == Actions.FLOW_GRAPH_EXEC:
297 if not self.get_page().get_pid():
298 Actions.FLOW_GRAPH_GEN()
299 if self.get_page().get_saved() and self.get_page().get_file_path():
300 ExecFlowGraphThread(self)
301 elif action == Actions.FLOW_GRAPH_KILL:
302 if self.get_page().get_pid():
303 try: os.kill(self.get_page().get_pid(), signal.SIGKILL)
304 except: print "could not kill pid: %s"%self.get_page().get_pid()
305 elif action == Actions.PAGE_CHANGE: #pass and run the global actions
307 else: print '!!! Action "%s" not handled !!!'%action
308 ##################################################
309 # Global Actions for all States
310 ##################################################
311 #update general buttons
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()