Esempio n. 1
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = False
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    asyncCallsStateChange = Signal()
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        # 'current_context' MUST be the first thing assigned to a widget
        self.current_context = settings.Context()
        if self.settingsHandler:
            stored_settings = kwargs.get("stored_settings", None)
            self.settingsHandler.initialize(self, stored_settings)

        # number of control signals that are currently being processed
        # needed by signalWrapper to know when everything was sent
        self.needProcessing = 0  # used by signalManager
        self.signalManager = kwargs.get("signal_manager", None)

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self._guiElements = []  # used for automatic widget debugging
        self.__reportData = None

        # TODO: position used to be saved like this. Reimplement.
        # if save_position:
        #    self.settingsList = getattr(self, "settingsList", []) + \
        #                        ["widgetShown", "savedWidgetGeometry"]

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        # TODO: kill me
        self.__dict__.update(environ.directories)

        if self.name:
            self.setCaption(self.name.replace("&", ""))
        self.setFocusPolicy(Qt.StrongFocus)

        self.wrappers = []  # stored wrappers for widget events
        self.linksIn = {}  # signalName : (dirty, widFrom, handler, signalData)
        self.linksOut = {}  # signalName: (signalData, id)
        self.connections = {}  # keys are (control, signal) and values are
        # wrapper instances. Used in connect/disconnect
        self.callbackDeposit = []
        self.startTime = time.time()  # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return Qt.Window if cls.resizing_enabled else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint

    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.topWidgetPart = gui.widgetBox(self, orientation="horizontal", margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart, orientation="vertical", margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(
                self.topWidgetPart,
                orientation="vertical",
                sizePolicy=QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding),
                margin=0,
            )
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart, orientation="vertical", margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart, orientation="horizontal", margin=4)
            self.graphButton = gui.button(graphButtonBackground, self, "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea, os.path.join(self.widgetDir, "icons/triangle-orange.png")
            )
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea, os.path.join(self.widgetDir + "icons/triangle-red.png")
            )

    # status bar handler functions
    def setState(self, stateType, id, text):
        stateChanged = super().setState(stateType, id, text)
        if not stateChanged or not hasattr(self, "widgetStatusArea"):
            return

        iconsShown = 0
        warnings = [("Warning", self._warningWidget, self._owWarning), ("Error", self._errorWidget, self._owError)]
        for state, widget, use in warnings:
            if not widget:
                continue
            if use and self.widgetState[state]:
                widget.setToolTip("\n".join(self.widgetState[state].values()))
                widget.show()
                iconsShown = 1
            else:
                widget.setToolTip("")
                widget.hide()

        if iconsShown:
            self.statusBarIconArea.show()
        else:
            self.statusBarIconArea.hide()

        if (stateType == "Warning" and self._owWarning) or (stateType == "Error" and self._owError):
            if text:
                self.setStatusBarText(stateType + ": " + text)
            else:
                self.setStatusBarText("")
        self.updateStatusBarState()

    def updateWidgetStateInfo(self, stateType, id, text):
        html = self.widgetStateToHtml(self._owInfo, self._owWarning, self._owError)
        if html:
            self.widgetStateInfoBox.show()
            self.widgetStateInfo.setText(html)
            self.widgetStateInfo.setToolTip(html)
        else:
            if not self.widgetStateInfoBox.isVisible():
                dHeight = -self.widgetStateInfoBox.height()
            else:
                dHeight = 0
            self.widgetStateInfoBox.hide()
            self.widgetStateInfo.setText("")
            self.widgetStateInfo.setToolTip("")
            width, height = self.width(), self.height() + dHeight
            self.resize(width, height)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass

    def getIconNames(self, iconName):
        # if canvas sent us a prepared list of valid names, just return those
        if type(iconName) == list:
            return iconName

        names = []
        name, ext = os.path.splitext(iconName)
        for num in [16, 32, 42, 60]:
            names.append("%s_%d%s" % (name, num, ext))
        fullPaths = []
        module_dir = os.path.dirname(sys.modules[self.__module__].__file__)
        for paths in [(self.widgetDir, name), (self.widgetDir, "icons", name), (module_dir, "icons", name)]:
            for name in names + [iconName]:
                fname = os.path.join(*paths)
                if os.path.exists(fname):
                    fullPaths.append(fname)
            if fullPaths != []:
                break

        if len(fullPaths) > 1 and fullPaths[-1].endswith(iconName):
            # if we have the new icons we can remove the default icon
            fullPaths.pop()
        return fullPaths

    def setWidgetIcon(self, iconName):
        iconNames = self.getIconNames(iconName)
        icon = QIcon()
        for name in iconNames:
            pix = QPixmap(name)
            icon.addPixmap(pix)

        self.setWindowIcon(icon)

    # ##############################################
    def isDataWithClass(self, data, wantedVarType=None, checkMissing=False):
        self.error([1234, 1235, 1236])
        if not data:
            return 0
        if not data.domain.classVar:
            self.error(1234, "A data set with a class attribute is required.")
            return 0
        if wantedVarType and data.domain.classVar.varType != wantedVarType:
            self.error(1235, "Unable to handle %s class." % str(data.domain.class_var.var_type).lower())
            return 0
        if checkMissing and not orange.Preprocessor_dropMissingClasses(data):
            self.error(1236, "Unable to handle data set with no known classes")
            return 0
        return 1

    # call processEvents(), but first remember position and size of widget in
    # case one of the events would be move or resize
    # call this function if needed in __init__ of the widget
    def safeProcessEvents(self):
        keys = ["widgetShown"]
        vals = [(key, getattr(self, key, None)) for key in keys]
        qApp.processEvents()
        for (key, val) in vals:
            if val != None:
                setattr(self, key, val)

    # this function is called at the end of the widget's __init__ when the
    # widgets is saving its position and size parameters
    def restoreWidgetPosition(self):
        if self.save_position:
            geometry = getattr(self, "savedWidgetGeometry", None)
            restored = False
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored:
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                # Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)

    # this is called in canvas when loading a schema. it opens the widgets
    # that were shown when saving the schema
    def restoreWidgetStatus(self):
        if self.save_position and getattr(self, "widgetShown", None):
            self.show()

    # when widget is resized, save new width and height into widgetWidth and
    # widgetHeight. some widgets can put this two variables into settings and
    # last widget shape is restored after restart
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives the resizeEvent before showEvent and we must not
        # overwrite the the savedGeometry before then)
        if self.save_position and self.isVisible():
            self.savedWidgetGeometry = str(self.saveGeometry())

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.widgetShown = 0
            self.savedWidgetGeometry = str(self.saveGeometry())
        QDialog.hideEvent(self, ev)

    # set widget state to shown
    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position:
            self.widgetShown = 1
        self.restoreWidgetPosition()

    def closeEvent(self, ev):
        if self.save_position:
            self.savedWidgetGeometry = str(self.saveGeometry())
        QDialog.closeEvent(self, ev)

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        if self.parent != None and isinstance(self.parent, QTabWidget):
            self.parent.setTabText(self.parent.indexOf(self), caption)
        else:
            # we have to save caption title in case progressbar will change it
            self.captionTitle = str(caption)
            self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        if self.current_context is not None:
            self.settingsHandler.close_context(self)
        self.current_context = None

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    # this function is only intended for derived classes to send appropriate
    # signals when all settings are loaded
    def activateLoadedSettings(self):
        pass

    # reimplemented in other widgets
    def onDeleteWidget(self):
        pass

    def setOptions(self):
        pass

    def handleNewSignals(self):
        # this is called after all new signals have been handled
        # implement this in your widget if you want to process something only
        # after you received multiple signals
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self):
        self.progressBarValue = 0
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")
        self.processingStateChanged.emit(1)

    def progressBarSet(self, value):
        if value > 0:
            self.__progressBarValue = value
            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle + " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        self.progressBarValueChanged.emit(value)

        qApp.processEvents()

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet, fget=progressBarValue)

    def progressBarAdvance(self, value):
        self.progressBarSet(self.progressBarValue + value)

    def progressBarFinished(self):
        self.setWindowTitle(self.captionTitle)
        self.processingStateChanged.emit(0)

    def openWidgetHelp(self):
        if "widgetInfo" in self.__dict__:  # This widget is on a canvas.
            qApp.canvasDlg.helpWindow.showHelpFor(self.widgetInfo, True)

    def keyPressEvent(self, e):
        if e.key() in (Qt.Key_Help, Qt.Key_F1):
            self.openWidgetHelp()
        #    e.ignore()
        elif (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)
        # self.setState("Warning", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, stateType, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[stateType]:
                    self.widgetState[stateType].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[stateType]:
                    self.widgetState[stateType].pop(id)
                    changed = 1
            else:
                self.widgetState[stateType][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(stateType, i, "")
            else:
                self.widgetStateChanged.emit(stateType, id, text or "")
        return changed

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        pixmaps = self.getWidgetStateIcons()
        items = []
        iconPath = {
            "Info": "canvasIcons:information.png",
            "Warning": "canvasIcons:warning.png",
            "Error": "canvasIcons:error.png",
        }
        for show, what in [(info, "Info"), (warning, "Warning"), (error, "Error")]:
            if show and self.widgetState[what]:
                items.append(
                    '<img src="%s" style="float: left;"> %s'
                    % (iconPath[what], "\n".join(self.widgetState[what].values()))
                )
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            iconsDir = os.path.join(environ.canvas_install_dir, "icons")
            QDir.addSearchPath("canvasIcons", os.path.join(environ.canvas_install_dir, "icons/"))
            info = QPixmap("canvasIcons:information.png")
            warning = QPixmap("canvasIcons:warning.png")
            error = QPixmap("canvasIcons:error.png")
            cls._cached__widget_state_icons = {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M): lambda self: self.showMaximized
            if self.isMinimized()
            else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W): lambda self: self.setVisible(not self.isVisible()),
        }

    def setBlocking(self, state=True):
        """ Set blocking flag for this widget. While this flag is set this
        widget and all its descendants will not receive any new signals from
        the signal manager
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """ Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)
Esempio n. 2
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = True
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    asyncCallsStateChange = Signal()
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)

        setattr(self, gui.CONTROLLED_ATTRIBUTES,
                ControlledAttributesDict(self))
        self._guiElements = []  # used for automatic widget debugging
        self.__reportData = None

        # TODO: position used to be saved like this. Reimplement.
        #if save_position:
        #    self.settingsList = getattr(self, "settingsList", []) + \
        #                        ["widgetShown", "savedWidgetGeometry"]

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()  # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False
        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled else Qt.Dialog
                | Qt.MSWindowsFixedSizeDialogHint)

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self,
                                         orientation="horizontal",
                                         margin=0,
                                         spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored,
                                       QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.topWidgetPart = gui.widgetBox(self,
                                           orientation="horizontal",
                                           margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart,
                                            orientation="vertical",
                                            margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(self.topWidgetPart,
                                          orientation="vertical",
                                          sizePolicy=QSizePolicy(
                                              QSizePolicy.Expanding,
                                              QSizePolicy.Expanding),
                                          margin=0)
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart,
                                             orientation="vertical",
                                             margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart,
                                                  orientation="horizontal",
                                                  margin=4)
            self.graphButton = gui.button(graphButtonBackground, self,
                                          "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-red.png"))

    # status bar handler functions
    def setState(self, stateType, id, text):
        stateChanged = super().setState(stateType, id, text)
        if not stateChanged or not hasattr(self, "widgetStatusArea"):
            return

        iconsShown = 0
        warnings = [("Warning", self._warningWidget, self._owWarning),
                    ("Error", self._errorWidget, self._owError)]
        for state, widget, use in warnings:
            if not widget:
                continue
            if use and self.widgetState[state]:
                widget.setToolTip("\n".join(self.widgetState[state].values()))
                widget.show()
                iconsShown = 1
            else:
                widget.setToolTip("")
                widget.hide()

        if iconsShown:
            self.statusBarIconArea.show()
        else:
            self.statusBarIconArea.hide()

        if (stateType == "Warning" and self._owWarning) or \
                (stateType == "Error" and self._owError):
            if text:
                self.setStatusBarText(stateType + ": " + text)
            else:
                self.setStatusBarText("")
        self.updateStatusBarState()

    def updateWidgetStateInfo(self, stateType, id, text):
        html = self.widgetStateToHtml(self._owInfo, self._owWarning,
                                      self._owError)
        if html:
            self.widgetStateInfoBox.show()
            self.widgetStateInfo.setText(html)
            self.widgetStateInfo.setToolTip(html)
        else:
            if not self.widgetStateInfoBox.isVisible():
                dHeight = -self.widgetStateInfoBox.height()
            else:
                dHeight = 0
            self.widgetStateInfoBox.hide()
            self.widgetStateInfo.setText("")
            self.widgetStateInfo.setToolTip("")
            width, height = self.width(), self.height() + dHeight
            self.resize(width, height)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass

    # ##############################################
    """
    def isDataWithClass(self, data, wantedVarType=None, checkMissing=False):
        self.error([1234, 1235, 1236])
        if not data:
            return 0
        if not data.domain.classVar:
            self.error(1234, "A data set with a class attribute is required.")
            return 0
        if wantedVarType and data.domain.classVar.varType != wantedVarType:
            self.error(1235, "Unable to handle %s class." %
                             str(data.domain.class_var.var_type).lower())
            return 0
        if checkMissing and not orange.Preprocessor_dropMissingClasses(data):
            self.error(1236, "Unable to handle data set with no known classes")
            return 0
        return 1
    """

    def restoreWidgetPosition(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored:
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored:
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before showEvent and we must not overwrite the the savedGeometry
        # with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position:
            # Restore saved geometry on show
            self.restoreWidgetPosition()
        self.__was_restored = True

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if not any(s.name == signalName for s in self.outputs):
            raise ValueError(
                '{} is not a valid output signal for widget {}'.format(
                    signalName, self.name))
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    # this function is only intended for derived classes to send appropriate
    # signals when all settings are loaded
    def activate_loaded_settings(self):
        pass

    # reimplemented in other widgets
    def onDeleteWidget(self):
        pass

    def handleNewSignals(self):
        # this is called after all new signals have been handled
        # implement this in your widget if you want to process something only
        # after you received multiple signals
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self, processEvents=QEventLoop.AllEvents):
        """
        Initialize the widget's progress (i.e show and set progress to 0%).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarSet(0, processEvents)

    def progressBarSet(self, value, processEvents=QEventLoop.AllEvents):
        """
        Set the current progress bar to `value`.

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param float value: Progress value
        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn(
                    "progressBarSet() called without a "
                    "preceding progressBarInit()",
                    stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(
                self.captionTitle +
                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        if old != value:
            self.progressBarValueChanged.emit(value)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float,
                                    fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents):
        self.progressBarSet(self.progressBarValue + value, processEvents)

    def progressBarFinished(self, processEvents=QEventLoop.AllEvents):
        """
        Stop the widget's progress (i.e hide the progress bar).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(highest_type,
                                     msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(highest_type, tooltip_lines[0],
                                     tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {
            "Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
            "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
            "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)
        }
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".format(
                background, foreground))
        self.warning_label.setText(text)
        self.warning_bar.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        pixmaps = self.getWidgetStateIcons()
        items = []
        iconPath = {
            "Info": "canvasIcons:information.png",
            "Warning": "canvasIcons:warning.png",
            "Error": "canvasIcons:error.png"
        }
        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconPath[what], "\n".join(
                                 self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            iconsDir = os.path.join(environ.canvas_install_dir, "icons")
            QDir.addSearchPath(
                "canvasIcons",
                os.path.join(environ.canvas_install_dir, "icons/"))
            info = QPixmap("canvasIcons:information.png")
            warning = QPixmap("canvasIcons:warning.png")
            error = QPixmap("canvasIcons:error.png")
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
            lambda self: self.showMaximized
            if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
            lambda self: self.setVisible(not self.isVisible())
        }

    def setBlocking(self, state=True):
        """ Set blocking flag for this widget. While this flag is set this
        widget and all its descendants will not receive any new signals from
        the signal manager
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """ Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)
Esempio n. 3
0
class ResultWindow(QMainWindow):
    def __init__(self, app):
        QMainWindow.__init__(self, None)
        self.app = app
        self._setupUi()
        self.resultsModel = app.RESULT_MODEL_CLASS(self.app, self.resultsView)
        self.stats = StatsLabel(app.model.stats_label, self.statusLabel)
        self._update_column_actions_status()
        
        self.menuColumns.triggered[QAction].connect(self.columnToggled)
        self.resultsView.doubleClicked.connect(self.resultsDoubleClicked)
        self.resultsView.spacePressed.connect(self.resultsSpacePressed)
        self.detailsButton.clicked.connect(self.actionDetails.triggered)
        self.dupesOnlyCheckBox.stateChanged.connect(self.powerMarkerTriggered)
        self.deltaValuesCheckBox.stateChanged.connect(self.deltaTriggered)
        self.searchEdit.searchChanged.connect(self.searchChanged)
        self.app.willSavePrefs.connect(self.appWillSavePrefs)
    
    def _setupActions(self):
        # (name, shortcut, icon, desc, func)
        ACTIONS = [
            ('actionDetails', 'Ctrl+I', '', tr("Details"), self.detailsTriggered),
            ('actionActions', '', '', tr("Actions"), self.actionsTriggered),
            ('actionPowerMarker', 'Ctrl+1', '', tr("Show Dupes Only"), self.powerMarkerTriggered),
            ('actionDelta', 'Ctrl+2', '', tr("Show Delta Values"), self.deltaTriggered),
            ('actionDeleteMarked', 'Ctrl+D', '', tr("Send Marked to Recycle Bin..."), self.deleteTriggered),
            ('actionMoveMarked', 'Ctrl+M', '', tr("Move Marked to..."), self.moveTriggered),
            ('actionCopyMarked', 'Ctrl+Shift+M', '', tr("Copy Marked to..."), self.copyTriggered),
            ('actionRemoveMarked', 'Ctrl+R', '', tr("Remove Marked from Results"), self.removeMarkedTriggered),
            ('actionReprioritize', '', '', tr("Re-Prioritize Results..."), self.reprioritizeTriggered),
            ('actionRemoveSelected', 'Ctrl+Del', '', tr("Remove Selected from Results"), self.removeSelectedTriggered),
            ('actionIgnoreSelected', 'Ctrl+Shift+Del', '', tr("Add Selected to Ignore List"), self.addToIgnoreListTriggered),
            ('actionMakeSelectedReference', 'Ctrl+Space', '', tr("Make Selected into Reference"), self.app.model.make_selected_reference),
            ('actionOpenSelected', 'Ctrl+O', '', tr("Open Selected with Default Application"), self.openTriggered),
            ('actionRevealSelected', 'Ctrl+Shift+O', '', tr("Open Containing Folder of Selected"), self.revealTriggered),
            ('actionRenameSelected', 'F2', '', tr("Rename Selected"), self.renameTriggered),
            ('actionMarkAll', 'Ctrl+A', '', tr("Mark All"), self.markAllTriggered),
            ('actionMarkNone', 'Ctrl+Shift+A', '', tr("Mark None"), self.markNoneTriggered),
            ('actionInvertMarking', 'Ctrl+Alt+A', '', tr("Invert Marking"), self.markInvertTriggered),
            ('actionMarkSelected', '', '', tr("Mark Selected"), self.markSelectedTriggered),
            ('actionExportToHTML', '', '', tr("Export To HTML"), self.app.model.export_to_xhtml),
            ('actionExportToCSV', '', '', tr("Export To CSV"), self.app.model.export_to_csv),
            ('actionSaveResults', 'Ctrl+S', '', tr("Save Results..."), self.saveResultsTriggered),
            ('actionInvokeCustomCommand', 'Ctrl+Alt+I', '', tr("Invoke Custom Command"), self.app.invokeCustomCommand),
        ]
        createActions(ACTIONS, self)
        self.actionDelta.setCheckable(True)
        self.actionPowerMarker.setCheckable(True)
        
    def _setupMenu(self):
        self.menubar = QMenuBar(self)
        self.menubar.setGeometry(QRect(0, 0, 630, 22))
        self.menuFile = QMenu(self.menubar)
        self.menuFile.setTitle(tr("File"))
        self.menuMark = QMenu(self.menubar)
        self.menuMark.setTitle(tr("Mark"))
        self.menuActions = QMenu(self.menubar)
        self.menuActions.setTitle(tr("Actions"))
        self.menuColumns = QMenu(self.menubar)
        self.menuColumns.setTitle(tr("Columns"))
        self.menuView = QMenu(self.menubar)
        self.menuView.setTitle(tr("View"))
        self.menuHelp = QMenu(self.menubar)
        self.menuHelp.setTitle(tr("Help"))
        self.setMenuBar(self.menubar)
        
        self.menuActions.addAction(self.actionDeleteMarked)
        self.menuActions.addAction(self.actionMoveMarked)
        self.menuActions.addAction(self.actionCopyMarked)
        self.menuActions.addAction(self.actionRemoveMarked)
        self.menuActions.addAction(self.actionReprioritize)
        self.menuActions.addSeparator()
        self.menuActions.addAction(self.actionRemoveSelected)
        self.menuActions.addAction(self.actionIgnoreSelected)
        self.menuActions.addAction(self.actionMakeSelectedReference)
        self.menuActions.addSeparator()
        self.menuActions.addAction(self.actionOpenSelected)
        self.menuActions.addAction(self.actionRevealSelected)
        self.menuActions.addAction(self.actionInvokeCustomCommand)
        self.menuActions.addAction(self.actionRenameSelected)
        self.menuMark.addAction(self.actionMarkAll)
        self.menuMark.addAction(self.actionMarkNone)
        self.menuMark.addAction(self.actionInvertMarking)
        self.menuMark.addAction(self.actionMarkSelected)
        self.menuView.addAction(self.actionPowerMarker)
        self.menuView.addAction(self.actionDelta)
        self.menuView.addSeparator()
        self.menuView.addAction(self.actionDetails)
        self.menuView.addAction(self.app.actionIgnoreList)
        self.menuView.addAction(self.app.actionPreferences)
        self.menuHelp.addAction(self.app.actionShowHelp)
        self.menuHelp.addAction(self.app.actionCheckForUpdate)
        self.menuHelp.addAction(self.app.actionOpenDebugLog)
        self.menuHelp.addAction(self.app.actionAbout)
        self.menuFile.addAction(self.actionSaveResults)
        self.menuFile.addAction(self.actionExportToHTML)
        self.menuFile.addAction(self.actionExportToCSV)
        self.menuFile.addSeparator()
        self.menuFile.addAction(self.app.actionQuit)
        
        self.menubar.addAction(self.menuFile.menuAction())
        self.menubar.addAction(self.menuMark.menuAction())
        self.menubar.addAction(self.menuActions.menuAction())
        self.menubar.addAction(self.menuColumns.menuAction())
        self.menubar.addAction(self.menuView.menuAction())
        self.menubar.addAction(self.menuHelp.menuAction())
        
        # Columns menu
        menu = self.menuColumns
        self._column_actions = []
        for index, (display, visible) in enumerate(self.app.model.result_table.columns.menu_items()):
            action = menu.addAction(display)
            action.setCheckable(True)
            action.setChecked(visible)
            action.item_index = index
            self._column_actions.append(action)
        menu.addSeparator()
        action = menu.addAction(tr("Reset to Defaults"))
        action.item_index = -1
        
        # Action menu
        actionMenu = QMenu(tr("Actions"), self.menubar)
        actionMenu.addAction(self.actionDeleteMarked)
        actionMenu.addAction(self.actionMoveMarked)
        actionMenu.addAction(self.actionCopyMarked)
        actionMenu.addAction(self.actionRemoveMarked)
        actionMenu.addSeparator()
        actionMenu.addAction(self.actionRemoveSelected)
        actionMenu.addAction(self.actionIgnoreSelected)
        actionMenu.addAction(self.actionMakeSelectedReference)
        actionMenu.addSeparator()
        actionMenu.addAction(self.actionOpenSelected)
        actionMenu.addAction(self.actionRevealSelected)
        actionMenu.addAction(self.actionInvokeCustomCommand)
        actionMenu.addAction(self.actionRenameSelected)
        self.actionActions.setMenu(actionMenu)
        self.actionsButton.setMenu(self.actionActions.menu())
    
    def _setupUi(self):
        self.setWindowTitle(tr("{} Results").format(self.app.NAME))
        self.resize(630, 514)
        self.centralwidget = QWidget(self)
        self.verticalLayout = QVBoxLayout(self.centralwidget)
        self.verticalLayout.setMargin(0)
        self.verticalLayout.setSpacing(0)
        self.actionsButton = QPushButton(tr("Actions"))
        self.detailsButton = QPushButton(tr("Details"))
        self.dupesOnlyCheckBox = QCheckBox(tr("Dupes Only"))
        self.deltaValuesCheckBox = QCheckBox(tr("Delta Values"))
        self.searchEdit = SearchEdit()
        self.searchEdit.setMaximumWidth(300)
        self.horizontalLayout = horizontalWrap([self.actionsButton, self.detailsButton,
            self.dupesOnlyCheckBox, self.deltaValuesCheckBox, None, self.searchEdit, 8])
        self.horizontalLayout.setSpacing(8)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.resultsView = ResultsView(self.centralwidget)
        self.resultsView.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.resultsView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.resultsView.setSortingEnabled(True)
        self.resultsView.verticalHeader().setVisible(False)
        h = self.resultsView.horizontalHeader()
        h.setHighlightSections(False)
        h.setMovable(True)
        h.setStretchLastSection(False)
        h.setDefaultAlignment(Qt.AlignLeft)
        self.verticalLayout.addWidget(self.resultsView)
        self.setCentralWidget(self.centralwidget)
        self._setupActions()
        self._setupMenu()
        self.statusbar = QStatusBar(self)
        self.statusbar.setSizeGripEnabled(True)
        self.setStatusBar(self.statusbar)
        self.statusLabel = QLabel(self)
        self.statusbar.addPermanentWidget(self.statusLabel, 1)
        
        if self.app.prefs.resultWindowIsMaximized:
            self.setWindowState(self.windowState() | Qt.WindowMaximized)
        else:
            if self.app.prefs.resultWindowRect is not None:
                self.setGeometry(self.app.prefs.resultWindowRect)
            else:
                moveToScreenCenter(self)
    
    #--- Private
    def _update_column_actions_status(self):
        # Update menu checked state
        menu_items = self.app.model.result_table.columns.menu_items()
        for action, (display, visible) in zip(self._column_actions, menu_items):
            action.setChecked(visible)
    
    #--- Actions
    def actionsTriggered(self):
        self.actionsButton.showMenu()
    
    def addToIgnoreListTriggered(self):
        self.app.model.add_selected_to_ignore_list()
    
    def copyTriggered(self):
        self.app.model.copy_or_move_marked(True)
    
    def deleteTriggered(self):
        self.app.model.delete_marked()
    
    def deltaTriggered(self, state=None):
        # The sender can be either the action or the checkbox, but both have a isChecked() method.
        self.resultsModel.delta_values = self.sender().isChecked()
        self.actionDelta.setChecked(self.resultsModel.delta_values)
        self.deltaValuesCheckBox.setChecked(self.resultsModel.delta_values)
    
    def detailsTriggered(self):
        self.app.show_details()
    
    def markAllTriggered(self):
        self.app.model.mark_all()
    
    def markInvertTriggered(self):
        self.app.model.mark_invert()
    
    def markNoneTriggered(self):
        self.app.model.mark_none()
    
    def markSelectedTriggered(self):
        self.app.model.toggle_selected_mark_state()
    
    def moveTriggered(self):
        self.app.model.copy_or_move_marked(False)
    
    def openTriggered(self):
        self.app.model.open_selected()
    
    def powerMarkerTriggered(self, state=None):
        # see deltaTriggered
        self.resultsModel.power_marker = self.sender().isChecked()
        self.actionPowerMarker.setChecked(self.resultsModel.power_marker)
        self.dupesOnlyCheckBox.setChecked(self.resultsModel.power_marker)
    
    def preferencesTriggered(self):
        self.app.show_preferences()
    
    def removeMarkedTriggered(self):
        self.app.model.remove_marked()
    
    def removeSelectedTriggered(self):
        self.app.model.remove_selected()
    
    def renameTriggered(self):
        index = self.resultsView.selectionModel().currentIndex()
        # Our index is the current row, with column set to 0. Our filename column is 1 and that's
        # what we want.
        index = index.sibling(index.row(), 1)
        self.resultsView.edit(index)
    
    def reprioritizeTriggered(self):
        dlg = PrioritizeDialog(self, self.app)
        result = dlg.exec()
        if result == QDialog.Accepted:
            dlg.model.perform_reprioritization()
    
    def revealTriggered(self):
        self.app.model.reveal_selected()
    
    def saveResultsTriggered(self):
        title = tr("Select a file to save your results to")
        files = tr("dupeGuru Results (*.dupeguru)")
        destination = QFileDialog.getSaveFileName(self, title, '', files)
        if destination:
            if not destination.endswith('.dupeguru'):
                destination = '{}.dupeguru'.format(destination)
            self.app.model.save_as(destination)
            self.app.recentResults.insertItem(destination)
    
    #--- Events
    def appWillSavePrefs(self):
        prefs = self.app.prefs
        prefs.resultWindowIsMaximized = self.isMaximized()
        prefs.resultWindowRect = self.geometry()
    
    def columnToggled(self, action):
        index = action.item_index
        if index == -1:
            self.app.model.result_table.columns.reset_to_defaults()
            self._update_column_actions_status()
        else:
            visible = self.app.model.result_table.columns.toggle_menu_item(index)
            action.setChecked(visible)
    
    def contextMenuEvent(self, event):
        self.actionActions.menu().exec_(event.globalPos())
    
    def resultsDoubleClicked(self, modelIndex):
        self.app.model.open_selected()
    
    def resultsSpacePressed(self):
        self.app.model.toggle_selected_mark_state()
    
    def searchChanged(self):
        self.app.model.apply_filter(self.searchEdit.text())
Esempio n. 4
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = True
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    asyncCallsStateChange = Signal()
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self._guiElements = []      # used for automatic widget debugging
        self.__reportData = None

        # TODO: position used to be saved like this. Reimplement.
        #if save_position:
        #    self.settingsList = getattr(self, "settingsList", []) + \
        #                        ["widgetShown", "savedWidgetGeometry"]

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()    # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False
        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled
                else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self, orientation="horizontal",
                                         margin=0, spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.topWidgetPart = gui.widgetBox(self,
                                           orientation="horizontal", margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart,
                                            orientation="vertical", margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(self.topWidgetPart,
                                          orientation="vertical",
                                          sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                                 QSizePolicy.Expanding),
                                          margin=0)
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart,
                                             orientation="vertical", margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart,
                                                  orientation="horizontal", margin=4)
            self.graphButton = gui.button(graphButtonBackground,
                                          self, "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-red.png"))

    # status bar handler functions
    def setState(self, stateType, id, text):
        stateChanged = super().setState(stateType, id, text)
        if not stateChanged or not hasattr(self, "widgetStatusArea"):
            return

        iconsShown = 0
        warnings = [("Warning", self._warningWidget, self._owWarning),
                    ("Error", self._errorWidget, self._owError)]
        for state, widget, use in warnings:
            if not widget:
                continue
            if use and self.widgetState[state]:
                widget.setToolTip("\n".join(self.widgetState[state].values()))
                widget.show()
                iconsShown = 1
            else:
                widget.setToolTip("")
                widget.hide()

        if iconsShown:
            self.statusBarIconArea.show()
        else:
            self.statusBarIconArea.hide()

        if (stateType == "Warning" and self._owWarning) or \
                (stateType == "Error" and self._owError):
            if text:
                self.setStatusBarText(stateType + ": " + text)
            else:
                self.setStatusBarText("")
        self.updateStatusBarState()

    def updateWidgetStateInfo(self, stateType, id, text):
        html = self.widgetStateToHtml(self._owInfo, self._owWarning,
                                      self._owError)
        if html:
            self.widgetStateInfoBox.show()
            self.widgetStateInfo.setText(html)
            self.widgetStateInfo.setToolTip(html)
        else:
            if not self.widgetStateInfoBox.isVisible():
                dHeight = - self.widgetStateInfoBox.height()
            else:
                dHeight = 0
            self.widgetStateInfoBox.hide()
            self.widgetStateInfo.setText("")
            self.widgetStateInfo.setToolTip("")
            width, height = self.width(), self.height() + dHeight
            self.resize(width, height)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass


    # ##############################################
    """
    def isDataWithClass(self, data, wantedVarType=None, checkMissing=False):
        self.error([1234, 1235, 1236])
        if not data:
            return 0
        if not data.domain.classVar:
            self.error(1234, "A data set with a class attribute is required.")
            return 0
        if wantedVarType and data.domain.classVar.varType != wantedVarType:
            self.error(1235, "Unable to handle %s class." %
                             str(data.domain.class_var.var_type).lower())
            return 0
        if checkMissing and not orange.Preprocessor_dropMissingClasses(data):
            self.error(1236, "Unable to handle data set with no known classes")
            return 0
        return 1
    """

    def restoreWidgetPosition(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored:
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored:
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before showEvent and we must not overwrite the the savedGeometry
        # with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position:
            # Restore saved geometry on show
            self.restoreWidgetPosition()
        self.__was_restored = True

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    # this function is only intended for derived classes to send appropriate
    # signals when all settings are loaded
    def activate_loaded_settings(self):
        pass

    # reimplemented in other widgets
    def onDeleteWidget(self):
        pass

    def handleNewSignals(self):
        # this is called after all new signals have been handled
        # implement this in your widget if you want to process something only
        # after you received multiple signals
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self):
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarValue = 0

    def progressBarSet(self, value):
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn("progressBarSet() called without a "
                              "preceding progressBarInit()",
                              stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle +
                                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        self.progressBarValueChanged.emit(value)

        if old != value:
            self.progressBarValueChanged.emit(value)

        qApp.processEvents()

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value):
        self.progressBarSet(self.progressBarValue + value)

    def progressBarFinished(self):
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(
                    highest_type, msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(
                    highest_type, tooltip_lines[0], tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {"Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
                  "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
                  "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)}
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".
            format(background, foreground))
        self.warning_label.setText(text)
        self.warning_label.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        pixmaps = self.getWidgetStateIcons()
        items = []
        iconPath = {"Info": "canvasIcons:information.png",
                    "Warning": "canvasIcons:warning.png",
                    "Error": "canvasIcons:error.png"}
        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconPath[what],
                              "\n".join(self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            iconsDir = os.path.join(environ.canvas_install_dir, "icons")
            QDir.addSearchPath("canvasIcons",
                               os.path.join(environ.canvas_install_dir,
                                            "icons/"))
            info = QPixmap("canvasIcons:information.png")
            warning = QPixmap("canvasIcons:warning.png")
            error = QPixmap("canvasIcons:error.png")
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
                lambda self: self.showMaximized
                if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
                lambda self: self.setVisible(not self.isVisible())}

    def setBlocking(self, state=True):
        """ Set blocking flag for this widget. While this flag is set this
        widget and all its descendants will not receive any new signals from
        the signal manager
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """ Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)
Esempio n. 5
0
class ViewerWnd(QMainWindow):
    def __init__(self, app, dictOpts):
        QMainWindow.__init__(self)
        self.setWindowTitle("PostGIS Layer Viewer - v.1.6.1")
        self.setTabPosition(Qt.BottomDockWidgetArea, QTabWidget.North)

        self.canvas = QgsMapCanvas()
        self.canvas.setCanvasColor(Qt.white)
        self.canvas.useImageToRender(True)
        self.canvas.enableAntiAliasing(True)
        self.setCentralWidget(self.canvas)

        actionZoomIn = QAction(QIcon(imgs_dir + "mActionZoomIn.png"),
                               QString("Zoom in"), self)
        actionZoomOut = QAction(QIcon(imgs_dir + "mActionZoomOut.png"),
                                QString("Zoom out"), self)
        actionPan = QAction(QIcon(imgs_dir + "mActionPan.png"), QString("Pan"),
                            self)
        actionZoomFullExtent = QAction(
            QIcon(imgs_dir + "mActionZoomFullExtent.png"),
            QString("Zoom full"), self)

        actionZoomIn.setCheckable(True)
        actionZoomOut.setCheckable(True)
        actionPan.setCheckable(True)

        self.connect(actionZoomIn, SIGNAL("triggered()"), self.zoomIn)
        self.connect(actionZoomOut, SIGNAL("triggered()"), self.zoomOut)
        self.connect(actionPan, SIGNAL("triggered()"), self.pan)
        self.connect(actionZoomFullExtent, SIGNAL("triggered()"),
                     self.zoomFullExtent)

        self.actionGroup = QActionGroup(self)
        self.actionGroup.addAction(actionPan)
        self.actionGroup.addAction(actionZoomIn)
        self.actionGroup.addAction(actionZoomOut)

        # Create the toolbar
        self.toolbar = self.addToolBar("Map tools")
        self.toolbar.addAction(actionPan)
        self.toolbar.addAction(actionZoomIn)
        self.toolbar.addAction(actionZoomOut)
        self.toolbar.addAction(actionZoomFullExtent)

        # Create the map tools
        self.toolPan = QgsMapToolPan(self.canvas)
        self.toolPan.setAction(actionPan)
        self.toolZoomIn = QgsMapToolZoom(self.canvas, False)  # false = in
        self.toolZoomIn.setAction(actionZoomIn)
        self.toolZoomOut = QgsMapToolZoom(self.canvas, True)  # true = out
        self.toolZoomOut.setAction(actionZoomOut)

        # Create the statusbar
        self.statusbar = QStatusBar(self)
        self.statusbar.setObjectName("statusbar")
        self.setStatusBar(self.statusbar)

        self.lblXY = QLabel()
        self.lblXY.setFrameStyle(QFrame.Box)
        self.lblXY.setMinimumWidth(170)
        self.lblXY.setAlignment(Qt.AlignCenter)
        self.statusbar.setSizeGripEnabled(False)
        self.statusbar.addPermanentWidget(self.lblXY, 0)

        self.lblScale = QLabel()
        self.lblScale.setFrameStyle(QFrame.StyledPanel)
        self.lblScale.setMinimumWidth(140)
        self.statusbar.addPermanentWidget(self.lblScale, 0)

        self.createLegendWidget()  # Create the legend widget

        self.connect(app, SIGNAL("loadPgLayer"), self.loadLayer)
        self.connect(self.canvas, SIGNAL("scaleChanged(double)"),
                     self.changeScale)
        self.connect(self.canvas, SIGNAL("xyCoordinates(const QgsPoint&)"),
                     self.updateXY)

        self.pan()  # Default

        self.plugins = Plugins(self, self.canvas, dictOpts['-h'],
                               dictOpts['-p'], dictOpts['-d'], dictOpts['-U'],
                               dictOpts['-W'])

        self.createAboutWidget()
        self.layerSRID = '-1'
        self.loadLayer(dictOpts)

    def zoomIn(self):
        self.canvas.setMapTool(self.toolZoomIn)

    def zoomOut(self):
        self.canvas.setMapTool(self.toolZoomOut)

    def pan(self):
        self.canvas.setMapTool(self.toolPan)

    def zoomFullExtent(self):
        self.canvas.zoomToFullExtent()

    def about(self):
        pass

    def createLegendWidget(self):
        """ Create the map legend widget and associate it to the canvas """
        self.legend = Legend(self)
        self.legend.setCanvas(self.canvas)
        self.legend.setObjectName("theMapLegend")

        self.LegendDock = QDockWidget("Layers", self)
        self.LegendDock.setObjectName("legend")
        self.LegendDock.setTitleBarWidget(QWidget())
        self.LegendDock.setWidget(self.legend)
        self.LegendDock.setContentsMargins(0, 0, 0, 0)
        self.addDockWidget(Qt.BottomDockWidgetArea, self.LegendDock)

    def createAboutWidget(self):
        self.AboutDock = QDockWidget("About", self)
        self.AboutDock.setObjectName("about")
        self.AboutDock.setTitleBarWidget(QWidget())
        self.AboutDock.setContentsMargins(0, 0, 0, 0)
        self.tabifyDockWidget(self.LegendDock, self.AboutDock)
        self.LegendDock.raise_()  # legendDock at the top

        from PyQt4.QtCore import QRect
        from PyQt4.QtGui import QSizePolicy, QGridLayout, QFont
        font = QFont()
        font.setFamily("Sans Serif")
        font.setPointSize(8.7)
        self.AboutWidget = QWidget()
        self.AboutWidget.setFont(font)
        self.AboutWidget.setObjectName("AboutWidget")
        self.AboutDock.setWidget(self.AboutWidget)
        self.labelAbout = QLabel(self.AboutWidget)
        self.labelAbout.setAlignment(Qt.AlignCenter)
        self.labelAbout.setWordWrap(True)
        self.gridLayout = QGridLayout(self.AboutWidget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setObjectName("gridLayout")
        self.gridLayout.addWidget(self.labelAbout, 0, 1, 1, 1)
        self.labelAbout.setTextInteractionFlags(Qt.LinksAccessibleByMouse
                                                | Qt.LinksAccessibleByKeyboard
                                                | Qt.TextSelectableByKeyboard
                                                | Qt.TextSelectableByMouse)
        self.labelAbout.setOpenExternalLinks(True)
        self.labelAbout.setText("<html><head/><body><a href=\"http://geotux.tuxfamily.org/index.php/en/geo-blogs/item/293-consola-sql-para-plugin-pgadmin-postgis-viewer\">PostGIS Layer Viewer</a> v.1.6.1 (2015.02.24)<br \><br \>" \
            "Copyright (c) 2010 Ivan Mincik,<br \>[email protected]<br \>" \
            u"Copyright (c) 2011-2015 Germán Carrillo,<br \>[email protected]<br \><br \>" \
            "<i>Licensed under the terms of GNU GPL v.2.0</i><br \><br \>" \
            "Based on PyQGIS. Plugin Fast SQL Layer by Pablo T. Carreira.</body></html>" )

    def loadLayer(self, dictOpts):
        print 'I: Loading the layer...'
        self.layerSRID = dictOpts[
            'srid']  # To access the SRID when querying layer properties

        if not self.isActiveWindow():
            self.activateWindow()
            self.raise_()

        if dictOpts['type'] == 'vector':
            # QGIS connection
            uri = QgsDataSourceURI()
            uri.setConnection(dictOpts['-h'], dictOpts['-p'], dictOpts['-d'],
                              dictOpts['-U'], dictOpts['-W'])
            uri.setDataSource(dictOpts['-s'], dictOpts['-t'], dictOpts['-g'])
            layer = QgsVectorLayer(uri.uri(),
                                   dictOpts['-s'] + '.' + dictOpts['-t'],
                                   "postgres")
        elif dictOpts['type'] == 'raster':
            connString = "PG: dbname=%s host=%s user=%s password=%s port=%s mode=2 " \
                "schema=%s column=%s table=%s" % ( dictOpts['-d'], dictOpts['-h'],
                dictOpts['-U'], dictOpts['-W'], dictOpts['-p'], dictOpts['-s'],
                dictOpts['col'], dictOpts['-t'] )
            layer = QgsRasterLayer(connString,
                                   dictOpts['-s'] + '.' + dictOpts['-t'])

            if layer.isValid():
                layer.setContrastEnhancement(
                    QgsContrastEnhancement.StretchToMinimumMaximum)

        self.addLayer(layer, self.layerSRID)

    def addLayer(self, layer, srid='-1'):
        if layer.isValid():
            # Only in case that srid != -1, read the layer SRS properties, otherwise don't since it will return 4326
            if srid != '-1':
                self.layerSRID = layer.crs().description() + ' (' + str(
                    layer.crs().postgisSrid()) + ')'
            else:
                self.layerSRID = 'Unknown SRS (-1)'

            if self.canvas.layerCount() == 0:
                self.canvas.setExtent(layer.extent())

                if srid != '-1':
                    print 'I: Map SRS (EPSG): %s' % self.layerSRID
                    self.canvas.setMapUnits(layer.crs().mapUnits())
                else:
                    print 'I: Unknown Reference System'
                    self.canvas.setMapUnits(0)  # 0: QGis.Meters

            return QgsMapLayerRegistry.instance().addMapLayer(layer)
        return False

    def activeLayer(self):
        """ Returns the active layer in the layer list widget """
        return self.legend.activeLayer()

    def getLayerProperties(self, l):
        """ Create a layer-properties string (l:layer)"""
        print 'I: Generating layer properties...'
        if l.type() == 0:  # Vector
            wkbType = [
                "WKBUnknown", "WKBPoint", "WKBLineString", "WKBPolygon",
                "WKBMultiPoint", "WKBMultiLineString", "WKBMultiPolygon",
                "WKBNoGeometry", "WKBPoint25D", "WKBLineString25D",
                "WKBPolygon25D", "WKBMultiPoint25D", "WKBMultiLineString25D",
                "WKBMultiPolygon25D"
            ]
            properties = "Source: %s\n" \
                         "Geometry type: %s\n" \
                         "Number of features: %s\n" \
                         "Number of fields: %s\n" \
                         "SRS (EPSG): %s\n" \
                         "Extent: %s " \
                          % ( l.source(), wkbType[l.wkbType()], l.featureCount(),
                              l.dataProvider().fields().count(), self.layerSRID,
                              l.extent().toString() )
        elif l.type() == 1:  # Raster
            rType = [
                "GrayOrUndefined (single band)", "Palette (single band)",
                "Multiband", "ColorLayer"
            ]
            properties = "Source: %s\n" \
                         "Raster type: %s\n" \
                         "Width-Height (pixels): %sx%s\n" \
                         "Bands: %s\n" \
                         "SRS (EPSG): %s\n" \
                         "Extent: %s" \
                         % ( l.source(), rType[l.rasterType()], l.width(), l.height(),
                             l.bandCount(), self.layerSRID, l.extent().toString() )

        self.layerSRID = '-1'  # Initialize the srid
        return properties

    def changeScale(self, scale):
        self.lblScale.setText("Scale 1:" + formatNumber(scale))

    def updateXY(self, p):
        if self.canvas.mapUnits() == 2:  # Degrees
            self.lblXY.setText( formatToDegrees( p.x() ) + " | " \
                + formatToDegrees( p.y() ) )
        else:  # Unidad lineal
            self.lblXY.setText( formatNumber( p.x() ) + " | " \
                + formatNumber( p.y() ) + "" )
Esempio n. 6
0
class OWWidget(QDialog, Report, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget Meta Description
    # -----------------------

    #: Widget name (:class:`str`) as presented in the Canvas
    name = None
    id = None
    category = None
    version = None
    #: Short widget description (:class:`str` optional), displayed in
    #: canvas help tooltips.
    description = None
    #: A longer widget description (:class:`str` optional)
    long_description = None
    #: Widget icon path relative to the defining module
    icon = "icons/Unknown.png"
    #: Widget priority used for sorting within a category
    #: (default ``sys.maxsize``).
    priority = sys.maxsize

    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None

    #: A list of published input definitions
    inputs = []
    #: A list of published output definitions
    outputs = []

    # Default widget GUI layout settings
    # ----------------------------------

    #: Should the widget have basic layout
    #: (If this flag is false then the `want_main_area` and
    #: `want_control_area` are ignored).
    want_basic_layout = True
    #: Should the widget construct a `mainArea` (this is a resizable
    #: area to the right of the `controlArea`).
    want_main_area = True
    #: Should the widget construct a `controlArea`.
    want_control_area = True
    #: Widget painted by `Save graph" button
    graph_name = None
    graph_writers = FileFormat.img_writers
    want_status_bar = False

    save_position = True

    #: If false the widget will receive fixed size constraint
    #: (derived from it's layout). Use for widgets which have simple
    #: static size contents.
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    #: A list of advice messages (:class:`Message`) to display to the user.
    #: When a widget is first shown a message from this list is selected
    #: for display. If a user accepts (clicks 'Ok. Got it') the choice is
    #: recorded and the message is never shown again (closing the message
    #: will not mark it as seen). Messages can be displayed again by pressing
    #: Shift + F1
    #:
    #: :type: list of :class:`Message`
    UserAdviceMessages = []

    def __new__(cls, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)
        self.__env = _asmappingproxy(kwargs.get("env", {}))

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self.__reportData = None

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()    # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False

        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        self.__msgwidget = None
        self.__msgchoice = 0

        if self.want_basic_layout:
            self.insertLayout()

        sc = QShortcut(QKeySequence(Qt.ShiftModifier | Qt.Key_F1), self)
        sc.activated.connect(self.__quicktip)

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    def inline_graph_report(self):
        box = gui.widgetBox(self.controlArea, orientation="horizontal")
        box.layout().addWidget(self.graphButton)
        box.layout().addWidget(self.report_button)
#        self.report_button_background.hide()
#        self.graphButtonBackground.hide()

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled
                else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)

    class Splitter(QSplitter):
        def createHandle(self):
            return self.Handle(self.orientation(), self,
                                   cursor=Qt.PointingHandCursor)
        class Handle(QSplitterHandle):
            def mouseReleaseEvent(self, event):
                if event.button() == Qt.LeftButton:
                    splitter = self.splitter()
                    splitter.setSizes([int(splitter.sizes()[0] == 0), 1000])
                super().mouseReleaseEvent(event)
            def mouseMoveEvent(self, event):
                return  # Prevent moving; just show/hide

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self, orientation="horizontal",
                                         margin=0, spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.want_main_area = self.graph_name is not None or self.want_main_area

        splitter = self.Splitter(Qt.Horizontal, self)
        self.layout().addWidget(splitter)

        if self.want_control_area:
            self.controlArea = gui.widgetBox(splitter,
                                             orientation="vertical",
                                             margin=0)
            splitter.setSizes([1])  # Results in smallest size allowed by policy

            if self.graph_name is not None or hasattr(self, "send_report"):
                leftSide = self.controlArea
                self.controlArea = gui.widgetBox(leftSide, margin=0)
            if self.graph_name is not None:
                self.graphButton = gui.button(leftSide, None, "&Save Graph")
                self.graphButton.clicked.connect(self.save_graph)
                self.graphButton.setAutoDefault(0)
            if hasattr(self, "send_report"):
                self.report_button = gui.button(leftSide, None, "&Report",
                                                callback=self.show_report)
                self.report_button.setAutoDefault(0)

            if self.want_main_area:
                self.controlArea.setSizePolicy(QSizePolicy.Fixed,
                                               QSizePolicy.MinimumExpanding)
            self.controlArea.layout().setContentsMargins(4, 4, 0 if self.want_main_area else 4, 4)
        if self.want_main_area:
            self.mainArea = gui.widgetBox(splitter,
                                          orientation="vertical",
                                          margin=4,
                                          sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                                 QSizePolicy.Expanding))
            splitter.setCollapsible(1, False)
            self.mainArea.layout().setContentsMargins(0 if self.want_control_area else 4, 4, 4, 4)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-red.png"))

        if not self.resizing_enabled:
            self.layout().setSizeConstraint(QVBoxLayout.SetFixedSize)

    def save_graph(self):
        graph_obj = getdeepattr(self, self.graph_name, None)
        if graph_obj is None:
            return
        saveplot.save_plot(graph_obj, self.graph_writers)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    def __restoreWidgetGeometry(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored and not self.windowState() & \
                    (Qt.WindowMaximized | Qt.WindowFullScreen):
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored and self.isVisible():
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before first showEvent and we must not overwrite the the
        # savedGeometry with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position and not self.__was_restored:
            # Restore saved geometry on show
            self.__restoreWidgetGeometry()
            self.__was_restored = True
        self.__quicktipOnce()

    def wheelEvent(self, event):
        # Silently accept the wheel event. This is to ensure combo boxes
        # and other controls that have focus don't receive this event unless
        # the cursor is over them.
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        """
        Send a `value` on the `signalName` widget output.

        An output with `signalName` must be defined in the class ``outputs``
        list.
        """
        if not any(s.name == signalName for s in self.outputs):
            raise ValueError('{} is not a valid output signal for widget {}'.format(
                signalName, self.name))
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    def onDeleteWidget(self):
        """
        Invoked by the canvas to notify the widget it has been deleted
        from the workflow.

        If possible, subclasses should gracefully cancel any currently
        executing tasks.
        """
        pass

    def handleNewSignals(self):
        """
        Invoked by the workflow signal propagation manager after all
        signals handlers have been called.

        Reimplement this method in order to coalesce updates from
        multiple updated inputs.
        """
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self, processEvents=QEventLoop.AllEvents):
        """
        Initialize the widget's progress (i.e show and set progress to 0%).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarSet(0, processEvents)

    def progressBarSet(self, value, processEvents=QEventLoop.AllEvents):
        """
        Set the current progress bar to `value`.

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param float value: Progress value
        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn("progressBarSet() called without a "
                              "preceding progressBarInit()",
                              stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle +
                                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        if old != value:
            self.progressBarValueChanged.emit(value)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents):
        self.progressBarSet(self.progressBarValue + value, processEvents)

    def progressBarFinished(self, processEvents=QEventLoop.AllEvents):
        """
        Stop the widget's progress (i.e hide the progress bar).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    @contextlib.contextmanager
    def progressBar(self, iterations=0):
        """
        Context manager for progress bar.

        Using it ensures that the progress bar is removed at the end without
        needing the `finally` blocks.

        Usage:

            with self.progressBar(20) as progress:
                ...
                progress.advance()

        or

            with self.progressBar() as progress:
                ...
                progress.advance(0.15)

        or

            with self.progressBar():
                ...
                self.progressBarSet(50)

        :param iterations: the number of iterations (optional)
        :type iterations: int
        """
        progress_bar = gui.ProgressBar(self, iterations)
        yield progress_bar
        progress_bar.finish()  # Let us not rely on garbage collector

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        """
        Set widget's status message.

        This is a short status string to be displayed inline next to
        the instantiated widget icon in the canvas.
        """
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        """
        Return the widget's status message.
        """
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        """
        Set/clear a widget information message (for `id`).

        Args:
            id (int or list): The id of the message
            text (str): Text of the message.
        """
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        """
        Set/clear a widget warning message (for `id`).

        Args:
            id (int or list): The id of the message
            text (str): Text of the message.
        """
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        """
        Set/clear a widget error message (for `id`).

        Args:
            id (int or list): The id of the message
            text (str): Text of the message.
        """
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(
                    highest_type, msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(
                    highest_type, tooltip_lines[0], tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {"Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
                  "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
                  "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)}
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".
            format(background, foreground))
        self.warning_label.setText(text)
        self.warning_bar.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        iconpaths = {
            "Info": gui.resource_filename("icons/information.png"),
            "Warning": gui.resource_filename("icons/warning.png"),
            "Error": gui.resource_filename("icons/error.png")
        }
        items = []

        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconpaths[what],
                              "\n".join(self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            info = QPixmap(gui.resource_filename("icons/information.png"))
            warning = QPixmap(gui.resource_filename("icons/warning.png"))
            error = QPixmap(gui.resource_filename("icons/error.png"))
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
                lambda self: self.showMaximized
                if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
                lambda self: self.setVisible(not self.isVisible())}

    def setBlocking(self, state=True):
        """
        Set blocking flag for this widget.

        While this flag is set this widget and all its descendants
        will not receive any new signals from the workflow signal manager.

        This is useful for instance if the widget does it's work in a
        separate thread or schedules processing from the event queue.
        In this case it can set the blocking flag in it's processNewSignals
        method schedule the task and return immediately. After the task
        has completed the widget can clear the flag and send the updated
        outputs.

        .. note::
            Failure to clear this flag will block dependent nodes forever.
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """
        Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)

    def workflowEnv(self):
        """
        Return (a view to) the workflow runtime environment.

        Returns
        -------
        env : types.MappingProxyType
        """
        return self.__env

    def workflowEnvChanged(self, key, value, oldvalue):
        """
        A workflow environment variable `key` has changed to value.

        Called by the canvas framework to notify widget of a change
        in the workflow runtime environment.

        The default implementation does nothing.
        """
        pass

    def __showMessage(self, message):
        if self.__msgwidget is not None:
            self.__msgwidget.hide()
            self.__msgwidget.deleteLater()
            self.__msgwidget = None

        if message is None:
            return

        buttons = MessageOverlayWidget.Ok | MessageOverlayWidget.Close
        if message.moreurl is not None:
            buttons |= MessageOverlayWidget.Help

        if message.icon is not None:
            icon = message.icon
        else:
            icon = Message.Information

        self.__msgwidget = MessageOverlayWidget(
            parent=self, text=message.text, icon=icon, wordWrap=True,
            standardButtons=buttons)

        b = self.__msgwidget.button(MessageOverlayWidget.Ok)
        b.setText("Ok, got it")

        self.__msgwidget.setStyleSheet("""
            MessageOverlayWidget {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1,
                    stop:0 #666, stop:0.3 #6D6D6D, stop:1 #666)
            }
            MessageOverlayWidget QLabel#text-label {
                color: white;
            }"""
        )

        if message.moreurl is not None:
            helpbutton = self.__msgwidget.button(MessageOverlayWidget.Help)
            helpbutton.setText("Learn more\N{HORIZONTAL ELLIPSIS}")
            self.__msgwidget.helpRequested.connect(
                lambda: QDesktopServices.openUrl(QUrl(message.moreurl)))

        self.__msgwidget.setWidget(self)
        self.__msgwidget.show()

    def __quicktip(self):
        messages = list(self.UserAdviceMessages)
        if messages:
            message = messages[self.__msgchoice % len(messages)]
            self.__msgchoice += 1
            self.__showMessage(message)

    def __quicktipOnce(self):
        filename = os.path.join(settings.widget_settings_dir(),
                               "user-session-state.ini")
        namespace = ("user-message-history/{0.__module__}.{0.__qualname__}"
                     .format(type(self)))
        session_hist = QSettings(filename, QSettings.IniFormat)
        session_hist.beginGroup(namespace)
        messages = self.UserAdviceMessages

        def ispending(msg):
            return not session_hist.value(
                "{}/confirmed".format(msg.persistent_id),
                defaultValue=False, type=bool)
        messages = list(filter(ispending, messages))

        if not messages:
            return

        message = messages[self.__msgchoice % len(messages)]
        self.__msgchoice += 1

        self.__showMessage(message)

        def userconfirmed():
            session_hist = QSettings(filename, QSettings.IniFormat)
            session_hist.beginGroup(namespace)
            session_hist.setValue(
                "{}/confirmed".format(message.persistent_id), True)
            session_hist.sync()

        self.__msgwidget.accepted.connect(userconfirmed)
class ViewerWnd( QMainWindow ):
    def __init__( self, app, dictOpts ):
        QMainWindow.__init__( self )
        self.setWindowTitle( "PostGIS Layer Viewer - v.1.6.1" )
        self.setTabPosition( Qt.BottomDockWidgetArea, QTabWidget.North )

        self.canvas = QgsMapCanvas()
        self.canvas.setCanvasColor( Qt.white )
        self.canvas.useImageToRender( True )
        self.canvas.enableAntiAliasing( True )
        self.setCentralWidget( self.canvas )

        actionZoomIn = QAction( QIcon( imgs_dir + "mActionZoomIn.png" ), QString( "Zoom in" ), self )
        actionZoomOut = QAction( QIcon( imgs_dir + "mActionZoomOut.png" ), QString( "Zoom out" ), self )
        actionPan = QAction( QIcon( imgs_dir + "mActionPan.png" ), QString( "Pan" ), self )
        actionZoomFullExtent = QAction( QIcon( imgs_dir + "mActionZoomFullExtent.png" ), QString( "Zoom full" ), self )

        actionZoomIn.setCheckable( True )
        actionZoomOut.setCheckable( True )
        actionPan.setCheckable( True )

        self.connect(actionZoomIn, SIGNAL( "triggered()" ), self.zoomIn )
        self.connect(actionZoomOut, SIGNAL( "triggered()" ), self.zoomOut )
        self.connect(actionPan, SIGNAL( "triggered()" ), self.pan )
        self.connect(actionZoomFullExtent, SIGNAL( "triggered()" ), self.zoomFullExtent )

        self.actionGroup = QActionGroup( self )
        self.actionGroup.addAction( actionPan )
        self.actionGroup.addAction( actionZoomIn )
        self.actionGroup.addAction( actionZoomOut )        

        # Create the toolbar
        self.toolbar = self.addToolBar( "Map tools" )
        self.toolbar.addAction( actionPan )
        self.toolbar.addAction( actionZoomIn )
        self.toolbar.addAction( actionZoomOut )
        self.toolbar.addAction( actionZoomFullExtent )

        # Create the map tools
        self.toolPan = QgsMapToolPan( self.canvas )
        self.toolPan.setAction( actionPan )
        self.toolZoomIn = QgsMapToolZoom( self.canvas, False ) # false = in
        self.toolZoomIn.setAction( actionZoomIn )
        self.toolZoomOut = QgsMapToolZoom( self.canvas, True ) # true = out
        self.toolZoomOut.setAction( actionZoomOut )
        
        # Create the statusbar
        self.statusbar = QStatusBar( self )
        self.statusbar.setObjectName( "statusbar" )
        self.setStatusBar( self.statusbar )

        self.lblXY = QLabel()
        self.lblXY.setFrameStyle( QFrame.Box )
        self.lblXY.setMinimumWidth( 170 )
        self.lblXY.setAlignment( Qt.AlignCenter )
        self.statusbar.setSizeGripEnabled( False )
        self.statusbar.addPermanentWidget( self.lblXY, 0 )

        self.lblScale = QLabel()
        self.lblScale.setFrameStyle( QFrame.StyledPanel )
        self.lblScale.setMinimumWidth( 140 )
        self.statusbar.addPermanentWidget( self.lblScale, 0 )

        self.createLegendWidget()   # Create the legend widget

        self.connect( app, SIGNAL( "loadPgLayer" ), self.loadLayer )
        self.connect( self.canvas, SIGNAL( "scaleChanged(double)" ),
            self.changeScale )
        self.connect( self.canvas, SIGNAL( "xyCoordinates(const QgsPoint&)" ),
            self.updateXY )

        self.pan() # Default

        self.plugins = Plugins( self, self.canvas, dictOpts['-h'], dictOpts['-p'], dictOpts['-d'], dictOpts['-U'], dictOpts['-W'] )

        self.createAboutWidget()
        self.layerSRID = '-1'
        self.loadLayer( dictOpts )
    
    def zoomIn( self ):
        self.canvas.setMapTool( self.toolZoomIn )

    def zoomOut( self ):
        self.canvas.setMapTool( self.toolZoomOut )

    def pan( self ):
        self.canvas.setMapTool( self.toolPan )

    def zoomFullExtent( self ):
        self.canvas.zoomToFullExtent()
    
    def about( self ):
        pass

    def createLegendWidget( self ):
        """ Create the map legend widget and associate it to the canvas """
        self.legend = Legend( self )
        self.legend.setCanvas( self.canvas )
        self.legend.setObjectName( "theMapLegend" )

        self.LegendDock = QDockWidget( "Layers", self )
        self.LegendDock.setObjectName( "legend" )
        self.LegendDock.setTitleBarWidget( QWidget() )
        self.LegendDock.setWidget( self.legend )
        self.LegendDock.setContentsMargins ( 0, 0, 0, 0 )
        self.addDockWidget( Qt.BottomDockWidgetArea, self.LegendDock )

    def createAboutWidget( self ):
        self.AboutDock = QDockWidget( "About", self )
        self.AboutDock.setObjectName( "about" )
        self.AboutDock.setTitleBarWidget( QWidget() )
        self.AboutDock.setContentsMargins( 0, 0, 0, 0 )
        self.tabifyDockWidget( self.LegendDock, self.AboutDock )
        self.LegendDock.raise_() # legendDock at the top

        from PyQt4.QtCore import QRect
        from PyQt4.QtGui import QSizePolicy, QGridLayout, QFont
        font = QFont()
        font.setFamily("Sans Serif")
        font.setPointSize(8.7)
        self.AboutWidget = QWidget()
        self.AboutWidget.setFont( font )
        self.AboutWidget.setObjectName("AboutWidget") 
        self.AboutDock.setWidget( self.AboutWidget )
        self.labelAbout = QLabel( self.AboutWidget )
        self.labelAbout.setAlignment(Qt.AlignCenter)
        self.labelAbout.setWordWrap(True)
        self.gridLayout = QGridLayout(self.AboutWidget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setObjectName("gridLayout")
        self.gridLayout.addWidget(self.labelAbout, 0, 1, 1, 1)
        self.labelAbout.setTextInteractionFlags(Qt.LinksAccessibleByMouse|Qt.LinksAccessibleByKeyboard|Qt.TextSelectableByKeyboard|Qt.TextSelectableByMouse) 
        self.labelAbout.setOpenExternalLinks( True )
        self.labelAbout.setText("<html><head/><body><a href=\"http://geotux.tuxfamily.org/index.php/en/geo-blogs/item/293-consola-sql-para-plugin-pgadmin-postgis-viewer\">PostGIS Layer Viewer</a> v.1.6.1 (2015.02.24)<br \><br \>" \
            "Copyright (c) 2010 Ivan Mincik,<br \>[email protected]<br \>" \
            u"Copyright (c) 2011-2015 Germán Carrillo,<br \>[email protected]<br \><br \>" \
            "<i>Licensed under the terms of GNU GPL v.2.0</i><br \><br \>" \
            "Based on PyQGIS. Plugin Fast SQL Layer by Pablo T. Carreira.</body></html>" )

    def loadLayer( self, dictOpts ):
        print 'I: Loading the layer...'
        self.layerSRID = dictOpts[ 'srid' ] # To access the SRID when querying layer properties

        if not self.isActiveWindow():
            self.activateWindow()            
            self.raise_() 

        if dictOpts['type'] == 'vector':
            # QGIS connection
            uri = QgsDataSourceURI()
            uri.setConnection( dictOpts['-h'], dictOpts['-p'], dictOpts['-d'], 
                dictOpts['-U'], dictOpts['-W'] )
            uri.setDataSource( dictOpts['-s'], dictOpts['-t'], dictOpts['-g'] )
            layer = QgsVectorLayer( uri.uri(), dictOpts['-s'] + '.' + dictOpts['-t'],
                "postgres" )        
        elif dictOpts['type'] == 'raster':          
            connString = "PG: dbname=%s host=%s user=%s password=%s port=%s mode=2 " \
                "schema=%s column=%s table=%s" % ( dictOpts['-d'], dictOpts['-h'], 
                dictOpts['-U'], dictOpts['-W'], dictOpts['-p'], dictOpts['-s'], 
                dictOpts['col'], dictOpts['-t'] )
            layer = QgsRasterLayer( connString, dictOpts['-s'] + '.' + dictOpts['-t'] )
            
            if layer.isValid():
                layer.setContrastEnhancement( QgsContrastEnhancement.StretchToMinimumMaximum )

        self.addLayer( layer, self.layerSRID )

    def addLayer( self, layer, srid='-1' ):
        if layer.isValid():
            # Only in case that srid != -1, read the layer SRS properties, otherwise don't since it will return 4326
            if srid != '-1': 
               self.layerSRID = layer.crs().description() + ' (' + str( layer.crs().postgisSrid() ) + ')'
            else:
               self.layerSRID = 'Unknown SRS (-1)'

            if self.canvas.layerCount() == 0:
                self.canvas.setExtent( layer.extent() )

                if srid != '-1':
                    print 'I: Map SRS (EPSG): %s' % self.layerSRID                    
                    self.canvas.setMapUnits( layer.crs().mapUnits() )
                else:
                    print 'I: Unknown Reference System'
                    self.canvas.setMapUnits( 0 ) # 0: QGis.Meters

            return QgsMapLayerRegistry.instance().addMapLayer( layer )
        return False

    def activeLayer( self ):
        """ Returns the active layer in the layer list widget """
        return self.legend.activeLayer()

    def getLayerProperties( self, l ):
        """ Create a layer-properties string (l:layer)"""
        print 'I: Generating layer properties...'
        if l.type() == 0: # Vector
            wkbType = ["WKBUnknown","WKBPoint","WKBLineString","WKBPolygon",
                       "WKBMultiPoint","WKBMultiLineString","WKBMultiPolygon",
                       "WKBNoGeometry","WKBPoint25D","WKBLineString25D","WKBPolygon25D",
                       "WKBMultiPoint25D","WKBMultiLineString25D","WKBMultiPolygon25D"]
            properties = "Source: %s\n" \
                         "Geometry type: %s\n" \
                         "Number of features: %s\n" \
                         "Number of fields: %s\n" \
                         "SRS (EPSG): %s\n" \
                         "Extent: %s " \
                          % ( l.source(), wkbType[l.wkbType()], l.featureCount(), 
                              l.dataProvider().fields().count(), self.layerSRID, 
                              l.extent().toString() )
        elif l.type() == 1: # Raster
            rType = [ "GrayOrUndefined (single band)", "Palette (single band)", "Multiband", "ColorLayer" ]
            properties = "Source: %s\n" \
                         "Raster type: %s\n" \
                         "Width-Height (pixels): %sx%s\n" \
                         "Bands: %s\n" \
                         "SRS (EPSG): %s\n" \
                         "Extent: %s" \
                         % ( l.source(), rType[l.rasterType()], l.width(), l.height(),
                             l.bandCount(), self.layerSRID, l.extent().toString() )

        self.layerSRID = '-1' # Initialize the srid 
        return properties

    def changeScale( self, scale ):
        self.lblScale.setText( "Scale 1:" + formatNumber( scale ) )

    def updateXY( self, p ):
        if self.canvas.mapUnits() == 2: # Degrees
            self.lblXY.setText( formatToDegrees( p.x() ) + " | " \
                + formatToDegrees( p.y() ) )
        else: # Unidad lineal
            self.lblXY.setText( formatNumber( p.x() ) + " | " \
                + formatNumber( p.y() ) + "" )
Esempio n. 8
0
class StatusBarManager ( HasFacets ):
    """ A status bar manager realizes itself in a status bar control. """

    #-- Facet Definitions ------------------------------------------------------

    # FIXME v3: Is this used anywhere?
    # The manager's unique identifier (if it has one).
    id = Str

    # The message displayed in the first field of the status bar.
    message = Property

    # The messages to be displayed in the status bar fields.
    messages = List( Unicode )

    # The toolkit-specific control that represents the status bar.
    status_bar = Any

    #-- StatusBarManager Interface ---------------------------------------------

    def create_status_bar ( self, parent ):
        """ Creates a status bar. """

        if self.status_bar is None:
            self.status_bar = QStatusBar( parent )
            self.status_bar.setSizeGripEnabled( False )

            if len( self.messages ) > 1:
                self._show_messages()
            else:
                self.status_bar.showMessage( self.message )

        return self.status_bar

    #-- Property Implementations -----------------------------------------------

    def _get_message ( self ):

        if len( self.messages ) > 0:
            message = self.messages[ 0 ]
        else:
            message = ''

        return message

    def _set_message ( self, value ):

        if len( self.messages ) > 0:
            old = self.messages[ 0 ]
            self.messages[ 0 ] = value
        else:
            old = ''
            self.messages.append( old )

        self.facet_property_set( 'message', old, value )

    #-- Facet Event Handlers ---------------------------------------------------

    def _messages_set ( self ):
        """ Sets the text displayed on the status bar. """

        if self.status_bar is not None:
            self._show_messages()


    def _messages_items_set ( self ):
        """ Sets the text displayed on the status bar. """

        if self.status_bar is not None:
            self._show_messages()

    #-- Private Methods --------------------------------------------------------

    def _show_messages ( self ):
        """ Display the list of messages. """

        # FIXME v3: At the moment we just string them together but we may
        # decide to put all but the first message into separate widgets.  We
        # probably also need to extend the API to allow a "message" to be a
        # widget - depends on what wx is capable of.
        self.status_bar.showMessage( "  ".join( self.messages ) )

#-- EOF ------------------------------------------------------------------------
Esempio n. 9
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = True
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)
        self.__env = _asmappingproxy(kwargs.get("env", {}))

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self.__reportData = None

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()    # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False
        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled
                else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self, orientation="horizontal",
                                         margin=0, spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.topWidgetPart = gui.widgetBox(self,
                                           orientation="horizontal", margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart,
                                            orientation="vertical", margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(self.topWidgetPart,
                                          orientation="vertical",
                                          sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                                 QSizePolicy.Expanding),
                                          margin=0)
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart,
                                             orientation="vertical", margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart,
                                                  orientation="horizontal", margin=4)
            self.graphButton = gui.button(graphButtonBackground,
                                          self, "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-red.png"))

        if not self.resizing_enabled:
            self.layout().setSizeConstraint(QVBoxLayout.SetFixedSize)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass

    def __restoreWidgetGeometry(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored and not self.windowState() & \
                    (Qt.WindowMaximized | Qt.WindowFullScreen):
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored and self.isVisible():
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before first showEvent and we must not overwrite the the
        # savedGeometry with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position and not self.__was_restored:
            # Restore saved geometry on show
            self.__restoreWidgetGeometry()
            self.__was_restored = True

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if not any(s.name == signalName for s in self.outputs):
            raise ValueError('{} is not a valid output signal for widget {}'.format(
                signalName, self.name))
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    def onDeleteWidget(self):
        """
        Invoked by the canvas to notify the widget it has been deleted
        from the workflow.

        If possible, subclasses should gracefully cancel any currently
        executing tasks.
        """
        pass

    def handleNewSignals(self):
        """
        Invoked by the workflow signal propagation manager after all
        signals handlers have been called.

        Reimplement this method in order to coalesce updates from
        multiple updated inputs.
        """
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self, processEvents=QEventLoop.AllEvents):
        """
        Initialize the widget's progress (i.e show and set progress to 0%).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarSet(0, processEvents)

    def progressBarSet(self, value, processEvents=QEventLoop.AllEvents):
        """
        Set the current progress bar to `value`.

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param float value: Progress value
        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn("progressBarSet() called without a "
                              "preceding progressBarInit()",
                              stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle +
                                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        if old != value:
            self.progressBarValueChanged.emit(value)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents):
        self.progressBarSet(self.progressBarValue + value, processEvents)

    def progressBarFinished(self, processEvents=QEventLoop.AllEvents):
        """
        Stop the widget's progress (i.e hide the progress bar).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(
                    highest_type, msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(
                    highest_type, tooltip_lines[0], tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {"Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
                  "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
                  "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)}
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".
            format(background, foreground))
        self.warning_label.setText(text)
        self.warning_bar.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        iconpaths = {
            "Info": gui.resource_filename("icons/information.png"),
            "Warning": gui.resource_filename("icons/warning.png"),
            "Error": gui.resource_filename("icons/error.png")
        }
        items = []

        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconpaths[what],
                              "\n".join(self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            info = QPixmap(gui.resource_filename("icons/information.png"))
            warning = QPixmap(gui.resource_filename("icons/warning.png"))
            error = QPixmap(gui.resource_filename("icons/error.png"))
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
                lambda self: self.showMaximized
                if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
                lambda self: self.setVisible(not self.isVisible())}

    def setBlocking(self, state=True):
        """
        Set blocking flag for this widget.

        While this flag is set this widget and all its descendants
        will not receive any new signals from the workflow signal manager.

        This is useful for instance if the widget does it's work in a
        separate thread or schedules processing from the event queue.
        In this case it can set the blocking flag in it's processNewSignals
        method schedule the task and return immediately. After the task
        has completed the widget can clear the flag and send the updated
        outputs.

        .. note::
            Failure to clear this flag will block dependent nodes forever.
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """
        Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)

    def workflowEnv(self):
        """
        Return (a view to) the workflow runtime environment.

        Returns
        -------
        env : types.MappingProxyType
        """
        return self.__env

    def workflowEnvChanged(self, key, value, oldvalue):
        """
        A workflow environment variable `key` has changed to value.

        Called by the canvas framework to notify widget of a change
        in the workflow runtime environment.

        The default implementation does nothing.
        """
        pass
class ViewerWnd(QMainWindow):
    def __init__(self, app, dictOpts):
        QMainWindow.__init__(self)

        self.canvas = QgsMapCanvas()
        self.canvas.setCanvasColor(Qt.white)
        self.canvas.useImageToRender(True)
        self.canvas.enableAntiAliasing(True)
        self.setCentralWidget(self.canvas)

        actionZoomIn = QAction(QIcon(imgs_dir + "mActionZoomIn.png"), QString("Zoom in"), self)
        actionZoomOut = QAction(QIcon(imgs_dir + "mActionZoomOut.png"), QString("Zoom out"), self)
        actionPan = QAction(QIcon(imgs_dir + "mActionPan.png"), QString("Pan"), self)

        actionZoomIn.setCheckable(True)
        actionZoomOut.setCheckable(True)
        actionPan.setCheckable(True)

        self.connect(actionZoomIn, SIGNAL("triggered()"), self.zoomIn)
        self.connect(actionZoomOut, SIGNAL("triggered()"), self.zoomOut)
        self.connect(actionPan, SIGNAL("triggered()"), self.pan)

        # Create the toolbar
        self.toolbar = self.addToolBar("Map tools")
        self.toolbar.addAction(actionZoomIn)
        self.toolbar.addAction(actionZoomOut)
        self.toolbar.addAction(actionPan)

        # Create the map tools
        self.toolPan = QgsMapToolPan(self.canvas)
        self.toolPan.setAction(actionPan)
        self.toolZoomIn = QgsMapToolZoom(self.canvas, False)  # false = in
        self.toolZoomIn.setAction(actionZoomIn)
        self.toolZoomOut = QgsMapToolZoom(self.canvas, True)  # true = out
        self.toolZoomOut.setAction(actionZoomOut)

        # Create the statusbar
        self.statusbar = QStatusBar(self)
        self.statusbar.setObjectName("statusbar")
        self.setStatusBar(self.statusbar)

        self.lblXY = QLabel()
        self.lblXY.setFrameStyle(QFrame.Box)
        self.lblXY.setMinimumWidth(170)
        self.lblXY.setAlignment(Qt.AlignCenter)
        self.statusbar.setSizeGripEnabled(False)
        self.statusbar.addPermanentWidget(self.lblXY, 0)

        self.lblScale = QLabel()
        self.lblScale.setFrameStyle(QFrame.StyledPanel)
        self.lblScale.setMinimumWidth(140)
        self.statusbar.addPermanentWidget(self.lblScale, 0)

        self.createLegendWidget()  # Create the legend widget

        self.connect(app, SIGNAL("loadPgLayer"), self.loadLayer)
        self.connect(self.canvas, SIGNAL("scaleChanged(double)"), self.changeScale)
        self.connect(self.canvas, SIGNAL("xyCoordinates(const QgsPoint&)"), self.updateXY)

        self.pan()

        self.layerSRID = ""
        self.loadLayer(dictOpts)

    def zoomIn(self):
        self.canvas.setMapTool(self.toolZoomIn)

    def zoomOut(self):
        self.canvas.setMapTool(self.toolZoomOut)

    def pan(self):
        self.canvas.setMapTool(self.toolPan)

    def createLegendWidget(self):
        """ Create the map legend widget and associate to the canvas """
        self.legend = Legend(self)
        self.legend.setCanvas(self.canvas)
        self.legend.setObjectName("theMapLegend")

        self.LegendDock = QDockWidget("Layers", self)
        self.LegendDock.setObjectName("legend")
        # self.LegendDock.setAllowedAreas( Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea )
        self.LegendDock.setWidget(self.legend)
        self.LegendDock.setContentsMargins(0, 0, 0, 0)
        self.addDockWidget(Qt.BottomDockWidgetArea, self.LegendDock)

    def loadLayer(self, dictOpts):
        print "I: Loading the layer..."
        self.layerSRID = dictOpts["srid"]  # To access the SRID when querying layer properties

        if not self.isActiveWindow():
            self.activateWindow()
            self.raise_()

        if dictOpts["type"] == "vector":
            # QGIS connection
            uri = QgsDataSourceURI()
            uri.setConnection(dictOpts["-h"], dictOpts["-p"], dictOpts["-d"], dictOpts["-U"], dictOpts["-W"])
            uri.setDataSource(dictOpts["-s"], dictOpts["-t"], dictOpts["-g"])
            layer = QgsVectorLayer(uri.uri(), dictOpts["-s"] + "." + dictOpts["-t"], "postgres")
        elif dictOpts["type"] == "raster":
            connString = "PG: dbname=%s host=%s user=%s password=%s port=%s schema=%s table=%s" % (
                dictOpts["-d"],
                dictOpts["-h"],
                dictOpts["-U"],
                dictOpts["-W"],
                dictOpts["-p"],
                dictOpts["-s"],
                dictOpts["-t"],
            )
            layer = QgsRasterLayer(connString, dictOpts["-s"] + "." + dictOpts["-t"])
            layer.setNoDataValue(-32768)
            layer.rasterTransparency().initializeTransparentPixelList(-32768)

        if layer.isValid():
            if self.canvas.layerCount() == 0:
                self.canvas.setExtent(layer.extent())

                if dictOpts["srid"] != "-1":
                    print "I: Map SRS (EPSG): %s" % dictOpts["srid"]
                    self.canvas.setMapUnits(layer.srs().mapUnits())
                else:
                    print "I: Unknown Reference System"
                    self.canvas.setMapUnits(0)  # 0: QGis.Meters

            QgsMapLayerRegistry.instance().addMapLayer(layer)

    def getLayerProperties(self, l):
        """ Create a layer-properties string (l:layer)"""
        print "I: Generating layer properties..."
        if l.type() == 0:  # Vector
            wkbType = [
                "WKBUnknown",
                "WKBPoint",
                "WKBLineString",
                "WKBPolygon",
                "WKBMultiPoint",
                "WKBMultiLineString",
                "WKBMultiPolygon",
                "WKBNoGeometry",
                "WKBPoint25D",
                "WKBLineString25D",
                "WKBPolygon25D",
                "WKBMultiPoint25D",
                "WKBMultiLineString25D",
                "WKBMultiPolygon25D",
            ]
            properties = (
                "Source: %s\n"
                "Geometry type: %s\n"
                "Number of features: %s\n"
                "Number of fields: %s\n"
                "SRS (EPSG): %s\n"
                "Extent: %s "
                % (
                    l.source(),
                    wkbType[l.wkbType()],
                    l.featureCount(),
                    l.dataProvider().fieldCount(),
                    self.layerSRID,
                    l.extent().toString(),
                )
            )
        elif l.type() == 1:  # Raster
            rType = ["GrayOrUndefined (single band)", "Palette (single band)", "Multiband"]
            properties = (
                "Source: %s\n"
                "Raster type: %s\n"
                "Width-Height (pixels): %sx%s\n"
                "Bands: %s\n"
                "SRS (EPSG): %s\n"
                "Extent: %s"
                % (
                    l.source(),
                    rType[l.rasterType()],
                    l.width(),
                    l.height(),
                    l.bandCount(),
                    self.layerSRID,
                    l.extent().toString(),
                )
            )
        return properties

    def changeScale(self, scale):
        self.lblScale.setText("Scale 1:" + formatNumber(scale))

    def updateXY(self, p):
        if self.canvas.mapUnits() == 2:  # Degrees
            self.lblXY.setText(formatToDegrees(p.x()) + " | " + formatToDegrees(p.y()))
        else:  # Unidad lineal
            self.lblXY.setText(formatNumber(p.x()) + " | " + formatNumber(p.y()) + "")
Esempio n. 11
0
class UI(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle(u"TransTES Hub" if not DEBUG else 'TransTES [DEBUG]')
        self.setWindowIcon(QIcon('icons/app.png'))

        panel = QWidget()
        panel.setLayout(QHBoxLayout())
        self.setCentralWidget(panel)

        games_panel = QWidget()
        games_panel.setLayout(QVBoxLayout())
        # self.setCentralWidget(games_panel)

        games_panel.resize(500, 100)

        is_skywind = os.path.isfile('config/Skywind.yml')
        is_skyblivion = os.path.isfile('config/Skyblivion.yml')

        self.Skyrim = GameInfoPanel('Skyrim', force_browse=True)

        if is_skywind:
            self.Morrowind = GameInfoPanel('Morrowind')
            games_panel.layout().addWidget(self.Morrowind)
            self.Skywind = ModInfoPanel('Skywind', self.Skyrim, self.Morrowind)

        if is_skyblivion:
            self.Oblivion = GameInfoPanel('Oblivion')
            games_panel.layout().addWidget(self.Oblivion)
            self.Skyblivion = ModInfoPanel('Skyblivion', self.Skyrim, self.Oblivion)

        games_panel.layout().addWidget(self.Skyrim)

        games_panel.layout().addWidget(QLabel('<hr>'))
        if is_skywind:
            games_panel.layout().addWidget(self.Skywind)

        if is_skyblivion:
            games_panel.layout().addWidget(self.Skyblivion)

        buttons = QWidget()
        buttons.setLayout(QHBoxLayout())

        games_panel.layout().setAlignment(Qt.AlignTop)

        panel.layout().addWidget(games_panel)

        self.statusBar = QStatusBar(self)
        self.statusBar.addPermanentWidget(QWidget().setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        self.setStatusBar(self.statusBar)
        self.statusBar.addPermanentWidget(
            QLabel(u'by <b>Averrin</b> for <b>Skywind Project</b>. Version: %s  ' % __version__)
        )
        self.statusBar.setSizeGripEnabled(False)

        self.sideBar = SideBar(self)

        self.sizeHint = QSize(500, 100)

        self.dm = DM()
        # self.dm.onShow = lambda: self.setMaximumWidth(games_panel.width())
        self.readme = Browser()
        # self.readme.onShow = lambda: self.setMaximumWidth(games_panel.width())
        readme = self.createSBAction(QIcon(icons_folder + 'readme.png'), 'Readme', self.readme, toolbar=True,
                                     widgetWidth=700, titleWidget=self.readme.toolbar)
        readme.showWidget()
        self.createSBAction(QIcon(icons_folder + 'dm.png'),
                            'Downloads', self.dm, toolbar=True,
                            titleWidget=self.dm.toolbar)

    def createSBAction(self, icon, name, widget, keyseq='', toolbar=False, titleWidget=None, widgetWidth=None):
        action = SBAction(self.sideBar.dock, icon, name, self)
        action.widget = widget
        if titleWidget is not None:
            action.titleWidget = titleWidget
        else:
            action.titleWidget = QToolBar()

        if widgetWidth is not None:
            action.widgetWidth = widgetWidth

        self.sideBar.stack.addWidget(widget)
        action.triggered.connect(functools.partial(action.showWidget))
        if keyseq:
            action.setShortcut(keyseq)
        if toolbar:
            self.sideBar.addAction(action)
        self.addAction(action)
        return action
Esempio n. 12
0
class QtStatusBar(QtWidget, ProxyStatusBar):
    """ A Qt implementation of an Enaml ProxyStatusBar.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QStatusBar)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the QStatusBar widget.

        """
        self.widget = QStatusBar(self.parent_widget())

    def init_widget(self):
        """ Initialize the widget.

        """
        super(QtStatusBar, self).init_widget()
        self.set_size_grip_enabled(self.declaration.size_grip_enabled)

    def init_layout(self):
        """ Initialize the layout for the widget.

        """
        super(QtStatusBar, self).init_layout()
        widget = self.widget
        for child in self.children():
            if isinstance(child, QtStatusItem):
                s_widget = child.status_widget()
                if s_widget is not None:
                    stretch = child.stretch()
                    if child.is_permanent():
                        widget.addPermanentWidget(s_widget, stretch)
                    else:
                        widget.addWidget(s_widget, stretch)

    #--------------------------------------------------------------------------
    # Utility Methods
    #--------------------------------------------------------------------------
    def refresh_item(self, item):
        """ A method invoked by a child status item.

        This method can be called when the widget for the item should
        be refreshed in the status bar.

        """
        w = self.widget
        s = item.status_widget()
        if s is not None:
            w.removeWidget(s)
            for index, child in enumerate(self.children()):
                if child is item:
                    stretch = item.stretch()
                    if item.is_permanent():
                        w.insertPermanentWidget(index, s, stretch)
                    else:
                        w.insertWidget(index, s, stretch)
                    s.show()
                    break

    #--------------------------------------------------------------------------
    # Child Events
    #--------------------------------------------------------------------------
    def child_added(self, child):
        """ Handle the child added event for a QtStatusBar.

        """
        super(QtStatusBar, self).child_added(child)
        if isinstance(child, QtStatusItem):
            w = self.widget
            s = child.status_widget()
            if s is not None:
                for index, item in enumerate(self.children()):
                    if child is item:
                        stretch = item.stretch()
                        if item.is_permanent():
                            w.insertPermanentWidget(index, s, stretch)
                        else:
                            w.insertWidget(index, s, stretch)
                        break

    def child_removed(self, child):
        """ Handle the child removed event for a QtStatusBar.

        """
        if isinstance(child, QtStatusItem):
            s = child.status_widget()
            if s is not None:
                self.widget.removeWidget(s)

    #--------------------------------------------------------------------------
    # ProxyStatusBar API
    #--------------------------------------------------------------------------
    def set_size_grip_enabled(self, enabled):
        """ Set the size grip enabled on the underlying widget.

        """
        self.widget.setSizeGripEnabled(enabled)

    def show_message(self, message, timeout=0):
        """ Show a temporary message in the status bar.

        """
        self.widget.showMessage(message, timeout)

    def clear_message(self):
        """ Clear any temporary message shown in the status bar.

        """
        self.widget.clearMessage()
Esempio n. 13
0
class NotesMainWindow(QMainWindow):
    """The view class"""
    def __init__(self, parent=None):
        super().__init__(parent)

        # Load saved application settings
        self.recentNotes = []

        self.numberOfTabs = 0  # keep track of the total number of tabs in the window
        self.currentPage = None
        self.__mediator = MITM.Mediator.Mediator(
        )  # the mediator object. intermediary btwn view and model

        # create a timer
        timer = QTimer(self)
        # dialogs: saved to avoid eating memory by constant cre- and re- creation
        self.findDialog = None
        self.renameDialog = None
        self.helpAboutDialog = HelpAboutDialog(self)
        self.helpHelpDialog = HelpHelpDialog(self)
        self.recentFilesListWidget = QListWidget

        # create menus first
        self.fileMenu = self.menuBar().addMenu("File")
        self.recentNotesMenu = self.fileMenu.addMenu("Open Recent")
        self.editMenu = self.menuBar().addMenu("Edit")
        self.syncMenu = self.menuBar().addMenu("Sync")
        self.helpMenu = self.menuBar().addMenu("Help")

        # region Actions
        # create the menu actions
        # first the file menu actions
        fileNewAct = self.createAction(self,
                                       "&New",
                                       shortcut=QKeySequence.New,
                                       tooltip="Create a new note",
                                       enabled=True,
                                       slot=self.onNewClick)
        fileOpenRecentNotesAct = self.createAction(self,
                                                   "&Open Recent Note",
                                                   tooltip="open recent notes",
                                                   enabled=True,
                                                   slot=self.onOpenRecentNotes)
        fileQuitAct = self.createAction(self,
                                        "&Exit",
                                        shortcut=QKeySequence.Quit,
                                        tooltip="Exit the application",
                                        enabled=True,
                                        slot=self.close)

        # recent files menu actions
        self.recentFilesMenuAct = self.createAction(self.recentNotesMenu,
                                                    "File 1")

        # edit menu actions
        editInsertAct = self.createAction(self,
                                          "Ins&ert...",
                                          shortcut="Ctrl+I",
                                          tooltip="Insert a media file",
                                          enabled=True)
        editPreferencesAct = self.createAction(
            self,
            "&Preferences",
            shortcut="Ctrl+Shift+P",
            tooltip="Set application preferences",
            enabled=True)
        editFindAct = self.createAction(self,
                                        "&Find",
                                        shortcut=QKeySequence.Find,
                                        tooltip="Find a text string",
                                        slot=self.onFind)
        editUndoAct = self.createAction(
            self,
            "&Undo",
            shortcut=QKeySequence.Undo,
            tooltip="Roll back changes to document",
            slot=self.onUndo)
        editRedoAct = self.createAction(self,
                                        "&Redo",
                                        shortcut=QKeySequence.Redo,
                                        tooltip="Repeat the last action",
                                        slot=self.onRedo)
        editRenameAct = self.createAction(self,
                                          "&Rename...",
                                          shortcut="Ctrl+Shift+R",
                                          tooltip="Rename current note",
                                          slot=self.onRenameNote)

        # sync menu actions
        synCloudAcctAct = self.createAction(self,
                                            "Cloud &Account",
                                            shortcut="Alt+A",
                                            tooltip="Sync with cloud accounts",
                                            enabled=True)

        # help menu actions
        helpHelpAct = self.createAction(self,
                                        "Help",
                                        shortcut=QKeySequence.HelpContents,
                                        tooltip="Display help",
                                        enabled=True,
                                        slot=self.onHelpHelp)
        helpAboutAct = self.createAction(self,
                                         "A&bout",
                                         shortcut="Ctrl+Shift+B",
                                         tooltip="About application",
                                         enabled=True,
                                         slot=self.onHelpAbout)

        boldTextAction = self.createAction(self,
                                           "B",
                                           shortcut="Ctrl+B",
                                           tooltip="Bold text")
        italicsTextAction = self.createAction(self,
                                              "<i>I</i>",
                                              shortcut="Ctrl+I",
                                              tooltip="Italics text")
        # endregion

        # add actions to corresponding menu
        self.addActions_(
            self.fileMenu,
            (fileNewAct, fileOpenRecentNotesAct, fileQuitAct))  # to file menu
        self.addActions_(self.editMenu,
                         (editRenameAct, editUndoAct, editRedoAct, editFindAct,
                          editInsertAct, editPreferencesAct))  # to edit menu
        self.addActions_(self.helpMenu,
                         (helpHelpAct, helpAboutAct))  # to help menu
        self.addActions_(self.recentNotesMenu, (self.recentFilesMenuAct, ))
        # create tool bar and add relevant actions
        allToolBar = self.addToolBar(
            "All Tools"
        )  # tool bar that contains all tool;not separated into file/edit/*
        self.addActions_(allToolBar,
                         (fileNewAct, synCloudAcctAct, editInsertAct,
                          editFindAct, editUndoAct, editRedoAct))

        fontDetailsToolBar = self.addToolBar("Font Details")
        self.fontTypeComboBox = QFontComboBox(
            fontDetailsToolBar)  # get font list present on the user system
        self.fontSizeSpinBox = QSpinBox(fontDetailsToolBar)  # size of the font
        self.fontSizeSpinBox.setMinimum(7)
        self.fontSizeSpinBox.setValue(12)
        self.textBoldButton = QPushButton("B")
        self.addNewNoteButton = QPushButton("+")
        self.textItalicsButton = QPushButton("I")
        maskLabel = QLabel(
            fontDetailsToolBar)  # TODO: experimental, to be removed
        pixmap = QPixmap("Asset/guit.png")  # TODO: experimental, remove
        maskLabel.setPixmap(pixmap.mask())  # TODO: experimental, remove
        self.addWidgetToToolBar(fontDetailsToolBar,
                                (self.fontTypeComboBox, self.fontSizeSpinBox,
                                 self.textBoldButton, self.textItalicsButton))
        # self.addActions_(fontDetailsToolBar, (boldTextAction, italicsTextAction))

        # create a QTabWidget
        self.tabWidget = QTabWidget(self)
        self.tabWidget.setMovable(True)
        self.tabWidget.setTabsClosable(True)
        self.tabWidget.setTabPosition(QTabWidget.South)
        # self.setTabPosition(Qt.BottomDockWidgetArea, QTabWidget.South)  # TODO: not working yet. Handled
        self.tabWidgetTabBar = self.tabWidget.tabBar()
        self.tabWidget.setCornerWidget(self.addNewNoteButton,
                                       Qt.TopRightCorner)

        # create pages  # TODO:     let an event handler handle this job: HANDLED
        self.onWindowStartOpenNote()
        # self.page1 = View.TabbedPage.TabbedPage(self, Note("First", "/path"))
        # # add the pages to the tab Widget
        # self.tabWidget.addTab(self.page1, "Note 1 ")
        # self.currentPage = self.tabWidget.currentWidget()
        self.setCurrentPage()
        #        print("count() returned: ", self.tabWidget.count())

        # create "+" bar:  TODO: find a better way to create a plus tab: DONE
        # self.plusTabIndex = self.tabWidgetTabBar.insertTab(self.tabWidget.count() + 1,
        #                                         "+")  # this tab bears the "+" sign that indicates 'create new tab'
        # self.plusTabWidget = self.tabWidget.widget(self.plusTabIndex)  # get a reference to the plus tab widget

        # create Dock widget that holds find dialog
        # self.dockWidget = QDockWidget(self)
        # self.dockWidget.setAllowedAreas(Qt.TopDockWidgetArea)
        # self.dockWidget.setFeatures(QDockWidget.DockWidgetClosable|QDockWidget.DockWidgetMovable)
        # self.dockWidget.setWidget(FindDialog.FindDialog(self.currentPage, self))
        # self.dockWidget.hide()

        # do window namings and other stuffs
        self.statusbar = QStatusBar(self)
        self.statusbar.setSizeGripEnabled(False)
        self.setStatusBar(self.statusbar)
        self.setCentralWidget(self.tabWidget)
        self.setWindowTitle("Notes")

        # region Signal And Slot Bindings
        self.tabWidget.currentChanged.connect(self.setCurrentPage)
        # self.currentPage.firstTime.connect(self.openPageOnFirstNavigation)
        self.tabWidget.currentChanged.connect(
            self.notifyMediatorOfCurrentPageChange)
        # fileNewAct.triggered.connect(self.onNewClick)
        # fileQuitAct.triggered.connect(self.close)
        self.tabWidget.tabCloseRequested.connect(
            self.confirmTabCloseAndCloseTab)
        # self.tabWidget.tabCloseRequested.connect(self.holla)
        self.fontTypeComboBox.currentFontChanged.connect(self.changeFont)
        self.fontSizeSpinBox.valueChanged.connect(self.changeFontSize)
        self.textBoldButton.clicked.connect(self.toggleTextBold)
        self.textItalicsButton.clicked.connect(self.toggleTextItalics)
        timer.timeout.connect(self.onSaveClick)
        timer.timeout.connect(self.timed)
        self.addNewNoteButton.clicked.connect(self.onNewClick)
        self.currentPage.cursorPositionChanged.connect(
            self.reportCurrentCursor)
        self.recentNotesMenu.aboutToShow.connect(self.updateRecentNotesMenu)
        # editUndoAct.triggered.connect(self.currentPage.undo)
        # editRedoAct.triggered.connect(self.currentPage.redo)
        # endregion

        self.readSettings()
        # QTimer.singleShot(5000, self.onSaveClick)
        # Start the timer
        timer.start(1500000)
        # Application settings

    # region Methods
    def updateRecentNotesMenu(self):
        """Repopulate the recent files menu"""
        self.recentNotesMenu.clear()
        for note in self.recentNotes:
            if os.path.exists(self.mediator.returnNotePath(note)):
                noteName, ext = os.path.basename(note).split(".")
                act = self.createAction(
                    self.recentNotesMenu, noteName, slot=self.loadNotes
                )  #TODO: incomplete update loadNotes() to receive the noteName
                self.addActions_(self.recentNotesMenu, (act, ))

    def onOpenRecentNotes(self):
        """"""

    def reportCurrentCursor(self):
        print("###### cursor position has changed now #####")  # TODO: remove
        print("current font is: ", self.currentPage.currentFont().family())
        self.fontTypeComboBox.setCurrentFont(self.currentPage.currentFont())
        self.fontSizeSpinBox.setValue(
            self.currentPage.currentFont().pointSize())

    def onRecentNotes(self):
        self.recentFilesListWidget.show()

    def toggleTextItalics(self):
        self.currentPage.setFontItalic(not self.currentPage.fontItalic())
        self.currentPage.setFocus()
        # if not self.textItalicsButton.isChecked():
        #     self.textItalicsButton.setChecked(True)
        #     self.currentPage.setFontItalic(True)
        #     self.currentPage.setFocus()
        #     return
        # self.textItalicsButton.setChecked(False)
        # self.currentPage.setFontItalic(False)
        # self.currentPage.setFontItalic(checked)
        # self.currentPage.setFontItalic(not self.currentPage.fontItalic)

    def keyPressEvent(self, event: QKeyEvent):
        if event.modifiers() and Qt.ControlModifier:
            handled = False
        if event.key() == Qt.Key_B:
            self.toggleTextBold()  # bolden text
            handled = True
        elif event.key() == Qt.Key_I:
            self.toggleTextItalics()  # italicise text
            handled = True
            print("Ctrl+I")

        if handled:
            event.accept()
            return
        else:
            QTextEdit.keyPressEvent(self.currentPage, event)

    def toggleTextBold(self):
        self.currentPage.setFontWeight(
            QFont.Normal
            if self.currentPage.fontWeight() > QFont.Normal else QFont.Bold)
        # if not self.textBoldButton.isChecked():
        #     # self.textBoldButton.setChecked(True)
        #     self.currentPage.setFontWeight(QFont.Bold)
        #     self.currentPage.setFocus()
        #     return
        # # self.textBoldButton.setChecked(False)
        # self.currentPage.setFontWeight(QFont.Normal)
        self.currentPage.setFocus()
        # self.currentPage.setFontWeight(QFont.Bold) if checked else \
        #                                     self.currentPage.setFontWeight(QFont.Normal)
        # self.currentPage.setFocus()

    def changeFontSize(self, value: float):
        self.currentPage.setFontPointSize(value)
        self.currentPage.setFocus()

    def changeFont(self, font: QFont):
        self.currentPage.setCurrentFont(font)
        self.currentPage.setFocus()

    def addWidgetToToolBar(self, toolBar: QToolBar, widgets: tuple):
        for widget in widgets:
            toolBar.addWidget(widget)

    def holla(self, index):
        """Debugging Method. To be removed in release code"""
        print("<<<<**** Hey! holla! Removing: .... ", index)

    def timed(self):
        """Debugging Method. To be removed in release code"""
        print("\n$$$$> Called by timer")

    # region Methods

    def onWindowStartOpenNote(self):
        for note_file in self.findNotesInCurrentDirectory(
                applicationDirectory):  # find all the notes in the app dir
            self.createNoteObjectFromNoteFile(
                note_file)  # create note objs from the files
        self.createAndLoadNotes()

    def createNoteObjectFromNoteFile(self, note_file: str):
        self.mediator.createNote(notePath=note_file)

    def createAndLoadNotes(
            self):  # Trent Harmon: When a man loves a woman....song request
        if len(self.mediator.notesCollection) > 0:
            self.loadNotes()
        else:  # couldn't find a note obj
            self.createOnlyOneNote()

    def loadNotes(self):
        """Creates a TabbedPage for each note found in notes collection
            Without reading the contents of the associated note file found
            In note path"""
        note = self.sender().getData() if self.sender() is QAction else None
        if note:
            pageIndex = self.createNotePage(note)
            self.tabWidget.setCurrentIndex(pageIndex)

        for note in self.mediator.NotesCollection:
            self.createNotePage(note)
            # self.mediator.setCurrentlyDisplayedNote(note)

    def createNotePage(self, note):
        page = View.TabbedPage.TabbedPage(self, note)
        pageIndex = self.tabWidget.addTab(page, note.NoteName)
        page.page_number = pageIndex
        return pageIndex

    def createOnlyOneNote(self):
        page1 = View.TabbedPage.TabbedPage(self, Note("First", "/path"))
        # add the pages to the tab Widget
        self.tabWidget.addTab(page1, "Note 1 ")

    def openPageOnFirstNavigation(self):
        """Reads the content of the file on first nav.
            Implements something like lazy loading
            Implementation is whack. 
            Implementation Path:
                onWindowStart -> createNoteObjectFromNoteFile -> createAndLoadNotes ->
                loadNotes -> openPageOnFirstNavigation
            TODO: make a better lazy loading implementatiion"""
        # TODO: make a better lazy loading implementatiion
        print("\n<<<<<I also work\n")
        self.mediator.setCurrentlyDisplayedNote(self.currentPage.getNote())
        try:
            contents = self.mediator.openCurrentNote()
            self.currentPage.setHtml(contents)
            self.currentPage.moveCursor(
                QTextCursor.End, QTextCursor.MoveAnchor
            )  # move the cursor to the end of the document
            self.currentPageIsNowDirty(False)
        except IOError as e:
            logging.error("Note is not on filesystem yet: {}".format(e))

    def currentPageIsNowDirty(self, bool_: bool = False):
        self.currentPage.dirty = False

    @property
    def mediator(self):
        return self.__mediator

    @mediator.setter
    def mediator(self, newMediator):
        if isinstance(newMediator, MITM.Mediator.Mediator):
            self.__mediator = newMediator
        else:
            raise TypeError("newMediator must be of type Mediator")

    def writeSettings(self) -> None:
        settings = QSettings()
        settings.setValue("mainWindow/pos", self.pos())
        settings.setValue("mainWindow/size", self.size())
        settings.setValue("mainWindow/state", self.windowState())
        openedNotes = [note.NotePath for note in self.mediator.NotesCollection]
        settings.setValue("list of opened notes", openedNotes)
        fontAttrList = self.fontTypeComboBox.currentFont().toString()
        print("/////////>>>>  ", fontAttrList)
        settings.setValue("mainWindow/font", fontAttrList)
        settings.setValue("mainWindow/fontSize", self.fontSizeSpinBox.value())

    def readSettings(self):
        settings = QSettings()
        self.resize(settings.value("mainWindow/size", QSize(800, 600)))
        pos = settings.value("mainWindow/pos", QPoint(0, 0))
        listOfOpenedNotes = settings.value("list of opened notes")
        print("Opened notes:", listOfOpenedNotes)
        fontDetails = settings.value("mainWindow/font",
                                     "Architext, -1, -1, False")
        fontSize = settings.value("mainWindow/fontSize", 12)
        family, _, _, fontWeight, italics, *_ = fontDetails.split(",")
        font = QFont(family, int(fontSize), int(fontWeight), bool(italics))
        self.fontTypeComboBox.setCurrentFont(font)
        self.fontSizeSpinBox.setValue(int(fontSize))
        self.textItalicsButton.click() if font.italic(
        ) else self.textItalicsButton.clearFocus(
        )  #TODO: code smell, review later
        self.textBoldButton.click() if font.bold(
        ) else self.textBoldButton.clearFocus(
        )  #TODO: code smell, review later
        self.move(pos)
        self.restoreState(settings.value("mainWindow/state"))

    def setCurrentPage(self):
        """Updates the self.currentPage variable"""
        self.currentPage = self.tabWidget.currentWidget()
        self.currentPage.I_becameCurrentPage()
        print(":::::::::  New current page set: ",
              self.currentPage.getNote().NoteName)  # TODO: remove

    def notifyMediatorOfCurrentPageChange(self):
        self.mediator.setCurrentlyDisplayedNote(self.currentPage.getNote())

    def onNewClick(self):
        """Create a new area to write new note"""
        tabCount = self.tabWidget.count() + 1
        noteObject = self.mediator.createNote(tabCount)
        page = View.TabbedPage.TabbedPage(self, note=noteObject)
        newPageIndex = self.tabWidget.addTab(
            page,
            "Note {}".format(tabCount))  # add the page to the parent tabWidget
        page.page_number = newPageIndex  # assign a number to the page
        self.numberOfTabs = self.tabWidget.count()
        # self.tabWidgetTabBar.moveTab(self.plusTabIndex,
        #             self.numberOfTabs)  # move the "+" tab to the end TODO: update the index of the plus tab widget
        self.moveToTab(newPageIndex)
        self.currentPage.setFocus()

    def moveToTab(self, newPageIndex: int):
        self.tabWidgetTabBar.setCurrentIndex(
            newPageIndex)  # switch to the just created page

    def onSaveClick(self, pages: list = []):
        """This method gets the text from the current page;
                Passes the text along with the attached Note object to the mediator to save
        """
        if len(pages) == 0:  # pages is a list of note pages to be saved
            pages.append(self.currentPage)

        for page in pages:
            if page.dirty:
                rich_text = page.toHtml(
                )  # get the text from the current document
                # note = self.currentPage.getNote()  # get the Note object attached to this page
                try:
                    self.mediator.saveNote(rich_text, page.getNote(
                    ))  # give the text document to the mediator
                    self.currentPageIsNowDirty(False)
                except IOError as error:
                    QMessageBox(self, "File Save Error",
                                "Could not save the file", "Ok")
                    assert isinstance(
                        error, IOError
                    )  # pycharm suggested this, I don't think it's needed here
                    print("Error", "IOError occurred", error)
                    logging.log(1, "IOError occurred", error)
                except NoNoteException as error:
                    # QMessageBox.critical(self, "File Save Error", "Could not save the file: {}".format(error),
                    #             "Ok")  # TODO:Not sure this will work yet. Handled
                    print("Error", "IOError occurred: No Note", error)
                    logging.log(1, error)
            else:
                print("\n-----> No changes made to code")

    def onRenameNote(self):
        """Rename a Note"""
        # TODO : create a rename dialog window later
        renameDialog = RenameDialog(self)
        if renameDialog.exec_():
            newName = renameDialog.getNewName()
            try:
                self.mediator.renameNote(newName)
                self.tabWidget.setTabText(self.currentPage.page_number,
                                          newName)
            except Exception as e:  # TODO: what error exactly
                print("Error renaming this file:   ",
                      e)  # TODO: do better, inform  the user of the problem
                raise e

    def onUndo(self):
        """Connect to QTextEdit undo slot. A work around for inability to connect a QAction to
           QTextEdit undo slot.
        """
        self.currentPage.undo()

    def onRedo(self):
        """Connect to QTextEdit undo slot. A work around for inability to connect a QAction to
            QTextEdit undo slot.
         """
        self.currentPage.redo()
        # cus = CustomSignal()
        # cus.redoTriggered.connect(QTextEdit.redo)

    def onFind(self):  # TODO: song request Paul I.K. Dairo: Mo wa dupe
        textSelected = self.currentPage.getDocumentCursor().selectedText()
        print("Text Selected is:  ", textSelected)
        self.findDialog = FindDialog.FindDialog(self.currentPage, self,
                                                textSelected)
        self.findDialog.show()
        # self.dockWidget.show()

    def onHelpAbout(self):
        self.helpAboutDialog.show()

    def onHelpHelp(self):
        self.helpHelpDialog.show()

    def closeEvent(self, event: QEvent):
        # TODO: raise a dialog that prompts the user to accept the closing of the application
        """
        :type event: QEvent
        """
        numberOfPages = self.tabWidget.count()
        widgetList = [
            self.tabWidget.widget(widget) for widget in range(numberOfPages)
        ]
        # self.onSaveClick(widgetList)  # save notes first
        self.writeSettings()
        event.accept()

    def confirmTabCloseAndCloseTab(self, index):
        pageToRemove = self.tabWidget.widget(index)
        if pageToRemove.dirty and isinstance(pageToRemove,
                                             View.TabbedPage.TabbedPage):
            print("Tab Close requested")
            dialog = QMessageBox.warning(self, "Confirm",
                                         "Do you want to close the tab?", "Ok",
                                         "Cancel", "", 1)
            if dialog != 0:
                return
        self.tabWidget.removeTab(pageToRemove.page_number)
        self.recentNotes.append(pageToRemove.getNote())

    @staticmethod
    def addActions_(parent, actions: tuple) -> None:
        for action in actions:
            parent.addAction(action)

    @staticmethod
    def createAction(parent: QObject,
                     text: str,
                     icon: object = None,
                     shortcut: str = None,
                     tooltip: str = None,
                     enabled: bool = False,
                     signal: str = "triggered",
                     slot: object = None) -> object:
        action = QAction(parent)
        action.setText(text)
        if icon:
            action.setIcon(
                QIcon(icon))  # icon is expected as a string argument
        if shortcut:
            action.setShortcut(shortcut)
        if tooltip:
            action.setToolTip(tooltip)
        if enabled:
            action.setEnabled(enabled)

        if slot and signal == "triggered":
            action.pyqtConfigure(triggered=slot)

        return action

    @staticmethod
    def findNotesInCurrentDirectory(directory: str):
        if os.path.exists(directory):
            for path, dirs, files in os.walk(directory):
                for file in files:
                    if file.endswith(".dn"):
                        fullPath = os.path.join(directory, path, file)
                        yield os.path.normpath(os.path.abspath(fullPath))

    def __str__(self):
        print("I am a note window with id {}".format(self.winId()))

    def __repr__(self):
        print(r"I am a note")