Esempio n. 1
0
    def close_file(self, event):
        """ Close the file with the given path and remove it from
        the document list. If multiple documents with the same file
        are open this only closes the first one it finds.

        """
        if isinstance(event, ExecutionEvent):
            path = event.parameters.get('path')
        else:
            path = event

        # Default to current document
        if path is None:
            path = self.active_document.name
        docs = self.documents
        opened = [d for d in docs if d.name == path]
        if not opened:
            return
        log.debug("Closing '{}'".format(path))
        doc = opened[0]
        self.documents.remove(doc)

        #: If we removed al of them
        if not self.documents:
            self.documents = self._default_documents()

        #: If we closed the active document
        if self.active_document == doc:
            self.active_document = self.documents[0]
Esempio n. 2
0
    def lineReceived(self, line):
        try:
            line = line.decode()
            log.debug(f"render | out | {line}")
            response = jsonpickle.loads(line)
        except Exception as e:
            response = {}

        #: Special case for startup
        response_id = response.get('id')
        if response_id == 'window_id':
            self.window_id = response['result']
            self.restarts = 0  # Clear the restart count
        elif response_id == 'render_error':
            self.errors = response['error']['message']
        elif response_id == 'render_ok':
            self.errors = ""
        elif response_id == 'capture_output':
            # Script output capture it
            self.output = response['result'].split("\n")
        elif response_id is not None:
            # Lookup the deferred object that should be stored for this id
            # when it is called and invoke the callback or errback based on the
            # result
            d = self._responses.get(response_id)
            if d is not None:
                del self._responses[response_id]
                error = response.get('error')
                if error is not None:
                    d.errback(error)
                else:
                    d.callback(response.get('result'))
        else:
            # Append to output
            self.output.append(line)
Esempio n. 3
0
    def _refresh_settings_pages(self, change=None):
        """ Reload all SettingsPages registered by any Plugins 
        
        Any plugin can add to this list by providing a SettingsPage 
        extension in their PluginManifest.
        
        """
        workbench = self.workbench
        point = workbench.get_extension_point(extensions.SETTINGS_PAGE_POINT)

        settings_pages = []
        typemap = {}
        for extension in sorted(point.extensions, key=lambda ext: ext.rank):
            for d in extension.get_children(extensions.SettingsPage):
                #: Save it
                settings_pages.append(d)

                #: Update the type map
                plugin = self.workbench.get_plugin(d.plugin_id)
                t = type(getattr(plugin, d.model) if d.model else plugin)
                typemap[t] = d.factory()

        #: Update items
        log.debug("Updating settings pages: {}".format(settings_pages))

        self.settings_typemap = typemap
        self.settings_pages = settings_pages
Esempio n. 4
0
    def _refresh_dock_items(self, change=None):
        """ Reload all DockItems registered by any Plugins

        Any plugin can add to this list by providing a DockItem
        extension in their PluginManifest.

        """
        workbench = self.workbench
        point = workbench.get_extension_point(extensions.DOCK_ITEM_POINT)

        #: Layout spec
        layout = {'main': [], 'left': [], 'right': [], 'bottom': [], 'top': []}

        dock_items = []
        for extension in sorted(point.extensions, key=lambda ext: ext.rank):
            for declaration in extension.get_children(extensions.DockItem):
                # Create the item
                DockItem = declaration.factory()
                plugin = workbench.get_plugin(declaration.plugin_id)
                item = DockItem(plugin=plugin)

                # Add to our layout
                layout[declaration.layout].append(item.name)

                # Save it
                dock_items.append(item)

        # Update items
        log.debug("Updating dock items: {}".format(dock_items))
        self.dock_items = dock_items
        self._refresh_layout(layout)
Esempio n. 5
0
 def errReceived(self, data):
     if b'XCB error' in data:
         return
     for line in data.split(b"\n"):
         try:
             log.debug(f"render | err | {line.encode()}")
         except:
             pass
Esempio n. 6
0
 def _default_source(self):
     """ Load the document from the path given by `name`.
     If it fails to load, nothing will be returned and an error
     will be set.
     """
     try:
         log.debug("Loading '{}' from disk.".format(self.name))
         with open(self.name) as f:
             return f.read()
     except Exception as e:
         self.errors = [str(e)]
     return ""
Esempio n. 7
0
    def restart(self):
        self.window_id = 0
        self.restarts += 1

        # TODO: 100 is probably excessive
        if self.restarts > self.max_retries:
            plugin = self.plugin
            plugin.workbench.message_critical(
                "Viewer failed to start",
                "Could not get the viewer to start after several attempts.")

            raise RuntimeError(
                "renderer | Failed to successfully start renderer aborting!")

        log.debug(f"Attempting to restart viewer {self.process}")
        deferred_call(self.start)
Esempio n. 8
0
    def err_received(self, data):
        """ Catch and log error output attempting to decode it

        """
        for line in data.split(b"\n"):
            if not line:
                continue
            if line.startswith(b"QWidget::") or line.startswith(b"QPainter::"):
                continue
            try:
                line = line.decode()
                log.debug(f"render | err | {line}")
                if self.document:
                    self.document.errors.append(line)
            except Exception as e:
                log.debug(f"render | err | {line}")
Esempio n. 9
0
    def send_message(self, method, *args, **kwargs):
        # Defer until it's ready
        if not self.transport:
            log.debug('renderer | message not ready deferring')
            timed_call(0, self.send_message, method, *args, **kwargs)
            return
        _id = kwargs.pop('_id')

        request = {
            'jsonrpc': '2.0',
            'method': method,
            'params': args or kwargs
        }
        if _id is not None:
            request['id'] = _id

        log.debug(f'renderer | sent | {request}')
        self.transport.write(jsonpickle.dumps(request).encode() + b'\r\n')
Esempio n. 10
0
    def _refresh_settings_pages(self, change=None):
        """ Reload all SettingsPages registered by any Plugins

        Any plugin can add to this list by providing a SettingsPage
        extension in their PluginManifest.

        """
        workbench = self.workbench
        point = workbench.get_extension_point(extensions.SETTINGS_PAGE_POINT)

        settings_pages = []
        for extension in sorted(point.extensions, key=lambda ext: ext.rank):
            for d in extension.get_children(extensions.SettingsPage):
                settings_pages.append(d)

        #: Update items
        settings_pages.sort(key=lambda p: p.name)
        log.debug("Updating settings pages: {}".format(settings_pages))
        self.settings_pages = settings_pages
Esempio n. 11
0
    def export(self, event):
        """ Export the current model to stl """
        options = event.parameters.get('options')
        if not options:
            raise ValueError("An export `options` parameter is required")

        # Pickle the configured exporter and send it over
        cmd = [sys.executable]
        if not sys.executable.endswith('declaracad'):
            cmd.extend(['-m', 'declaracad'])

        data = jsonpickle.dumps(options)
        assert data != 'null', f"Exporter failed to serialize: {options}"
        cmd.extend(['export', data])
        log.debug(" ".join(cmd))
        protocol = ProcessLineReceiver()
        loop = asyncio.get_event_loop()
        deferred_call(loop.subprocess_exec, lambda: protocol, *cmd)
        return protocol
Esempio n. 12
0
    def send_message(self, method, *args, **kwargs):
        # Defer until it's ready
        if not self.transport or not self.window_id:
            #log.debug('renderer | message not ready deferring')
            timed_call(1000, self.send_message, method, *args, **kwargs)
            return
        _id = kwargs.pop('_id')
        _silent = kwargs.pop('_silent', False)

        request = {
            'jsonrpc': '2.0',
            'method': method,
            'params': args or kwargs
        }
        if _id is not None:
            request['id'] = _id
        if not _silent:
            log.debug(f'renderer | sent | {request}')
        encoded_msg = jsonpickle.dumps(request).encode() + b'\r\n'
        deferred_call(self.transport.write, encoded_msg)
Esempio n. 13
0
    def open_file(self, event):
        """ Open a file from the local filesystem

        """
        path = event.parameters['path']

        #: Check if the document is already open
        for doc in self.documents:
            if doc.name == path:
                self.active_document = doc
                return
        log.debug("Opening '{}'".format(path))

        #: Otherwise open it
        doc = Document(name=path, unsaved=False)
        with open(path) as f:
            doc.source = f.read()
        self.documents.append(doc)
        self.active_document = doc
        editor = self.get_editor()
        if editor:
            editor.set_text(doc.source)
Esempio n. 14
0
    def export(self, event):
        """ Export the current model to stl """
        from twisted.internet import reactor
        options = event.parameters.get('options')
        if not options:
            raise ValueError("An export `options` parameter is required")

        # Pickle the configured exporter and send it over
        cmd = [sys.executable]
        if not sys.executable.endswith('declaracad'):
            cmd.extend(['-m', 'declaracad'])

        data = jsonpickle.dumps(options)
        assert data != 'null', f"Exporter failed to serialize: {options}"
        cmd.extend(['export', data])

        log.debug(" ".join(cmd))
        protocol = ProcessLineReceiver()
        reactor.spawnProcess(protocol,
                             sys.executable,
                             args=cmd,
                             env=os.environ)
        return protocol
Esempio n. 15
0
    def close_file(self, event):
        """ Close the file with the given path and remove it from
        the document list. If multiple documents with the same file
        are open this only closes the first one it finds.

        """
        if isinstance(event, ExecutionEvent):
            path = event.parameters.get('path')
        else:
            path = event

        # Default to current document
        if path is None:
            path = self.active_document.name
        docs = self.documents
        opened = [d for d in docs if d.name == path]
        if not opened:
            return
        log.debug("Closing '%s'", path)
        doc = opened[0]
        self.documents.remove(doc)

        # If any viewer was bound to this document, unbind it
        viewer_plugin = self.workbench.get_plugin('declaracad.viewer')
        for viewer in viewer_plugin.get_viewers():
            if viewer.document == doc:
                viewer.document = None

        # If we removed all of them create a new empty one
        if not self.documents:
            self.documents = self._default_documents()
            self.active_document = self.documents[0]

        # If we closed the active document
        elif self.active_document == doc:
            self.active_document = self.documents[0]
Esempio n. 16
0
 def _observe_settings_page(self, change):
     log.debug("Settings page: {}".format(change))
Esempio n. 17
0
    def data_received(self, data):
        line = data.decode()
        try:
            response = jsonpickle.loads(line)
            # log.debug(f"viewer | resp | {response}")
        except Exception as e:
            log.debug(f"viewer | out | {line.rstrip()}")
            response = {}

        doc = self.document

        if not isinstance(response, dict):
            log.debug(f"viewer | out | {response.rstrip()}")
            return

        #: Special case for startup
        response_id = response.get('id')
        if response_id == 'window_id':
            self.window_id = response['result']
            self.restarts = 0  # Clear the restart count
            return
        elif response_id == 'keep_alive':
            return
        elif response_id == 'invoke_command':
            command_id = response.get('command_id')
            parameters = response.get('parameters', {})
            log.debug(f"viewer | out | {command_id}({parameters})")
            self.plugin.workbench.invoke_command(command_id, parameters)
        elif response_id == 'render_error':
            if doc:
                doc.errors.extend(response['error']['message'].split("\n"))
            return
        elif response_id == 'render_success':
            if doc:
                doc.errors = []
            return
        elif response_id == 'capture_output':
            # Script output capture it
            if doc:
                doc.output = response['result'].split("\n")
            return
        elif response_id == 'shape_selection':
            #: TODO: Do something with this?
            if doc:
                doc.output.append(str(response['result']))
            return
        elif response_id is not None:
            # Lookup the deferred object that should be stored for this id
            # when it is called and invoke the callback or errback based on the
            # result
            d = self._responses.get(response_id)
            if d is not None:
                del self._responses[response_id]
                try:
                    error = response.get('error')
                    if error is not None:
                        if doc:
                            doc.errors.extend(
                                error.get('message', '').split("\n"))
                        d.add_done_callback(error)
                    else:
                        d.add_done_callback(response.get('result'))
                    return
                except Exception as e:
                    log.warning("RPC response not properly handled!")
                    log.exception(e)

            else:
                log.warning("Got unexpected reply")
            # else we got a response from something else, possibly an error?
        if 'error' in response and doc:
            doc.errors.extend(response['error'].get('message', '').split("\n"))
            doc.output.append(line)
        elif 'message' in response and doc:
            doc.output.extend(response['message'].split("\n"))
        elif doc:
            # Append to output
            doc.output.extend(line.split("\n"))
Esempio n. 18
0
    def _update_area_layout(self, change):
        """ When a document is opened or closed, add or remove it
        from the currently active TabLayout.
        
        The layout update is deferred so it fires after the items are
        updated by the Looper.
        
        """
        if change['type'] == 'create':
            return

        #: Get the dock area
        area = self.get_dock_area()

        #: Refresh the dock items
        #area.looper.iterable = self.documents[:]

        #: Determine what change to apply
        removed = set()
        added = set()
        if change['type'] == 'container':
            op = change['operation']
            if op in ['append', 'insert']:
                added = set([change['item']])
            elif op == 'extend':
                added = set(change['items'])
            elif op in ['pop', 'remove']:
                removed = set([change['item']])
        elif change['type'] == 'update':
            old = set(change['oldvalue'])
            new = set(change['value'])

            #: Determine which changed
            removed = old.difference(new)
            added = new.difference(old)
        elif change['type'] == 'manual':
            old = set([self.active_document])
            new = set(self.documents)
            #: Determine which changed
            removed = old.difference(new)
            added = new.difference(old)

        #: Update operations to apply
        ops = []

        #: Remove any old items
        for doc in removed:
            ops.append(RemoveItem(item='editor-item-{}'.format(doc.name)))

        #: Add any new items
        for doc in added:
            log.debug("Adding: editor-item-{}".format(doc.name))
            targets = [
                'editor-item-{}'.format(d.name) for d in self.documents
                if d.name != doc.name
            ]
            item = EditorDockItem(area, plugin=self, doc=doc)
            ops.append(
                InsertTab(item=item.name,
                          target=targets[0] if targets else ''))

        #: Now apply all layout update operations
        area.update_layout(ops)
        self.save_dock_area(change)
Esempio n. 19
0
    def _update_area_layout(self, change):
        """ When a document is opened or closed, add or remove it
        from the currently active TabLayout.

        The layout update is deferred so it fires after the items are
        updated by the Looper.

        """
        if change['type'] == 'create':
            return

        #: Get the dock area
        area = self.get_dock_area()

        #: Refresh the dock items
        #area.looper.iterable = self.documents[:]

        #: Determine what change to apply
        removed = set()
        added = set()
        if change['type'] == 'container':
            op = change['operation']
            if op in ['append', 'insert']:
                added = set([change['item']])
            elif op == 'extend':
                added = set(change['items'])
            elif op in ['pop', 'remove']:
                removed = set([change['item']])
        elif change['type'] == 'update':
            old = set(change['oldvalue'])
            new = set(change['value'])

            #: Determine which changed
            removed = old.difference(new)
            added = new.difference(old)
        elif change['type'] == 'load':
            removed = {item.doc for item in self.get_editor_items()}
            added = set(self.documents)

        #: Update operations to apply
        ops = []
        removed_targets = set()

        #: Remove any old items
        for doc in removed:
            for item in self.get_editor_items():
                if item.doc == doc:
                    removed_targets.add(item.name)
                    ops.append(RemoveItem(item=item.name))

        # Remove ops
        if ops:
            log.debug(ops)
            area.update_layout(ops)

        # Add each one at a time
        targets = set([
            item.name for item in area.dock_items()
            if (item.name.startswith("editor-item")
                and item.name not in removed_targets)
        ])

        log.debug("Editor added=%s removed=%s targets=%s", added, removed,
                  targets)

        # Sort documents so active is last so it's on top when we restore
        # from a previous state
        for doc in sorted(added, key=lambda d: int(d == self.active_document)):
            item = create_editor_item(area, plugin=self, doc=doc)
            if targets:
                op = InsertTab(item=item.name, target=list(targets)[-1])
                try:
                    area.update_layout(op)
                except Exception as e:
                    # If it fails to add as a tab just insert it
                    log.exception(e)
                    op = InsertItem(item=item.name)
                    area.update_layout(op)
            else:
                op = InsertItem(item=item.name)
                area.update_layout(op)
            targets.add(item.name)

        # Now save it
        self.save_dock_area(change)