def DataFactory(path, mimetype=None, **kwargs): path = Path(path) default_content = kwargs[ 'default_content'] if 'default_content' in kwargs else None dtype = kwargs.pop('dtype', None) if path.isfile(): if default_content is not None: raise ValueError( "got multiple values for content (parameter and '%s')" % path.name) else: path, mimetype = arrange_data_args(path, mimetype, dtype) klass = DataClass(mimetype) return klass(path=path, mimetype=mimetype) elif path.exists(): raise ValueError("'%s' exists but is not a file" % path) elif not path.exists(): if default_content is None: default_content = b'' try: f = path.open('wb') except IOError: content = default_content else: f.write(default_content) f.close() content = None path, mimetype = arrange_data_args(path, mimetype, dtype) klass = DataClass(mimetype) return klass(path=path, mimetype=mimetype, content=content)
def load(self, name, projectdir=None, **kwargs): """ Load existing project :use: >>> project1 = project_manager.load('project1') >>> project2 = project_manager.load('project2', '/path/to/project') # doctest: +SKIP :param name: name of project to load. Must be a string. :param projectdir: path of project to load. Must be a path (see module path.py). By default, try to guess with name only. If there are various projects with the same name, return the first. :return: Project """ project = None if 'proj_path' in kwargs: projectdir = kwargs['proj_path'] elif 'path' in kwargs: projectdir = kwargs['path'] if not projectdir: try: project = self.item(name) except UnknownItemError: pass else: full_path = Path(projectdir) / name if full_path.exists(): project = Project(full_path) else: print 'Project %s in repository %s does not exist' % (name, projectdir) if project: self.cproject = project return self.cproject
def discover(self, config_name='oaproject.cfg'): """ Discover projects from your disk and put them in self.projects. Projects are not loaded, only metadata are. :use: >>> project_manager.discover() >>> list_of_projects = project_manager.projects To discover new projects, you can add path into *self.repositories* .. code-block:: python project_manager.repositories.append('path/to/search/projects') project_manager.discover() """ projects = {} for _path in self.repositories: _path = Path(_path) if not _path.exists(): continue for p in _path.walkfiles(config_name): project = Project(p.parent) projects[project.path] = project self.projects = projects.values()
def DataFactory(path, mimetype=None, **kwargs): path = Path(path) default_content = kwargs['default_content'] if 'default_content' in kwargs else None dtype = kwargs.pop('dtype', None) if path.isfile(): if default_content is not None: raise ValueError( "got multiple values for content (parameter and '%s')" % path.name) else: path, mimetype = arrange_data_args(path, mimetype, dtype) klass = DataClass(mimetype) return klass(path=path, mimetype=mimetype) elif path.exists(): raise ValueError("'%s' exists but is not a file" % path) elif not path.exists(): if default_content is None: default_content = b'' try: f = path.open('wb') except IOError: content = default_content else: f.write(default_content) f.close() content = None path, mimetype = arrange_data_args(path, mimetype, dtype) klass = DataClass(mimetype) return klass(path=path, mimetype=mimetype, content=content)
def load(self, filename): """ :param filename: filename to convert into python object :return: a python object interpreted from string "text" """ filename = Path(filename) if filename.exists(): obj = open(filename, 'rU').read() return obj
def list_colormaps(): colormap_names = [] colormaps_path = Path(shared_data(openalea.oalab, 'colormaps/grey.lut')).parent for colormap_file in colormaps_path.walkfiles('*.lut'): colormap_name = str(colormap_file.name[:-4]) colormap_names.append(colormap_name) colormap_names.sort() return colormap_names
def load_colormaps(): from openalea.oalab.colormap.colormap_utils import Colormap, colormap_from_file colormaps = {} colormaps_path = Path(shared_data(openalea.oalab, 'colormaps/grey.lut')).parent for colormap_file in colormaps_path.walkfiles('*.lut'): colormap_name = str(colormap_file.name[:-4]) colormaps[colormap_name] = colormap_from_file( colormap_file, name=colormap_name) return colormaps
def _rename_item(self, category, old, new): pold = Path(old) pnew = Path(new) if pold.isabs() or pnew.isabs() or pnew.name != new or pold.name != old: raise ValueError('You must give filename only, not path') new_path = self.path / category / new data = self.get_item(category, old) data.move(new_path) self._remove_item(category, filename=old) self._add_item(category, data)
def load_colormaps(): from openalea.oalab.colormap.colormap_utils import Colormap, colormap_from_file colormaps = {} colormaps_path = Path(shared_data(openalea.oalab, 'colormaps/grey.lut')).parent for colormap_file in colormaps_path.walkfiles('*.lut'): colormap_name = str(colormap_file.name[:-4]) colormaps[colormap_name] = colormap_from_file(colormap_file, name=colormap_name) return colormaps
def _open_file(self, path): filename = Path(path) if filename.isdir(): print 'BUG: filename is a dir' return try: file_ = open(filename, "w") except IOError: newdir, fn = filename.splitpath() if not Path(newdir).isdir(): newdir.makedirs() file_ = open(filename, "w") return file_
def load(self, filename): """ :param filename: filename to convert into python object :return: a python object interpreted from string "text" """ filename = Path(filename) if filename.exists(): obj = open(filename, 'rU').read() try: return eval(obj) except SyntaxError: return obj except NameError: return obj
def qicon(filename): if filename is None: return QtGui.QIcon(get_shared_data('icons/oxygen_application-x-desktop.png')) if filename.startswith(':/'): return QtGui.QIcon(filename) else: path = Path(filename) if not path.isfile(): path = get_shared_data(filename) if path is None: path = get_shared_data('icons/%s' % filename) if path: return QtGui.QIcon(path) else: return QtGui.QIcon(":/images/resources/%s" % filename)
def load(self, filename): """ :param filename: filename to convert into python object :return: a python object interpreted from string "text" """ filename = Path(filename) if filename.exists(): try: from openalea.plantgl.all import Scene sc = Scene() sc.clear() sc.read(str(filename), "BGEOM") return sc except ImportError: warnings.warn("You must install PlantGL if you want to load a BGEOM object.") except Exception, e: print e warnings.warn("Impossible to load the scene")
def arrange_path(path, path_class=Path): u""" Return a Path, FilePath or DirPath dependings on path nature. Path is used for special path like device "files" or path not existing on disk. If path is empty, returns None. If path do not exists on disk or is not file nor directory (like /dev/xyz on linux),it return a path_class. """ if not path: return None path = Path(unicode(path)) if path.isfile(): return FilePath(path) elif path.isdir(): return DirPath(path) else: return path_class(path)
def arrange_path(path, path_class=Path): """ Return a Path, FilePath or DirPath dependings on path nature. Path is used for special path like device "files" or path not existing on disk. If path is empty, returns None. If path do not exists on disk or is not file nor directory (like /dev/xyz on linux),it return a path_class. """ if not path: return None path = Path(str(path)) if path.isfile(): return FilePath(path) elif path.isdir(): return DirPath(path) else: return path_class(path)
def search_path(): """ Return a list of all path containing projects """ repositories = set() # 1. Add default user project dir repositories.add(Path(settings.get_project_dir())) # 2. Add project repositories defined by packages for plugin in plugins( 'oalab.plugin', criteria=dict(implement="ProjectRepositoryList")): for repository in plugin(): repositories.add(repository) # 3. Read repositories defined by users and saved in config config = settings.Settings() lst = list(repositories) try: s = config.get("ProjectManager", "Path") lst = eval(s, {"path": Path}) except NoSectionError: config.add_section("ProjectManager") config.add_option("ProjectManager", "Path", str([str(path) for path in lst])) except NoOptionError: config.add_option("ProjectManager", "Path", str([str(path) for path in lst])) for repo in lst: repositories.add(repo) # Remove all paths to directories that don't exist final_list = set() for p in repositories: p = Path(p).abspath() if not p.isdir(): continue final_list.add(p) return list(final_list)
def save(self, obj, path, protocol=None, **kwds): mode = kwds.pop('mode', 'all') path = Path(path) config_filename = kwds.get('config_filename', 'oaproject.cfg') config_path = path / config_filename if mode == 'all': if not path.exists(): path.makedirs() self._save_metadata(obj, config_path) lines = self._save_controls(obj) with open(path / 'control.py', 'w') as f: for line in lines: f.write(line) elif mode == 'metadata': if path.exists(): self._save_metadata(obj, config_path) else: raise NotImplementedError('mode=%s' % mode)
def load(self, filename): """ :param filename: filename to convert into python object :return: a python object interpreted from string "text" """ filename = Path(filename) if filename.exists(): try: from openalea.plantgl.all import Scene sc = Scene() sc.clear() sc.read(str(filename), "BGEOM") return sc except ImportError: warnings.warn( "You must install PlantGL if you want to load a BGEOM object." ) except Exception, e: print e warnings.warn("Impossible to load the scene")
def _normpath(path): """ Replace all symlink in path with real path and return its absolute path For example, if given path is "local/bin/python" and "local" is a symbolic link to "/usr/local", returned path will be "/usr/local/bin/python" """ if hasattr(os, 'readlink'): parts = Path(path).splitall() _path = Path('') for p in parts: _path = _path / p if _path.islink(): # readlink return an absolute path or relative path depending on symlink. # If symlink is a relative link, parent path is used to generate an absolute path # Default path behaviour when concatenating two absolute paths is to keep only second one: # path('/a/1')/path('/b/2') -> path('/b/2') # So, if symlink is absolute, all is ok _path = _path.parent / _path.readlink() return _path.abspath() else: return path.abspath()
def MimeType(path=None, name=None): """ Return mimetype for path. First, try to find extension in registery filled by models. If datatype is not found, use builtin module "mimetypes". If it cannot guess, returns False. Search in module allows to specify """ if path: name = Path(path).ext[1:].lower() if name in REGISTERY_NAME_MIME: return REGISTERY_NAME_MIME[name] else: mtype, encoding = mimetypes.guess_type(path) return mtype else: name = name.lower() if name in REGISTERY_NAME_MIME: return REGISTERY_NAME_MIME[name] else: return False
def load(self, filename): """ :param filename: filename to convert into python object :return: a python object interpreted from filename """ filename = Path(filename) if filename.exists(): cpik = "False" try: import cPickle cpik = "True" except ImportError: warnings.warn("You must install cPickle.") if cpik: try: file_ = open(filename, "r") ret = cPickle.load(file_) file_.close() return ret except Exception, e: print "Can't load file " + filename + " with loader CPickleLoader. " print e
def search_path(): """ Return a list of all path containing projects """ repositories = set() # 1. Add default user project dir repositories.add(Path(settings.get_project_dir())) # 2. Add project repositories defined by packages for plugin in plugins('oalab.plugin', criteria=dict(implement="ProjectRepositoryList")): for repository in plugin(): repositories.add(repository) # 3. Read repositories defined by users and saved in config config = settings.Settings() lst = list(repositories) try: s = config.get("ProjectManager", "Path") lst = eval(s, {"path": Path}) except NoSectionError: config.add_section("ProjectManager") config.add_option("ProjectManager", "Path", str([str(path) for path in lst])) except NoOptionError: config.add_option("ProjectManager", "Path", str([str(path) for path in lst])) for repo in lst: repositories.add(repo) # Remove all paths to directories that don't exist final_list = set() for p in repositories: p = Path(p).abspath() if not p.isdir(): continue final_list.add(p) return list(final_list)
def __init__(self, **kwargs): """ Classical use : *path* exists. Nothing is loaded in memory. Use :meth:`~Data.read` to get content """ # TODO: document args self.path = Path(kwargs.pop('path')) if 'path' in kwargs else None self._filename = Path(kwargs.pop('filename')).name if 'filename' in kwargs else None if self._filename is None and self.path is None: raise ValueError('path or filename required') if self._filename and self.path and self.path.name != self._filename: raise ValueError("path '%s' and filename '%s' are not compatible" % (self.path, self._filename)) self.dtype = kwargs.pop('dtype', None) self._content = kwargs.pop('content', None) self.mimetype = kwargs.pop('mimetype', None)
def _load_layout(self, layout=None, **kwds): layout_file = kwds.pop('layout_file', self.DEFAULT_LAYOUT_PATH) default_layout = kwds.pop('default_layout', self.DEFAULT_LAYOUT) self.layout_filepath = Path(layout_file).abspath() if layout is None: if self.layout_filepath.exists(): with open(self.layout_filepath) as layout_file: content = layout_file.read() try: layout = json.loads(content) except ValueError: l = eval(content) layout = dict(children=l[0], parents=l[1], properties=l[2]) if layout is None: layout = default_layout return layout
def compile_ui_files(module, import_instructions=None): """ Reads recursively all *.py files in root directory looking for "generate_pyfile_from_uifile" calls. If this call is found, execute it in order to compile ui file. import_instructions : python code containing required imports. example : >>> import_instructions = 'from openalea.plantgl.gui.qt.designer import generate_pyfile_from_uifile\n' if None, uses default imports : generate_pyfile_from_uifile, Path, hardbook and get_data """ import ast from openalea.core import codegen if import_instructions is None: import_instructions = "from openalea.plantgl.gui.qt.designer import generate_pyfile_from_uifile\n" module = __import__(module) paths = [] for root in module.__path__: root = Path(root) for py in root.walkfiles('*.py'): paths.append((root, py)) for root, py in paths: f = open(py) lines = f.readlines() f.close() code = ''.join(lines) try: r = ast.parse(code) except SyntaxError: print('SYNTAX ERROR: cannot read ...', py) else: for instr in r.body: if isinstance(instr, ast.Expr): value = instr.value if isinstance(value, ast.Call): try: func_name = value.func.id except AttributeError: pass else: if func_name == 'generate_pyfile_from_uifile': true = ast.parse('True').body[0] for keyword in value.keywords: if keyword.arg == 'force': keyword.value = true break else: value.keywords.append(ast.keyword('force', true)) src = codegen.to_source(instr) if py.startswith('./') or py.startswith('.\\'): py = Path(py[2:]) name = replaceext(root.parent.relpathto(py), '').replace(os.sep, '.') src = src.replace('__name__', repr(name)) try: code = compile(import_instructions + src, "<string>", "exec") exec(code) except Exception as e: print(repr(e)) print('COMPILATION ERROR: cannot compile', py) print()
def compile_ui_files(module, import_instructions=None): """ Reads recursively all *.py files in root directory looking for "generate_pyfile_from_uifile" calls. If this call is found, execute it in order to compile ui file. import_instructions : python code containing required imports. example : >>> import_instructions = 'from openalea.vpltk.qt.designer import generate_pyfile_from_uifile\n' if None, uses default imports : generate_pyfile_from_uifile, Path, hardbook and get_data """ import ast from openalea.core import codegen if import_instructions is None: import_instructions = "from openalea.vpltk.qt.designer import generate_pyfile_from_uifile\n" module = __import__(module) paths = [] for root in module.__path__: root = Path(root) for py in root.walkfiles('*.py'): paths.append((root, py)) for root, py in paths: f = open(py) lines = f.readlines() f.close() code = ''.join(lines) try: r = ast.parse(code) except SyntaxError: print 'SYNTAX ERROR: cannot read ...', py else: for instr in r.body: if isinstance(instr, ast.Expr): value = instr.value if isinstance(value, ast.Call): try: func_name = value.func.id except AttributeError: pass else: if func_name == 'generate_pyfile_from_uifile': true = ast.parse('True').body[0] for keyword in value.keywords: if keyword.arg == 'force': keyword.value = true break else: value.keywords.append(ast.keyword('force', true)) src = codegen.to_source(instr) if py.startswith('./') or py.startswith('.\\'): py = Path(py[2:]) name = replaceext(root.parent.relpathto(py), '').replace(os.sep, '.') src = src.replace('__name__', repr(name)) try: code = compile(import_instructions + src, "<string>", "exec") exec code except Exception as e: print repr(e) print 'COMPILATION ERROR: cannot compile', py print
class OALabMainWin(QtGui.QMainWindow): appletSet = QtCore.Signal(object, object) DEFAULT_MENU_NAMES = ('File', 'Edit', 'View', 'Help') DEFAULT_LAYOUT = dict( name='default', alias='Default Layout', children={}, parents={0: None}, properties={ 0: { 'widget': { 'properties': {'position': 0}, 'applets': [{'name': 'ShellWidget'}] } }} ) DEFAULT_LAYOUT_PATH = 'layout.oaui' LAB = None def __init__(self, layout=None, **kwds): QtGui.QMainWindow.__init__(self) self._lab = kwds.get('lab', None) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.DEFAULT_MENU_NAMES = list(self.DEFAULT_MENU_NAMES) if 'Edit' not in self.DEFAULT_MENU_NAMES: self.DEFAULT_MENU_NAMES.insert(0, 'Edit') if 'File' not in self.DEFAULT_MENU_NAMES: self.DEFAULT_MENU_NAMES.insert(0, 'File') if 'Help' not in self.DEFAULT_MENU_NAMES: self.DEFAULT_MENU_NAMES.append('Help') # Classic menu self._registered_applets = [] self._create_menus() self._create_actions() self._pre_fill_menus() self._splittable_list = [] self.layout_selector = LayoutSelector(parent=self) layout = self._load_layout(layout, **kwds) if isinstance(layout, (list, tuple)): layouts = layout else: layouts = [layout] for layout in layouts: title = layout.get('title', None) if 'children' not in layout: layout = None splittable = OALabSplittableUi(parent=self) splittable.setAttribute(QtCore.Qt.WA_DeleteOnClose) splittable.appletSet.connect(self.appletSet.emit) self.appletSet.connect(self._on_applet_set) if layout is None: container = AppletContainer() splittable.setContentAt(0, container) else: splittable.fromJSON(layout) self._splittable_list.append(splittable) self.layout_selector.add_widget(splittable, title=title) self.setCentralWidget(self.layout_selector) self._post_fill_menus() self.set_edit_mode(False) QtGui.QApplication.instance().focusChanged.connect(self._on_focus_changed) def emit_applet_set(self): self.splittable.emit_applet_set() @property def splittable(self): return self.layout_selector.widget() def _create_menus(self): self.menu_classic = {} menubar = QtGui.QMenuBar() self.setMenuBar(menubar) for menu_name in self.DEFAULT_MENU_NAMES: self.menu_classic[menu_name] = menubar.addMenu(menu_name) def _create_actions(self): self.action_edit = QtGui.QAction("Edit Layout", self.menu_classic['Edit']) self.action_edit.setCheckable(True) self.action_edit.toggled.connect(self.set_edit_mode) self.action_edit.setChecked(False) icon = QtGui.QApplication.style().standardIcon(QtGui.QStyle.SP_TitleBarCloseButton) self.action_quit = QtGui.QAction(icon, "Quit application", self.menu_classic['File']) self.action_quit.triggered.connect(self.close) self.action_quit.setChecked(False) def _pre_fill_menus(self): self.menu_classic['Edit'].addAction(self.action_edit) def _post_fill_menus(self): self.menu_classic['File'].addSeparator() self.menu_classic['File'].addAction(self.action_quit) def _load_layout(self, layout=None, **kwds): layout_file = kwds.pop('layout_file', self.DEFAULT_LAYOUT_PATH) default_layout = kwds.pop('default_layout', self.DEFAULT_LAYOUT) self.layout_filepath = Path(layout_file).abspath() if layout is None: if self.layout_filepath.exists(): with open(self.layout_filepath) as layout_file: content = layout_file.read() try: layout = json.loads(content) except ValueError: l = eval(content) layout = dict(children=l[0], parents=l[1], properties=l[2]) if layout is None: layout = default_layout return layout def _save_layout(self): with open(self.layout_filepath, 'w') as layout_file: json.dump(self.layout(), layout_file, sort_keys=True, indent=2) def closeEvent(self, event): close = True # If a lab is used, check if it can be close if hasattr(self._lab, 'readytoclose'): close = self._lab.readytoclose() # If lab is not ready, stop closing if close is False: event.ignore() return # If lab is ready to close, or no lab is used, close widget if self.splittable.close(): if hasattr(self._lab, 'finalize'): self._lab.finalize() self._save_layout() if hasattr(self._lab, 'stop'): self._lab.stop() event.accept() else: event.ignore() def set_edit_mode(self, mode=True): for widget in self.splittable.getAllContents(): if hasattr(widget, 'set_edit_mode'): widget.set_edit_mode(mode) if mode is True and self.LAB: print self.LAB.connections self.splittable.set_edit_mode(mode) def initialize(self): self.pm = PluginManager() for instance in plugin_instances('oalab.applet'): if hasattr(instance, 'initialize'): instance.initialize() def _widget_actions(self, obj): actions = None if hasattr(obj, 'toolbar_actions'): if isinstance(obj.toolbar_actions, list): actions = obj.toolbar_actions else: actions = obj.toolbar_actions() if actions is None: return [] else: return actions def _widget_name(self, obj): if hasattr(obj, 'name'): return obj.name def _on_focus_changed(self, old, new): self.clear_toolbar() if old is new: return # Generally focus is on "leaf" widget on widget hierarchy. # We try to browse all tree to get widget defining actions # For example, if an editor is defined as MyEditor -> Container -> Editor -> QTextEdit # Widget with focus is QTextEdit but widget that define actions is MyEditor # Search stops if widget has no more parents or if widget is AppletContainer parent = new actions = self._widget_actions(parent) name = self._widget_name(parent) while parent is not None: try: parent = parent.parent() except TypeError: break else: if isinstance(parent, AppletContainer): break name = name or self._widget_name(parent) actions += self._widget_actions(parent) if actions: self.fill_toolbar(name, actions) # toolbar creation/destruction set focus to toolbar so we reset it to widget if isinstance(new, QtGui.QWidget): new.setFocus(QtCore.Qt.OtherFocusReason) def fill_toolbar(self, name, actions): menus = plugin_instances('oalab.applet', 'ContextualMenu') for menu in menus: menu.set_actions(name, actions) def clear_toolbar(self): menus = plugin_instances('oalab.applet', 'ContextualMenu') for menu in menus: menu.clear() def _merge_menus(self, menus): parent = self default_menus = self.menu_classic menubar = self.menuBar() for _menu in menus: menu_name = _menu.title() if menu_name in default_menus: menu = default_menus[menu_name] else: menu = QtGui.QMenu(menu_name, parent) default_menus[menu_name] = menu menubar.addMenu(menu) for _menu in menus: menu_name = _menu.title() menu = default_menus[menu_name] for action in _menu.actions(): if isinstance(action, QtGui.QAction): menu.addAction(action) elif isinstance(action, QtGui.QMenu): menu.addMenu(action) elif action == '-': menu.addSeparator() def _on_applet_set(self, old, new): if new in self._registered_applets: return self._registered_applets.append(new) applet = plugin_instance('oalab.applet', new) # Add global menus if applet and hasattr(applet, 'menus'): menus = applet.menus() if menus is None: return self._merge_menus(menus) # Add global toolbars if applet and hasattr(applet, 'toolbars'): toolbars = applet.toolbars() if toolbars is None: return for toolbar in toolbars: self.addToolBar(QtCore.Qt.TopToolBarArea, toolbar) def layout(self): return self.splittable._repr_json_()
def _add_item(self, category, obj=None, **kwargs): mode = kwargs.pop('mode', self.MODE_COPY) if obj and isinstance(obj, Data): # TODO: Check obj follow Data or Model interface ?? new_path = self.path / category / obj.path.name if obj.path != new_path and mode == self.MODE_COPY: # TODO: use Data.copy instead return self._add_item(category, path=obj.path, **kwargs) category_dict = getattr(self, category) if obj.filename not in category_dict: category_dict[str(obj.filename)] = obj else: raise ValueError("data '%s' already exists in project '%s'" % (obj.filename, self.alias)) elif obj: category_dict = getattr(self, category) if obj.name not in category_dict: category_dict[str(obj.name)] = obj else: raise ValueError("data '%s' already exists in project '%s'" % (obj.name, self.alias)) else: filename = Path(kwargs.pop('filename')) if 'filename' in kwargs else None content = kwargs.pop('content', None) dtype = kwargs.pop('dtype', None) mimetype = kwargs.pop('mimetype', None) path = Path(kwargs.pop('path')) if 'path' in kwargs else None # If project path exists, ie project exists on disk, # Create category dir if necessary category_path = self.path / category if self.path.exists() and not category_path.exists(): category_path.makedirs() if filename: new_path = self.path / category / filename.name elif path: if not path.exists(): raise ErrorInvalidItem("path '%s' doesn't exists" % path) filename = path.name new_path = self.path / category / filename else: raise ValueError("path or filename required") if path is None: path = new_path # If data was outside project, we try to fix it. # If mode is "prefer copy", we try to copy file inside project # If copy fails, we get original content and pass it to new data # If mode is "prefer link", we just keep original path (keep outside project) # TODO: Move to Data.copy data_obj = None if new_path.abspath() != path.abspath() and mode == self.MODE_COPY: try: path.copyfile(new_path) except IOError: data_obj = DataFactory(path, mimetype, dtype=dtype, default_content=content) content = data_obj.read() else: content = None elif new_path.abspath() != path.abspath() and mode == self.MODE_LINK: new_path = path else: pass # Nothing to do, data is yet in the right place data_obj = DataFactory(new_path, mimetype, dtype=dtype, default_content=content) obj = self._add_item(category, data_obj, **kwargs) obj.package = self return obj
def generate_pyfile_from_uifile(name, src=None, dest=None, uibasename=None, force=None): """ Function searches ... if src is None, search in this order : - <moduledir>/designer/<modulename>.ui - <moduledir>/resources/<modulename>.ui - <moduledir>/<modulename>.ui else : - src File generated is if dest is None : - _<uifilebase>.py (Ex: mywdget.ui -> _mywidget.py) else : - dest .. warning :: To work, this function has to be called in an **imported** module. (__name__ must differ from __main__) else, nothing is done ! Do not edit generated file because all data written here are lost. :param name: :type name: str :return: Qt class (corresponding to filename), Qt type class (type of first value) :rtype: couple """ if force is None: force = FORCE_UI_GENERATION modulename = name if uibasename: name = uibasename else: name = name.split('.')[-1] if modulename == '__main__': return paths = [] if src: filepath = Path(src) paths.append(filepath) else: path = 'designer/%s.ui' % name filepath = Path(get_data(modulename, path)) paths.append(filepath) path = 'resources/%s.ui' % name filepath = Path(get_data(modulename, path)) paths.append(filepath) path = '%s.ui' % name filepath = Path(get_data(modulename, path)) paths.append(filepath) for path in paths: if path.isfile(): break # tmpdir = mkdtempu() if dest is None: pyfilename = path.parent / '_' + path.name.replace('.ui', '.py') else: pyfilename = Path(dest) if not pyfilename.exists(): generate = True else: mtime_py = mtime_datetime(pyfilename) mtime_ui = mtime_datetime(path) if mtime_py > mtime_ui: # If py file is more recent than ui, check user has not changed QT_API with open(pyfilename, 'r') as f: content = f.read() generate = QT_MODULE_NAME not in content else: generate = True if generate or force: module_dir = str(path.parent) if module_dir not in sys.path: sys.path.append(module_dir) if force: print 'build %s from %s\n' % (pyfilename, path) else: print '%s has changed, build %s\n' % (path, pyfilename) pyfile = open(pyfilename, 'w') compileUi(path, pyfile, **compile_args) pyfile.close()
def generate_pyfile_from_uifile(name, src=None, dest=None, uibasename=None, force=None): """ Function searches ... if src is None, search in this order : - <moduledir>/designer/<modulename>.ui - <moduledir>/resources/<modulename>.ui - <moduledir>/<modulename>.ui else : - src File generated is if dest is None : - _<uifilebase>.py (Ex: mywdget.ui -> _mywidget.py) else : - dest .. warning :: To work, this function has to be called in an **imported** module. (__name__ must differ from __main__) else, nothing is done ! Do not edit generated file because all data written here are lost. :param name: :type name: str :return: Qt class (corresponding to filename), Qt type class (type of first value) :rtype: couple """ if force is None: force = FORCE_UI_GENERATION modulename = name if uibasename: name = uibasename else: name = name.split('.')[-1] if modulename == '__main__': return paths = [] if src: filepath = Path(src) paths.append(filepath) else: path = 'designer/%s.ui' % name filepath = Path(get_data(modulename, path)) paths.append(filepath) path = 'resources/%s.ui' % name filepath = Path(get_data(modulename, path)) paths.append(filepath) path = '%s.ui' % name filepath = Path(get_data(modulename, path)) paths.append(filepath) for path in paths: if path.isfile(): break # tmpdir = mkdtempu() if dest is None: pyfilename = path.parent / '_' + path.name.replace('.ui', '.py') else: pyfilename = Path(dest) if not pyfilename.exists(): generate = True else: mtime_py = mtime_datetime(pyfilename) mtime_ui = mtime_datetime(path) if mtime_py > mtime_ui: # If py file is more recent than ui, check user has not changed QT_API with open(pyfilename, 'r') as f: content = f.read() generate = QT_MODULE_NAME not in content else: generate = True if generate or force: module_dir = str(path.parent) if module_dir not in sys.path: sys.path.append(module_dir) if force: print('build %s from %s\n' % (pyfilename, path)) else: print('%s has changed, build %s\n' % (path, pyfilename)) pyfile = open(pyfilename, 'w') compileUi(path, pyfile, **compile_args) pyfile.close()
class Data(object): mimetype = None default_name = 'Data' default_file_name = "filename.ext" pattern = "*.ext" extension = "ext" icon = "Crystal_Clear_app_kcmdf.png" def __init__(self, **kwargs): """ Classical use : *path* exists. Nothing is loaded in memory. Use :meth:`~Data.read` to get content """ # TODO: document args self.path = Path(kwargs.pop('path')) if 'path' in kwargs else None self._filename = Path(kwargs.pop('filename')).name if 'filename' in kwargs else None if self._filename is None and self.path is None: raise ValueError('path or filename required') if self._filename and self.path and self.path.name != self._filename: raise ValueError("path '%s' and filename '%s' are not compatible" % (self.path, self._filename)) self.dtype = kwargs.pop('dtype', None) self._content = kwargs.pop('content', None) self.mimetype = kwargs.pop('mimetype', None) def get_documentation(self): return "No documentation for %s" % self.filename def is_same_data(self, other): if self.exists() and other.exists(): return self.path == other.path elif not self.exists() and not other.exists(): return self._content == other._content else: return False def save(self): if self.path is None: raise ValueError('You must specify a path to be able to save data') if self._content is not None: with open(self.path, 'wb') as f: f.write(self._content) self._content = None def read(self): if self.exists(): with open(self.path, 'rb') as f: return f.read() else: return self._content def rename(self, new): pnew = Path(new) if pnew.isabs() or pnew.name != new: raise ValueError('You must give filename only, not path') new_path = self.path.parent / new self.move(new_path) def move(self, new_path): new_path = Path(new_path) if self._filename is not None: self._filename = new_path.name if self.path.isfile(): self.path.move(new_path) self.path = new_path def exists(self): if self.path: return self.path.exists() else: return False @property def filename(self): if self._filename is None: return self.path.name else: return self._filename @property def name(self): return self.filename @filename.setter def filename(self, value): self._filename = value def _set_content(self, content): self._content = content def _get_content(self): if self._content is None: return self.read() else: return self._content content = property(fget=_get_content, fset=_set_content) code = property()
def _add_item(self, category, obj=None, **kwargs): mode = kwargs.pop('mode', self.MODE_COPY) if obj and isinstance(obj, Data): # TODO: Check obj follow Data or Model interface ?? new_path = self.path / category / obj.path.name if obj.path != new_path and mode == self.MODE_COPY: # TODO: use Data.copy instead return self._add_item(category, path=obj.path, **kwargs) category_dict = getattr(self, category) if obj.filename not in category_dict: category_dict[str(obj.filename)] = obj else: raise ValueError("data '%s' already exists in project '%s'" % (obj.filename, self.alias)) elif obj: category_dict = getattr(self, category) if obj.name not in category_dict: category_dict[str(obj.name)] = obj else: raise ValueError("data '%s' already exists in project '%s'" % (obj.name, self.alias)) else: filename = Path( kwargs.pop('filename')) if 'filename' in kwargs else None content = kwargs.pop('content', None) dtype = kwargs.pop('dtype', None) mimetype = kwargs.pop('mimetype', None) path = Path(kwargs.pop('path')) if 'path' in kwargs else None # If project path exists, ie project exists on disk, # Create category dir if necessary category_path = self.path / category if self.path.exists() and not category_path.exists(): category_path.makedirs() if filename: new_path = self.path / category / filename.name elif path: if not path.exists(): raise ErrorInvalidItem("path '%s' doesn't exists" % path) filename = path.name new_path = self.path / category / filename else: raise ValueError("path or filename required") if path is None: path = new_path # If data was outside project, we try to fix it. # If mode is "prefer copy", we try to copy file inside project # If copy fails, we get original content and pass it to new data # If mode is "prefer link", we just keep original path (keep outside project) # TODO: Move to Data.copy data_obj = None if new_path.abspath() != path.abspath() and mode == self.MODE_COPY: try: path.copyfile(new_path) except IOError: data_obj = DataFactory(path, mimetype, dtype=dtype, default_content=content) content = data_obj.read() else: content = None elif new_path.abspath() != path.abspath( ) and mode == self.MODE_LINK: new_path = path else: pass # Nothing to do, data is yet in the right place data_obj = DataFactory(new_path, mimetype, dtype=dtype, default_content=content) obj = self._add_item(category, data_obj, **kwargs) obj.package = self return obj
class OALabMainWin(QtGui.QMainWindow): appletSet = QtCore.Signal(object, object) DEFAULT_MENU_NAMES = ('File', 'Edit', 'View', 'Help') DEFAULT_LAYOUT = dict( name='default', label='Default Layout', children={}, parents={0: None}, properties={ 0: { 'widget': { 'properties': {'position': 0}, 'applets': [{'name': 'ShellWidget'}] } }} ) DEFAULT_LAYOUT_PATH = 'layout.oaui' LAB = None def __init__(self, layout=None, **kwds): QtGui.QMainWindow.__init__(self) self.autosave = kwds.get('autosave', False) self._lab = kwds.get('lab', None) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.DEFAULT_MENU_NAMES = list(self.DEFAULT_MENU_NAMES) if 'Edit' not in self.DEFAULT_MENU_NAMES: self.DEFAULT_MENU_NAMES.insert(0, 'Edit') if 'File' not in self.DEFAULT_MENU_NAMES: self.DEFAULT_MENU_NAMES.insert(0, 'File') if 'Help' not in self.DEFAULT_MENU_NAMES: self.DEFAULT_MENU_NAMES.append('Help') # Classic menu self._registered_applets = [] self._create_menus() self._create_actions() self._pre_fill_menus() self._splittable_list = [] self.layout_selector = LayoutSelector(parent=self) layout = self._load_layout(layout, **kwds) if isinstance(layout, (list, tuple)): layouts = layout else: layouts = [layout] for layout in layouts: title = layout.get('title', None) if 'children' not in layout: layout = None splittable = OALabSplittableUi(parent=self) splittable.setAttribute(QtCore.Qt.WA_DeleteOnClose) splittable.appletSet.connect(self.appletSet.emit) self.appletSet.connect(self._on_applet_set) if layout is None: container = AppletContainer() splittable.setContentAt(0, container) else: splittable.fromJSON(layout) self._splittable_list.append(splittable) self.layout_selector.add_widget(splittable, title=title) self.setCentralWidget(self.layout_selector) self._post_fill_menus() self.set_edit_mode(False) QtGui.QApplication.instance().focusChanged.connect(self._on_focus_changed) def emit_applet_set(self): self.splittable.emit_applet_set() @property def splittable(self): return self.layout_selector.widget() def _create_menus(self): self.menu_classic = {} menubar = QtGui.QMenuBar() self.setMenuBar(menubar) for menu_name in self.DEFAULT_MENU_NAMES: self.menu_classic[menu_name] = menubar.addMenu(menu_name) def _create_actions(self): self.action_edit = QtGui.QAction("Edit Layout", self.menu_classic['Edit']) self.action_edit.setCheckable(True) self.action_edit.toggled.connect(self.set_edit_mode) self.action_edit.setChecked(False) self.action_about = QtGui.QAction("About", self.menu_classic['Help']) self.action_about.triggered.connect(self.show_about) self.action_plugins = QtGui.QAction("Plugins", self.menu_classic['Help']) self.action_plugins.triggered.connect(self.show_plugins) icon = QtGui.QApplication.style().standardIcon(QtGui.QStyle.SP_TitleBarCloseButton) self.action_quit = QtGui.QAction(icon, "Quit application", self.menu_classic['File']) self.action_quit.triggered.connect(self.close) self.action_quit.setChecked(False) def _pre_fill_menus(self): self.menu_classic['Edit'].addAction(self.action_edit) self.menu_classic['Help'].addAction(self.action_about) self.menu_classic['Help'].addAction(self.action_plugins) def _post_fill_menus(self): self.menu_classic['File'].addSeparator() self.menu_classic['File'].addAction(self.action_quit) def _load_layout(self, layout=None, **kwds): layout_file = kwds.pop('layout_file', self.DEFAULT_LAYOUT_PATH) default_layout = kwds.pop('default_layout', self.DEFAULT_LAYOUT) self.layout_filepath = Path(layout_file).abspath() if layout is None: if self.layout_filepath.exists(): with open(self.layout_filepath) as layout_file: content = layout_file.read() try: layout = json.loads(content) except ValueError: l = eval(content) layout = dict(children=l[0], parents=l[1], properties=l[2]) if layout is None: layout = default_layout return layout def _save_layout(self): if self.autosave: with open(self.layout_filepath, 'w') as layout_file: json.dump(self.layout(), layout_file, sort_keys=True, indent=2) def closeEvent(self, event): close = True # If a lab is used, check if it can be close if hasattr(self._lab, 'readytoclose'): close = self._lab.readytoclose() # If lab is not ready, stop closing if close is False: event.ignore() return # If lab is ready to close, or no lab is used, close widget if self.splittable.close(): if hasattr(self._lab, 'finalize'): self._lab.finalize() self._save_layout() if hasattr(self._lab, 'stop'): self._lab.stop() event.accept() else: event.ignore() def set_edit_mode(self, mode=True): for widget in self.splittable.getAllContents(): if hasattr(widget, 'set_edit_mode'): widget.set_edit_mode(mode) if mode is True and self.LAB: print self.LAB.connections self.splittable.set_edit_mode(mode) def initialize(self): for instance in plugin_instances('oalab.applet'): if hasattr(instance, 'initialize'): instance.initialize() def _widget_actions(self, obj, methodname='toolbar_actions'): actions = None if hasattr(obj, methodname): method_or_list = getattr(obj, methodname) if isinstance(method_or_list, list): actions = method_or_list else: actions = method_or_list() if actions is None: return [] else: return actions def _widget_name(self, obj): if hasattr(obj, 'name'): return obj.name def _on_focus_changed(self, old, new): self.clear_toolbar() if old is new: return # Generally focus is on "leaf" widget on widget hierarchy. # We try to browse all tree to get widget defining actions # For example, if an editor is defined as MyEditor -> Container -> Editor -> QTextEdit # Widget with focus is QTextEdit but widget that define actions is MyEditor # Search stops if widget has no more parents or if widget is AppletContainer parent = new actions = self._widget_actions(parent) name = self._widget_name(parent) while parent is not None: try: parent = parent.parent() except TypeError: break else: if isinstance(parent, AppletContainer): break name = name or self._widget_name(parent) actions += self._widget_actions(parent) if actions: self.fill_toolbar(name, actions) # toolbar creation/destruction set focus to toolbar so we reset it to widget if isinstance(new, QtGui.QWidget): new.setFocus(QtCore.Qt.OtherFocusReason) def fill_toolbar(self, name, actions): menus = plugin_instances('oalab.applet', 'ContextualMenu') for menu in menus: menu.set_actions(name, actions) def clear_toolbar(self): menus = plugin_instances('oalab.applet', 'ContextualMenu') for menu in menus: menu.clear() def _merge_menus(self, menus): parent = self default_menus = self.menu_classic menubar = self.menuBar() for _menu in menus: menu_name = _menu.title() if menu_name in default_menus: menu = default_menus[menu_name] else: menu = QtGui.QMenu(menu_name, parent) default_menus[menu_name] = menu menubar.addMenu(menu) for _menu in menus: menu_name = _menu.title() menu = default_menus[menu_name] for action in _menu.actions(): if isinstance(action, QtGui.QAction): menu.addAction(action) elif isinstance(action, QtGui.QMenu): menu.addMenu(action) elif action == '-': menu.addSeparator() def _on_applet_set(self, old, new): if new in self._registered_applets: return self._registered_applets.append(new) applet = plugin_instance('oalab.applet', new) # Add global menus if applet and hasattr(applet, 'menus'): menus = applet.menus() if menus is None: return self._merge_menus(menus) # Add global toolbars if applet and hasattr(applet, 'toolbars'): toolbars = applet.toolbars() if toolbars is None: return for toolbar in toolbars: self.addToolBar(QtCore.Qt.TopToolBarArea, toolbar) def layout(self): return self.splittable._repr_json_() def show_about(self): about = About() dialog = ModalDialog(about) dialog.resize(400, 600) dialog.setWindowTitle("About OpenAleaLab ...") dialog.exec_() def show_plugins(self): explorer = PluginExplorer() dialog = ModalDialog(explorer) dialog.resize(600, 600) dialog.setWindowTitle("OpenAleaLab plugin's ...") dialog.exec_()
def rename(self, new): pnew = Path(new) if pnew.isabs() or pnew.name != new: raise ValueError('You must give filename only, not path') new_path = self.path.parent / new self.move(new_path)