402 lines
13 KiB
Python
402 lines
13 KiB
Python
#!/usr/bin/python
|
|
|
|
import os
|
|
import sys
|
|
|
|
import pygtk
|
|
pygtk.require('2.0')
|
|
import gobject
|
|
import gtk
|
|
import gtk.gdk
|
|
import gtk.glade
|
|
import gnome
|
|
import gnome.ui
|
|
import scipy
|
|
import pango
|
|
import projectview, workflow, dataset, view, navigator, dialogs, selections, plots, main
|
|
from logger import logger, LogView
|
|
|
|
|
|
PROGRAM_NAME = 'laydi'
|
|
VERSION = '0.1.0'
|
|
DATADIR = os.path.join(main.PYDIR, 'laydi')
|
|
#ICONDIR = os.path.join(DATADIR,"..","icons")
|
|
ICONDIR = main.ICONDIR
|
|
GLADEFILENAME = os.path.join(main.PYDIR, 'laydi/laydi.glade')
|
|
_icon_mapper = {dataset.Dataset: 'dataset',
|
|
dataset.CategoryDataset: 'category_dataset',
|
|
dataset.GraphDataset: 'graph_dataset',
|
|
plots.Plot: 'line_plot'}
|
|
|
|
class IconFactory:
|
|
"""Factory for icons that ensures that each icon is only loaded once."""
|
|
|
|
def __init__(self, path):
|
|
self._path = path
|
|
self._icons = {}
|
|
|
|
def get(self, iconname):
|
|
"""Returns the gdk loaded PixBuf for the given icon.
|
|
Reads the icon from file if necessary."""
|
|
|
|
# if iconname isnt a string, try to autoconvert
|
|
if not isinstance(iconname, str):
|
|
for cls in _icon_mapper.keys():
|
|
if isinstance(iconname, cls):
|
|
iconname = _icon_mapper[cls]
|
|
|
|
if self._icons.has_key(iconname):
|
|
return self._icons[iconname]
|
|
|
|
icon_fname = os.path.join(self._path, '%s.png' % iconname)
|
|
icon = gtk.gdk.pixbuf_new_from_file(icon_fname)
|
|
self._icons[iconname] = icon
|
|
return icon
|
|
|
|
icon_factory = IconFactory(ICONDIR)
|
|
|
|
class TableSizeSelection(gtk.Window):
|
|
|
|
def __init__(self):
|
|
self._SIZE = size = 5
|
|
gtk.Window.__init__(self, gtk.WINDOW_POPUP)
|
|
self._table = gtk.Table(size, size, True)
|
|
self._items = []
|
|
|
|
## Create a 3x3 table of EventBox object, doubly stored because
|
|
## gtk.Table does not support indexed retrieval.
|
|
|
|
for y in range(size):
|
|
line = []
|
|
for x in range(size):
|
|
ebox = gtk.EventBox()
|
|
ebox.add(gtk.Frame())
|
|
ebox.set_size_request(20, 20)
|
|
ebox.set_visible_window(True)
|
|
self._table.attach(ebox, x, x+1, y, y+1, gtk.FILL, gtk.FILL)
|
|
line.append(ebox)
|
|
self._items.append(line)
|
|
|
|
self.set_border_width(5)
|
|
self.add(self._table)
|
|
self.connect_signals()
|
|
|
|
def _get_child_pos(self, child):
|
|
size = self._SIZE
|
|
for x in range(size):
|
|
for y in range(size):
|
|
if self._items[y][x] == child:
|
|
return (x, y)
|
|
return None
|
|
|
|
def connect_signals(self):
|
|
size = self._SIZE
|
|
for x in range(size):
|
|
for y in range(size):
|
|
self._items[y][x].add_events(gtk.gdk.ENTER_NOTIFY_MASK)
|
|
self._items[y][x].connect("enter-notify-event",
|
|
self._on_enter_notify)
|
|
self._items[y][x].connect("button-release-event",
|
|
self._on_button_release)
|
|
|
|
def _on_enter_notify(self, widget, event):
|
|
size = self._SIZE
|
|
x, y = self._get_child_pos(widget)
|
|
for i in range(size):
|
|
for j in range(size):
|
|
if i <= x and j <= y:
|
|
self._items[j][i].set_state(gtk.STATE_SELECTED)
|
|
else:
|
|
self._items[j][i].set_state(gtk.STATE_NORMAL)
|
|
self.x = x
|
|
self.y = y
|
|
|
|
def _on_button_release(self, widget, event):
|
|
size = self._SIZE
|
|
self.emit('table-size-set', self.x+1, self.y+1)
|
|
self.hide_all()
|
|
|
|
for x in range(size):
|
|
for y in range(size):
|
|
self._items[y][x].set_state(gtk.STATE_NORMAL)
|
|
|
|
|
|
class ViewFrameToolButton (gtk.ToolItem):
|
|
|
|
def __init__(self):
|
|
gtk.ToolItem.__init__(self)
|
|
|
|
fname = os.path.join(ICONDIR, "table_size.png")
|
|
image = gtk.Image()
|
|
image.set_from_file(fname)
|
|
|
|
self._button = gtk.Button()
|
|
self._button.set_image(image)
|
|
self._button.set_property("can-focus", False)
|
|
|
|
eb = gtk.EventBox()
|
|
eb.add(self._button)
|
|
self.add(eb)
|
|
self._item = TableSizeSelection()
|
|
self._button.connect("button-press-event", self._on_show_menu)
|
|
image.show()
|
|
self._image = image
|
|
|
|
self._item.connect("table-size-set", self._on_table_size_set)
|
|
self._button.set_relief(gtk.RELIEF_NONE)
|
|
self.show_all()
|
|
|
|
def _on_show_menu(self, widget, event):
|
|
x, y = self._image.window.get_origin()
|
|
x2, y2, w, h, b = self._image.window.get_geometry()
|
|
|
|
self._item.move(x, y+h)
|
|
self._item.show_all()
|
|
|
|
def _on_table_size_set(self, widget, width, height):
|
|
main.application['main_view'].resize_table(width, height)
|
|
|
|
|
|
class LaydiApp:
|
|
|
|
def __init__(self): # Application variables
|
|
self.current_data = None
|
|
self._last_view = None
|
|
self._plot_toolbar = None
|
|
self._toolbar_state = None
|
|
|
|
gtk.glade.set_custom_handler(self.custom_object_factory)
|
|
self.widget_tree = gtk.glade.XML(GLADEFILENAME, 'appwindow')
|
|
# self.workflow = wf
|
|
|
|
self.idlist_crt = selections.IdListController(self['identifier_list'])
|
|
self.sellist_crt = selections.SelectionListController(self['selection_tree'],
|
|
self.idlist_crt)
|
|
self.dimlist_crt = selections.DimListController(self['dim_list'],
|
|
self.sellist_crt)
|
|
self.sellist_crt.set_dimlist_controller(self.dimlist_crt)
|
|
|
|
def init_gui(self):
|
|
self['appwindow'].set_size_request(800, 600)
|
|
|
|
# Set up workflow
|
|
self.wf_view = workflow.WorkflowView(main.workflow)
|
|
self.wf_view.show()
|
|
self['workflow_vbox'].pack_end(self.wf_view)
|
|
|
|
self._wf_menu = workflow.WorkflowMenu(main.workflow)
|
|
self._wf_menu.show()
|
|
wf_menuitem = gtk.MenuItem('Fu_nctions')
|
|
wf_menuitem.set_submenu(self._wf_menu)
|
|
wf_menuitem.show()
|
|
|
|
self['menubar1'].insert(wf_menuitem, 2)
|
|
|
|
# Connect signals
|
|
signals = {'on_quit1_activate' : (gtk.main_quit),
|
|
'on_appwindow_delete_event' : (gtk.main_quit),
|
|
'on_zoom_in_button_clicked' : (self.on_single_view),
|
|
'on_zoom_out_button_clicked' : (self.on_multiple_view),
|
|
'on_new1_activate' : (self.on_create_project),
|
|
'on_button_new_clicked' : (self.on_create_project),
|
|
'on_workflow_refresh_clicked' : (self.on_workflow_refresh_clicked),
|
|
'on_index1_activate' : (self.on_help_index),
|
|
'on_about1_activate' : (self.on_help_about),
|
|
'on_report_bug1_activate' : (self.on_help_report_bug),
|
|
'on_small_view1_activate' : (self.on_multiple_view),
|
|
'on_large_view1_activate' : (self.on_single_view),
|
|
|
|
'on_left1_activate' : (self.on_left),
|
|
'on_right1_activate' : (self.on_right),
|
|
'on_up1_activate' : (self.on_up),
|
|
'on_down1_activate' : (self.on_down),
|
|
|
|
'on_navigator1_activate' : (self.on_show_navigator),
|
|
'on_workflow1_activate' : (self.on_show_workflow),
|
|
'on_information1_activate' : (self.on_show_infopane),
|
|
}
|
|
self.widget_tree.signal_autoconnect(signals)
|
|
|
|
self['main_view'].connect('view-changed', self.on_view_changed)
|
|
|
|
# Log that we've set up the app now
|
|
logger.debug('Program started')
|
|
|
|
# Add ViewFrame table size to toolbar
|
|
tb = ViewFrameToolButton()
|
|
self['toolbar'].add(tb)
|
|
|
|
def set_projectview(self, proj):
|
|
logger.notice('Welcome to your new project. Grasp That Data!')
|
|
self.navigator_view.add_projectview(proj)
|
|
self.dimlist_crt.set_projectview(proj)
|
|
self.sellist_crt.set_projectview(proj)
|
|
|
|
def set_workflow(self, workflow):
|
|
main.workflow = workflow
|
|
self.wf_view.set_workflow(main.workflow)
|
|
|
|
def show(self):
|
|
self.init_gui()
|
|
|
|
def change_plot(self, plot):
|
|
"""Sets the plot in the currently active ViewFrame. If the plot is
|
|
already shown in another ViewFrame it will be moved from there."""
|
|
# Set current selection in the plot before showing it.
|
|
plot.selection_changed(None, main.projectview.get_selection())
|
|
|
|
self['main_view'].insert_view(plot)
|
|
self._update_toolbar(plot)
|
|
|
|
def change_plots(self, plots):
|
|
"""Changes all plots."""
|
|
self['main_view'].set_all_plots(plots)
|
|
v = self.get_active_view_frame().get_view()
|
|
self._update_toolbar(v)
|
|
|
|
def get_active_view_frame(self):
|
|
return self['main_view'].get_active_view_frame()
|
|
|
|
def _update_toolbar(self, view):
|
|
"""Set the plot specific toolbar to the toolbar of the currently
|
|
active plot."""
|
|
|
|
# don't do anything on no change
|
|
if self._last_view == view:
|
|
return
|
|
self._last_view = view
|
|
|
|
logger.debug("view changed to %s" % view)
|
|
|
|
window = self['plot_toolbar_dock']
|
|
if self._plot_toolbar:
|
|
toolbar_state = self._plot_toolbar.get_mode()
|
|
window.remove(self._plot_toolbar)
|
|
else:
|
|
toolbar_state = "default"
|
|
|
|
if view:
|
|
self._plot_toolbar = view.get_toolbar()
|
|
self._plot_toolbar.set_mode(toolbar_state)
|
|
else:
|
|
self._plot_toolbar = None
|
|
|
|
if self._plot_toolbar:
|
|
window.add(self._plot_toolbar)
|
|
|
|
# Methods to create GUI widgets from CustomWidgets in the glade file.
|
|
# The custom_object_factory calls other functions to generate specific
|
|
# widgets.
|
|
|
|
def custom_object_factory(self, glade, fun_name, widget_name, s1, s2, i1, i2):
|
|
"Called by the glade file reader to create custom GUI widgets."
|
|
handler = getattr(self, fun_name)
|
|
return handler(s1, s2, i1, i2)
|
|
|
|
def create_logview(self, str1, str2, int1, int2):
|
|
self.log_view = LogView(logger)
|
|
self.log_view.show()
|
|
return self.log_view
|
|
|
|
def create_main_view(self, str1, str2, int1, int2):
|
|
self.main_view = view.MainView()
|
|
self.main_view.show()
|
|
return self.main_view
|
|
|
|
def create_navigator_view(self, str1, str2, int1, int2):
|
|
self.navigator_view = navigator.NavigatorView()
|
|
self.navigator_view.show()
|
|
return self.navigator_view
|
|
|
|
def create_dim_list(self, str1, str2, int1, int2):
|
|
self.dim_list = selections.DimList()
|
|
self.dim_list.show()
|
|
return self.dim_list
|
|
|
|
def create_selection_tree(self, str1, str2, int1, int2):
|
|
self.selection_tree = selections.SelectionTree()
|
|
self.selection_tree.show()
|
|
return self.selection_tree
|
|
|
|
def create_identifier_list(self, str1, str2, int1, int2):
|
|
self.identifier_list = selections.IdentifierList()
|
|
self.identifier_list.show()
|
|
return self.identifier_list
|
|
|
|
def __getitem__(self, key):
|
|
return self.widget_tree.get_widget(key)
|
|
|
|
# Event handlers.
|
|
# These methods are called by the gtk framework in response to events and
|
|
# should not be called directly.
|
|
|
|
def on_single_view(self, *ignored):
|
|
self['main_view'].goto_large()
|
|
|
|
def on_multiple_view(self, *ignored):
|
|
self['main_view'].goto_small()
|
|
|
|
def on_create_project(self, *rest):
|
|
d = dialogs.CreateProjectDruid(self)
|
|
d.run()
|
|
|
|
def on_help_about(self, *rest):
|
|
widget_tree = gtk.glade.XML(GLADEFILENAME, 'aboutdialog')
|
|
about = widget_tree.get_widget('aboutdialog')
|
|
about.run()
|
|
|
|
def on_help_index(self, *ignored):
|
|
gnome.help_display_uri('https://dev.pvv.org/projects/laydi/wiki/help')
|
|
|
|
def on_help_report_bug(self, *ignored):
|
|
gnome.help_display_uri('https://dev.pvv.org/projects/laydi/newticket')
|
|
|
|
def on_workflow_refresh_clicked(self, *ignored):
|
|
try:
|
|
reload(sys.modules[main.workflow.__class__.__module__])
|
|
except Exception, e:
|
|
logger.warning('Cannot reload workflow')
|
|
logger.warning(e)
|
|
else:
|
|
logger.notice('Successfully reloaded workflow')
|
|
|
|
def on_view_changed(self, widget, vf):
|
|
self._update_toolbar(vf.get_view())
|
|
|
|
def on_show_navigator(self, item):
|
|
if item.get_active():
|
|
self['data_vbox'].show()
|
|
else:
|
|
self['data_vbox'].hide()
|
|
|
|
def on_show_workflow(self, item):
|
|
if item.get_active():
|
|
self['workflow_vbox'].show()
|
|
else:
|
|
self['workflow_vbox'].hide()
|
|
|
|
def on_show_infopane(self, item):
|
|
if item.get_active():
|
|
self['bottom_notebook'].show()
|
|
else:
|
|
self['bottom_notebook'].hide()
|
|
|
|
def on_left(self, item):
|
|
self.main_view.move_focus_left()
|
|
|
|
def on_right(self, item):
|
|
self.main_view.move_focus_right()
|
|
|
|
def on_up(self, item):
|
|
self.main_view.move_focus_up()
|
|
|
|
def on_down(self, item):
|
|
self.main_view.move_focus_down()
|
|
|
|
|
|
gobject.signal_new('table-size-set', TableSizeSelection,
|
|
gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_INT, gobject.TYPE_INT))
|
|
|