def __init__(self, model, parent): super(DetailsTab, self).__init__() self.model = model self._parent = parent self.set_spacing(6) self.set_border_width(6) self.klist = ObjectTree(self.get_columns()) self.populate() self.pack_start(self.klist) self.klist.show() if len(self.klist) and self.get_details_dialog_class(): self.button_box = gtk.HButtonBox() self.button_box.set_layout(gtk.BUTTONBOX_START) details_button = gtk.Button(self.details_lbl) self.button_box.pack_start(details_button) details_button.set_sensitive(bool(self.klist.get_selected())) details_button.show() self.pack_end(self.button_box, False, False) self.button_box.show() self.button_box.details_button = details_button details_button.connect('clicked', self._on_details_button__clicked) self.klist.connect('row-activated', self._on_klist__row_activated) self.klist.connect('selection-changed', self._on_klist__selection_changed) self.setup_widgets()
def __init__(self, with_code=True, create_mode=False): self.create_mode = create_mode self._accounts = {} columns = [StockTextColumn('description', title=_("Account name"), data_type=str, pack_end=True, expand=True, sorted=True, sort_func=sort_models)] if with_code: columns.append(Column('code', title=_("Code"), data_type=str, width=120)) if not create_mode: # FIXME: This needs to be much better colorized, and moved to the # domain classes def colorize(account): if (account.kind == 'account' and account.account_type == Account.TYPE_INCOME): return False else: return account.total < 0 columns.append(ColoredColumn('total', title=_("Total"), width=100, data_type=currency, color='red', data_func=colorize, use_data_model=True)) ObjectTree.__init__(self, columns, mode=gtk.SELECTION_SINGLE) def render_icon(icon): return self.render_icon(icon, gtk.ICON_SIZE_MENU) self._pixbuf_money = render_icon(STOQ_MONEY) self._pixbuf_payable = render_icon(STOQ_PAYABLE_APP) self._pixbuf_receivable = render_icon(STOQ_BILLS) self._pixbuf_till = render_icon(STOQ_TILL_APP) if self.create_mode: self.set_headers_visible(False)
def __init__(self, store, columns=None, editor_class=None, klist_objects=None, visual_mode=False, restore_name=None, tree=False): """ Creates a new AdditionListSlave object :param store: a store :param columns: column definitions :type columns: sequence of :class:`kiwi.ui.objectlist.Columns` :param editor_class: the window that is going to be open when user clicks on add_button or edit_button. :type: editor_class: a :class:`stoqlib.gui.editors.BaseEditor` subclass :param klist_objects: initial objects to insert into the list :param visual_mode: if we are working on visual mode, that means, not possible to edit the model on this object type visual_mode: bool :param restore_name: the name used to save and restore the columns on a cache system (e.g. pickle) :type restore_name: basestring :param tree: Indication of which kind of list we are adding. If `True` ObjectTree otherwise ObjectList will be added """ columns = columns or self.get_columns() SearchSlave.__init__(self, columns=columns, restore_name=restore_name, store=store) self.tree = tree self.klist = ObjectTree() if tree else ObjectList() self.list_vbox.add(self.klist) self.list_vbox.show_all() if not self.columns: raise StoqlibError("columns must be specified") self.visual_mode = visual_mode self.store = store self.set_editor(editor_class) self._can_edit = True self._callback_id = None if self.visual_mode: self.hide_add_button() self.hide_edit_button() self.hide_del_button() items = klist_objects or self.get_items() self._setup_klist(items) self._update_sensitivity()
def __init__(self, model, parent): super(DetailsTab, self).__init__() self.model = model self._parent = parent self.set_spacing(6) self.set_border_width(6) self.klist = ObjectTree(self.get_columns()) self.populate() self.pack_start(self.klist, True, True, 0) self.klist.show() if len(self.klist) and self.get_details_dialog_class(): self.button_box = Gtk.HButtonBox() self.button_box.set_layout(Gtk.ButtonBoxStyle.START) details_button = Gtk.Button.new_with_label(self.details_lbl) self.button_box.pack_start(details_button, True, True, 0) details_button.set_sensitive(bool(self.klist.get_selected())) details_button.show() self.pack_end(self.button_box, False, False, 0) self.button_box.show() self.button_box.details_button = details_button details_button.connect('clicked', self._on_details_button__clicked) self.klist.connect('row-activated', self._on_klist__row_activated) self.klist.connect('selection-changed', self._on_klist__selection_changed) self.setup_widgets()
def __init__(self, parent, menubar): self.project_data = ObjectTree([Column('name', use_markup=True, data_type=str, sorted=True), Column('info')]) self.project_data.connect('row-activated', self.on_row_activated) self.model_tree = Project() self._set_treeview_hooks() self.menubar = menubar self.set_parent(parent) self.filename = '' self.undo_stack = UndoStack( self.model_tree.__repr__, self.import_xml_file, self.project_data.select, menubar, self.meta, 'Initialization') SlaveDelegate.__init__(self, toplevel=self.project_data)
def setup_wikitree(self): columns = ['name', 'id', 'lastModified', 'perms', 'size'] columns = [Column(s) for s in columns] self.objectlist = ObjectTree(columns) self.objectlist.connect("selection-changed", self.selected) self.view.vbox2.add(self.objectlist)
def __init__(self, parent, mo): self.log = logging.getLogger('MINICAL') self.parent = parent self.mo = mo self.factory = Factory() self.__stop_auto_highlight = False # Disable automatic highlighting of events. self.__stop_auto_dayjump = False # Disable automatically jumping to the start of the event on selection. self.__stop_auto_treeview_update = False # FIXME GladeSlaveDelegate.__init__(self, gladefile='mo_tab_events', toplevel_name='window_main') # Set up the user interface eventColumns = [ Column('start', title='Start', data_type=datetime.datetime, sorted=True), Column('end', title='End', data_type=datetime.datetime), Column('summaryformat', title='Summary', use_markup=True), Column('duration', title='Duration', justify=gtk.JUSTIFY_RIGHT) ] self.treeview_event = ObjectTree(eventColumns) self.vbox_eventslist.add(self.treeview_event) self.combobox_display_range.set_active(self.show_ranges.index(self.mo.config['events.default_show'].lower())) cal_options = gtk.CALENDAR_WEEK_START_MONDAY if self.mo.config['events.cal_show_weeknr']: cal_options |= gtk.CALENDAR_SHOW_WEEK_NUMBERS self.calendar.set_display_options((self.calendar.get_display_options() | cal_options)) # Connect signals self.treeview_event.connect('selection-changed', self.treeview_event__selection_changed) self.treeview_event.connect('row-activated', self.treeview_event__row_activated) self.treeview_event.connect('key-press-event', self.treeview_event__key_press_event) self.on_toolbutton_today__clicked()
def setup_wikitree(self): columns = ['id', 'lastModified', 'perms', 'size'] columns = [Column(s) for s in columns] columns.insert(0, Column('icon', title='name', data_type=gtk.gdk.Pixbuf)) self.objectlist = ObjectTree(columns) columns.insert(1, Column('name', column='icon')) self.objectlist.set_columns(columns) self.objectlist.connect("selection-changed", self.selected) self.view.vbox2.add(self.objectlist)
def __init__(self, parent, menubar): self.project_data = ObjectTree([ Column('name', use_markup=True, data_type=str, sorted=True), Column('info') ]) self.project_data.connect('row-activated', self.on_row_activated) self.model_tree = Project() self._set_treeview_hooks() self.menubar = menubar self.set_parent(parent) self.filename = '' self.undo_stack = UndoStack(self.model_tree.__repr__, self.import_file, self.project_data.select, menubar, self.meta, 'Initialization') SlaveDelegate.__init__(self, toplevel=self.project_data)
def __init__(self, parent, mo): self.parent = parent self.mo = mo self.factory = Factory() # Set up the user interface GladeSlaveDelegate.__init__(self, gladefile="mo_tab_notes", toplevel_name="window_main") noteColumns = [ Column("summary", title='Title', data_type=str), ] #self.treeview_note = ObjectList(noteColumns) self.treeview_note = ObjectTree(noteColumns) self.vbox_notelist.add(self.treeview_note) # Connect signals self.treeview_note.connect('row-activated', self.treeview_note__row_activated) self.treeview_note.connect('selection-changed', self.treeview_note__selection_changed) self.treeview_note.connect('key-press-event', self.treeview_note__key_press_event) self.refresh()
def __init__(self, parent, mo): self.parent = parent self.mo = mo self.factory = Factory() GladeSlaveDelegate.__init__(self, gladefile="mo_tab_todo", toplevel_name="window_main") # Set up the user interface todoColumns = [ Column("done", title='Done', data_type=bool, editable=True), Column("summaryformat", title='Summary', use_markup=True), Column('priority', title='Priority', sorted=True, order=gtk.SORT_DESCENDING), ColoredColumn('due', title='Due', data_type=datetime.datetime, color='red', data_func=self.color_due), Column('created', title='Created', data_type=datetime.datetime) ] self.treeview_todo = ObjectTree(todoColumns) self.vbox_todolist.add(self.treeview_todo) # Connect signals self.treeview_todo.connect('row-activated', self.treeview_todo__row_activated) self.treeview_todo.connect('selection-changed', self.treeview_todo__selection_changed) self.treeview_todo.connect('key-press-event', self.treeview_todo__key_press_event) self.refresh()
class GTKProject(SlaveDelegate): """A facade of kmcos.types.Project so that pygtk can display in a TreeView. """ def __init__(self, parent, menubar): self.project_data = ObjectTree([ Column('name', use_markup=True, data_type=str, sorted=True), Column('info') ]) self.project_data.connect('row-activated', self.on_row_activated) self.model_tree = Project() self._set_treeview_hooks() self.menubar = menubar self.set_parent(parent) self.filename = '' self.undo_stack = UndoStack(self.model_tree.__repr__, self.import_file, self.project_data.select, menubar, self.meta, 'Initialization') SlaveDelegate.__init__(self, toplevel=self.project_data) def _set_treeview_hooks(self): """Fudge function to import to access function to kmcos.types.Project to kmcos.gui.GTKProject. """ self.project_data.clear() # Meta self.meta = self.project_data.append(None, self.model_tree.meta) self.model_tree.meta = self.meta # Layer List self.model_tree.add_layer = self.add_layer self.layer_list = self.project_data.append(None, self.model_tree.layer_list) self.get_layers = lambda: \ sorted(self.project_data.get_descendants(self.layer_list), key=lambda x: x.name) self.model_tree.get_layers = self.get_layers self.lattice = self.layer_list # Parameter List self.parameter_list = self.project_data.append( None, self.model_tree.parameter_list) self.add_parameter = lambda parameter: \ self.project_data.append(self.parameter_list, parameter) self.model_tree.add_parameter = self.add_parameter self.get_parameters = lambda: \ sorted(self.project_data.get_descendants(self.parameter_list), key=lambda x: x.name) self.model_tree.get_parameters = self.get_parameters # Species List self.species_list = self.project_data.append( None, self.model_tree.species_list) self.add_species = lambda species: \ self.project_data.append(self.species_list, species) self.model_tree.add_species = self.add_species self.get_speciess = lambda: \ sorted(self.project_data.get_descendants(self.species_list), key=lambda x: x.name) self.model_tree.get_speciess = self.get_speciess # Process List self.process_list = self.project_data.append( None, self.model_tree.process_list) self.add_process = lambda process:\ self.project_data.append(self.process_list, process) self.model_tree.add_process = self.add_process self.get_processes = lambda: \ sorted(self.project_data.get_descendants(self.process_list), key=lambda x: x.name) self.model_tree.get_processes = self.get_processes # Output List self.output_list = self.project_data.append( None, self.model_tree.output_list) self.add_output = lambda output:\ self.project_data.append(self.output_list, output) self.model_tree.add_output = self.add_output self.get_outputs = lambda: \ sorted(self.project_data.get_descendants(self.output_list), key=lambda x: x.name) self.model_tree.get_outputs = self.get_outputs def add_layer(self, layer): self.project_data.append(self.layer_list, layer) if len(self.get_layers()) == 1: self.set_default_layer(layer.name) self.set_substrate_layer(layer.name) return layer def set_default_species(self, species): self.model_tree.species_list.default_species = species def set_substrate_layer(self, layer): self.model_tree.layer_list.substrate_layer = layer def set_default_layer(self, layer): self.model_tree.layer_list.default_layer = layer def update(self, model): """Update the object tree.""" self.project_data.update(model) def on_row_activated(self, _tree, data): if isinstance(data, Layer): data.active = not data.active def get_name(self): """Return project name.""" if self.filename: return os.path.basename(self.filename) else: return 'Untitled' def __repr__(self): return str(self.model_tree) def import_file(self, filename): """Import XML project file into editor GUI, unfolding the object tree. """ self.filename = filename self.model_tree.import_file(filename) self.expand_all() def expand_all(self): """Expand all list of the project tree """ self.project_data.expand(self.species_list) self.project_data.expand(self.layer_list) self.project_data.expand(self.parameter_list) self.project_data.expand(self.process_list) self.project_data.expand(self.output_list) def on_key_press(self, _, event): """When the user hits the keyboard focusing the treeview this event is triggered. Right now the only supported function is to deleted the selected item """ selection = self.project_data.get_selected() if gtk.gdk.keyval_name(event.keyval) == 'Delete': if (isinstance(selection, Species) or isinstance(selection, Process) or isinstance(selection, Parameter) or isinstance(selection, Layer)): if kiwi.ui.dialogs.yesno( "Do you really want to delete '%s'?" \ % selection.name) == gtk.RESPONSE_YES: self.project_data.remove(selection) def on_project_data__selection_changed(self, _, elem): """When a new item is selected in the treeview this function loads the main area of the window with the corresponding form and data. """ slave = self.get_parent().get_slave('workarea') if slave: self.get_parent().detach_slave('workarea') if isinstance(elem, Layer): if self.meta.model_dimension in [1, 3]: self.get_parent().toast('Only 2d supported') return self.undo_stack.start_new_action('Edit Layer %s' % elem.name, elem) form = LayerEditor(elem, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, Meta): self.undo_stack.start_new_action('Edit Meta', elem) meta_form = MetaForm(self.meta, self) self.get_parent().attach_slave('workarea', meta_form) meta_form.focus_toplevel() meta_form.focus_topmost() elif isinstance(elem, OutputList): self.undo_stack.start_new_action('Edit Output', elem) form = OutputForm(self.output_list, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, Parameter): self.undo_stack.start_new_action('Edit Parameter %s' % elem.name, elem) form = ParameterForm(elem, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, Process): if self.meta.model_dimension in [1, 3]: self.get_parent().toast('Only 2d supported') return self.undo_stack.start_new_action('Edit Process %s' % elem.name, elem) form = ProcessForm(elem, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, ProcessList): if self.meta.model_dimension in [1, 3]: self.get_parent().toast('Only 2d supported') return self.undo_stack.start_new_action('Batch process editing', elem) form = BatchProcessForm(self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, Species): self.undo_stack.start_new_action('Edit species', elem) form = SpeciesForm(elem, self.project_data) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, SpeciesList): self.undo_stack.start_new_action('Edit default species', elem) form = SpeciesListForm(elem, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, LayerList): self.undo_stack.start_new_action('Edit lattice', elem) dimension = self.meta.model_dimension form = LatticeForm(elem, dimension, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() else: self.get_parent().toast('Not implemented, yet(%s).' % type(elem))
class EventUI(GladeSlaveDelegate): show_ranges = ['day', 'week', 'month', 'year'] def __init__(self, parent, mo): self.log = logging.getLogger('MINICAL') self.parent = parent self.mo = mo self.factory = Factory() self.__stop_auto_highlight = False # Disable automatic highlighting of events. self.__stop_auto_dayjump = False # Disable automatically jumping to the start of the event on selection. self.__stop_auto_treeview_update = False # FIXME GladeSlaveDelegate.__init__(self, gladefile='mo_tab_events', toplevel_name='window_main') # Set up the user interface eventColumns = [ Column('start', title='Start', data_type=datetime.datetime, sorted=True), Column('end', title='End', data_type=datetime.datetime), Column('summaryformat', title='Summary', use_markup=True), Column('duration', title='Duration', justify=gtk.JUSTIFY_RIGHT) ] self.treeview_event = ObjectTree(eventColumns) self.vbox_eventslist.add(self.treeview_event) self.combobox_display_range.set_active(self.show_ranges.index(self.mo.config['events.default_show'].lower())) cal_options = gtk.CALENDAR_WEEK_START_MONDAY if self.mo.config['events.cal_show_weeknr']: cal_options |= gtk.CALENDAR_SHOW_WEEK_NUMBERS self.calendar.set_display_options((self.calendar.get_display_options() | cal_options)) # Connect signals self.treeview_event.connect('selection-changed', self.treeview_event__selection_changed) self.treeview_event.connect('row-activated', self.treeview_event__row_activated) self.treeview_event.connect('key-press-event', self.treeview_event__key_press_event) self.on_toolbutton_today__clicked() def refresh(self): """ Refresh the entire events tab. This clears everything and rebuilds it. Call this when events are removed outside of this class. """ self.treeview_event.clear() self.calendar.clear_marks() self.on_calendar__month_changed(self.calendar) self.treeview_event__update() def on_toolbutton_add__clicked(self, *args): now = datetime.datetime.now() sel_day = self.calendar.get_date() start = datetime.datetime(sel_day[0], sel_day[1]+1, sel_day[2], now.hour, now.minute) end = start + datetime.timedelta(hours=+1) event = self.factory.event(start, end) event = miniorganizer.ui.EventEditUI(self.mo, event).run() if event: self.mo.cal_model.add(event) self.treeview_event.append(None, event) self.on_calendar__month_changed(self.calendar) self.on_calendar__day_selected(self.calendar) self.parent.menuitem_save.set_sensitive(True) def on_toolbutton_remove__clicked(self, *args): sel_event = self.treeview_event.get_selected() sel_real_event = getattr(sel_event, 'real_event', sel_event) # Delete real event instead of recurring event if sel_event != sel_real_event: response = dialogs.yesno('This is a recurring event. Deleting it will delete all recurrences. Are you sure you want to delete it?') if response == gtk.RESPONSE_NO: return else: sel_event = sel_real_event if sel_event: self.mo.cal_model.delete(sel_event) self.treeview_event.remove(sel_event) self.on_calendar__month_changed(self.calendar) self.on_calendar__day_selected(self.calendar) self.parent.menuitem_save.set_sensitive(True) def on_toolbutton_edit__clicked(self, *args): sel_event = self.treeview_event.get_selected() self.treeview_event__row_activated(self.treeview_event, sel_event) def on_toolbutton_today__clicked(self, *args): today_dt = datetime.date.today() self.calendar.select_month(today_dt.month - 1, today_dt.year) self.calendar.select_day(today_dt.day) def on_calendar__month_changed(self, calendar, *args): self.calendar.clear_marks() sel_date = self.calendar.get_date() month_start = datetime.datetime(sel_date[0], sel_date[1]+1, 1) month_end = month_start + relativedelta(months=+1, seconds=-1) events = self.mo.cal_model.get_events() + self.mo.cal_model.get_events_recurring(month_start, month_end) for event in events: event_start = event.get_start() event_end = event.get_end() self.log.debug('Event %s, start: %s, end %s' % (event.get_summary(), event_start, event_end)) # If the event falls in the month, mark the days the event spans in # the calendar. if (month_start >= event_start and month_start <= event_end) or \ (month_end >= event_start and month_end <= event_end) or \ (event_start >= month_start and event_end <= month_end): # Walk through the days of the event, marking them. delta_iter = datetime.datetime(*event_start.timetuple()[0:3]) while True: if delta_iter.year == month_start.year and delta_iter.month == month_start.month: self.calendar.mark_day(delta_iter.day) delta_iter = delta_iter + datetime.timedelta(days=+1) if delta_iter >= event_end: break def on_calendar__day_selected(self, calendar, *args): # Make sure the correct display range is shown. self.on_combobox_display_range__changed() # Retrieve the day the user selected. sel_day = self.calendar.get_date() day_start = datetime.datetime(sel_day[0], sel_day[1]+1, sel_day[2]) day_end = day_start + datetime.timedelta(days=+1) display_month = datetime.datetime(day_start.year, day_start.month, 1) # Highlight an event if it starts on the selected day. highlight_events = [] events = [event for event in self.treeview_event] for event in events: event_start = event.get_start() event_end = event.get_end() # If this is the first event that starts on the day the user # selected, highlight the item in the list of events. if event_start >= day_start and event_start < day_end: highlight_events.insert(0, event) # If the selected day occurs during an event, highlight it. We # append it to the list of events to be highlighted, so it'll only # be highlighted if no event actually starts on that day. elif (day_start > event_start and day_start < event_end) or \ (day_end > event_start and day_end < event_end) or \ (event_start > day_start and event_end < day_end): highlight_events.append(event) # Highlight the first event on the day the user selected, unless the # user manually selected an event. if not self.__stop_auto_highlight: if highlight_events and highlight_events[0] in self.treeview_event: self.__stop_auto_dayjump = True self.treeview_event.select(highlight_events[0], True) self.__stop_auto_dayjump = False else: self.treeview_event.unselect_all() def on_calendar__day_selected_double_click(self, *args): self.on_toolbutton_add__clicked() def on_combobox_display_range__changed(self, *args): # Get the currently selected date in the calendar. sel_date = self.calendar.get_date() sel_dt_start = datetime.datetime(sel_date[0], sel_date[1]+1, sel_date[2]) sel_dt_end = sel_dt_start + datetime.timedelta(days=+1) # Determine the start and end of the period that needs to be shown. display_range = self.combobox_display_range.get_active_text() if display_range == 'Day': display_start = sel_dt_start display_end = display_start + datetime.timedelta(days=+1, seconds=-1) text = '%s' % (display_start.strftime('%a %b %d %Y')) elif display_range == 'Week': display_start = sel_dt_start + datetime.timedelta(days=-sel_dt_start.weekday()) display_end = display_start + datetime.timedelta(weeks=+1, seconds=-1) text = '%s - %s' % (display_start.strftime('%a %b %d %Y'), display_end.strftime('%a %b %d %Y')) elif display_range == 'Month': display_start = sel_dt_start + datetime.timedelta(days=-(sel_dt_start.day - 1)) display_end = display_start + relativedelta(months=+1, seconds=-1) text = '%s' % (display_start.strftime('%b %Y')) elif display_range == 'Year': display_start = datetime.datetime(sel_dt_start.year, 1, 1) display_end = display_start + relativedelta(years=+1, seconds=-1) text = '%s' % (display_start.strftime('%Y')) else: raise Exception('No selected display range!') # Update the displayed range self.displayed_range.set_text(text) self.display_start = display_start self.display_end = display_end self.treeview_event__update() def treeview_event__update(self): if self.__stop_auto_treeview_update: return # First, remove all the recurring events, because they're generated on # the fly, so we can't know which ones in the list we need to remove. # Therefor we remove them every time. events_rm = [] for event in self.treeview_event: if hasattr(event, 'real_event'): events_rm.append(event) for event in events_rm: self.treeview_event.remove(event) # Add the events for the displayed range to the list events = self.mo.cal_model.get_events() + self.mo.cal_model.get_events_recurring(self.display_start, self.display_end) for event in events: event_start = event.get_start() event_end = event.get_end() # If the currently displayed range includes an event, add it to the list. if (self.display_start >= event_start and self.display_start < event_end) or \ (self.display_end >= event_start and self.display_end < event_end) or \ (event_start >= self.display_start and event_end < self.display_end): if not event in self.treeview_event: self.treeview_event.append(None, event) # Otherwise, we remove it from the list, if it's present. else: if event in self.treeview_event: self.treeview_event.remove(event) def treeview_event__row_activated(self, list, object): # FIXME: This might be more complicated than it needs to be. See todo.py's row_activated. sel_event = self.treeview_event.get_selected() sel_event = getattr(sel_event, 'real_event', sel_event) # Edit real event instead of recurring event event = miniorganizer.ui.EventEditUI(self.mo, sel_event).run() self.on_calendar__month_changed(self.calendar) self.on_calendar__day_selected(self.calendar) if sel_event in self.treeview_event: self.treeview_event.select(sel_event, True) self.parent.menuitem_save.set_sensitive(True) def treeview_event__selection_changed(self, list, selection): # Stop the treeview from automatically updating itself because that # will remove the recurring events and regenerate them (with different # instance IDs) which means the selection may be invalid. self.__stop_auto_treeview_update = True sel_event = self.treeview_event.get_selected() has_selection = sel_event is not None # Enable / disable toolbuttons self.toolbutton_remove.set_sensitive(has_selection) self.toolbutton_edit.set_sensitive(has_selection) # Do not jump to the day of the event. This is needed because an event # can be automatically selected even if it doesn't start on a # particular day. if self.__stop_auto_dayjump: self.__stop_auto_treeview_update = False return # Stop this selection from being overwritten. self.__stop_auto_highlight = True if has_selection: # Make the calendar jump to the day on which this event begins. sel_event_start = sel_event.get_start() self.calendar.select_month(sel_event_start.month - 1, sel_event_start.year) self.calendar.select_day(sel_event_start.day) # Enable automatic highlighting of items self.__stop_auto_highlight = False self.__stop_auto_treeview_update = False def treeview_event__key_press_event(self, treeview, event): if event.keyval == gtk.keysyms.Delete: self.on_toolbutton_remove__clicked()
def __init__(self): ## FIXME: The GUI should appear straight away, connecting should ## happen after! # Set up the main window self.window = gtk.Window() self.window.set_title("PubSub Browser") self.window.set_default_size(400, 400) self.window.connect("destroy", self.quit) # Divide it vertically self.vbox = gtk.VBox() self.window.add(self.vbox) # This holds the location entry and the Get button self.top_box = gtk.HBox() self.vbox.pack_start(self.top_box, expand=False) self.location_label = gtk.Label("Server:") self.top_box.pack_start(self.location_label, expand=False) # This is where the server location is given self.location_entry = gtk.Entry() self.top_box.pack_start(self.location_entry) # This button run get_button_released to fetch the server's nodes self.get_button = gtk.Button(label="Get") self.get_button.connect("released", self.get_button_released) self.top_box.pack_end(self.get_button, expand=False) # Draw the tree using Kiwi, since plain GTK is a pain :P # The attribute is the data, ie. a Column looking for attribute # "foo", when appended by an object bar, will show bar.foo # We can put multiple things into a column (eg. an icon and a # label) by making 2 columns and passing the first to the second self.tree_columns = [\ Column(attribute='icon', title='Nodes', use_stock=True, \ justify=gtk.JUSTIFY_LEFT, icon_size=gtk.ICON_SIZE_MENU), \ Column(attribute='name', justify=gtk.JUSTIFY_LEFT, column='icon')] self.tree_columns[0].expand = False self.tree_columns[1].expand = False self.tree_view = ObjectTree(self.tree_columns) self.tree_view.connect("selection-changed", self.selection_changed) self.vbox.pack_start(self.tree_view) # This holds the Add button and the Delete button self.bottom_box = gtk.HBox() self.vbox.pack_end(self.bottom_box, expand=False) # Make the Delete button, which runs the delete_button_released method self.delete_button = gtk.Button(stock=gtk.STOCK_REMOVE) try: # Attempt to change the label of the stock button delete_label = self.delete_button.get_children()[0] delete_label = delete_label.get_children()[0].get_children()[1] delete_label = delete_label.set_label("Delete Selection") except: # If it fails then just go back to the default self.delete_button = gtk.Button(stock=gtk.STOCK_REMOVE) self.delete_button.connect("released", self.delete_button_released) self.delete_button.set_sensitive(False) self.bottom_box.pack_start(self.delete_button, expand=True) # Make the Properties button, which runs the properties_button_released method self.properties_button = gtk.Button(stock=gtk.STOCK_PROPERTIES) try: # Attempt to change the label of the stock button properties_label = self.properties_button.get_children()[0] properties_label = properties_label.get_children()[0].get_children()[1] properties_label = properties_label.set_label("Node Properties...") except: # If it fails then just go back to the default self.properties_button = gtk.Button(stock=gtk.STOCK_PROPERTIES) self.properties_button.connect("released", self.properties_button_released) self.properties_button.set_sensitive(False) self.bottom_box.pack_start(self.properties_button, expand=True) # Make the Add button, which runs the add_button_released method self.add_button = gtk.Button(stock=gtk.STOCK_ADD) try: # Attempt to change the label of the stock button add_label = self.add_button.get_children()[0] add_label = add_label.get_children()[0].get_children()[1] add_label = add_label.set_label("Add Child...") except: # If it fails then just go back to the default self.add_button = gtk.Button(stock=gtk.STOCK_ADD) self.add_button.connect("released", self.add_button_released) self.add_button.set_sensitive(False) self.bottom_box.pack_end(self.add_button, expand=True) # This handles our XMPP connection. Feel free to change the JID # and password self.client = pubsubclient.PubSubClient("test1@localhost", "test") # Using the tree to store everything seems to have a few # glitches, so we use a regular list as our definitive memory self.known = []
class DetailsTab(gtk.VBox): details_dialog_class = None def __init__(self, model, parent): super(DetailsTab, self).__init__() self.model = model self._parent = parent self.set_spacing(6) self.set_border_width(6) self.klist = ObjectTree(self.get_columns()) self.populate() self.pack_start(self.klist) self.klist.show() if len(self.klist) and self.get_details_dialog_class(): self.button_box = gtk.HButtonBox() self.button_box.set_layout(gtk.BUTTONBOX_START) details_button = gtk.Button(self.details_lbl) self.button_box.pack_start(details_button) details_button.set_sensitive(bool(self.klist.get_selected())) details_button.show() self.pack_end(self.button_box, False, False) self.button_box.show() self.button_box.details_button = details_button details_button.connect('clicked', self._on_details_button__clicked) self.klist.connect('row-activated', self._on_klist__row_activated) self.klist.connect('selection-changed', self._on_klist__selection_changed) self.setup_widgets() def refresh(self): """Refreshes the list of respective tab.""" self.klist.clear() self.klist.add_list(self.populate()) def get_columns(self): """Returns a list of columns this tab should show.""" raise NotImplementedError def show_details(self): """Called when the details button is clicked. Displays the details of the selected object in the list.""" model = self.get_details_model(self.klist.get_selected()) run_dialog(self.get_details_dialog_class(), parent=self._parent, store=self._parent.store, model=model, visual_mode=True) def get_label(self): """Returns the name of the tab.""" label = gtk.Label(self.labels[1]) return label def get_details_model(self, model): """Subclassses can overwrite this method if the details dialog class needs a model different than the one on the list.""" return model def get_details_dialog_class(self): """Subclasses must return the dialog that should be displayed for more information about the item on the list""" return self.details_dialog_class def setup_widgets(self): """Override this if tab needs to do some custom widget setup.""" # # Callbacks # def _on_details_button__clicked(self, button): self.show_details() def _on_klist__row_activated(self, klist, item): self.show_details() def _on_klist__selection_changed(self, klist, data): self.button_box.details_button.set_sensitive(bool(data))
class DokuwikiView(GladeDelegate): """ A dokuwiki editor window """ def __init__(self): GladeDelegate.__init__(self, gladefile="pydoku", delete_handler=self.quit_if_last) self.setup_wikitree() self.setup_attachments() self.setup_side() self.setup_sourceview() self.setup_htmlview() self.page_edit = self.view.notebook1.get_nth_page(0) self.page_view = self.view.notebook1.get_nth_page(1) self.page_attach = self.view.notebook1.get_nth_page(2) self.show_all() def quit_if_last(self, *args): self.htmlview.destroy() # for some reason has to be deleted explicitly GladeDelegate.quit_if_last(self) # general interface functions def post(self, text): id = self.view.statusbar.get_context_id("zap") self.view.statusbar.push(id, text) # setup functions def setup_side(self): columns = ['user', 'sum', 'type', 'version', 'ip'] columns = [Column(s) for s in columns] self.versionlist = ObjectList(columns) self.view.side_vbox.pack_start(gtk.Label('Version Log:'), False, False) self.view.side_vbox.add(self.versionlist) self.view.side_vbox.pack_start(gtk.Label('BackLinks:'), False, False) self.backlinks = ObjectList([Column('name')]) self.view.side_vbox.add(self.backlinks) def setup_attachments(self): columns = ['id', 'size', 'lastModified', 'writable', 'isimg', 'perms'] columns = [Column(s) for s in columns] self.attachmentlist = ObjectList(columns) self.view.attachments_vbox.add(self.attachmentlist) def setup_wikitree(self): columns = ['name', 'id', 'lastModified', 'perms', 'size'] columns = [Column(s) for s in columns] self.objectlist = ObjectTree(columns) self.objectlist.connect("selection-changed", self.selected) self.view.vbox2.add(self.objectlist) def setup_htmlview(self): self.htmlview = gtkmozembed.MozEmbed() self.view.html_scrolledwindow.add(self.htmlview) self.htmlview.realize() self.htmlview.show() def setup_sourceview(self): self.buffer = DokuwikiBuffer(table) self.editor = gtksourceview.SourceView(self.buffer) accel_group = gtk.AccelGroup() self.get_toplevel().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) #self.editor = gtk.TextView(self.buffer) self.editor.set_left_margin(5) self.editor.set_right_margin(5) self.editor.set_wrap_mode(gtk.WRAP_WORD_CHAR) self.view.scrolledwindow1.add(self.editor) # dokuwiki operations def get_version(self): version = self._rpc.dokuwiki.getVersion() self.view.version.set_text(version) def get_pagelist(self): pages = self._rpc.wiki.getAllPages() self._sections = {} self.objectlist.clear() for page in pages: self.add_page(page) self.view.new_page.set_sensitive(True) self.view.delete_page.set_sensitive(True) def get_attachments(self, ns): attachments = self._rpc.wiki.getAttachments(ns, {}) attachments = [DictWrapper(s) for s in attachments] self.attachmentlist.add_list(attachments) def get_backlinks(self, pagename): backlinks = self._rpc.wiki.getBackLinks(pagename) backlinks = [Section(s) for s in backlinks] self.backlinks.add_list(backlinks) def get_versions(self, pagename): versionlist = self._rpc.wiki.getPageVersions(pagename, 0) versionlist = [DictWrapper(s) for s in versionlist] self.versionlist.add_list(versionlist) def get_htmlview(self, pagename): text = self._rpc.wiki.getPageHTML(pagename) self.htmlview.render_data(text, len(text), self.url.get_text(), 'text/html') # XXX following is for gtkhtml (not used) #self.document.clear() #self.document.open_stream('text/html') #self.document.write_stream(text) #self.document.close_stream() def put_page(self, text, summary, minor): pars = {} if summary: pars['sum'] = summary if minor: pars['minor'] = minor self._rpc.wiki.putPage(self.current, text, pars) if not self.current in self._sections: self.add_page({"id":self.current}) # put a page into the page tree def add_page(self, page): name = page["id"] path = name.split(":") prev = None for i,pathm in enumerate(path): if i == len(path)-1: # a page new = DictWrapper(page, pathm) self._sections[name] = new self.objectlist.append(prev, new, False) else: # a namespace part_path = ":".join(path[:i+1]) if not part_path in self._sections: new = Section(pathm, part_path) self._sections[part_path] = new self.objectlist.append(prev, new, False) else: new = self._sections[part_path] prev = new # page selected callback def selected(self, widget, object): if not object: return if isinstance(object, Section): self.get_attachments(object.id) if not isinstance(object, DictWrapper): return text = self._rpc.wiki.getPage(object.id) self.current = object.id self.buffer.add_text(text) self.get_htmlview(self.current) self.get_backlinks(object.id) self.get_versions(object.id) # kiwi interface callbacks def on_view_edit__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_edit, gtk.Label('edit'), 0) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_edit)) def on_view_view__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_view, gtk.Label('view'), 1) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_view)) def on_view_attachments__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_attach, gtk.Label('attach')) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_attach)) def on_view_extra__toggled(self, widget): if widget.get_active(): self.backlinks.show() self.versionlist.show() self.view.hpaned2.set_position(self._prevpos) else: self.backlinks.hide() self.versionlist.hide() self._prevpos = self.view.hpaned2.get_position() self.view.hpaned2.set_position(self.view.hpaned2.allocation.width) def on_button_list__clicked(self, *args): self.post("Connecting...") dialog = ModalDialog("User Details") # prepare widgets = {} items = ["user", "password"] for i,item in enumerate(items): widgets[item] = gtk.Entry() if i == 1: widgets[item].set_visibility(False) hbox = gtk.HBox() hbox.pack_start(gtk.Label(item+': ')) hbox.add(widgets[item]) dialog.vbox.add(hbox) dialog.show_all() # run response = dialog.run() user = widgets['user'].get_text() password = widgets['password'].get_text() dialog.destroy() if not response == gtk.RESPONSE_ACCEPT: return # following commented line is for gtkhtml (not used) #simplebrowser.currentUrl = self.view.url.get_text() # handle response params = urlencode({'u':user,'p':password}) fullurl = self.view.url.get_text() + "/lib/exe/xmlrpc.php?"+ params self._rpc = ServerProxy(fullurl) try: self.get_version() except: self.post("Failure to connect") self.get_pagelist() self.post("Connected") def on_delete_page__clicked(self, *args): dialog = ModalDialog("Are you sure?") response = dialog.run() if response == gtk.RESPONSE_ACCEPT: value = self._sections[self.current] sel = self.objectlist.remove(value) self._rpc.wiki.putPage(self.current, "", {}) self.current = None dialog.destroy() def on_new_page__clicked(self, *args): dialog = ModalDialog("Name for the new page") text_w = gtk.Entry() text_w.show() response = [] dialog.vbox.add(text_w) response = dialog.run() if response == gtk.RESPONSE_ACCEPT: text = text_w.get_text() if text: self.current = text dialog.destroy() def on_button_h1__clicked(self, *args): self.buffer.set_style('h1') def on_button_h2__clicked(self, *args): self.buffer.set_style('h2') def on_button_h3__clicked(self, *args): self.buffer.set_style('h3') def on_button_h4__clicked(self, *args): self.buffer.set_style('h4') def on_button_h5__clicked(self, *args): self.buffer.set_style('h5') def on_button_h6__clicked(self, *args): self.buffer.set_style('h6') def on_button_bold__clicked(self, *args): self.buffer.set_style('bold') def on_button_italic__clicked(self, *args): self.buffer.set_style('italic') def on_button_clear_style__clicked(self, *args): self.buffer.clear_style() def on_button_save__clicked(self, *args): self.post("Saving...") dialog = ModalDialog("Commit message") entry = gtk.Entry() minor = gtk.CheckButton("Minor") dialog.vbox.add(gtk.Label("Your attention to detail\nIs greatly appreciated")) dialog.vbox.add(entry) dialog.vbox.add(minor) dialog.show_all() response = dialog.run() if response == gtk.RESPONSE_ACCEPT: text = self.buffer.process_text() self.put_page(text, entry.get_text(), minor.get_active()) self.get_htmlview(self.current) self.get_versions(self.current) self.post("Saved") dialog.destroy() # unused stuff def request_url(self, document, url, stream): f = simplebrowser.open_url(url) stream.write(f.read()) def setup_htmlview_gtkhtml(self): # XXX not used now self.document = gtkhtml2.Document() self.document.connect('request_url', self.request_url) self.htmlview = gtkhtml2.View() self.htmlview.set_document(self.document) def setup_sourceview_gtksourceview(self): # XXX not used now self.buffer = gtksourceview.Buffer(table) self.editor = gtksourceview.View(self.buffer) if True: self.editor.set_show_line_numbers(True) lm = gtksourceview.LanguageManager() self.editor.set_indent_on_tab(True) self.editor.set_indent_width(4) self.editor.set_property("auto-indent", True) self.editor.set_property("highlight-current-line", True) self.editor.set_insert_spaces_instead_of_tabs(True) lang = lm.get_language("python") self.buffer.set_language(lang) self.buffer.set_highlight_syntax(True)
class Window(object): """This is the browser application.""" def __init__(self): ## FIXME: The GUI should appear straight away, connecting should ## happen after! # Set up the main window self.window = gtk.Window() self.window.set_title("PubSub Browser") self.window.set_default_size(400, 400) self.window.connect("destroy", self.quit) # Divide it vertically self.vbox = gtk.VBox() self.window.add(self.vbox) # This holds the location entry and the Get button self.top_box = gtk.HBox() self.vbox.pack_start(self.top_box, expand=False) self.location_label = gtk.Label("Server:") self.top_box.pack_start(self.location_label, expand=False) # This is where the server location is given self.location_entry = gtk.Entry() self.top_box.pack_start(self.location_entry) # This button run get_button_released to fetch the server's nodes self.get_button = gtk.Button(label="Get") self.get_button.connect("released", self.get_button_released) self.top_box.pack_end(self.get_button, expand=False) # Draw the tree using Kiwi, since plain GTK is a pain :P # The attribute is the data, ie. a Column looking for attribute # "foo", when appended by an object bar, will show bar.foo # We can put multiple things into a column (eg. an icon and a # label) by making 2 columns and passing the first to the second self.tree_columns = [\ Column(attribute='icon', title='Nodes', use_stock=True, \ justify=gtk.JUSTIFY_LEFT, icon_size=gtk.ICON_SIZE_MENU), \ Column(attribute='name', justify=gtk.JUSTIFY_LEFT, column='icon')] self.tree_columns[0].expand = False self.tree_columns[1].expand = False self.tree_view = ObjectTree(self.tree_columns) self.tree_view.connect("selection-changed", self.selection_changed) self.vbox.pack_start(self.tree_view) # This holds the Add button and the Delete button self.bottom_box = gtk.HBox() self.vbox.pack_end(self.bottom_box, expand=False) # Make the Delete button, which runs the delete_button_released method self.delete_button = gtk.Button(stock=gtk.STOCK_REMOVE) try: # Attempt to change the label of the stock button delete_label = self.delete_button.get_children()[0] delete_label = delete_label.get_children()[0].get_children()[1] delete_label = delete_label.set_label("Delete Selection") except: # If it fails then just go back to the default self.delete_button = gtk.Button(stock=gtk.STOCK_REMOVE) self.delete_button.connect("released", self.delete_button_released) self.delete_button.set_sensitive(False) self.bottom_box.pack_start(self.delete_button, expand=True) # Make the Properties button, which runs the properties_button_released method self.properties_button = gtk.Button(stock=gtk.STOCK_PROPERTIES) try: # Attempt to change the label of the stock button properties_label = self.properties_button.get_children()[0] properties_label = properties_label.get_children()[0].get_children()[1] properties_label = properties_label.set_label("Node Properties...") except: # If it fails then just go back to the default self.properties_button = gtk.Button(stock=gtk.STOCK_PROPERTIES) self.properties_button.connect("released", self.properties_button_released) self.properties_button.set_sensitive(False) self.bottom_box.pack_start(self.properties_button, expand=True) # Make the Add button, which runs the add_button_released method self.add_button = gtk.Button(stock=gtk.STOCK_ADD) try: # Attempt to change the label of the stock button add_label = self.add_button.get_children()[0] add_label = add_label.get_children()[0].get_children()[1] add_label = add_label.set_label("Add Child...") except: # If it fails then just go back to the default self.add_button = gtk.Button(stock=gtk.STOCK_ADD) self.add_button.connect("released", self.add_button_released) self.add_button.set_sensitive(False) self.bottom_box.pack_end(self.add_button, expand=True) # This handles our XMPP connection. Feel free to change the JID # and password self.client = pubsubclient.PubSubClient("test1@localhost", "test") # Using the tree to store everything seems to have a few # glitches, so we use a regular list as our definitive memory self.known = [] def selection_changed(self, list, object): self.add_button.set_sensitive(True) if type(object) == type(Node()): self.delete_button.set_sensitive(True) self.properties_button.set_sensitive(True) elif type(object) == type(Server()): self.delete_button.set_sensitive(False) self.properties_button.set_sensitive(False) def get_button_released(self, arg): """This is run when the Get button is pressed. It adds the given server to the tree and runs get_nodes with that server.""" listed_server = False # Assume this server is not listed # Check every row of the tree to see if it matches given server for known_server in self.tree_view: if type(known_server) == type(Server()) and \ self.location_entry.get_text() == str(known_server): listed_server = True # True if server is listed # If we didn't find the server in the tree then add it if not listed_server: server = Server(name=self.location_entry.get_text()) server.icon = gtk.STOCK_NETWORK self.tree_view.append(None, server) self.known.append(server) # Get the PubSub nodes on this server self.client.get_nodes(self.location_entry.get_text(), None, return_function=self.handle_incoming) def handle_incoming(self, nodes): # Go through each new node for node in nodes: # Assume we do not already know about this node node_is_known = False # Go through each node that we know about for known_entry in self.known: # See if this node is the same as the known node being checked if known_entry.name == node.name: ## FIXME: Needs to check server node_is_known = True if not node_is_known: parent = None for known_entry in self.known: if known_entry.name == node.parent.name: ## FIXME: Needs to check server parent = known_entry self.known.append(node) self.tree_view.append(parent, node) node.get_information(self.client, self.handle_information) node.get_sub_nodes(self.client, self.handle_incoming) def handle_information(self, node): known = False if node.type == 'leaf': if node.name is not None and node.server is not None: for known_entry in self.known: if known_entry.name == node.name: known_entry.set_type('leaf') known_entry.icon = gtk.STOCK_FILE elif node.type == 'collection': if node.name is not None and node.server is not None: for known_entry in self.known: if known_entry.name == node.name and known_entry.server == node.server: known_entry.set_type('collection') known_entry.icon = gtk.STOCK_DIRECTORY node.get_sub_nodes(self.client, self.handle_incoming) def handle_node_creation(self, return_value): if return_value == 0: self.tree_view.get_selected().get_sub_nodes(self.client, self.handle_incoming) else: print return_value def delete_button_released(self, args): to_delete = self.tree_view.get_selected() if type(to_delete) == type(Node()): self.client.delete_a_node(to_delete.server, str(to_delete), return_function=self.handle_deleted) def handle_deleted(self, return_value): if return_value == 0: deleted = self.tree_view.get_selected() self.tree_view.remove(deleted) self.known.remove(deleted) else: print return_value def add_button_released(self, args): self.add_window = {} self.add_window["parent"] = self.tree_view.get_selected() self.add_window["window"] = gtk.Window() self.add_window["window"].set_title("Add new node to " + self.add_window["parent"].name) self.add_window["vbox"] = gtk.VBox() self.add_window["window"].add(self.add_window["vbox"]) self.add_window["top_box"] = gtk.HBox() self.add_window["vbox"].pack_start(self.add_window["top_box"], expand=False) self.add_window["name_label"] = gtk.Label("Name:") self.add_window["name_entry"] = gtk.Entry() self.add_window["top_box"].pack_start(self.add_window["name_label"], expand=False) self.add_window["top_box"].pack_end(self.add_window["name_entry"], expand=True) self.add_window["bottom_box"] = gtk.HBox() self.add_window["vbox"].pack_end(self.add_window["bottom_box"], expand=False) self.add_window["add_button"] = gtk.Button(stock=gtk.STOCK_ADD) self.add_window["add_button"].connect("released", self.add_add_released) self.add_window["bottom_box"].pack_end(self.add_window["add_button"], expand=False) self.add_window["middle_box"] = gtk.HBox() self.add_window["vbox"].pack_end(self.add_window["middle_box"], expand=False) self.add_window["type_label"] = gtk.Label("Type:") self.add_window["type_select"] = gtk.combo_box_new_text() self.add_window["type_select"].append_text("Leaf") self.add_window["type_select"].append_text("Collection") self.add_window["middle_box"].pack_start(self.add_window["type_label"], expand=False) self.add_window["middle_box"].pack_end(self.add_window["type_select"], expand=True) self.add_window["window"].show_all() def add_add_released(self, args): name = self.add_window["name_entry"].get_text() node_type = self.add_window["type_select"].get_active_text() parent = self.add_window["parent"] self.add_node(name, node_type, parent) self.add_window["window"].destroy() del(self.add_window) def add_node(self, name, node_type, parent): """Request a new child node of parent. For top-level a node parent should be a Server. node_type is either "leaf" or "collection".""" # This half runs if the parent is a Server if type(parent) == type(pubsubclient.Server()): # Request a top-level leaf node if node_type == 'Leaf': self.client.get_new_leaf_node(parent, \ self.add_window["name_entry"].get_text(), None, None, return_function=self.handle_node_creation) # Request a top-level collection node elif node_type == 'Collection': self.client.get_new_collection_node(parent, \ self.add_window["name_entry"].get_text(), None, None, return_function=self.handle_node_creation) # This half runs if the parent is a Node elif type(parent) == type(pubsubclient.Node()): # Request a child leaf node if node_type == 'Leaf': self.client.get_new_leaf_node(parent.server, \ self.add_window["name_entry"].get_text(), parent, None, return_function=self.handle_node_creation) # Request a child collection node elif node_type == 'Collection': self.client.get_new_collection_node(parent.server, \ self.add_window["name_entry"].get_text(), parent, None, return_function=self.handle_node_creation) def properties_button_released(self, args): ## FIXME: This should allow multiple properties windows to be open at once # Setup a window containing a notebook self.properties_window = {} self.properties_window["node"] = self.tree_view.get_selected() self.properties_window["window"] = gtk.Window() self.properties_window["window"].set_title("Properties of " + str(self.properties_window["node"])) self.properties_window["window"].set_default_size(350, 450) self.properties_window["notebook"] = gtk.Notebook() self.properties_window["window"].add(self.properties_window["notebook"]) # Add the Metadata page self.properties_window["metadata_page"] = gtk.VBox() self.properties_window["metadata_label"] = gtk.Label("Metadata") self.properties_window["notebook"].append_page(self.properties_window["metadata_page"], tab_label=self.properties_window["metadata_label"]) # Add the Affiliations page self.properties_window["affiliations_page"] = gtk.VBox() self.properties_window["affiliations_label"] = gtk.Label("Affiliations") self.properties_window["notebook"].append_page(self.properties_window["affiliations_page"], tab_label=self.properties_window["affiliations_label"]) node = self.tree_view.get_selected() node.get_information(self.client, self.information_received) node.request_all_affiliated_entities(self.client, return_function=self.properties_received) def information_received(self, info_dict): # Generate the contents of the Metadata page ## FIXME: It would be awesome if this were generated automatically from what is found :) # The Name entry self.properties_window["name_box"] = gtk.HBox() self.properties_window["metadata_page"].pack_start(self.properties_window["name_box"], expand=False) self.properties_window["name_label"] = gtk.Label("Name:") self.properties_window["name_box"].pack_start(self.properties_window["name_label"], expand=False) self.properties_window["name_entry"] = gtk.Entry() self.properties_window["name_entry"].set_text(str(self.properties_window["node"])) # Default to Node's name self.properties_window["name_box"].pack_start(self.properties_window["name_entry"], expand=True) self.properties_window["name_set"] = gtk.Button(label="Set") self.properties_window["name_set"].connect("released", self.set_name) self.properties_window["name_box"].pack_end(self.properties_window["name_set"], expand=False) # The Title entry ## FIXME: This should default to the node's title self.properties_window["title_box"] = gtk.HBox() self.properties_window["metadata_page"].pack_start(self.properties_window["title_box"], expand=False) self.properties_window["title_label"] = gtk.Label("Title:") self.properties_window["title_box"].pack_start(self.properties_window["title_label"], expand=False) self.properties_window["title_entry"] = gtk.Entry() self.properties_window["title_box"].pack_start(self.properties_window["title_entry"], expand=True) self.properties_window["title_set"] = gtk.Button(label="Set") self.properties_window["title_set"].connect("released", self.set_title) self.properties_window["title_box"].pack_end(self.properties_window["title_set"], expand=False) def properties_received(self, affiliation_dictionary): # Add a warning about affiliation status self.properties_window["warning_label"] = gtk.Label("Note that a Jabber ID can only be in one state at a time. Adding a Jabber ID to a category will remove it from the others. There must always be at least one owner.") self.properties_window["warning_label"].set_line_wrap(True) self.properties_window["affiliations_page"].pack_start(self.properties_window["warning_label"], expand=False) # Owners frame self.properties_window["owners_frame"] = gtk.Frame("Owners") self.properties_window["affiliations_page"].pack_start(self.properties_window["owners_frame"], expand=True) self.properties_window["owners_box"] = gtk.HBox() self.properties_window["owners_frame"].add(self.properties_window["owners_box"]) # Owners list self.properties_window["owners_column"] = Column(attribute="name", title="Jabber ID") self.properties_window["owners"] = ObjectList([self.properties_window["owners_column"]]) self.properties_window["owners_box"].pack_start(self.properties_window["owners"], expand=True) # Add Owner button self.properties_window["owners_buttons"] = gtk.VBox() self.properties_window["owners_box"].pack_end(self.properties_window["owners_buttons"], expand=False) self.properties_window["add_owner"] = gtk.Button(stock=gtk.STOCK_ADD) try: # Attempt to change the label of the stock button label = self.properties_window["add_owner"].get_children()[0] label = label.get_children()[0].get_children()[1] label = label.set_label("Add...") except: # If it fails then just go back to the default self.properties_window["add_owner"] = gtk.Button(stock=gtk.STOCK_ADD) self.properties_window["add_owner"].connect("released", self.add_owner) self.properties_window["owners_buttons"].pack_start(self.properties_window["add_owner"], expand=False) # Remove Owner button self.properties_window["remove_owner"] = gtk.Button(stock=gtk.STOCK_REMOVE) self.properties_window["remove_owner"].connect("released", self.remove_owner) self.properties_window["owners_buttons"].pack_end(self.properties_window["remove_owner"], expand=False) # Publishers frame self.properties_window["publishers_frame"] = gtk.Frame("Publishers") self.properties_window["affiliations_page"].pack_start(self.properties_window["publishers_frame"], expand=True) self.properties_window["publishers_box"] = gtk.HBox() self.properties_window["publishers_frame"].add(self.properties_window["publishers_box"]) # Add Publisher button self.properties_window["publishers_buttons"] = gtk.VBox() self.properties_window["publishers_box"].pack_end(self.properties_window["publishers_buttons"], expand=False) self.properties_window["add_publisher"] = gtk.Button(stock=gtk.STOCK_ADD) try: # Attempt to change the label of the stock button label = self.properties_window["add_publisher"].get_children()[0] label = label.get_children()[0].get_children()[1] label = label.set_label("Add...") except: # If it fails then just go back to the default self.properties_window["add_publisher"] = gtk.Button(stock=gtk.STOCK_ADD) self.properties_window["add_publisher"].connect("released", self.add_publisher) self.properties_window["publishers_buttons"].pack_start(self.properties_window["add_publisher"], expand=False) # Remove Publisher button self.properties_window["remove_publisher"] = gtk.Button(stock=gtk.STOCK_REMOVE) self.properties_window["remove_publisher"].connect("released", self.remove_publisher) self.properties_window["publishers_buttons"].pack_end(self.properties_window["remove_publisher"], expand=False) # Publishers list self.properties_window["publishers_column"] = Column(attribute="name", title="Jabber ID") self.properties_window["publishers"] = ObjectList([self.properties_window["publishers_column"]]) self.properties_window["publishers_box"].pack_start(self.properties_window["publishers"], expand=True) # Outcasts frame self.properties_window["outcasts_frame"] = gtk.Frame("Outcasts") self.properties_window["affiliations_page"].pack_start(self.properties_window["outcasts_frame"], expand=True) self.properties_window["outcasts_box"] = gtk.HBox() self.properties_window["outcasts_frame"].add(self.properties_window["outcasts_box"]) # Add Outcast button self.properties_window["outcasts_buttons"] = gtk.VBox() self.properties_window["outcasts_box"].pack_end(self.properties_window["outcasts_buttons"], expand=False) self.properties_window["add_outcast"] = gtk.Button(stock=gtk.STOCK_ADD) try: # Attempt to change the label of the stock button label = self.properties_window["add_outcast"].get_children()[0] label = label.get_children()[0].get_children()[1] label = label.set_label("Add...") except: # If it fails then just go back to the default self.properties_window["add_outcast"] = gtk.Button(stock=gtk.STOCK_ADD) self.properties_window["add_outcast"].connect("released", self.add_outcast) self.properties_window["outcasts_buttons"].pack_start(self.properties_window["add_outcast"], expand=False) # Remove Outcast button self.properties_window["remove_outcast"] = gtk.Button(stock=gtk.STOCK_REMOVE) self.properties_window["remove_outcast"].connect("released", self.remove_outcast) self.properties_window["outcasts_buttons"].pack_end(self.properties_window["remove_outcast"], expand=False) # Outcasts list self.properties_window["outcasts_column"] = Column(attribute="name", title="Jabber ID") self.properties_window["outcasts"] = ObjectList([self.properties_window["outcasts_column"]]) self.properties_window["outcasts_box"].pack_start(self.properties_window["outcasts"], expand=True) self.properties_window["window"].show_all() if "owner" in affiliation_dictionary.keys(): for jid in affiliation_dictionary["owner"]: self.properties_window["owners"].append(jid) if "publisher" in affiliation_dictionary.keys(): for jid in affiliation_dictionary["publisher"]: self.properties_window["publishers"].append(jid) if "outcast" in affiliation_dictionary.keys(): for jid in affiliation_dictionary["outcast"]: self.properties_window["outcasts"].append(jid) def set_name(self, args): pass def set_title(self, args): pass def add_owner(self, args): self.add_owner_window = {} self.add_owner_window["window"] = gtk.Window() self.add_owner_window["window"].set_title("Add owner to " + str(self.tree_view.get_selected())) self.add_owner_window["box"] = gtk.HBox() self.add_owner_window["window"].add(self.add_owner_window["box"]) self.add_owner_window["jid_label"] = gtk.Label("Jabber ID:") self.add_owner_window["box"].pack_start(self.add_owner_window["jid_label"], expand=False) self.add_owner_window["jid_entry"] = gtk.Entry() self.add_owner_window["box"].pack_start(self.add_owner_window["jid_entry"], expand=False) self.add_owner_window["add_button"] = gtk.Button(stock=gtk.STOCK_ADD) self.add_owner_window["add_button"].connect("released", self.add_owner_send) self.add_owner_window["box"].pack_end(self.add_owner_window["add_button"], expand=False) self.add_owner_window["window"].show_all() def add_owner_send(self, args): self.add_owner_window["jid"] = JID(self.add_owner_window["jid_entry"].get_text()) self.tree_view.get_selected().modify_affiliations(self.client, {self.add_owner_window["jid"]:"owner"}, self.owner_added) def owner_added(self, reply): if reply == 0: self.properties_window["owners"].append(self.add_owner_window["jid"]) for jid in self.properties_window["publishers"]: if str(jid) == str(self.add_owner_window["jid"]): self.properties_window["publishers"].remove(jid) for jid in self.properties_window["outcasts"]: if str(jid) == str(self.add_owner_window["jid"]): self.properties_window["outcasts"].remove(jid) else: print "Error" self.add_owner_window["window"].destroy() del self.add_owner_window def remove_owner(self, args): self.tree_view.get_selected().modify_affiliations(self.client, {self.properties_window["owners"].get_selected():"none"}, self.owner_removed) def owner_removed(self, reply): if reply == 0: self.properties_window["owners"].remove(self.properties_window["owners"].get_selected()) else: print "Error" def add_publisher(self, args): self.add_publisher_window = {} self.add_publisher_window["window"] = gtk.Window() self.add_publisher_window["window"].set_title("Add publisher to " + str(self.tree_view.get_selected())) self.add_publisher_window["hbox"] = gtk.HBox() self.add_publisher_window["window"].add(self.add_publisher_window["hbox"]) self.add_publisher_window["jid_label"] = gtk.Label("Jabber ID:") self.add_publisher_window["hbox"].pack_start(self.add_publisher_window["jid_label"], expand=False) self.add_publisher_window["jid_entry"] = gtk.Entry() self.add_publisher_window["hbox"].pack_start(self.add_publisher_window["jid_entry"], expand=False) self.add_publisher_window["add_button"] = gtk.Button(stock=gtk.STOCK_ADD) self.add_publisher_window["add_button"].connect("released", self.add_publisher_send) self.add_publisher_window["hbox"].pack_end(self.add_publisher_window["add_button"], expand=False) self.add_publisher_window["window"].show_all() def add_publisher_send(self, args): self.add_publisher_window["jid"] = JID(self.add_publisher_window["jid_entry"].get_text()) self.tree_view.get_selected().modify_affiliations(self.client, {self.add_publisher_window["jid"]:"publisher"}, self.publisher_added) def publisher_added(self, reply): if reply == 0: self.properties_window["publishers"].append(self.add_publisher_window["jid"]) for jid in self.properties_window["owners"]: if str(jid) == str(self.add_publisher_window["jid"]): self.properties_window["owners"].remove(jid) for jid in self.properties_window["outcasts"]: if str(jid) == str(self.add_publisher_window["jid"]): self.properties_window["outcasts"].remove(jid) else: print "Error" self.add_publisher_window["window"].destroy() del self.add_publisher_window def remove_publisher(self, args): self.tree_view.get_selected().modify_affiliations(self.client, {self.properties_window["publishers"].get_selected():"none"}, self.publisher_removed) def publisher_removed(self, reply): if reply == 0: self.properties_window["publishers"].remove(self.properties_window["publishers"].get_selected()) else: print "Error" def add_outcast(self, args): self.add_outcast_window = {} self.add_outcast_window["window"] = gtk.Window() self.add_outcast_window["window"].set_title("Add outcast to " + str(self.tree_view.get_selected())) self.add_outcast_window["hbox"] = gtk.HBox() self.add_outcast_window["window"].add(self.add_outcast_window["hbox"]) self.add_outcast_window["jid_label"] = gtk.Label("Jabber ID:") self.add_outcast_window["hbox"].pack_start(self.add_outcast_window["jid_label"], expand=False) self.add_outcast_window["jid_entry"] = gtk.Entry() self.add_outcast_window["hbox"].pack_start(self.add_outcast_window["jid_entry"], expand=False) self.add_outcast_window["add_button"] = gtk.Button(stock=gtk.STOCK_ADD) self.add_outcast_window["add_button"].connect("released", self.add_outcast_send) self.add_outcast_window["hbox"].pack_end(self.add_outcast_window["add_button"], expand=False) self.add_outcast_window["window"].show_all() def add_outcast_send(self, args): self.add_outcast_window["jid"] = JID(self.add_outcast_window["jid_entry"].get_text()) self.tree_view.get_selected().modify_affiliations(self.client, {self.add_outcast_window["jid"]:"outcast"}, self.outcast_added) def outcast_added(self, reply): if reply == 0: self.properties_window["outcasts"].append(self.add_outcast_window["jid"]) for jid in self.properties_window["publishers"]: if str(jid) == str(self.add_outcast_window["jid"]): self.properties_window["publishers"].remove(jid) for jid in self.properties_window["owners"]: if str(jid) == str(self.add_outcast_window["jid"]): self.properties_window["owners"].remove(jid) else: print "Error" self.add_outcast_window["window"].destroy() del self.add_outcast_window def remove_outcast(self, args): self.tree_view.get_selected().modify_affiliations(self.client, {self.properties_window["outcasts"].get_selected():"none"}, self.outcast_removed) def outcast_removed(self, reply): if reply == 0: self.properties_window["outcasts"].remove(self.properties_window["outcasts"].get_selected()) else: print "Error" def main(self): self.window.show_all() self.client.connect() #gobject.idle_add(self.idle_process, priority=gobject.PRIORITY_LOW) gobject.timeout_add(250, self.idle_process) gtk.main() def idle_process(self): """A PubSubClient needs its process method run regularly. This method can be used to do that in gobject.timeout or idle_add.""" self.client.process() return True def quit(self, arg): """Exits the application. Runs when a signal to quit is received.""" gtk.main_quit()
def __init__(self, columns): ObjectTree.__init__(self, columns)
class TreeDataTests(unittest.TestCase): def setUp(self): self.win = gtk.Window() self.win.set_default_size(400, 400) self.tree = ObjectTree([Column('name'), Column('age')]) self.win.add(self.tree) refresh_gui() def tearDown(self): self.win.destroy() del self.win def testGetRoot(self): root = Person('Big Kahuna', 7000) child1 = Person('Craf Kahuna', 200) child2 = Person('Sorcerer Kahuna', 150) self.tree.append(None, root) self.tree.append(root, child1) self.tree.append(root, child2) test_root = self.tree.get_root(child1) self.assertEqual(test_root, root) test_root = self.tree.get_root(child2) self.assertEqual(test_root, root) test_root = self.tree.get_root(root) self.assertEqual(test_root, root) def testGetDescendants(self): root = Person('Big Kahuna', 7000) child1 = Person('Craf Kahuna', 200) child2 = Person('Sorcerer Kahuna', 150) self.tree.append(None, root) self.tree.append(root, child1) self.tree.append(child1, child2) test_descendants = self.tree.get_descendants(root) test_descendants.sort() self.assertEqual(test_descendants, [child1, child2]) test_descendants = self.tree.get_descendants(child1) self.assertEqual(test_descendants, [child2]) test_descendants = self.tree.get_descendants(child2) self.assertEqual(test_descendants, [])
class GTKProject(SlaveDelegate): """A facade of kmos.types.Project so that pygtk can display in a TreeView. """ def __init__(self, parent, menubar): self.project_data = ObjectTree([Column('name', use_markup=True, data_type=str, sorted=True), Column('info')]) self.project_data.connect('row-activated', self.on_row_activated) self.model_tree = Project() self._set_treeview_hooks() self.menubar = menubar self.set_parent(parent) self.filename = '' self.undo_stack = UndoStack( self.model_tree.__repr__, self.import_xml_file, self.project_data.select, menubar, self.meta, 'Initialization') SlaveDelegate.__init__(self, toplevel=self.project_data) def _set_treeview_hooks(self): """Fudge function to import to access function to kmos.types.Project to kmos.gui.GTKProject. """ self.project_data.clear() # Meta self.meta = self.project_data.append(None, self.model_tree.meta) self.model_tree.meta = self.meta # Layer List self.model_tree.add_layer = self.add_layer self.layer_list = self.project_data.append(None, self.model_tree.layer_list) self.get_layers = lambda :\ sorted(self.project_data.get_descendants(self.layer_list), key=lambda x: x.name) self.model_tree.get_layers = self.get_layers self.lattice = self.layer_list # Parameter List self.parameter_list = self.project_data.append(None, self.model_tree.parameter_list) self.add_parameter = lambda parameter :\ self.project_data.append(self.parameter_list, parameter) self.model_tree.add_parameter = self.add_parameter self.get_parameters = lambda :\ sorted(self.project_data.get_descendants(self.parameter_list), key=lambda x: x.name) self.model_tree.get_parameters = self.get_parameters # Species List self.species_list = self.project_data.append(None, self.model_tree.species_list) self.add_species = lambda species :\ self.project_data.append(self.species_list, species) self.model_tree.add_species = self.add_species self.get_speciess = lambda :\ sorted(self.project_data.get_descendants(self.species_list), key=lambda x: x.name) self.model_tree.get_speciess = self.get_speciess # Process List self.process_list = self.project_data.append(None, self.model_tree.process_list) self.add_process = lambda process:\ self.project_data.append(self.process_list, process) self.model_tree.add_process = self.add_process self.get_processes = lambda :\ sorted(self.project_data.get_descendants(self.process_list), key=lambda x: x.name) self.model_tree.get_processes = self.get_processes # Output List self.output_list = self.project_data.append(None, self.model_tree.output_list) self.add_output = lambda output:\ self.project_data.append(self.output_list, output) self.model_tree.add_output = self.add_output self.get_outputs = lambda : \ sorted(self.project_data.get_descendants(self.output_list), key=lambda x: x.name) self.model_tree.get_outputs = self.get_outputs def add_layer(self, layer): self.project_data.append(self.layer_list, layer) if len(self.get_layers()) == 1 : self.set_default_layer(layer.name) self.set_substrate_layer(layer.name) def set_default_species(self, species): self.model_tree.species_list.default_species = species def set_substrate_layer(self, layer): self.model_tree.layer_list.substrate_layer = layer def set_default_layer(self, layer): self.model_tree.layer_list.default_layer = layer def update(self, model): self.project_data.update(model) def on_row_activated(self, tree, data): if isinstance(data, Layer): data.active = not data.active def get_name(self): if self.filename: return os.path.basename(self.filename) else: return 'Untitled' def __repr__(self): return str(self.model_tree) def import_xml_file(self, filename): self.model_tree.import_xml_file(filename) self.expand_all() def expand_all(self): """Expand all list of the project tree """ self.project_data.expand(self.species_list) self.project_data.expand(self.layer_list) self.project_data.expand(self.parameter_list) self.project_data.expand(self.process_list) self.project_data.expand(self.output_list) def on_key_press(self, _, event): """When the user hits the keyboard focusing the treeview this event is triggered. Right now the only supported function is to deleted the selected item """ selection = self.project_data.get_selected() if gtk.gdk.keyval_name(event.keyval) == 'Delete': if(isinstance(selection, Species) or isinstance(selection, Process) or isinstance(selection, Parameter) or isinstance(selection, Layer)): if kiwi.ui.dialogs.yesno( "Do you really want to delete '%s'?" \ % selection.name) == gtk.RESPONSE_YES: self.project_data.remove(selection) def on_project_data__selection_changed(self, _, elem): """When a new item is selected in the treeview this function loads the main area of the window with the corresponding form and data. """ slave = self.get_parent().get_slave('workarea') if slave: self.get_parent().detach_slave('workarea') if isinstance(elem, Layer): if self.meta.model_dimension in [1, 3]: self.get_parent().toast('Only 2d supported') return self.undo_stack.start_new_action('Edit Layer %s' % elem.name, elem) form = LayerEditor(elem, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, Meta): self.undo_stack.start_new_action('Edit Meta', elem) meta_form = MetaForm(self.meta, self) self.get_parent().attach_slave('workarea', meta_form) meta_form.focus_toplevel() meta_form.focus_topmost() elif isinstance(elem, OutputList): self.undo_stack.start_new_action('Edit Output', elem) form = OutputForm(self.output_list, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, Parameter): self.undo_stack.start_new_action('Edit Parameter %s' % elem.name, elem) form = ParameterForm(elem, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, Process): if self.meta.model_dimension in [1, 3]: self.get_parent().toast('Only 2d supported') return self.undo_stack.start_new_action('Edit Process %s' % elem.name, elem) form = ProcessForm(elem, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, ProcessList): if self.meta.model_dimension in [1, 3]: self.get_parent().toast('Only 2d supported') return self.undo_stack.start_new_action('Batch process editing', elem) form = BatchProcessForm(self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, Species): self.undo_stack.start_new_action('Edit species', elem) form = SpeciesForm(elem, self.project_data) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, SpeciesList): self.undo_stack.start_new_action('Edit default species', elem) form = SpeciesListForm(elem, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() elif isinstance(elem, LayerList): self.undo_stack.start_new_action('Edit lattice', elem) dimension = self.meta.model_dimension form = LatticeForm(elem, dimension, self) self.get_parent().attach_slave('workarea', form) form.focus_topmost() else: self.get_parent().toast('Not implemented, yet(%s).' % type(elem))
class TreeDataTests(unittest.TestCase): def setUp(self): self.win = Gtk.Window() self.win.set_default_size(400, 400) self.tree = ObjectTree([Column('name'), Column('age')]) self.win.add(self.tree) refresh_gui() def tearDown(self): self.win.destroy() del self.win def testGetRoot(self): root = Person('Big Kahuna', 7000) child1 = Person('Craf Kahuna', 200) child2 = Person('Sorcerer Kahuna', 150) self.tree.append(None, root) self.tree.append(root, child1) self.tree.append(root, child2) test_root = self.tree.get_root(child1) self.assertEqual(test_root, root) test_root = self.tree.get_root(child2) self.assertEqual(test_root, root) test_root = self.tree.get_root(root) self.assertEqual(test_root, root) def testGetDescendants(self): root = Person('Big Kahuna', 7000) child1 = Person('Craf Kahuna', 200) child2 = Person('Sorcerer Kahuna', 150) self.tree.append(None, root) self.tree.append(root, child1) self.tree.append(child1, child2) test_descendants = self.tree.get_descendants(root) self.assertTrue(child1 in test_descendants) self.assertTrue(child2 in test_descendants) test_descendants = self.tree.get_descendants(child1) self.assertEqual(test_descendants, [child2]) test_descendants = self.tree.get_descendants(child2) self.assertEqual(test_descendants, [])
def setUp(self): self.win = Gtk.Window() self.win.set_default_size(400, 400) self.tree = ObjectTree([Column('name'), Column('age')]) self.win.add(self.tree) refresh_gui()
class NoteUI(GladeSlaveDelegate): def __init__(self, parent, mo): self.parent = parent self.mo = mo self.factory = Factory() # Set up the user interface GladeSlaveDelegate.__init__(self, gladefile="mo_tab_notes", toplevel_name="window_main") noteColumns = [ Column("summary", title='Title', data_type=str), ] #self.treeview_note = ObjectList(noteColumns) self.treeview_note = ObjectTree(noteColumns) self.vbox_notelist.add(self.treeview_note) # Connect signals self.treeview_note.connect('row-activated', self.treeview_note__row_activated) self.treeview_note.connect('selection-changed', self.treeview_note__selection_changed) self.treeview_note.connect('key-press-event', self.treeview_note__key_press_event) self.refresh() def refresh(self): self.treeview_note.clear() for journal in self.mo.cal_model.get_journals(): parent = self.mo.cal_model.get_model_by_uid(journal.get_related_to()) self.treeview_note.append(parent, journal) def on_toolbutton_add__clicked(self, *args): journal = self.factory.journal() journal = miniorganizer.ui.NoteEditUI(self.mo, journal).run() if journal: self.mo.cal_model.add(journal) self.treeview_note.append(None, journal) self.parent.menuitem_save.set_sensitive(True) def on_toolbutton_addsub__clicked(self, *args): parent_journal = self.treeview_note.get_selected() journal = self.factory.journal(parent_journal) if miniorganizer.ui.NoteEditUI(self.mo, journal).run(): self.mo.cal_model.add(journal) self.treeview_note.append(parent_journal, journal) self.treeview_note.expand(parent_journal) self.parent.menuitem_save.set_sensitive(True) def on_toolbutton_edit__clicked(self, *args): sel_note = self.treeview_note.get_selected() self.treeview_note__row_activated(self.treeview_note, sel_note) def on_toolbutton_remove__clicked(self, *args): sel_note = self.treeview_note.get_selected() if sel_note: children = self.mo.cal_model.get_models_related_by_uid(sel_note.get_uid()) if children: response = dialogs.warning('This note contains sub-notes. Removing it will also remove the sub-notes. Is this what you want?', buttons=gtk.BUTTONS_YES_NO) if response != gtk.RESPONSE_YES: return self.mo.cal_model.delete(sel_note, True) self.treeview_note.remove(sel_note, True) self.parent.menuitem_save.set_sensitive(True) def treeview_note__selection_changed(self, list, selection): has_selection = selection is not None self.toolbutton_addsub.set_sensitive(has_selection) self.toolbutton_remove.set_sensitive(has_selection) self.toolbutton_edit.set_sensitive(has_selection) def treeview_note__row_activated(self, list, object): sel_note = self.treeview_note.get_selected() note = miniorganizer.ui.NoteEditUI(self.mo, sel_note).run() def treeview_note__key_press_event(self, treeview, event): if event.keyval == gtk.keysyms.Delete: self.on_toolbutton_remove__clicked()
class DetailsTab(Gtk.VBox): details_dialog_class = None def __init__(self, model, parent): super(DetailsTab, self).__init__() self.model = model self._parent = parent self.set_spacing(6) self.set_border_width(6) self.klist = ObjectTree(self.get_columns()) self.populate() self.pack_start(self.klist, True, True, 0) self.klist.show() if len(self.klist) and self.get_details_dialog_class(): self.button_box = Gtk.HButtonBox() self.button_box.set_layout(Gtk.ButtonBoxStyle.START) details_button = Gtk.Button.new_with_label(self.details_lbl) self.button_box.pack_start(details_button, True, True, 0) details_button.set_sensitive(bool(self.klist.get_selected())) details_button.show() self.pack_end(self.button_box, False, False, 0) self.button_box.show() self.button_box.details_button = details_button details_button.connect('clicked', self._on_details_button__clicked) self.klist.connect('row-activated', self._on_klist__row_activated) self.klist.connect('selection-changed', self._on_klist__selection_changed) self.setup_widgets() def refresh(self): """Refreshes the list of respective tab.""" self.klist.clear() self.klist.add_list(self.populate()) def get_columns(self): """Returns a list of columns this tab should show.""" raise NotImplementedError def show_details(self): """Called when the details button is clicked. Displays the details of the selected object in the list.""" model = self.get_details_model(self.klist.get_selected()) run_dialog(self.get_details_dialog_class(), parent=self._parent, store=self._parent.store, model=model, visual_mode=True) def get_label(self): """Returns the name of the tab.""" label = Gtk.Label(label=self.labels[1]) return label def get_details_model(self, model): """Subclassses can overwrite this method if the details dialog class needs a model different than the one on the list.""" return model def get_details_dialog_class(self): """Subclasses must return the dialog that should be displayed for more information about the item on the list""" return self.details_dialog_class def setup_widgets(self): """Override this if tab needs to do some custom widget setup.""" # # Callbacks # def _on_details_button__clicked(self, button): self.show_details() def _on_klist__row_activated(self, klist, item): self.show_details() def _on_klist__selection_changed(self, klist, data): self.button_box.details_button.set_sensitive(bool(data))
class AdditionListSlave(SearchSlave): """A slave that offers a simple list and its management. This slave also has the option to display a small message right next to the buttons """ domain = 'stoq' toplevel_name = gladefile = 'AdditionListSlave' widgets = ('add_button', 'delete_button', 'klist', 'list_vbox', 'edit_button') gsignal('before-edit-item', object, retval=object) gsignal('on-edit-item', object) gsignal('on-add-item', object) gsignal('before-delete-items', object) gsignal('after-delete-items') def __init__(self, store, columns=None, editor_class=None, klist_objects=None, visual_mode=False, restore_name=None, tree=False): """ Creates a new AdditionListSlave object :param store: a store :param columns: column definitions :type columns: sequence of :class:`kiwi.ui.objectlist.Columns` :param editor_class: the window that is going to be open when user clicks on add_button or edit_button. :type: editor_class: a :class:`stoqlib.gui.editors.BaseEditor` subclass :param klist_objects: initial objects to insert into the list :param visual_mode: if we are working on visual mode, that means, not possible to edit the model on this object type visual_mode: bool :param restore_name: the name used to save and restore the columns on a cache system (e.g. pickle) :type restore_name: basestring :param tree: Indication of which kind of list we are adding. If `True` ObjectTree otherwise ObjectList will be added """ columns = columns or self.get_columns() SearchSlave.__init__(self, columns=columns, restore_name=restore_name, store=store) self.tree = tree self.klist = ObjectTree() if tree else ObjectList() self.list_vbox.add(self.klist) self.list_vbox.show_all() if not self.columns: raise StoqlibError("columns must be specified") self.visual_mode = visual_mode self.store = store self.set_editor(editor_class) self._can_edit = True self._callback_id = None if self.visual_mode: self.hide_add_button() self.hide_edit_button() self.hide_del_button() items = klist_objects or self.get_items() self._setup_klist(items) self._update_sensitivity() def _setup_klist(self, klist_objects): self.klist.set_columns(self.columns) self.klist.set_selection_mode(gtk.SELECTION_MULTIPLE) if self.tree: (self.klist.append(obj.parent_item, obj) for obj in klist_objects) else: self.klist.add_list(klist_objects) if self.visual_mode: self.klist.set_sensitive(False) def _update_sensitivity(self, *args): if self.visual_mode: return can_delete = _can_edit = True objs = self.get_selection() if not objs: _can_edit = can_delete = False elif len(objs) > 1: _can_edit = False self.add_button.set_sensitive(True) self.edit_button.set_sensitive(_can_edit) self.delete_button.set_sensitive(can_delete) def _edit_model(self, model=None, parent=None): edit_mode = model result = self.emit('before-edit-item', model) if result is None: result = self.run_editor(model) if not result: return if edit_mode: self.emit('on-edit-item', result) self.klist.update(result) else: if self.tree: self.klist.append(parent, result) else: self.klist.append(result) # Emit the signal after we added the item to the list to be able to # check the length of the list in our validation callbacks. self.emit('on-add-item', result) # As we have a selection extended mode for kiwi list, we # need to unselect everything before select the new instance. self.klist.unselect_all() self.klist.select(result) self._update_sensitivity() def _edit(self): if not self._can_edit: return objs = self.get_selection() qty = len(objs) if qty != 1: raise SelectionError( ("Please select only one item before choosing Edit." "\nThere are currently %d items selected") % qty) self._edit_model(objs[0]) def _clear(self): objs = self.get_selection() qty = len(objs) if qty < 1: raise SelectionError('There are no objects selected') msg = stoqlib_ngettext( _('Delete this item?'), _('Delete these %d items?') % qty, qty) delete_label = stoqlib_ngettext( _("Delete item"), _("Delete items"), qty) keep_label = stoqlib_ngettext( _("Keep it"), _("Keep them"), qty) if not yesno(msg, gtk.RESPONSE_NO, delete_label, keep_label): return self.emit('before-delete-items', objs) if qty == len(self.klist): self.klist.clear() else: for obj in objs: self.klist.remove(obj) self.klist.unselect_all() self._update_sensitivity() self.emit('after-delete-items') # # Hooks # def get_items(self): return [] def get_columns(self): raise NotImplementedError("get_columns must be implemented in " "subclasses") def run_editor(self, model): """This can be overriden to provide a custom run_dialog line, or a conversion function for the model """ if self._editor_class is None: raise TypeError( "%s cannot create or edit items without the editor_class " "argument set" % (self.__class__.__name__)) self.store.savepoint('before_run_editor_addition') retval = run_dialog(self._editor_class, None, store=self.store, model=model) if not retval: self.store.rollback_to_savepoint('before_run_editor_addition') return retval def delete_model(self, model): """Deletes a model, can be overridden in subclass :param model: model to delete """ model.__class__.delete(model.id, store=self.store) # # Public API # def add_extra_button(self, label=None, stock=None): """Add an extra button on the this slave The extra button will be appended at the end of the button box, the one containing the add/edit/delete buttons :param label: label of the button, can be ``None`` if stock is passed :param stock: stock label of the button, can be ``None`` if label is passed :param returns: the button added :rtype: gtk.Button """ if label is None and stock is None: raise TypeError("You need to provide a label or a stock argument") button = gtk.Button(label=label, stock=stock) button.set_property('can_focus', True) self.button_box.pack_end(button, False, False) button.show() return button def set_message(self, message, details_callback=None): """Display a simple message on a label, next to the add, edit, delete buttons :param message: a message with properly escaped markup """ self.message_hbox.set_visible(True) self.message_details_button.set_visible(bool(details_callback)) if details_callback: if self._callback_id: self.message_details_button.disconnect(self._callback_id) self._callback_id = self.message_details_button.connect( 'clicked', details_callback) self.message_label.set_markup(message) def clear_message(self): self.message_hbox.set_visible(False) def get_selection(self): # XXX: add get_selected_rows and raise exceptions if not in the # right mode if self.klist.get_selection_mode() == gtk.SELECTION_MULTIPLE: return self.klist.get_selected_rows() selection = self.klist.get_selected() if not selection: return [] return [selection] def hide_add_button(self): self.add_button.hide() def hide_edit_button(self): self._can_edit = False self.edit_button.hide() def hide_del_button(self): self.delete_button.hide() def set_editor(self, editor_class): if editor_class and not issubclass(editor_class, (BaseEditor, BaseWizard)): raise TypeError("editor_class must be a BaseEditor subclass") self._editor_class = editor_class # # Signal handlers # def on_klist__row_activated(self, *args): self._edit() def on_klist__selection_changed(self, *args): self._update_sensitivity() def on_add_button__clicked(self, button): self._edit_model() def on_edit_button__clicked(self, button): self._edit() def on_delete_button__clicked(self, button): self._clear()
class AdditionListSlave(SearchSlave): """A slave that offers a simple list and its management. This slave also has the option to display a small message right next to the buttons """ domain = 'stoq' toplevel_name = gladefile = 'AdditionListSlave' widgets = ('add_button', 'delete_button', 'klist', 'list_vbox', 'edit_button') gsignal('on-edit-item', object) gsignal('on-add-item', object) gsignal('before-delete-items', object) gsignal('after-delete-items') def __init__(self, store, columns=None, editor_class=None, klist_objects=None, visual_mode=False, restore_name=None, tree=False): """ Creates a new AdditionListSlave object :param store: a store :param columns: column definitions :type columns: sequence of :class:`kiwi.ui.objectlist.Columns` :param editor_class: the window that is going to be open when user clicks on add_button or edit_button. :type: editor_class: a :class:`stoqlib.gui.editors.BaseEditor` subclass :param klist_objects: initial objects to insert into the list :param visual_mode: if we are working on visual mode, that means, not possible to edit the model on this object type visual_mode: bool :param restore_name: the name used to save and restore the columns on a cache system (e.g. pickle) :type restore_name: basestring :param tree: Indication of which kind of list we are adding. If `True` ObjectTree otherwise ObjectList will be added """ columns = columns or self.get_columns() SearchSlave.__init__(self, columns=columns, restore_name=restore_name, store=store) self.tree = tree self.klist = ObjectTree() if tree else ObjectList() self.list_vbox.add(self.klist) self.list_vbox.show_all() if not self.columns: raise StoqlibError("columns must be specified") self.visual_mode = visual_mode self.store = store self.set_editor(editor_class) self._can_edit = True self._callback_id = None if self.visual_mode: self.hide_add_button() self.hide_edit_button() self.hide_del_button() items = klist_objects or self.get_items() self._setup_klist(items) self._update_sensitivity() def _setup_klist(self, klist_objects): self.klist.set_columns(self.columns) self.klist.set_selection_mode(gtk.SELECTION_MULTIPLE) if self.tree: (self.klist.append(obj.parent_item, obj) for obj in klist_objects) else: self.klist.add_list(klist_objects) if self.visual_mode: self.klist.set_sensitive(False) def _update_sensitivity(self, *args): if self.visual_mode: return can_delete = _can_edit = True objs = self.get_selection() if not objs: _can_edit = can_delete = False elif len(objs) > 1: _can_edit = False self.add_button.set_sensitive(True) self.edit_button.set_sensitive(_can_edit) self.delete_button.set_sensitive(can_delete) def _edit_model(self, model=None, parent=None): edit_mode = model result = self.run_editor(model) if not result: return if edit_mode: self.emit('on-edit-item', result) self.klist.update(result) else: if self.tree: self.klist.append(parent, result) else: self.klist.append(result) # Emit the signal after we added the item to the list to be able to # check the length of the list in our validation callbacks. self.emit('on-add-item', result) # As we have a selection extended mode for kiwi list, we # need to unselect everything before select the new instance. self.klist.unselect_all() self.klist.select(result) self._update_sensitivity() def _edit(self): if not self._can_edit: return objs = self.get_selection() qty = len(objs) if qty != 1: raise SelectionError( ("Please select only one item before choosing Edit." "\nThere are currently %d items selected") % qty) self._edit_model(objs[0]) def _clear(self): objs = self.get_selection() qty = len(objs) if qty < 1: raise SelectionError('There are no objects selected') msg = stoqlib_ngettext(_('Delete this item?'), _('Delete these %d items?') % qty, qty) delete_label = stoqlib_ngettext(_("Delete item"), _("Delete items"), qty) keep_label = stoqlib_ngettext(_("Keep it"), _("Keep them"), qty) if not yesno(msg, gtk.RESPONSE_NO, delete_label, keep_label): return self.emit('before-delete-items', objs) if qty == len(self.klist): self.klist.clear() else: for obj in objs: self.klist.remove(obj) self.klist.unselect_all() self._update_sensitivity() self.emit('after-delete-items') # # Hooks # def get_items(self): return [] def get_columns(self): raise NotImplementedError("get_columns must be implemented in " "subclasses") def run_editor(self, model): """This can be overriden to provide a custom run_dialog line, or a conversion function for the model """ if self._editor_class is None: raise TypeError( "%s cannot create or edit items without the editor_class " "argument set" % (self.__class__.__name__)) self.store.savepoint('before_run_editor_addition') retval = run_dialog(self._editor_class, None, store=self.store, model=model) if not retval: self.store.rollback_to_savepoint('before_run_editor_addition') return retval def delete_model(self, model): """Deletes a model, can be overridden in subclass :param model: model to delete """ model.__class__.delete(model.id, store=self.store) # # Public API # def add_extra_button(self, label=None, stock=None): """Add an extra button on the this slave The extra button will be appended at the end of the button box, the one containing the add/edit/delete buttons :param label: label of the button, can be ``None`` if stock is passed :param stock: stock label of the button, can be ``None`` if label is passed :param returns: the button added :rtype: gtk.Button """ if label is None and stock is None: raise TypeError("You need to provide a label or a stock argument") button = gtk.Button(label=label, stock=stock) button.set_property('can_focus', True) self.button_box.pack_end(button, False, False) button.show() return button def set_message(self, message, details_callback=None): """Display a simple message on a label, next to the add, edit, delete buttons :param message: a message with properly escaped markup """ self.message_hbox.set_visible(True) self.message_details_button.set_visible(bool(details_callback)) if details_callback: if self._callback_id: self.message_details_button.disconnect(self._callback_id) self._callback_id = self.message_details_button.connect( 'clicked', details_callback) self.message_label.set_markup(message) def clear_message(self): self.message_hbox.set_visible(False) def get_selection(self): # XXX: add get_selected_rows and raise exceptions if not in the # right mode if self.klist.get_selection_mode() == gtk.SELECTION_MULTIPLE: return self.klist.get_selected_rows() selection = self.klist.get_selected() if not selection: return [] return [selection] def hide_add_button(self): self.add_button.hide() def hide_edit_button(self): self._can_edit = False self.edit_button.hide() def hide_del_button(self): self.delete_button.hide() def set_editor(self, editor_class): if editor_class and not issubclass(editor_class, (BaseEditor, BaseWizard)): raise TypeError("editor_class must be a BaseEditor subclass") self._editor_class = editor_class # # Signal handlers # def on_klist__row_activated(self, *args): self._edit() def on_klist__selection_changed(self, *args): self._update_sensitivity() def on_add_button__clicked(self, button): self._edit_model() def on_edit_button__clicked(self, button): self._edit() def on_delete_button__clicked(self, button): self._clear()
import gtk from kiwi.ui.objectlist import Column, ObjectTree class Fruit: def __init__(self, name, price): self.name = name self.price = price class FruitDesc: def __init__(self, name): self.name = name fruits = ObjectTree([Column('name', data_type=str), Column('price', data_type=int)]) for name, price in [('Apple', 4), ('Pineapple', 2), ('Kiwi', 8), ('Banana', 3), ('Melon', 5)]: row = fruits.append(None, FruitDesc(name)) fruits.append(row, Fruit('Before taxes', price * 0.25)) fruits.append(row, Fruit('After taxes', price)) window = gtk.Window() window.connect('delete-event', gtk.main_quit) window.set_title('Fruits') window.set_size_request(150, 180)
def __init__(self): ObjectTree.__init__(self) self.connect('double-click', self._on__double_click) self.connect('row-activated', self._on__row_activated) self.connect('right-click', self._on__right_click)
class DokuwikiView(GladeDelegate): """ A dokuwiki editor window """ def __init__(self): GladeDelegate.__init__(self, gladefile="pydoku", delete_handler=self.quit_if_last) self._icons = {} self.throbber_icon = Throbber(self.view.throbber) self.setup_wikitree() self.setup_wikislist() self.setup_attachments() self.setup_lastchanges() self.setup_side() self.setup_sourceview() self.setup_htmlview() self.page_edit = self.view.notebook1.get_nth_page(0) self.page_view = self.view.notebook1.get_nth_page(1) self.page_attach = self.view.notebook1.get_nth_page(2) self.show_all() if len(cfg.getChildren()): wiki = cfg.getChildren()[0] self.connect(wiki.url, wiki.user, wiki.password) self.wiki = wiki if wiki.current: self.load_page(wiki.current) # quit override to work with twisted def quit_if_last(self, *args): self.htmlview.destroy() # for some reason has to be deleted explicitly windows = [toplevel for toplevel in gtk.window_list_toplevels() if toplevel.get_property('type') == gtk.WINDOW_TOPLEVEL] if len(windows) == 1: reactor.stop() # general interface functions def post(self, text): id = self.view.statusbar.get_context_id("zap") self.view.statusbar.push(id, text) # setup functions def setup_wikislist(self): columns = [Column('url',format_func=self.get_favicon,data_type=gtk.gdk.Pixbuf,icon_size=gtk.ICON_SIZE_SMALL_TOOLBAR)] self.wikislist = ObjectList(columns) columns.append(Column('url', title='Wiki', column='url')) self.wikislist.set_columns(columns) self.view.vbox2.pack_start(self.wikislist) self.view.vbox2.reorder_child(self.wikislist, 0) self.wikislist.add_list(cfg.getChildren()) self.wikislist.connect("selection-changed", self.wiki_selected) threads.deferToThread(self.download_favicons, cfg) def download_favicons(self, cfg): for wiki in cfg.getChildren(): self.download_favicon(wiki) def download_favicon(self, wiki): import urllib icon_url = wiki.url+"/lib/tpl/sidebar/images/favicon.ico" filename, headers = urllib.urlretrieve(icon_url, "/tmp/ico.ico") if headers["Content-Type"] == 'image/x-icon': self.add_favicon(wiki, filename) def get_favicon(self, wiki_url): return self._icons.get(wiki_url, page_icon) def add_favicon(self, wiki, filename): pixbuf = gtk.gdk.pixbuf_new_from_file(filename) pixbuf = pixbuf.scale_simple(16,16,gtk.gdk.INTERP_BILINEAR) self._icons[wiki.url] = pixbuf self.objectlist.refresh() def setup_side(self): columns = ['sum', 'user', 'type', 'version', 'ip'] columns = [Column(s) for s in columns] self.versionlist = ObjectList(columns) self.view.side_vbox.pack_start(gtk.Label('Version Log:'), False, False) self.view.side_vbox.add(self.versionlist) self.versionlist.connect("selection-changed", self.version_selected) self.view.side_vbox.pack_start(gtk.Label('BackLinks:'), False, False) self.backlinks = ObjectList([Column('name')]) self.backlinks.connect("selection-changed", self.change_selected) self.view.side_vbox.add(self.backlinks) def setup_attachments(self): columns = ['id', 'size', 'lastModified', 'writable', 'isimg', 'perms'] columns = [Column(s) for s in columns] self.attachmentlist = ObjectList(columns) self.view.attachments_vbox.add(self.attachmentlist) def setup_lastchanges(self): columns = ['name', 'author', 'lastModified', 'perms', 'version', 'size'] columns = [Column(s) for s in columns] columns.append(Column('lastModified', sorted=True, order=gtk.SORT_DESCENDING)) self.lastchangeslist = ObjectList(columns) self.lastchangeslist.connect("selection-changed", self.change_selected) self.view.side_vbox.add(self.lastchangeslist) def setup_wikitree(self): columns = ['id', 'lastModified', 'perms', 'size'] columns = [Column(s) for s in columns] columns.insert(0, Column('icon', title='name', data_type=gtk.gdk.Pixbuf)) self.objectlist = ObjectTree(columns) columns.insert(1, Column('name', column='icon')) self.objectlist.set_columns(columns) self.objectlist.connect("selection-changed", self.selected) self.view.vbox2.add(self.objectlist) def html_realized(self, widget): if self.wiki and self.wiki.current: self.get_htmlview(self.wiki.current) def setup_htmlview(self): self.htmlview = gtkmozembed.MozEmbed() self.view.html_scrolledwindow.add_with_viewport(self.htmlview) self.htmlview.connect('realize', self.html_realized) #self.htmlview.set_size_request(800,600) #self.htmlview.realize() #self.view.html_scrolledwindow.show_all() #self.htmlview.show() def setup_sourceview(self): self.buffer = DokuwikiBuffer(table) self.editor = gtksourceview.SourceView(self.buffer) #self.editor.set_show_line_numbers(True) accel_group = gtk.AccelGroup() self.get_toplevel().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) #self.editor = gtk.TextView(self.buffer) self.editor.set_left_margin(5) self.editor.set_right_margin(5) self.editor.set_wrap_mode(gtk.WRAP_WORD_CHAR) self.view.scrolledwindow1.add(self.editor) lm = gtksourceview.SourceLanguagesManager() langs = lm.get_available_languages() lang_diffs = filter(lambda s: s.get_name() == 'Diff', langs) if lang_diffs: self.buffer.set_language(lang_diffs[0]) # dokuwiki operations def _getVersion(self): return self._rpc.dokuwiki.getVersion() def get_version(self): return threads.deferToThread(self._getVersion) def get_pagelist(self): print "getpagelist1" pages = self._rpc.wiki.getAllPages() self._sections = {} self.objectlist.clear() print "getpagelist1.5" print "PAGES",pages for page in pages: self.add_page(page) print "getpagelist2" self.view.new_page.set_sensitive(True) self.view.delete_page.set_sensitive(True) if self.wiki.current: self.set_selection(self.wiki.current) print "getpagelist3" # XXX self.get_recent_changes() print "getpagelist2" def _getRecentChanges(self): return self._rpc.wiki.getRecentChanges(int(time.time()-(60*60*24*7*12))) def _gotRecentChanges(self, changes): changes = [DictWrapper(s) for s in changes] self.lastchangeslist.add_list(changes) def get_recent_changes(self): self.callDeferred(self._getRecentChanges, self._gotRecentChanges) def get_attachments(self, ns): attachments = self._rpc.wiki.getAttachments(ns, {}) attachments = [DictWrapper(s) for s in attachments] self.attachmentlist.add_list(attachments) def _getBackLinks(self, pagename): return self._rpc.wiki.getBackLinks(pagename) def _gotBackLinks(self, backlinks): backlinks = [Section(s) for s in backlinks] self.backlinks.add_list(backlinks) def get_backlinks(self, pagename): self.callDeferred(self._getBackLinks, self._gotBackLinks, pagename) def _getVersions(self, pagename): return self._rpc.wiki.getPageVersions(pagename, 0) def _gotVersions(self, versionlist): versionlist = [DictWrapper(s) for s in versionlist] self.versionlist.add_list(versionlist) def get_versions(self, pagename): self.callDeferred(self._getVersions, self._gotVersions, pagename) def _getHtmlData(self, pagename): text = self._rpc.wiki.getPageHTML(pagename) return text def _gotHtmlData(self, text): self.throbber_icon.stop() if not self.htmlview.window: return text = """<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head><body>"""+text+"</body>" self.htmlview.render_data(text, len(text), self.wiki.url, 'text/html') self.htmlview.realize() self.htmlview.show() def get_htmlview(self, pagename): self.throbber_icon.start() self.callDeferred(self._getHtmlData, self._gotHtmlData, pagename) #d.addErrback(self.someError) # XXX following is for gtkhtml (not used) #self.document.clear() #self.document.open_stream('text/html') #self.document.write_stream(text) #self.document.close_stream() def callDeferred(self, get_func, got_func, *args): d = threads.deferToThread(get_func, *args) d.addCallback(got_func) def _getEditText(self, pagename): return self._rpc.wiki.getPage(pagename) def _gotEditText(self, text): self.throbber_icon.stop() self.buffer.set_highlight(False) self.editor.set_editable(True) self.buffer.add_text(text) def _getDiffText(self, pagename, version, idx): return self._rpc.wiki.getPageVersion(pagename, version), idx def _gotDiffText(self, data): import difflib import StringIO text, idx = data self.textstack[idx] = text if not None in self.textstack: fromlines = self.textstack[0].split("\n") tolines = self.textstack[1].split("\n") diff_text = difflib.unified_diff(fromlines, tolines, "a", "b", self.versions[0], self.versions[1]) self.throbber_icon.stop() self.buffer.clear() str_buffer = StringIO.StringIO() for line in diff_text: str_buffer.write(line+'\n') str_buffer.seek(0) self.buffer.add_text(str_buffer.read()) self.buffer.set_highlight(True) self.editor.set_editable(False) def get_difftext(self, pagename, version, prev_version): self.throbber_icon.start() self.textstack = [None, None] self.versions = [version, prev_version] self.callDeferred(self._getDiffText, self._gotDiffText, pagename, prev_version, 0) self.callDeferred(self._getDiffText, self._gotDiffText, pagename, version, 1) def get_edittext(self, pagename): self.throbber_icon.start() self.callDeferred(self._getEditText, self._gotEditText, pagename) def put_page(self, text, summary, minor): pars = {} if summary: pars['sum'] = summary if minor: pars['minor'] = minor d = threads.deferToThread(self._rpc.wiki.putPage, self.wiki.current, text, pars) return d # put a page into the page tree def add_page(self, page): print page try: name = page["id"] path = name.split(":") prev = None for i, pathm in enumerate(path): if i == len(path)-1: # a page new = DictWrapper(page, pathm) self._sections[name] = new self.objectlist.append(prev, new, False) else: # a namespace part_path = ":".join(path[:i+1]) if not part_path in self._sections: new = Section(pathm, part_path) self._sections[part_path] = new self.objectlist.append(prev, new, False) else: new = self._sections[part_path] prev = new except: traceback.print_exc() def expand_to(self, pagename): path = pagename.split(":") for i, pathm in enumerate(path): if not i == len(path)-1: section = self._sections[":".join(path[:i+1])] self.view.objectlist.expand(section) def set_selection(self, pagename): obj = self._sections[pagename] self.expand_to(pagename) self.view.objectlist.select(obj, True) #self.selected(widget, obj) # page selected callback def wiki_selected(self, widget, wiki): self.connect(wiki.url, wiki.user, wiki.password) self.objectlist.clear() self.versionlist.clear() self.lastchangeslist.clear() self.backlinks.clear() self._sections = {} self.wiki = wiki self.buffer.clear() if wiki.current: self.load_page(wiki.current) def version_selected(self, widget, object): # yes, the previous item is the next in the widget if object == None: return previous = widget.get_next(object) if not previous: return prev_version = previous.version self.get_difftext(self.wiki.current, int(object.version), int(prev_version)) def change_selected(self, widget, object): if not object: return self.set_selection(object.name) def selected(self, widget, object): if not object: return if isinstance(object, Section): self.get_attachments(object.id) if not isinstance(object, DictWrapper): return self.wiki.current = object.id cfg.save() self.load_page(object.id) def load_page(self, pagename): self.get_edittext(pagename) self.get_htmlview(pagename) self.get_backlinks(pagename) self.get_versions(pagename) # kiwi interface callbacks def on_view_edit__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_edit, gtk.Label('edit'), 0) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_edit)) def on_view_view__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_view, gtk.Label('view'), 1) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_view)) def on_view_attachments__toggled(self, widget): if widget.get_active(): self.notebook1.insert_page(self.page_attach, gtk.Label('attach')) else: self.notebook1.remove_page(self.notebook1.page_num(self.page_attach)) def on_view_extra__toggled(self, widget): if widget.get_active(): self.backlinks.show() self.versionlist.show() self.view.hpaned2.set_position(self._prevpos) else: self.backlinks.hide() self.versionlist.hide() self._prevpos = self.view.hpaned2.get_position() self.view.hpaned2.set_position(self.view.hpaned2.allocation.width) def on_button_add__clicked(self, *args): dialog = ModalDialog("User Details") # prepare widgets = {} items = ["url","user", "password"] for i, item in enumerate(items): widgets[item] = gtk.Entry() if i == 2: widgets[item].set_visibility(False) hbox = gtk.HBox() hbox.pack_start(gtk.Label(item+': ')) hbox.add(widgets[item]) dialog.vbox.add(hbox) dialog.show_all() # run response = dialog.run() user = widgets['user'].get_text() password = widgets['password'].get_text() url = widgets['url'].get_text() dialog.destroy() if not response == gtk.RESPONSE_ACCEPT: return self.wiki = cfg.new(Dokuwiki, url=url, user=user, password=password) cfg.addChild(self.wiki) cfg.save() self.connect(url, user, password) def get_full_url(self, url, user, password): try: if user and password: split_url = url.split('://') proto = split_url[0] base_url = split_url[1] return proto + '://' + user + ':' + password + '@' + base_url return url except: traceback.print_exc() def connect(self, url, user, password): # following commented line is for gtkhtml (not used) #simplebrowser.currentUrl = self.view.url.get_text() # handle response self.post("Connecting to " + url) params = urlencode({'u':user, 'p':password}) print self.get_full_url(url, user, password) fullurl = self.get_full_url(url, user, password) + "/lib/exe/xmlrpc.php?"+ params print "serverproxy1" self._rpc = ServerProxy(fullurl) print "serverproxy1" d = self.get_version() d.addCallback(self.connected) d.addErrback(self.error_connecting) def error_connecting(self, failure): self.post("Error connecting to " + self.wiki.url) print failure.getErrorMessage() def connected(self, version): print "connected1" self.view.version.set_text(version) print "connected1.5" self.get_pagelist() print "connected2" self.post("Connected") def on_delete_page__clicked(self, *args): dialog = ModalDialog("Are you sure?") response = dialog.run() if response == gtk.RESPONSE_ACCEPT: value = self._sections[self.wiki.current] sel = self.objectlist.remove(value) self._rpc.wiki.putPage(self.wiki.current, "", {}) self.wiki.current = '' cfg.save() dialog.destroy() def on_new_page__clicked(self, *args): dialog = ModalDialog("Name for the new page") text_w = gtk.Entry() text_w.show() response = [] dialog.vbox.add(text_w) response = dialog.run() if response == gtk.RESPONSE_ACCEPT: text = text_w.get_text() if text: self.wiki.current = text cfg.save() self.buffer.clear() dialog.destroy() def on_button_h1__clicked(self, *args): self.buffer.set_style('h1') def on_button_h2__clicked(self, *args): self.buffer.set_style('h2') def on_button_h3__clicked(self, *args): self.buffer.set_style('h3') def on_button_h4__clicked(self, *args): self.buffer.set_style('h4') def on_button_h5__clicked(self, *args): self.buffer.set_style('h5') def on_button_h6__clicked(self, *args): self.buffer.set_style('h6') def on_button_bold__clicked(self, *args): self.buffer.set_style('bold') def on_button_italic__clicked(self, *args): self.buffer.set_style('italic') def on_button_clear_style__clicked(self, *args): self.buffer.clear_style() def _pagePut(self, *args): if not self.wiki.current in self._sections: self.add_page({"id":self.wiki.current}) self.get_htmlview(self.wiki.current) self.get_versions(self.wiki.current) self.post("Saved") def on_button_save__clicked(self, *args): """ Save button callback """ dialog = ModalDialog("Commit message") entry = gtk.Entry() minor = gtk.CheckButton("Minor") dialog.vbox.add(gtk.Label("Your attention to detail\nis greatly appreciated")) dialog.vbox.add(entry) dialog.vbox.add(minor) dialog.show_all() response = dialog.run() if response == gtk.RESPONSE_ACCEPT: self.post("Saving...") text = self.buffer.process_text() self.throbber_icon.start() d = self.put_page(text, entry.get_text(), minor.get_active()) d.addCallback(self._pagePut) dialog.destroy() # unused stuff def request_url(self, document, url, stream): f = simplebrowser.open_url(url) stream.write(f.read()) def setup_htmlview_gtkhtml(self): # XXX not used now self.document = gtkhtml2.Document() self.document.connect('request_url', self.request_url) self.htmlview = gtkhtml2.View() self.htmlview.set_document(self.document)
def setUp(self): self.win = gtk.Window() self.win.set_default_size(400, 400) self.tree = ObjectTree([Column('name'), Column('age')]) self.win.add(self.tree) refresh_gui()
class TodoUI(GladeSlaveDelegate): def __init__(self, parent, mo): self.parent = parent self.mo = mo self.factory = Factory() GladeSlaveDelegate.__init__(self, gladefile="mo_tab_todo", toplevel_name="window_main") # Set up the user interface todoColumns = [ Column("done", title='Done', data_type=bool, editable=True), Column("summaryformat", title='Summary', use_markup=True), Column('priority', title='Priority', sorted=True, order=gtk.SORT_DESCENDING), ColoredColumn('due', title='Due', data_type=datetime.datetime, color='red', data_func=self.color_due), Column('created', title='Created', data_type=datetime.datetime) ] self.treeview_todo = ObjectTree(todoColumns) self.vbox_todolist.add(self.treeview_todo) # Connect signals self.treeview_todo.connect('row-activated', self.treeview_todo__row_activated) self.treeview_todo.connect('selection-changed', self.treeview_todo__selection_changed) self.treeview_todo.connect('key-press-event', self.treeview_todo__key_press_event) self.refresh() def refresh(self): """ Refresh the entire todo tab. This clears everything and rebuilds it. Call this when todos are removed outside of this class. """ self.treeview_todo.clear() # Fill the user interface with information for todo in self.mo.cal_model.get_todos(): parent = self.mo.cal_model.get_model_by_uid(todo.get_related_to()) self.treeview_todo.append(parent, todo) def color_due(self, value): if not value: return(False) return(value < datetime.datetime.now()) def on_toolbutton_add__clicked(self, *args): todo = self.factory.todo() todo = miniorganizer.ui.TodoEditUI(self.mo, todo).run() if todo: self.mo.cal_model.add(todo) self.treeview_todo.append(None, todo) self.parent.menuitem_save.set_sensitive(True) def on_toolbutton_edit__clicked(self, *args): sel_todo = self.treeview_todo.get_selected() self.treeview_todo__row_activated(self.treeview_todo, sel_todo) def on_toolbutton_remove__clicked(self, *args): sel_todo = self.treeview_todo.get_selected() if sel_todo: children = self.mo.cal_model.get_models_related_by_uid(sel_todo.get_uid()) if children: response = dialogs.warning('This Todo contains sub-todos. Removing it will also remove the sub-todos. Is this what you want?', buttons=gtk.BUTTONS_YES_NO) if response != gtk.RESPONSE_YES: return self.treeview_todo.remove(sel_todo, True) self.mo.cal_model.delete(sel_todo, True) self.parent.menuitem_save.set_sensitive(True) def on_toolbutton_addsub__clicked(self, *args): parent_todo = self.treeview_todo.get_selected() todo = self.factory.todo(parent_todo) if miniorganizer.ui.TodoEditUI(self.mo, todo).run(): self.mo.cal_model.add(todo) self.treeview_todo.append(parent_todo, todo) self.treeview_todo.expand(parent_todo) self.parent.menuitem_save.set_sensitive(True) def treeview_todo__selection_changed(self, list, selection): has_selection = selection is not None self.toolbutton_addsub.set_sensitive(has_selection) self.toolbutton_remove.set_sensitive(has_selection) self.toolbutton_edit.set_sensitive(has_selection) def treeview_todo__row_activated(self, list, object): todo = miniorganizer.ui.TodoEditUI(self.mo, object).run() self.parent.menuitem_save.set_sensitive(True) def treeview_todo__key_press_event(self, treeview, event): if event.keyval == gtk.keysyms.Delete: self.on_toolbutton_remove__clicked()