import freej import os import sys import gtk import gobject import threading import gtk.glade import traceback import gtksourceview2 as gtksourceview from pythoncontext import PythonExecutionContext gtk.gdk.threads_init () try: import numpy has_numpy = True except: has_numpy = False #freej.set_debug(3) MENU = -1 CONTROLLER = 0 LAYER = 1 FILTER = 2 SCREEN = 3 class MyConsole(freej.ConsoleController): def __init__(self, statuslist, statusbar): self.statuslist = statuslist #self.scrolledwindow = scroll self.infoicon = self.statuslist.render_icon(gtk.STOCK_INFO, gtk.ICON_SIZE_MENU) self.w_icon = self.statuslist.render_icon(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_MENU) self.e_icon = self.statuslist.render_icon(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU) self.d_icon = self.statuslist.render_icon(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU) self.u_icon = self.statuslist.render_icon(gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_MENU) self.statusmodel = statuslist.get_model() self.statusbar = statusbar self.status_id = self.statusbar.get_context_id('freej') self.statusbar.push(self.status_id, "FreeJ started") freej.ConsoleController.__init__(self) def poll(self): return 0 def dispatch(self): return 0 def notice(self, msg): """ task opened """ self.advance(self.infoicon, msg) def error(self, msg): """ fatal error """ self.advance(self.e_icon, msg) def warning(self, msg): """ warning """ self.advance(self.w_icon, msg) def act(self, msg): """ normal message """ self.advance(self.d_icon, msg) def func(self, msg): """ debug """ self.advance(self.u_icon, msg) def advance(self, icon, msg): iter = self.statusmodel.append([icon, msg]) self.statusbar.push(self.status_id, msg) path = self.statuslist.get_model().get_path(iter) self.statuslist.scroll_to_cell(path, None) def refresh(self, msg): print " X*", msg # ----------------------------------------------------- class MyCall(freej.DumbCall): def __init__(self, func, *args): super(MyCall, self).__init__() self.func = func self.args = args def callback(self): self.func(*self.args) class ContextMenu(gtk.Menu): """ A context menu for immediate use. Takes a list of button names, and a parent widget, and it will show itself, and set callbacks on the parent, on the form: on_cm_ It will also replace spaces in the button name, and set lower letters. """ def __init__(self, parent, buttons, icons=[]): gtk.Menu.__init__(self) for i, butname in enumerate(buttons): if icons and icons[i]: item = gtk.ImageMenuItem(icons[i]) #item.set_text(butname) else: item = gtk.MenuItem(butname) socketname = "on_cm_" + butname.replace(" ", "_").lower() item.connect("activate", getattr(parent, socketname)) item.show() self.append(item) def popup(self, selected_obj, time): self._selected = selected_obj gtk.Menu.popup(self, None, None, None, 3, time, selected_obj) class FreeJ(object): def __init__(self): self.cx = freej.Context() self.cx.init() self.cx.clear_all = True self.scr = freej.ScreenFactory.get_instance("Screen") self.scr.init( 400 , 300 , 32 ) self.cx.add_screen( self.scr ) if len(sys.argv) > 1: for lay in sys.argv[1:]: self.open_layer(lay) self.th = threading.Thread(target = self.cx.start , name = "freej") self.th.start(); #cb = MyCall(self.finished) #v.add_eos_call(cb) def delete_layer(self, layer, layer_idx=-1): layer.rem() # self.scr.rem_layer(layer) # freej.delete_layer(layer) def open_layer(self, filename): v = self.cx.open(filename) if not v: return v.init(self.cx) v.open(filename) v.start() self.cx.add_layer( v ) v.thisown = False v.set_blit("ADD") return v def finished(self): print "video looping!" class JsBuffer(gtksourceview.Buffer): def __init__(self): gtksourceview.Buffer.__init__(self) self.lang_manager = gtksourceview.LanguageManager() self.set_syntax_highlight('js') def set_syntax_highlight(self, language): lang = self.lang_manager.get_language(language) self.set_language(lang) self.filename = None def reset(self): self.filename = None self.set_property('text', "") def save(self, filename=None): if not filename: filename = self.filename else: self.filename = filename if filename: text = self.get_property('text') f = open(filename, 'w') f.write(text) f.close() print " * saved as", filename def load_file(self, filename): f = open(filename, 'r') self.set_property('text', f.read()) f.close() self.filename = filename class App(FreeJ): def __init__(self): # load the xml interface self.fname = 'freej_mixer.glade' self.wTree = gtk.glade.XML(self.fname) # get some widgets self.window = self.wTree.get_widget("main") self.statusbar = self.wTree.get_widget("statusbar") self.label_mode = self.wTree.get_widget("label_mode") self.pythonmode = self.wTree.get_widget("menuitem_pythonmode") self.history_w = self.wTree.get_widget("window_history") self.history_t = self.wTree.get_widget("textview_history") self.vbox2 = self.wTree.get_widget("vbox2") # setup subsystems self.preview_box = None self.setup_editor() self.setup_file_dialogs() self.prepare_status() self.prepare_tree() self.console = MyConsole(self.statustree, self.statusbar) self.popup = ContextMenu(self, ["delete"]) self.pyctx = PythonExecutionContext() freej.set_console(self.console) # init freej FreeJ.__init__(self) # setup python execution context main engine pointers self.pyctx['Context'] = self.cx self.pyctx['Screen'] = self.cx.screens.selected() # fill lists with engine contents self.fill_tree() self.fill_effects() # connect signals self.autoconnect_signals() self.history_w.connect('delete-event', self.hide_history) self.window.connect('destroy', gtk.main_quit) def autoconnect_signals(self): self.wTree.signal_autoconnect({"open_file": self.open_file, "open_script": self.open_script, "tree_button": self.on_treeview_button_press_event, "add_effect" : self.add_effect, "do_reset" : self.do_reset, "on_debug" : self.on_debug, "on_script_save" : self.on_script_save, "on_script_save_as" : self.on_script_save_as, "on_script_new" : self.on_script_new, "set_python_mode" : self.set_python_mode, "on_script_play" : self.on_script_play, "on_script_stop" : self.on_script_stop, "run_command": self.run_command, "on_item_activated": self.on_item_activated, "show_preview": self.show_preview, "show_history": self.show_history}) def on_item_activated(self, treeview, path, view_column): """ An item from the main tree view was selected. """ obj = self.get_selected_object(treeview.get_model().get_iter(path)) if not obj: # type of object not handled return self.console.act(self.get_description(obj)) def invert_array(self, data): if not has_numpy: return data datac = numpy.frombuffer(data, numpy.uint8) data = numpy.copy(datac) data[0::4], data[2::4] = datac[2::4], datac[0::4] return data def set_python_mode(self, toggle): if toggle.get_active(): self.lang = "python" self.window.set_title('freej: python mode') self.label_mode.set_text('py>') else: self.lang = "js" self.window.set_title('freej: javascript mode') self.label_mode.set_text('js>') self.buffer.set_syntax_highlight(self.lang) def update_previews(self): self.scr = self.cx.scr.selected() if(not self.scr): return self.scr.lock() self.scr.layers.lock() data = self.scr.get_surface_buffer() w = self.scr.geo.w h = self.scr.geo.h #data = self.invert_array(data) self.images = {} data_array = [] if len(self.scr.layers): # layer preview for layer in self.scr.layers: data = layer.get_surface_buffer() data = self.invert_array(data) w = layer.geo.w h = layer.geo.h data_array.append([data,w,h,layer.get_filename()]) else: return if self.preview_box: self.vbox2.remove(self.preview_box) self.preview_box = None self.preview_box = gtk.Table(3,3) self.vbox2.pack_start(self.preview_box, expand=False) i = 0 for data,w,h,name in data_array: self.pixbuf = gtk.gdk.pixbuf_new_from_data(data, gtk.gdk.COLORSPACE_RGB, True, 8, w, h, w*4) self.scr.layers.unlock() self.scr.unlock() self.pixbuf = self.pixbuf.scale_simple(200, 150, gtk.gdk.INTERP_BILINEAR) self.gtkpixmap = gtk.Image() self.gtkpixmap.set_tooltip_text(name) self.gtkpixmap.set_from_pixbuf(self.pixbuf) self.preview_box.attach(self.gtkpixmap, i/2, (i/2)+1, i%2, (i%2)+1) self.gtkpixmap.show() i += 1 self.preview_box.show() def __timeout(self, widget): self.update_previews() self._timeout_id = gobject.idle_add(self.__timeout, self.window) def on_script_play(self, button): # self.on_script_save(button) if self.lang == 'js': if self.buffer.filename: self.cx.parse_js_cmd(self.buffer.get_property('text')) # self.cx.open_script(self.buffer.filename) elif self.lang == 'python': if self.buffer.filename: self.run_python_command(self.buffer.get_property('text')) self.fill_tree() def on_script_stop(self, button): self.do_reset(button) def on_script_save(self, button): if self.buffer.filename: self.buffer.save() else: self.on_script_save_as(button) def on_script_save_as(self, button): self.filew = gtk.FileChooserDialog("SaveAs", None, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)) self.filew.set_default_response(gtk.RESPONSE_OK) response = self.filew.run() if response == gtk.RESPONSE_OK: filename = self.filew.get_filename() self.buffer.save(filename) self.filew.hide() def on_script_new(self, button): self.buffer.reset() def setup_editor(self): self.lang = "js" self.content_pane = self.wTree.get_widget("text_scroll") self.buffer = JsBuffer() self.editor = gtksourceview.View(self.buffer) self.editor.set_show_line_numbers(True) self.content_pane.add(self.editor) self.editor.show() accel_group = gtk.AccelGroup() self.window.add_accel_group(accel_group) self.editor.add_accelerator("paste-clipboard", accel_group, ord('v'), gtk.gdk.CONTROL_MASK, 0) self.editor.add_accelerator("copy-clipboard", accel_group, ord('c'), gtk.gdk.CONTROL_MASK, 0) self.editor.add_accelerator("cut-clipboard", accel_group, ord('x'), gtk.gdk.CONTROL_MASK, 0) def on_debug(self, menuitem): if menuitem.get_active(): freej.set_debug(3) else: freej.set_debug(1) def on_cm_delete(self, args): model, iter = self.main_tree.get_selection().get_selected() obj_type = model.get_value(iter, 2) if obj_type == LAYER: layer_idx = model.get_value(iter, 1) layer = self.scr.layers[layer_idx+1] self.delete_layer(layer, layer_idx) elif obj_type == FILTER: self.delete_effect() elif obj_type == CONTROLLER: c_idx = model.get_value(iter, 1) ctrl = self.cx.controllers[c_idx+1] self.cx.rem_controller(ctrl) self.fill_tree() def on_treeview_button_press_event(self, treeview, event): if event.button == 3: x = int(event.x) y = int(event.y) time = event.time pthinfo = treeview.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo treeview.grab_focus() treeview.set_cursor( path, col, 0) self.popup.popup( None, time) return 1 def do_reset(self, button): #del self.cx self.cx.reset() # print " * delete controllers" # for controller in list(self.cx.controllers): # self.cx.rem_controller(controller) # print " * delete layers" # for layer in list(self.scr.layers): # layer.rem() # # self.delete_layer(layer) # print " * clear layers" # print " * init context" # self.init_context() self.fill_tree() def add_effect(self, button): model, iter = self.main_tree.get_selection().get_selected() obj_type = model.get_value(iter, 2) if obj_type == LAYER: layer_idx = model.get_value(iter, 1) layer = self.scr.layers[layer_idx+1] idx = self.effects_cb.get_active() effect = self.cx.filters.pick(idx+1) self.eff = effect.apply(layer) self.fill_tree() def get_selected_object(self, iter): if not iter: return model = self.main_tree.get_model() idx = model.get_value(iter, 1) obj_type = model.get_value(iter, 2) if obj_type == LAYER: return self.scr.layers[idx+1] elif obj_type == FILTER: parent_iter = model.iter_parent(iter) lay_idx = model.get_value(parent_iter, 1) layer = self.scr.layers[lay_idx+1] filter = layer.filters[idx+1] return filter elif obj_type == CONTROLLER: return self.cx.controllers[idx+1] elif obj_type == SCREEN: return self.cx.screens[idx+1] def delete_effect(self, button=None): model, iter = self.main_tree.get_selection().get_selected() effect_name = model.get_value(iter, 0) parent_iter = model.iter_parent(iter) lay_idx = model.get_value(parent_iter, 1) layer = self.scr.layers[lay_idx+1] for idx, filter in enumerate(layer.filters): if filter.name == effect_name: layer.filters.rem(idx+1) filter.rem() self.fill_tree() return def prepare_status(self): self.statustree = self.wTree.get_widget("status_list") self.statusmodel = gtk.ListStore(gtk.gdk.Pixbuf, str) self.statustree.set_model(self.statusmodel) px = gtk.CellRendererPixbuf() text = gtk.CellRendererText() col = gtk.TreeViewColumn() col.pack_start(px, expand=False) col.pack_start(text, expand=True) col.add_attribute(px, "pixbuf", 0) col.add_attribute(text, "text", 1) self.statustree.append_column(col) def fill_effects(self): self.effects_cb = self.wTree.get_widget("combobox_effects") self.effect_model = gtk.ListStore(str) self.effects_cb.set_model(self.effect_model) self.cx.plugger.refresh(self.cx) for effect in self.cx.filters: self.effect_model.append([effect.name]) cell = gtk.CellRendererText() self.effects_cb.pack_start(cell, True) self.effects_cb.add_attribute(cell, 'text', 0) def on_tree_tooltip(self, widget, x, y, keyboard_mode, tooltip): path = self.main_tree.get_path_at_pos(x, y) if not path: return iter = self.main_tree.get_model().get_iter(path[0]) obj = self.get_selected_object(iter) if not obj: return tooltip.set_text(self.get_description(obj)) return True def get_description(self, obj): if isinstance(obj, freej.Layer): return obj.name+"\n active: "+str(obj.active) elif isinstance(obj, freej.ViewPort): return "a viewport has no name" else: return obj.name def prepare_tree(self): self.main_tree = self.wTree.get_widget("main_tree") #self.main_tree.set_tooltip_column(4) self.main_tree.set_property('has-tooltip', True) self.main_tree.connect('query-tooltip', self.on_tree_tooltip) self.main_model = gtk.TreeStore(str, int, int, gtk.gdk.Pixbuf, str) self.main_tree.set_model(self.main_model) self.folder_icon = self.main_tree.render_icon(gtk.STOCK_DIRECTORY, gtk.ICON_SIZE_MENU) self.layer_icon = self.main_tree.render_icon(gtk.STOCK_FILE, gtk.ICON_SIZE_MENU) self.screen_icon = self.main_tree.render_icon(gtk.STOCK_FILE, gtk.ICON_SIZE_MENU) self.effect_icon = self.main_tree.render_icon(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU) self.ctl_icon = self.main_tree.render_icon(gtk.STOCK_CONNECT, gtk.ICON_SIZE_MENU) cell = gtk.CellRendererText() px = gtk.CellRendererPixbuf() column = gtk.TreeViewColumn('name') column.pack_start(px, expand=False) column.pack_start(cell) self.main_tree.append_column(column) column.add_attribute(cell, "text", 0) column.add_attribute(px, "pixbuf", 3) def fill_tree(self): self.main_model.clear() tooltip = "tooltip" screens = self.main_model.append(None, ["Screens", 0, MENU, self.folder_icon, tooltip]) layers = self.main_model.append(None, ["Layers", 0, MENU, self.folder_icon, tooltip]) for c_idx, screen in enumerate(self.cx.screens): c_iter = self.main_model.append(screens, [ "screen", c_idx, SCREEN, self.screen_icon, tooltip]) for l_idx, layer in enumerate(screen.layers): name = layer.get_filename() if not name: name = layer.name lay_iter = self.main_model.append(layers, [name, l_idx, LAYER, self.layer_icon, tooltip]) for f_idx, filter in enumerate(layer.filters): iter = self.main_model.append(lay_iter, [filter.name, f_idx, FILTER, self.effect_icon, tooltip]) controllers = self.main_model.append(None, ["Controllers", 0, MENU, self.folder_icon, tooltip]) for c_idx, controller in enumerate(self.cx.controllers): c_iter = self.main_model.append(controllers, [controller.name, c_idx, CONTROLLER, self.ctl_icon, tooltip]) self.main_tree.expand_all() def hide_history(self, window, event): self.wTree.get_widget("history_menu").set_active(False) window.hide() return 1 def show_history(self, checkitem): if checkitem.get_active(): self.history_w.show() else: self.history_w.hide() def show_preview(self, checkitem): if checkitem.get_active(): self._timeout_id = gobject.idle_add(self.__timeout, self.window) else: gobject.source_remove(self._timeout_id) if self.preview_box: self.vbox2.remove(self.preview_box) self.preview_box = None def run_command(self, entry): text = entry.get_text() if self.lang == 'js': self.run_js_command(text) else: self.run_python_command(text) entry.set_text("") iter = self.history_t.get_buffer().get_end_iter() self.history_t.get_buffer().insert(iter, text+"\n") def run_python_command(self, text): class OutputRedirector(object): def __init__(s): pass def write(s, msg): if msg.strip(): self.console.notice(msg) def printfunc(msg): self.console.error(msg) _stdout = sys.stdout sys.stdout = OutputRedirector() self.pyctx.RunUserCode(text, printfunc) sys.stdout = _stdout def run_js_command(self, text): self.cx.parse_js_cmd(text) def setup_file_dialogs(self): # video selection dialog self.filew = gtk.FileChooserDialog("Video selection", None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) self.filew.set_default_response(gtk.RESPONSE_OK) # script selection dialog self.files = gtk.FileChooserDialog("Script selection", None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) self.files.set_default_response(gtk.RESPONSE_OK) currdir = os.path.realpath(os.path.curdir) examplesdir = os.path.join(currdir, '..', '..', 'javascript', 'examples') if os.path.exists(examplesdir): self.files.set_current_folder(examplesdir) def open_file(self, *args): response = self.filew.run() if response == gtk.RESPONSE_OK: filename = self.filew.get_filename() self.open_layer(filename) self.filew.hide() self.fill_tree() def open_script(self, *args): response = self.files.run() if response == gtk.RESPONSE_OK: filename = self.files.get_filename() if filename.endswith('.py'): self.pythonmode.set_active(True) elif filename.endswith('.js'): self.pythonmode.set_active(False) self.buffer.load_file(filename) self.files.hide() self.fill_tree() # ----------------------------------------------------- # ----------------------------------------------------- if __name__ == "__main__": app = App() gtk.main() print "exiting" app.cx.quit = False