class DataFactoryManager(AbstractDataFactoryManager): data_created = QtCore.Signal(object) data_property_set_request = QtCore.Signal(object, str, object) def __init__(self): AbstractDataFactoryManager.__init__(self) self.__mimeMap = {} def gather_items(self, refresh=False): items = AbstractDataFactoryManager.gather_items(self, refresh) if refresh: self.__mimeMap.clear() for datatype in items.itervalues(): if not datatype.started: datatype._start_0() if datatype is None or not datatype.supports_open(): continue fmts = datatype.opened_mimetypes for fmt in fmts: self.__mimeMap.setdefault(fmt, set()).add(datatype) return items def get_handlers_for_mimedata(self, formats): datatypes = self.gather_items() handlers = set() # for unicity for fm in formats: fmt_datatypes = self.__mimeMap.get(fm) if fmt_datatypes is not None: handlers.update(fmt_datatypes) return list(handlers)
class ProjectManager(QtCore.QObject): __metaclass__ = make_metaclass((ProxySingleton, ), (QtCore.pyqtWrapperType, )) active_project_changed = QtCore.Signal(Project, Project) data_added = QtCore.Signal(object, object) mimeformat = "application/secondnature-project-data-id" def __init__(self): QtCore.QObject.__init__(self) self.__activeProject = None def new_active_project(self, name): proj = Project(name) return self.set_active_project(proj) def has_active_project(self): return self.__activeProject is not None def set_active_project(self, project): if self.has_active_project() and self.get_active_project().is_modified( ): return False # raise something old = self.__activeProject self.__activeProject = project self.__activeProject.data_added.connect(self.data_added) self.active_project_changed.emit(self.__activeProject, old) return True def get_active_project(self): return self.__activeProject def close_active_project(self): if self.__activeProject: self.__activeProject.close() self.__activeProject = None def save_active_project(self, filepath): self.__activeProject.save_to(filepath) def add_data_to_active_project(self, data): if data and data.registerable and self.__activeProject: self.__activeProject.add_data(data) def set_property_to_active_project(self, data, key, value): if data and self.__activeProject: self.__activeProject.set_data_property(data, key, value)
class FilterBox(QtGui.QWidget): filter_changed = QtCore.Signal(str) def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self._layout = QtGui.QHBoxLayout(self) self.setContentsMargins(0, 0, 0, 0) self._layout.setContentsMargins(0, 0, 0, 0) self._cb_groupby = QtGui.QComboBox() self._cb_groupby.currentIndexChanged.connect( self._on_current_index_changed) self._layout.addWidget(self._cb_groupby) def _on_current_index_changed(self, idx): self.filter_changed.emit(self._criteria[idx][0]) def set_criteria(self, criteria): self._criteria = criteria for criterion in self._criteria: self._cb_groupby.addItem(criterion[1]) def set_filter(self, name): for i, criterion in enumerate(self._criteria): if criterion[0] == name: self._cb_groupby.setCurrentIndex(i) return
class ColormapRectangle(QColormapBar, AbstractQtControlWidget): valueChanged = QtCore.Signal(dict) def __init__(self): QColormapBar.__init__(self) self.setAutoFillBackground(True) AbstractQtControlWidget.__init__(self) self.setMinimumHeight(40) self.value_changed_signal = self.valueChanged def reset(self, value=dict(name='grey', color_points=dict([(0.0, (0.0, 0.0, 0.0)), (1.0, (1.0, 1.0, 1.0))])), **kwargs): self.setValue(value) def read(self, control): self.reset(control.value) def apply(self, control): AbstractQtControlWidget.apply(self, control)
class AleaQGraphicsEmitingTextItem(QtGui.QGraphicsTextItem): """A QtGui.QGraphicsTextItem that emits geometryModified whenever its geometry can have changed.""" ###################### # The Missing Signal # ###################### geometryModified = QtCore.Signal(QtCore.QRectF) def __init__(self, *args, **kwargs): QtGui.QGraphicsTextItem.__init__(self, *args, **kwargs) self.document().contentsChanged.connect(self.__onDocumentChanged) self.hoveredIn = AleaSignal() self.hoveredOut = AleaSignal() def __onDocumentChanged(self): self.geometryModified.emit(self.boundingRect()) def hoverEnterEvent(self, event): QtGui.QGraphicsTextItem.hoverEnterEvent(self, event) self.hoveredIn.emit(event) def hoverLeaveEvent(self, event): QtGui.QGraphicsTextItem.hoverLeaveEvent(self, event) self.hoveredOut.emit(event)
class QFloatSlider(QtGui.QSlider): floatValueChanged = QtCore.Signal(float) def __init__(self, orientation=QtCore.Qt.Horizontal): QtGui.QSlider.__init__(self, orientation) self.connect(self, QtCore.SIGNAL('valueChanged(int)'), self.notifyValueChanged) self.slider_step = 0.1 self.floatValue = 0.0 def notifyValueChanged(self, value): self.floatValue = value * self.slider_step self.floatValueChanged.emit(self.floatValue) def setFloatValue(self, value): self.floatValue = value self.setValue(round(value / self.slider_step)) # self.floatValueChanged.emit(self.floatValue) # self.setValue(int(float(value)/self.slider_step)) def value(self): return self.floatValue def setStep(self, step): self.slider_step = step
class IntRangeSpinBoxes(QtGui.QWidget, AbstractIntRangeWidget): valueChanged = QtCore.Signal(tuple) def __init__(self): QtGui.QWidget.__init__(self) self.start_spinbox = QtGui.QSpinBox() self.end_spinbox = QtGui.QSpinBox() # Fill background to avoid to see text or widget behind self.setAutoFillBackground(True) AbstractIntRangeWidget.__init__(self) self.start_spinbox.setMinimumHeight(18) self.end_spinbox.setMinimumHeight(18) self.start_spinbox.valueChanged.connect(self.end_spinbox.setMinimum) self.end_spinbox.valueChanged.connect(self.start_spinbox.setMaximum) self.start_spinbox.valueChanged.connect( self.notify_start_value_changed) self.end_spinbox.valueChanged.connect(self.notify_end_value_changed) layout = QtGui.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.start_spinbox) layout.addWidget(self.end_spinbox) self.value_changed_signal = self.valueChanged def reset(self, value=(0, 255), minimum=None, maximum=None, **kwargs): if minimum is not None: self.start_spinbox.setMinimum(minimum) if maximum is not None: self.end_spinbox.setMaximum(maximum) self.start_spinbox.setMaximum(value[1]) self.end_spinbox.setMinimum(value[0]) self.setValue(value) def apply(self, control): AbstractQtControlWidget.apply(self, control) control.interface.min = self.start_spinbox.minimum() control.interface.max = self.end_spinbox.maximum() def value(self, interface=None): return (self.start_spinbox.value(), self.end_spinbox.value()) def setValue(self, value): self.start_spinbox.setValue(value[0]) self.start_spinbox.setMaximum(value[1]) self.end_spinbox.setValue(value[1]) self.end_spinbox.setMinimum(value[0]) def notify_start_value_changed(self, value): self.valueChanged.emit((value, self.end_spinbox.value())) def notify_end_value_changed(self, value): self.valueChanged.emit((self.start_spinbox.value(), value))
class QColormapBar(QtGui.QWidget): valueChanged = QtCore.Signal(dict) def __init__(self, orientation=QtCore.Qt.Horizontal): QtGui.QWidget.__init__(self) self.color_points = {} self.colormap_name = "" self.colormap_painter = PainterColormap() self.orientation = orientation def setValue(self, value): self.color_points = value['color_points'] self.colormap_name = value['name'] self.valueChanged.emit(dict(color_points=self.color_points, name=self.colormap_name)) self.update() def value(self): return dict(color_points=self.color_points, name=self.colormap_name) def setOrientation(self, orientation): self.orientation = orientation def paintEvent(self, event): painter = QtGui.QPainter(self) rectangle = event.rect() self.colormap_painter.paint_data(self.value(), painter, rectangle, orientation=self.orientation)
class PluginSelector(WelcomePage): pluginSelected = QtCore.Signal(object) def __init__(self, category, parent=None): WelcomePage.__init__(self, parent=parent) self._actions = {} self._sorted_actions = [] for plugin_class in plugins(category): action = QtGui.QAction(obj_icon(plugin_class), alias(plugin_class), self) action.triggered.connect(self._on_action_triggered) self._actions[action] = plugin_class self._sorted_actions.append(action) self.set_actions(self._sorted_actions) def _on_action_triggered(self): plugin_class = self._actions[self.sender()] self.plugin_class = plugin_class self.pluginSelected.emit(plugin_class) def resize(self, *args, **kwargs): WelcomePage.resize(self, *args, **kwargs) self.set_actions(self._sorted_actions)
class ManagerItemSelector(WelcomePage): item_selected = QtCore.Signal(object) def __init__(self, manager, group='default', parent=None, style=None): """ items: function returning items for a given group """ self.manager = manager if style is None: style = WelcomePage.STYLE_MEDIUM WelcomePage.__init__(self, parent=parent, style=style) self._actions = {} items = sorted(self.manager.items(group), key=lambda item: item.label) self._sorted_actions = [] for item in items: action = QtGui.QAction(obj_icon(item), item.label, self) action.triggered.connect(self._on_action_triggered) self._actions[action] = item self._sorted_actions.append(action) self.set_actions(self._sorted_actions) def _on_action_triggered(self): plugin_class = self._actions[self.sender()] self.plugin_class = plugin_class self.item_selected.emit(plugin_class) def resize(self, *args, **kwargs): WelcomePage.resize(self, *args, **kwargs) self.set_actions(self._sorted_actions)
class IntRangeSimpleSlider(QSpanSlider, AbstractIntRangeWidget): valueChanged = QtCore.Signal(tuple) def __init__(self): QSpanSlider.__init__(self) AbstractIntRangeWidget.__init__(self) self.spanChanged.connect(self.notify_value_changed) self.value_changed_signal = self.valueChanged def reset(self, value=(0, 255), minimum=0, maximum=255, **kwargs): if minimum is not None: self.setMinimum(minimum) if maximum is not None: self.setMaximum(maximum) self.setSpan(value[0], value[1]) def apply(self, control): AbstractQtControlWidget.apply(self, control) control.interface.min = self.minimum() control.interface.max = self.maximum() control.value = self.value() def notify_value_changed(self, lower_value, upper_value): self.valueChanged.emit((lower_value, upper_value)) def setValue(self, value): self.setSpan(value[0], value[1]) def value(self, interface=None): return (self.lowerValue(), self.upperValue())
class AbstractSource(QtCore.QObject): __metaclass__ = make_metaclass((ProxySingleton, ), (QtCore.pyqtWrapperType, )) # -- EXTENSION POINT SOURCES API ATTRIBUTES -- __concrete_manager__ = None __key__ = "name" # -- SIGNALS -- item_list_changed = QtCore.Signal(object, dict) # -- PROPERTIES -- name = property(lambda x: x.__name) valid = property(lambda x: x.is_valid()) def __init__(self): QtCore.QObject.__init__(self) self.__name = self.__class__.__name__ mgrCls = self.__concrete_manager__ if mgrCls is not None: mgrCls()._add_source(self) ################# # EXTENSION API # ################# def is_valid(self): raise NotImplementedError def gather_items(self): raise NotImplementedError def get_items(self): raise NotImplementedError
class ParadigmCreator(QtCore.QObject): paradigm_clicked = QtCore.Signal(str) def __init__(self, parent=None): QtCore.QObject.__init__(self) self._parent = parent self.reload() def reload(self): self.dtype = None self._name_to_applet = {} self._name_to_action = {} self._action_to_name = {} for plugin in plugins('oalab.plugin', criteria=dict(implement='IParadigmApplet')): applet = debug_plugin('oalab.plugin', func=plugin) if applet: name = applet.default_name self._name_to_applet[name] = applet action = QtGui.QAction(qicon(applet.icon), "New " + name, self._parent) action.triggered.connect(self._on_action_triggered) self._name_to_action[name] = action self._action_to_name[action] = name def applet(self, obj, dtype, mimetype=None): applet_class = None if dtype in self._name_to_applet: # Check in paradigm.default_name applet_class = self._name_to_applet[dtype] else: # Check in paradigm.extension for value in self._name_to_applet.values(): if dtype == value.extension: applet_class = value if applet_class is None: applet_class = self._name_to_applet["Textual"] return applet_class(data=obj).instantiate_widget() def actions(self): return self._action_to_name.keys() def action(self, paradigm): """ action("Python") -> QAction "New Python" or None """ return self._name_to_action.get(paradigm) def _on_action_triggered(self): try: self.dtype = self._action_to_name[self.sender()] except KeyError: self.dtype = None self.paradigm_clicked.emit(self.dtype)
class SimpleCurve2DView(Curve2DEditor): clicked = QtCore.Signal(object, object) deleteRequested = QtCore.Signal(object, object) def __init__(self, pos, curve, parent): Curve2DEditor.__init__(self, parent) self.pos = pos self.accessorType[NurbsCurve] = ProfileEditor.NurbsAccessor self.setFixedSize(100, 90) self.setEnabled(True) self.setAutoFillBackground(True) self.bkgdColor = self.backgroundColor() if curve: self.setCurve(curve) def highlight(self, val): if val: self.defaultColor = QtGui.QColor(100, 200, 100) else: self.defaultColor = self.bkgdColor self.update() def drawGrid(self): return def mousePressEvent(self, event): self.clicked.emit(self.pos, self.getCurve()) def mouseDoubleClickEvent(self, event): QGLViewer.mouseDoubleClickEvent(self, event) def mouseReleaseEvent(self, event): QGLViewer.mouseReleaseEvent(self, event) def contextMenuEvent(self, event): menu = QtGui.QMenu(self) deleteAction = menu.addAction("Delete section") deleteAction.triggered.connect(self.__on_delete_request) menu.popup(event.globalPos()) def __on_delete_request(self): self.deleteRequested.emit(self.pos, self.getCurve())
class GenericFileBrowser(QtGui.QWidget): pathSelected = QtCore.Signal(object) def __init__(self): super(GenericFileBrowser, self).__init__() self.model = QtGui.QFileSystemModel() self.tree = QtGui.QTreeView() self.tree.setModel(self.model) self.tree.header().setResizeMode(QtGui.QHeaderView.ResizeToContents) self._home_dir = settings.get_default_home_dir() self._cwd = Path(".").abspath() self.model.setRootPath(self._home_dir) self.tree.setRootIndex(self.model.index(self._home_dir)) layout = QtGui.QGridLayout(self) layout.addWidget(self.tree) self._create_actions() self._create_connections() def _create_connections(self): self.tree.doubleClicked.connect(self._on_index_clicked) def _create_actions(self): self._action_go_to_parent = QtGui.QAction(qicon('oxygen_go-up.png'), 'Parent dir', self) self._action_go_to_parent.triggered.connect(self.go_to_parent) def toolbar_actions(self): return [self._action_go_to_parent] def go_to(self, path): self.model.setRootPath(path) self.tree.setRootIndex(self.model.index(path)) def go_to_parent(self): path = Path(self.model.filePath(self.tree.rootIndex())) self.go_to(path.parent) def _on_index_clicked(self, index): filename = self.model.filePath(index) self.pathSelected.emit(Path(filename)) def set_properties(self, properties): columns = properties.get('columns', [0]) for icol in range(self.model.columnCount()): self.tree.setColumnHidden(icol, icol not in columns) def properties(self): columns = [ i for i in range(self.model.columnCount()) if not self.tree.isColumnHidden(i) ] return dict(columns=columns)
class NodeFactoryTreeView(QtGui.QTreeView, NodeFactoryView): compositeFactoryOpenRequest = QtCore.Signal(CompositeNodeFactory) def __init__(self, siblings=[], parent=None): QtGui.QTreeView.__init__(self, parent) NodeFactoryView.__init__(self, siblings) self.setDragEnabled(True) self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.setIconSize(QtCore.QSize(25,25)) self.setHeaderHidden(True)
class CustomSplittable(SplittableUI): paneMenuRequest = QtCore.Signal(object, int, QtCore.QPoint) def getPlaceHolder(self): proj = ProjectManager().get_active_project() return AppletSpace(proj) def _install_child(self, paneId, widget, **kwargs): w = SplittableUI._install_child(self, paneId, widget, **kwargs) self._raise_overlays(paneId) return w
class IntSlider(QtGui.QWidget, AbstractIntWidget): valueChanged = QtCore.Signal(int) def __init__(self): QtGui.QWidget.__init__(self) self.slider = QtGui.QSlider(QtCore.Qt.Horizontal) self.spinbox = QtGui.QSpinBox() # Fill background to avoid to see text or widget behind self.setAutoFillBackground(True) AbstractIntWidget.__init__(self) # To be compatible with tree or table views, slider must keep focus self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) self.setMinimumHeight(22) self.spinbox.setMinimumHeight(18) self.slider.setMinimumHeight(18) self.slider.valueChanged.connect(self.spinbox.setValue) self.spinbox.valueChanged.connect(self.slider.setValue) self.slider.valueChanged.connect(self.valueChanged) layout = QtGui.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.spinbox) layout.addWidget(self.slider) self.value_changed_signal = self.valueChanged def reset(self, value=1, minimum=None, maximum=None, **kwargs): if minimum is not None: self.slider.setMinimum(minimum) self.spinbox.setMinimum(minimum) if maximum is not None: self.slider.setMaximum(maximum) self.spinbox.setMaximum(maximum) self.setValue(value) def apply(self, control): AbstractQtControlWidget.apply(self, control) control.interface.min = self.slider.minimum() control.interface.max = self.slider.maximum() def value(self, interface=None): return self.spinbox.value() def setValue(self, value): self.slider.setValue(value) self.spinbox.setValue(value)
class ManagerExplorerView(QtGui.QTreeView): item_changed = QtCore.Signal(object) search_item_request = QtCore.Signal() def __init__(self, parent=None): QtGui.QTreeView.__init__(self, parent=parent) self.setContentsMargins(0, 0, 0, 0) self._model = ManagerExplorerModel() self.setModel(self._model) self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.setIconSize(QtCore.QSize(24, 24)) self.setHeaderHidden(True) def set_items(self, items): self._model.set_items(items) if len(items): first = self._model._group.values()[0][0] self.setCurrentIndex(self._model.indexFromItem(first)) self.expandAll() def selectionChanged(self, selected, deselected): for idx in selected.indexes(): if self._model.search_item_selected(idx): self.search_item_request.emit() else: self.item_changed.emit(self._model.item(idx)) return QtGui.QTreeView.selectionChanged(self, selected, deselected) def groupby(self, **kwds): self._model.groupby(**kwds) self.expandAll() def set_default_group_icon(self, icon_path): self._model.default_group_icon = icon_path def set_default_item_icon(self, icon_path): self._model.default_item_icon = icon_path
class QControlContainer(QtCore.QObject, ControlContainer): controlValueChanged = QtCore.Signal(object, object) def __init__(self, *args, **kwargs): QtCore.QObject.__init__(self) ControlContainer.__init__(self, *args, **kwargs) self._action = {} self._control = {} def notify(self, sender, event): ControlContainer.notify(self, sender, event) if isinstance(sender, Control): signal, data = event if signal == 'value_changed': self.controlValueChanged.emit(sender, data) if 'IBool' in str(sender.interface.__class__): action = self._action[sender] action.setChecked(sender.value) def create_actions(self, parent): for control in self.controls(): interface = control.interface label = control.label action = QtGui.QAction(label, parent) self._control[action] = control self._action[control] = action if 'IBool' in str(interface.__class__): action.setCheckable(True) action.setChecked(control.value) action.toggled.connect(self._on_action_toggled) else: action.triggered.connect(self._on_action_triggered) def actions(self): return self._action.values() def _on_action_triggered(self, *args): control = self._control[self.sender()] from openalea.oalab.service.qt_control import qt_dialog value = qt_dialog(control, autoapply=False) if value is not None: control.value = value def _on_action_toggled(self, toggled): control = self._control[self.sender()] control.value = toggled
class SenderWidget(QtCore.QObject): """A helper class that reemits signals from its inner widget with self as first argument, to identify the origin of a signal.""" valueChanged = QtCore.Signal(QtCore.QObject, object, object) def __init__(self, widget, typ, parent): QtCore.QObject.__init__(self, parent) self.inner = widget self.type = typ if hasattr(widget, "valueChanged"): widget.valueChanged.connect(self.__on_value_changed) self.setValue = lambda x, y: x.inner.setValue(y) elif hasattr(widget, "stateChanged"): widget.stateChanged.connect(self.__on_value_changed) self.setValue = lambda x, y: x.inner.setCheckState( QtCore.Qt.Checked if y else QtCore.Qt.Unchecked) elif hasattr(widget, "textChanged"): widget.textChanged.connect(self.__on_value_changed) self.setValue = lambda x, y: x.inner.setText(str(y)) def __on_value_changed(self, value): self.valueChanged.emit(self, value, self.type)
class ValueControlDelegate(QtGui.QStyledItemDelegate): external_edit_required = QtCore.Signal(QtCore.QModelIndex) def createEditor(self, parent, option, index): model = index.model() control = model.control(index) widget = qt_editor(control, shape='hline', preferred=control.widget) if widget is None: self.external_edition(index) else: widget.setParent(parent) widget.set(control, True, True) return widget def setEditorData(self, editor, index): pass def paint(self, painter, option, index): model = index.model() control = model.control(index) paint = qt_painter(control, shape='hline') if paint: paint(control, painter, option.rect, option) else: QtGui.QStyledItemDelegate.paint(self, painter, option, index) def setModelData(self, editor, model, index): model.setData(index, str(editor.value()), QtCore.Qt.DisplayRole) model.setData(index, editor.value(), QtCore.Qt.EditRole) control = model.control(index) editor.set(control, False, False) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) def external_edition(self, index): self.external_edit_required.emit(index)
class WorldControlPanel(QtGui.QWidget, AbstractListener): StyleTableView = 0 StylePanel = 1 DEFAULT_STYLE = StylePanel attributeChanged = QtCore.Signal(str, dict) def __init__(self, parent=None, style=None): AbstractListener.__init__(self) QtGui.QWidget.__init__(self, parent=parent) self.world = None self.model = WorldModel() if style is None: style = self.DEFAULT_STYLE self.style = style self._manager = {} self._cb_world_object = QtGui.QComboBox() p = QtGui.QSizePolicy self._cb_world_object.setSizePolicy(p(p.Expanding, p.Maximum)) self._cb_world_object.currentIndexChanged.connect( self._selected_object_changed) self._current = None self._default_manager = self._create_manager() self.interpreter = get_interpreter() self.interpreter.locals['world_control'] = self actionClearWorld = QtGui.QAction( QtGui.QIcon(":/images/resources/plant.png"), "Clear World", self) actionClearWorld.triggered.connect(self.clear) self._actions = [["Project", "World", actionClearWorld, 0]] self._layout = QtGui.QVBoxLayout(self) self._layout.addWidget(self._cb_world_object) if self.style == self.StyleTableView: self._view = ControlManagerWidget(manager=self._default_manager) self._layout.addWidget(self._view) elif self.style == self.StylePanel: self._view = None self._set_manager(self._default_manager) else: raise NotImplementedError('style %s' % self.style) def set_properties(self, properties): if self.style == self.StyleTableView: self._view.set_properties(properties) def properties(self): if self.style == self.StyleTableView: return self._view.properties() else: return [] def set_style(self, style): if style == self.style: return world = self.world self.clear() if self.style == self.StyleTableView: view = self._view elif self.style == self.StylePanel: if self._view and self._view(): view = self._view() else: return # Remove old view view.setAttribute(QtCore.Qt.WA_DeleteOnClose) self._layout.removeWidget(view) view.close() del view self._view = None self.style = style if style == self.StyleTableView: self._view = ControlManagerWidget(manager=self._default_manager) self._layout.addWidget(self._view) self.set_world(world) def __getitem__(self, key): return self._manager[self._current].control(name=key) def initialize(self): from openalea.oalab.world.world import World from openalea.core.service.ipython import interpreter world = World() world.update_namespace(interpreter()) self.set_world(world) def actions(self): return self._actions def toolbar_actions(self): return self.actions() def notify(self, sender, event=None): signal, data = event if signal == 'world_changed': self.refresh() elif signal == 'world_object_removed': self.refresh() elif signal == 'world_object_changed': world, old_object, world_object = data self.refresh_manager(world_object) elif signal == 'world_object_item_changed': world, world_object, item, old, new = data self.refresh_manager(world_object) #self.refresh_item(world_object, item, old, new) elif signal == 'world_sync': self.refresh() def clear_managers(self): self._current = None self._cb_world_object.clear() for name, manager in self._manager.items(): manager.clear_followers() del self._manager[name] self._set_manager(self._default_manager) def clear(self): self.clear_managers() if self.world: self.world.unregister_listener(self) self.world = None def set_world(self, world): self.clear() self.world = world self.world.register_listener(self) if self.style == self.StyleTableView: self.model.set_world(world) for object_name in world.keys(): self.refresh_manager(world[object_name]) def _fill_manager(self, manager, world_object): if world_object: for attribute in world_object.attributes: manager.add(attribute['name'], interface=attribute['interface'], value=attribute['value'], alias=attribute['alias'], constraints=attribute['constraints']) manager.register_follower( attribute['name'], self._attribute_changed(world_object, attribute['name'])) def _get_manager(self, world_object): object_name = world_object.name if object_name not in self._manager: manager = self._create_manager(world_object) self._manager[object_name] = manager self._cb_world_object.addItem(object_name) return self._manager[object_name] def _create_manager(self, world_object=None): from openalea.core.control.manager import ControlContainer manager = ControlContainer() self._fill_manager(manager, world_object) return manager def _selected_object_changed(self, idx): if idx != -1: self.select_world_object(self._cb_world_object.itemText(idx)) def _set_manager(self, manager): if self.style == self.StylePanel: view = self._view if self._view is not None: view = self._view() if view: self._layout.removeWidget(view) view.close() del view from openalea.oalab.service.qt_control import edit view = edit(manager) view.setAttribute(QtCore.Qt.WA_DeleteOnClose) self._view = weakref.ref(view) self._layout.addWidget(view) view.show() self.repaint() elif self.style == self.StyleTableView: self._view.model.set_manager(manager) else: raise NotImplementedError('style %s' % self.style) def select_world_object(self, object_name): if object_name != self._current: self._current = object_name object_manager = self._manager[object_name] object_manager.disable_followers() self._set_manager(object_manager) object_manager.enable_followers() def refresh_item(self, world_object, item, old, new): object_name = world_object.name if item == 'attribute': manager = self._get_manager(world_object) attr_name = new['name'] attr_value = new['value'] control = manager.control(name=attr_name) if control: control.value = attr_value else: self.refresh_manager(world_object) def refresh_manager(self, world_object): object_name = world_object.name object_manager = self._get_manager(world_object) manager_attr_names = [ c.name for c in self._manager[object_name].controls() ] object_attr_names = [a['name'] for a in world_object.attributes] if manager_attr_names != object_attr_names: object_manager.clear_followers() object_manager.clear() self._fill_manager(object_manager, world_object) if self._current == object_name: self._set_manager(object_manager) object_manager.enable_followers() else: for a in world_object.attributes: if a['value'] != self._manager[object_name].control( a['name']).value: self._manager[object_name].control(a['name']).set_value( a['value']) def refresh(self): if self.world is not None: self.set_world(self.world) def _attribute_changed(self, world_object, attribute_name): def _changed(old, new): self._object_attribute_changed(world_object.name, attribute_name, old, new) return _changed def _object_attribute_changed(self, object_name, attribute_name, old, new): self.world[object_name].set_attribute(attribute_name, new)
class ProfileViewer(QGLViewer): _posCurveColorF = [0.0, 1.0, 0.0] positionChanged = QtCore.Signal(int) userSectionHighlighted = QtCore.Signal(object, object) def __init__(self, parent, profile=None): QGLViewer.__init__(self, parent) self.__profile = None self.__position = 0 self.__positionCurve = None self.increment = 0.01 self._axisScaleTolerance = abs(pi / 8.) # (radians) #states self.__oldY = None self.__scalingDirection = None #ranges self.__param_min = 0.0 self.__param_max = 1.0 # --rendering components -- self.factor = [1., 1., 1.] #x, y, z scene scaling factors. self.discretizer = Discretizer() self.renderer = GLRenderer(self.discretizer) self.renderer.renderingMode = GLRenderer.Dynamic # -- addons -- self._addons = [] self.visualSections = VisualCrossSectionsAddOn(self) self.grids = GridAddOn(self) self.userSlices = UserSlicesAddOn(self) def init(self): self.camera().setUpVector(Vec(0, 1, 0)) self.camera().setType(Camera.ORTHOGRAPHIC) self.camConstraint = WorldConstraint() self.camera().frame().setConstraint(self.camConstraint) self.camera().setZClippingCoefficient(4) #arbitrary. self.showEntireScene() def set_axis_scale_tolerance(self, tolerance): self._axisScaleTolerance = abs(tolerance) def set_profile(self, profile): if profile is not None: profile.add_update_callback(self.__profileChanged) self.__profile = profile # -- recompute stuff -- self.__param_min, self.__param_max = profile.get_param_range() self.increment = (self.__param_max - self.__param_min) / 100 for addon in self._addons: addon._init_from_profile(profile) self.set_position(self.range()[0]) else: self.__clearCaches() self.__profile = None def profile(self): return self.__profile def range(self): return self.__param_min, self.__param_max def position(self): return self.__position def set_position(self, position): # -- clamp position -- min, max = self.range() if position > max: position = max if position < min: position = min self.__position = position self.update_displayed_geometry() self.positionChanged.emit(position) if position not in self.__profile: position = self.round_within_increment(position) if position in self.__profile: self.userSectionHighlighted.emit(position, self.__profile[position]) def round_within_increment(self, pos): keys = map(self.unnormalised_parameter, self.__profile.iterkeys()) for k in keys: if abs(k - pos) <= self.increment: return k return pos ######################### # Normalisation Helpers # ######################### def normalised_parameter(self, par, **kwargs): if self.__profile: return self.__profile.normalised_parameter(par, **kwargs) def normalised_abscissa(self, par, **kwargs): if self.__profile: return self.__profile.normalised_abscissa(par, **kwargs) def unnormalised_parameter(self, par, **kwargs): if self.__profile: return self.__profile.unnormalised_parameter(par, **kwargs) def unnormalised_abscissa(self, par, **kwargs): if self.__profile: return self.__profile.unnormalised_abscissa(par, **kwargs) ################# # Painting code # ################# def draw(self): self.setBackgroundColor(QtGui.QColor(0, 0, 0)) glDisable(GL_LIGHTING) glEnable(GL_LINE_SMOOTH) glEnable(GL_BLEND) glEnable(GL_ALPHA_TEST) glAlphaFunc(GL_GREATER, 0.1) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST) glMatrixMode(GL_MODELVIEW) glPushMatrix() mat = 16 * [0] mat[0] = self.factor[0] mat[5] = self.factor[1] mat[10] = self.factor[2] mat[15] = 1. glMultMatrixd(mat) if self.__profile is not None: self.discretizer.clear() if self.__positionCurve: for addon in self._addons: addon._draw(self.renderer) glColor4f(0.0, 1.0, 0.0, 1.0) glDisable(GL_DEPTH_TEST) self.__positionCurve.apply(self.renderer) glEnable(GL_DEPTH_TEST) glPopMatrix() ############################### # Qt Event Handlers overloads # ############################### def mousePressEvent(self, event): if event.button() == QtCore.Qt.MidButton: self.__oldX = event.x() self.__oldY = event.y() else: QGLViewer.mousePressEvent(self, event) def mouseReleaseEvent(self, event): self.__oldX = None self.__oldY = None self.__scalingDirection = None QGLViewer.mouseReleaseEvent(self, event) def mouseMoveEvent(self, event): if self.__oldY is not None: pos = self.__position #some values that are used a bit everywhere newX, newY = event.x(), event.y() xDiff = float(newX - self.__oldX) yDiff = float(newY - self.__oldY) cartDist = sqrt(yDiff**2 + xDiff**2) mod = event.modifiers() if mod != QtCore.Qt.ControlModifier: delta = newY - self.__oldY if delta > 0: pos += self.increment elif delta < 0: pos -= self.increment self.__oldY = newY self.set_position(pos) elif self.__scalingDirection is None: if cartDist < 50: #pixels return #compute direction coefficient of the mouse pointer to the screen center if xDiff == 0.0: return pA = atan2(yDiff, xDiff) tol = self._axisScaleTolerance #compute projected direction coefficients of axes camera = self.camera() origin = camera.getProjectedCoordinatesOf(Vec(0., 0., 0.)) xProj = camera.getProjectedCoordinatesOf(Vec(1., 0., 0.)) yProj = camera.getProjectedCoordinatesOf(Vec(0., 1., 0.)) zProj = camera.getProjectedCoordinatesOf(Vec(0., 0., 1.)) xVec = (xProj[0] - origin[0]), (xProj[1] - origin[1]) yVec = (yProj[0] - origin[0]), (yProj[1] - origin[1]) zVec = (zProj[0] - origin[0]), (zProj[1] - origin[1]) #angles if xVec[0] == 0.0 or yVec[0] == 0.0 or zVec[0] == 0.0: return xA = atan(xVec[1] / xVec[0]) yA = atan(yVec[1] / yVec[0]) zA = atan(zVec[1] / zVec[0]) dire = min((abs(xA % pi - pA % pi), 0, pA, xVec), (abs(yA % pi - pA % pi), 1, pA, yVec), (abs(zA % pi - pA % pi), 2, pA, zVec)) self.__scalingDirection = dire if (dire[0] <= tol) else None elif self.__scalingDirection is not None: angD, dire, angle, vec = self.__scalingDirection #cartDist is used to obtain a significant difference #or else computations are screwed. if xDiff != 0.0 and yDiff != 0 and cartDist > 5: pA = atan2(yDiff, xDiff) if pA < 0: pA += 2 * pi if angle < 0: angle += 2 * pi angleDiv = int(angle / pi * 2) mouseDiv = int(pA / pi * 2) positive = angleDiv == mouseDiv f = self.factor[dire] f = f * 1.02 if positive else f * 0.98 self.factor[dire] = f self.__oldY = newY self.__oldX = newX self.update() else: QGLViewer.mouseMoveEvent(self, event) def keyPressEvent(self, event): key = event.key() if key in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Return]: self.__profile.create_cross_section(self.__position) elif key == QtCore.Qt.Key_Delete: position = self.round_within_increment(self.__position) if position in self.__profile: del self.__profile[position] ############################## # Internal State Maintenance # ############################## def update_displayed_geometry(self): if self.__profile: # -- 'add-ons' -- for addon in self._addons: addon._compute(self.__profile) self.__positionCurve = self.__profile(self.__position) self.update() def __clearCaches(self): for addon in self._addons: addon.clear_caches() def __profileChanged(self): # -- cleanup caches -- self.__clearCaches() # -- recompute stuff -- self.__param_min, self.__param_max = self.__profile.get_param_range() self.increment = (self.__param_max - self.__param_min) / 100 for addon in self._addons: addon._interpolation_changed() self.update_displayed_geometry()
class CurvePanel(QtGui.QScrollArea): class SimpleCurve2DView(Curve2DEditor): clicked = QtCore.Signal(object, object) deleteRequested = QtCore.Signal(object, object) def __init__(self, pos, curve, parent): Curve2DEditor.__init__(self, parent) self.pos = pos self.accessorType[NurbsCurve] = ProfileEditor.NurbsAccessor self.setFixedSize(100, 90) self.setEnabled(True) self.setAutoFillBackground(True) self.bkgdColor = self.backgroundColor() if curve: self.setCurve(curve) def highlight(self, val): if val: self.defaultColor = QtGui.QColor(100, 200, 100) else: self.defaultColor = self.bkgdColor self.update() def drawGrid(self): return def mousePressEvent(self, event): self.clicked.emit(self.pos, self.getCurve()) def mouseDoubleClickEvent(self, event): QGLViewer.mouseDoubleClickEvent(self, event) def mouseReleaseEvent(self, event): QGLViewer.mouseReleaseEvent(self, event) def contextMenuEvent(self, event): menu = QtGui.QMenu(self) deleteAction = menu.addAction("Delete section") deleteAction.triggered.connect(self.__on_delete_request) menu.popup(event.globalPos()) def __on_delete_request(self): self.deleteRequested.emit(self.pos, self.getCurve()) curveEditRequested = QtCore.Signal(object, object) curveDeleteRequested = QtCore.Signal(object, object) curveMoveRequested = QtCore.Signal(object, object) def __init__(self, orientation=QtGui.QBoxLayout.LeftToRight, parent=None): QtGui.QScrollArea.__init__(self, parent) self.orientation = orientation self.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.__curveViews = [] self.__curveLabels = [] self.__refreshTimout = QtCore.QTimer() self.__refreshTimout.setInterval(500) self.__refreshTimout.timeout.connect(self.__set_curves) self.__crvs = None def set_curves(self, crvs): self.__crvs = crvs if self.__refreshTimout.isActive(): self.__refreshTimout.stop() self.__refreshTimout.start() def get_curve(self, id): w = self.widget().layout().itemAt(id).widget() return w.getCurve() def __set_curves(self): self.__refreshTimout.stop() crvs = self.__crvs if len(crvs) == len(self.__curveViews): #refresh the existing views for i in range(len(crvs)): pos, c = crvs[i] v = self.__curveViews[i] v.setCurve(c) v.pos = pos self.__curveLabels[i].setValue(pos) else: #recreate them del self.__curveViews[:] del self.__curveLabels[:] scrolledWidget = QtGui.QWidget(self) layout = QtGui.QBoxLayout(self.orientation) scrolledWidget.setLayout(layout) # -- used to lock the bounding cross section line edits -- minPos = min(crvs, key=lambda x: x[0])[0] maxPos = max(crvs, key=lambda x: x[0])[0] for pos, c in crvs: frame = QtGui.QFrame(scrolledWidget) frame.setFrameShape(QtGui.QFrame.Panel) frame.setFrameShadow(QtGui.QFrame.Raised) subLay = QtGui.QBoxLayout(QtGui.QBoxLayout.TopToBottom) frame.setLayout(subLay) w = CurvePanel.SimpleCurve2DView(pos, c, frame) w.clicked.connect(self.curveEditRequested) w.deleteRequested.connect(self.curveDeleteRequested) label = QtGui.QDoubleSpinBox() label.setRange(-100000., 100000.) label.setValue(pos) label.setAlignment(QtCore.Qt.AlignHCenter) sender = SenderWidget(label, float, self) if pos in [minPos, maxPos]: label.setEnabled(False) else: sender.valueChanged.connect(self.__on_label_changed) subLay.addWidget(w, QtCore.Qt.AlignHCenter) subLay.addWidget(label, QtCore.Qt.AlignHCenter) layout.addWidget(frame) self.__curveViews.append(w) self.__curveLabels.append(label) self.setWidget(scrolledWidget) def highlight_curve_at_position(self, pos, crv): for cv in self.__curveViews: if cv.pos == pos: cv.highlight(True) else: cv.highlight(False) ################# # Private stuff # ################# def __on_label_changed(self, sender, value, typ): try: i = self.__curveLabels.index(sender.inner) except: return oldPosition = self.__curveViews[i].pos newPosition = value self.curveMoveRequested.emit(oldPosition, newPosition)
class ControlView(QtGui.QTreeView): controlsSelected = QtCore.Signal(list) def __init__(self): QtGui.QTreeView.__init__(self) self.setEditTriggers(self.DoubleClicked) self.setSelectionMode(self.SingleSelection) self.setSelectionBehavior(self.SelectRows) self.setDragEnabled(True) self.setDragDropMode(self.DragOnly) self.setSortingEnabled(False) self.delegate = ValueControlDelegate() self.delegate0 = NameControlDelegate() self.setItemDelegateForColumn(0, self.delegate0) self.setItemDelegateForColumn(1, self.delegate) self.setUniformRowHeights(True) self.setHeaderHidden(False) self._selected_indexes = None def contextMenuEvent(self, event): menu = QtGui.QMenu(self) action = QtGui.QAction("New control", menu) action.triggered.connect(self.new_control) menu.addAction(action) if self.selectedIndexes(): self._selected_indexes = self.selectedIndexes() action = QtGui.QAction("Delete control", menu) action.triggered.connect(self.delete_control) menu.addAction(action) action = QtGui.QAction("Import L-Py controls", menu) action.triggered.connect(self.import_lpy) menu.addAction(action) action = QtGui.QAction("Export L-Py controls", menu) action.triggered.connect(self.export_lpy) menu.addAction(action) action = QtGui.QAction("Save controls", menu) action.triggered.connect(self.save_controls) menu.addAction(action) action = QtGui.QAction("Load controls", menu) action.triggered.connect(self.load_controls) menu.addAction(action) menu.exec_(event.globalPos()) def new_control(self): editor = ControlEditor('control') dialog = ModalDialog(editor) if dialog.exec_(): control = editor.control() if self.model()._manager.control(control.name): QtGui.QMessageBox.information(self, 'Error on adding control', 'A control with name %s already exists' % control.name) else: self.model().add_control(control) def delete_control(self): if self._selected_indexes is None: return self.model().remove_controls(self._selected_indexes) self._selected_indexes = None def save_controls(self, filename=None): if not filename: filename = QtGui.QFileDialog.getSaveFileName(self, 'Select python file') if filename: save_controls(self.model()._manager.controls(), filename) def load_controls(self, filename=None): if not filename: filename = QtGui.QFileDialog.getOpenFileName(self, 'Select python file') if filename: if path(filename).exists(): self.model()._manager.clear() code = file(filename, 'r').read() exec(code) def import_lpy(self): from openalea.plantlab.lpycontrol import import_lpy_controls filename = QtGui.QFileDialog.getOpenFileName(self, 'Select L-Py file') if filename: import_lpy_controls(filename) def export_lpy(self): from openalea.plantlab.lpycontrol import export_lpy_controls filename = QtGui.QFileDialog.getSaveFileName(self, 'Select L-Py file') if filename: mcontrols = [(c.name, c.interface, c.value) for c in self.model()._manager.controls()] export_lpy_controls(mcontrols, filename) def selectionChanged(self, selected, deselected): rows = set() for index in selected.indexes(): rows.add(index.row()) controls = [] for row in rows: index = self.model().createIndex(row, 1) controls.append(self.model().control(index)) self.controlsSelected.emit(controls) return QtGui.QTreeView.selectionChanged(self, selected, deselected) def onRowsInserted(self, *args, **kwargs): self.resizeColumnToContents(0)
class ImageStackViewerWidget(QtGui.QWidget): """ Widget based on openalea.image.gui.slide_viewer.PixmapStackView """ valueChanged = QtCore.Signal(object) stackChanged = QtCore.Signal(object) def __init__(self): QtGui.QWidget.__init__(self) self._im_view = PixmapStackView() self._label = ScalableLabel() self._layout = QtGui.QVBoxLayout(self) self._layout.addWidget(self._label) self._label.setMouseTracking(True) self._last_mouse_x = 0 self._last_mouse_y = 0 self.connect(self._label, QtCore.SIGNAL("mouse_press"), self.mouse_pressed) self.connect(self._label, QtCore.SIGNAL("mouse_move"), self.mouse_pressed) self.axis = 2 self.inc = 1 # index increment self._palette_name = None ############################################## # Qt Control API ############################################## def setValue(self, img): if img is self.value(): return self._im_view.set_image(img) try: self.resolution = img.resolution[:] except AttributeError: pass self._im_view._reconstruct_pixmaps() self.change_axis(0) self.slice_changed((0 + self._im_view.nb_slices() - 1) / 2) self.emit_stack_changed() def value(self): return self._im_view.image() ############################################## # slots ############################################## def emit_stack_changed(self): img = self.value() if img is None: self.stackChanged.emit(None) else: args = dict(shape=img.shape, axis=self.axis, slice=self._im_view.current_slice(), resolution=self.resolution, inc=self.inc, palette_name=self._palette_name) self.stackChanged.emit(args) def change_axis(self, ind): if self.axis == ind: return try: res = list(self.resolution) del res[self.axis] tr = self._im_view._transform print res if tr % 180: self._label._resolution = res[1], res[0] else: self._label.set_resolution(*res[:2]) except AttributeError: pass self._im_view._reconstruct_pixmaps(self.axis) self.emit_stack_changed() self.update_pix() #self.fill_infos() def mouse_pressed(self, event): self._last_mouse_x = event.x() self._last_mouse_y = event.y() def change_palette(self, palette_name): if palette_name == self._palette_name: return self._palette_name = palette_name self.emit_stack_changed() img = self._im_view.image() if img is None: return palette = palette_factory(palette_name, img.max()) self._im_view.set_palette(palette, self.axis) self.update_pix() def update_pix(self): pix = self._im_view.pixmap() if pix is not None: self._label.setPixmap(pix) def get_pixel_value_str(self, img, x, y, z): px = img[x, y, z] if isinstance(px, np.ndarray): return str(px) else: return "%3d" % px def slice_changed(self, ind): img = self.value() if img is None: return if ind >= img.shape[self.axis]: ind = img.shape[self.axis] - 1 if ind < 0: ind = 0 if self._im_view.current_slice() == ind: return self._im_view.set_current_slice(ind) self.emit_stack_changed() self.update_pix() def snapshot(self): """write the current image """ pix = self._im_view.pixmap() if pix is not None: pix.save("slice%.4d.png" % self.panel.img_slider.value()) def wheelEvent(self, event): self.inc = event.delta() / 8 / 15 idx = self._im_view.current_slice() self.slice_changed(idx + self.inc) def rotate_left(self): res = self._label._resolution self._label._resolution = res[1], res[0] self._im_view.rotate(-1) self.update_pix() def rotate_right(self): res = self._label._resolution self._label._resolution = res[1], res[0] self._im_view.rotate(1) self.update_pix()
class RichTextEditor(QtGui.QWidget): textChanged = QtCore.Signal() def __init__(self, parent=None): super(RichTextEditor, self).__init__(parent) self.completer = DictionaryCompleter(parent=self) self.editor = self._default_editor(parent=self) self.editor.textChanged.connect(self.textChanged.emit) # self.editor.setCompleter(self.completer) self.goto_widget = GoToWidget(parent=self.editor) self.search_widget = SearchWidget(parent=self.editor) self.layout = QtGui.QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.layout.addWidget(self.editor) self.layout.addWidget(self.search_widget) self.setLayout(self.layout) self.search_widget.hide() def _default_editor(self, *args, **kwargs): return TextEditor(*args, **kwargs) def actions(self): """ :return: list of actions to set in the menu. """ return None def mainMenu(self): return "Project" def set_text(self, txt): """ Set text in the editor :param text: text you want to set """ self.editor.set_text(txt) set_script = set_text def get_selected_text(self): return self.editor.get_selected_text() def get_text(self, start='sof', end='eof'): """ Return a part of the text. :param start: is the begining of what you want to get :param end: is the end of what you want to get :return: text which is contained in the editor between 'start' and 'end' """ return self.editor.get_text(start=start, end=end) def get_code(self, start='sof', end='eof'): return self.get_text(start=start, end=end) def replace_tab(self): return self.editor.replace_tab() def goto(self): self.goto_widget.show() def comment(self): self.editor.comment() def uncomment(self): self.editor.uncomment() def undo(self): self.editor.undo() def redo(self): self.editor.redo() def search(self): if self.search_widget.hiden: cursor = self.editor.textCursor() cursor.setPosition(0) self.editor.setTextCursor(cursor) self.search_widget.show() # self.search_widget.raise_() self.search_widget.lineEdit.setFocus() txt = self.get_selected_text() self.search_widget.lineEdit.setText(txt) self.search_widget.hiden = False else: self.search_widget.hide() self.search_widget.hiden = True
class Project(QtCore.QObject): # -- SIGNALS -- closed = QtCore.Signal(object) saved = QtCore.Signal(object) modified = QtCore.Signal(object) data_added = QtCore.Signal(object, object) data_name_changed = QtCore.Signal(object, object, str) project_name_changed = QtCore.Signal(object, str) # -- PROPERTIES -- name = property(lambda x: x.__name, lambda x, y: x.set_name(y)) def __init__(self, name): QtCore.QObject.__init__(self) self.__name = name self.__modified = False self.__names = set() self.__docIdCtr = 0 self.__docs = {} self.__docToIds = {} self.__docprops = {} def set_name(self, value): self.__name = value self.project_name_changed.emit(self, value) def __fix_name(self, name): i = 1 original = name candidate = name while candidate in self.__names: candidate = name + " (%i)" % i i += 1 self.__names.add(candidate) return candidate def add_data(self, doc): name = self.__fix_name(doc.name) doc.name = name self.__docs[self.__docIdCtr] = doc self.__docToIds[doc] = self.__docIdCtr self.__docIdCtr += 1 self.data_added.emit(self, doc) self.mark_as_modified() def get_data_id(self, data): return self.__docToIds.get(data, -1) def get_data(self, doc_id): return self.__docs.get(doc_id) def get_data_by_name(self, name): for datum in self.__docs.itervalues(): if name == datum.name: return datum return None def del_data(self, doc_id): self.mark_as_modified() def has_data(self, doc): return doc in self.__docs.itervalues() def is_modified(self): return self.__modified def __iter__(self): return self.__docs.iteritems() def set_data_name(self, doc, name): if not self.has_data(doc): return #raise something oldName = doc.name self.__names.discard(oldName) fixedName = self.__fix_name(name) doc.name = fixedName # doc._set_name( fixedName ) self.data_name_changed.emit(self, doc, fixedName) def set_data_property(self, doc, key, val): if doc not in self.__docToIds: raise Exception() self.__docprops.setdefault(doc, {})[key] = val def get_data_property(self, doc, key): if not self.has_data_property(doc, key): return None props = self.__docprops.get(doc) if props is not None: return props.get(key) def pop_data_property(self, doc, key): if not self.has_data_property(doc, key): return None props = self.__docprops.get(doc) if props is not None: return props.pop(key) def has_data_property(self, doc, key): props = self.__docprops.get(doc) if not props: return False #TODO : raise something dude return key in props def close(self): self.closed.emit(self) self.__names.clear() self.__docs.clear() self.__docToIds.clear() self.__docprops.clear() # self.closed.disconnect() # self.saved.disconnect() # self.modified.disconnect() def mark_as_modified(self): self.__modified = True self.modified.emit(self) def mark_as_saved(self): self.__modified = False self.saved.emit(self) ############ # Pickling # ############ def save_to(self, filepath): docnames = [doc.name+":"+doc.factory_name+":"+doc.type \ for doc in self.__docs.itervalues()] manifest = reduce(lambda x, y: x + "\n" + y, docnames, "name=" + self.name) print manifest with zipfile.ZipFile(filepath, "w") as z: z.writestr("manifest.txt", manifest) for d in self.__docs.itervalues(): try: stream = io.BytesIO() d.to_stream(stream) except Exception, e: print "couldn't write", f, " : ", e else: z.writestr(d.name, stream.getvalue())
class AbstractSourceManager(QtCore.QObject): __metaclass__ = make_metaclass((ProxySingleton, ), (QtCore.pyqtWrapperType, )) item_list_changed = QtCore.Signal(list) __managers__ = [] def __init__(self): QtCore.QObject.__init__(self) self._sources = {} self._items = {} self._customItems = {} AbstractSourceManager.__managers__.append(self) def __iter__(self): return self._items.iteritems() def add_custom_item(self, name, value): self._customItems[name] = value def _add_source(self, src): if src.name in self._sources: return #TODO : raise something dude src.item_list_changed.connect(self.update_with_source) self._sources[src.name] = src def iter_sources(self): return self._sources.itervalues() def gather_items(self, refresh=False): if refresh: self._items = {} for src in self.iter_sources(): if not src.is_valid(): continue src.gather_items() self._items.update(src.get_items()) self._items.update(self._customItems) self.item_list_changed.emit(list(self._items.iterkeys())) return self._items.copy() def get(self, name): return self._items.get(name, None) def iter_item_names(self): return self.gather_items().iterkeys() def update_with_source(self, src, items): self._items.update(items) self.item_list_changed.emit(list(self._items.iterkeys())) @classmethod def init_sources(cls): raise NotImplementedError @classmethod def init(cls): from openalea.secondnature.layouts import LayoutManager from openalea.secondnature.applets import AppletFactoryManager from openalea.secondnature.data import DataFactoryManager lm = LayoutManager() am = AppletFactoryManager() dm = DataFactoryManager() lm.init_sources() am.init_sources() dm.init_sources() lm.gather_items(refresh=True) am.gather_items(refresh=True) dm.gather_items(refresh=True) @staticmethod def post_status_message(msg): app = QtCore.QCoreApplication.instance() app.post_status_message(msg)