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 return Actions.handle_key_press(event)
86 def _quit(self, window, event):
88 Handle the delete event from the main window.
89 Generated by pressing X to close, alt+f4, or right click+close.
90 This method in turns calls the state handler to quit.
93 Actions.APPLICATION_QUIT()
96 def _handle_action(self, action):
98 ##################################################
100 ##################################################
101 if action == Actions.APPLICATION_INITIALIZE:
102 for action in Actions.get_all_actions(): action.set_sensitive(False) #set all actions disabled
103 #enable a select few actions
105 Actions.APPLICATION_QUIT, Actions.FLOW_GRAPH_NEW,
106 Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS,
107 Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY,
108 Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY,
109 Actions.TYPES_WINDOW_DISPLAY,
110 ): action.set_sensitive(True)
111 if not self.init_file_paths:
112 self.init_file_paths = Preferences.files_open()
113 if not self.init_file_paths: self.init_file_paths = ['']
114 for file_path in self.init_file_paths:
115 if file_path: self.main_window.new_page(file_path) #load pages from file paths
116 if Preferences.file_open() in self.init_file_paths:
117 self.main_window.new_page(Preferences.file_open(), show=True)
118 if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists
119 elif action == Actions.APPLICATION_QUIT:
120 if self.main_window.close_pages():
123 ##################################################
125 ##################################################
126 elif action == Actions.ELEMENT_SELECT:
127 pass #do nothing, update routines below
128 elif action == Actions.NOTHING_SELECT:
129 self.get_flow_graph().unselect()
130 ##################################################
132 ##################################################
133 elif action == Actions.BLOCK_ENABLE:
134 if self.get_flow_graph().enable_selected(True):
135 self.get_flow_graph().update()
136 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
137 self.get_page().set_saved(False)
138 elif action == Actions.BLOCK_DISABLE:
139 if self.get_flow_graph().enable_selected(False):
140 self.get_flow_graph().update()
141 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
142 self.get_page().set_saved(False)
143 ##################################################
145 ##################################################
146 elif action == Actions.BLOCK_CUT:
148 Actions.ELEMENT_DELETE()
149 elif action == Actions.BLOCK_COPY:
150 self.clipboard = self.get_flow_graph().copy_to_clipboard()
151 elif action == Actions.BLOCK_PASTE:
153 self.get_flow_graph().paste_from_clipboard(self.clipboard)
154 self.get_flow_graph().update()
155 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
156 self.get_page().set_saved(False)
157 ##################################################
158 # Move/Rotate/Delete/Create
159 ##################################################
160 elif action == Actions.BLOCK_MOVE:
161 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
162 self.get_page().set_saved(False)
163 elif action == Actions.BLOCK_ROTATE_CCW:
164 if self.get_flow_graph().rotate_selected(90):
165 self.get_flow_graph().update()
166 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
167 self.get_page().set_saved(False)
168 elif action == Actions.BLOCK_ROTATE_CW:
169 if self.get_flow_graph().rotate_selected(-90):
170 self.get_flow_graph().update()
171 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
172 self.get_page().set_saved(False)
173 elif action == Actions.ELEMENT_DELETE:
174 if self.get_flow_graph().remove_selected():
175 self.get_flow_graph().update()
176 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
177 Actions.NOTHING_SELECT()
178 self.get_page().set_saved(False)
179 elif action == Actions.ELEMENT_CREATE:
180 self.get_flow_graph().update()
181 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
182 Actions.NOTHING_SELECT()
183 self.get_page().set_saved(False)
184 elif action == Actions.BLOCK_INC_TYPE:
185 if self.get_flow_graph().type_controller_modify_selected(1):
186 self.get_flow_graph().update()
187 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
188 self.get_page().set_saved(False)
189 elif action == Actions.BLOCK_DEC_TYPE:
190 if self.get_flow_graph().type_controller_modify_selected(-1):
191 self.get_flow_graph().update()
192 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
193 self.get_page().set_saved(False)
194 elif action == Actions.PORT_CONTROLLER_INC:
195 if self.get_flow_graph().port_controller_modify_selected(1):
196 self.get_flow_graph().update()
197 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
198 self.get_page().set_saved(False)
199 elif action == Actions.PORT_CONTROLLER_DEC:
200 if self.get_flow_graph().port_controller_modify_selected(-1):
201 self.get_flow_graph().update()
202 self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
203 self.get_page().set_saved(False)
204 ##################################################
206 ##################################################
207 elif action == Actions.ABOUT_WINDOW_DISPLAY:
208 Dialogs.AboutDialog(self.get_flow_graph().get_parent())
209 elif action == Actions.HELP_WINDOW_DISPLAY:
211 elif action == Actions.TYPES_WINDOW_DISPLAY:
212 Dialogs.TypesDialog(self.get_flow_graph().get_parent())
213 elif action == Actions.ERRORS_WINDOW_DISPLAY:
214 Dialogs.ErrorsDialog(self.get_flow_graph())
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.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid())
313 Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements()))
314 Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(self.get_flow_graph().get_selected_block()))
315 Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
316 Actions.BLOCK_ROTATE_CW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
317 #update cut/copy/paste
318 Actions.BLOCK_CUT.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
319 Actions.BLOCK_COPY.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
320 Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard))
321 #update enable/disable
322 Actions.BLOCK_ENABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
323 Actions.BLOCK_DISABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
324 #set the exec and stop buttons
325 self.update_exec_stop()
327 Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved())
328 self.main_window.update()
329 try: #set the size of the flow graph area (if changed)
330 new_size = self.get_flow_graph().get_option('window_size')
331 if self.get_flow_graph().get_size() != tuple(new_size):
332 self.get_flow_graph().set_size(*new_size)
335 self.get_flow_graph().update_selected()
336 self.get_flow_graph().queue_draw()
337 return True #action was handled
339 def update_exec_stop(self):
341 Update the exec and stop buttons.
342 Lock and unlock the mutex for race conditions with exec flow graph threads.
344 sensitive = self.get_flow_graph().is_valid() and not self.get_page().get_pid()
345 Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive)
346 Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive)
347 Actions.FLOW_GRAPH_KILL.set_sensitive(self.get_page().get_pid() != None)
349 class ExecFlowGraphThread(Thread):
350 """Execute the flow graph as a new process and wait on it to finish."""
352 def __init__ (self, action_handler):
354 ExecFlowGraphThread constructor.
355 @param action_handler an instance of an ActionHandler
357 Thread.__init__(self)
358 self.update_exec_stop = action_handler.update_exec_stop
359 self.flow_graph = action_handler.get_flow_graph()
360 #store page and dont use main window calls in run
361 self.page = action_handler.get_page()
362 Messages.send_start_exec(self.page.get_generator().get_file_path())
365 self.p = self.page.get_generator().get_popen()
366 self.page.set_pid(self.p.pid)
368 self.update_exec_stop()
371 Messages.send_verbose_exec(str(e))
372 Messages.send_end_exec()
376 Wait on the executing process by reading from its stdout.
377 Use gobject.idle_add when calling functions that modify gtk objects.
382 gobject.idle_add(Messages.send_verbose_exec, r)
383 r = os.read(self.p.stdout.fileno(), 1024)
384 gobject.idle_add(self.done)
387 """Perform end of execution tasks."""
388 Messages.send_end_exec()
389 self.page.set_pid(None)
390 self.update_exec_stop()