Пример #1
0
class EmulicaModel:
    """Graphical application for emulica: Modelling part.
    
    Attributes:
        main -- the Emulica main application
        builder -- the GtkBuilder object
        cmd_manager -- undo/redo manager
        canvas -- the model canvas  (EmulicaCanvas)
        clipboard -- the module clipboard
    """
    def __init__(self, main_app):
        """Create the modeling widget, connect signal, and add an empty model"""
        self.main = main_app
        self.builder = main_app.builder
        self.model = main_app.model
        self.cmd_manager = CommandManager()
        self.cmd_manager.handler = self.main.update_undo_redo_menuitem
        self.canvas = canvas.EmulicaCanvas(self.model, self.cmd_manager)
        self.canvas.connect('selection-changed',
                            self.on_emulation_selection_changed)
        self.canvas.connect('add-done', self.on_emulation_add_done)
        #self.clipboard = Gtk.Clipboard(selection = '_SEME_MODULE_CLIPBOARD')
        self.clipboard = Gtk.Clipboard.get(gdk.SELECTION_CLIPBOARD)
        gobject.timeout_add(1500, self.check_clipboard)
        model_view = self.builder.get_object('model_view')
        #TODO: connect signal changed
        self.canvas.contextmenu = self.builder.get_object(
            'emulation_contextmenu')
        model_view.add(self.canvas)
        self.canvas.show()

    def reset(self, model, main_layout=None, sub_layout=None):
        """Reset the model"""
        self.model = model
        self.canvas.setup_model(self.model)
        #then apply the layouts
        if (main_layout != None):
            self.canvas.apply_layout(main_layout)
        if (sub_layout != None):
            for (submodel, layout) in sub_layout.items():
                self.canvas.apply_layout(layout, submodel=submodel)
        #TODO: clear the command stack

    def get_layout(self):
        """Wrapper around the canvas get_layout function"""
        return self.canvas.get_layout()

    def set_animate(self, option):
        """Set wether the simulation should be animated or not"""
        self.canvas.animate = option

    def check_clipboard(self):
        """This method repetitively check for text in the special 
        _SEME_MODULE_CLIPBOARD clipboard, and set paste button accordingly
        """
        widget = self.builder.get_object('paste')

        def callback(clipboard, text, data):
            if text == None or text == '':
                widget.set_sensitive(False)
            else:
                widget.set_sensitive(True)

        self.clipboard.request_text(callback, None)
        return True

    def on_import_emulation_menuitem_activate(self, menuitem, data=None):
        """Callback for the imports emulation model menuitem."""
        chooser = Gtk.FileChooserDialog(
            _("Import emulation model..."), self.main,
            Gtk.FileChooserAction.OPEN,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE,
             Gtk.ResponseType.OK))
        response = chooser.run()
        if response == Gtk.ResponseType.OK:
            #change dir to the dir of the imported model
            filename = chooser.get_filename()
            os.chdir(os.path.dirname(filename))
            self.model = emuML.load(filename)
            self.canvas.setup_model(self.model)
        chooser.destroy()

    def on_export_emulation_menuitem_activate(self, menuitem, data=None):
        """Callback for the export emulation menuitem."""
        chooser = Gtk.FileChooserDialog(
            _("Export emulation model..."), self.main,
            Gtk.FileChooserAction.SAVE,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE,
             Gtk.ResponseType.OK))
        if self.main.filename:
            chooser.set_filename(
                os.path.splitext(self.main.filename)[0] + '.xml')
        response = chooser.run()
        if response == Gtk.ResponseType.OK:
            emuML.save(self.model, chooser.get_filename())
        chooser.destroy()

    def undo(self):
        """Undo"""
        self.cmd_manager.undo()

    def redo(self):
        """Redo"""
        self.cmd_manager.redo()

    def can_undo(self):
        """Return True if there are undoable actions."""
        return self.cmd_manager.can_undo()

    def can_redo(self):
        """Return True if there are rendoable actions."""
        return self.cmd_manager.can_redo()

    def cut(self):
        """Cut"""
        self.canvas.cut_clipboard(self.clipboard)

    def copy(self):
        """Copy"""
        self.canvas.copy_clipboard(self.clipboard)

    def paste(self):
        """Paste"""
        self.canvas.paste_clipboard(self.clipboard)

    def delete(self):
        """Delete"""
        self.canvas.delete_selection()

    def on_zoom_in_activate(self, menuitem):
        """Callback for the zoom in menuitem. increment scale."""
        scale = self.canvas.get_scale() * 1.25
        self.canvas.set_scale(scale)
        self.adjust_canvas_size()

    def on_zoom_out_activate(self, menuitem):
        """Callback for the zoom in menuitem. increment scale."""
        scale = self.canvas.get_scale() / 1.25
        self.canvas.set_scale(scale)
        self.adjust_canvas_size()

    def on_zoom_100_activate(self, menuitem):
        """Callback for the zoom in menuitem. increment scale."""
        self.canvas.set_scale(1)
        self.adjust_canvas_size()

    def on_zoom_fit_activate(self, menuitem):
        """Callback for the zoom fit menuitem. Set scale and scrolled window
        adjustment to fit the model canvas."""
        (x1, y1, x2, y2) = self.canvas.fit_size()
        vp = self.builder.get_object('model_view')
        vadj = vp.get_vadjustment()
        hadj = vp.get_hadjustment()
        hscale = hadj.get_page_size() / float(x2 - x1)
        vscale = vadj.get_page_size() / float(y2 - y1)
        self.canvas.set_scale(min(hscale, vscale))
        self.adjust_canvas_size()
        s = self.canvas.get_scale()
        hadj.value = x1 * s
        vadj.value = y1 * s

    def adjust_canvas_size(self):
        scale = self.canvas.get_scale()
        (x1, y1, x2, y2) = self.canvas.get_bounds()  #fit_size or get_bounds ??
        self.canvas.set_size_request(int((x2 - x1) * scale),
                                     int((y2 - y1) * scale))

    def on_button_actuator_activate(self, widget, data=None):
        """Change arrow orientation and change visibility of table"""
        palette_actuator_table = self.builder.get_object('table_actuator')
        palette_actuator_arrow = self.builder.get_object('arrow_actuator')
        if palette_actuator_table.props.visible:
            palette_actuator_table.hide()
            palette_actuator_arrow.set(Gtk.ArrowType.RIGHT, Gtk.ShadowType.OUT)
        else:
            palette_actuator_table.show()
            palette_actuator_arrow.set(Gtk.ArrowType.DOWN, Gtk.ShadowType.OUT)

    def on_button_holder_activate(self, widget, data=None):
        """Change arrow orientation and change visibility of table"""
        palette_holder_arrow = self.builder.get_object('arrow_holder')
        palette_holder_table = self.builder.get_object('table_holder')

        if palette_holder_table.props.visible:
            palette_holder_table.hide()
            palette_holder_arrow.set(Gtk.ArrowType.RIGHT, Gtk.ShadowType.OUT)
        else:
            palette_holder_table.show()
            palette_holder_arrow.set(Gtk.ArrowType.DOWN, Gtk.ShadowType.OUT)

    def on_button_observer_activate(self, widget, data=None):
        """Change arrow orientation and change visibility of table"""
        palette_observer_arrow = self.builder.get_object('arrow_observer')
        palette_observer_table = self.builder.get_object('table_observer')
        if palette_observer_table.props.visible:
            palette_observer_table.hide()
            palette_observer_arrow.set(Gtk.ArrowType.RIGHT, Gtk.ShadowType.OUT)
        else:
            palette_observer_table.show()
            palette_observer_arrow.set(Gtk.ArrowType.DOWN, Gtk.ShadowType.OUT)

    def on_add_submodel_activate(self, button):
        """Callback for the 'add submodel' button"""
        #display file chooser, and parse file
        sub_file = self.main.get_open_filename()
        #make subfile a relative path
        name = "Submodel" + str(len(self.model.modules))
        gsf = emuML.EmuFile(sub_file, 'r', parent_model=self.model, name=name)
        (submodel, subcontrol) = gsf.read()
        for (name, prop) in gsf.get_properties().items():
            submodel = self.model.get_module(name)
            layout = dict()
            for (mod_name, position) in prop['layout'].items():
                if submodel.has_module(mod_name):
                    module = submodel.get_module(mod_name)
                    layout[module] = position
            #then apply the layouts
            self.canvas.apply_layout(layout, submodel=submodel)
        emuML.compile_control(submodel, subcontrol)

    def on_add_create_toggled(self, widget, data=None):
        self.add_module(widget, 'add_create', emulation.CreateAct)

    def on_add_dispose_toggled(self, widget, data=None):
        self.add_module(widget, 'add_dispose', emulation.DisposeAct)

    def on_add_shape_toggled(self, widget, data=None):
        self.add_module(widget, 'add_shape', emulation.ShapeAct)

    def on_add_space_toggled(self, widget, data=None):
        self.add_module(widget, 'add_space', emulation.SpaceAct)

    def on_add_assy_toggled(self, widget, data=None):
        self.add_module(widget, 'add_assy', emulation.AssembleAct)

    def on_add_unassy_toggled(self, widget, data=None):
        self.add_module(widget, 'add_unassy', emulation.DisassembleAct)

    def on_add_holder_toggled(self, widget, data=None):
        self.add_module(widget, 'add_holder', emulation.Holder)

    def on_add_failure_toggled(self, widget, data=None):
        self.add_module(widget, 'add_failure', emulation.Failure)

    def on_add_pushobs_toggled(self, widget, data=None):
        self.add_module(widget, 'add_pushobs', emulation.PushObserver)

    def on_add_pullobs_toggled(self, widget, data=None):
        self.add_module(widget, 'add_pullobs', emulation.PullObserver)

    def add_module(self, widget, my_act, mod_type):
        """Set the canvas in 'adding mode'."""
        act_list = [
            'add_create', 'add_dispose', 'add_shape', 'add_space', 'add_assy',
            'add_unassy', 'add_holder', 'add_failure', 'add_pushobs',
            'add_pullobs'
        ]
        act_list.remove(my_act)
        if widget.get_active():
            for act_name in act_list:
                act = self.builder.get_object(act_name)
                act.set_active(False)
                self.canvas.set_adding(True, mod_type)
        else:
            self.canvas.set_adding(False)

    def on_properties_activate(self, menuitem, data=None):
        """Properties button or menuitem is activated (or clicked)"""
        #get module from selection
        for module in self.canvas.selection:
            prop_win = PropertiesDialog(self.main, module, self.model,
                                        self.cmd_manager)
            prop_win.show()
            self.changed = True

    def on_model_properties_activate(self, menuitem, data=None):
        """Properties of a model"""
        dialog = ModelPropertiesDialog()
        dialog.set_model(self.model)
        self.changed = True
        response = dialog.run()

    def on_emulation_selection_changed(self, source, num_selected):
        """Callback for change in the selection of modules.
        Change sensitivity of some buttons
        """
        value = not (num_selected == 0)
        widgets_names = ['properties', 'cut', 'copy', 'delete']
        for name in widgets_names:
            widget = self.builder.get_object(name)
            widget.set_sensitive(value)

    def on_emulation_add_done(self, source):
        """Callback connect to the add-done signal of the model canvas."""
        act_list = [
            'add_create', 'add_dispose', 'add_shape', 'add_space', 'add_assy',
            'add_unassy', 'add_holder', 'add_pushobs', 'add_pullobs'
        ]
        for act_name in act_list:
            act = self.builder.get_object(act_name)
            act.set_active(False)