Files
FreeJ/scripts/python/gtkgui/main.py

684 lines
25 KiB
Python

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
def old_printlog(self, msg):
self.advance(self.infoicon, 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_<butname>
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