def action_new(self): """The new model menu action. This action will create a new UML model. This will trigger a FileManagerStateChange event.""" element_factory = self.element_factory main_window = self.main_window if element_factory.size(): dialog = QuestionDialog( gettext("Opening a new model will flush the" " currently loaded model.\nAny changes" " made will not be saved. Do you want to" " continue?"), parent=main_window.window, ) answer = dialog.answer dialog.destroy() if not answer: return element_factory.flush() with element_factory.block_events(): model = element_factory.create(UML.Package) model.name = gettext("New model") diagram = element_factory.create(UML.Diagram) diagram.package = model diagram.name = gettext("main") self.filename = None element_factory.model_ready()
def action_new_from_template(self): """This menu action opens the new model from template dialog.""" filters = [ { "name": gettext("Gaphor Models"), "pattern": "*.gaphor" }, { "name": gettext("All Files"), "pattern": "*" }, ] file_dialog = FileDialog(gettext("New Gaphor Model From Template"), filters=filters) filename = file_dialog.selection file_dialog.destroy() log.debug(filename) if filename: self.load(filename) self.filename = None
def construct(self): page = Gtk.VBox() hbox = Gtk.HBox() label = Gtk.Label(label="") label.set_justify(Gtk.Justification.LEFT) self.size_group.add_widget(label) hbox.pack_start(label, False, True, 0) button = Gtk.CheckButton(label=gettext("Orthogonal")) button.set_active(self.item.orthogonal) button.connect("toggled", self._on_orthogonal_change) hbox.pack_start(button, True, True, 0) page.pack_start(hbox, False, True, 0) hbox = Gtk.HBox() label = Gtk.Label(label="") label.set_justify(Gtk.Justification.LEFT) self.size_group.add_widget(label) hbox.pack_start(label, False, True, 0) button = Gtk.CheckButton(label=gettext("Horizontal")) button.set_active(self.item.horizontal) button.connect("toggled", self._on_horizontal_change) hbox.pack_start(button, True, True, 0) page.pack_start(hbox, False, True, 0) return page
def construct(self): page = super().construct() if not self.subject: return page hbox = Gtk.HBox() label = Gtk.Label(label="") label.set_justify(Gtk.Justification.LEFT) self.size_group.add_widget(label) hbox.pack_start(label, False, True, 0) button = Gtk.CheckButton(label=gettext("Show direction")) button.set_active(self.item.show_direction) button.connect("toggled", self._on_show_direction_change) hbox.pack_start(button, True, True, 0) button = Gtk.Button.new_from_icon_name( "object-flip-horizontal-symbolic", Gtk.IconSize.BUTTON) button.connect("clicked", self._on_invert_direction_change) hbox.pack_start(button, True, True, 0) page.pack_start(hbox, False, True, 0) box = self.construct_end(gettext("Head"), self.item.head_end) if box: page.pack_start(box, False, True, 0) box = self.construct_end(gettext("Tail"), self.item.tail_end) if box: page.pack_start(box, False, True, 0) return page
class XMIExport(Service, ActionProvider): def __init__(self, element_factory, file_manager, export_menu): self.element_factory = element_factory self.file_manager = file_manager export_menu.add_actions(self) def shutdown(self): pass @action( name="file-export-xmi", label=gettext("Export to XMI"), tooltip=gettext("Export model to XMI (XML Model Interchange) format"), ) def execute(self): filename = self.file_manager.filename filename = filename.replace(".gaphor", ".xmi") if filename else "model.xmi" file_dialog = FileDialog( gettext("Export model to XMI file"), action="save", filename=filename ) filename = file_dialog.selection if filename and len(filename) > 0: logger.debug(f"Exporting XMI model to: {filename}") export = exportmodel.XMIExport(self.element_factory) try: export.export(filename) except Exception as e: logger.error(f"Error while saving model to file {filename}: {e}")
def construct(self): page = super().construct() subject = self.subject if not subject: return page hbox = create_hbox_label(self, page, gettext("Entry")) entry = Gtk.Entry() if subject.entry: entry.set_text(subject.entry.name or "") entry.connect("changed", self.on_text_change, self.set_entry) hbox.pack_start(entry, True, True, 0) hbox = create_hbox_label(self, page, gettext("Exit")) entry = Gtk.Entry() if subject.exit: entry.set_text(subject.exit.name or "") entry.connect("changed", self.on_text_change, self.set_exit) hbox.pack_start(entry, True, True, 0) hbox = create_hbox_label(self, page, gettext("Do Activity")) entry = Gtk.Entry() if subject.doActivity: entry.set_text(self.subject.doActivity.name or "") entry.connect("changed", self.on_text_change, self.set_do_activity) hbox.pack_start(entry, True, True, 0) page.show_all() return page
def load(self, filename): """Load the Gaphor model from the supplied file name. A status window displays the loading progress. The load generator updates the progress queue. The loader is passed to a GIdleThread which executes the load generator. If loading is successful, the filename is set.""" queue = Queue() status_window = StatusWindow( gettext("Loading..."), gettext("Loading model from {filename}").format(filename=filename), parent=self.main_window.window, queue=queue, ) try: loader = storage.load_generator(filename.encode("utf-8"), self.element_factory) worker = GIdleThread(loader, queue) worker.start() worker.wait() if worker.error: worker.reraise() self.filename = filename self.event_manager.handle(FileLoaded(self, filename)) except (QueueEmpty, QueueFull): error_handler(message=gettext( "Error while loading model from file {filename}").format( filename=filename)) raise finally: status_window.destroy()
def zoom_buttons(): box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0) box.get_style_context().add_class("linked") box.pack_start( icon_button( "zoom-in-symbolic", "diagram.zoom-in", gettext("Zoom in") + f" ({primary()}++)", ), False, False, 0, ) box.pack_start( icon_button( "zoom-original-symbolic", "diagram.zoom-100", gettext("Zoom 100%") + f" ({primary()}+0)", ), False, False, 0, ) box.pack_start( icon_button( "zoom-out-symbolic", "diagram.zoom-out", gettext("Zoom out") + f" ({primary()}+-)", ), False, False, 0, ) box.show() return box
def undo_buttons(): box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0) box.get_style_context().add_class("linked") box.pack_start( icon_button( "edit-undo-symbolic", "win.edit-undo", gettext("Undo") + f" ({primary()}+Z)", ), False, False, 0, ) box.pack_start( icon_button( "edit-redo-symbolic", "win.edit-redo", gettext("Redo") + f" ({primary()}+Shift+Z)", ), False, True, 0, ) box.show() return box
def construct(self): page = super().construct() subject = self.subject if not subject: return page hbox = create_hbox_label(self, page, gettext("Upper bound")) entry = Gtk.Entry() entry.set_text(subject.upperBound or "") entry.connect("changed", self._on_upper_bound_change) hbox.pack_start(entry, True, True, 0) hbox = create_hbox_label(self, page, "") combo = Gtk.ComboBoxText() for v in self.ORDERING_VALUES: combo.append_text(v) combo.set_active(self.ORDERING_VALUES.index(subject.ordering)) combo.connect("changed", self._on_ordering_change) hbox.pack_start(combo, False, True, 0) hbox = create_hbox_label(self, page, "") button = Gtk.CheckButton(gettext("Ordering")) button.set_active(self.item.show_ordering) button.connect("toggled", self._on_ordering_show_change) hbox.pack_start(button, False, True, 0) return page
def construct(self): page = Gtk.VBox() if not self.item.subject: return page # Show operations toggle hbox = Gtk.HBox() label = Gtk.Label(label="") label.set_justify(Gtk.Justification.LEFT) hbox.pack_start(label, False, True, 0) button = Gtk.CheckButton(label=gettext("Show operations")) button.set_active(self.item.show_operations) button.connect("toggled", self._on_show_operations_change) hbox.pack_start(button, True, True, 0) page.pack_start(hbox, False, True, 0) def create_model(): return ClassOperations(self.item, (str, bool, bool, object)) self.model = create_model() tip = """\ Add and edit class operations according to UML syntax. Operation syntax examples - call() - + call(a: int, b: str) - # call(a: int: b: str): bool """ tree_view = create_tree_view( self.model, (gettext("Operation"), gettext("A"), gettext("S")), tip ) page.pack_start(tree_view, True, True, 0) @AsyncIO(single=True) def handler(event): if not tree_view.props.has_focus and self.item and self.item.subject: self.model = create_model() tree_view.set_model(self.model) self.watcher.watch("ownedOperation.name", handler).watch( "ownedOperation.isAbstract", handler ).watch("ownedOperation.visibility", handler).watch( "ownedOperation.returnResult.lowerValue", handler ).watch( "ownedOperation.returnResult.upperValue", handler ).watch( "ownedOperation.returnResult.typeValue", handler ).watch( "ownedOperation.formalParameter.lowerValue", handler ).watch( "ownedOperation.formalParameter.upperValue", handler ).watch( "ownedOperation.formalParameter.typeValue", handler ).watch( "ownedOperation.formalParameter.defaultValue", handler ).subscribe_all() tree_view.connect("destroy", self.watcher.unsubscribe_all) return page
def create_stereotype_tree_view(model, toggle_stereotype, set_slot_value): """ Create a tree view for an editable tree model. :Parameters: model Model, for which tree view is created. """ tree_view = Gtk.TreeView.new_with_model(model) tree_view.set_search_column(-1) # Stereotype/Attributes col = Gtk.TreeViewColumn.new() col.set_title("{} / {}".format(gettext("Stereotype"), gettext("Attribute"))) col.set_expand(True) renderer = Gtk.CellRendererToggle() renderer.set_property("active", True) renderer.set_property("activatable", True) renderer.connect("toggled", toggle_stereotype, model, 2) col.pack_start(renderer, False) col.add_attribute(renderer, "active", 2) def show_checkbox(column, cell, model, iter, data): # value = model.get_value(iter, 4) # cell.set_property('active', value is not None) value = model.get_value(iter, 3) cell.set_property("visible", isinstance(value, UML.Stereotype)) col.set_cell_data_func(renderer, show_checkbox) renderer = Gtk.CellRendererText.new() renderer.set_property("editable", False) renderer.set_property("is-expanded", True) col.pack_start(renderer, False) col.add_attribute(renderer, "text", 0) tree_view.append_column(col) # TODO: use col.set_cell_data_func(renderer, func, None) to toggle visibility # Value renderer = Gtk.CellRendererText() renderer.set_property("is-expanded", True) renderer.connect("edited", set_slot_value, model, 1) col = Gtk.TreeViewColumn(gettext("Value"), renderer, text=1) col.set_expand(True) def set_editable(column, cell, model, iter, data): value = model.get_value(iter, 4) cell.set_property("editable", bool(value)) col.set_cell_data_func(renderer, set_editable) tree_view.append_column(col) # tree_view.connect('key_press_event', remove_on_keypress) # tree_view.connect('key_press_event', swap_on_keypress) return tree_view
def load_default_model(session): element_factory = session.get_service("element_factory") element_factory.flush() with element_factory.block_events(): model = element_factory.create(UML.Package) model.name = gettext("New model") diagram = element_factory.create(UML.Diagram) diagram.package = model diagram.name = gettext("main") element_factory.model_ready()
def create_hamburger_model(export_menu, tools_menu): model = Gio.Menu.new() part = Gio.Menu.new() part.append(gettext("New Window"), "app.file-new") part.append(gettext("New from Template"), "app.file-new-template") model.append_section(None, part) part = Gio.Menu.new() part.append(gettext("Save"), "win.file-save") part.append(gettext("Save As..."), "win.file-save-as") part.append_submenu(gettext("Export"), export_menu) model.append_section(None, part) part = Gio.Menu.new() part.append_submenu(gettext("Tools"), tools_menu) model.append_section(None, part) part = Gio.Menu.new() part.append(gettext("Preferences"), "win.preferences") part.append(gettext("Keyboard Shortcuts"), "app.shortcuts") part.append(gettext("About Gaphor"), "app.about") model.append_section(None, part) return model
def construct(self): page = Gtk.VBox() if not self.item.subject: return page # Show attributes toggle hbox = Gtk.HBox() label = Gtk.Label(label="") label.set_justify(Gtk.Justification.LEFT) hbox.pack_start(label, False, True, 0) button = Gtk.CheckButton(label=gettext("Show attributes")) button.set_active(self.item.show_attributes) button.connect("toggled", self._on_show_attributes_change) hbox.pack_start(button, True, True, 0) page.pack_start(hbox, False, True, 0) def create_model(): return ClassAttributes(self.item, (str, bool, object)) self.model = create_model() tip = """\ Add and edit class attributes according to UML syntax. Attribute syntax examples - attr - + attr: int - # /attr: int """ tree_view = create_tree_view( self.model, (gettext("Attributes"), gettext("S")), tip ) page.pack_start(tree_view, True, True, 0) @AsyncIO(single=True) def handler(event): # Single it's asynchronous, make sure all properties are still there if not tree_view.props.has_focus and self.item and self.item.subject: self.model = create_model() tree_view.set_model(self.model) self.watcher.watch("ownedAttribute.name", handler).watch( "ownedAttribute.isDerived", handler ).watch("ownedAttribute.visibility", handler).watch( "ownedAttribute.isStatic", handler ).watch( "ownedAttribute.lowerValue", handler ).watch( "ownedAttribute.upperValue", handler ).watch( "ownedAttribute.defaultValue", handler ).watch( "ownedAttribute.typeValue", handler ).subscribe_all() tree_view.connect("destroy", self.watcher.unsubscribe_all) return page
def new(self): element_factory = self.element_factory element_factory.flush() with element_factory.block_events(): model = element_factory.create(UML.Package) model.name = gettext("New model") diagram = element_factory.create(UML.Diagram) diagram.package = model diagram.name = gettext("main") self.filename = None element_factory.model_ready()
def update_shapes(self, event=None): self.shape = Box( Box( Text( text=lambda: stereotypes_str(self.subject, ["block"]), style={ "min-width": 0, "min-height": 0 }, ), EditableText( text=lambda: self.subject.name or "", width=lambda: self.width - 4, style={ "font-weight": FontWeight.BOLD, "font-style": FontStyle.ITALIC if self.subject and self.subject.isAbstract else FontStyle.NORMAL, }, ), Text( text=lambda: from_package_str(self), style={ "font-size": 10, "min-width": 0, "min-height": 0 }, ), style={"padding": (12, 4, 12, 4)}, ), *(self.show_parts and self.subject and [ self.block_compartment( gettext("parts"), lambda a: a.aggregation == "composite", ) ] or []), *(self.show_references and self.subject and [ self.block_compartment( gettext("references"), lambda a: a.aggregation != "composite", ) ] or []), *(self.show_stereotypes and stereotype_compartments(self.subject) or []), style={ "min-width": 100, "min-height": 50, "vertical-align": VerticalAlign.TOP, }, draw=draw_border, )
def verify_orphans(self): """Verify that no orphaned elements are saved. This method checks of there are any orphan references in the element factory. If orphans are found, a dialog is displayed asking the user if it is OK to unlink them. """ orphans = verify.orphan_references(self.element_factory) if orphans: log.info("Found orphan references %s", orphans) main_window = self.main_window dialog = QuestionDialog( gettext( "The model contains some references to items that are not maintained. Do you want to clean the model before saving?" ), parent=main_window.window, ) answer = dialog.answer dialog.destroy() if not answer: for orphan in orphans: orphan.unlink()
def action_new_from_template(self): """This menu action opens the new model from template dialog.""" filename = self.file_dialog(gettext("New Gaphor Model From Template")) if filename: self.load(filename) self.filename = None
def construct(self): page = super().construct() subject = self.subject if not subject: return page hbox = create_hbox_label(self, page, gettext("Guard")) entry = Gtk.Entry() v = subject.guard.specification entry.set_text(v if v else "") changed_id = entry.connect("changed", self._on_guard_change) hbox.pack_start(entry, True, True, 0) def handler(event): entry.handler_block(changed_id) v = event.new_value entry.set_text(v if v else "") entry.handler_unblock(changed_id) self.watcher.watch("guard[Constraint].specification", handler).subscribe_all() entry.connect("destroy", self.watcher.unsubscribe_all) return page
def action_open(self): """This menu action opens the standard model open dialog.""" filename = self.file_dialog(gettext("Open Gaphor Model")) if filename: self.load(filename)
def construct(self): page = super().construct() item = self.item # Fold toggle hbox = Gtk.HBox() label = Gtk.Label(label="") label.set_justify(Gtk.Justification.LEFT) self.size_group.add_widget(label) hbox.pack_start(label, False, True, 0) button = Gtk.CheckButton(gettext("Folded")) button.set_active(item.folded != Folded.NONE) button.connect("toggled", self._on_fold_change) connected_items = [ c.item for c in item.canvas.get_connections(connected=item) ] disallowed = (ConnectorItem, ) can_fold = not any( map(lambda i: isinstance(i, disallowed), connected_items)) button.set_sensitive(can_fold) hbox.pack_start(button, True, True, 0) page.pack_start(hbox, False, True, 0) return page
def save_svg_action(self): diagram = self.diagrams.get_current_diagram() filename = self.save_dialog( diagram, gettext("Export diagram to SVG"), "svg", "image/svg+xml" ) if filename: self.save_svg(filename, diagram)
def save_png_action(self): diagram = self.diagrams.get_current_diagram() filename = self.save_dialog( diagram, gettext("Export diagram to PNG"), "png", "image/png" ) if filename: self.save_png(filename, diagram)
def create_recent_files_model(recent_manager=None): model = Gio.Menu.new() model.append_section( gettext("Recently opened files"), RecentFilesMenu(recent_manager or Gtk.RecentManager.get_default()), ) return model
def save_pdf_action(self): diagram = self.diagrams.get_current_diagram() filename = self.save_dialog( diagram, gettext("Export diagram to PDF"), "pdf", "application/pdf" ) if filename: self.save_pdf(filename, diagram)
def save_dialog(self, diagram, title, ext): filename = (diagram.name or "export") + ext file_dialog = FileDialog(title, action="save", filename=filename) save = False while True: filename = file_dialog.selection if os.path.exists(filename): question = gettext( "The file {filename} already exists. Do you want to replace it?" ).format(filename=filename) question_dialog = QuestionDialog(question) answer = question_dialog.answer question_dialog.destroy() if answer: save = True break else: save = True break file_dialog.destroy() if save and filename: return filename
def save_pdf_action(self): title = gettext("Export diagram to PDF") ext = ".pdf" diagram = self.diagrams.get_current_diagram() filename = self.save_dialog(diagram, title, ext) if filename: self.save_pdf(filename, diagram)
def save_svg_action(self): title = gettext("Export diagram to SVG") ext = ".svg" diagram = self.diagrams.get_current_diagram() filename = self.save_dialog(diagram, title, ext) if filename: self.save_svg(filename, diagram)
def error_message(e): if not isinstance(e, IOError): return gettext( "Gaphor was not able to store the model, probably due to an internal error:\n{exc}\nIf you think this is a bug, please contact the developers." ).format(exc=str(e)) if e.errno == 13: return gettext( "You do not have the permissions necessary to save the model.\nPlease check that you typed the location correctly and try again." ) elif e.errno == 28: return gettext( "You do not have enough free space on the device to save the model.\nPlease free up some disk space and try again or save it in a different location." ) return gettext( "The model cannot be stored at this location:\n{exc}\nPlease check that you typed the location correctly and try again." ).format(exc=str(e))