preliminary implementation of Barista offline debugging environment for Mocha class libraries

This commit is contained in:
Michael Becker 2024-08-31 00:47:04 -04:00
parent 9da10a15e7
commit 078e5b38c9
20 changed files with 1383 additions and 0 deletions

5
.gitignore vendored
View File

@ -14,3 +14,8 @@
# Python cache
__pycache__
# Built Mocha Class Libraries / eXecutables
*.mcl
*.mcx

View File

@ -0,0 +1,380 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
from gi.repository import Gtk, Gdk, Pango
from mocha.lib.mcx.objectmodels import McxObjectModel, McxInstance, McxAttribute, McxRelationship
from TreeViewFilter2 import TreeViewFilter2
from BaristaSettings import BaristaSettings
class BaristaEditor(Gtk.Box):
def on_search(self, entry : Gtk.SearchEntry):
query = entry.get_text()
self.treeview_filter.update(query)
pass
def get_selected_item(self):
select = self.tv.get_selection()
model, paths = select.get_selected_rows()
if paths is not None:
if len(paths) > 0:
item = self.treeview_filter.data[paths[0].to_string()]
return item
return None
def lookup_selected_inst_instance(self, args):
item = self.get_selected_item()
self.lookup_instance_key(str(item.get_instance_key()))
def lookup_selected_inst_class(self, args):
item = self.get_selected_item()
self.lookup_instance_key('1$' + str(item.get_class_index()))
def search_selected_inst_instance(self, args):
item = self.get_selected_item()
self.txtSearch.set_text(str(item.get_instance_global_identifier()))
def search_selected_inst_class(self, args):
item = self.get_selected_item()
self.txtSearch.set_text(str(item.get_class_global_identifier()))
def search_selected_attr_instance(self, args):
item = self.get_selected_item()
self.txtSearch.set_text(str(item.get_source_instance().get_instance_global_identifier()))
def search_selected_attr_attribute(self, args):
item = self.get_selected_item()
self.txtSearch.set_text(str(item.get_attribute_instance().get_instance_global_identifier()))
def search_selected_rel_instance(self, args):
item = self.get_selected_item()
self.txtSearch.set_text(str(item.get_source_instance().get_instance_global_identifier()))
def search_selected_rel_relationship(self, args):
item = self.get_selected_item()
self.txtSearch.set_text(str(item.get_relationship_instance().get_instance_global_identifier()))
def search_selected_rel_target(self, args):
item = self.get_selected_item()
self.txtSearch.set_text(str(item.get_target_instance().get_instance_global_identifier()))
def lookup_selected_rel_source(self, args):
item = self.get_selected_item()
self.lookup_instance(item.get_source_instance())
def lookup_selected_rel_relationship(self, args):
item = self.get_selected_item()
self.lookup_instance(item.get_relationship_instance())
def lookup_selected_rel_target(self, args):
item = self.get_selected_item()
self.lookup_instance(item.get_target_instance())
def lookup_instance(self, inst : McxInstance):
gid = str(inst.get_instance_key())
self.lookup_instance_key(gid)
def lookup_instance_key(self, key : str):
url = "https://" + self.parent.toolbar.get_selected_suv() + "/super/d/inst/" + key + ".htmld"
import subprocess
subprocess.run(["xdg-open", url])
def update(self):
self.treeview_filter.settings = self.settings
self.treeview_filter.update()
def on_context_menu(self, widget : Gtk.TreeView):
item = self.get_selected_item()
if item is not None:
popup = Gtk.Menu()
if isinstance(item, McxInstance):
mnu = Gtk.MenuItem()
mnu.set_sensitive(False)
mnu.set_label("Instance: " + str(item.get_instance_global_identifier()))
popup.add(mnu)
mnu = Gtk.MenuItem()
mnu.set_label("_Look up MADI Instance")
mnu.connect("activate", self.lookup_selected_inst_instance)
mnu.set_use_underline(True)
popup.add(mnu)
mnu = Gtk.MenuItem()
mnu.set_label("_Look up MADI Parent Class")
mnu.connect("activate", self.lookup_selected_inst_class)
mnu.set_use_underline(True)
popup.add(mnu)
popup.add(Gtk.SeparatorMenuItem())
mnu = Gtk.MenuItem()
mnu.set_label("_Search Instance GUID")
mnu.set_use_underline(True)
mnu.connect("activate", self.search_selected_inst_instance)
popup.add(mnu)
mnu = Gtk.MenuItem()
mnu.set_label("_Search Class GUID")
mnu.set_use_underline(True)
mnu.connect("activate", self.search_selected_inst_class)
popup.add(mnu)
popup.add(Gtk.SeparatorMenuItem())
mnu = Gtk.MenuItem()
mnu.set_label("_Copy Source Instance GUID [" + str(item.get_instance_global_identifier()) + "]")
mnu.set_use_underline(True)
popup.add(mnu)
mnu = Gtk.MenuItem()
mnu.set_label("_Copy Source Instance Key [" + str(item.get_instance_key()) + "]")
mnu.set_use_underline(True)
popup.add(mnu)
elif isinstance(item, McxAttribute):
mnu = Gtk.MenuItem()
mnu.set_sensitive(False)
mnu.set_label("Attribute")
popup.add(mnu)
popup.add(Gtk.SeparatorMenuItem())
mnu = Gtk.MenuItem()
mnu.set_label("_Search Source Instance")
mnu.set_use_underline(True)
mnu.connect("activate", self.search_selected_attr_instance)
popup.add(mnu)
mnu = Gtk.MenuItem()
mnu.set_label("_Search Attribute Instance")
mnu.set_use_underline(True)
mnu.connect("activate", self.search_selected_attr_attribute)
popup.add(mnu)
popup.add(Gtk.SeparatorMenuItem())
mnu = Gtk.MenuItem()
mnu.set_label("_Copy Source Instance GUID [" + str(item.get_source_instance().get_instance_global_identifier()) + "]")
mnu.set_use_underline(True)
popup.add(mnu)
mnu = Gtk.MenuItem()
mnu.set_label("_Copy Source Instance Key [" + str(item.get_source_instance().get_instance_key()) + "]")
mnu.set_use_underline(True)
popup.add(mnu)
popup.add(Gtk.SeparatorMenuItem())
mnu = Gtk.MenuItem()
mnu.set_label("_Copy Attribute Instance GUID [" + str(item.get_attribute_instance().get_instance_global_identifier()) + "]")
mnu.set_use_underline(True)
popup.add(mnu)
mnu = Gtk.MenuItem()
mnu.set_label("_Copy Attribute Instance Key [" + str(item.get_attribute_instance().get_instance_key()) + "]")
mnu.set_use_underline(True)
popup.add(mnu)
popup.add(Gtk.SeparatorMenuItem())
mnu = Gtk.MenuItem()
mnu.set_label("_Copy Value [" + str(item.get_value()) + "]")
mnu.set_use_underline(True)
popup.add(mnu)
elif isinstance(item, McxRelationship):
mnu = Gtk.MenuItem()
mnu.set_sensitive(False)
mnu.set_label("Relationship")
popup.add(mnu)
popup.add(Gtk.SeparatorMenuItem())
mnu = Gtk.MenuItem()
mnu.set_label("_Source Instance")
mnu.set_submenu(Gtk.Menu())
mnu1 = Gtk.MenuItem()
mnu1.set_sensitive(False)
mnu1.set_label(str(item.get_source_instance()))
mnu.get_submenu().add(mnu1)
mnu.get_submenu().add(Gtk.SeparatorMenuItem())
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Look Up MADI Instance")
mnu1.set_use_underline(True)
mnu1.connect("activate", self.lookup_selected_rel_source)
mnu.get_submenu().add(mnu1)
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Search")
mnu1.set_use_underline(True)
mnu1.connect("activate", self.search_selected_rel_instance)
mnu.get_submenu().add(mnu1)
mnu.get_submenu().add(Gtk.SeparatorMenuItem())
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Copy Instance GUID [" + str(item.get_source_instance().get_instance_global_identifier()) + "]")
mnu1.set_use_underline(True)
mnu.get_submenu().add(mnu1)
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Copy Instance Key [" + str(item.get_source_instance().get_instance_key()) + "]")
mnu1.set_use_underline(True)
mnu.get_submenu().add(mnu1)
popup.add(mnu)
mnu = Gtk.MenuItem()
mnu.set_label("_Relationship Instance")
mnu.set_submenu(Gtk.Menu())
mnu1 = Gtk.MenuItem()
mnu1.set_sensitive(False)
mnu1.set_label(str(item.get_relationship_instance()))
mnu.get_submenu().add(mnu1)
mnu.get_submenu().add(Gtk.SeparatorMenuItem())
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Look Up MADI Instance")
mnu1.set_use_underline(True)
mnu1.connect("activate", self.lookup_selected_rel_relationship)
mnu.get_submenu().add(mnu1)
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Search")
mnu1.set_use_underline(True)
mnu1.connect("activate", self.search_selected_rel_relationship)
mnu.get_submenu().add(mnu1)
mnu.get_submenu().add(Gtk.SeparatorMenuItem())
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Copy Instance GUID [" + str(item.get_relationship_instance().get_instance_global_identifier()) + "]")
mnu1.set_use_underline(True)
mnu.get_submenu().add(mnu1)
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Copy Instance Key [" + str(item.get_relationship_instance().get_instance_key()) + "]")
mnu1.set_use_underline(True)
mnu.get_submenu().add(mnu1)
popup.add(mnu)
mnu = Gtk.MenuItem()
mnu.set_label("_Target Instance")
mnu.set_submenu(Gtk.Menu())
mnu1 = Gtk.MenuItem()
mnu1.set_sensitive(False)
mnu1.set_label(str(item.get_target_instance()))
mnu.get_submenu().add(mnu1)
mnu.get_submenu().add(Gtk.SeparatorMenuItem())
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Look Up MADI Instance")
mnu1.set_use_underline(True)
mnu1.connect("activate", self.lookup_selected_rel_target)
mnu.get_submenu().add(mnu1)
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Search")
mnu1.set_use_underline(True)
mnu1.connect("activate", self.search_selected_rel_target)
mnu.get_submenu().add(mnu1)
mnu.get_submenu().add(Gtk.SeparatorMenuItem())
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Copy Instance GUID [" + str(item.get_target_instance().get_instance_global_identifier()) + "]")
mnu1.set_use_underline(True)
mnu.get_submenu().add(mnu1)
mnu1 = Gtk.MenuItem()
mnu1.set_label("_Copy Instance Key [" + str(item.get_target_instance().get_instance_key()) + "]")
mnu1.set_use_underline(True)
mnu.get_submenu().add(mnu1)
popup.add(mnu)
popup.show_all()
popup.popup_at_pointer()
def on_button_press(self, widget : Gtk.TreeView, e : Gdk.EventButton):
if e.button == 3:
# why TF is this not automatic
self.on_context_menu(widget)
def __init__(self, parent, om : McxObjectModel):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
self.row_items = dict()
self.parent = parent
self.settings = parent.settings
sc = Gtk.Paned()
self.txtSearch = Gtk.SearchEntry()
self.txtSearch.connect("search-changed", self.on_search)
self.txtSearch.show()
self.pack_start(self.txtSearch, False, False, 0)
self.ts = Gtk.TreeStore(bool, Pango.Weight, str, str, str, str)
self.om = om
self.tv = Gtk.TreeView()
# self.treeview_filter = TreeViewFilter(self.tv, self.ts, 0, 1)
self.treeview_filter = TreeViewFilter2(om, self.tv, self.ts, 0, 1)
col = Gtk.TreeViewColumn(cell_renderer=Gtk.CellRendererText(), text=2, weight=1)
col.set_title("Source")
self.tv.append_column(col)
col = Gtk.TreeViewColumn(cell_renderer=Gtk.CellRendererText(), text=3, weight=1)
col.set_title("Parent / Attribute / Relationship")
self.tv.append_column(col)
col = Gtk.TreeViewColumn(cell_renderer=Gtk.CellRendererText(), text=4, weight=1)
col.set_title("Value / Target")
self.tv.append_column(col)
col = Gtk.TreeViewColumn(cell_renderer=Gtk.CellRendererText(), text=5, weight=1)
col.set_title("Effective Date")
self.tv.append_column(col)
# tv.set_model(ts)
self.tv.connect("popup-menu", self.on_context_menu)
self.tv.connect("button-press-event", self.on_button_press)
self.tv.show()
tv_wrapper = Gtk.ScrolledWindow()
tv_wrapper.add(self.tv)
tv_wrapper.show()
sc.add1(tv_wrapper)
sc.show()
self.treeview_filter.settings = self.settings
self.treeview_filter.update("")
self.pack_start(sc, True, True, 0)

View File

@ -0,0 +1,21 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
class BaristaSettings:
def __init__(self):
self.show_entity_definitions = False

View File

@ -0,0 +1,142 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
from gi.repository import Gtk, Gio, GLib
class BaristaToolbar (Gtk.Box):
def __init__(self, parent):
super().__init__(orientation=Gtk.Orientation.HORIZONTAL)
self.suvs = [
( "i-0708947a3b88c3f6.privatesuv.com", "i-0708947a3b88c3f6.privatesuv.com", False ),
( " SPOT : ", "", False ),
( "quovii.com", "quovii.com", True )
]
self.suvs_menus = dict()
self.selected_suv = None
i = 0
for (k, v, running) in self.suvs:
act = Gio.SimpleAction.new("suv_menu_activate" + str(i))
act.connect("activate", self.suv_menu_activate)
act.set_enabled(True)
parent.add_action(act)
self.suvs_menus[act] = (k, v, running)
i += 1
self.btn = Gtk.MenuButton()
box2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
menu1 = Gio.Menu()
menu2 = Gio.Menu()
ico_network_server = Gio.ThemedIcon(name="network-server")
sel_suv = None
i = -1
for (k, v, running) in self.suvs:
i += 1
if running:
if self.selected_suv is None:
sel_suv = (k, v, running)
mnuSuv1 = Gio.MenuItem()
mnuSuv1.set_label(k)
mnuSuv1.set_action_and_target_value("win.suv_menu_activate" + str(i), None)
mnuSuv1.set_icon(ico_network_server)
menu2.append_item(mnuSuv1)
menu1.append_section("Running", menu2)
menu2 = Gio.Menu()
i = -1
for (k, v, running) in self.suvs:
i += 1
if not running:
mnuSuv1 = Gio.MenuItem()
mnuSuv1.set_label(k)
mnuSuv1.set_action_and_target_value("win.suv_menu_activate" + str(i), None)
mnuSuv1.set_icon(ico_network_server)
menu2.append_item(mnuSuv1)
menu1.append_section("Custom", menu2)
menu2 = Gio.Menu()
m_ManageSUVConnections = Gio.MenuItem()
m_ManageSUVConnections.set_label("Manage SUV Connections")
menu2.append_item(m_ManageSUVConnections)
m_RefreshSUVConnections = Gio.MenuItem()
m_RefreshSUVConnections.set_label("Refresh SUV Connections")
menu2.append_item(m_RefreshSUVConnections)
menu1.append_section("Manage SUVs", menu2)
self.btn.set_use_popover(True)
self.btn.set_menu_model(menu1)
self.btn_label = Gtk.Label(label="Current SUV: SPOT:\t(Running)")
self.btn_label.set_padding(8, 0)
btnp = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
btnp.pack_start(Gtk.Image.new_from_gicon(ico_network_server, Gtk.IconSize.BUTTON), False, False, 0)
btnp.pack_start(self.btn_label, True, True, 0)
btnp.pack_start(Gtk.Image.new_from_icon_name("go-down", Gtk.IconSize.MENU), False, False, 0)
self.btn.add(btnp)
self.pack_end(self.btn, False, False, 0)
# lbl = Gtk.Label("Current SUV: ")
# self.lm = Gtk.ListStore(str)
# self.cbo = Gtk.ComboBox()
# text = Gtk.CellRendererText()
# self.cbo.pack_start(text, True)
# self.cbo.add_attribute(text, 'text', 0)
# self.cbo.set_model(self.lm)
# for (k, v, running) in self.suvs:
# row = self.lm.append(None)
# self.lm.set_value(row, 0, k + "\t(Running)" if running else k)
# self.cbo.set_active(0)
# self.pack_end(self.cbo, False, False, 0)
# self.pack_end(lbl, False, False, 0)
if not sel_suv is None:
self.set_selected_suv(sel_suv)
def get_selected_suv(self) -> str:
if self.selected_suv is None:
return None
return self.selected_suv[1]
def set_selected_suv(self, suv):
self.selected_suv = suv
self.btn_label.set_label("Current SUV: " + suv[0] + ("\t(Running)" if suv[2] else ""))
def suv_menu_activate(self, arg1, arg2):
self.set_selected_suv(self.suvs_menus[arg1])

View File

@ -0,0 +1,44 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
from gi.repository import Gtk
class BaseTreeViewFilter :
def __init__(self, treeview : Gtk.TreeView, treestore : Gtk.TreeStore, col_visible : int, col_weight : int):
self._treeview = treeview
self._treestore = treestore
self.expand_by_default = False
self.show_subtrees_of_matches = False
self.col_visible = col_visible
self.col_weight = col_weight
self.col_start = 2
self.last_query = ""
self.last_query_case_sensitive = False
def update(self, query : str = None, case_sensitive : bool = None):
if query is None:
query = self.last_query
if case_sensitive is None:
case_sensitive = self.last_query_case_sensitive
self.update_internal(query, case_sensitive)
self.last_query = query
self.last_query_case_sensitive = case_sensitive
def update_internal(self, query : str, case_sensitive : bool):
pass

View File

@ -0,0 +1,218 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
from gi.repository import Gtk, Gdk, Pango
from mocha.lib.mcx.dataformats.McxDataFormat import McxDataFormat
from mocha.lib.mcx.objectmodels.McxObjectModel import McxObjectModel
from BaristaEditor import BaristaEditor
from BaristaToolbar import BaristaToolbar
from BaristaSettings import BaristaSettings
class MainWindow (Gtk.ApplicationWindow):
def __init__(self):
super().__init__()
self.settings = BaristaSettings()
self.settings.show_entity_definitions = False
self.current_editor = None
from os.path import dirname
self.set_default_icon_from_file(dirname(__file__) + '/barista.png')
self.set_icon_name('barista')
self.set_size_request(1000, 700)
self.set_title('Barista - []')
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
accel_group = Gtk.AccelGroup()
self.add_accel_group(accel_group)
menu = Gtk.MenuBar()
Gtk.AccelMap.add_entry("<Barista-MainWindow>/FileOpen", Gdk.KEY_O, Gdk.ModifierType.CONTROL_MASK)
Gtk.AccelMap.add_entry("<Barista-MainWindow>/FileQuit", Gdk.KEY_Q, Gdk.ModifierType.CONTROL_MASK)
mnuFile = Gtk.MenuItem()
mnuFile.set_label("_File")
mnuFile.set_use_underline(True)
mnuFile.set_submenu(Gtk.Menu())
mnuFile.get_submenu().set_accel_group(accel_group)
mnuFileOpen = Gtk.MenuItem()
mnuFileOpen.set_accel_path("<Barista-MainWindow>/FileOpen")
mnuFileOpen.connect("activate", self.mnuFileOpen_activate)
mnuFileOpen.set_label("_Open")
mnuFileOpen.set_use_underline(True)
mnuFile.get_submenu().add(mnuFileOpen)
mnuFileClose = Gtk.MenuItem()
mnuFileClose.connect("activate", self.mnuFileClose_activate)
mnuFileClose.set_label("_Close")
mnuFileClose.set_use_underline(True)
mnuFile.get_submenu().add(mnuFileClose)
mnuFile.get_submenu().add(Gtk.SeparatorMenuItem())
mnuFileExit = Gtk.MenuItem()
mnuFileExit.set_accel_path("<Barista-MainWindow>/FileQuit")
mnuFileExit.connect("activate", self.mnuFileExit_activate)
mnuFileExit.set_label("E_xit")
mnuFileExit.set_use_underline(True)
mnuFile.get_submenu().add(mnuFileExit)
menu.add(mnuFile)
mnuView = Gtk.MenuItem()
mnuView.set_label("_View")
mnuView.set_use_underline(True)
mnuView.set_submenu(Gtk.Menu())
mnuViewEntities = Gtk.CheckMenuItem()
mnuViewEntities.connect("activate", self.mnuViewEntities_activate)
mnuViewEntities.set_label("_Entity Definitions")
mnuViewEntities.set_use_underline(True)
mnuView.get_submenu().add(mnuViewEntities)
menu.add(mnuView)
mnuHelp = Gtk.MenuItem()
mnuHelp.set_submenu(Gtk.Menu())
mnuHelp.set_label("_Help")
mnuHelp.set_use_underline(True)
mnuHelpAbout = Gtk.MenuItem()
mnuHelpAbout.connect("activate", self.mnuHelpAbout_activate)
mnuHelpAbout.set_label("_About")
mnuHelpAbout.set_use_underline(True)
mnuHelp.get_submenu().add(mnuHelpAbout)
menu.add(mnuHelp)
menu.show()
box.pack_start(menu, False, False, 0)
self.toolbar = BaristaToolbar(self)
box.pack_start(self.toolbar, False, False, 0)
self.tabs = Gtk.Notebook()
self.tabs.show()
box.pack_start(self.tabs, True, True, 0)
box.show()
self.add(box)
def create_editor_tab_label(self, filename : str):
from os.path import basename, dirname
tab_label = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
label = Gtk.Label(label=basename(filename))
label2 = Gtk.Label(label=dirname(filename))
label2.set_sensitive(False)
label2.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
lst = Pango.AttrList()
lst.insert(Pango.attr_size_new_absolute(Pango.SCALE * 10))
label2.set_attributes(lst)
tab_label.pack_start(label, True, True, 0)
label.show()
tab_label.pack_start(label2, True, True, 0)
label2.show()
tab_label.show()
return tab_label
def add_editor_tab(self, filename : str):
f = open(filename, 'rb')
df = McxDataFormat()
om = McxObjectModel()
df.load(om, f)
print("barista: creating editor")
box = self.create_editor(om)
self.tabs.add(box)
box.show()
self.tabs.set_tab_label(box, self.create_editor_tab_label(filename))
f.close()
def create_editor(self, om : McxObjectModel) -> BaristaEditor:
box = BaristaEditor(self, om)
self.current_editor = box
self.current_editor.settings = self.settings
return box
def mnuFileOpen_activate(self, args):
dlg = Gtk.FileChooserDialog(title="Open File", action=Gtk.FileChooserAction.OPEN)
dlg.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
filtr = Gtk.FileFilter()
filtr.add_pattern("*.mcx")
filtr.add_pattern("*.mcl")
filtr.set_name("Mocha library or executable")
dlg.add_filter(filtr)
ret = dlg.run()
filenames = dlg.get_filenames()
dlg.destroy()
if ret == Gtk.ResponseType.OK:
for filename in filenames:
self.add_editor_tab (filename)
def mnuFileClose_activate(self, args):
if self.tabs.get_n_pages() <= 1:
self.close()
else:
pass
def mnuFileExit_activate(self, args):
self.app.stop()
def mnuViewEntities_activate(self, widget : Gtk.CheckMenuItem):
if widget.get_active():
self.settings.show_entity_definitions = True
else:
self.settings.show_entity_definitions = False
if not self.current_editor is None:
self.current_editor.update()
def mnuHelpAbout_activate(self, args):
dlg = Gtk.AboutDialog()
from os.path import dirname
dlg.set_title("About Barista")
dlg.set_program_name("Barista")
dlg.set_version("1.2")
dlg.set_license_type(Gtk.License.GPL_3_0)
dlg.set_authors([ "Michael Becker" ])
dlg.set_copyright("Copyright ©2024 Mike Becker's Software")
dlg.set_comments("View and manipulate compiled Mocha class library packages")
dlg.run()
dlg.destroy()

View File

@ -0,0 +1,89 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
from BaseTreeViewFilter import BaseTreeViewFilter
from gi.repository import Gtk, Pango
class TreeViewFilter (BaseTreeViewFilter) :
def __init__(self, treeview : Gtk.TreeView, treestore : Gtk.TreeStore, col_visible : int, col_weight : int):
super().__init__(treeview, treestore, col_visible, col_weight)
self.filter = self._treestore.filter_new()
self.filter.set_visible_column(self.col_visible)
self._treeview.set_model(self.filter)
def _reset_row(self, model, path, iter, make_visible):
self._treestore.set_value(iter, self.col_weight, Pango.Weight.NORMAL)
self._treestore.set_value(iter, self.col_visible, make_visible)
def update(self, query : str, case_sensitive : bool = False):
search_query = query.strip()
if not case_sensitive:
search_query = search_query.lower()
if search_query == "":
self._treeview.freeze_child_notify()
self._treestore.foreach(self._reset_row, True)
if self.expand_by_default:
self._treeview.expand_all()
# else:
# self._treeview.collapse_all()
self._treeview.thaw_child_notify()
else:
# self._treestore.foreach(self._reset_row, False)
self._treeview.freeze_child_notify()
self._treestore.foreach(self._show_matches, search_query, self.show_subtrees_of_matches)
# self._treeview.expand_all()
self._treeview.thaw_child_notify()
self.filter.refilter()
def _make_path_visible(self, model, iter):
"Make a row and its ancestors visible"
while iter:
self._treestore.set_value(iter, self.col_visible, True)
iter = model.iter_parent(iter)
def _make_subtree_visible(self, model, iter):
"Make descendants of a row visible"
for i in range(model.iter_n_children(iter)):
subtree = model.iter_nth_child(iter, i)
if model.get_value(subtree, self.col_visible):
# Subtree already visible
continue
self._treestore.set_value(subtree, self.col_visible, True)
self.make_subtree_visible(model, subtree)
def _show_matches(self, model, path, iter, search_query, show_subtrees_of_matches):
for i in range(self.col_start, self._treestore.get_n_columns()):
text = str(model.get_value(iter, i)).lower()
if search_query in text:
# Highlight direct match with bold
self._treestore.set_value(iter, self.col_weight, Pango.Weight.BOLD)
self._treestore.set_value(iter, self.col_visible, True)
# Propagate visibility change up
self._make_path_visible(model, iter)
if show_subtrees_of_matches:
# Propagate visibility change down
self._make_subtree_visible(model, iter)
else:
self._treestore.set_value(iter, self.col_weight, Pango.Weight.NORMAL)
self._treestore.set_value(iter, self.col_visible, False)

View File

@ -0,0 +1,128 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
from BaseTreeViewFilter import BaseTreeViewFilter
from mocha.lib.mcx.dataformats.McxDataFormat import McxDataFormat
from mocha.lib.mcx.objectmodels.McxObjectModel import McxObjectModel
from gi.repository import Gtk, Pango
from BaristaSettings import BaristaSettings
class TreeViewFilter2 (BaseTreeViewFilter):
def __init__(self, om : McxObjectModel, treeview : Gtk.TreeView, treestore : Gtk.TreeStore, col_visible : int, col_weight : int):
super().__init__(treeview, treestore, col_visible, col_weight)
treeview.set_model(treestore)
self.om = om
self.data = dict()
self.settings = BaristaSettings()
def update_internal(self, query : str, case_sensitive : bool = False):
self._treestore.clear()
query = query.strip()
if not case_sensitive:
query = query.lower()
row = self._treestore.append(None)
self.data[self._treestore.get_path(row).to_string()] = None
self._treestore.set_value(row, 2, 'Instances')
self._treestore.set_value(row, self.col_visible, True)
self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL)
print("barista: adding instances")
for item in self.om.get_instances():
if not (query == "" or (query in str(item.get_instance_global_identifier())) or (query in str(item.get_class_global_identifier())) or (query in str(item.get_instance_key()))):
continue
row2 = self._treestore.append(row)
self.data[self._treestore.get_path(row2).to_string()] = item
self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL)
gid = str(item.get_instance_global_identifier())
print("gid in has_entity_value")
if self.settings.show_entity_definitions and self.om.has_entity_value(gid):
gid = self.om.get_entity_key(gid)
cid = str(item.get_class_global_identifier())
if self.settings.show_entity_definitions and self.om.has_entity_value(cid):
cid = self.om.get_entity_key(cid)
self._treestore.set_value(row2, 2, gid + " [" + str(item.get_instance_key()) + "]")
self._treestore.set_value(row2, 3, cid)
row = self._treestore.append(None)
self.data[self._treestore.get_path(row).to_string()] = None
self._treestore.set_value(row, 2, 'Attributes')
self._treestore.set_value(row, self.col_visible, True)
self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL)
print("barista: adding attributes")
for item in self.om.get_attributes():
if not (query == "" or (query in str(item.get_source_instance())) or (query in str(item.get_attribute_instance())) or (query in str(item.get_value()).lower())):
continue
s_gid = str(item.get_source_instance().get_instance_global_identifier()) if item.get_source_instance() is not None else ""
if self.settings.show_entity_definitions and self.om.has_entity_value(s_gid):
s_gid = self.om.get_entity_key(s_gid)
a_gid = str(item.get_attribute_instance().get_instance_global_identifier()) if item.get_attribute_instance() is not None else ""
if self.settings.show_entity_definitions and self.om.has_entity_value(a_gid):
a_gid = self.om.get_entity_key(a_gid)
row2 = self._treestore.append(row)
self.data[self._treestore.get_path(row2).to_string()] = item
self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL)
self._treestore.set_value(row2, 2, s_gid)
self._treestore.set_value(row2, 3, a_gid)
self._treestore.set_value(row2, 4, str(item.get_value()))
row = self._treestore.append(None)
self.data[self._treestore.get_path(row).to_string()] = None
self._treestore.set_value(row, 2, 'Relationships')
self._treestore.set_value(row, self.col_visible, True)
self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL)
rels = True
if rels:
if len(query) < 5:
return
print("barista: adding relationships")
for item in self.om.get_relationships():
if not (query == "" or (query in str(item.get_source_instance())) or (query in str(item.get_relationship_instance())) or (query in str(item.get_target_instance()))):
continue
s_gid = str(item.get_source_instance().get_instance_global_identifier()) if item.get_source_instance() is not None else ""
if self.settings.show_entity_definitions and self.om.has_entity_value(s_gid):
s_gid = self.om.get_entity_key(s_gid)
r_gid = str(item.get_relationship_instance().get_instance_global_identifier()) if item.get_relationship_instance() is not None else ""
if self.settings.show_entity_definitions and self.om.has_entity_value(r_gid):
r_gid = self.om.get_entity_key(r_gid)
t_gid = str(item.get_target_instance().get_instance_global_identifier()) if item.get_target_instance() is not None else ""
if self.settings.show_entity_definitions and self.om.has_entity_value(t_gid):
t_gid = self.om.get_entity_key(t_gid)
row2 = self._treestore.append(row)
self.data[self._treestore.get_path(row2).to_string()] = item
self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL)
self._treestore.set_value(row2, 2, s_gid)
self._treestore.set_value(row2, 3, r_gid)
self._treestore.set_value(row2, 4, t_gid)

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,32 @@
from framework.desktop import DesktopApplication
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Pango
from mocha.lib.mcx.dataformats.McxDataFormat import McxDataFormat
from mocha.lib.mcx.objectmodels.McxObjectModel import McxObjectModel
from MainWindow import MainWindow
class Program (DesktopApplication):
def __init__(self):
super().__init__('com.mochapowered.python.barista')
def startup(self, args):
from gi.repository import GLib
GLib.set_prgname('barista')
def activate(self, args):
wnd = MainWindow()
wnd.app = self
self.app.add_window(wnd)
wnd.show_all()
if __name__ == '__main__':
Program().start()

View File

@ -0,0 +1 @@
../mocha

View File

@ -14,6 +14,7 @@ class InstanceCache:
self.dbids_gid = dict()
def add_instance(self, inst_key, inst_gid):
print ("registering instance key " + str(inst_key) + " = " + str(inst_gid))
self.inst_guids[inst_key] = inst_gid
self.inst_indices[inst_gid] = inst_key

View File

@ -0,0 +1,144 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of yaml2mcl.
#
# yaml2mcl is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# yaml2mcl is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with yaml2mcl. If not, see <https://www.gnu.org/licenses/>.
from editor.core import ObjectModel, DataFormat, InvalidDataFormatException, ObjectModelNotSupportedException
from editor.core.io import Reader
from io import FileIO
from ..objectmodels import McxObjectModel, McxInstance, McxAttribute, McxRelationship
class McxDataFormat (DataFormat):
def __init__(self):
pass
def get_signature(self) -> str:
return "MCX!"
def load_internal(self, object_model : ObjectModel, stream : FileIO):
if not isinstance(object_model, McxObjectModel):
raise ObjectModelNotSupportedException
r = Reader(stream)
# SectionFile header
signature = r.read_fixedstring(4)
if signature != self.get_signature():
raise InvalidDataFormatException
version = r.read_single()
if version != 2.0:
raise InvalidDataFormatException
flags = r.read_int32()
section_count = r.read_int32()
guid = r.read_guid()
sections = [ ]
for i in range(0, section_count):
section_name = r.read_fixedstring(16).strip(' \0')
offset = r.read_int32()
length = r.read_int32()
length2 = r.read_int32()
itemcount = r.read_int32()
print(' '+section_name)
sections.append((section_name, offset, length, length2, itemcount))
gids = [ ]
insts = dict()
strs = [ ]
for (section_name, offset, length, length2, itemcount) in sections:
if section_name == 'GUIDTable':
print("mcx: reading GUID table")
r.get_stream().seek(offset)
for i in range(0, itemcount):
gids.append(r.read_guid())
elif section_name == 'StringTable':
print("mcx: reading string table")
r.get_stream().seek(offset)
for i in range(0, itemcount):
strs.append(r.read_terminatedstring())
elif section_name == 'Defs':
print("mcx: reading definitions")
r.get_stream().seek(offset)
for i in range(0, itemcount):
key = r.read_terminatedstring()
value = r.read_terminatedstring()
object_model.define_entity(key, value.lower())
for (section_name, offset, length, length2, itemcount) in sections:
if section_name == 'Instances':
print("mcx: reading instances")
r.get_stream().seek(offset)
for i in range(0, itemcount):
cgidi = r.read_int32()
cgid = gids[cgidi]
igidi = r.read_int32()
igid = gids[igidi]
ckey = r.read_int32()
ikey = r.read_int32()
inst = McxInstance(cgid, igid, ckey, ikey)
insts[igid] = inst
object_model.get_instances().append(inst)
elif section_name == 'Attributes':
print("mcx: reading attributes")
r.get_stream().seek(offset)
for i in range(0, itemcount):
srci = r.read_int32()
src = gids[srci]
atti = r.read_int32()
att = gids[atti]
vali = r.read_int32()
val = strs[vali]
isrc = insts[src] if src in insts else None
iatt = insts[att] if att in insts else None
object_model.get_attributes().append(McxAttribute(isrc, iatt, val))
elif section_name == 'Relationships':
print("mcx: reading relationships")
r.get_stream().seek(offset)
for i in range(0, itemcount):
srci = r.read_int32()
src = gids[srci]
reli = r.read_int32()
rel = gids[reli]
tgti = r.read_int32()
tgt = gids[tgti]
isrc = insts[src] if src in insts else None
irel = insts[rel] if rel in insts else None
itgt = insts[tgt] if tgt in insts else None
object_model.get_relationships().append(McxRelationship(isrc, irel, itgt))
def save_internal(self, object_model : ObjectModel, stream : FileIO):
pass

View File

@ -0,0 +1 @@
from .McxDataFormat import McxDataFormat

View File

@ -0,0 +1,33 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
from .McxInstance import McxInstance
class McxAttribute:
def __init__(self, source_instance : McxInstance, attribute_instance : McxInstance, value : any):
self._source_instance = source_instance
self._attribute_instance = attribute_instance
self._value = value
def get_source_instance(self) -> McxInstance:
return self._source_instance
def get_attribute_instance(self) -> McxInstance:
return self._attribute_instance
def get_value(self):
return self._value

View File

@ -0,0 +1,44 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
from framework import Guid
class McxInstance:
def __init__(self, class_global_identifier : Guid, instance_global_identifier : Guid, class_index : int, instance_index : int):
self._class_global_identifier = class_global_identifier
self._instance_global_identifier = instance_global_identifier
self._class_index = class_index
self._instance_index = instance_index
def get_instance_global_identifier(self) -> Guid:
return self._instance_global_identifier
def get_class_global_identifier(self) -> Guid:
return self._class_global_identifier
def get_class_index(self) -> int:
return self._class_index
def get_instance_index(self) -> int:
return self._instance_index
def get_instance_key(self) -> str:
return str(self._class_index) + '$' + str(self._instance_index)
def __str__(self) -> str:
return str(self.get_instance_global_identifier()) + " [" + str(self.get_class_index()) + "$" + str(self.get_instance_index()) + "]"

View File

@ -0,0 +1,39 @@
from .McxInstance import McxInstance
from .McxAttribute import McxAttribute
from .McxRelationship import McxRelationship
from editor.core import ObjectModel
class McxObjectModel (ObjectModel):
def __init__(self):
super().__init__()
self._instances = list[McxInstance]()
self._attributes = list[McxAttribute]()
self._relationships = list[McxRelationship]()
self.__entityDefinitionsKeys = dict()
self.__entityDefinitionsValues = dict()
def get_instances(self) -> list[McxInstance]:
return self._instances
def get_attributes(self) -> list[McxAttribute]:
return self._attributes
def get_relationships(self) -> list[McxRelationship]:
return self._relationships
def define_entity(self, key : str, value : str):
self.__entityDefinitionsKeys[value] = key
self.__entityDefinitionsValues[key] = value
def get_entity_key(self, value : str):
return self.__entityDefinitionsKeys[value]
def get_entity_value(self, key : str):
return self.__entityDefinitionsValues[key]
def has_entity_key(self, key : str):
return key in self.__entityDefinitionsValues
def has_entity_value(self, value : str):
return value in self.__entityDefinitionsKeys

View File

@ -0,0 +1,36 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
from .McxInstance import McxInstance
class McxRelationship:
def __init__(self, source_instance : McxInstance, relationship_instance : McxInstance, target_instance : McxInstance):
self._source_instance = source_instance
self._relationship_instance = relationship_instance
self._target_instance = target_instance
def get_source_instance(self) -> McxInstance:
return self._source_instance
def get_relationship_instance(self) -> McxInstance:
return self._relationship_instance
def get_target_instance(self) -> McxInstance:
return self._target_instance
def __str__(self) -> str:
return str(self._source_instance) + " : " + str(self._relationship_instance) + " = " + str(self._target_instance)

View File

@ -0,0 +1,3 @@
class McxSection :
pass

View File

@ -0,0 +1,22 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# This file is part of mocha-python.
#
# mocha-python is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# mocha-python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with mocha-python. If not, see <https://www.gnu.org/licenses/>.
from .McxAttribute import McxAttribute
from .McxInstance import McxInstance
from .McxObjectModel import McxObjectModel
from .McxRelationship import McxRelationship
from .McxSection import McxSection