diff -Nru editobj3-0.1+ds1/debian/changelog editobj3-0.2+ds1/debian/changelog --- editobj3-0.1+ds1/debian/changelog 2019-11-12 20:52:19.000000000 +0000 +++ editobj3-0.2+ds1/debian/changelog 2022-08-17 20:36:47.000000000 +0000 @@ -1,3 +1,16 @@ +editobj3 (0.2+ds1-1) unstable; urgency=medium + + [ Debian Janitor ] + * Bump debhelper from old 12 to 13. + * Set upstream metadata fields: Repository. + * Update standards version to 4.5.1, no changes needed. + + [ Sebastian Ramacher ] + * New upstream version 0.2+ds1 + * Bump Standards-Version + + -- Sebastian Ramacher Wed, 17 Aug 2022 22:36:47 +0200 + editobj3 (0.1+ds1-2) unstable; urgency=medium * Upload to unstable diff -Nru editobj3-0.1+ds1/debian/control editobj3-0.2+ds1/debian/control --- editobj3-0.1+ds1/debian/control 2019-11-04 19:39:59.000000000 +0000 +++ editobj3-0.2+ds1/debian/control 2022-08-17 20:36:30.000000000 +0000 @@ -4,11 +4,11 @@ Maintainer: Debian Multimedia Maintainers Uploaders: Sebastian Ramacher Build-Depends: - debhelper-compat (= 12), + debhelper-compat (= 13), dh-python, python3-all, python3-setuptools -Standards-Version: 4.4.1 +Standards-Version: 4.6.1 Homepage: http://www.lesfleursdunormal.fr/static/informatique/editobj/index_en.html Vcs-Git: https://salsa.debian.org/multimedia-team/editobj3.git Vcs-Browser: https://salsa.debian.org/multimedia-team/editobj3 diff -Nru editobj3-0.1+ds1/debian/copyright editobj3-0.2+ds1/debian/copyright --- editobj3-0.1+ds1/debian/copyright 2019-11-04 19:39:59.000000000 +0000 +++ editobj3-0.2+ds1/debian/copyright 2022-08-17 20:34:29.000000000 +0000 @@ -50,7 +50,7 @@ Lesser General Public License for more details. . You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . . On Debian systems, the complete text of the GNU Lesser General - Public License can be found in "/usr/share/common-licenses/LGPL-3". + Public License can be found in "/usr/share/common-licenses/LGPL-3". \ No newline at end of file diff -Nru editobj3-0.1+ds1/debian/upstream/metadata editobj3-0.2+ds1/debian/upstream/metadata --- editobj3-0.1+ds1/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ editobj3-0.2+ds1/debian/upstream/metadata 2022-08-17 20:34:29.000000000 +0000 @@ -0,0 +1,2 @@ +--- +Repository: https://bitbucket.org/jibalamy/editobj3 diff -Nru editobj3-0.1+ds1/Editobj3.egg-info/PKG-INFO editobj3-0.2+ds1/Editobj3.egg-info/PKG-INFO --- editobj3-0.1+ds1/Editobj3.egg-info/PKG-INFO 2017-09-10 19:38:38.000000000 +0000 +++ editobj3-0.2+ds1/Editobj3.egg-info/PKG-INFO 2022-08-02 09:10:06.000000000 +0000 @@ -1,60 +1,11 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: Editobj3 -Version: 0.1 +Version: 0.2 Summary: An automatic dialog box generator for Python objects, supporting multiple graphical backends: Qt, GTK and HTML (single-user or with multiple users). Home-page: https://bitbucket.org/jibalamy/editobj3 Author: Lamy Jean-Baptiste (Jiba) Author-email: jibalamy@free.fr License: LGPLv3+ -Description: Editobj3 - ======== - - Editobj3 is an automatic dialog box generator for Python objects. It supports several backends; currenlty - Qt, GTK and HTML are supported. The HTML backend is based on `W2UI `_, and can be used - either in local single user mode, or in distributed multiple users mode. - - Editobj3 dialog boxes are composed of an attribute list, a luxurious good-looking but useless icon and - title bar, and a tree view (if the edited object is part of a tree-like structure). Editobj3 includes an - advanced introspection module that usually guesses how to edit any object; it can also be customized for a - given class of object through the editobj3.introsp module. Editobj3 also supports the simultaneous - edition of a group of objects, as if they were a single object. - - Additional helper modules are included: - - - editobj3.observe: Observation framework - - - editobj3.undoredo: Multiple undo/redo framework - - - editobj3.http_ws_server: HTTP server with WebSocket support, with an interface similar to Python's http.server module - - Editobj3 has been created by Jean-Baptiste Lamy. It is available under the GNU LGPL licence. - - In case of trouble, please contact Jean-Baptiste Lamy - - - Installation - ------------ - - First untar the tarball. - - EditObj 3 uses Python's DistUtils for installation. To install, type (as root): - - cd EditObj3-* - python3 ./setup.py install - - By default, EditObj 3 is installed in /usr, you can modify the - setup.cfg file if you prefer another location. - - - Links - ----- - - Editobj3 on BitBucket (development repository): https://bitbucket.org/jibalamy/editobj3 - - Mail me for any comment, problem, suggestion or help ! - - Jiba -- Jean-Baptiste LAMY -- jibalamy @ free.fr - Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers @@ -68,3 +19,55 @@ Classifier: Topic :: Scientific/Engineering :: Human Machine Interfaces Classifier: Topic :: Software Development :: User Interfaces Classifier: Topic :: Software Development :: Libraries :: Python Modules +License-File: LICENSE.txt + +Editobj3 +======== + +Editobj3 is an automatic dialog box generator for Python objects. It supports several backends; currenlty +Qt, GTK and HTML are supported. The HTML backend is based on `W2UI `_, and can be used +either in local single user mode, or in distributed multiple users mode. + +Editobj3 dialog boxes are composed of an attribute list, a luxurious good-looking but useless icon and +title bar, and a tree view (if the edited object is part of a tree-like structure). Editobj3 includes an +advanced introspection module that usually guesses how to edit any object; it can also be customized for a +given class of object through the editobj3.introsp module. Editobj3 also supports the simultaneous +edition of a group of objects, as if they were a single object. + +Additional helper modules are included: + + - editobj3.observe: Observation framework + + - editobj3.undoredo: Multiple undo/redo framework + + - editobj3.http_ws_server: HTTP server with WebSocket support, with an interface similar to Python's http.server module + +Editobj3 has been created by Jean-Baptiste Lamy. It is available under the GNU LGPL licence. + +In case of trouble, please contact Jean-Baptiste Lamy + + +Installation +------------ + +First untar the tarball. + +EditObj 3 uses Python's DistUtils for installation. To install, type (as root): + +cd EditObj3-* +python3 ./setup.py install + +By default, EditObj 3 is installed in /usr, you can modify the +setup.cfg file if you prefer another location. + + +Links +----- + +Editobj3 on BitBucket (development repository): https://bitbucket.org/jibalamy/editobj3 + +Mail me for any comment, problem, suggestion or help ! + +Jiba -- Jean-Baptiste LAMY -- jibalamy @ free.fr + + diff -Nru editobj3-0.1+ds1/Editobj3.egg-info/SOURCES.txt editobj3-0.2+ds1/Editobj3.egg-info/SOURCES.txt --- editobj3-0.1+ds1/Editobj3.egg-info/SOURCES.txt 2017-09-10 19:38:38.000000000 +0000 +++ editobj3-0.2+ds1/Editobj3.egg-info/SOURCES.txt 2022-08-02 09:10:07.000000000 +0000 @@ -67,47 +67,35 @@ doc/html/py-modindex.html doc/html/search.html doc/html/searchindex.js -doc/html/.doctrees/environment.pickle -doc/html/.doctrees/index.doctree doc/html/_images/editobj3_gtk.png doc/html/_images/editobj3_html.png doc/html/_sources/index.rst.txt -doc/html/_sources/index.txt -doc/html/_static/ajax-loader.gif +doc/html/_static/_sphinx_javascript_frameworks_compat.js doc/html/_static/basic.css -doc/html/_static/comment-bright.png -doc/html/_static/comment-close.png -doc/html/_static/comment.png -doc/html/_static/default.css doc/html/_static/dialog-note.png doc/html/_static/dialog-seealso.png doc/html/_static/dialog-todo.png doc/html/_static/dialog-topic.png doc/html/_static/dialog-warning.png doc/html/_static/doctools.js -doc/html/_static/down-pressed.png -doc/html/_static/down.png +doc/html/_static/documentation_options.js doc/html/_static/epub.css doc/html/_static/file.png doc/html/_static/footerbg.png doc/html/_static/headerbg.png doc/html/_static/ie6.css -doc/html/_static/jquery-1.11.1.js -doc/html/_static/jquery-3.1.0.js +doc/html/_static/jquery-3.6.0.js doc/html/_static/jquery.js +doc/html/_static/language_data.js doc/html/_static/middlebg.png doc/html/_static/minus.png doc/html/_static/plus.png doc/html/_static/pygments.css doc/html/_static/pyramid.css doc/html/_static/searchtools.js -doc/html/_static/sidebar.js doc/html/_static/transparent.gif -doc/html/_static/underscore-1.3.1.js +doc/html/_static/underscore-1.13.1.js doc/html/_static/underscore.js -doc/html/_static/up-pressed.png -doc/html/_static/up.png -doc/html/_static/websupport.js doc/images/editobj3_gtk.png doc/images/editobj3_html.png icons/add.svg diff -Nru editobj3-0.1+ds1/editor.py editobj3-0.2+ds1/editor.py --- editobj3-0.1+ds1/editor.py 2016-03-07 20:48:48.000000000 +0000 +++ editobj3-0.2+ds1/editor.py 2019-09-05 15:16:28.000000000 +0000 @@ -100,7 +100,12 @@ self.editor_panes[name] = editor_pane = EditorPane(self.gui, self, self.edit_child_in_self, self.undo_stack, self.direction) if not o is None: editor_pane.edit(o) return editor_pane - + + def show_tab(self, editor_pane): + if editor_pane.func: + o = editor_pane.func() + editor_pane.edit(o) + def remove_tab(self, name): del self.editor_panes[name] def get_current_editor_pane(self): raise NotImplementedError def get_selected_object(self): return self.get_current_editor_pane().o @@ -142,13 +147,14 @@ class AttributePane(MultiGUIEditor, PaneRepartitor): def __init__(self, gui, master, edit_child = None, undo_stack = None): - self.gui = gui - self.master = master - self.undo_stack = undo_stack or undoredo.stack - self.o = None - self.attributes = [] - self.ignored_attrs = set() - self.pane_repartitor = self + self.gui = gui + self.master = master + self.undo_stack = undo_stack or undoredo.stack + self.o = None + self.attributes = [] + self.attr_2_attribute = {} + self.ignored_attrs = set() + self.pane_repartitor = self if edit_child: self.edit_child = edit_child @@ -171,33 +177,58 @@ field = self.fields.get(attr) if field: field.update() + def _listener2(self, o, diffs): + from owlready2 import IRIS + diffs2 = [] + for attr, new, old in diffs: + pred = IRIS.get(attr) + if pred: diffs2.append((pred.python_name, new, old)) + else: diffs2.append((attr, new, old)) + diffs = diffs2 + + for attr, new, old in diffs: + if (not attr in self.attr_2_attribute) and (old == []): + self._re_edit() + return + + need_updates = { attr for attr, new_val, old_val in diffs } + need_updates.update(attribute.name for attribute in self.property_attributes) + need_updates.update(attribute.name for attribute in self.descr.property_attributes_of(self.o)) + + for attr in need_updates: + field = self.fields.get(attr) + if field: field.update() + def _destroyed(self, *args): observe.unobserve(self.o, self._listener) def edit(self, o, ignored_attrs = None): + if not self.o is None: + if self.descr.is_owl_entity: + import owlready2.observe + owlready2.observe.unobserve(self.o, owlready2.observe.CollapsedListener(self._listener2)) + else: + observe.unobserve(self.o, self._listener) + self.descr = introsp.description_for_object(o) if ignored_attrs: self.ignored_attrs = ignored_attrs elif self.ignored_attrs: self.ignored_attrs = set() - if hasattr(o, "__edited__"): o.__edited__(self) - - if o is None: attributes = [] - else: attributes = [attribute for attribute in self.descr.attributes_of(o) if not attribute.name in self.ignored_attrs] - + if o is None: + attributes = [] + else: + if hasattr(o, "__edited__"): o.__edited__(self) + attributes = [attribute for attribute in self.descr.attributes_of(o) if not attribute.name in self.ignored_attrs] + if o and self.o and (self.attributes == attributes): - if not self.o is None: - observe.unobserve(self.o, self._listener) - self.o = o self.property_attributes = self.descr.property_attributes_of(o) if not o is None: for field in self.fields.values(): field.edit(o) - observe.observe(self.o, self._listener) else: if not self.o is None: - observe.unobserve(self.o, self._listener) self._delete_all_fields() self.o = o @@ -216,7 +247,13 @@ self.fields[attribute.name] = self._new_field(attribute, field_class, attribute.unit_for(o), i) i += 1 + if not o is None: + if self.descr.is_owl_entity: + import owlready2.observe + owlready2.observe.observe(self.o, owlready2.observe.CollapsedListener(self._listener2)) + else: observe.observe(self.o, self._listener) + edit_child = edit def _delete_all_fields(self): pass @@ -232,8 +269,13 @@ def edit(self, o): if o is self.o: return - if not self.o is None: observe.unobserve(self.o, self._listener) - + if not self.o is None: + if self.descr.is_owl_entity: + import owlready2.observe + owlready2.observe.unobserve(self.o, owlready2.observe.CollapsedListener(self._listener)) + else: + observe.unobserve(self.o, self._listener) + self.o = o self.descr = introsp.description_for_object(o) @@ -241,17 +283,26 @@ if not o is None: self._update() - observe.observe(self.o, self._listener) + if self.descr.is_owl_entity: + import owlready2.observe + owlready2.observe.observe(self.o, owlready2.observe.CollapsedListener(self._listener)) + else: + observe.observe(self.o, self._listener) else: self._set_icon_filename_label_details("", "", "") - def _listener(self, o, type, new, old): self._update() + def _listener(self, o, type = None, new = None, old = None): self._update() def _update(self): self._set_icon_filename_label_details(self.descr.icon_filename_for(self.o), self.descr.label_for(self.o), self.descr.details_for(self.o)) def _set_icon_filename_label_details(self, icon_filename, label, details): pass - def _destroyed(self, *args): observe.unobserve(self.o, self._listener) + def _destroyed(self, *args): + if self.descr.is_owl_entity: + import owlready2.observe + owlready2.observe.unobserve(self.o, owlready2.observe.CollapsedListener(self._listener)) + else: + observe.unobserve(self.o, self._listener) def _not_empty(g): @@ -274,23 +325,32 @@ self.o = o can_reorder = can_remove = can_add = False - if attribute: - if isinstance(o, introsp.ObjectPack): - for i in range(len(o.objects)): - if attribute[i]: - if attribute[i].can_reorder (parent.objects[i]): can_reorder = True - if attribute[i].can_remove_from(parent.objects[i]): can_remove = True - if attribute[i].can_add_to (parent.objects[i]) and attribute[i].addable_values_to(parent.objects[i]): can_add = True - else: - can_reorder = attribute.can_reorder(parent) - can_remove = attribute.can_remove_from(parent) - can_add = attribute.can_add_to(parent) and attribute.addable_values_to(parent) - if not can_add: + if parent is o: # ObjectListField / flat list => edit the hidden "root" element + if attribute: + if isinstance(o, introsp.ObjectPack): + for i in range(len(o.objects)): + if attribute[i]: + if attribute[i].can_add_to (parent.objects[i]) and attribute[i].addable_values_to(parent.objects[i]): can_add = True + else: + can_add = attribute.can_add_to(parent) and attribute.addable_values_to(parent) + + else: + if attribute: + if isinstance(o, introsp.ObjectPack): + for i in range(len(o.objects)): + if attribute[i]: + if attribute[i].can_reorder (parent.objects[i]): can_reorder = True + if attribute[i].can_remove_from(parent.objects[i]): can_remove = True + if attribute[i].can_add_to (parent.objects[i]) and attribute[i].addable_values_to(parent.objects[i]): can_add = True + else: + can_reorder = attribute.can_reorder(parent) + can_remove = attribute.can_remove_from(parent) + can_add = attribute.can_add_to(parent) and attribute.addable_values_to(parent) + if (not can_add) and not attribute: for o_attribute in introsp.description_for_object(o).attributes_of(o): if (o_attribute.can_add_to(o) and o_attribute.addable_values_to(o)): can_add = True break - self.set_button_visibilities(can_reorder, can_add, can_remove, can_reorder) def set_button_visibilities(self, visible1, visible2, visible3, visible4): pass @@ -329,21 +389,24 @@ def on_move_down(self, *args): introsp.description_for_object(self.parent_o).do_action(introsp._MOVE_DOWN_ACTION, self.undo_stack, self.hierarchy_pane.get_editor(), self.parent_o, self.attribute, self.o) - + + class HierarchyPane(MultiGUIEditor, PaneRepartitor): Node = None def __init__(self, gui, master, edit_child, undo_stack = None, restrict_to_attribute = None, flat_list = False): - self.gui = gui - self.master = master - self.edit_child = edit_child - self.o = None - self.root_node = None - self.undo_stack = undo_stack or undoredo.stack - self.childhood_pane = None - self.restrict_to_attribute = restrict_to_attribute - self.flat_list = flat_list - self.pane_repartitor = self + self.gui = gui + self.master = master + self.edit_child = edit_child + self.o = None + self.root_node = None + self.undo_stack = undo_stack or undoredo.stack + self.childhood_pane = None + self.childhood_pane_parent = None + self.childhood_pane_attribute = None + self.restrict_to_attribute = restrict_to_attribute + self.flat_list = flat_list + self.pane_repartitor = self def is_displayed_in_hierarchy_pane(self, attribute, o, field_class = None): if not field_class: field_class = attribute.field_class_for(o) @@ -353,8 +416,10 @@ if not field_class: field_class = attribute.field_class_for(o) return field_class.display_in_attribute_pane - def set_childhood_pane(self, childhood_pane): - self.childhood_pane = childhood_pane + def set_childhood_pane(self, childhood_pane, parent = None, attribute = None): + self.childhood_pane = childhood_pane + self.childhood_pane_parent = parent + self.childhood_pane_attribute = attribute self.childhood_pane.hierarchy_pane = self def _destroyed(self, *args): @@ -377,7 +442,7 @@ self.descr = introsp.description_for_object(o) self.root_node = self.Node(self, self.tree, None, o, self.restrict_to_attribute, self.flat_list) - if self.childhood_pane: self.childhood_pane.edit(None, None, o) + if self.childhood_pane: self.childhood_pane.edit(self.childhood_pane_parent, self.childhood_pane_attribute, o) def get_actions(self, parent_o, attribute, o): return [action for action in introsp.description_for_object(o).actions_for(parent_o, attribute, o) @@ -419,6 +484,8 @@ def show_action_menu(self, actions): pass def get_ignored_attrs(self, nodes): return { node.attribute.inverse_attr for node in nodes if node.parent and node.attribute } + + def _ordered(x): @@ -452,11 +519,19 @@ super(HierarchyNode, self).__init__(parent_node) def _observe(self, x): - observe.observe(x, self._listener) + if self.descr.is_owl_entity: + import owlready2.observe + owlready2.observe.observe(x, owlready2.observe.CollapsedListener(self._listener2)) + else: + observe.observe(x, self._listener) self.observeds.append(x) def _unobserve(self, x): - observe.unobserve(x, self._listener) + if self.descr.is_owl_entity: + import owlready2.observe + owlready2.observe.unobserve(x, owlready2.observe.CollapsedListener(self._listener2)) + else: + observe.unobserve(x, self._listener) self.observeds.remove(x) def can_have_children(self): return bool(self.hierarchy_attributes) @@ -512,6 +587,7 @@ old = { (child.attribute, id(child.o)) : child for child in old_children } children = [] + for attribute, star in self.hierarchy_attributes: if self.o_has_children[attribute]: if star: @@ -524,9 +600,6 @@ return children def _listener(self, o, type, new, old): - #print() - #if type is object: print(self, o, type, observe.diffdict(new, old, Inexistent)) - #else: print(self, o, type, new, old) self.update() if (type is list) or (type is set) or (type is dict): if self.o_children is None: @@ -535,7 +608,6 @@ else: for attribute, star in self.hierarchy_attributes: - #if self.o_has_children[attribute] is o: if o is attribute.get_value_for(self.o): self.update_children_for_attribute(attribute, star) @@ -547,7 +619,6 @@ changed_attrs = { attr for attr, new_value, old_value in observe.diffdict(new, old, Inexistent) } need_update = 0 if self.o_children is None: - #for attribute, star in self.hierarchy_attributes: self.update_has_children_for_attribute(attribute, star) for attribute, star in self.hierarchy_attributes: if attribute.name in changed_attrs: self.update_has_children_for_attribute(attribute, star); need_update = 1 else: @@ -565,9 +636,44 @@ for attribute, star in self.hierarchy_attributes: self.update_children_for_attribute(attribute, star) self.update_children() + def _listener2(self, o, diffs): + from owlready2 import IRIS + + self.update() + + changed_attrs = set() + for attr, new_value, old_value in diffs: + pred = IRIS.get(attr) + if pred: changed_attrs.add(pred.python_name) + else: changed_attrs.add(attr) + changed_attrs.update(attribute.name for attribute in self.descr.property_attributes_of(self.o)) + + if ("http://www.w3.org/1999/02/22-rdf-syntax-ns#type" in changed_attrs): + self.descr = introsp.description_for_object(self.o) + self.update_hierarchy_attributes() + if self.o_children is None: + for attribute, star in self.hierarchy_attributes: self.update_has_children_for_attribute(attribute, star) + else: + for attribute, star in self.hierarchy_attributes: self.update_children_for_attribute(attribute, star) + self.update_children() + + else: + self.descr = introsp.description_for_object(self.o) + self.update_hierarchy_attributes() + + need_update = 0 + if self.o_children is None: + for attribute, star in self.hierarchy_attributes: + if attribute.name in changed_attrs: self.update_has_children_for_attribute(attribute, star); need_update = 1 + else: + for attribute, star in self.hierarchy_attributes: + if attribute.name in changed_attrs: self.update_children_for_attribute(attribute, star); need_update = 1 + + if need_update: self.update_children() + def destroy(self): super().destroy() - for i in self.observeds: observe.unobserve(i, self._listener) + for i in list(self.observeds): self._unobserve(i) def node_for_object(self, o): if self.o is o: return self diff -Nru editobj3-0.1+ds1/editor_qt.py editobj3-0.2+ds1/editor_qt.py --- editobj3-0.1+ds1/editor_qt.py 2016-12-21 12:07:43.000000000 +0000 +++ editobj3-0.2+ds1/editor_qt.py 2022-08-02 08:45:28.000000000 +0000 @@ -129,8 +129,8 @@ else: self.current_radio_group = None - if stock and (not image) and qtgui.QIcon.hasThemeIcon(stock): - image = qtgui.QIcon.fromTheme(stock) + if isinstance(stock, qtgui.QIcon): image = stock + elif stock and (not image) and qtgui.QIcon.hasThemeIcon(stock): image = qtgui.QIcon.fromTheme(stock) if image: action.setIcon(image) if command: action.triggered.connect(command2) @@ -199,14 +199,20 @@ # For updating the childhood pane layout def on_tab_changed(self, index): - self.notebook.widget(index).hi_box.layout().invalidate() + editor_pane = self.notebook.widget(index) + self.show_tab(editor_pane) + editor_pane.hi_box.layout().invalidate() + def show(self): QtBaseDialog.show(self) - widget = self.notebook.currentWidget() - if widget: widget.hi_box.layout().invalidate() - - def add_tab(self, name, label, o = None): + editor_pane = self.notebook.currentWidget() + if editor_pane: + self.show_tab(editor_pane) + editor_pane.hi_box.layout().invalidate() + + def add_tab(self, name, label, o = None, func = None): editor_pane = super().add_tab(name, label, o) + editor_pane.func = func self.notebook.addTab(editor_pane, label) return editor_pane @@ -489,7 +495,7 @@ #self.button_add .setIconSize(icon_size) #self.button_remove .setIconSize(icon_size) #self.button_move_down.setIconSize(icon_size) - + layout = qtwidgets.QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.button_move_up) @@ -677,17 +683,17 @@ else: attr_label = "" if attr_label: return '''%s : %s''' % (attr_label, self.descr.label_for(self.o)) return self.descr.label_for(self.o) - + # class HierarchyScrollBar(qtwidgets.QScrollBar): # def __init__(self, hierarchy_pane): # qtwidgets.QScrollBar.__init__(self, qtcore.Qt.Vertical) # self.hierarchy_pane = hierarchy_pane - + # def hideEvent(self, event): # qtwidgets.QScrollBar.hideEvent(self, event) # print("cachée") # #self..overlay_offset_x = 3 - + # def showEvent(self, event): # qtwidgets.QScrollBar.showEvent(self, event) # print("visible") @@ -730,7 +736,7 @@ self.setModel(self.tree) self.selectionModel().selectionChanged.connect(self.on_selection_changed) if self.flat_list: - self.childhood_pane.edit(None, None, self.o) + self.childhood_pane.edit(self.childhood_pane_parent, self.childhood_pane_attribute, self.o) else: self.setExpanded(self.root_node.qt_index(), True) self.selectionModel().select(self.root_node.qt_index(), qtcore.QItemSelectionModel.ClearAndSelect) @@ -740,7 +746,7 @@ def select_node(self, node): self.selectionModel().clearSelection() if node: self.selectionModel().select(node.qt_index(), qtcore.QItemSelectionModel.Select) - elif self.flat_list and self.childhood_pane: self.childhood_pane.edit(None, None, self.o) + elif self.flat_list and self.childhood_pane: self.childhood_pane.edit(self.childhood_pane_parent, self.childhood_pane_attribute, self.o) def on_collapsed(self, index): node = index.internalPointer() @@ -762,7 +768,7 @@ indexes = self.selectionModel().selectedIndexes() if len(indexes) == 0: self.edit_child(None) - if self.childhood_pane: self.childhood_pane.edit(None, None, None) + if self.childhood_pane: self.childhood_pane.edit(self.childhood_pane_parent, self.childhood_pane_attribute, None) self.selected_node = None return @@ -785,8 +791,12 @@ self.edit_child(self.selected, self.get_ignored_attrs(nodes)) if self.childhood_pane: self.childhood_pane.edit(self.selected_parent, self.selected_attribute, self.selected) - def on_action_activated(self, action): self._action_activated(action, self.selected_parent, self.selected_attribute, self.selected) - + def on_action_activated(self, action): + if self.selected: + self._action_activated(action, self.selected_parent, self.selected_attribute, self.selected) + else: + self._action_activated(action, None, self.restrict_to_attribute, self.o) + def show_action_menu(self, actions): self._popup_menu = qtwidgets.QMenu() # The menu must be kept in memory, else it does not appear. for action in actions: diff -Nru editobj3-0.1+ds1/field.py editobj3-0.2+ds1/field.py --- editobj3-0.1+ds1/field.py 2017-09-10 08:26:16.000000000 +0000 +++ editobj3-0.2+ds1/field.py 2018-05-30 09:57:41.000000000 +0000 @@ -203,7 +203,8 @@ return self.format_func(v) def set_value(self, s): - if s and (s[0] != "<"): + if (s == "") and self.attribute.allow_none: s = None + elif s and (s[0] != "<"): try: s = self.parse_func(s) except: sys.excepthook(*sys.exc_info()); return Field.set_value(self, s) @@ -217,10 +218,12 @@ """A field for editing a float. Displays as a one-line text field for entering a float number.""" def format_func(self, v): return str(v) def parse_func (self, s): return float(editobj3.eval(s.replace(",", "."))) - + class StringField(EntryField): """A field for editing a string (without breakline). Displays as a one-line text field for entering a string.""" - def format_func(self, v): return str(v) + def format_func(self, v): + if v is None: return "" + return str(v) def parse_func (self, s): return str(s) class PasswordField(StringField): """A field for entering a password. Displays as a one-line text field whoose value is hidden value.""" @@ -299,8 +302,8 @@ else: old = introsp.description_for_object(self.o)._get_attribute(self.attribute.name).get_value_for(self.o) if (old is True) or (old is False): value = bool(value) - elif value: value = 1 - else: value = 0 + elif value: value = True + else: value = False MultiGUIField.set_value(self, value) class ProgressBarField(MultiGUIField): @@ -516,13 +519,19 @@ import editobj3.editor as editor self.hierarchy_pane = editor.HierarchyPane(gui, self, self.edit_child, undo_stack, attribute, 1) self.childhood_pane = editor.ChildhoodPane(gui, self, undo_stack, attribute) - self.hierarchy_pane.set_childhood_pane(self.childhood_pane) + self.hierarchy_pane.set_childhood_pane(self.childhood_pane, o, attribute) self.update() def edit_child(self, o, ignored_attrs = None): pass - def update(self): self.hierarchy_pane.edit(self.o) - + def edit(self, o): + self.hierarchy_pane.set_childhood_pane(self.childhood_pane, o, self.attribute) + self.hierarchy_pane.edit(o) + super().edit(o) + + def update(self): + self.hierarchy_pane.edit(self.o) + HierarchyOrObjectAttributeField = fixed_pane_field(ObjectAttributeField, True, False) HierarchyOrObjectSelectorField = fixed_pane_field(ObjectSelectorField , True, False) HierarchyOrObjectListField = fixed_pane_field(ObjectListField , True, False) diff -Nru editobj3-0.1+ds1/field_qt.py editobj3-0.2+ds1/field_qt.py --- editobj3-0.1+ds1/field_qt.py 2016-03-19 17:20:47.000000000 +0000 +++ editobj3-0.2+ds1/field_qt.py 2018-05-18 11:58:49.000000000 +0000 @@ -54,7 +54,7 @@ def update(self): self.updating += 1 - try: self.setText(self.get_value() or "") # GTK does not accept None as a text + try: self.setText(self.get_value() or "") # Qt does not accept None as a text finally: self.updating -= 1 class QtTextField(QtField, TextField, qtwidgets.QPlainTextEdit): @@ -410,7 +410,7 @@ # Hack self.hierarchy_pane.focusInEvent = self.hierarchy_focusInEvent self.hierarchy_pane.focusOutEvent = self.hierarchy_focusOutEvent - + # For updating childhood position def showEvent(self, event): qtwidgets.QWidget.showEvent(self, event) diff -Nru editobj3-0.1+ds1/__init__.py editobj3-0.2+ds1/__init__.py --- editobj3-0.1+ds1/__init__.py 2017-09-10 09:13:16.000000000 +0000 +++ editobj3-0.2+ds1/__init__.py 2022-08-02 09:03:24.000000000 +0000 @@ -64,7 +64,7 @@ def eval(s): return _eval(s) -VERSION = "0.1" +VERSION = "0.2" GUI = "Qt" diff -Nru editobj3-0.1+ds1/introsp.py editobj3-0.2+ds1/introsp.py --- editobj3-0.1+ds1/introsp.py 2017-06-14 10:11:18.000000000 +0000 +++ editobj3-0.2+ds1/introsp.py 2019-09-05 15:15:23.000000000 +0000 @@ -26,6 +26,14 @@ import editobj3, editobj3.undoredo as undoredo from collections import defaultdict +_entity_classes = () +def _init_for_owlready2(): + global _entity_classes + import owlready2 + _entity_classes = (owlready2.ThingClass, owlready2.Thing, owlready2.PropertyClass) + import editobj3.observe + editobj3.observe._has_owlready2 = True + PROPERTY_TYPE_NAMES = {"property", "getset_descriptor"} IGNORED_ATTRS = {"__weakref__", "__abstractmethods__"} BOOL_ATTRS = {"visible", "hidden", "active"} @@ -86,6 +94,7 @@ self.remove_method = remove_method self.reorder_method = reorder_method self.has_item_method = has_item_method + self.allow_none = False if (self.get_method and (self.get_method != "auto")) or (self.set_method and (self.set_method != "auto")): self.is_property = True @@ -291,13 +300,14 @@ self.label = None self.details = None self.constructor = None - #inherited_classes = getattr(klass, "__mro__", None) or klass.mro() if not object is inherited_classes[-1]: inherited_classes = inherited_classes + [object] self.inherited_descrs = [self._get_parent_description(klass) for klass in inherited_classes[1:]] self.inherited_descrs.insert(0, self) self.attributes.update(self.search_attributes(klass)) + self.is_owl_entity = isinstance(klass, _entity_classes) + def create_attribute(self, name): if name in self.attributes: return self.attributes[name] for descr in self.inherited_descrs: @@ -305,40 +315,8 @@ attribute = descr.attributes[name].copy() break else: attribute = Attribute(name) - #self.attributes[name] = attribute return attribute - # def search_attributes(self): - # for attr in dir(self.klass): - # if attr in IGNORED_ATTRS: continue - # kval = getattr(self.klass, attr) - - # if type(kval).__name__ in PROPERTY_TYPE_NAMES: - # attribute = self.create_attribute(attr) - # attribute.is_property = True - # attribute.optional = False - # if isinstance(kval, property): - # if not kval.fset: attribute.set_method = None - # if not kval.fget: attribute.get_method = None - # else: - # if not kval.__set__: attribute.set_method = None - # if not kval.__get__: attribute.get_method = None - - # elif attr.startswith("set") and (len(attr) > 3) and _method_has_nb_args(kval, 2): - # if attr[3] == "_": attr2 = attr[4:] - # else: attr2 = attr[3].lower() + attr[4:] - # attribute = self.create_attribute(attr2) - # attribute.set_method = attr - # attribute.is_property = True - - # elif attr.startswith("get") and (len(attr) > 3) and _method_has_nb_args(kval, 1): - # if attr[3] == "_": attr2 = attr[4:] - # else: attr2 = attr[3].lower() + attr[4:] - # attribute = self.create_attribute(attr2) - # attribute.get_method = attr - # attribute.is_property = True - # attribute.optional = False - def search_attributes(self, klass): attributes = {} for attr in dir(klass): @@ -413,7 +391,7 @@ :param constructor: a Constructor object, a FormConstructor subclass, or None for using the class itself as a constructor.""" self.constructor = constructor - #def def_attr(self, attr, field_class = "auto", unit = "auto", priority = "auto", inverse_attr = "auto", optional = "auto", get_method = "auto", set_method = "auto", addable_values = "auto", add_method = "auto", remove_method = "auto", reorder_method = "auto", has_item_method = "auto", label = None): + #def def_attr(self, attr, field_class = "auto", unit = "auto", priority = "auto", inverse_attr = "auto", optional = "auto", get_method = "auto", set_method = "auto", addable_values = "auto", add_method = "auto", remove_method = "auto", reorder_method = "auto", has_item_method = "auto", label = None, allow_none = False): def def_attr(self, attr, field_class = 0, **kargs): """def_attr(attr, field_class = "auto", unit = "auto", priority = "auto", inverse_attr = "auto", optional = "auto", get_method = "auto", set_method = "auto", addable_values = "auto", add_method = "auto", remove_method = "auto", reorder_method = "auto", has_item_method = "auto", label = None) @@ -800,7 +778,7 @@ :param has_item_method: the method for testing the presence of items in the attribute's value. :param label: the label for displaying the attribute (default to the attribute name). -All \*_method parameters can be a method name, a callable, or None (to disable the corresponding functionality). +All method parameters can be a method name, a callable, or None (to disable the corresponding functionality). Except for the two first parameters, all parameters to def_attr() must be given by name, for example: @@ -898,10 +876,14 @@ for i in range(len(pack.objects)): if self.name in pack.attrs[i]: description_for_object(pack.objects[i])._get_attribute(self.name).set_value_for(pack.objects[i], value) - + + def __getitem__(self, index): return self + + class ObjectPackDescription(object): attributes = {} # For def_attr inherited_descrs = [] + is_owl_entity = False def icon_filename_for(self, pack): icon_filenames = set() for o in pack.objects: diff -Nru editobj3-0.1+ds1/observe.py editobj3-0.2+ds1/observe.py --- editobj3-0.1+ds1/observe.py 2015-05-21 15:25:48.000000000 +0000 +++ editobj3-0.2+ds1/observe.py 2018-03-03 17:38:12.000000000 +0000 @@ -235,9 +235,10 @@ new.update(o.__dict__) return new else: return o.__dict__.copy() - +_has_owlready2 = False + def scan(): """Checks for changes in all listened objects, and calls the corresponding listeners if needed.""" for i, observation in list(_observed_seqs.items()): @@ -271,7 +272,11 @@ if not observation.old_class is o.__class__: for listener in observation.listeners[:]: listener(o, "__class__", o.__class__, observation.old_class) observation.old_class = o.__class__ - + + if _has_owlready2: + import owlready2.observe + owlready2.observe.scan_collapsed_changes() + def diffdict(new, old, inexistent_value = None): """Returns the differences between two dictionaries. diff -Nru editobj3-0.1+ds1/PKG-INFO editobj3-0.2+ds1/PKG-INFO --- editobj3-0.1+ds1/PKG-INFO 2017-09-10 19:38:38.000000000 +0000 +++ editobj3-0.2+ds1/PKG-INFO 2022-08-02 09:10:07.084604500 +0000 @@ -1,60 +1,11 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: Editobj3 -Version: 0.1 +Version: 0.2 Summary: An automatic dialog box generator for Python objects, supporting multiple graphical backends: Qt, GTK and HTML (single-user or with multiple users). Home-page: https://bitbucket.org/jibalamy/editobj3 Author: Lamy Jean-Baptiste (Jiba) Author-email: jibalamy@free.fr License: LGPLv3+ -Description: Editobj3 - ======== - - Editobj3 is an automatic dialog box generator for Python objects. It supports several backends; currenlty - Qt, GTK and HTML are supported. The HTML backend is based on `W2UI `_, and can be used - either in local single user mode, or in distributed multiple users mode. - - Editobj3 dialog boxes are composed of an attribute list, a luxurious good-looking but useless icon and - title bar, and a tree view (if the edited object is part of a tree-like structure). Editobj3 includes an - advanced introspection module that usually guesses how to edit any object; it can also be customized for a - given class of object through the editobj3.introsp module. Editobj3 also supports the simultaneous - edition of a group of objects, as if they were a single object. - - Additional helper modules are included: - - - editobj3.observe: Observation framework - - - editobj3.undoredo: Multiple undo/redo framework - - - editobj3.http_ws_server: HTTP server with WebSocket support, with an interface similar to Python's http.server module - - Editobj3 has been created by Jean-Baptiste Lamy. It is available under the GNU LGPL licence. - - In case of trouble, please contact Jean-Baptiste Lamy - - - Installation - ------------ - - First untar the tarball. - - EditObj 3 uses Python's DistUtils for installation. To install, type (as root): - - cd EditObj3-* - python3 ./setup.py install - - By default, EditObj 3 is installed in /usr, you can modify the - setup.cfg file if you prefer another location. - - - Links - ----- - - Editobj3 on BitBucket (development repository): https://bitbucket.org/jibalamy/editobj3 - - Mail me for any comment, problem, suggestion or help ! - - Jiba -- Jean-Baptiste LAMY -- jibalamy @ free.fr - Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers @@ -68,3 +19,55 @@ Classifier: Topic :: Scientific/Engineering :: Human Machine Interfaces Classifier: Topic :: Software Development :: User Interfaces Classifier: Topic :: Software Development :: Libraries :: Python Modules +License-File: LICENSE.txt + +Editobj3 +======== + +Editobj3 is an automatic dialog box generator for Python objects. It supports several backends; currenlty +Qt, GTK and HTML are supported. The HTML backend is based on `W2UI `_, and can be used +either in local single user mode, or in distributed multiple users mode. + +Editobj3 dialog boxes are composed of an attribute list, a luxurious good-looking but useless icon and +title bar, and a tree view (if the edited object is part of a tree-like structure). Editobj3 includes an +advanced introspection module that usually guesses how to edit any object; it can also be customized for a +given class of object through the editobj3.introsp module. Editobj3 also supports the simultaneous +edition of a group of objects, as if they were a single object. + +Additional helper modules are included: + + - editobj3.observe: Observation framework + + - editobj3.undoredo: Multiple undo/redo framework + + - editobj3.http_ws_server: HTTP server with WebSocket support, with an interface similar to Python's http.server module + +Editobj3 has been created by Jean-Baptiste Lamy. It is available under the GNU LGPL licence. + +In case of trouble, please contact Jean-Baptiste Lamy + + +Installation +------------ + +First untar the tarball. + +EditObj 3 uses Python's DistUtils for installation. To install, type (as root): + +cd EditObj3-* +python3 ./setup.py install + +By default, EditObj 3 is installed in /usr, you can modify the +setup.cfg file if you prefer another location. + + +Links +----- + +Editobj3 on BitBucket (development repository): https://bitbucket.org/jibalamy/editobj3 + +Mail me for any comment, problem, suggestion or help ! + +Jiba -- Jean-Baptiste LAMY -- jibalamy @ free.fr + + diff -Nru editobj3-0.1+ds1/setup.py editobj3-0.2+ds1/setup.py --- editobj3-0.1+ds1/setup.py 2017-09-10 19:38:11.000000000 +0000 +++ editobj3-0.2+ds1/setup.py 2022-08-02 09:06:20.000000000 +0000 @@ -17,22 +17,21 @@ # along with this program. If not, see . -import os, os.path, sys, glob#, distutils.core +import os, os.path, sys, glob -HERE = os.path.dirname(sys.argv[0]) or "." +HERE = os.path.relpath(os.path.dirname(os.path.abspath(__file__))) if len(sys.argv) <= 1: sys.argv.append("install") import setuptools -data_files = [os.path.join("js", file) for file in os.listdir("js") if (file != "CVS") and (file != ".hg") and (file != ".svn") and (file != ".arch-ids")] \ - + [os.path.join("icons", file) for file in os.listdir("icons") if (file != "CVS") and (file != ".hg") and (file != ".svn") and (file != ".arch-ids")] +#data_files = [os.path.join("js", file) for file in os.listdir("js") if (file != "CVS") and (file != ".hg") and (file != ".svn") and (file != ".arch-ids")] \ +# + [os.path.join("icons", file) for file in os.listdir("icons") if (file != "CVS") and (file != ".hg") and (file != ".svn") and (file != ".arch-ids")] setuptools.setup( -#distutils.core.setup( name = "Editobj3", - version = "0.1", + version = "0.2", license = "LGPLv3+", description = "An automatic dialog box generator for Python objects, supporting multiple graphical backends: Qt, GTK and HTML (single-user or with multiple users).", long_description = open(os.path.join(HERE, "README.rst")).read(), @@ -56,9 +55,8 @@ "Topic :: Software Development :: Libraries :: Python Modules", ], - package_dir = {"editobj3" : "."}, + package_dir = {"editobj3" : HERE}, packages = ["editobj3"], package_data = {"editobj3" : ["icons/*", "js/*"]} - #package_data = {"editobj3" : data_files} )