Esempio n. 1
0
class MemUsageDialog(QDialog):
    def __init__(self, parent=None, update=True):
        QDialog.__init__(self, parent=parent)
        layout = QVBoxLayout()
        self.tree = QTreeWidget()
        layout.addWidget(self.tree)
        self.setLayout(layout)

        self._mgr = CacheMemoryManager()

        self._tracked_caches = {}

        # tree setup code
        self.tree.setHeaderLabels(
            ["cache", "memory", "roi", "dtype", "type", "info", "id"])
        self._idIndex = self.tree.columnCount() - 1
        self.tree.setColumnHidden(self._idIndex, True)
        self.tree.setSortingEnabled(True)
        self.tree.clear()

        self._root = TreeNode()

        # refresh every x seconds (see showEvent())
        self.timer = QTimer(self)
        if update:
            self.timer.timeout.connect(self._updateReport)

    def _updateReport(self):
        # we keep track of dirty reports so we just have to update the tree
        # instead of reconstructing it
        reports = []
        for c in self._mgr.getFirstClassCaches():
            r = MemInfoNode()
            c.generateReport(r)
            reports.append(r)
        self._root.handleChildrenReports(
            reports, root=self.tree.invisibleRootItem())

    def hideEvent(self, event):
        self.timer.stop()

    def showEvent(self, show):
        # update once so we don't have to wait for initial report
        self._updateReport()
        # update every 5 sec.
        self.timer.start(5*1000)
Esempio n. 2
0
class ErrorsTree(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        IDE.register_service("errors_tree", self)
        box = QVBoxLayout(self)
        box.setContentsMargins(0, 0, 0, 0)
        self._tree = QTreeWidget()
        self._tree.header().setHidden(True)
        self._tree.setAnimated(True)
        box.addWidget(self._tree)

    def refresh(self, errors, path):
        self._tree.clear()
        parent = QTreeWidgetItem(self._tree, [path])
        for lineno, msg in errors.items():
            item = QTreeWidgetItem(parent, [msg])
            self._tree.addTopLevelItem(item)

    def display_name(self):
        return 'Errors'
Esempio n. 3
0
class Results(QWidget):

    """Show results of occurrences in files inside the tools dock."""

    def __init__(self, parent):
        super(Results, self).__init__(parent)
        self._parent = parent
        vbox = QVBoxLayout(self)
        self._tree = QTreeWidget()
        self._tree.setHeaderLabels((translations.TR_CONTENT,
            translations.TR_FILE, translations.TR_LINE))
        self._tree.header().setHorizontalScrollMode(
            QAbstractItemView.ScrollPerPixel)
        self._tree.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self._tree.header().setSectionResizeMode(1, QHeaderView.ResizeToContents)
        self._tree.header().setSectionResizeMode(2, QHeaderView.ResizeToContents)
        self._tree.header().setStretchLastSection(True)
        self._tree.sortByColumn(1, Qt.AscendingOrder)

        vbox.addWidget(self._tree)

        #Signals
        self._tree.itemActivated['QTreeWidgetItem*', int].connect(self._open_result)
        self._tree.itemClicked['QTreeWidgetItem*', int].connect(self._open_result)

    def _open_result(self, item, col):
        """Get the data of the selected item and open the file."""
        filename = item.toolTip(1)
        line = int(item.text(2)) - 1
        main_container = IDE.get_service('main_container')
        if main_container:
            main_container.open_file(filename=filename, line=line)
        self._parent.hide()

    def update_result(self, items):
        """Update the result tree with the new items."""
        self._tree.clear()
        for i in items:
            item = QTreeWidgetItem(self._tree, (i[3], i[0], str(i[2] + 1)))
            item.setToolTip(1, i[1])
Esempio n. 4
0
class InspectorWindow(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent, Qt.Tool)
        self.setWindowTitle(self.tr("Inspector"))
        self._font = None
        self._glyph = None

        glyphGroup = AccordionBox(self.tr("Glyph"), self)
        glyphLayout = QGridLayout(self)
        columnOneWidth = self.fontMetrics().width('0') * 7

        nameLabel = RLabel(self.tr("Name:"), self)
        self.nameEdit = QLineEdit(self)
        self.nameEdit.editingFinished.connect(self.writeGlyphName)
        unicodesLabel = RLabel(self.tr("Unicode:"), self)
        self.unicodesEdit = QLineEdit(self)
        self.unicodesEdit.editingFinished.connect(self.writeUnicodes)
        unicodesRegExp = QRegularExpression(
            "(|([a-fA-F0-9]{4,6})( ([a-fA-F0-9]{4,6}))*)")
        unicodesValidator = QRegularExpressionValidator(unicodesRegExp, self)
        self.unicodesEdit.setValidator(unicodesValidator)
        widthLabel = RLabel(self.tr("Width:"), self)
        self.widthEdit = QLineEdit(self)
        self.widthEdit.editingFinished.connect(self.writeWidth)
        self.widthEdit.setMaximumWidth(columnOneWidth)
        self.widthEdit.setValidator(QIntValidator(self))
        leftSideBearingLabel = RLabel(self.tr("Left:"), self)
        self.leftSideBearingEdit = QLineEdit(self)
        self.leftSideBearingEdit.editingFinished.connect(
            self.writeLeftSideBearing)
        self.leftSideBearingEdit.setMaximumWidth(columnOneWidth)
        self.leftSideBearingEdit.setValidator(QIntValidator(self))
        rightSideBearingLabel = RLabel(self.tr("Right:"), self)
        self.rightSideBearingEdit = QLineEdit(self)
        self.rightSideBearingEdit.editingFinished.connect(
            self.writeRightSideBearing)
        self.rightSideBearingEdit.setMaximumWidth(columnOneWidth)
        self.rightSideBearingEdit.setValidator(QIntValidator(self))
        markColorLabel = RLabel(self.tr("Flag:"), self)
        self.markColorWidget = ColorVignette(self)
        self.markColorWidget.colorChanged.connect(
            self.writeMarkColor)
        self.markColorWidget.setMaximumWidth(columnOneWidth)

        l = 0
        glyphLayout.addWidget(nameLabel, l, 0)
        glyphLayout.addWidget(self.nameEdit, l, 1, 1, 5)
        l += 1
        glyphLayout.addWidget(unicodesLabel, l, 0)
        glyphLayout.addWidget(self.unicodesEdit, l, 1, 1, 5)
        l += 1
        glyphLayout.addWidget(widthLabel, l, 0)
        glyphLayout.addWidget(self.widthEdit, l, 1)
        l += 1
        glyphLayout.addWidget(leftSideBearingLabel, l, 0)
        glyphLayout.addWidget(self.leftSideBearingEdit, l, 1)
        glyphLayout.addWidget(rightSideBearingLabel, l, 2)
        glyphLayout.addWidget(self.rightSideBearingEdit, l, 3)
        l += 1
        glyphLayout.addWidget(markColorLabel, l, 0)
        glyphLayout.addWidget(self.markColorWidget, l, 1)
        glyphGroup.setLayout(glyphLayout)

        transformGroup = AccordionBox(self.tr("Transform"), self)
        transformLayout = QGridLayout(self)

        self.alignmentWidget = GlyphAlignmentWidget(self)
        hMirrorButton = QToolButton()
        hMirrorButton.clicked.connect(self.hMirror)
        hMirrorButton.setIcon(QIcon(":swap.svg"))
        vMirrorButton = QToolButton()
        vMirrorButton.clicked.connect(self.vMirror)
        vMirrorButton.setIcon(QIcon(":swap-vertical.svg"))
        # TODO: implement
        alignVTopButton = QToolButton()
        alignVTopButton.setEnabled(False)
        alignVTopButton.setIcon(QIcon(":format-vertical-align-top.svg"))
        alignVCenterButton = QToolButton()
        alignVCenterButton.setEnabled(False)
        alignVCenterButton.setIcon(QIcon(":format-vertical-align-center.svg"))
        alignVBottomButton = QToolButton()
        alignVBottomButton.setEnabled(False)
        alignVBottomButton.setIcon(QIcon(":format-vertical-align-bottom.svg"))
        alignHLeftButton = QToolButton()
        alignHLeftButton.setEnabled(False)
        alignHLeftButton.setIcon(QIcon(":format-horizontal-align-left.svg"))
        alignHCenterButton = QToolButton()
        alignHCenterButton.setEnabled(False)
        alignHCenterButton.setIcon(
            QIcon(":format-horizontal-align-center.svg"))
        alignHRightButton = QToolButton()
        alignHRightButton.setEnabled(False)
        alignHRightButton.setIcon(QIcon(":format-horizontal-align-right.svg"))

        buttonsLayout = QGridLayout()
        l = 0
        buttonsLayout.addWidget(hMirrorButton, l, 0)
        buttonsLayout.addWidget(vMirrorButton, l, 1)
        l += 1
        buttonsLayout.addWidget(alignVTopButton, l, 0)
        buttonsLayout.addWidget(alignVCenterButton, l, 1)
        buttonsLayout.addWidget(alignVBottomButton, l, 2)
        buttonsLayout.addWidget(alignHLeftButton, l, 3)
        buttonsLayout.addWidget(alignHCenterButton, l, 4)
        buttonsLayout.addWidget(alignHRightButton, l, 5)
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        buttonsLayout.addWidget(spacer, l, 7)

        # TODO: should this be implemented for partial selection?

        moveButton = QPushButton(self.tr("Move"), self)
        moveButton.clicked.connect(self.moveGlyph)
        moveXLabel = RLabel("x:", self)
        self.moveXEdit = QLineEdit("0", self)
        self.moveXEdit.setValidator(QIntValidator(self))
        moveYLabel = RLabel("y:", self)
        self.moveYEdit = QLineEdit("0", self)
        self.moveYEdit.setValidator(QIntValidator(self))
        moveXYBox = QCheckBox("x=y", self)
        moveXYBox.clicked.connect(self.lockMove)

        scaleButton = QPushButton(self.tr("Scale"), self)
        scaleButton.clicked.connect(self.scaleGlyph)
        scaleXLabel = RLabel("x:", self)
        self.scaleXEdit = QLineEdit("100", self)
        self.scaleXEdit.setValidator(QIntValidator(self))
        scaleYLabel = RLabel("y:", self)
        self.scaleYEdit = QLineEdit("100", self)
        self.scaleYEdit.setValidator(QIntValidator(self))
        scaleXYBox = QCheckBox("x=y", self)
        scaleXYBox.clicked.connect(self.lockScale)

        rotateButton = QPushButton(self.tr("Rotate"), self)
        rotateButton.clicked.connect(self.rotateGlyph)
        rotateLabel = RLabel("α:", self)
        self.rotateEdit = QLineEdit("0", self)
        self.rotateEdit.setValidator(QIntValidator(self))

        skewButton = QPushButton(self.tr("Skew"), self)
        skewButton.clicked.connect(self.skewGlyph)
        skewXLabel = RLabel("α:", self)
        self.skewXEdit = QLineEdit("0", self)
        self.skewXEdit.setValidator(QIntValidator(self))
        skewYLabel = RLabel("β:", self)
        self.skewYEdit = QLineEdit("0", self)
        self.skewYEdit.setValidator(QIntValidator(self))
        skewXYBox = QCheckBox("α=β", self)
        skewXYBox.clicked.connect(self.lockSkew)

        snapButton = QPushButton(self.tr("Snap"), self)
        snapButton.clicked.connect(self.snapGlyph)
        self.snapEdit = QLineEdit("1", self)
        self.snapEdit.setValidator(QIntValidator(self))

        l = 0
        transformLayout.addWidget(self.alignmentWidget, l, 0)
        transformLayout.addLayout(buttonsLayout, l, 1, 1, 5)
        l += 1
        transformLayout.addWidget(moveButton, l, 0)
        transformLayout.addWidget(moveXLabel, l, 1)
        transformLayout.addWidget(self.moveXEdit, l, 2)
        transformLayout.addWidget(moveYLabel, l, 3)
        transformLayout.addWidget(self.moveYEdit, l, 4)
        transformLayout.addWidget(moveXYBox, l, 5)
        l += 1
        transformLayout.addWidget(scaleButton, l, 0)
        transformLayout.addWidget(scaleXLabel, l, 1)
        transformLayout.addWidget(self.scaleXEdit, l, 2)
        transformLayout.addWidget(scaleYLabel, l, 3)
        transformLayout.addWidget(self.scaleYEdit, l, 4)
        transformLayout.addWidget(scaleXYBox, l, 5)
        transformGroup.setLayout(transformLayout)
        l += 1
        transformLayout.addWidget(rotateButton, l, 0)
        transformLayout.addWidget(rotateLabel, l, 1)
        transformLayout.addWidget(self.rotateEdit, l, 2)
        transformGroup.setLayout(transformLayout)
        l += 1
        transformLayout.addWidget(skewButton, l, 0)
        transformLayout.addWidget(skewXLabel, l, 1)
        transformLayout.addWidget(self.skewXEdit, l, 2)
        transformLayout.addWidget(skewYLabel, l, 3)
        transformLayout.addWidget(self.skewYEdit, l, 4)
        transformLayout.addWidget(skewXYBox, l, 5)
        transformGroup.setLayout(transformLayout)
        l += 1
        transformLayout.addWidget(snapButton, l, 0)
        transformLayout.addWidget(self.snapEdit, l, 2)
        transformGroup.setLayout(transformLayout)

        layerSetGroup = AccordionBox(self.tr("Layers"), self)
        layerSetLayout = QVBoxLayout(self)

        self.layerSetWidget = QTreeWidget(self)
        self.layerSetWidget.setHeaderLabels(
            (self.tr("Layer Name"), self.tr("Color")))
        self.layerSetWidget.setRootIsDecorated(False)
        self.layerSetWidget.setSelectionBehavior(QAbstractItemView.SelectRows)
        # TODO: make this work correctly, top-level items only
        # self.layerSetWidget.setDragDropMode(QAbstractItemView.InternalMove)

        layerSetLayout.addWidget(self.layerSetWidget)
        layerSetGroup.setLayout(layerSetLayout)

        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(glyphGroup)
        mainLayout.addWidget(transformGroup)
        mainLayout.addWidget(layerSetGroup)
        mainLayout.addWidget(spacer)
        self.setLayout(mainLayout)
        self.resize(200, self.height())

    # -------------
    # Notifications
    # -------------

    def _unsubscribeFromGlyph(self):
        glyph = self._glyph
        if glyph is not None:
            glyph.removeObserver(self, "Glyph.Changed")

    def _subscribeToGlyph(self, glyph):
        if glyph is not None:
            glyph.addObserver(
                self, "_updateGlyphAttributes", "Glyph.Changed")

    def _unsubscribeFromFont(self):
        font = self._font
        if font is not None:
            layerSet = font.layers
            if layerSet is not None:
                layerSet.removeObserver(self, "LayerSet.Changed")

    def _subscribeToFont(self, font):
        if font is not None:
            layerSet = font.layers
            if layerSet is not None:
                layerSet.addObserver(
                    self, "_updateLayerAttributes", "LayerSet.Changed")

    def _updateGlyph(self, notification=None):
        self._unsubscribeFromGlyph()
        app = QApplication.instance()
        self._glyph = app.currentGlyph()
        self._subscribeToGlyph(self._glyph)
        self.alignmentWidget.setGlyph(self._glyph)
        self._updateGlyphAttributes()

    def _updateFont(self, notification=None):
        self._unsubscribeFromFont()
        app = QApplication.instance()
        self._font = app.currentFont()
        self._subscribeToFont(self._font)
        self._updateLayerAttributes()

    def _updateGlyphAttributes(self, notification=None):
        name = None
        unicodes = None
        width = None
        leftSideBearing = None
        rightSideBearing = None
        markColor = None
        if self._glyph is not None:
            name = self._glyph.name
            unicodes = " ".join("%06X" % u if u > 0xFFFF else "%04X" %
                                u for u in self._glyph.unicodes)
            width = str(int(self._glyph.width))
            if self._glyph.leftMargin is not None:
                leftSideBearing = str(int(self._glyph.leftMargin))
            if self._glyph.rightMargin is not None:
                rightSideBearing = str(int(self._glyph.rightMargin))
            if self._glyph.markColor is not None:
                markColor = QColor.fromRgbF(
                    *tuple(self._glyph.markColor))

        self.nameEdit.setText(name)
        self.unicodesEdit.setText(unicodes)
        self.widthEdit.setText(width)
        self.leftSideBearingEdit.setText(leftSideBearing)
        self.rightSideBearingEdit.setText(rightSideBearing)
        self.markColorWidget.setColor(markColor)

    def _updateLayerAttributes(self, notification=None):
        self.layerSetWidget.clear()
        if self._font is None:
            return
        layerSet = self._font.layers
        if layerSet is None:
            return
        for layer in layerSet:
            item = QTreeWidgetItem(self.layerSetWidget)
            item.setFlags(item.flags() | Qt.ItemIsEditable)
            item.setText(0, layer.name)
            widget = ColorVignette(self)
            color = layer.color
            if color is not None:
                color = QColor.fromRgbF(*tuple(color))
            widget.setColor(color)
            widget.setMargins(2, 2, 2, 2)
            widget.setMayClearColor(False)
            widget.colorChanged.connect(self.writeLayerColor)
            widget.setProperty("layer", layer)
            self.layerSetWidget.setItemWidget(item, 1, widget)
        self.layerSetWidget.setColumnWidth(1, 100)

    # ---------
    # Callbacks
    # ---------

    # glyph attributes

    def writeGlyphName(self):
        if self._glyph is None:
            return
        self._glyph.name = self.nameEdit.text()

    def writeUnicodes(self):
        if self._glyph is None:
            return
        unicodes = self.unicodesEdit.text().split(" ")
        if len(unicodes) == 1 and unicodes[0] == "":
            self._glyph.unicodes = []
        else:
            self._glyph.unicodes = [int(uni, 16) for uni in unicodes]

    def writeWidth(self):
        if self._glyph is None:
            return
        self._glyph.width = int(self.widthEdit.text())

    def writeLeftSideBearing(self):
        if self._glyph is None:
            return
        self._glyph.leftMargin = int(self.leftSideBearingEdit.text())

    def writeRightSideBearing(self):
        if self._glyph is None:
            return
        self._glyph.rightMargin = int(self.rightSideBearingEdit.text())

    def writeMarkColor(self):
        color = self.markColorWidget.color()
        if color is not None:
            color = color.getRgbF()
        self._glyph.markColor = color

    def writeLayerColor(self):
        widget = self.sender()
        color = widget.color()
        layer = widget.property("layer")
        if color is not None:
            color = color.getRgbF()
        layer.color = color

    # transforms

    def hMirror(self):
        glyph = self._glyph
        if None in (glyph, glyph.controlPointBounds):
            return
        xMin, _, xMax, _ = glyph.controlPointBounds
        for contour in glyph:
            for point in contour:
                point.x = xMin + xMax - point.x
        glyph.dirty = True

    def vMirror(self):
        glyph = self._glyph
        if None in (glyph, glyph.controlPointBounds):
            return
        _, yMin, _, yMax = glyph.controlPointBounds
        for contour in glyph:
            for point in contour:
                point.y = yMin + yMax - point.y
        glyph.dirty = True

    def lockMove(self, checked):
        self.moveYEdit.setEnabled(not checked)

    def moveGlyph(self):
        x = self.moveXEdit.text()
        if not self.moveYEdit.isEnabled():
            y = x
        else:
            y = self.moveYEdit.text()
        x, y = int(x) if x != "" else 0, int(y) if y != "" else 0
        self._glyph.move((x, y))

    def lockScale(self, checked):
        self.scaleYEdit.setEnabled(not checked)

    def scaleGlyph(self):
        glyph = self._glyph
        # TODO: consider disabling the buttons in that case?
        if glyph is None:
            return
        sX = self.scaleXEdit.text()
        if not self.scaleYEdit.isEnabled():
            sY = sX
        else:
            sY = self.scaleYEdit.text()
        sX, sY = int(sX) if sX != "" else 100, int(sY) if sY != "" else 100
        sX /= 100
        sY /= 100
        center = self.alignmentWidget.origin()
        glyph.scale((sX, sY), center=center)

    def rotateGlyph(self):
        glyph = self._glyph
        if glyph is None:
            return
        r = self.rotateEdit.text()
        r = int(r) if r != "" else 0
        origin = self.alignmentWidget.origin()
        glyph.rotate(r, offset=origin)

    def lockSkew(self, checked):
        self.skewYEdit.setEnabled(not checked)

    def skewGlyph(self):
        glyph = self._glyph
        if glyph is None:
            return
        sX = self.skewXEdit.text()
        if not self.skewYEdit.isEnabled():
            sY = sX
        else:
            sY = self.skewYEdit.text()
        sX, sY = int(sX) if sX != "" else 0, int(sY) if sY != "" else 0
        glyph.skew((sX, sY))

    def snapGlyph(self):
        glyph = self._glyph
        if glyph is None:
            return
        base = self.snapEdit.text()
        base = int(base) if base != "" else 0
        glyph.snap(base)

    # ----------
    # Qt methods
    # ----------

    def showEvent(self, event):
        super().showEvent(event)
        # positioning
        screenRect = QApplication.desktop().availableGeometry(self)
        widgetRect = self.frameGeometry()
        x = screenRect.width() - (widgetRect.width() + 20)
        y = screenRect.center().y() - widgetRect.height() / 2
        self.move(x, y)
        # notifications
        app = QApplication.instance()
        self._updateGlyph()
        app.dispatcher.addObserver(self, "_updateGlyph", "currentGlyphChanged")
        self._updateFont()
        app.dispatcher.addObserver(self, "_updateFont", "currentFontChanged")

    def closeEvent(self, event):
        super().closeEvent(event)
        if event.isAccepted():
            app = QApplication.instance()
            app.dispatcher.removeObserver(self, "currentGlyphChanged")
            app.dispatcher.removeObserver(self, "currentFontChanged")
Esempio n. 5
0
class QrcPackageEditor(QGridLayout):
    """ A resource file system package editor.  Note that this is a QLayout and
    not a QWidget.
    """

    # Emitted when the package has changed.
    package_changed = pyqtSignal()

    def __init__(self, show_root=False, scan="Scan", scan_whats_this='', whats_this=''):
        """ Initialise the editor. """

        super().__init__()

        self.package = None
        self.project = None

        self._show_root = show_root

        self._package_edit = QTreeWidget(whatsThis=whats_this)
        self._package_edit.header().hide()
        self._package_edit.itemChanged.connect(self._package_changed)
        self.addWidget(self._package_edit, 0, 0, 3, 1)

        self._scan_button = QPushButton(scan, whatsThis=scan_whats_this,
                clicked=self._scan, enabled=False)
        self.addWidget(self._scan_button, 0, 1)

        self._remove_button = QPushButton("Remove all",
                whatsThis="Remove all of the scanned directories and files.",
                clicked=self._remove_all, enabled=False)
        self.addWidget(self._remove_button, 0, 2)

        self._include_button = QPushButton("Include all",
                whatsThis="Select all of the scanned directories and files.",
                clicked=self._include_all, enabled=False)
        self.addWidget(self._include_button, 1, 1)

        self._exclude_button = QPushButton("Exclude all",
                whatsThis="Deselect all of the scanned directories and files.",
                clicked=self._exclude_all, enabled=False)
        self.addWidget(self._exclude_button, 1, 2)

        self._exclusions_edit = QTreeWidget(
                whatsThis="Any directory or file that matches any of the "
                        "these patterns will be automatically ignored when "
                        "scanning. Double-click on a pattern to edit or remove "
                        "it. Double-click below the last pattern in order to "
                        "add a new one.")
        self._exclusions_edit.setHeaderLabel("Exclusions")
        self._exclusions_edit.setEditTriggers(
                QTreeWidget.DoubleClicked|QTreeWidget.SelectedClicked|
                        QTreeWidget.EditKeyPressed)
        self._exclusions_edit.setRootIsDecorated(False)
        self._exclusions_edit.itemChanged.connect(self._exclusion_changed)

        self.addWidget(self._exclusions_edit, 2, 1, 1, 2)

    def configure(self, package, project):
        """ Configure the editor with the contents of the given package and
        project.
        """

        # Save the configuration.
        self.package = package
        self.project = project

        # Set the package itself.
        self._visualise()

        # Set the exclusions.
        self._exclusions_edit.clear()

        for exclude in package.exclusions:
            self._add_exclusion_item(exclude)

        # Add one to be edited to create a new entry.
        self._add_exclusion_item()

        self._scan_button.setEnabled(package is not None)

    def get_root_dir(self):
        """ Return the root directory to scan, or '' if there was an error or
        the user cancelled.
        """

        raise NotImplementedError

    def filter(self, name):
        """ See if a scanned name should be discarded. """

        # Include everything by default.
        return False

    def required(self, name):
        """ See if a scanned name is required. """

        # Nothing is required by default.
        return False

    def _add_exclusion_item(self, exclude=''):
        """ Add a QTreeWidgetItem that holds an exclusion. """

        itm = QTreeWidgetItem([exclude])

        itm.setFlags(
                Qt.ItemIsSelectable|Qt.ItemIsEditable|Qt.ItemIsEnabled|
                        Qt.ItemNeverHasChildren)

        self._exclusions_edit.addTopLevelItem(itm)

    def _exclusion_changed(self, itm, column):
        """ Invoked when an exclusion has changed. """

        exc_edit = self._exclusions_edit

        new_exc = itm.data(0, Qt.DisplayRole).strip()
        itm_index = exc_edit.indexOfTopLevelItem(itm)

        if new_exc != '':
            # See if we have added a new one.
            if itm_index == exc_edit.topLevelItemCount() - 1:
                self._add_exclusion_item()
        else:
            # It is empty so remove it.
            exc_edit.takeTopLevelItem(itm_index)

        # Save the new exclusions.
        self.package.exclusions = [
                exc_edit.topLevelItem(i).data(0, Qt.DisplayRole).strip()
                        for i in range(exc_edit.topLevelItemCount() - 1)]

        self.package_changed.emit()

    def _get_items(self):
        """ Return an iterator over the tree widget items. """

        it = QTreeWidgetItemIterator(self._package_edit)

        if self._show_root:
            it += 1

        itm = it.value()
        while itm is not None:
            yield itm
            it += 1
            itm = it.value()

    def _include_all(self, _):
        """ Invoked when the user clicks on the include all button. """

        for itm in self._get_items():
            itm.setCheckState(0, Qt.Checked)

    def _exclude_all(self, _):
        """ Invoked when the user clicks on the exclude all button. """

        for itm in self._get_items():
            if not itm.isDisabled():
                itm.setCheckState(0, Qt.Unchecked)
                itm.setExpanded(False)

    def _remove_all(self, _):
        """ Invoked when the use clicks on the remove all button. """

        blocked = self._package_edit.blockSignals(True)
        self._package_edit.clear()
        self._package_edit.blockSignals(blocked)

        self._enable_buttons()

        # This is a bit of a hack but is currently the only way to completely
        # remove the application package.
        if self._show_root:
            self.package.name = ''

        del self.package.contents[:]
        self.package_changed.emit()

    def _enable_buttons(self):
        """ Set the enabled state of those buttons that require content. """

        enable = (len(list(self._get_items())) != 0)

        self._remove_button.setEnabled(enable)
        self._include_button.setEnabled(enable)
        self._exclude_button.setEnabled(enable)

    def _scan(self, _):
        """ Invoked when the user clicks on the scan button. """

        project = self.project
        package = self.package

        # Get the root directory to scan.
        root = self.get_root_dir()
        if root == '':
            return

        # Save the included state of any existing contents so that they can be
        # restored after the scan.
        old_state = {}

        for itm in self._get_items():
            rel_path = [itm.data(0, Qt.DisplayRole)]

            parent = itm.parent()
            while parent is not None:
                rel_path.append(parent.data(0, Qt.DisplayRole))
                parent = parent.parent()

            rel_path.reverse()

            if self._show_root:
                rel_path = rel_path[1:]

            old_state['/'.join(rel_path)] = (itm.checkState(0) == Qt.Checked)

        # Walk the package.
        root_dir = QDir(root)
        if not root_dir.exists():
            QMessageBox.warning(self.parentWidget(), "Scan Directory",
                    "{0} is not a valid directory.".format(
                            QDir.toNativeSeparators(root)))
            return

        self._add_to_container(package, root_dir, [], old_state)
        self._visualise()

        self.package_changed.emit()

    def _add_to_container(self, container, content_dir, dir_stack, old_state):
        """ Add the files and directories of a package or sub-package to a
        container.
        """

        dir_contents = content_dir.entryInfoList(
                QDir.Files|QDir.Dirs|QDir.NoDotAndDotDot)

        # Make sure any filter is applied in a predictable order.
        dir_contents.sort(key=lambda fi: fi.fileName().lower()[1:] if fi.fileName().startswith('_') else fi.fileName().lower())

        dir_stack.append(content_dir.dirName())
        contents = []

        for content in dir_contents:
            name = content.fileName()

            # Apply any exclusions.
            for exc in self.package.exclusions:
                if fnmatch.fnmatch(name, exc):
                    name = None
                    break

            if name is None:
                continue

            # Apply any filter.
            if len(dir_stack) > 1:
                module_path = dir_stack[1:]
                module_path.append(name)
                path_name = '/'.join(module_path)
            else:
                path_name = name

            if self.filter(path_name):
                continue

            # See if we already know the included state.
            included = old_state.get(path_name, False)

            # Add the content.
            if content.isDir():
                qrc = QrcDirectory(name, included)

                self._add_to_container(qrc, QDir(content.canonicalFilePath()),
                        dir_stack, old_state)
            elif content.isFile():
                qrc = QrcFile(name, included)
            else:
                continue

            contents.append(qrc)

        container.contents = contents
        dir_stack.pop()

    def _visualise(self):
        """ Update the GUI with the package content. """

        blocked = self._package_edit.blockSignals(True)

        self._package_edit.clear()

        if self.package.name is not None:
            if self._show_root:
                parent = QTreeWidgetItem([':/' + self.package.name])
                self._package_edit.addTopLevelItem(parent)
                parent.setExpanded(True)
            else:
                parent = self._package_edit

            self._visualise_contents(self.package.contents, parent)

        self._package_edit.blockSignals(blocked)

        self._enable_buttons()

    def _visualise_contents(self, contents, parent):
        """ Visualise the contents for a parent. """

        p = parent
        while p is not None and isinstance(p, QTreeWidgetItem):
            p = p.parent()

        for content in contents:
            itm = QTreeWidgetItem(parent, [content.name])

            itm.setCheckState(0,
                    Qt.Checked if content.included else Qt.Unchecked)

            itm.setData(0, Qt.UserRole, content)

            if isinstance(content, QrcDirectory):
                self._visualise_contents(content.contents, itm)

    def _package_changed(self, itm, column):
        """ Invoked when part of the package changes. """

        if itm.checkState(0) == Qt.Checked:
            itm.data(0, Qt.UserRole).included = True
            itm.setExpanded(True)
        else:
            self._exclude(itm)
            itm.setExpanded(False)

        self.package_changed.emit()

    def _exclude(self, itm):
        """ Exclude an item and any children it may have. """

        for idx in range(itm.childCount()):
            self._exclude(itm.child(idx))

        itm.data(0, Qt.UserRole).included = False
        itm.setCheckState(0, Qt.Unchecked)
class Waterfall(QWidget, waterfall.Ui_Waterfall):
    
    plot_settings_signal = QtCore.pyqtSignal(list) #send list of plotting params
    updated_rectangles_signal = QtCore.pyqtSignal(list) #send list of updated artists for redrawing

    def __init__(self, parent):
        super(Waterfall,self).__init__(parent)
        self.setupUi(self)
        
        self.get_settings()
        #self.send_settings()

        #Button functions
        self.btn_apply_general_settings.clicked.connect(self.send_settings)
        self.btn_apply_keys_and_colors_settings.clicked.connect(self.send_settings)
        self.patient_tree = self.create_patient_tree()
        self.data_viewer_container.addWidget(self.patient_tree)
        self.btn_color_test.clicked.connect(self.get_color)
        
    def get_color(self):
        self.color = QColorDialog.getColor() #returns a color object
        print(color)

    def get_settings(self):
        try:
            with shelve.open('WaterfallSettings') as shelfFile: 
                self.keys_and_colors = shelfFile['keys_and_colors']
                shelfFile.close()
        except:
            #set and use default settings
            self.keys_and_colors = {
                                    'CR':'#03945D',
                                    'PR':'#B1EE97',
                                    'PD':'#FF6F69',
                                    'SD':'#707070'}
            with shelve.open('WaterfallSettings') as shelfFile:
                shelfFile['keys_and_colors'] = self.keys_and_colors
                shelfFile.close()
        
    def on_waterfall_data_signal(self,signal):
        self.waterfall_data = signal['waterfall_data'] #pandas dataframe
        
    def on_generated_rectangles_signal(self,signal):
        self.rectangles_received = signal[0]
        self.add_items() #display in table
        self.btn_apply_general_settings.setEnabled(True)
        self.btn_finalize_plot.setEnabled(True)
        self.btn_apply_keys_and_colors_settings.setEnabled(True)

    def send_settings(self):
        '''
        Emit both general plot settings, and color labeling settings. These are the settings to be used when the plot is created.
        
        '''
        self.general_settings = [
                                        self.plot_title.text(),
                                        self.x_label.text(),
                                        self.y_label.text(),
                                        [self.twenty_percent_line.isChecked(),
                                        self.thirty_percent_line.isChecked(),
                                        self.zero_percent_line.isChecked()],
                                        [self.display_responses_as_text.isChecked(),
                                        self.display_responses_as_color.isChecked(),
                                        self.display_no_responses.isChecked()],
                                        self.include_table.isChecked(),
                                        self.show_cancer_type.isChecked(),
                                        self.get_updated_color_coding()
                                    ]
        self.plot_settings_signal.emit(self.general_settings)

    def create_patient_tree(self):
        '''
        Create QTreeWidget populated with a patient's data for the DataEntry dialog.
        Assumes that self.temp_patient is the patient of interest and that the variable belongs to the dialog.
        '''
        self.tree = QTreeWidget()
        self.root = self.tree.invisibleRootItem()
        self.headers = [
                        'Patient #',
                        'Best response %',
                        'Response',
                        'Cancer',
                        'Color key',
                        ]
        self.headers_item = QTreeWidgetItem(self.headers)
        self.tree.setColumnCount(len(self.headers))
        self.tree.setHeaderItem(self.headers_item)
        self.root.setExpanded(True)
        self.tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.tree.header().setStretchLastSection(False)
        return self.tree

    def add_items(self):
        '''
        Populate viewing tree
        '''
        self.tree.clear() #clear prior to entering items, prevent aggregation
        i=0
        for rect in self.rectangles_received:
            #populate editable tree with rect data
            self.rect_item = QTreeWidgetItem(self.root)
            self.rect_params = [
                                self.waterfall_data['Patient number'][i], 
                                rect.get_height(),
                                self.waterfall_data['Overall response'][i],
                                self.waterfall_data['Cancer'][i]
                                ]
            for col in range(0,4):
                self.rect_item.setText(col,str(self.rect_params[col]))
                self.rect_item.setTextAlignment(col,4)
            self.tree.setItemWidget(self.rect_item, 4, CustomCombo(self,self.keys_and_colors,self.waterfall_data['Overall response'][i]))
            self.rect_item.setFlags(self.rect_item.flags() | QtCore.Qt.ItemIsEditable)
            i+=1

    def get_updated_color_coding(self):
        tmp_updated_color_coding = []
        self.root = self.tree.invisibleRootItem()
        child_count = self.root.childCount()
        #return list of keys (children are iterated in order they were entered, which agrees with order of patient data in waterfall_data lists)
        return [self.tree.itemWidget(self.root.child(i),4).currentText() for i in range(child_count)]

    def on_updated_tree_item(self):
        #update the rectangle which was edited
        pass
Esempio n. 7
0
class NodeSelector(QDialog):
    """ Popup window for creating new nodes """
    def __init__(self, moduleManager, modfilter={}):
        super().__init__()

        self.modMan = moduleManager
        self.modfilter = modfilter

        self.gridLayout = QGridLayout(self)

        self.lineEdit = QLineEdit()
        self.lineEdit.textChanged.connect(self.textChanged)
        self.gridLayout.addWidget(self.lineEdit, 0, 0, 1, 1)

        self.treeWidget = QTreeWidget()
        self.treeWidget.itemActivated.connect(self.itemActivated)
        self.gridLayout.addWidget(self.treeWidget, 1, 0, 1, 1)

        self.data = {"node": None, "pin": None}

        self.rebuildTree()



    def rebuildTree(self):
        # Fill Tree with items
        ## Find all available Categories
        self.treeWidget.clear()
        self.categories = []
        for key in self.modMan.availableNodes:
            if self.modMan.availableNodes[key].placable and self.filterModule(self.modMan.availableNodes[key]):
                self.categories.append(self.modMan.availableNodes[key].category)
        self.categories = list(set(self.categories))  # Make list unique

        self.categoryTreeitems = {}
        for category in self.categories:
            newTreeitem = QTreeWidgetItem()
            newTreeitem.setText(0, category)
            self.treeWidget.addTopLevelItem(newTreeitem)

            self.categoryTreeitems[category] = newTreeitem

        self.moduleDict = {}
        for key in self.modMan.availableNodes:
            if self.modMan.availableNodes[key].placable and self.filterModule(self.modMan.availableNodes[key]): # Todo: Could reuse previous results if performance becomes critical
                newTreeitem = QTreeWidgetItem()
                newTreeitem.setText(0, self.modMan.availableNodes[key].name)
                newTreeitem.setToolTip(0, self.modMan.availableNodes[key].desc)
                self.categoryTreeitems[self.modMan.availableNodes[key].category].addChild(newTreeitem)

                self.moduleDict[key] = newTreeitem

        for key in self.categoryTreeitems:
            self.categoryTreeitems[key].setExpanded(True)


    def filterModule(self, module):
        ratio = 0
        compatibleType = False
        if "type" in self.modfilter:
            if self.modfilter["type"]["dir"] == "input":
                for input in module.inputDefs:
                    if input.pintype == self.modfilter["type"]["type"]:
                        compatibleType = True
                        break
            elif self.modfilter["type"]["dir"] == "output":
                for output in module.outputDefs:
                    if output.pintype == self.modfilter["type"]["type"]:
                        compatibleType = True
                        break

            if not compatibleType:
                return False

        if "text" in self.modfilter:    # Filter by text input
            if self.modfilter["text"] in module.name:
                return True
            if not self.modfilter["text"]:  # Text entry is empty
                return True
            ratio = fuzz.ratio(self.modfilter["text"], module.name)
            ratio = max(ratio, fuzz.partial_ratio(self.modfilter["text"], module.desc))
        else:
            return True     # Don't filter by text? Return all remaining

        if ratio > 40:
            return True
        else:
            return False

    def textChanged(self, newText):
        self.modfilter["text"] = newText
        self.rebuildTree()

    def itemActivated(self, item, column):
        for key in self.moduleDict:
            if self.moduleDict[key] == item:

                if "type" in self.modfilter:
                    if self.modfilter["type"]["dir"] == "input":
                        for input in self.modMan.availableNodes[key].inputDefs:
                            if input.pintype == self.modfilter["type"]["type"]:
                                self.data["pin"] = self.modMan.availableNodes[key].inputDefs.index(input)
                                break
                    elif self.modfilter["type"]["dir"] == "output":
                        for output in self.modMan.availableNodes[key].outputDefs:
                            if output.pintype == self.modfilter["type"]["type"]:
                                self.data["pin"] = self.modMan.availableNodes[key].outputDefs.index(output)
                                break

                self.data["node"] = self.modMan.availableNodes[key]
                self.done(1)
Esempio n. 8
0
class CueListDialog(QDialog):
    def __init__(self, cues=None, properties=('index', 'name'), **kwargs):
        super().__init__(**kwargs)

        self.setMinimumSize(600, 400)

        self._properties = list(properties)
        self._cues = {}

        self.list = QTreeWidget(self)
        self.list.setSelectionMode(QTreeWidget.SingleSelection)
        self.list.setSelectionBehavior(QTreeWidget.SelectRows)
        self.list.setAlternatingRowColors(True)
        self.list.setIndentation(0)
        self.list.setHeaderLabels([prop.title() for prop in properties])
        self.list.header().setSectionResizeMode(QHeaderView.Fixed)
        self.list.header().setSectionResizeMode(1, QHeaderView.Stretch)
        self.list.header().setStretchLastSection(False)
        self.list.sortByColumn(0, Qt.AscendingOrder)
        self.list.setSortingEnabled(True)

        if cues is not None:
            self.add_cues(cues)

        self.setLayout(QVBoxLayout())
        self.layout().addWidget(self.list)

        self.buttons = QDialogButtonBox(self)
        self.buttons.addButton(QDialogButtonBox.Cancel)
        self.buttons.addButton(QDialogButtonBox.Ok)
        self.layout().addWidget(self.buttons)

        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)

    def add_cue(self, cue):
        item = QTreeWidgetItem()
        item.setTextAlignment(0, Qt.AlignCenter)

        for n, prop in enumerate(self._properties):
            try:
                item.setData(n, Qt.DisplayRole, getattr(cue, prop, 'Undefined'))
            except Exception as e:
                logging.exception('Cannot display {0} property'.format(prop), e,
                                  dialog=False)

        self._cues[cue] = item
        item.setData(0, Qt.UserRole, cue)
        self.list.addTopLevelItem(item)

    def add_cues(self, cues):
        self.list.setSortingEnabled(False)
        for cue in cues:
            self.add_cue(cue)
        self.list.setSortingEnabled(True)

    def remove_cue(self, cue):
        index = self.list.indexOfTopLevelItem(self._cues.pop(cue))
        self.list.takeTopLevelItem(index)

    def reset(self):
        self.list.clear()
        self._cues.clear()

    def selected_cues(self):
        cues = []
        for item in self.list.selectedItems():
            cues.append(item.data(0, Qt.UserRole))
        return cues
Esempio n. 9
0
class GuiWritingStats(QDialog):

    C_TIME   = 0
    C_LENGTH = 1
    C_IDLE   = 2
    C_COUNT  = 3
    C_BAR    = 4

    FMT_JSON = 0
    FMT_CSV  = 1

    def __init__(self, mainGui):
        QDialog.__init__(self, mainGui)

        logger.debug("Initialising GuiWritingStats ...")
        self.setObjectName("GuiWritingStats")

        self.mainConf   = novelwriter.CONFIG
        self.mainGui    = mainGui
        self.mainTheme  = mainGui.mainTheme
        self.theProject = mainGui.theProject

        self.logData    = []
        self.filterData = []
        self.timeFilter = 0.0
        self.wordOffset = 0

        pOptions = self.theProject.options

        self.setWindowTitle(self.tr("Writing Statistics"))
        self.setMinimumWidth(self.mainConf.pxInt(420))
        self.setMinimumHeight(self.mainConf.pxInt(400))
        self.resize(
            self.mainConf.pxInt(pOptions.getInt("GuiWritingStats", "winWidth",  550)),
            self.mainConf.pxInt(pOptions.getInt("GuiWritingStats", "winHeight", 500))
        )

        # List Box
        wCol0 = self.mainConf.pxInt(
            pOptions.getInt("GuiWritingStats", "widthCol0", 180)
        )
        wCol1 = self.mainConf.pxInt(
            pOptions.getInt("GuiWritingStats", "widthCol1", 80)
        )
        wCol2 = self.mainConf.pxInt(
            pOptions.getInt("GuiWritingStats", "widthCol2", 80)
        )
        wCol3 = self.mainConf.pxInt(
            pOptions.getInt("GuiWritingStats", "widthCol3", 80)
        )

        self.listBox = QTreeWidget()
        self.listBox.setHeaderLabels([
            self.tr("Session Start"),
            self.tr("Length"),
            self.tr("Idle"),
            self.tr("Words"),
            self.tr("Histogram"),
        ])
        self.listBox.setIndentation(0)
        self.listBox.setColumnWidth(self.C_TIME, wCol0)
        self.listBox.setColumnWidth(self.C_LENGTH, wCol1)
        self.listBox.setColumnWidth(self.C_IDLE, wCol2)
        self.listBox.setColumnWidth(self.C_COUNT, wCol3)

        hHeader = self.listBox.headerItem()
        hHeader.setTextAlignment(self.C_LENGTH, Qt.AlignRight)
        hHeader.setTextAlignment(self.C_IDLE, Qt.AlignRight)
        hHeader.setTextAlignment(self.C_COUNT, Qt.AlignRight)

        sortCol = checkIntRange(pOptions.getInt("GuiWritingStats", "sortCol", 0), 0, 2, 0)
        sortOrder = checkIntTuple(
            pOptions.getInt("GuiWritingStats", "sortOrder", Qt.DescendingOrder),
            (Qt.AscendingOrder, Qt.DescendingOrder), Qt.DescendingOrder
        )
        self.listBox.sortByColumn(sortCol, sortOrder)
        self.listBox.setSortingEnabled(True)

        # Word Bar
        self.barHeight = int(round(0.5*self.mainTheme.fontPixelSize))
        self.barWidth = self.mainConf.pxInt(200)
        self.barImage = QPixmap(self.barHeight, self.barHeight)
        self.barImage.fill(self.palette().highlight().color())

        # Session Info
        self.infoBox = QGroupBox(self.tr("Sum Totals"), self)
        self.infoForm = QGridLayout(self)
        self.infoBox.setLayout(self.infoForm)

        self.labelTotal = QLabel(formatTime(0))
        self.labelTotal.setFont(self.mainTheme.guiFontFixed)
        self.labelTotal.setAlignment(Qt.AlignVCenter | Qt.AlignRight)

        self.labelIdleT = QLabel(formatTime(0))
        self.labelIdleT.setFont(self.mainTheme.guiFontFixed)
        self.labelIdleT.setAlignment(Qt.AlignVCenter | Qt.AlignRight)

        self.labelFilter = QLabel(formatTime(0))
        self.labelFilter.setFont(self.mainTheme.guiFontFixed)
        self.labelFilter.setAlignment(Qt.AlignVCenter | Qt.AlignRight)

        self.novelWords = QLabel("0")
        self.novelWords.setFont(self.mainTheme.guiFontFixed)
        self.novelWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)

        self.notesWords = QLabel("0")
        self.notesWords.setFont(self.mainTheme.guiFontFixed)
        self.notesWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)

        self.totalWords = QLabel("0")
        self.totalWords.setFont(self.mainTheme.guiFontFixed)
        self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)

        lblTTime   = QLabel(self.tr("Total Time:"))
        lblITime   = QLabel(self.tr("Idle Time:"))
        lblFTime   = QLabel(self.tr("Filtered Time:"))
        lblNvCount = QLabel(self.tr("Novel Word Count:"))
        lblNtCount = QLabel(self.tr("Notes Word Count:"))
        lblTtCount = QLabel(self.tr("Total Word Count:"))

        self.infoForm.addWidget(lblTTime,   0, 0)
        self.infoForm.addWidget(lblITime,   1, 0)
        self.infoForm.addWidget(lblFTime,   2, 0)
        self.infoForm.addWidget(lblNvCount, 3, 0)
        self.infoForm.addWidget(lblNtCount, 4, 0)
        self.infoForm.addWidget(lblTtCount, 5, 0)

        self.infoForm.addWidget(self.labelTotal,  0, 1)
        self.infoForm.addWidget(self.labelIdleT,  1, 1)
        self.infoForm.addWidget(self.labelFilter, 2, 1)
        self.infoForm.addWidget(self.novelWords,  3, 1)
        self.infoForm.addWidget(self.notesWords,  4, 1)
        self.infoForm.addWidget(self.totalWords,  5, 1)

        self.infoForm.setRowStretch(6, 1)

        # Filter Options
        sPx = self.mainTheme.baseIconSize

        self.filterBox = QGroupBox(self.tr("Filters"), self)
        self.filterForm = QGridLayout(self)
        self.filterBox.setLayout(self.filterForm)

        self.incNovel = QSwitch(width=2*sPx, height=sPx)
        self.incNovel.setChecked(
            pOptions.getBool("GuiWritingStats", "incNovel", True)
        )
        self.incNovel.clicked.connect(self._updateListBox)

        self.incNotes = QSwitch(width=2*sPx, height=sPx)
        self.incNotes.setChecked(
            pOptions.getBool("GuiWritingStats", "incNotes", True)
        )
        self.incNotes.clicked.connect(self._updateListBox)

        self.hideZeros = QSwitch(width=2*sPx, height=sPx)
        self.hideZeros.setChecked(
            pOptions.getBool("GuiWritingStats", "hideZeros", True)
        )
        self.hideZeros.clicked.connect(self._updateListBox)

        self.hideNegative = QSwitch(width=2*sPx, height=sPx)
        self.hideNegative.setChecked(
            pOptions.getBool("GuiWritingStats", "hideNegative", False)
        )
        self.hideNegative.clicked.connect(self._updateListBox)

        self.groupByDay = QSwitch(width=2*sPx, height=sPx)
        self.groupByDay.setChecked(
            pOptions.getBool("GuiWritingStats", "groupByDay", False)
        )
        self.groupByDay.clicked.connect(self._updateListBox)

        self.showIdleTime = QSwitch(width=2*sPx, height=sPx)
        self.showIdleTime.setChecked(
            pOptions.getBool("GuiWritingStats", "showIdleTime", False)
        )
        self.showIdleTime.clicked.connect(self._updateListBox)

        self.filterForm.addWidget(QLabel(self.tr("Count novel files")),        0, 0)
        self.filterForm.addWidget(QLabel(self.tr("Count note files")),         1, 0)
        self.filterForm.addWidget(QLabel(self.tr("Hide zero word count")),     2, 0)
        self.filterForm.addWidget(QLabel(self.tr("Hide negative word count")), 3, 0)
        self.filterForm.addWidget(QLabel(self.tr("Group entries by day")),     4, 0)
        self.filterForm.addWidget(QLabel(self.tr("Show idle time")),           5, 0)
        self.filterForm.addWidget(self.incNovel,     0, 1)
        self.filterForm.addWidget(self.incNotes,     1, 1)
        self.filterForm.addWidget(self.hideZeros,    2, 1)
        self.filterForm.addWidget(self.hideNegative, 3, 1)
        self.filterForm.addWidget(self.groupByDay,   4, 1)
        self.filterForm.addWidget(self.showIdleTime, 5, 1)
        self.filterForm.setRowStretch(6, 1)

        # Settings
        self.histMax = QSpinBox(self)
        self.histMax.setMinimum(100)
        self.histMax.setMaximum(100000)
        self.histMax.setSingleStep(100)
        self.histMax.setValue(
            pOptions.getInt("GuiWritingStats", "histMax", 2000)
        )
        self.histMax.valueChanged.connect(self._updateListBox)

        self.optsBox = QHBoxLayout()
        self.optsBox.addStretch(1)
        self.optsBox.addWidget(QLabel(self.tr("Word count cap for the histogram")), 0)
        self.optsBox.addWidget(self.histMax, 0)

        # Buttons
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.rejected.connect(self._doClose)

        self.btnClose = self.buttonBox.addButton(QDialogButtonBox.Close)
        self.btnClose.setAutoDefault(False)

        self.btnSave = self.buttonBox.addButton(self.tr("Save As"), QDialogButtonBox.ActionRole)
        self.btnSave.setAutoDefault(False)

        self.saveMenu = QMenu(self)
        self.btnSave.setMenu(self.saveMenu)

        self.saveJSON = QAction(self.tr("JSON Data File (.json)"), self)
        self.saveJSON.triggered.connect(lambda: self._saveData(self.FMT_JSON))
        self.saveMenu.addAction(self.saveJSON)

        self.saveCSV = QAction(self.tr("CSV Data File (.csv)"), self)
        self.saveCSV.triggered.connect(lambda: self._saveData(self.FMT_CSV))
        self.saveMenu.addAction(self.saveCSV)

        # Assemble
        self.outerBox = QGridLayout()
        self.outerBox.addWidget(self.listBox,   0, 0, 1, 2)
        self.outerBox.addLayout(self.optsBox,   1, 0, 1, 2)
        self.outerBox.addWidget(self.infoBox,   2, 0)
        self.outerBox.addWidget(self.filterBox, 2, 1)
        self.outerBox.addWidget(self.buttonBox, 3, 0, 1, 2)
        self.outerBox.setRowStretch(0, 1)

        self.setLayout(self.outerBox)

        logger.debug("GuiWritingStats initialisation complete")

        return

    def populateGUI(self):
        """Populate list box with data from the log file.
        """
        qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
        self._loadLogFile()
        self._updateListBox()
        qApp.restoreOverrideCursor()
        return

    ##
    #  Slots
    ##

    def _doClose(self):
        """Save the state of the window, clear cache, end close.
        """
        self.logData = []

        winWidth     = self.mainConf.rpxInt(self.width())
        winHeight    = self.mainConf.rpxInt(self.height())
        widthCol0    = self.mainConf.rpxInt(self.listBox.columnWidth(0))
        widthCol1    = self.mainConf.rpxInt(self.listBox.columnWidth(1))
        widthCol2    = self.mainConf.rpxInt(self.listBox.columnWidth(2))
        widthCol3    = self.mainConf.rpxInt(self.listBox.columnWidth(3))
        sortCol      = self.listBox.sortColumn()
        sortOrder    = self.listBox.header().sortIndicatorOrder()
        incNovel     = self.incNovel.isChecked()
        incNotes     = self.incNotes.isChecked()
        hideZeros    = self.hideZeros.isChecked()
        hideNegative = self.hideNegative.isChecked()
        groupByDay   = self.groupByDay.isChecked()
        showIdleTime = self.showIdleTime.isChecked()
        histMax      = self.histMax.value()

        pOptions = self.theProject.options
        pOptions.setValue("GuiWritingStats", "winWidth",     winWidth)
        pOptions.setValue("GuiWritingStats", "winHeight",    winHeight)
        pOptions.setValue("GuiWritingStats", "widthCol0",    widthCol0)
        pOptions.setValue("GuiWritingStats", "widthCol1",    widthCol1)
        pOptions.setValue("GuiWritingStats", "widthCol2",    widthCol2)
        pOptions.setValue("GuiWritingStats", "widthCol3",    widthCol3)
        pOptions.setValue("GuiWritingStats", "sortCol",      sortCol)
        pOptions.setValue("GuiWritingStats", "sortOrder",    sortOrder)
        pOptions.setValue("GuiWritingStats", "incNovel",     incNovel)
        pOptions.setValue("GuiWritingStats", "incNotes",     incNotes)
        pOptions.setValue("GuiWritingStats", "hideZeros",    hideZeros)
        pOptions.setValue("GuiWritingStats", "hideNegative", hideNegative)
        pOptions.setValue("GuiWritingStats", "groupByDay",   groupByDay)
        pOptions.setValue("GuiWritingStats", "showIdleTime", showIdleTime)
        pOptions.setValue("GuiWritingStats", "histMax",      histMax)
        pOptions.saveSettings()
        self.close()

        return

    def _saveData(self, dataFmt):
        """Save the content of the list box to a file.
        """
        fileExt = ""
        textFmt = ""

        if dataFmt == self.FMT_JSON:
            fileExt = "json"
            textFmt = self.tr("JSON Data File")
        elif dataFmt == self.FMT_CSV:
            fileExt = "csv"
            textFmt = self.tr("CSV Data File")
        else:
            return False

        # Generate the file name
        saveDir = self.mainConf.lastPath
        if not os.path.isdir(saveDir):
            saveDir = os.path.expanduser("~")

        fileName = "sessionStats.%s" % fileExt
        savePath = os.path.join(saveDir, fileName)

        savePath, _ = QFileDialog.getSaveFileName(
            self, self.tr("Save Data As"), savePath, "%s (*.%s)" % (textFmt, fileExt)
        )
        if not savePath:
            return False

        self.mainConf.setLastPath(savePath)

        # Do the actual writing
        wSuccess = False
        errMsg = ""

        try:
            with open(savePath, mode="w", encoding="utf-8") as outFile:
                if dataFmt == self.FMT_JSON:
                    jsonData = []
                    for _, sD, tT, wD, wA, wB, tI in self.filterData:
                        jsonData.append({
                            "date": sD,
                            "length": tT,
                            "newWords": wD,
                            "novelWords": wA,
                            "noteWords": wB,
                            "idleTime": tI,
                        })
                    json.dump(jsonData, outFile, indent=2)
                    wSuccess = True

                if dataFmt == self.FMT_CSV:
                    outFile.write(
                        '"Date","Length (sec)","Words Changed",'
                        '"Novel Words","Note Words","Idle Time (sec)"\n'
                    )
                    for _, sD, tT, wD, wA, wB, tI in self.filterData:
                        outFile.write(f'"{sD}",{tT:.0f},{wD},{wA},{wB},{tI}\n')
                    wSuccess = True

        except Exception as exc:
            errMsg = formatException(exc)
            wSuccess = False

        # Report to user
        if wSuccess:
            self.mainGui.makeAlert([
                self.tr("{0} file successfully written to:").format(textFmt), savePath
            ], nwAlert.INFO)
        else:
            self.mainGui.makeAlert([
                self.tr("Failed to write {0} file.").format(textFmt), errMsg
            ], nwAlert.ERROR)

        return wSuccess

    ##
    #  Internal Functions
    ##

    def _loadLogFile(self):
        """Load the content of the log file into a buffer.
        """
        logger.debug("Loading session log file")

        self.logData = []
        self.wordOffset = 0

        ttNovel = 0
        ttNotes = 0
        ttTime = 0
        ttIdle = 0

        logFile = os.path.join(self.theProject.projMeta, nwFiles.SESS_STATS)
        if not os.path.isfile(logFile):
            logger.info("This project has no writing stats logfile")
            return False

        try:
            with open(logFile, mode="r", encoding="utf-8") as inFile:
                for inLine in inFile:
                    if inLine.startswith("#"):
                        if inLine.startswith("# Offset"):
                            self.wordOffset = checkInt(inLine[9:].strip(), 0)
                            logger.verbose(
                                "Initial word count when log was started is %d" % self.wordOffset
                            )
                        continue

                    inData = inLine.split()
                    if len(inData) < 6:
                        continue

                    dStart = datetime.fromisoformat(" ".join(inData[0:2]))
                    dEnd   = datetime.fromisoformat(" ".join(inData[2:4]))

                    sIdle = 0
                    if len(inData) > 6:
                        sIdle = checkInt(inData[6], 0)

                    tDiff = dEnd - dStart
                    sDiff = tDiff.total_seconds()
                    ttTime += sDiff
                    ttIdle += sIdle

                    wcNovel = int(inData[4])
                    wcNotes = int(inData[5])
                    ttNovel = wcNovel
                    ttNotes = wcNotes

                    self.logData.append((dStart, sDiff, wcNovel, wcNotes, sIdle))

        except Exception as exc:
            self.mainGui.makeAlert(self.tr(
                "Failed to read session log file."
            ), nwAlert.ERROR, exception=exc)
            return False

        ttWords = ttNovel + ttNotes
        self.labelTotal.setText(formatTime(round(ttTime)))
        self.labelIdleT.setText(formatTime(round(ttIdle)))
        self.novelWords.setText(f"{ttNovel:n}")
        self.notesWords.setText(f"{ttNotes:n}")
        self.totalWords.setText(f"{ttWords:n}")

        return True

    ##
    #  Slots
    ##

    def _updateListBox(self):
        """Load/reload the content of the list box.
        """
        self.listBox.clear()
        self.timeFilter = 0.0

        incNovel     = self.incNovel.isChecked()
        incNotes     = self.incNotes.isChecked()
        hideZeros    = self.hideZeros.isChecked()
        hideNegative = self.hideNegative.isChecked()
        groupByDay   = self.groupByDay.isChecked()
        histMax      = self.histMax.value()

        # Group the data
        if groupByDay:
            tempData = []
            sessDate = None
            sessTime = 0
            sessIdle = 0
            lstNovel = 0
            lstNotes = 0

            for n, (dStart, sDiff, wcNovel, wcNotes, sIdle) in enumerate(self.logData):
                if n == 0:
                    sessDate = dStart.date()
                if sessDate != dStart.date():
                    tempData.append((sessDate, sessTime, lstNovel, lstNotes, sessIdle))
                    sessDate = dStart.date()
                    sessTime = sDiff
                    sessIdle = sIdle
                    lstNovel = wcNovel
                    lstNotes = wcNotes
                else:
                    sessTime += sDiff
                    sessIdle += sIdle
                    lstNovel = wcNovel
                    lstNotes = wcNotes

            if sessDate is not None:
                tempData.append((sessDate, sessTime, lstNovel, lstNotes, sessIdle))

        else:
            tempData = self.logData

        # Calculate Word Diff
        self.filterData = []
        pcTotal = 0
        listMax = 0
        isFirst = True
        for dStart, sDiff, wcNovel, wcNotes, sIdle in tempData:

            wcTotal = 0
            if incNovel:
                wcTotal += wcNovel
            if incNotes:
                wcTotal += wcNotes

            dwTotal = wcTotal - pcTotal
            if hideZeros and dwTotal == 0:
                continue
            if hideNegative and dwTotal < 0:
                pcTotal = wcTotal
                continue

            if isFirst:
                # Subtract the offset from the first list entry
                dwTotal -= self.wordOffset
                dwTotal = max(dwTotal, 1)  # Don't go zero or negative
                isFirst = False

            if groupByDay:
                sStart = dStart.strftime(nwConst.FMT_DSTAMP)
            else:
                sStart = dStart.strftime(nwConst.FMT_TSTAMP)

            self.filterData.append((dStart, sStart, sDiff, dwTotal, wcNovel, wcNotes, sIdle))
            listMax = min(max(listMax, dwTotal), histMax)
            pcTotal = wcTotal

        # Populate the list
        showIdleTime = self.showIdleTime.isChecked()
        for _, sStart, sDiff, nWords, _, _, sIdle in self.filterData:

            if showIdleTime:
                idleEntry = formatTime(sIdle)
            else:
                sRatio = sIdle/sDiff if sDiff > 0.0 else 0.0
                idleEntry = "%d %%" % round(100.0 * sRatio)

            newItem = QTreeWidgetItem()
            newItem.setText(self.C_TIME, sStart)
            newItem.setText(self.C_LENGTH, formatTime(round(sDiff)))
            newItem.setText(self.C_IDLE, idleEntry)
            newItem.setText(self.C_COUNT, f"{nWords:n}")

            if nWords > 0 and listMax > 0:
                theBar = self.barImage.scaled(
                    int(200*min(nWords, histMax)/listMax),
                    self.barHeight,
                    Qt.IgnoreAspectRatio,
                    Qt.FastTransformation
                )
                newItem.setData(self.C_BAR, Qt.DecorationRole, theBar)

            newItem.setTextAlignment(self.C_LENGTH, Qt.AlignRight)
            newItem.setTextAlignment(self.C_IDLE, Qt.AlignRight)
            newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight)
            newItem.setTextAlignment(self.C_BAR, Qt.AlignLeft | Qt.AlignVCenter)

            newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed)
            newItem.setFont(self.C_LENGTH, self.mainTheme.guiFontFixed)
            newItem.setFont(self.C_COUNT, self.mainTheme.guiFontFixed)
            if showIdleTime:
                newItem.setFont(self.C_IDLE, self.mainTheme.guiFontFixed)
            else:
                newItem.setFont(self.C_IDLE, self.mainTheme.guiFont)

            self.listBox.addTopLevelItem(newItem)
            self.timeFilter += sDiff

        self.labelFilter.setText(formatTime(round(self.timeFilter)))

        return True
Esempio n. 10
0
class BookmarksWindow(QDialog):
    """
    A simple UI for showing bookmarks and navigating to them.

    FIXME: For now, this window is tied to a particular lane.
           If your project has more than one lane, then each one
           will have it's own bookmark window, which is kinda dumb.
    """
    def __init__(self, parent, topLevelOperatorView):
        super(BookmarksWindow, self).__init__(parent)
        self.setWindowTitle("Bookmarks")
        self.topLevelOperatorView = topLevelOperatorView
        self.bookmark_tree = QTreeWidget(self)
        self.bookmark_tree.setHeaderLabels(["Location", "Notes"])
        self.bookmark_tree.setSizePolicy(QSizePolicy.Preferred,
                                         QSizePolicy.Preferred)
        self.bookmark_tree.setColumnWidth(0, 200)
        self.bookmark_tree.setColumnWidth(1, 300)

        self.note_edit = QLineEdit(self)
        self.add_bookmark_button = QPushButton("Add Bookmark",
                                               self,
                                               clicked=self.add_bookmark)

        geometry = self.geometry()
        geometry.setSize(QSize(520, 520))
        self.setGeometry(geometry)

        layout = QVBoxLayout()
        layout.addWidget(self.bookmark_tree)
        layout.addWidget(self.note_edit)
        layout.addWidget(self.add_bookmark_button)
        self.setLayout(layout)

        self._load_bookmarks()

        self.bookmark_tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.bookmark_tree.customContextMenuRequested.connect(
            self.showContextMenu)

        self.bookmark_tree.itemDoubleClicked.connect(self._handle_doubleclick)

    def _handle_doubleclick(self, item, col):
        """
        Navigate to the bookmark
        """
        data = item.data(0, Qt.UserRole).toPyObject()
        if data is None:
            return

        (coord, notes) = data
        axes = self.topLevelOperatorView.InputImages.meta.getAxisKeys()
        axes = axes[:-1]  # drop channel
        axes = sorted(axes)
        assert len(axes) == len(coord)
        tagged_coord = dict(list(zip(axes, coord)))
        tagged_location = OrderedDict(list(zip("txyzc", (0, 0, 0, 0, 0))))
        tagged_location.update(tagged_coord)
        t = list(tagged_location.values())[0]
        coord3d = list(tagged_location.values())[1:4]

        self.parent().editor.posModel.time = t
        self.parent().editor.navCtrl.panSlicingViews(coord3d, [0, 1, 2])
        self.parent().editor.posModel.slicingPos = coord3d

    def showContextMenu(self, pos):
        item = self.bookmark_tree.itemAt(pos)
        data = item.data(0, Qt.UserRole).toPyObject()
        if data is None:
            return

        def delete_bookmark():
            (coord, notes) = data
            bookmarks = list(self.topLevelOperatorView.Bookmarks.value)
            i = bookmarks.index((coord, notes))
            bookmarks.pop(i)
            self.topLevelOperatorView.Bookmarks.setValue(bookmarks)
            self._load_bookmarks()

        menu = QMenu(parent=self)
        menu.addAction(QAction("Delete", menu, triggered=delete_bookmark))
        globalPos = self.bookmark_tree.viewport().mapToGlobal(pos)
        menu.exec_(globalPos)
        # selection = menu.exec_( globalPos )
        # if selection is removeLanesAction:
        #    self.removeLanesRequested.emit( self._selectedLanes )

    def add_bookmark(self):
        coord_txyzc = self.parent().editor.posModel.slicingPos5D
        tagged_coord_txyzc = dict(list(zip("txyzc", coord_txyzc)))
        axes = self.topLevelOperatorView.InputImages.meta.getAxisKeys()
        axes = axes[:-1]  # drop channel
        axes = sorted(axes)
        coord = tuple(tagged_coord_txyzc[c] for c in axes)

        notes = str(self.note_edit.text())
        bookmarks = list(self.topLevelOperatorView.Bookmarks.value)
        bookmarks.append((coord, notes))
        self.topLevelOperatorView.Bookmarks.setValue(bookmarks)

        self._load_bookmarks()

    def _load_bookmarks(self):
        self.bookmark_tree.clear()
        lane_index = self.topLevelOperatorView.current_view_index()
        lane_nickname = self.topLevelOperatorView.InputImages.meta.nickname or "Lane {}".format(
            lane_index)
        bookmarks = self.topLevelOperatorView.Bookmarks.value
        group_item = QTreeWidgetItem(self.bookmark_tree, [lane_nickname])

        for coord, notes in bookmarks:
            item = QTreeWidgetItem(group_item, [])
            item.setText(0, str(coord))
            item.setData(0, Qt.UserRole, (coord, notes))
            item.setText(1, notes)

        self.bookmark_tree.expandAll()
class OtherExtensionModulesPage(QWidget):
    """ The GUI for the other extension modules page of a project. """

    # The page's label.
    label = "Other Extension Modules"

    @property
    def project(self):
        """ The project property getter. """

        return self._project

    @project.setter
    def project(self, value):
        """ The project property setter. """

        if self._project != value:
            self._project = value
            self._extension_modules_delegate.set_project(value)
            self._update_page()

    def __init__(self):
        """ Initialise the page. """

        super().__init__()

        self._project = None

        # Create the page's GUI.
        layout = QVBoxLayout()

        self._extension_modules_edit = QTreeWidget(
                whatsThis="This shows a list of additional compiled "
                        "extension modules to be linked with the application. "
                        "<b>Name</b> should be the full (dot separated) "
                        "package name of the extension module. <b>LIBS</b> "
                        "should be the value of the corresponding "
                        "<tt>qmake</tt> variable needed to link the extension "
                        "module. Double-click on an entry to edit or remove "
                        "it. Double-click below the last entry in order to "
                        "add a new one. Values may be prefixed by a platform "
                        "specific <tt>qmake</tt> scope.")
        self._extension_modules_edit.setHeaderLabels(["Name", "LIBS"])
        self._extension_modules_edit.setEditTriggers(
                QTreeWidget.DoubleClicked|QTreeWidget.SelectedClicked|
                QTreeWidget.EditKeyPressed)
        self._extension_modules_edit.setRootIsDecorated(False)
        self._extension_modules_edit.itemChanged.connect(
                self._extension_module_changed)

        self._extension_modules_delegate = FilenameEditorDelegate(
                "Extension Module Directory", directory=True)

        self._extension_modules_edit.setItemDelegateForColumn(1,
                self._extension_modules_delegate)

        layout.addWidget(self._extension_modules_edit)

        self.setLayout(layout)

    def _update_page(self):
        """ Update the page using the current project. """

        project = self.project

        # Set the extension modules.
        self._extension_modules_edit.clear()

        for extension_module in project.other_extension_modules:
            self._add_extension_module_item(extension_module)

        # Add one to be edited to create a new entry.
        self._add_extension_module_item()

    def _add_extension_module_item(self, extension_module=None):
        """ Add a QTreeWidgetItem that holds an exclusion. """

        if extension_module is not None:
            name = extension_module.name
            libs = extension_module.libs
        else:
            name = libs = ''

        itm = QTreeWidgetItem([name, libs])

        itm.setFlags(
                Qt.ItemIsSelectable|Qt.ItemIsEditable|Qt.ItemIsEnabled|
                        Qt.ItemNeverHasChildren)

        self._extension_modules_edit.addTopLevelItem(itm)

    def _extension_module_changed(self, itm, value):
        """ Invoked when an extension module has changed. """

        project = self.project
        em_edit = self._extension_modules_edit

        new_name = itm.data(0, Qt.DisplayRole).strip()
        new_libs = itm.data(1, Qt.DisplayRole).strip()
        itm_index = em_edit.indexOfTopLevelItem(itm)

        if new_name != '' or new_libs != '':
            # See if we have added a new one.
            if itm_index == em_edit.topLevelItemCount() - 1:
                self._add_extension_module_item()
        else:
            # It is empty so remove it.
            em_edit.takeTopLevelItem(itm_index)

        # Save the new extension modules.
        project.other_extension_modules = [
                ExtensionModule(
                        em_edit.topLevelItem(i).data(0, Qt.DisplayRole).strip(),
                        em_edit.topLevelItem(i).data(1, Qt.DisplayRole).strip())
                        for i in range(em_edit.topLevelItemCount() - 1)]

        project.modified = True
Esempio n. 12
0
File: tab.py Progetto: maphew/GDBee
class Tab(QWidget):
    """Tab in the QTableWidget where user executes query and sees the result."""

    # ----------------------------------------------------------------------
    def __init__(self):
        """Initialize Tab with layout and behavior."""
        super(Tab, self).__init__()

        # regex pattern for SQL query block comments
        self.block_comment_re = re.compile(
            r'(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?',
            re.DOTALL | re.MULTILINE)

        main_layout = QVBoxLayout(self)

        # define gdb props
        self.gdb = None
        self.gdb_items = None
        self.gdb_columns_names = None
        self.gdb_schemas = None

        # connected geodatabase path toolbar
        self.connected_gdb_path_label = QLabel('')
        self.connected_gdb_path_label.setTextInteractionFlags(
            Qt.TextSelectableByMouse)
        self.connected_gdb_path_label.setToolTip(
            'Connected geodatabase that queries will be run against')
        self.connected_gdb_path_label.setText(not_connected_to_gdb_message)

        self.browse_to_gdb = QPushButton('Browse')
        self.browse_to_gdb.setShortcut(QKeySequence('Ctrl+B'))
        self.browse_to_gdb.clicked.connect(
            lambda evt, arg=True: self.connect_to_geodatabase(
                evt, triggered_with_browse=True))

        self.gdb_sql_dialect_combobox = QComboBox()
        for dialect in sql_dialects_names:
            self.gdb_sql_dialect_combobox.addItem(dialect)

        self.gdb_browse_toolbar = QToolBar()
        self.gdb_browse_toolbar.setMaximumHeight(50)
        self.gdb_browse_toolbar.addWidget(self.browse_to_gdb)
        self.gdb_browse_toolbar.addWidget(self.connected_gdb_path_label)
        self.gdb_browse_toolbar.addSeparator()
        self.gdb_browse_toolbar.addWidget(self.gdb_sql_dialect_combobox)

        # table with results
        self.table = ResultTable()

        # execute SQL query
        self.execute = QAction('Execute', self)
        self.execute.setShortcuts(
            [QKeySequence('F5'),
             QKeySequence('Ctrl+Return')])
        self.execute.triggered.connect(self.run_query)
        self.addAction(self.execute)

        # enter a SQL query
        self.query = TextEditor()
        self.query.setPlainText('')
        font = self.query.font()
        font.setFamily('Consolas')
        font.setStyleHint(QFont.Monospace)

        # TODO: add line numbers to the text editor
        font.setPointSize(14)
        self.query.setFont(font)
        self.query.setTabStopWidth(20)
        self.highlighter = Highlighter(self.query.document())

        # TODO select block of text - Ctrl+/ and they become comments
        self.completer = Completer()
        self.query.set_completer(self.completer.completer)

        # errors panel to show if query fails to execute properly
        self.errors_panel = QPlainTextEdit()
        font = self.query.font()
        font.setPointSize(12)
        self.errors_panel.setStyleSheet('color:red')
        self.errors_panel.setFont(font)
        self.errors_panel.hide()

        # splitter between the toolbar, query window, and the result set table
        splitter = QSplitter(Qt.Vertical)
        splitter.addWidget(self.gdb_browse_toolbar)
        splitter.addWidget(self.query)
        splitter.addWidget(self.table)
        splitter.addWidget(self.errors_panel)

        # add the settings after the widget have been added
        splitter.setCollapsible(0, True)
        splitter.setCollapsible(1, False)
        splitter.setCollapsible(2, False)
        splitter.setCollapsible(3, False)
        splitter.setStretchFactor(0, 3)
        splitter.setStretchFactor(1, 7)
        splitter.setSizes((100, 200, 300))
        self.table.hide()

        # TOC
        self.toc = QTreeWidget()
        self.toc.setHeaderHidden(True)

        # second splitter between the TOC to the left and the query/table to the
        # right
        toc_splitter = QSplitter(Qt.Horizontal)
        toc_splitter.addWidget(self.toc)
        toc_splitter.addWidget(splitter)
        toc_splitter.setCollapsible(0, True)
        toc_splitter.setSizes((200, 800))  # set the TOC vs data panel

        main_layout.addWidget(toc_splitter)

        margins = QMargins()
        margins.setBottom(10)
        margins.setLeft(10)
        margins.setRight(10)
        margins.setTop(10)
        main_layout.setContentsMargins(margins)

        self.setLayout(main_layout)
        QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
        self.show()

        return

    # ----------------------------------------------------------------------
    def connect_to_geodatabase(self, evt, triggered_with_browse=True):
        """Connect to geodatabase by letting user browse to a gdb folder."""
        if triggered_with_browse:
            gdb_connect_dialog = QFileDialog(self)
            gdb_connect_dialog.setFileMode(QFileDialog.Directory)
            gdb_path = gdb_connect_dialog.getExistingDirectory()

            # TODO: add a filter to show only .gdb folders?
            # https://stackoverflow.com/questions/4893122/filtering-in-qfiledialog
            if gdb_path and gdb_path.endswith('.gdb'):
                self.gdb = Geodatabase(gdb_path)
                if self.gdb.is_valid():
                    self.connected_gdb_path_label.setText(self.gdb.path)
                    self._set_gdb_items_highlight()
                    self._set_gdb_items_complete()
                    self._fill_toc()
                else:
                    msg = QMessageBox()
                    msg.setText('This is not a valid file geodatabase')
                    msg.setWindowTitle('Validation error')
                    msg.setStandardButtons(QMessageBox.Ok)
                    msg.exec_()
        else:
            if self.gdb.is_valid():
                self._set_gdb_items_highlight()
                self._set_gdb_items_complete()

        return

    # ----------------------------------------------------------------------
    def wheelEvent(self, event):  # noqa: N802
        """Override built-in method to handle mouse wheel scrolling.

        Necessary to do when the tab is focused.
        """
        modifiers = QApplication.keyboardModifiers()
        if modifiers == Qt.ControlModifier:
            if event.angleDelta().y() > 0:  # scroll forward
                self.query.zoomIn(1)
            else:
                self.query.zoomOut(1)
        return

    # ----------------------------------------------------------------------
    def run_query(self):
        """Run SQL query and draw the record set and call table drawing."""
        if not self.gdb:
            self.print_sql_execute_errors(not_connected_to_gdb_message)
            return
        try:
            if not self.gdb.is_valid():
                return

            # use the text of what user selected, if none -> need to run the
            # whole query
            part_sql_query = self.query.textCursor().selection().toPlainText()

            if part_sql_query:
                sql_query = part_sql_query
            else:
                sql_query = self.query.toPlainText()

            if sql_query:
                # removing block comments and single line comments
                sql_query = self.block_comment_re.sub(
                    self._strip_block_comments, sql_query)
                sql_query = self._strip_single_comments(sql_query)
            else:
                return

            # TODO: add threading to allow user to cancel a long running query
            QApplication.setOverrideCursor(Qt.WaitCursor)
            start_time = time.time()
            self.gdb.open_connection()
            res, errors = self.gdb.execute_sql(
                sql_query, self.gdb_sql_dialect_combobox.currentText())
            end_time = time.time()
            if errors:
                self.print_sql_execute_errors(errors)

            if res:
                self.table.show()
                self.errors_panel.hide()
                self.draw_result_table(res)
                msg = 'Executed in {exec_time:.1f} secs | {rows} rows'.format(
                    exec_time=end_time - start_time,
                    rows=self.table.table_data.number_layer_rows)
                self.update_app_status_bar(msg)

        except Exception as err:
            print(err)
        finally:
            QApplication.restoreOverrideCursor()
        return

    # ----------------------------------------------------------------------
    def result_should_include_geometry(self):
        """Get the setting defining whether to include the geometry column."""
        try:
            return self.parentWidget().parentWidget().parentWidget(
            ).do_include_geometry.isChecked()
        except BaseException:
            return True

    # ----------------------------------------------------------------------
    def update_app_status_bar(self, message):
        """Update app status bar with the execution result details."""
        try:
            self.parentWidget().parentWidget().parentWidget().statusBar(
            ).showMessage(message)
        except BaseException:
            pass
        return

    # ----------------------------------------------------------------------
    def draw_result_table(self, res):
        """Draw table with the record set received from the geodatabase."""
        geom_col_name = res.GetGeometryColumn(
        )  # shape col was in the sql query
        self.geometry_isin_query = bool(geom_col_name)

        self.table.draw_result(res,
                               show_shapes=bool(
                                   self.result_should_include_geometry()))
        self.table.view.resizeColumnsToContents()
        return

    # ----------------------------------------------------------------------
    def print_sql_execute_errors(self, err):
        """Print to a special panel errors that occurred during execution."""
        self.table.hide()
        self.errors_panel.show()
        self.errors_panel.setPlainText(err)
        return

    # ----------------------------------------------------------------------
    def _set_gdb_items_highlight(self):
        """Set completer and highlight properties for geodatabase items."""
        self.gdb_items = self.gdb.get_items()
        self.highlighter.set_highlight_rules_gdb_items(self.gdb_items, 'Table')

        self.gdb_schemas = self.gdb.get_schemas()
        self.gdb_columns_names = sorted(list(
            set(
                itertools.chain.from_iterable(
                    [i.keys() for i in self.gdb_schemas.values()]))),
                                        key=lambda x: x.lower())

    # ----------------------------------------------------------------------
    def _set_gdb_items_complete(self):
        """Update completer rules to include geodatabase items."""
        self.completer.update_completer_string_list(self.gdb_items +
                                                    self.gdb_columns_names)
        self.highlighter.set_highlight_rules_gdb_items(self.gdb_columns_names,
                                                       'Column')
        return

    # ----------------------------------------------------------------------
    def _fill_toc(self):
        """Fill TOC with geodatabase datasets and columns."""
        self.toc.clear()
        if not self.gdb_items:
            return

        for tbl_name in sorted(self.gdb_items, key=lambda i: i.lower()):
            if tbl_name.islower() or tbl_name.isupper():
                item = QTreeWidgetItem([tbl_name.title()])
            else:
                item = QTreeWidgetItem([tbl_name])
            font = QFont()
            font.setBold(True)
            item.setFont(0, font)

            for col_name, col_type in sorted(
                    self.gdb_schemas[tbl_name].items()):
                if col_name.islower() or col_name.isupper():
                    col_name = col_name.title()

                item_child = QTreeWidgetItem(
                    ['{0} ({1})'.format(col_name, col_type)])
                item.addChild(item_child)
            self.toc.addTopLevelItem(item)
        return

    # ----------------------------------------------------------------------
    def _do_toc_hide_show(self):
        """Hide TOC with tables and columns."""
        if self.toc.isVisible():
            self.toc.setVisible(False)
        else:
            self.toc.setVisible(True)
        return

    # ----------------------------------------------------------------------
    def _strip_block_comments(self, sql_query):
        """Strip the block comments in SQL query."""
        start, mid, end = sql_query.group(1, 2, 3)
        if mid is None:
            # this is a single-line comment
            return ''
        elif start is not None or end is not None:
            # this is a multi-line comment at start/end of a line
            return ''
        elif '\n' in mid:
            # this is a multi-line comment with line break
            return '\n'
        else:
            # this is a multi-line comment without line break
            return ' '

    # ----------------------------------------------------------------------
    def _strip_single_comments(self, sql_query):
        """Strip the single line comments in SQL query."""
        clean_query = []
        for line in sql_query.rstrip().split('\n'):
            clean_line = line.split('--')[0]
            if clean_line:
                clean_query.append(clean_line)
        return ' '.join([line for line in clean_query])
Esempio n. 13
0
class client(QMainWindow):
    def __init__(self):
        super().__init__()
        # self.localPath = os.environ['HOME']
        self.localPath = '/'
        self.serverPath = '/'
        self.linkData = []
        self.serverFileInfo = []
        self.waitingTaskQueue = []
        self.finishedTaskQueue = []
        self.createServerDirQueue = []
        self.downloadingTask = []
        self.connectionNow = {
            'hostname': '',
            'username': '',
            'passwd': '',
            'port': 21
        }
        self.FTP = None
        self.clearFlag = 0
        self.t1 = None
        self.timer = QTimer(self)
        self.lock = threading.Lock()
        self.Mutex = threading.Semaphore(1)
        self.initUI()

    def initUI(self):
        self.setGeometry(100, 100, 1100, 600)
        self.setMinimumWidth(650)
        self.setWindowTitle('ftpClient')
        self.initMenuBar()
        self.initCenterWidget()
        self.show()

    def initMenuBar(self):
        linkManage = QAction('管理连接(&L)', self)
        linkManage.setShortcut('Ctrl+M')
        linkManage.setStatusTip('管理所有连接')
        linkManage.triggered.connect(self.startLinkManageDialog)

        exitAct = QAction('退出(&E)', self)
        exitAct.setShortcut('Ctrl+Q')
        exitAct.setStatusTip('退出程序')
        exitAct.triggered.connect(qApp.quit)

        menuBar = self.menuBar()
        fileMenu = menuBar.addMenu('文件(&F)')
        fileMenu.addAction(linkManage)
        fileMenu.addAction(exitAct)

    def initCenterWidget(self):
        centerWidget = QWidget()

        self.centerBox = QVBoxLayout()
        self.centerBox.setAlignment(Qt.AlignTop)
        self.initQuickLink()

        localWidget = QWidget()
        serverWidget = QWidget()

        localWidgetLayout = QVBoxLayout()
        serverWidgetLayout = QVBoxLayout()

        localLabelLayout = QHBoxLayout()
        serverLabelLayout = QHBoxLayout()

        self.localPathLineEdit = QLineEdit()
        self.serverPathLineEdit = QLineEdit()

        self.localPathLineEdit.setFocusPolicy(Qt.NoFocus)
        self.serverPathLineEdit.setFocusPolicy(Qt.NoFocus)

        self.initLocalFileBox()
        self.initServerFileBox()

        localLabelLayout.addWidget(QLabel('本地文件:'))
        localLabelLayout.addWidget(self.localPathLineEdit)

        serverLabelLayout.addWidget(QLabel('服务器文件:'))
        serverLabelLayout.addWidget(self.serverPathLineEdit)

        localWidgetLayout.addLayout(localLabelLayout)
        localWidgetLayout.addWidget(self.localFileTreeView)
        localWidgetLayout.setContentsMargins(0, 0, 0, 0)

        serverWidgetLayout.addLayout(serverLabelLayout)
        serverWidgetLayout.addWidget(self.serverFileTree)
        serverWidgetLayout.setContentsMargins(0, 0, 0, 0)

        localWidget.setLayout(localWidgetLayout)
        localWidget.setContentsMargins(0, 0, 0, 0)
        serverWidget.setLayout(serverWidgetLayout)
        serverWidget.setContentsMargins(0, 0, 0, 0)

        self.taskQueueTreeWidget = QTreeWidget()
        self.taskQueueTreeWidget.setColumnCount(6)
        self.taskQueueTreeWidget.setHeaderLabels(
            ['文件名', '本地文件夹', '传输方向', ' 远程文件夹', '文件大小', '当前状态'])
        self.taskQueueTreeWidget.setItemDelegate(MyDelegate())

        taskQueueWidget = QWidget()
        taskQueueLayout = QVBoxLayout()
        taskQueueLayout.addWidget(QLabel('任务队列:'))
        taskQueueLayout.addWidget(self.taskQueueTreeWidget)
        taskQueueLayout.setContentsMargins(0, 0, 0, 0)
        taskQueueWidget.setLayout(taskQueueLayout)
        taskQueueWidget.setContentsMargins(0, 0, 0, 0)

        splitter1 = QSplitter(Qt.Horizontal)
        splitter2 = QSplitter(Qt.Vertical)
        splitter3 = QSplitter(Qt.Vertical)
        splitter4 = QSplitter(Qt.Vertical)

        splitter2.addWidget(localWidget)
        splitter2.addWidget(self.localFileTreeWidget)
        splitter3.addWidget(serverWidget)
        splitter3.addWidget(self.serverFileTable)
        splitter1.addWidget(splitter2)
        splitter1.addWidget(splitter3)
        splitter4.addWidget(splitter1)
        splitter4.addWidget(taskQueueWidget)

        self.centerBox.addWidget(splitter4)

        centerWidget.setLayout(self.centerBox)
        self.setCentralWidget(centerWidget)

        self.refreshTableButton.clicked.connect(self.tableRefresh)

    def initQuickLink(self):
        self.hostInput = QLineEdit(self)
        self.userNameInput = QLineEdit(self)
        self.passwdInput = QLineEdit(self)
        self.portInput = QLineEdit('21', self)
        self.quickLoginButton = QPushButton('快速连接', self)
        self.anonymousLoginCheckBox = QCheckBox('匿名连接')
        self.refreshTableButton = QPushButton('刷新文件列表')

        self.passwdInput.setEchoMode(QLineEdit.Password)
        self.portInput.setValidator(QIntValidator(0, 65535))
        self.portInput.setMaximumWidth(40)

        quickLinkBox = QHBoxLayout()
        quickLinkBox.addWidget(QLabel('  主机:', self))
        quickLinkBox.addWidget(self.hostInput)
        quickLinkBox.addWidget(QLabel(' 用户名:', self))
        quickLinkBox.addWidget(self.userNameInput)
        quickLinkBox.addWidget(QLabel(' 密码:', self))
        quickLinkBox.addWidget(self.passwdInput)
        quickLinkBox.addWidget(QLabel(' 端口:', self))
        quickLinkBox.addWidget(self.portInput)
        quickLinkBox.addWidget(self.quickLoginButton)
        quickLinkBox.addWidget(self.anonymousLoginCheckBox)
        quickLinkBox.addWidget(self.refreshTableButton)
        quickLinkBox.addStretch(1)
        self.centerBox.addLayout(quickLinkBox)

        self.quickLoginButton.clicked.connect(self.connectFromQuickLink)
        self.anonymousLoginCheckBox.stateChanged.connect(
            self.quickLinkCheckBoxChanged)

    def initLocalFileBox(self):
        self.localFileTreeView = QTreeView()
        self.localFileTreeWidget = QTreeWidget()

        self.localDirModel = ChangedQDirModel()
        self.localFileTreeView.setModel(self.localDirModel)
        self.localFileTreeView.setColumnWidth(0, 240)
        self.localFileTreeView.setColumnWidth(2, 60)

        self.localFileTreeView.clicked.connect(self.localTreeClicked)

        self.localFileTreeWidget.setColumnCount(4)
        self.localFileTreeWidget.setHeaderLabels(
            ['文件名', '文件大小', '文件类型', '修改时间'])
        self.localFileTreeWidget.setColumnWidth(0, 240)
        self.localFileTreeWidget.setColumnWidth(2, 60)
        self.localFileTreeWidget.setItemDelegate(MyDelegate())

        self.localFileTable = []

        self.localFileRefesh()

        self.localFileTreeWidget.doubleClicked.connect(
            self.localTableDoubleClicked)
        self.localFileTreeWidget.itemPressed.connect(
            self.localTableRightClicked)

    def initServerFileBox(self):
        self.serverFileTree = QTreeWidget()
        self.serverFileTable = QTreeWidget()

        self.serverFileTree.setHeaderLabels(['目录结构'])
        self.serverFileTree.setItemDelegate(MyDelegate())

        self.serverFileTable.setColumnCount(5)
        self.serverFileTable.setHeaderLabels(
            ['文件名', '文件类型', '文件大小', '权限', '修改时间'])
        self.serverFileTable.setColumnWidth(0, 240)
        self.serverFileTable.setColumnWidth(1, 60)
        self.serverFileTable.setColumnWidth(2, 60)
        self.serverFileTable.setColumnWidth(3, 70)
        self.serverFileTable.setItemDelegate(MyDelegate())

        self.serverFileTree.itemExpanded.connect(self.serverFileTreeRefresh)
        self.serverFileTree.itemClicked.connect(self.serverFileTreeClicked)

        self.serverFileTable.doubleClicked.connect(
            self.serverTableDoubleClicked)
        self.serverFileTable.itemPressed.connect(self.serverTableRightClicked)

    def connectFromQuickLink(self):
        hostName = self.hostInput.text()
        userName = self.userNameInput.text()
        passwd = self.passwdInput.text()
        port = int(self.portInput.text())
        with open('linkdata.json', 'r') as f:
            self.linkData = json.load(f)
        data = {
            'hostname': hostName,
            'username': userName,
            'passwd': passwd,
            'port': port,
            'remark': '来自快速连接'
        }
        self.linkData[userName + '@' + hostName + ':' + str(port) + ' ' +
                      '来自快速连接'] = data
        with open('linkdata.json', 'w') as f:
            json.dump(self.linkData, f)

        try:
            self.aNewConnection(hostName, userName, passwd, port)
        except socket.gaierror:
            QMessageBox.information(self, '主机名错误', '主机名错误,请输入正确的主机名',
                                    QMessageBox.Ok, QMessageBox.Ok)
            return
        except ConnectionRefusedError:
            QMessageBox.information(self, '连接出错', '连接失败,请检查是否输入了正确的主机名或端口',
                                    QMessageBox.Ok, QMessageBox.Ok)
            return
        except ftplib.error_perm:
            QMessageBox.information(self, '登陆出错', '登陆失败,请检查是否输入了正确的用户名或密码',
                                    QMessageBox.Ok, QMessageBox.Ok)
            return

    def localFileRefesh(self):
        self.localFileTable.clear()
        self.localFileTreeWidget.clear()

        if self.localPath != '/':
            node = QTreeWidgetItem(self.localFileTreeWidget)
            node.setText(0, '..')
            self.localFileTable.append(node)

        for i in os.listdir(self.localPath):
            node = QTreeWidgetItem(self.localFileTreeWidget)
            node.setText(0, i)
            tempPath = os.path.join(self.localPath, i)
            if os.path.isfile(tempPath):
                node.setText(1, str(os.path.getsize(tempPath)))
                node.setText(2, 'File')
            elif os.path.isdir(tempPath):
                node.setText(1, '')
                node.setText(2, 'Folder')
            elif os.path.islink(tempPath):
                node.setText(1, '')
                node.setText(2, 'Shortcut')
            elif os.path.ismount(tempPath):
                node.setText(1, '')
                node.setText(2, 'Mount')
            try:
                node.setText(3, TimeStampToTime(os.path.getmtime(tempPath)))
            except FileNotFoundError:
                pass
            except PermissionError:
                pass
            self.localFileTable.append(node)

        self.localPathLineEdit.setText(self.localPath)

        for i in self.localFileTable:
            self.localFileTreeWidget.addTopLevelItem(i)

    def serverFileTableRefresh(self):
        self.Mutex.acquire()
        try:
            fileinfo = self.FTP.getdirinfo(self.serverPath)
        except ftplib.error_temp:
            self.reconnect()
            fileinfo = self.FTP.getdirinfo(self.serverPath)
        self.Mutex.release()

        for i in fileinfo:
            node = QTreeWidgetItem(self.serverFileTable)
            node.setText(0, i[0])
            node.setText(1, i[1])
            node.setText(2, i[2])
            node.setText(3, i[3])
            node.setText(4, i[4])
            self.serverFileInfo.append(node)

        self.serverPathLineEdit.setText(self.serverPath)

        for i in self.serverFileInfo:
            self.serverFileTable.addTopLevelItem(i)

    def serverFileTreeRefresh(self, item):
        if self.clearFlag == 0:
            self.clearFlag = 1
            return

        path = item.text(0)
        fatherNode = item
        while fatherNode != self.serverFileTreeRoot:
            fatherNode = fatherNode.parent()
            path = fatherNode.text(0) + '/' + path

        path = path[1:] + '/'

        childrenItemList = item.takeChildren()
        for i in childrenItemList:
            item.removeChild(i)
        self.Mutex.acquire()
        fileinfo = self.FTP.getdirinfo(path)

        for i in fileinfo:
            if i[1] == 'Folder':
                node = QTreeWidgetItem(item)
                node.setText(0, i[0])
                tempinfo = self.FTP.getdirinfo(path + i[0])
                for j in tempinfo:
                    if j[1] == 'Folder':
                        tempnode = QTreeWidgetItem(node)
                        tempnode.setText(0, j[0])
                        node.addChild(tempnode)
                item.addChild(node)
        self.Mutex.release()

    def serverFileTreeClicked(self, item, int_p):
        path = item.text(0)
        fatherNode = item
        while fatherNode != self.serverFileTreeRoot:
            fatherNode = fatherNode.parent()
            path = fatherNode.text(0) + '/' + path
        if path != '/':
            self.serverPath = path[1:]
        else:
            self.serverPath = path

        self.serverFileTable.clear()
        self.serverFileInfo.clear()

        if self.serverPath != '/':
            node = QTreeWidgetItem(self.serverFileTable)
            node.setText(0, '..')
            self.serverFileInfo.append(node)

        self.serverFileTableRefresh()

    def serverTableDoubleClicked(self, index):
        if qApp.mouseButtons() == Qt.RightButton:
            return

        if self.serverFileInfo[index.row()].text(1) == 'File':
            return

        if index.row() == 0:
            if self.serverPath == '/':
                self.localPath = self.serverPath + self.serverFileInfo[0].text(
                    0)
            else:
                tempPath = self.serverPath.split('/')[:-1]
                self.serverPath = ''
                for i in tempPath:
                    self.serverPath = self.serverPath + '/' + i
                if self.serverPath != '/':
                    self.serverPath = self.serverPath[1:]
        else:
            self.serverPath = os.path.join(
                self.serverPath, self.serverFileInfo[index.row()].text(0))

        self.serverFileTable.clear()
        self.serverFileInfo.clear()

        if self.serverPath != '/':
            node = QTreeWidgetItem(self.serverFileTable)
            node.setText(0, '..')
            self.serverFileInfo.append(node)

        self.serverFileTableRefresh()

    def serverTableRightClicked(self, item, int_p):
        if item.text(0) == '..':
            return
        if qApp.mouseButtons() == Qt.RightButton:
            serverMenu = QMenu()
            downLoadFile = QAction('download')
            downLoadFolder = QAction('downloadfolder')
            downLoadFile.triggered.connect(self.downloadFile)
            downLoadFolder.triggered.connect(self.downloadFolder)
            if item.text(1) == 'Folder':
                serverMenu.addAction(downLoadFolder)
            else:
                serverMenu.addAction(downLoadFile)

            serverMenu.exec_(QCursor.pos())

    def downloadFile(self):
        filename = self.serverFileTable.selectedItems()[0].text(0)
        self.lock.acquire()
        try:
            self.waitingTaskQueue.append({
                'filename':
                filename,
                'localpath':
                self.localPath,
                'direction':
                '<--',
                'serverpath':
                self.serverPath,
                'filesize':
                self.serverFileTable.selectedItems()[0].text(2)
            })

        finally:
            self.lock.release()
        self.taskQueueRefresh()
        if len(self.downloadingTask) == 0:
            if self.t1 == None:
                self.t1 = threading.Thread(target=self.taskQueueOpertion)
                self.t1.start()
            else:
                if self.t1.is_alive():
                    pass
                else:
                    self.t1 = threading.Thread(target=self.taskQueueOpertion)
                    self.t1.start()

    def downloadFolder(self):
        folderpath = os.path.join(
            self.serverPath,
            self.serverFileTable.selectedItems()[0].text(0))
        if self.localPath == '/':
            os.mkdir('/' + self.serverFileTable.selectedItems()[0].text(0))
        else:
            os.mkdir(self.localPath + '/' +
                     self.serverFileTable.selectedItems()[0].text(0))
        self.Mutex.acquire()
        self.lock.acquire()
        try:
            self.traversalServerDir(folderpath, self.localPath,
                                    self.serverPath)
        except ftplib.error_temp:
            self.reconnect()
            self.traversalServerDir(folderpath, self.localPath,
                                    self.serverPath)
        finally:
            self.lock.release()
        self.Mutex.release()
        self.taskQueueRefresh()
        if len(self.downloadingTask) == 0:
            if self.t1 == None:
                self.t1 = threading.Thread(target=self.taskQueueOpertion)
                self.t1.start()
            else:
                if self.t1.is_alive():
                    pass
                else:
                    self.t1 = threading.Thread(target=self.taskQueueOpertion)
                    self.t1.start()

    def localTreeClicked(self, index):
        if self.localDirModel.fileInfo(index).isDir():
            self.localPath = self.localDirModel.filePath(index)
        else:
            self.localPath = self.localDirModel.filePath(index)
            tempPath = self.localPath.split('/')[:-1]
            self.localPath = ''
            for i in tempPath:
                self.localPath = self.localPath + '/' + i
            if self.localPath != '/':
                self.localPath = self.localPath[1:]

        self.localFileRefesh()

    def localTableDoubleClicked(self, index):
        if qApp.mouseButtons() == Qt.RightButton:
            return

        if os.path.isdir(
                os.path.join(
                    self.localPath,
                    self.localFileTable[index.row()].text(0))) == False:
            return

        if index.row() == 0:
            if self.localPath == '/':
                self.localPath = self.localPath + self.localFileTable[0].text(
                    0)
            else:
                tempPath = self.localPath.split('/')[:-1]
                self.localPath = ''
                for i in tempPath:
                    self.localPath = self.localPath + '/' + i
                if self.localPath != '/':
                    self.localPath = self.localPath[1:]
        else:
            self.localPath = os.path.join(
                self.localPath, self.localFileTable[index.row()].text(0))

        self.localFileRefesh()

    def localTableRightClicked(self, item, int_p):
        if item.text(0) == '..':
            return
        if qApp.mouseButtons() == Qt.RightButton:
            localMenu = QMenu()
            upLoadFile = QAction('upload')
            upLoadFolder = QAction('uploadfolder')
            upLoadFile.triggered.connect(self.uploadFile)
            upLoadFolder.triggered.connect(self.uploadFolder)
            if item.text(2) == 'Folder':
                localMenu.addAction(upLoadFolder)
            else:
                localMenu.addAction(upLoadFile)

            localMenu.exec_(QCursor.pos())

    def uploadFile(self):
        filename = self.localFileTreeWidget.selectedItems()[0].text(0)
        self.lock.acquire()
        try:
            self.waitingTaskQueue.append({
                'filename':
                filename,
                'localpath':
                self.localPath,
                'direction':
                '-->',
                'serverpath':
                self.serverPath,
                'filesize':
                self.localFileTreeWidget.selectedItems()[0].text(1)
            })

        finally:
            self.lock.release()
        self.taskQueueRefresh()
        if len(self.downloadingTask) == 0:
            if self.t1 == None:
                self.t1 = threading.Thread(target=self.taskQueueOpertion)
                self.t1.start()
            else:
                if self.t1.is_alive():
                    pass
                else:
                    self.t1 = threading.Thread(target=self.taskQueueOpertion)
                    self.t1.start()

    def uploadFolder(self):
        folderpath = os.path.join(
            self.localPath,
            self.localFileTreeWidget.selectedItems()[0].text(0))
        self.lock.acquire()
        try:
            self.traversalLocalDir(folderpath, self.localPath, self.serverPath)
        except ftplib.error_temp:
            self.reconnect()
            self.traversalLocalDir(folderpath, self.localPath, self.serverPath)
        finally:
            self.lock.release()
        self.taskQueueRefresh()

        if len(self.downloadingTask) == 0:
            if self.t1 == None:
                self.t1 = threading.Thread(target=self.taskQueueOpertion)
                self.t1.start()
            else:
                if self.t1.is_alive():
                    pass
                else:
                    self.t1 = threading.Thread(target=self.taskQueueOpertion)
                    self.t1.start()

    def taskQueueOpertion(self):

        while len(self.createServerDirQueue) != 0:
            self.FTP.mkd(self.createServerDirQueue[0])
            self.createServerDirQueue.pop(0)

        while len(self.waitingTaskQueue) != 0:
            self.lock.acquire()
            try:
                self.downloadingTask.append(self.waitingTaskQueue[0])
                print('taskadded' + str(self.waitingTaskQueue[0]))
                self.waitingTaskQueue.pop(0)
                print('self.waitingTaskQueue.pop(0)')
            finally:
                self.lock.release()
            print('before self.taskQueueRefresh()')
            self.Mutex.acquire()
            if self.downloadingTask[0]['direction'] == '<--':
                if ' ' in self.downloadingTask[0]['filename']:
                    localName = self.downloadingTask[0]['filename'].replace(
                        ' ', '_')
                else:
                    localName = self.downloadingTask[0]['filename']
                with open(
                        self.downloadingTask[0]['localpath'] + '/' + localName,
                        'wb') as fp:
                    if self.downloadingTask[0]['serverpath'] == '/':
                        tempserverpath = '/' + self.downloadingTask[0][
                            'filename']
                    else:
                        tempserverpath = self.downloadingTask[0][
                            'serverpath'] + '/' + self.downloadingTask[0][
                                'filename']
                    try:
                        self.FTP.retrbinary('RETR ' + tempserverpath, fp.write,
                                            10240)
                    except ftplib.error_temp:
                        self.reconnect()
                        self.FTP.retrbinary('RETR ' + tempserverpath, fp.write,
                                            10240)
                    self.FTP.set_debuglevel(0)
            elif self.downloadingTask[0]['direction'] == '-->':
                if self.downloadingTask[0]['serverpath'] == '/':
                    tempserverpath = '/' + self.downloadingTask[0]['filename']
                else:
                    tempserverpath = self.downloadingTask[0][
                        'serverpath'] + '/' + self.downloadingTask[0][
                            'filename']
                with open(
                        self.downloadingTask[0]['localpath'] + '/' +
                        self.downloadingTask[0]['filename'], 'rb') as fp:
                    try:
                        self.FTP.storbinary('STOR ' + tempserverpath, fp,
                                            10240)
                    except ftplib.error_temp:
                        self.reconnect()
                        self.FTP.storbinary('STOR ' + tempserverpath, fp,
                                            10240)
                    self.FTP.set_debuglevel(0)
            self.Mutex.release()
            self.lock.acquire()
            try:
                self.finishedTaskQueue.insert(0, self.downloadingTask[0])
                print('finish' + str(self.downloadingTask[0]))
                self.downloadingTask.clear()
                print('downloadingTask.clear()')
            finally:
                self.lock.release()

            print('after self.taskQueueRefresh()')

    def startLinkManageDialog(self):
        with open('linkdata.json', 'r') as f:
            self.linkData = json.load(f)
        self.linkManageDialog = QDialog()
        self.linkManageDialog.setModal(True)
        linkManageLayout = QVBoxLayout()
        self.linkManageDialog.setLayout(linkManageLayout)
        self.linkManageDialog.setWindowTitle('连接管理')

        linkDisplayLayout = QHBoxLayout()
        bottomButtomGroupLayout = QHBoxLayout()

        connectButtom = QPushButton('连接')
        confirmButtom = QPushButton('确定')
        cancleButtom = QPushButton('取消')

        bottomButtomGroupLayout.addStretch(1)
        bottomButtomGroupLayout.addWidget(connectButtom)
        bottomButtomGroupLayout.addWidget(confirmButtom)
        bottomButtomGroupLayout.addWidget(cancleButtom)

        linkManageLayout.addLayout(linkDisplayLayout)
        linkManageLayout.addLayout(bottomButtomGroupLayout)

        linkListLayout = QVBoxLayout()
        linkEditLayout = QVBoxLayout()

        linkDisplayLayout.addLayout(linkListLayout)
        linkDisplayLayout.addLayout(linkEditLayout)

        self.linkList = QListWidget()
        addLinkButton = QPushButton('新建')
        removeLinkButton = QPushButton('删除')
        linkManageButtonGroupLayout = QHBoxLayout()
        linkManageButtonGroupLayout.addWidget(addLinkButton)
        linkManageButtonGroupLayout.addWidget(removeLinkButton)

        linkListLayout.addWidget(QLabel('连接列表:'), 0, Qt.AlignTop)
        linkListLayout.addWidget(self.linkList)
        linkListLayout.addLayout(linkManageButtonGroupLayout)

        hBox1 = QHBoxLayout()
        hBox2 = QHBoxLayout()
        hBox3 = QHBoxLayout()
        hBox4 = QHBoxLayout()
        hBox5 = QHBoxLayout()
        hBox6 = QHBoxLayout()

        self.host = QLineEdit()
        self.userName = QLineEdit()
        self.passwd = QLineEdit()
        self.port = QLineEdit()
        self.remark = QLineEdit()
        self.passwd.setEchoMode(QLineEdit.Password)
        self.port.setValidator(QIntValidator(0, 65535))
        self.anonymousLogin = QCheckBox('匿名登录')
        confirmEdit = QPushButton('确定修改')
        confirmEdit.setFixedWidth(80)

        self.anonymousLogin.stateChanged.connect(
            self.linkManageCheckBoxChanged)

        hBox1.addWidget(QLabel('主机:   '))
        hBox1.addWidget(self.host)
        hBox2.addWidget(QLabel('用户名:'))
        hBox2.addWidget(self.userName)
        hBox3.addWidget(QLabel('密码:   '))
        hBox3.addWidget(self.passwd)
        hBox4.addWidget(QLabel('端口:   '))
        hBox4.addWidget(self.port)
        hBox6.addWidget(QLabel('备注:   '))
        hBox6.addWidget(self.remark)
        hBox5.addWidget(self.anonymousLogin)
        hBox5.addWidget(confirmEdit, Qt.AlignRight)

        linkEditLayout.addLayout(hBox1)
        linkEditLayout.addLayout(hBox2)
        linkEditLayout.addLayout(hBox3)
        linkEditLayout.addLayout(hBox4)
        linkEditLayout.addLayout(hBox6)
        linkEditLayout.addLayout(hBox5)

        for key in self.linkData:
            item = QListWidgetItem(self.linkList)
            item.setText(key)

        self.linkList.setCurrentRow(0)
        if len(self.linkData) != 0:
            tempdata = self.linkData[self.linkList.currentItem().text()]

            self.host.setText(tempdata['hostname'])
            self.port.setText(str(tempdata['port']))
            self.remark.setText(tempdata['remark'])
            if tempdata['username'] == 'anonymous':
                self.anonymousLogin.setCheckState(Qt.Checked)
            else:
                self.userName.setText(tempdata['username'])
                self.passwd.setText(tempdata['passwd'])

        cancleButtom.clicked.connect(self.linkManageDialog.close)
        self.linkList.itemClicked.connect(self.listItemClicked)
        addLinkButton.clicked.connect(self.addNewLink)
        confirmEdit.clicked.connect(self.confirmEditLink)
        confirmButtom.clicked.connect(self.saveData)
        removeLinkButton.clicked.connect(self.removeLink)
        connectButtom.clicked.connect(self.connectFromDialog)

        self.linkManageDialog.show()

    def connectFromDialog(self):
        hostName = self.host.text()
        userName = self.userName.text()
        passwd = self.passwd.text()
        port = int(self.port.text())

        try:
            self.aNewConnection(hostName, userName, passwd, port)
        except socket.gaierror:
            QMessageBox.information(self, '主机名错误', '主机名错误,请输入正确的主机名',
                                    QMessageBox.Ok, QMessageBox.Ok)
            return
        except ConnectionRefusedError:
            QMessageBox.information(self, '连接出错', '连接失败,请检查是否输入了正确的主机名或端口',
                                    QMessageBox.Ok, QMessageBox.Ok)
            return
        except ftplib.error_perm:
            QMessageBox.information(self, '登陆出错', '登陆失败,请检查是否输入了正确的用户名或密码',
                                    QMessageBox.Ok, QMessageBox.Ok)
            return
        self.saveData()

    def removeLink(self):
        if len(self.linkData) == 0:
            return

        rowNow = self.linkList.currentRow()
        itemNow = self.linkList.currentItem()

        self.linkData.pop(itemNow.text())
        self.linkList.removeItemWidget(itemNow)

        self.linkList.clear()

        for key in self.linkData:
            item = QListWidgetItem(self.linkList)
            item.setText(key)

        if len(self.linkData) == 0:
            self.host.setText('')
            self.port.setText('')
            self.anonymousLogin.setCheckState(Qt.Unchecked)
            self.userName.setText('')
            self.passwd.setText('')
            self.remark.setText('')
            return
        elif len(self.linkData) < rowNow + 1:
            rowNow = len(self.linkData) - 1
            self.linkList.setCurrentRow(len(self.linkData) - 1)
        else:
            self.linkList.setCurrentRow(rowNow)

        self.listItemClicked(self.linkList.currentItem())

    def saveData(self):
        self.confirmEditLink()
        with open('linkdata.json', 'w') as f:
            json.dump(self.linkData, f)
        self.linkManageDialog.close()

    def confirmEditLink(self):
        hostName = self.host.text()
        userName = self.userName.text()
        passwd = self.passwd.text()
        port = int(self.port.text())
        remark = self.remark.text()

        data = {
            'hostname': hostName,
            'username': userName,
            'passwd': passwd,
            'port': port,
            "remark": remark
        }

        self.linkData.pop(self.linkList.currentItem().text())
        self.linkData[userName + '@' + hostName + ':' + str(port) + ' ' +
                      remark] = data

        self.linkList.clear()

        for key in self.linkData:
            item = QListWidgetItem(self.linkList)
            item.setText(key)

        self.linkList.setCurrentRow(len(self.linkList) - 1)

    def addNewLink(self):
        if '新连接' in self.linkData:
            return
        self.linkData['新连接'] = {
            'hostname': '',
            'username': '',
            'passwd': '',
            'port': '',
            'remark': ''
        }
        item = QListWidgetItem(self.linkList)
        item.setText('新连接')
        self.linkList.setCurrentRow(len(self.linkList) - 1)

        self.host.setText('')
        self.port.setText('21')
        self.anonymousLogin.setCheckState(Qt.Unchecked)
        self.userName.setText('')
        self.passwd.setText('')
        self.remark.setText('')

    def listItemClicked(self, item):
        tempdata = self.linkData[item.text()]

        self.host.setText(tempdata['hostname'])
        self.port.setText(str(tempdata['port']))
        self.remark.setText(tempdata['remark'])
        if tempdata['username'] == 'anonymous':
            self.anonymousLogin.setCheckState(Qt.Checked)
        else:
            self.anonymousLogin.setCheckState(Qt.Unchecked)
            self.userName.setText(tempdata['username'])
            self.passwd.setText(tempdata['passwd'])

    def quickLinkCheckBoxChanged(self):
        if self.anonymousLoginCheckBox.checkState() == Qt.Checked:
            self.userNameInput.setText('anonymous')
            self.passwdInput.setText('')
            self.userNameInput.setEnabled(False)
            self.passwdInput.setEnabled(False)
        elif self.anonymousLoginCheckBox.checkState() == Qt.Unchecked:
            self.userNameInput.setText('')
            self.passwdInput.setText('')
            self.userNameInput.setEnabled(True)
            self.passwdInput.setEnabled(True)

    def linkManageCheckBoxChanged(self):
        if self.anonymousLogin.checkState() == Qt.Checked:
            self.userName.setText('anonymous')
            self.passwd.setText('')
            self.userName.setEnabled(False)
            self.passwd.setEnabled(False)
        elif self.anonymousLogin.checkState() == Qt.Unchecked:
            self.userName.setText('')
            self.passwd.setText('')
            self.userName.setEnabled(True)
            self.passwd.setEnabled(True)

    def aNewConnection(self, host, username, passwd, port):
        if self.FTP != None:
            try:
                self.FTP.quit()
            except AttributeError:
                pass
            except EOFError:
                pass
        self.FTP = myFtp()
        self.FTP.set_pasv(True)
        self.connectionNow['hostname'] = host
        self.connectionNow['username'] = username
        self.connectionNow['passwd'] = passwd
        self.connectionNow['port'] = port
        self.FTP.connect(host, port)
        self.FTP.login(username, passwd)

        self.serverFileInfo = []
        self.serverPath = '/'
        self.serverFileTable.clear()
        self.serverFileTree.clear()

        self.serverFileTreeRoot = QTreeWidgetItem(self.serverFileTree)
        self.serverFileTreeRoot.setText(0, '/')
        self.serverFileTree.addTopLevelItem(self.serverFileTreeRoot)

        self.Mutex.acquire()
        fileinfo = self.FTP.getdirinfo(self.serverPath)

        for i in fileinfo:
            if i[1] == 'Folder':
                node = QTreeWidgetItem(self.serverFileTreeRoot)
                node.setText(0, i[0])
                tempinfo = self.FTP.getdirinfo(self.serverPath + i[0])
                for j in tempinfo:
                    if j[1] == 'Folder':
                        tempnode = QTreeWidgetItem(node)
                        tempnode.setText(0, j[0])
                        node.addChild(tempnode)
                self.serverFileTreeRoot.addChild(node)
        self.Mutex.release()

        self.serverFileTreeRoot.setExpanded(True)

        self.serverFileTableRefresh()

        if self.timer.isActive():
            self.timer.disconnect()

        self.timer.timeout.connect(self.taskQueueRefresh)
        self.timer.start(500)

    def reconnect(self):
        self.FTP.connect(self.connectionNow['hostname'],
                         self.connectionNow['port'])
        self.FTP.login(self.connectionNow['username'],
                       self.connectionNow['passwd'])

    def taskQueueRefresh(self):
        self.lock.acquire()
        try:
            self.taskQueueTreeWidget.clear()
            if len(self.downloadingTask) != 0:
                node = QTreeWidgetItem(self.taskQueueTreeWidget)
                node.setText(0, self.downloadingTask[0]['filename'])
                node.setText(1, self.downloadingTask[0]['localpath'])
                node.setText(2, self.downloadingTask[0]['direction'])
                node.setText(3, self.downloadingTask[0]['serverpath'])
                node.setText(4, self.downloadingTask[0]['filesize'])
                node.setText(5, '正在传输')
                self.taskQueueTreeWidget.addTopLevelItem(node)

            for i in self.waitingTaskQueue:
                node = QTreeWidgetItem(self.taskQueueTreeWidget)
                node.setText(0, i['filename'])
                node.setText(1, i['localpath'])
                node.setText(2, i['direction'])
                node.setText(3, i['serverpath'])
                node.setText(4, i['filesize'])
                node.setText(5, '等待传输')
                self.taskQueueTreeWidget.addTopLevelItem(node)

            for i in self.finishedTaskQueue:
                node = QTreeWidgetItem(self.taskQueueTreeWidget)
                node.setText(0, i['filename'])
                node.setText(1, i['localpath'])
                node.setText(2, i['direction'])
                node.setText(3, i['serverpath'])
                node.setText(4, i['filesize'])
                node.setText(5, '传输完成')
                self.taskQueueTreeWidget.addTopLevelItem(node)

        finally:
            self.lock.release()

    def tableRefresh(self):
        if self.connectionNow['hostname'] != '':
            self.serverFileTable.clear()
            self.serverFileInfo.clear()

            if self.serverPath != '/':
                node = QTreeWidgetItem(self.serverFileTable)
                node.setText(0, '..')
                self.serverFileInfo.append(node)

            self.serverFileTableRefresh()

        self.localFileRefesh()

    def traversalLocalDir(self, dir, localpath, serverpath):
        fs = os.listdir(dir)
        for i in fs:
            temppath = os.path.join(dir, i)
            if os.path.isdir(temppath) == False:
                if serverpath == '/' and localpath == '/':
                    print(i + ' ' + dir + ' ' + dir + ' ' +
                          str(os.path.getsize(temppath)))
                    self.waitingTaskQueue.append({
                        'filename':
                        i,
                        'localpath':
                        dir,
                        'direction':
                        '-->',
                        'serverpath':
                        dir,
                        'filesize':
                        str(os.path.getsize(temppath))
                    })
                elif serverpath == '/':
                    self.waitingTaskQueue.append({
                        'filename':
                        i,
                        'localpath':
                        dir,
                        'direction':
                        '-->',
                        'serverpath':
                        dir[len(localpath):],
                        'filesize':
                        str(os.path.getsize(temppath))
                    })
                    print(i + ' ' + dir + ' ' + dir[len(localpath):] + ' ' +
                          str(os.path.getsize(temppath)))
                elif localpath == '/':
                    self.waitingTaskQueue.append({
                        'filename':
                        i,
                        'localpath':
                        dir,
                        'direction':
                        '-->',
                        'serverpath':
                        serverpath + dir,
                        'filesize':
                        str(os.path.getsize(temppath))
                    })
                    print(i + ' ' + dir + ' ' + serverpath + dir + ' ' +
                          str(os.path.getsize(temppath)))
                else:
                    self.waitingTaskQueue.append({
                        'filename':
                        i,
                        'localpath':
                        dir,
                        'direction':
                        '-->',
                        'serverpath':
                        serverpath + dir[len(localpath):],
                        'filesize':
                        str(os.path.getsize(temppath))
                    })
                    print(i + ' ' + dir + ' ' + serverpath +
                          dir[len(localpath):] + ' ' +
                          str(os.path.getsize(temppath)))
            else:
                if serverpath == '/' and localpath == '/':
                    print(i + ' ' + temppath + ' ' + temppath)
                    self.createServerDirQueue.append(temppath)
                elif serverpath == '/':
                    print(i + ' ' + temppath + ' ' + temppath[len(localpath):])
                    self.createServerDirQueue.append(temppath[len(localpath):])
                elif localpath == '/':
                    print(i + ' ' + temppath + ' ' + serverpath + temppath)
                    self.createServerDirQueue.append(serverpath + temppath)
                else:
                    print(i + ' ' + temppath + ' ' + serverpath +
                          temppath[len(localpath):])
                    self.createServerDirQueue.append(serverpath +
                                                     temppath[len(localpath):])
                self.traversalLocalDir(temppath, localpath, serverpath)

    def traversalServerDir(self, dir, localpath, serverpath):
        fs = self.FTP.getdirinfo(dir)
        for i in fs:
            temppath = os.path.join(dir, i[0])
            if i[1] == 'File':
                if serverpath == '/' and localpath == '/':
                    print(i[0] + ' ' + dir + ' ' + dir + ' ' + i[2])
                    self.waitingTaskQueue.append({
                        'filename': i[0],
                        'localpath': dir,
                        'direction': '<--',
                        'serverpath': dir,
                        'filesize': i[2]
                    })
                elif serverpath == '/':
                    self.waitingTaskQueue.append({
                        'filename': i[0],
                        'localpath': localpath + dir,
                        'direction': '<--',
                        'serverpath': dir,
                        'filesize': i[2]
                    })
                    print(i[0] + ' ' + localpath + dir + ' ' + dir + ' ' +
                          i[2])
                elif localpath == '/':
                    self.waitingTaskQueue.append({
                        'filename':
                        i[0],
                        'localpath':
                        dir[len(serverpath):],
                        'direction':
                        '<--',
                        'serverpath':
                        dir,
                        'filesize':
                        i[2]
                    })
                    print(i[0] + ' ' + dir[len(serverpath):] + ' ' + dir +
                          ' ' + i[2])
                else:
                    self.waitingTaskQueue.append({
                        'filename':
                        i[0],
                        'localpath':
                        localpath + dir[len(serverpath):],
                        'direction':
                        '<--',
                        'serverpath':
                        dir,
                        'filesize':
                        i[2]
                    })
                    print(i[0] + ' ' + localpath + dir[len(serverpath):] +
                          ' ' + dir + ' ' + i[2])
            else:
                if serverpath == '/' and localpath == '/':
                    print(i[0] + ' ' + temppath + ' ' + temppath)
                    os.makedirs(temppath)
                elif serverpath == '/':
                    print(i[0] + ' ' + localpath + temppath + ' ' + temppath)
                    os.makedirs(localpath + temppath)
                elif localpath == '/':
                    print(i[0] + ' ' + temppath[len(serverpath):] + ' ' +
                          temppath)
                    os.makedirs(temppath[len(serverpath):])
                else:
                    print(i[0] + ' ' + localpath + temppath[len(serverpath):] +
                          ' ' + temppath)
                    os.makedirs(localpath + temppath[len(serverpath):])
                self.traversalServerDir(temppath, localpath, serverpath)
Esempio n. 14
0
class FileReaderPanel(ToolInstance):
    SESSION_ENDURING = False
    SESSION_SAVE = False
    help = "https://github.com/QChASM/SEQCROW/wiki/Model-Manager-Tool"

    NAME_COL = 0
    ID_COL = 1
    COORDSETS_COL = 2
    NRG_COL = 3
    FREQ_COL = 4

    def __init__(self, session, name):
        super().__init__(session, name)

        self.display_name = "SEQCROW Models"

        self.tool_window = MainToolWindow(self)

        self._build_ui()
        self.fill_tree()

        self._fr_change = self.session.filereader_manager.triggers.add_handler(
            FILEREADER_CHANGE, lambda *args: self.fill_tree(*args))
        self._add_models = self.session.triggers.add_handler(
            ADD_MODELS, lambda *args: self.fill_tree(*args))
        self._molid_change = self.session.triggers.add_handler(
            MODEL_ID_CHANGED, lambda *args: self.fill_tree(*args))
        self._molname_change = self.session.triggers.add_handler(
            MODEL_NAME_CHANGED, lambda *args: self.fill_tree(*args))

    def _build_ui(self):
        layout = QGridLayout()

        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        #TODO: make buttons disabled/enabled if items are selected that don't have the info
        self.tree = QTreeWidget()
        self.tree.setSelectionMode(QTreeWidget.ExtendedSelection)
        self.tree.setHeaderLabels(
            ["Name", "ID", "movie", "energy", "frequencies"])
        self.tree.setUniformRowHeights(True)

        self.tree.setColumnWidth(self.NAME_COL, 200)
        layout.addWidget(self.tree, 0, 0, 3, 1)

        restore_button = QPushButton("restore")
        restore_button.clicked.connect(self.restore_selected)
        layout.addWidget(restore_button, 0, 1)

        nrg_plot_button = QPushButton("energy plot")
        nrg_plot_button.clicked.connect(self.open_nrg_plot)
        layout.addWidget(nrg_plot_button, 1, 1)

        coordset_slider_button = QPushButton("movie slider")
        coordset_slider_button.clicked.connect(self.open_movie_slider)
        layout.addWidget(coordset_slider_button, 2, 1)

        self.tool_window.ui_area.setLayout(layout)

        self.tool_window.manage(placement="side")

    def fill_tree(self, *args):
        item_stack = [self.tree.invisibleRootItem()]

        self.tree.clear()
        self._items = []

        fr_dict = self.session.filereader_manager.filereader_dict

        for model in fr_dict.keys():
            id = model.id
            if id is None:
                continue

            name = model.name
            parent = item_stack[0]
            item = QTreeWidgetItem(parent)
            item._model = model
            item_stack.append(item)
            self._items.append(item)

            item.setData(self.NAME_COL, Qt.DisplayRole, model)
            item.setText(self.NAME_COL, name)
            item.setText(self.ID_COL, ".".join([str(x) for x in id]))

            if any(x.all_geom is not None and len(x.all_geom) > 1
                   for x in fr_dict[model]):
                item.setText(self.COORDSETS_COL, "yes")
            else:
                item.setText(self.COORDSETS_COL, "no")

            if any("energy" in x.other for x in fr_dict[model]):
                item.setText(self.NRG_COL, "yes")
            else:
                item.setText(self.NRG_COL, "no")

            if any("frequency" in x.other for x in fr_dict[model]):
                item.setText(self.FREQ_COL, "yes")
            else:
                item.setText(self.FREQ_COL, "no")

            for fr in fr_dict[model]:
                child = QTreeWidgetItem(item)
                child.setData(self.NAME_COL, Qt.DisplayRole, fr)
                child.setText(self.NAME_COL, fr.name)
                if fr.all_geom is not None and len(fr.all_geom) > 1:
                    child.setText(self.COORDSETS_COL, "yes")
                else:
                    child.setText(self.COORDSETS_COL, "no")

                if "energy" in fr.other:
                    child.setText(self.NRG_COL, "%.6f" % fr.other["energy"])
                else:
                    child.setText(self.NRG_COL, "")

                if "frequency" in fr.other:
                    child.setText(self.FREQ_COL, "yes")
                else:
                    child.setText(self.FREQ_COL, "no")

            #self.tree.expandItem(item)

        for i in [
                self.ID_COL, self.COORDSETS_COL, self.NRG_COL, self.FREQ_COL
        ]:
            self.tree.resizeColumnToContents(i)

    def restore_selected(self):
        items = [item for item in self.tree.selectedItems()]
        model_dict = self.session.filereader_manager.filereader_dict
        models = list(model_dict.keys())
        for item in items:
            parent = item.parent()
            mdl = models[self.tree.indexOfTopLevelItem(parent)]
            if parent is None:
                fr = model_dict[mdl][-1]
            else:
                fr = model_dict[mdl][parent.indexOfChild(item)]

            fr_rescol = ResidueCollection(fr)
            fr_rescol.update_chix(mdl)
            if fr.all_geom is not None and len(fr.all_geom) > 1:
                coordsets = fr_rescol.all_geom_coordsets(fr)

                mdl.remove_coordsets()
                mdl.add_coordsets(coordsets)

                for i, coordset in enumerate(coordsets):
                    mdl.active_coordset_id = i + 1

                    for atom, coord in zip(mdl.atoms, coordset):
                        atom.coord = coord

                mdl.active_coordset_id = 1

    def open_nrg_plot(self):
        items = [item for item in self.tree.selectedItems()]
        model_dict = self.session.filereader_manager.filereader_dict
        models = list(model_dict.keys())
        for item in items:
            parent = item.parent()
            mdl = models[self.tree.indexOfTopLevelItem(parent)]
            if parent is None:
                fr = model_dict[mdl][-1]
            else:
                fr = model_dict[mdl][parent.indexOfChild(item)]

            EnergyPlot(self.session, mdl, fr)

    def open_movie_slider(self):
        items = [item for item in self.tree.selectedItems()]
        model_dict = self.session.filereader_manager.filereader_dict
        models = list(model_dict.keys())
        for item in items:
            parent = item.parent()
            mdl = models[self.tree.indexOfTopLevelItem(parent)]
            #coordset doesn't start out with the current coordset id
            #it looks like it should, but it doesn't
            #it starts at 1 instead
            slider = CoordinateSetSlider(self.session, mdl)
            slider.set_slider(mdl.active_coordset_id)
            #run(self.session, "coordset slider %s" % mdl.atomspec)

    def display_help(self):
        """Show the help for this tool in the help viewer."""
        from chimerax.core.commands import run
        run(self.session,
            'open %s' % self.help if self.help is not None else "")

    def delete(self):
        """overload delete"""
        self.session.filereader_manager.triggers.remove_handler(
            self._fr_change)
        self.session.triggers.remove_handler(self._add_models)
        self.session.triggers.remove_handler(self._molid_change)
        self.session.triggers.remove_handler(self._molname_change)
        super().delete()

    def close(self):
        """overload close"""
        self.session.filereader_manager.triggers.remove_handler(
            self._fr_change)
        self.session.triggers.remove_handler(self._add_models)
        self.session.triggers.remove_handler(self._molid_change)
        self.session.triggers.remove_handler(self._molname_change)
        super().close()
Esempio n. 15
0
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__()
        self.pageInfo = PageInfo("", "")
        self.setWindowTitle("河北腾云信息科技有限公司")
        self.icon = QtGui.QIcon()
        self.icon.addPixmap(QtGui.QPixmap("images/logo.ico"),
                            QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.setWindowIcon(self.icon)
        self.treeWidget = QTreeWidget()
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        self.root = QTreeWidgetItem(self.treeWidget)
        self.treeWidget.addTopLevelItem(self.root)
        # self.setStyleSheet(helper.CommonHelper.read_css('./css/style.css'))

        # 设置列数
        self.treeWidget.setColumnCount(1)
        # 设置树形控件头部的标题
        self.treeWidget.setHeaderLabels(['财税大数据平台'])
        self.treeWidget.setFixedWidth(500)

        self.rightWidget = QWidget()
        self.rightLayout = QVBoxLayout()

        self.dataBox = QGroupBox("数据库连接")
        self.dataBox.setStyleSheet("QGroupBox{border: 1px solid black;}")
        self.connectBtn = QPushButton("生成查找树")

        self.yearLab = QLabel("年份")
        self.yearEdit = QLineEdit()

        self.dataLayout = QGridLayout()
        self.dataLayout.setContentsMargins(10, 20, 10, 15)
        self.hostLab = QLabel("主机名或ip地址:")
        self.portLab = QLabel("端口:")
        self.userLab = QLabel("用户名:")
        self.passLab = QLabel("密码")
        self.dbLab = QLabel("数据库名:")
        self.charsetLab = QLabel("字符集:")

        self.hostEdit = QLineEdit()
        self.userEdit = QLineEdit()
        self.portEdit = QLineEdit()
        self.passEdit = QLineEdit()
        self.dbEdit = QLineEdit()
        self.charsetEdit = QLineEdit()

        self.hostEdit.setText("192.168.110.201")
        self.portEdit.setText("3306")
        self.userEdit.setText("root")
        self.passEdit.setEchoMode(QLineEdit.Password)
        self.passEdit.setText("tengyun2020")
        self.dbEdit.setText("fiscal_tax")
        self.charsetEdit.setText("utf8")
        self.yearEdit.setText("2019")

        self.pageTextInfo = []

        self.dataLayout.addWidget(self.hostLab, 0, 0)
        self.dataLayout.addWidget(self.hostEdit, 0, 1)
        self.dataLayout.addWidget(self.portLab, 0, 2)
        self.dataLayout.addWidget(self.portEdit, 0, 3)
        self.dataLayout.addWidget(self.userLab, 0, 4)
        self.dataLayout.addWidget(self.userEdit, 0, 5)
        self.dataLayout.addWidget(self.yearLab, 0, 6)
        self.dataLayout.addWidget(self.yearEdit, 0, 7)
        self.dataLayout.addWidget(self.passLab, 1, 0)
        self.dataLayout.addWidget(self.passEdit, 1, 1)
        self.dataLayout.addWidget(self.dbLab, 1, 2)
        self.dataLayout.addWidget(self.dbEdit, 1, 3)
        self.dataLayout.addWidget(self.charsetLab, 1, 4)
        self.dataLayout.addWidget(self.charsetEdit, 1, 5)
        self.dataLayout.addWidget(self.connectBtn, 1, 7, 1, 1)

        self.dataBox.setLayout(self.dataLayout)

        self.budgetBox = QGroupBox("预决算信息提取")
        self.budgetBox.setStyleSheet("QGroupBox{border: 1px solid black;}")

        self.comboBox = QComboBox()
        self.comboBox.addItem("一般公共预算收支科目")
        self.comboBox.addItem("政府性基金预算收支科目")
        self.comboBox.addItem("国有资本经营预算收支科目")
        self.comboBox.addItem("社会保险基金预算收支科目")
        self.comboBox.addItem("支出经济分类科目")
        # self.comboBox.addItem("一般公共预算收入科目")
        # self.comboBox.addItem("一般公共预算支出科目")
        # self.comboBox.addItem("政府性基金预算收入科目")
        # self.comboBox.addItem("政府性基金预算支出科目")
        # self.comboBox.addItem("国有资本经营预算收入科目")
        # self.comboBox.addItem("国有资本经营预算支出科目")
        # self.comboBox.addItem("社会保险基金预算收入科目")
        # self.comboBox.addItem("社会保险基金预算支出科目")
        self.comboBox.setStyleSheet("""
                    QComboBox {border:none;background:#000000;color:#ffffff;
                    padding-left:30px;font-size:16px "SimHei";}
                    QComboBox QAbstractItemView {background:#000000;color:#ffffff;padding-left:30px;} 
                    QComboBox QAbstractItemView::item {min-height:30px;font-size:16px "SimHei";}
         """)
        self.comboBox.setView(QListView())
        self.pathLab = QLabel("文件路径:")
        self.extractBtn = QPushButton("提取信息")
        self.genBtn = QPushButton("生成Excel")
        self.initBtn = QPushButton("清空文件")
        self.codeLab = QLabel("科目编码:")
        self.subLab = QLabel("科目名称:")
        self.budgetLab = QLabel("预算数:")
        self.actualLab = QLabel("决算数:")
        self.pageLab = QLabel("页码:")
        self.pageInfoBtn = QPushButton("页面信息")
        self.regxLab = QLabel("分割符:")
        self.posLab = QLabel("名称位置:")
        self.targetLab = QLabel("目标文件路径:")
        self.boundLab = QLabel("pdf无边框表格位置:")
        self.currentPageLab = QLabel("无边框表格页码:")

        self.pathEdit = LineEdit()
        self.codeEdit = QLineEdit()
        self.subEdit = QLineEdit()
        self.budgetEdit = QLineEdit()
        self.actualEdit = QLineEdit()
        self.pageEdit = QLineEdit()
        self.regxEdit = QLineEdit()
        self.posEdit = QLineEdit()
        self.targetEdit = LineEdit()
        self.boundEdit = QLineEdit()
        self.currentPageEdit = QLineEdit()

        current_page_validator = QtGui.QRegExpValidator(
            QtCore.QRegExp(r"^[1-9]\d*$"), self.currentPageEdit)
        self.currentPageEdit.setValidator(current_page_validator)

        page_validator = QtGui.QRegExpValidator(
            QtCore.QRegExp(r"(^[1-9]\d*)(-[1-9]\d*)||(^[1-9]\d*)(,[1-9]\d*)+"),
            self.pageEdit)
        self.pageEdit.setValidator(page_validator)

        sub_validator = QtGui.QRegExpValidator(
            QtCore.QRegExp(r"(^[1-9]\d*)(,[1-9]\d*)+"), self.subEdit)
        self.subEdit.setValidator(sub_validator)

        code_validator = QtGui.QRegExpValidator(
            QtCore.QRegExp(r"(^[1-9]\d*)(,[1-9]\d*)+"), self.codeEdit)
        self.codeEdit.setValidator(code_validator)

        budget_validator = QtGui.QRegExpValidator(
            QtCore.QRegExp(r"(^[1-9]\d*)(,[1-9]\d*)+"), self.budgetEdit)
        self.budgetEdit.setValidator(budget_validator)

        actual_validator = QtGui.QRegExpValidator(
            QtCore.QRegExp(r"(^[1-9]\d*)(,[1-9]\d*)+"), self.actualEdit)
        self.actualEdit.setValidator(actual_validator)

        bound_validator = QtGui.QRegExpValidator(
            QtCore.QRegExp(r"(^[0-9]\d*)(,[0-9]\d*)+"), self.boundEdit)
        self.boundEdit.setValidator(bound_validator)
        """
        self.pathEdit.setText("C:/Users/localhost/Desktop/bb/新乐市2019年政府预算公开附表.xlsx")
        self.targetEdit.setText("C:/Users/localhost/Desktop/cc")
        self.subEdit.setText("1")
        self.codeEdit.setText("")
        self.budgetEdit.setText("2")
        self.actualEdit.setText("")
        self.pageEdit.setText("1")
        self.boundEdit.setText("0,700,550,0")
        """
        self.checkBtn = QPushButton("查看")
        self.pathEdit.setObjectName("path")
        self.targetEdit.setObjectName("target")
        self.configEdit = QTextEdit()
        self.configEdit.setStyleSheet("""
            QTextEdit{font-size:14px;background:#3D1140;color:#FFFFFF;}
        """)
        self.configEdit.setFixedHeight(300)
        self.json_str = """
        {
            "一般公共预算收支科目":{
               "parsed":{ 
                    "page":"",
                    "code":"",
                    "budget":"",
                    "actual":"",
                    "type":""
                },
               "unparsed":{ 
                    "page":"",
                    "name":"",
                    "budget":"",
                    "actual":"",
                    "regex":{
                        "sep":"",
                        "pos":""
                    },
                    "type":""
                }
            },
            "政府性基金预算收支科目":{
            "parsed":{ 
                    "page":"",
                    "code":"",
                    "budget":"",
                    "actual":"",
                    "type":""
                },
               "unparsed":{ 
                    "page":"",
                    "name":"",
                    "budget":"",
                    "actual":"",
                    "regex":{
                        "sep":"",
                        "pos":""
                    },
                    "type":""
                }
            },
            "国有资本经营预算收支科目":{
              "parsed":{ 
                    "page":"",
                    "code":"",
                    "budget":"",
                    "actual":"",
                    "type":""
                },
               "unparsed":{ 
                    "page":"",
                    "name":"",
                    "budget":"",
                    "actual":"",
                    "regex":{
                        "sep":"",
                        "pos":""
                    },
                    "type":""
                }
            },
            "社会保险基金预算收支科目":{
               "parsed":{ 
                    "page":"",
                    "code":"",
                    "budget":"",
                    "actual":"",
                    "type":""
                },
               "unparsed":{ 
                    "page":"",
                    "name":"",
                    "budget":"",
                    "actual":"",
                    "regex":{
                        "sep":"",
                        "pos":""
                    },
                    "type":""
                }
            },
            "支出经济分类科目":{
             "parsed":{ 
                    "page":"",
                    "code":"",
                    "budget":"",
                    "actual":"",
                    "type":""
                },
               "unparsed":{ 
                    "page":"",
                    "name":"",
                    "budget":"",
                    "actual":"",
                    "regex":{
                        "sep":"",
                        "pos":""
                    },
                    "type":""
                }
            }
        }
        """
        self.configEdit.setText(
            json.dumps(json.loads(self.json_str),
                       indent=4,
                       sort_keys=False,
                       ensure_ascii=False))
        self.json = {
            "一般公共预算收支科目": {},
            "政府性基金预算收支科目": {},
            "国有资本经营预算收支科目": {},
            "社会保险基金预算收支科目": {},
            "支出经济分类科目": {}
        }
        self.page_settings = QPushButton("")

        self.budgetLayout = QGridLayout()
        self.budgetLayout.setContentsMargins(10, 20, 10, 15)
        self.budgetLayout.addWidget(self.pathLab, 0, 0)
        self.budgetLayout.addWidget(self.pathEdit, 0, 1, 1, 3)
        self.budgetLayout.addWidget(self.targetLab, 1, 0)
        self.budgetLayout.addWidget(self.targetEdit, 1, 1, 1, 3)

        self.spiderBox = QGroupBox("文件下载")
        self.urlLab = QLabel("下载页地址:")
        self.urlEdit = QLineEdit()
        self.downloadLab = QLabel("文件下载路径:")
        self.downloadEdit = LineEdit()
        self.downloadEdit.setObjectName("download")

        self.downloadBtn = QPushButton("下载当前页文件")
        self.spiderLayout = QGridLayout()
        self.spiderLayout.addWidget(self.urlLab, 0, 0)
        self.spiderLayout.addWidget(self.urlEdit, 0, 1)
        # self.spiderLayout.addSpacing(20)
        self.spiderLayout.addWidget(self.downloadLab, 1, 0)
        self.spiderLayout.addWidget(self.downloadEdit, 1, 1)
        self.spiderLayout.addWidget(self.downloadBtn, 1, 2)
        self.spiderBox.setLayout(self.spiderLayout)

        url_validator = QtGui.QRegExpValidator(
            QtCore.QRegExp(
                r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+"
            ), self.urlEdit)
        self.urlEdit.setValidator(url_validator)

        self.budgetLayout.addWidget(self.spiderBox, 3, 0, 4, 4)
        # self.budgetLayout.addWidget(self.configEdit, 2, 0, 6, 4)
        self.budgetLayout.addWidget(self.comboBox, 8, 0, 1, 4)
        self.budgetLayout.addWidget(self.subLab, 9, 0)
        self.budgetLayout.addWidget(self.subEdit, 9, 1)
        # self.budgetLayout.addWidget(self.regxLab, 3, 2)
        # self.budgetLayout.addWidget(self.regxEdit, 3, 3)
        # self.budgetLayout.addWidget(self.posLab, 4, 2)
        # self.budgetLayout.addWidget(self.posEdit, 4, 3)

        self.budgetLayout.addWidget(self.codeLab, 9, 2)
        self.budgetLayout.addWidget(self.codeEdit, 9, 3)

        self.budgetLayout.addWidget(self.budgetLab, 10, 0)
        self.budgetLayout.addWidget(self.budgetEdit, 10, 1)
        self.budgetLayout.addWidget(self.actualLab, 10, 2)
        self.budgetLayout.addWidget(self.actualEdit, 10, 3)

        self.budgetLayout.addWidget(self.pageLab, 11, 0)
        self.budgetLayout.addWidget(self.pageEdit, 11, 1)
        self.budgetLayout.addWidget(self.pageInfoBtn, 11, 4)

        self.budgetLayout.addWidget(self.boundLab, 12, 0)
        self.budgetLayout.addWidget(self.boundEdit, 12, 1)
        self.budgetLayout.addWidget(self.currentPageLab, 12, 2)
        self.budgetLayout.addWidget(self.currentPageEdit, 12, 3)
        self.budgetLayout.addWidget(self.checkBtn, 12, 4)

        self.btnWidget = QWidget()
        self.btnLayout = QHBoxLayout()
        self.btnLayout.addWidget(self.initBtn)
        self.btnLayout.addSpacing(20)
        self.btnLayout.addWidget(self.extractBtn)
        self.btnLayout.addSpacing(20)
        self.btnLayout.addWidget(self.genBtn)

        # self.testBtn = QPushButton("测试")
        # self.btnLayout.addSpacing(20)
        # self.btnLayout.addWidget(self.testBtn)

        self.btnWidget.setLayout(self.btnLayout)
        self.budgetLayout.addWidget(self.btnWidget, 13, 0, 1, 4)

        # self.budgetLayout.addWidget(self.initBtn, 10, 0)
        # self.budgetLayout.addWidget(self.extractBtn, 10, 1)
        # self.budgetLayout.addWidget(self.genBtn, 10, 3)
        for i in range(14):
            self.budgetLayout.setRowMinimumHeight(i, 25)
        self.budgetBox.setLayout(self.budgetLayout)

        self.rightLayout.addWidget(self.dataBox)
        self.rightLayout.addSpacing(20)
        self.rightLayout.addWidget(self.budgetBox)
        self.rightLayout.addStretch(1)
        self.rightWidget.setLayout(self.rightLayout)

        # 节点全部展开
        self.treeWidget.expandAll()
        self.leftWidget = QWidget()
        self.leftLayout = QHBoxLayout()
        self.leftLayout.addWidget(self.treeWidget)

        self.leftWidget.setLayout(self.leftLayout)
        self.mainLayout = QHBoxLayout()
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)

        self.mainLayout.addStretch(1)
        self.mainLayout.addWidget(self.leftWidget)
        self.mainLayout.addStretch(1)
        self.mainLayout.addWidget(self.rightWidget)
        self.mainLayout.addStretch(1)
        self.centralWidget.setLayout(self.mainLayout)
        self.setMinimumSize(1200, 800)
        # 查找树
        self.tree = MultiTree({
            "id": 0,
            "pid": -1,
            "name": "政府科目",
            "code": "0"
        })
        # 预声明子线程
        self.initThread = RunThread(self.root, self.tree, "2020")
        self.downloadThread = DownloadThread("", "")
        # 子树对应字典
        self.tree_dict = {}
        self.sheet_name_list = [
            "一般公共预算收支科目", "政府性基金预算收支科目", "国有资本经营预算收支科目", "社会保险基金预算收支科目",
            "支出经济分类科目"
        ]

        self.connectBtn.clicked.connect(self.on_connect_clicked)
        self.pathEdit.clicked.connect(self.on_edit_double_clicked)
        self.checkBtn.clicked.connect(self.on_check_btn_clicked)

        self.targetEdit.clicked.connect(self.on_edit_double_clicked)
        self.downloadEdit.clicked.connect(self.on_edit_double_clicked)
        self.extractBtn.clicked.connect(self.on_extract_clicked)
        self.initBtn.clicked.connect(self.on_init_btn_clicked)
        self.genBtn.clicked.connect(self.on_gen_btn_clicked)
        self.downloadBtn.clicked.connect(self.on_download_btn_clicked)
        self.pageInfoBtn.clicked.connect(self.on_page_info_btn_clicked)
        # self.testBtn.clicked.connect(self.find_code_by_name)

    @pyqtSlot()
    def on_page_info_btn_clicked(self):
        if self.pageEdit.text().strip() == "":
            QMessageBox.information(self, "提示", '    页码不能为空!    ')
            return
        page = process_param(self.pageEdit.text().strip())
        if len(page) > 0:
            suffix = self.pathEdit.text().split(".")[-1]
            if suffix.lower() == "doc" or suffix.lower(
            ) == "docx" or suffix.lower() == "pdf":
                pdf = camelot.read_pdf(self.pathEdit.text().strip().replace(
                    "docx", "pdf").replace("doc", "pdf"),
                                       flavor='stream',
                                       pages=str(page[0]))
                print(len(pdf))
                if len(pdf) > 0:
                    info = ""
                    for i, row in enumerate(pdf[0].df.values.tolist()):
                        info += ",".join(row) + "\n"
                    self.pageInfo = PageInfo(page[0], info)
                    self.pageInfo.setWindowModality(QtCore.Qt.ApplicationModal)
                    self.pageInfo.show()
            elif suffix.lower() == "xls" or suffix.lower() == "xlsx":
                wb = xlrd.open_workbook(self.pathEdit.text().strip())
                sheet_names = wb.sheet_names()
                info = "\n"
                for i, sheet_name in enumerate(sheet_names):
                    info += str(i + 1) + "\t" + sheet_name + "\n"
                self.pageInfo = PageInfo(page[0], info)
                self.pageInfo.setWindowModality(QtCore.Qt.ApplicationModal)
                self.pageInfo.show()
            else:
                pass

    @pyqtSlot()
    def on_download_btn_clicked(self):
        if self.urlEdit.text().strip() == "":
            QMessageBox.information(self, "提示", '    url地址不能为空!    ')
            return
        if self.downloadEdit.text().strip() == "":
            QMessageBox.information(self, "提示", '    文件夹地址不能为空!    ')
            return

        self.downloadThread = DownloadThread(
            url=self.urlEdit.text().strip(),
            path=self.downloadEdit.text().strip())
        self.downloadThread.resSignal.connect(
            lambda _msg: self.on_download_thread(_msg))
        self.downloadThread.start()

        # downloader = DownLoader(timeout=30, url=self.urlEdit.text().strip(), path=self.downloadEdit.text().strip())
        # downloader.download_file()
        # QMessageBox.information(self, "提示", '    文件下载完成!    ')
        # pass

    # @staticmethod
    # def check_excel_is_open(path):
    #     file_name = path.split("\\")[-1]
    #     print(file_name)
    #     print(path)
    #     print(path.replace(file_name, '~$' + file_name))
    #     if os.path.exists(path.replace(file_name, '~$' + file_name)):
    #         print("True")
    #         return True
    #     print("False")
    #     return False

    @pyqtSlot()
    def on_gen_btn_clicked(self):
        if len(self.json) > 0:
            # print(os.path.join(self.targetEdit.text().strip(), "预决算.xls"))
            # if self.check_excel_is_open(os.path.join(self.targetEdit.text().strip().replace("/", "\\"), "预决算.xls")):
            #     QMessageBox.information(self, "提示", '    Excel文件已经打开,请先关闭!    ')
            #     return
            excel_writer = Excel(
                os.path.join(self.targetEdit.text().strip().replace("/", "\\"),
                             "预决算.xls"), self.sheet_name_list, self.json)
            try:
                excel_writer.write_excel()
            except:
                QMessageBox.information(self, "提示", '    写入失败查看是否文件已经打开!    ')
                return

            QMessageBox.information(self, "提示", '    Json信息写入Excel成功!    ')

    @pyqtSlot()
    def on_init_btn_clicked(self):
        try:
            if self.targetEdit.text().strip() == "" or not os.path.isdir(
                    self.targetEdit.text().strip()):
                QMessageBox.information(self, "提示", '    输入不能为空,或者路径有错误!    ')
            else:
                self.json.clear()
                for file in os.listdir(self.targetEdit.text().strip().replace(
                        "/", "\\")):
                    # print(os.path.join(self.targetEdit.text().strip().replace("/", "\\"), file))
                    if os.path.isfile(
                            os.path.join(
                                self.targetEdit.text().strip().replace(
                                    "/", "\\"), file)):
                        try:
                            os.remove(
                                os.path.join(
                                    self.targetEdit.text().strip().replace(
                                        "/", "\\"), file))
                        except:
                            QMessageBox.information(self, "提示",
                                                    '    删除文件失败请查看是否打开!    ')
                            return
                QMessageBox.information(self, "提示", '    清空文件成功!    ')
        except Exception as e:
            QMessageBox.information(self, "提示", e)

    @pyqtSlot()
    def on_check_btn_clicked(self):
        # self.find_code_by_name()
        if self.currentPageEdit.text().strip() == "" or self.pathEdit.text(
        ).strip() == "":
            QMessageBox.information(self, "提示", '    输入不能为空!    ')
            return
        elif not self.pathEdit.text().strip().endswith(".pdf"):
            QMessageBox.information(self, "提示", '    只有PDF文件需要次操作!    ')
            return
        else:
            print(self.pathEdit.text().strip().replace('.docx',
                                                       '.pdf').replace(
                                                           ".doc", '.pdf'))
            pdf = camelot.read_pdf(self.pathEdit.text().strip().replace(
                '.docx', '.pdf').replace(".doc", '.pdf'),
                                   flavor='stream',
                                   pages=self.currentPageEdit.text().strip())
            if pdf:
                plt = camelot.plot(pdf[0], kind='textedge')
                plt.show()
                axis('tight')
                fig = pylab.gcf()
                fig.canvas.set_window_title(
                    "第" + self.currentPageEdit.text().strip() + "页表格解析示意图")

    @pyqtSlot()
    def on_extract_clicked(self):
        # print(self.tree_dict)
        # self.find_code("")
        #
        if len(self.tree_dict) == 0:
            QMessageBox.information(self, "提示", '    未生成查找树!    ')
            return
        if self.pathEdit.text().strip() == "" or self.targetEdit.text().strip(
        ) == "":
            QMessageBox.information(self, "提示", '    文件路径和目标路径不能为空!    ')
            return
        if self.pageEdit.text().strip() == "":
            QMessageBox.information(self, "提示", '    页码不能为空!    ')
            return
        if self.budgetEdit.text().strip() == "" and self.actualEdit.text(
        ).strip() == "":
            QMessageBox.information(self, "提示", '    预算数、决算数不能同时为空!    ')
            return
        if self.subEdit.text().strip() == "" and self.codeEdit.text().strip(
        ) == "":
            QMessageBox.information(self, "提示", '    科目名称和科目编码不能同时为空!    ')
            return
        if self.subEdit.text().strip() != "" and self.codeEdit.text().strip(
        ) != "":
            QMessageBox.information(self, "提示", '    科目名称和科目编码不能同时非空!    ')
            return
        try:
            process_file(self.pathEdit.text().strip())
            suffix = self.pathEdit.text().split(".")[-1]
            # print(self.pathEdit.text())
            page = process_param(self.pageEdit.text().strip())
            if suffix.lower() == "doc" or suffix.lower(
            ) == "docx" or suffix.lower() == "pdf":
                bound_info = self.boundEdit.text().strip()
                print(bound_info)
                if bound_info != "":
                    if bound_info.endswith(","):
                        bound_info = bound_info.rstrip(",")
                    # print(page)
                    if len(page) > 0:
                        # print((list(map(str, page))))
                        pdf = camelot.read_pdf(self.pathEdit.text().strip(),
                                               flavor='stream',
                                               pages=','.join(
                                                   list(map(str, page))),
                                               table_areas=[bound_info])
                        print(len(pdf))
                        for i in range(len(pdf)):
                            table_list = []
                            for row_data in pdf[i].df.values.tolist():
                                table_list.append(row_data)
                            self.parse(table_list)
                        QMessageBox.information(self, "提示", '    提取信息结束!    ')
                        print(self.json)
                else:
                    """
                    pdf = pdfplumber.open(self.pathEdit.text().strip())
                    # print(pdf.pages)
                    # print(len(pdf.pages))
                    # print(page)
                    for i, _page in enumerate(pdf.pages):
                        if i + 1 in page:
                            table_list = []
                            print(_page.extract_text())
                            for pdf_table in _page.extract_tables():
                                for row in pdf_table:
                                    table_list.append(row)
                                    print(row)
                            print(table_list)
                            self.parse(table_list)
                    """
                    if len(page) > 0:
                        # print((list(map(str, page))))
                        if suffix.lower() == "doc" or suffix.lower() == "docx":
                            pdf = camelot.read_pdf(
                                self.pathEdit.text().strip().replace(
                                    "." + suffix, ".pdf"),
                                flavor='stream',
                                pages=','.join(list(map(str, page))))
                        else:
                            pdf = camelot.read_pdf(
                                self.pathEdit.text().strip(),
                                flavor='stream',
                                pages=','.join(list(map(str, page))))
                        # print(len(pdf))
                        for i in range(len(pdf)):
                            table_list = []
                            for row_data in pdf[i].df.values.tolist():
                                table_list.append(row_data)
                            self.parse(table_list)
                    QMessageBox.information(self, "提示", '    提取信息结束!    ')

            elif suffix.lower() == "xls" or suffix.lower() == "xlsx":
                wb = xlrd.open_workbook(self.pathEdit.text().strip())
                sheet_names = wb.sheet_names()
                for i, sheet_name in enumerate(sheet_names):
                    # print(sheet_name)
                    if i + 1 in page:
                        table = wb.sheet_by_index(i)
                        table_list = []
                        print(table.nrows)

                        for ii in range(table.nrows):
                            # print(type(table.row_values(ii)))
                            # print(table.row_values(ii))
                            table_list.append(table.row_values(ii))
                        print(table_list)
                        self.parse(table_list)
                QMessageBox.information(self, "提示", '    提取信息结束!    ')
        except:
            QMessageBox.information(self, "提示", "提取信息失败请查看输入是否有误!")

    @pyqtSlot()
    def find_code_by_name(self):
        """
        Test of new method for finding the code
        :param name_list:
        :return:
        """
        pdf = camelot.read_pdf(r'C:\Users\localhost\Desktop\政府性基金预算支出表.pdf',
                               flavor='stream',
                               pages='1')
        for i in range(len(pdf)):
            name_list = []
            for row_data in pdf[i].df.values.tolist():
                name = re.sub(r'\s+', '', row_data[0])
                name = name.split("、")[-1]
                name = name.split(":")[-1]
                if name.strip() != "":
                    name_list.append(name.strip())
            self.tree.prepare_search_name(self.tree_dict.get("政府性基金预算收支科目"))
            res = []
            for name in name_list:
                res.append(self.tree.search_node_by_name(name, 0.8))
            for item in res:
                print(item)

        pass

    def backtracking(self, index, data_list, res=[]):
        if index == len(data_list) - 1:
            return
        if len(data_list[index]) == 0:
            return
        for data in data_list:
            res_temp = res[:]
            if len(res_temp) == 0:
                pass
            for item in res_temp:
                if data.get("pid") == item.get("id"):
                    pass

        pass

    def find_code(self, name_list):
        """
        print(self.comboBox.currentText())
        print(self.tree_dict.get(self.comboBox.currentText()))
        name_list = ["一般公共服务支出", "人大事务", "行政运行", "政协事务", "行政运行", "机关服务", "教育支出", "教育管理事务", "行政运行"]
        name_list = ["我拉个区", "一般公共服务支出", "援助其他地区支出", "垃圾", "一般公共服务", "国防支出", "公共安全支出", "教育支出"]
        name_list = ["一般公共服务支出", "援助其他地区支出", "一般公共服务", "国防支出", "公共安全支出", "教育支出"]
        """
        try:
            self.tree.prepare_search_name(
                self.tree_dict.get(self.comboBox.currentText()))
            res = []
            mark = ""
            for i, name in enumerate(name_list):
                search_res = self.tree.search_name(name)
                # print(search_res)
                # res.append(search_res)
                if len(search_res) == 1:
                    mark = search_res[0]
                    # print(i, search_res[0])
                    res.append((i, mark))
                elif len(search_res) > 1:
                    search_res.sort(key=lambda j: len(j))
                    for search_item in search_res:
                        if search_item.startswith(mark):
                            # print(i, search_item)
                            mark = search_item
                            res.append((i, mark))
                            break
                        elif len(search_item) == len(mark):
                            # print(i, search_item)
                            mark = search_item
                            res.append((i, mark))
                            break
            return res
        except Exception as e:
            QMessageBox.information(self, "提示", e)

    def parse(self, data_list):
        # print(data_list)
        try:
            if len(data_list) == 0:
                QMessageBox.information(self, "提示", '    表格解析失败!    ')
                return
            budget_num = process_param(self.budgetEdit.text().strip())
            actual_num = process_param(self.actualEdit.text().strip())
            if self.codeEdit.text().strip() != "":
                code_num = process_param(self.codeEdit.text().strip())
                if (len(budget_num) > 0 and len(budget_num) != len(code_num)
                    ) or (len(actual_num) > 0
                          and len(actual_num) != len(code_num)):
                    QMessageBox.information(self, "提示", '    长度不对应!    ')
                    return
                if (len(budget_num) > 0 and budget_num[-1] > len(data_list[0])
                    ) or (len(actual_num) > 0 and actual_num[-1] > len(
                        data_list[0])) or code_num[-1] > len(data_list[0]):
                    QMessageBox.information(self, "提示", '    列数越界!    ')
                    return
                for data in data_list:
                    for i in range(len(code_num)):
                        key = re.sub(r'\s+', '', str(data[code_num[i] - 1]))
                        # print(key)
                        if isinstance(data[code_num[i] - 1], float):
                            key = str(int(data[code_num[i] - 1]))
                        if key and key.isdigit():
                            if len(budget_num) > 0:
                                if self.json.get(self.comboBox.currentText().
                                                 strip()).get(key):
                                    self.json.get(self.comboBox.currentText(
                                    ).strip()).get(key).update(
                                        {"预算数": data[budget_num[i] - 1]})
                                else:
                                    self.json.get(self.comboBox.currentText(
                                    ).strip()).update({
                                        key: {
                                            "预算数": data[budget_num[i] - 1]
                                        }
                                    })
                            if len(actual_num) > 0:
                                if self.json.get(self.comboBox.currentText().
                                                 strip()).get(key):
                                    self.json.get(self.comboBox.currentText(
                                    ).strip()).get(key).update(
                                        {"决算数": data[actual_num[i] - 1]})
                                else:
                                    self.json.get(self.comboBox.currentText(
                                    ).strip()).update({
                                        key: {
                                            "决算数": data[actual_num[i] - 1]
                                        }
                                    })
            else:
                sub_num = process_param(self.subEdit.text().strip())
                if (len(budget_num) > 0 and len(budget_num) != len(sub_num)
                    ) or (len(actual_num) > 0
                          and len(actual_num) != len(sub_num)):
                    QMessageBox.information(self, "提示", '    长度不对应!    ')
                    return
                if (len(budget_num) > 0 and budget_num[-1] > len(data_list[0])
                    ) or (len(actual_num) > 0 and actual_num[-1] > len(
                        data_list[0])) or sub_num[-1] > len(data_list[0]):
                    QMessageBox.information(self, "提示", '    列数越界!    ')
                    return
                name_list = []
                for i in range(len(data_list)):
                    row_name = []
                    for j in range(len(sub_num)):
                        name = re.sub(r'\s+', '', data_list[i][sub_num[j] - 1])
                        name = name.split("、")[-1]
                        name = name.split(":")[-1]
                        row_name.append(name)
                    name_list.append(row_name)
                name_array = np.array(name_list)
                for j in range(len(sub_num)):
                    for index_code in self.find_code(name_array[:,
                                                                j].tolist()):
                        key = index_code[1]
                        if key.isdigit():
                            if len(budget_num) > 0:
                                if self.json.get(self.comboBox.currentText().
                                                 strip()).get(key):
                                    self.json.get(self.comboBox.currentText(
                                    ).strip()).get(key).update({
                                        "预算数":
                                        data_list[index_code[0]][budget_num[j]
                                                                 - 1]
                                    })
                                else:
                                    self.json.get(self.comboBox.currentText(
                                    ).strip()).update({
                                        key: {
                                            "预算数":
                                            data_list[index_code[0]][
                                                budget_num[j] - 1]
                                        }
                                    })
                            if len(actual_num) > 0:
                                print("------------>", actual_num[j] - 1,
                                      type(actual_num[j]))
                                print("___________", data_list[index_code[0]])
                                if self.json.get(self.comboBox.currentText().
                                                 strip()).get(key):
                                    self.json.get(self.comboBox.currentText(
                                    ).strip()).get(key).update({
                                        "决算数":
                                        data_list[index_code[0]][actual_num[j]
                                                                 - 1]
                                    })
                                else:
                                    self.json.get(self.comboBox.currentText(
                                    ).strip()).update({
                                        key: {
                                            "决算数":
                                            data_list[index_code[0]][
                                                actual_num[j] - 1]
                                        }
                                    })
        except Exception as e:
            QMessageBox.information(self, "提示", e)

    # def check_input(self, num, budget, actual, data):
    #     if (len(budget) > 0 and len(budget) != len(num)) or (
    #             len(actual) > 0 and len(actual) != len(num)):
    #         QMessageBox.information(self, "提示", '    长度不对应!    ')
    #         return
    #     if budget[-1] >= len(data[0]) or actual[-1] >= len(data[0]) or num[-1] >= len(
    #             data[0]):
    #         QMessageBox.information(self, "提示", '    列数越界!    ')
    #         return

    @pyqtSlot()
    def on_edit_double_clicked(self):
        if self.sender().objectName() == "path":
            filepath = QFileDialog.getOpenFileName(self, "请选择文件路径", "/")
            if filepath:
                self.pathEdit.setText(filepath[0].strip())
        elif self.sender().objectName() == "target":
            directory = QFileDialog.getExistingDirectory(self, "请选择文件夹路径", "/")
            if directory:
                self.targetEdit.setText(directory.strip())
        elif self.sender().objectName() == "download":
            directory = QFileDialog.getExistingDirectory(self, "请选择文件夹路径", "/")
            if directory:
                self.downloadEdit.setText(directory.strip())
        else:
            pass

    @pyqtSlot()
    def on_connect_clicked(self):
        print("connect button is clicked!")
        if self.hostEdit.text().strip() == "" or self.portEdit.text().strip(
        ) == "" or self.userEdit.text().strip() == "" or self.yearEdit.text(
        ).strip() == "" or self.passEdit.text().strip(
        ) == "" or self.dbEdit.text().strip() == "" or self.charsetEdit.text(
        ).strip() == "":
            QMessageBox.information(self, "提示", '    输入不能为空!    ')

        else:
            year = self.yearEdit.text().strip()
            self.tree = MultiTree({
                "id": 0,
                "pid": -1,
                "name": year + "年政府科目",
                "code": "0"
            })

            self.treeWidget.clear()
            self.root = QTreeWidgetItem(self.treeWidget)
            self.root.setText(0, year + "年树状结构生成中...")
            self.treeWidget.addTopLevelItem(self.root)

            self.initThread = RunThread(self.root, self.tree, year)
            self.initThread.resSignal.connect(
                lambda _year, _tree: self.on_init_thread(_year, _tree))
            self.initThread.start()

    @pyqtSlot()
    def on_download_thread(self, msg):
        if msg == "ok":
            QMessageBox.information(self, "提示", '    文件下载成功!    ')
        elif msg == "fail":
            QMessageBox.information(self, "提示", '    文件下载失败!    ')
        else:
            pass
            # QMessageBox.information(self, "提示", msg)

    @pyqtSlot()
    def on_init_thread(self, year, tree):
        self.tree = tree
        self.root.setText(0, year + "年树状结构")
        # 遍历树状结构
        # self.tree.traverse(self.tree)
        self.tree_dict.clear()
        self.generate_tree_dict()
        self.treeWidget.expandAll()

    def generate_tree_dict(self):
        self.tree_dict.update({
            "一般公共预算收入":
            self.tree.tree.children[0].children[0],
            "一般公共预算支出":
            self.tree.tree.children[0].children[1],
            "政府性基金预算收入":
            self.tree.tree.children[1].children[0],
            "政府性基金预算支出":
            self.tree.tree.children[1].children[1],
            "国有资本经营预算收入":
            self.tree.tree.children[2].children[0],
            "国有资本经营预算支出":
            self.tree.tree.children[2].children[1],
            "社会保险基金预算收入":
            self.tree.tree.children[3].children[0],
            "社会保险基金预算支出":
            self.tree.tree.children[3].children[1],
            "支出经济分类科目":
            self.tree.tree.children[4],
            "一般公共预算收支科目":
            self.tree.tree.children[0],
            "政府性基金预算收支科目":
            self.tree.tree.children[1],
            "国有资本经营预算收支科目":
            self.tree.tree.children[2],
            "社会保险基金预算收支科目":
            self.tree.tree.children[3]
        })
class GuiProjectDetailsContents(QWidget):

    C_TITLE = 0
    C_WORDS = 1
    C_PAGES = 2
    C_PAGE = 3
    C_PROG = 4

    def __init__(self, theParent, theProject):
        QWidget.__init__(self, theParent)

        self.mainConf = nw.CONFIG
        self.theParent = theParent
        self.theProject = theProject
        self.theTheme = theParent.theTheme
        self.theIndex = theParent.theIndex
        self.optState = theProject.optState

        # Internal
        self._theToC = []

        iPx = self.theTheme.baseIconSize
        hPx = self.mainConf.pxInt(12)
        vPx = self.mainConf.pxInt(4)

        # Contents Tree
        # =============

        self.tocTree = QTreeWidget()
        self.tocTree.setIconSize(QSize(iPx, iPx))
        self.tocTree.setIndentation(0)
        self.tocTree.setColumnCount(6)
        self.tocTree.setSelectionMode(QAbstractItemView.NoSelection)
        self.tocTree.setHeaderLabels(
            ["Title", "Words", "Pages", "Page", "Progress", ""])

        treeHeadItem = self.tocTree.headerItem()
        treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
        treeHeadItem.setTextAlignment(self.C_PAGES, Qt.AlignRight)
        treeHeadItem.setTextAlignment(self.C_PAGE, Qt.AlignRight)
        treeHeadItem.setTextAlignment(self.C_PROG, Qt.AlignRight)

        treeHeader = self.tocTree.header()
        treeHeader.setStretchLastSection(True)
        treeHeader.setMinimumSectionSize(hPx)

        wCol0 = self.mainConf.pxInt(
            self.optState.getInt("GuiProjectDetails", "widthCol0", 200))
        wCol1 = self.mainConf.pxInt(
            self.optState.getInt("GuiProjectDetails", "widthCol1", 60))
        wCol2 = self.mainConf.pxInt(
            self.optState.getInt("GuiProjectDetails", "widthCol2", 60))
        wCol3 = self.mainConf.pxInt(
            self.optState.getInt("GuiProjectDetails", "widthCol3", 60))
        wCol4 = self.mainConf.pxInt(
            self.optState.getInt("GuiProjectDetails", "widthCol4", 90))

        self.tocTree.setColumnWidth(0, wCol0)
        self.tocTree.setColumnWidth(1, wCol1)
        self.tocTree.setColumnWidth(2, wCol2)
        self.tocTree.setColumnWidth(3, wCol3)
        self.tocTree.setColumnWidth(4, wCol4)
        self.tocTree.setColumnWidth(5, hPx)

        # Options
        # =======

        wordsPerPage = self.optState.getInt("GuiProjectDetails",
                                            "wordsPerPage", 350)
        countFrom = self.optState.getInt("GuiProjectDetails", "countFrom", 1)
        clearDouble = self.optState.getInt("GuiProjectDetails", "clearDouble",
                                           True)

        wordsHelp = (
            "Typical word count for a 5 by 8 inch book page with 11 pt font is 350."
        )
        offsetHelp = ("Start counting page numbers from this page.")
        dblHelp = (
            "Assume a new chapter or partition always start on an odd numbered page."
        )

        self.wpLabel = QLabel("Words per page")
        self.wpLabel.setToolTip(wordsHelp)

        self.wpValue = QSpinBox()
        self.wpValue.setMinimum(10)
        self.wpValue.setMaximum(1000)
        self.wpValue.setSingleStep(10)
        self.wpValue.setValue(wordsPerPage)
        self.wpValue.setToolTip(wordsHelp)
        self.wpValue.valueChanged.connect(self._populateTree)

        self.poLabel = QLabel("Count pages from")
        self.poLabel.setToolTip(offsetHelp)

        self.poValue = QSpinBox()
        self.poValue.setMinimum(1)
        self.poValue.setMaximum(9999)
        self.poValue.setSingleStep(1)
        self.poValue.setValue(countFrom)
        self.poValue.setToolTip(offsetHelp)
        self.poValue.valueChanged.connect(self._populateTree)

        self.dblLabel = QLabel("Clear double pages")
        self.dblLabel.setToolTip(dblHelp)

        self.dblValue = QSwitch(self, 2 * iPx, iPx)
        self.dblValue.setChecked(clearDouble)
        self.dblValue.setToolTip(dblHelp)
        self.dblValue.clicked.connect(self._populateTree)

        self.optionsBox = QGridLayout()
        self.optionsBox.addWidget(self.wpLabel, 0, 0)
        self.optionsBox.addWidget(self.wpValue, 0, 1)
        self.optionsBox.addWidget(self.dblLabel, 0, 3)
        self.optionsBox.addWidget(self.dblValue, 0, 4)
        self.optionsBox.addWidget(self.poLabel, 1, 0)
        self.optionsBox.addWidget(self.poValue, 1, 1)
        self.optionsBox.setHorizontalSpacing(hPx)
        self.optionsBox.setVerticalSpacing(vPx)
        self.optionsBox.setColumnStretch(2, 1)

        # Assemble
        # ========

        self.outerBox = QVBoxLayout()
        self.outerBox.addWidget(QLabel("<b>Table of Contents</b>"))
        self.outerBox.addWidget(self.tocTree)
        self.outerBox.addLayout(self.optionsBox)

        self.setLayout(self.outerBox)

        self._prepareData()
        self._populateTree()

        return

    def getColumnSizes(self):
        """Return the column widths for the tree columns.
        """
        retVals = [
            self.tocTree.columnWidth(0),
            self.tocTree.columnWidth(1),
            self.tocTree.columnWidth(2),
            self.tocTree.columnWidth(3),
            self.tocTree.columnWidth(4),
        ]
        return retVals

    ##
    #  Internal Functions
    ##

    def _prepareData(self):
        """Extract the data for the tree.
        """
        self._theToC = []
        self._theToC = self.theIndex.getTableOfContents(2)
        self._theToC.append(("", 0, "END", 0))
        return

    ##
    #  Slots
    ##

    def _populateTree(self):
        """Set the content of the chapter/page tree.
        """
        dblPages = self.dblValue.isChecked()
        wpPage = self.wpValue.value()
        fstPage = self.poValue.value() - 1

        pTotal = 0
        tPages = 1

        theList = []
        for _, tLevel, tTitle, wCount in self._theToC:
            pCount = math.ceil(wCount / wpPage)
            if dblPages:
                pCount += pCount % 2

            pTotal += pCount
            theList.append((tLevel, tTitle, wCount, pCount))

        pMax = pTotal - fstPage

        self.tocTree.clear()
        for tLevel, tTitle, wCount, pCount in theList:
            newItem = QTreeWidgetItem()

            if tPages <= fstPage:
                progPage = numberToRoman(tPages, True)
                progText = ""
            else:
                cPage = tPages - fstPage
                pgProg = 100.0 * (cPage - 1) / pMax if pMax > 0 else 0.0
                progPage = f"{cPage:n}"
                progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%"

            newItem.setIcon(self.C_TITLE,
                            self.theTheme.getIcon("doc_h%d" % tLevel))
            newItem.setText(self.C_TITLE, tTitle)
            newItem.setText(self.C_WORDS, f"{wCount:n}")
            newItem.setText(self.C_PAGES, f"{pCount:n}")
            newItem.setText(self.C_PAGE, progPage)
            newItem.setText(self.C_PROG, progText)

            newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
            newItem.setTextAlignment(self.C_PAGES, Qt.AlignRight)
            newItem.setTextAlignment(self.C_PAGE, Qt.AlignRight)
            newItem.setTextAlignment(self.C_PROG, Qt.AlignRight)

            # Make pages and titles/partitions stand out
            if tLevel < 2:
                bFont = newItem.font(self.C_TITLE)
                if tLevel == 0:
                    bFont.setItalic(True)
                else:
                    bFont.setBold(True)
                    bFont.setUnderline(True)
                newItem.setFont(self.C_TITLE, bFont)

            tPages += pCount

            self.tocTree.addTopLevelItem(newItem)

        return
Esempio n. 17
0
class ImperiumWidget(QWidget):
    def __init__(self, parent=None):
        super(ImperiumWidget, self).__init__(parent)
        # objects, sub-windows
        self._world = XNovaWorld_instance()
        self._layout = None
        self._layout_topbuttons = None
        self._tree = None
        self._btn_reload = None
        # initialization
        self.setup_ui()

    def setup_ui(self):
        self._layout = QVBoxLayout()
        self.setLayout(self._layout)
        # create layout for top line of buttons
        self._layout_topbuttons = QHBoxLayout()
        self._layout.addLayout(self._layout_topbuttons)
        # create reload button
        self._btn_reload = QPushButton(self.tr('Refresh imperium'), self)
        self._btn_reload.setIcon(QIcon(':i/reload.png'))
        self._btn_reload.clicked.connect(self.on_btn_refresh_imperium)
        self._layout_topbuttons.addWidget(self._btn_reload)
        # finalize top buttons layout
        self._layout_topbuttons.addStretch()
        # create tree
        self._tree = QTreeWidget(self)
        self._tree.setAnimated(False)
        self._tree.setExpandsOnDoubleClick(True)
        self._tree.setHeaderHidden(False)
        self._tree.setItemsExpandable(True)
        self._tree.setRootIsDecorated(True)
        self._tree.setSortingEnabled(False)
        self._tree.setColumnCount(1)
        self._tree.setHeaderLabels(['None'])
        self._layout.addWidget(self._tree)
        self._tree.show()

    # called once after full world load
    def update_planets(self):
        def additem_helper(item_texts, twi_parent=None, align_flag=0):
            # align_flag = Qt::AlignLeft / Qt::AlignRight / Qt::AlignHCenter
            if align_flag == 0:
                align_flag = Qt.AlignHCenter | Qt.AlignVCenter
            twi = QTreeWidgetItem(item_texts)
            for it_col in range(len(item_texts)):
                if it_col > 0:
                    # void QTreeWidgetItem::setTextAlignment(int column, int alignment)
                    twi.setTextAlignment(it_col, align_flag)
            if twi_parent is None:
                self._tree.addTopLevelItem(twi)
            else:
                twi_parent.addChild(twi)
            return True

        self._tree.clear()  # clear the tree first
        planets = self._world.get_planets()  # get planets from the world
        #
        # setup header and its labels
        header_labels = ['-']
        for i in range(len(planets)):
            header_labels.append(planets[i].name)
        header_labels.append(self.tr('Total'))  # last column - totals
        self._tree.setHeaderLabels(header_labels)
        # alignment of text in header labels
        self._tree.header().setDefaultAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        # default column widths
        for i in range(len(planets)):
            if i < 1:
                self._tree.setColumnWidth(i, 150)
            else:
                self._tree.setColumnWidth(i, 75)
        #
        # planets names
        item_strings = [self.tr('Name')]
        for pl in planets:
            item_strings.append(pl.name)
        additem_helper(item_strings)
        #
        # planets coords
        item_strings = [self.tr('Coords')]
        for pl in planets:
            item_strings.append('[{0}:{1}:{2}]'.format(pl.coords.galaxy, pl.coords.system, pl.coords.position))
        additem_helper(item_strings)
        #
        # planets fields
        item_strings = [self.tr('Fields')]
        total_busy = 0
        total_fields = 0
        for pl in planets:
            total_busy += pl.fields_busy
            total_fields = pl.fields_total
            item_strings.append('{0} / {1}'.format(pl.fields_busy, pl.fields_total))
        item_strings.append('{0} / {1}'.format(total_busy, total_fields))
        additem_helper(item_strings)
        #
        # resources
        res_root = QTreeWidgetItem([self.tr('Resources')])
        item_strings = [self.tr('Metal')]
        total_res = 0
        for pl in planets:
            total_res += pl.res_current.met
            item_strings.append('{0}'.format(number_format(pl.res_current.met)))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, res_root)
        #
        item_strings = [self.tr('Crystal')]
        total_res = 0
        for pl in planets:
            total_res += pl.res_current.cry
            item_strings.append('{0}'.format(number_format(pl.res_current.cry)))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, res_root)
        #
        item_strings = [self.tr('Deit')]
        total_res = 0
        for pl in planets:
            total_res += pl.res_current.deit
            item_strings.append('{0}'.format(number_format(pl.res_current.deit)))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, res_root)
        #
        item_strings = [self.tr('Energy')]
        total_busy = 0
        total_fields = 0
        for pl in planets:
            total_busy += pl.energy.energy_left
            total_fields += pl.energy.energy_total
            item_strings.append('{0} / {1}'.format(
                    pl.energy.energy_left,
                    pl.energy.energy_total))
        item_strings.append('{0} / {1}'.format(total_busy, total_fields))
        additem_helper(item_strings, res_root)
        #
        item_strings = [self.tr('Charge')]
        for pl in planets:
            item_strings.append('{0}%'.format(pl.energy.charge_percent))
        additem_helper(item_strings, res_root)
        self._tree.addTopLevelItem(res_root)
        res_root.setExpanded(True)
        #
        # resources per hour
        rph_root = QTreeWidgetItem([self.tr('Production')])
        item_strings = [self.tr('Met/h')]
        total_res = 0
        for pl in planets:
            total_res += pl.res_per_hour.met
            item_strings.append('{0}'.format(number_format(pl.res_per_hour.met)))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, rph_root)
        #
        item_strings = [self.tr('Cry/h')]
        total_res = 0
        for pl in planets:
            total_res += pl.res_per_hour.cry
            item_strings.append('{0}'.format(number_format(pl.res_per_hour.cry)))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, rph_root)
        #
        item_strings = [self.tr('Deit/h')]
        total_res = 0
        for pl in planets:
            total_res += pl.res_per_hour.deit
            item_strings.append('{0}'.format(number_format(pl.res_per_hour.deit)))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, rph_root)
        self._tree.addTopLevelItem(rph_root)
        rph_root.setExpanded(True)
        #
        # buildings
        buildings_root = QTreeWidgetItem([self.tr('Buildings')])
        item_strings = [self.tr('Metal factory')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.met_factory))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Crystal factory')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.cry_factory))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Deit factory')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.deit_factory))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Solar station')]
        for pl in planets:
            # testing: detect solar station level up
            addstr = '{0}'.format(pl.buildings.solar_station)
            if pl.is_build_in_progress('Солнечная батарея'):
                addstr = '{0} -> {1}'.format(pl.buildings.solar_station, pl.buildings.solar_station+1)
            item_strings.append(addstr)
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Nuclear station')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.nuclear_station))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Robotics factory')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.robotics_factory))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Nanites factory')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.nanites_factory))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Shipyard')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.shipyard))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Metal silo')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.met_silo))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Crystal silo')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.cry_silo))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Deit silo')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.deit_silo))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Lab')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.lab))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('TerraFormer')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.terraformer))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Alliance silo')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.alliance_silo))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Rocket silo')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.rocket_silo))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Lunar Base')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.lunar_base))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Lunar Phalanx')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.lunar_phalanx))
        additem_helper(item_strings, buildings_root)
        item_strings = [self.tr('Gates')]
        for pl in planets:
            item_strings.append('{0}'.format(pl.buildings.gates))
        additem_helper(item_strings, buildings_root)
        # add/expand
        self._tree.addTopLevelItem(buildings_root)
        buildings_root.setExpanded(True)
        #
        # defense
        defense_root = QTreeWidgetItem([self.tr('Defense')])
        item_strings = [self.tr('Rocket Launcher')]
        total_res = 0
        for pl in planets:
            total_res += pl.defense.ru
            item_strings.append(number_format(pl.defense.ru))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, defense_root)
        #
        item_strings = [self.tr('Light Laser')]
        total_res = 0
        for pl in planets:
            total_res += pl.defense.ll
            item_strings.append(number_format(pl.defense.ll))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, defense_root)
        #
        item_strings = [self.tr('Heavy Laser')]
        total_res = 0
        for pl in planets:
            total_res += pl.defense.tl
            item_strings.append(number_format(pl.defense.tl))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, defense_root)
        #
        item_strings = [self.tr('Gauss')]
        total_res = 0
        for pl in planets:
            total_res += pl.defense.gauss
            item_strings.append(number_format(pl.defense.gauss))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, defense_root)
        #
        item_strings = [self.tr('Ion')]
        total_res = 0
        for pl in planets:
            total_res += pl.defense.ion
            item_strings.append(number_format(pl.defense.ion))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, defense_root)
        #
        item_strings = [self.tr('Plasma')]
        total_res = 0
        for pl in planets:
            total_res += pl.defense.plasma
            item_strings.append(number_format(pl.defense.plasma))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, defense_root)
        #
        item_strings = [self.tr('Small Dome')]
        total_res = 0
        for pl in planets:
            total_res += pl.defense.small_dome
            item_strings.append(number_format(pl.defense.small_dome))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, defense_root)
        #
        item_strings = [self.tr('Big Dome')]
        total_res = 0
        for pl in planets:
            total_res += pl.defense.big_dome
            item_strings.append(number_format(pl.defense.big_dome))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, defense_root)
        #
        item_strings = [self.tr('Defender Missile')]
        total_res = 0
        for pl in planets:
            total_res += pl.defense.defender_rocket
            item_strings.append(number_format(pl.defense.defender_rocket))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, defense_root)
        #
        item_strings = [self.tr('Attack Missile')]
        total_res = 0
        for pl in planets:
            total_res += pl.defense.attack_rocket
            item_strings.append(number_format(pl.defense.attack_rocket))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, defense_root)
        # add/expand
        self._tree.addTopLevelItem(defense_root)
        defense_root.setExpanded(True)
        #
        # fleet
        fleet_root = QTreeWidgetItem([self.tr('Fleet')])
        item_strings = [self.tr('Small Transport')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.mt
            item_strings.append(number_format(pl.ships.mt))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Big Transport')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.bt
            item_strings.append(number_format(pl.ships.bt))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Light Fighter')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.li
            item_strings.append(number_format(pl.ships.li))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Heavy Fighter')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.ti
            item_strings.append(number_format(pl.ships.ti))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Cruiser')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.crus
            item_strings.append(number_format(pl.ships.crus))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Battleship')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.link
            item_strings.append(number_format(pl.ships.link))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Colonizer')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.col
            item_strings.append(number_format(pl.ships.col))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Refiner')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.rab
            item_strings.append(number_format(pl.ships.rab))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Spy')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.spy
            item_strings.append(number_format(pl.ships.spy))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Bomber')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.bomber
            item_strings.append(number_format(pl.ships.bomber))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Solar Satellite')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.ss
            item_strings.append(number_format(pl.ships.ss))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Destroyer')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.unik
            item_strings.append(number_format(pl.ships.unik))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Death Star')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.zs
            item_strings.append(number_format(pl.ships.zs))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('BattleCruiser')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.lk
            item_strings.append(number_format(pl.ships.lk))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('War Base')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.warbase
            item_strings.append(number_format(pl.ships.warbase))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Corvett')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.f_corvett
            item_strings.append(number_format(pl.ships.f_corvett))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Interceptor')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.f_ceptor
            item_strings.append(number_format(pl.ships.f_ceptor))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Dreadnought')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.f_dread
            item_strings.append(number_format(pl.ships.f_dread))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        #
        item_strings = [self.tr('Corsair')]
        total_res = 0
        for pl in planets:
            total_res += pl.ships.f_corsair
            item_strings.append(number_format(pl.ships.f_corsair))
        item_strings.append(number_format(total_res))
        additem_helper(item_strings, fleet_root)
        # add/expand
        self._tree.addTopLevelItem(fleet_root)
        fleet_root.setExpanded(True)

    def update_planet_resources(self):
        res_top_twi = self._tree.topLevelItem(3)  # 4th top level item is "Resources"
        if res_top_twi is None:
            return
        res_met_twi = res_top_twi.child(0)   # first child is metal
        res_cry_twi = res_top_twi.child(1)   # 2nd is crystal
        res_deit_twi = res_top_twi.child(2)  # 3rd is deit
        res_en_twi = res_top_twi.child(3)    # 4th is energy
        if (res_met_twi is None) or (res_cry_twi is None) or (res_deit_twi is None) \
                or (res_en_twi is None):
            return
        planets = self._world.get_planets()  # get planets from the world
        ncolumn = 1  # column #0 is description, planets start at #1
        totals = [0, 0, 0]  # count total resources
        for planet in planets:
            res_met_twi.setText(ncolumn, number_format(int(planet.res_current.met)))
            res_cry_twi.setText(ncolumn, number_format(int(planet.res_current.cry)))
            res_deit_twi.setText(ncolumn, number_format(int(planet.res_current.deit)))
            res_en_twi.setText(ncolumn, '{0} / {1}'.format(
                planet.energy.energy_left, planet.energy.energy_total))
            ncolumn += 1
            totals[0] += int(planet.res_current.met)
            totals[1] += int(planet.res_current.cry)
            totals[2] += int(planet.res_current.deit)
        # set values for "totals" column
        res_met_twi.setText(ncolumn, number_format(totals[0]))
        res_cry_twi.setText(ncolumn, number_format(totals[1]))
        res_deit_twi.setText(ncolumn, number_format(totals[2]))

    @pyqtSlot()
    def on_btn_refresh_imperium(self):
        self._world.signal(self._world.SIGNAL_RELOAD_PAGE, page_name='imperium')
Esempio n. 18
0
class sender(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        MainLayout = QHBoxLayout(self)
        self.ContentTree = QTreeWidget(self)
        RightLayout = QGridLayout()
        MainLayout.addWidget(self.ContentTree)
        MainLayout.addLayout(RightLayout)

        Templates_Label = QLabel('Templates')
        self.Templates_ComboBox = QComboBox()
        Templates = ['TCP', 'UDP', 'ICMP', 'DNS']
        self.Templates_ComboBox.addItems(Templates)

        NumToSend_Label = QLabel('Num to send')
        self.NumToSend_SpinBox = QSpinBox()
        self.NumToSend_SpinBox.setValue(1)

        Interval_Label = QLabel('Interval')
        self.Interval_SpinBox = QDoubleSpinBox()
        self.Interval_SpinBox.setValue(0.0)

        Thread_Label = QLabel('Threads')
        self.Thread_SpinBox = QSpinBox()
        self.Thread_SpinBox.setValue(1)

        self.Follow_CheckBox = QCheckBox('Follow Stream')

        Send_Button = QPushButton('Send')
        Reset_Button = QPushButton('Reset')

        self.ContentTree.setColumnCount(2)
        self.ContentTree.setHeaderLabels(['Item', 'Detail'])

        RightLayout.addWidget(Templates_Label, 0, 0)
        RightLayout.addWidget(self.Templates_ComboBox, 0, 1)
        RightLayout.addWidget(NumToSend_Label, 1, 0)
        RightLayout.addWidget(self.NumToSend_SpinBox, 1, 1)
        RightLayout.addWidget(Interval_Label, 2, 0)
        RightLayout.addWidget(self.Interval_SpinBox, 2, 1)
        RightLayout.addWidget(Thread_Label, 3, 0)
        RightLayout.addWidget(self.Thread_SpinBox, 3, 1)
        RightLayout.addWidget(self.Follow_CheckBox, 4, 0)
        RightLayout.addWidget(Send_Button, 5, 0)
        RightLayout.addWidget(Reset_Button, 5, 1)
        RightLayout.setSpacing(15)
        RightLayout.setContentsMargins(10, 10, 10, 10)

        Send_Button.clicked.connect(self.SendPacket)
        Reset_Button.clicked.connect(self.reset_pkt)

        self.Templates_ComboBox.currentIndexChanged[int].connect(
            self.initTemplate)
        self.ContentTree.itemDoubleClicked[QTreeWidgetItem, int].connect(
            self.on_treeWidgetItem_doubleClicked)
        self.ContentTree.itemClicked[QTreeWidgetItem, int].connect(
            self.on_treeWidgetItem_itemClicked)
        self.initTemplate(0)

    def initPkt(self):
        self.ether_dic = {
            # 'dst': 'ff:ff:ff:ff:ff:ff',
            # 'src': '00:00:00:00:00:00',
            # 'type': 0x800
        }

        self.ip_dic = {
            # 'version': 4,
            # 'ihl': None,
            # 'tos': 0x0,
            # 'len': 0,
            # 'id': 0,
            # 'flags': 0,
            # 'ttl': 64,
            # 'proto': 'tcp',
            # 'chksum': None,
            # 'src': '127.0.0.1',
            # 'dst': '127.0.0.1'
        }
        self.tcp_dic = {
            # 'sport': 20,
            # 'dport': 80,
            # 'seq': 0,
            # 'ack': 0,
            # 'dataofs': None,
            # 'reserved': 0,
            # 'flags': 2,
            # 'window': 8192,
            # 'chksum': None,
            # 'urgptr': 0,
            # 'options': []
        }
        self.udp_dic = {}
        self.icmp_dic = {}
        self.dns_dic = {}
        self.data = b'payload'

    def SendPacket(self):
        if self.Templates_ComboBox.currentIndex() == 0:
            pkt = Ether(**self.ether_dic) / IP(**self.ip_dic) / TCP(
                **self.tcp_dic) / self.data
        elif self.Templates_ComboBox.currentIndex() == 1:
            pkt = Ether(**self.ether_dic) / IP(**self.ip_dic) / UDP(
                **self.udp_dic) / self.data
        elif self.Templates_ComboBox.currentIndex() == 2:
            pkt = Ether(**self.ether_dic) / IP(**self.ip_dic) / ICMP(
                **self.icmp_dic) / self.data
        elif self.Templates_ComboBox.currentIndex() == 3:
            pkt = Ether(**self.ether_dic) / IP(**self.ip_dic) / UDP(
                **self.udp_dic) / DNS(**self.dns_dic)

        baseNum = self.NumToSend_SpinBox.value() // self.Thread_SpinBox.value()
        remainderNum = self.NumToSend_SpinBox.value(
        ) % self.Thread_SpinBox.value()
        sendNum_list = [baseNum] * self.Thread_SpinBox.value()
        while remainderNum > 0:
            sendNum_list[remainderNum] += 1
            remainderNum -= 1
        tmp_thread = self.Thread_SpinBox.value()
        self.threadList = []  #avoid being garbage collected by Python.
        while tmp_thread > 0:
            thread = senderthread()
            self.threadList.append(thread)
            thread.set_send_num(sendNum_list[tmp_thread - 1])
            thread.set_interval(self.Interval_SpinBox.value())
            thread.set_pkt(pkt)
            thread.start()
            tmp_thread -= 1

        self.initPkt()

    def reset_pkt(self):
        self.initTemplate(self.Templates_ComboBox.currentIndex())

    def initTemplate(self, index):
        self.initPkt()
        # self.text = FakeOut()
        if index == 0:
            pkt = Ether() / IP() / TCP() / b'payload'
        elif index == 1:
            pkt = Ether() / IP() / UDP() / b'payload'
        elif index == 2:
            pkt = Ether() / IP() / ICMP() / b'payload'
        elif index == 3:
            pkt = Ether() / IP() / UDP() / DNS()
        # old = sys.stdout
        # sys.stdout = self.text
        # pkt.show2()
        # sys.stdout = old
        # tmp = self.text.str
        # self.formatString(tmp)
        self.packetDict = scapy2ordereddict.to_dict(pkt)
        self.buildTree()

    # def formatString(self, tmp):
    #     self.final_dict = collections.OrderedDict()
    #     title_pattern = re.compile(r'###[ [a-zA-Z]+ ]###')  #abstract titles
    #     tmp_titles = title_pattern.findall(tmp)
    #     self.titles = []
    #     for title in tmp_titles:
    #         refine_pattern = re.compile(r'###\[ | \]###')
    #         self.titles.append(refine_pattern.sub('', title))
    #     #print(self.titles)

    #     content_split_pattern = title_pattern  #abstract contents
    #     tmp_content = re.split(content_split_pattern, tmp)
    #     self.contents = [i for i in tmp_content if i != '']
    #     #print(self.contents)

    #     for (title, content) in zip(self.titles, self.contents):
    #         tmp_dict = {}
    #         tmp_lists = re.split(r'\n', content)
    #         tmp_lists = [i.replace(' ', '') for i in tmp_lists if i != '']

    #         #print(tmp_lists)
    #         for i in tmp_lists:
    #             tmp_item = i.split('=')
    #             #print(tmp_item)
    #             if len(tmp_item) == 2:
    #                 tmp_dict[tmp_item[0]] = tmp_item[1]
    #         self.final_dict[title] = tmp_dict
    #     #print(self.final_dict)

    def buildTree(self):
        self.ContentTree.clear()
        self.doubleclicked = False
        self.lastColumn = 0
        for title in self.packetDict.keys():
            tree_item = QTreeWidgetItem(self.ContentTree)
            tree_item.setText(0, title)
            tree_item.setExpanded(True)
            detail_dic = self.packetDict[title]
            for i in detail_dic.keys():
                #print(i,detail_dic[i])
                leaf = QTreeWidgetItem(tree_item, [i, str(detail_dic[i])])
                tree_item.addChild(leaf)
                leaf.setToolTip(1, str(detail_dic[i]))
                self.lastItem = tree_item
            self.ContentTree.addTopLevelItem(tree_item)

    def on_treeWidgetItem_doubleClicked(self, item, column):
        if column == 1:
            self.ContentTree.openPersistentEditor(item, column)
            self.pretext = item.text(1)
            self.doubleclicked = True
        self.lastColumn = column
        self.lastItem = item

    def on_treeWidgetItem_itemClicked(self, item, column):
        if self.lastColumn != column or self.lastItem != item:
            if self.doubleclicked:
                self.ContentTree.closePersistentEditor(self.lastItem,
                                                       self.lastColumn)
                self.doubleclicked = False
                if self.pretext != self.lastItem.text(1):
                    parent_title = self.lastItem.parent().text(0)
                    if parent_title == 'Ethernet':
                        self.ether_dic[self.lastItem.text(
                            0)] = self.lastItem.text(1)
                    elif parent_title == 'IP':
                        self.ip_dic[self.lastItem.text(
                            0)] = self.lastItem.text(1)
                    elif parent_title == 'UDP':
                        self.udp_dic[self.lastItem.text(
                            0)] = self.lastItem.text(1)
                    elif parent_title == 'TCP':
                        self.tcp_dic[self.lastItem.text(
                            0)] = self.lastItem.text(1)
                    elif parent_title == 'ICMP':
                        self.icmp_dic[self.lastItem.text(
                            0)] = self.lastItem.text(1)
                    elif parent_title == 'DNS':
                        self.dns_dic[self.lastItem.text(
                            0)] = self.lastItem.text(1)
                    elif parent_title == 'Raw':
                        self.data = self.lastItem.text(1)

        self.lastColumn = column
        self.lastItem = item
Esempio n. 19
0
class TreeSymbolsWidget(QDialog):
    """ Class of Dialog for Tree Symbols """

    dockWidget = pyqtSignal('PyQt_PyObject')
    undockWidget = pyqtSignal('PyQt_PyObject')

    def __init__(self, parent=None):
        super(TreeSymbolsWidget, self).__init__(parent)
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)
        self.tree = QTreeWidget()
        self.tree.setFrameShape(0)
        vbox.addWidget(self.tree)
        self.tree.header().setHidden(True)
        self.tree.setSelectionMode(self.tree.SingleSelection)
        self.tree.setAnimated(True)
        self.tree.header().setHorizontalScrollMode(
            QAbstractItemView.ScrollPerPixel)
        self.tree.header().setSectionResizeMode(0,
                                                QHeaderView.ResizeToContents)
        self.tree.header().setStretchLastSection(False)
        self.actualSymbols = ('', {})
        self.docstrings = {}
        self.collapsedItems = {}

        self.tree.itemClicked.connect(self._go_to_definition)
        self.tree.itemActivated.connect(self._go_to_definition)
        self.tree.setContextMenuPolicy(Qt.CustomContextMenu)

        self.customContextMenuRequested['const QPoint &'].connect(
            self._menu_context_tree)
        # self.connect(
        #    self,
        #    SIGNAL("customContextMenuRequested(const QPoint &)"),
        #    self._menu_context_tree)
        # self.connect(self, SIGNAL("itemCollapsed(QTreeWidgetItem *)"),
        #             self._item_collapsed)
        # self.connect(self, SIGNAL("itemExpanded(QTreeWidgetItem *)"),
        #             self._item_expanded)

        IDE.register_service('symbols_explorer', self)
        # ExplorerContainer.register_tab(translations.TR_TAB_SYMBOLS, self)

    def install_tab(self):
        """ Connect signals for goingdown """

        ide = IDE.get_service('ide')
        ide.goingDown.connect(self.close)

    def _menu_context_tree(self, point):
        """Context menu"""
        index = self.tree.indexAt(point)
        if not index.isValid():
            return

        menu = QMenu(self)
        f_all = menu.addAction(translations.TR_FOLD_ALL)
        u_all = menu.addAction(translations.TR_UNFOLD_ALL)
        menu.addSeparator()
        u_class = menu.addAction(translations.TR_UNFOLD_CLASSES)
        u_class_method = menu.addAction(
            translations.TR_UNFOLD_CLASSES_AND_METHODS)
        u_class_attr = menu.addAction(
            translations.TR_UNFOLD_CLASSES_AND_ATTRIBUTES)
        menu.addSeparator()
        # save_state = menu.addAction(self.tr("Save State"))

        # self.connect(f_all, SIGNAL("triggered()"),
        #             lambda: self.tree.collapseAll())
        # self.connect(u_all, SIGNAL("triggered()"),
        #             lambda: self.tree.expandAll())
        # self.connect(u_class, SIGNAL("triggered()"), self._unfold_class)
        # self.connect(u_class_method, SIGNAL("triggered()"),
        #             self._unfold_class_method)
        # self.connect(u_class_attr, SIGNAL("triggered()"),
        #              self._unfold_class_attribute)
        # self.connect(save_state, SIGNAL("triggered()"),
        #    self._save_symbols_state)

        menu.exec_(QCursor.pos())

    def _get_classes_root(self):
        """Return the root of classes"""
        class_root = None
        for i in range(self.tree.topLevelItemCount()):
            item = self.tree.topLevelItem(i)
            if item.isClass and not item.isClickable:
                class_root = item
                break
        return class_root

    def _unfold_class(self):
        """Method to Unfold Classes"""
        self.tree.collapseAll()
        classes_root = self._get_classes_root()
        if not classes_root:
            return

        classes_root.setExpanded(True)

    def _unfold_class_method(self):
        """Method to Unfold Methods"""
        self.tree.expandAll()
        classes_root = self._get_classes_root()
        if not classes_root:
            return
        # for each class!
        for i in range(classes_root.childCount()):
            class_item = classes_root.child(i)
            # for each attribute or functions
            for j in range(class_item.childCount()):
                item = class_item.child(j)
                # METHODS ROOT!!
                if not item.isMethod and not item.isClickable:
                    item.setExpanded(False)
                    break

    def _unfold_class_attribute(self):
        """Method to Unfold Attributes"""
        self.tree.expandAll()
        classes_root = self._get_classes_root()
        if not classes_root:
            return
        # for each class!
        for i in range(classes_root.childCount()):
            class_item = classes_root.child(i)
            # for each attribute or functions
            for j in range(class_item.childCount()):
                item = class_item.child(j)
                # ATTRIBUTES ROOT!!
                if not item.isAttribute and not item.isClickable:
                    item.setExpanded(False)
                    break

    def _save_symbols_state(self):
        """Method to Save a persistent Symbols state"""
        # filename = self.actualSymbols[0]
        # TODO: persist self.collapsedItems[filename] in QSettings
        pass

    def _get_expand(self, item):
        """
        Returns True or False to be used as setExpanded() with the items
        It method is based on the click that the user made in the tree
        """
        name = self._get_unique_name(item)
        filename = self.actualSymbols[0]
        collapsed_items = self.collapsedItems.get(filename, [])
        can_check = (not item.isClickable) or item.isClass or item.isMethod
        if can_check and name in collapsed_items:
            return False
        return True

    @staticmethod
    def _get_unique_name(item):
        """
        Returns a string used as unique name
        """
        # className_Attributes/className_Functions
        parent = item.parent()
        if parent:
            return "%s_%s" % (parent.text(0), item.text(0))
        return "_%s" % item.text(0)

    def update_symbols_tree(self, symbols, filename='', parent=None):
        """Method to Update the symbols on the Tree"""
        TIP = "{} {}"
        if not parent:
            if filename == self.actualSymbols[0] and \
                    self.actualSymbols[1] and not symbols:
                return

            if symbols == self.actualSymbols[1]:
                # Nothing new then return
                return

            # we have new symbols refresh it
            self.tree.clear()
            self.actualSymbols = (filename, symbols)
            self.docstrings = symbols.get('docstrings', {})
            parent = self.tree
        if 'attributes' in symbols:
            globalAttribute = ItemTree(parent, [translations.TR_ATTRIBUTES])
            globalAttribute.isClickable = False
            globalAttribute.isAttribute = True
            globalAttribute.setExpanded(self._get_expand(globalAttribute))
            globalAttribute.setToolTip(
                0,
                TIP.format(len(symbols['attributes']),
                           translations.TR_ATTRIBUTES))
            for glob in sorted(symbols['attributes']):
                globItem = ItemTree(globalAttribute, [glob],
                                    lineno=symbols['attributes'][glob])
                globItem.isAttribute = True
                # globItem.setIcon(
                #    0, ui_tools.colored_icon(":img/attr", "#5dade2"))
                globItem.setIcon(0, QIcon(":img/attr"))
                globItem.setExpanded(self._get_expand(globItem))

        if 'functions' in symbols and symbols['functions']:
            functionsItem = ItemTree(parent, [translations.TR_FUNCTIONS])
            functionsItem.isClickable = False
            functionsItem.isMethod = True
            functionsItem.setExpanded(self._get_expand(functionsItem))
            functionsItem.setToolTip(
                0,
                TIP.format(len(symbols['functions']),
                           translations.TR_FUNCTIONS))
            for func in sorted(symbols['functions']):
                item = ItemTree(functionsItem, [func],
                                lineno=symbols['functions'][func]['lineno'])
                tooltip = self.create_tooltip(
                    func, symbols['functions'][func]['lineno'])
                item.isMethod = True
                item.setIcon(0, QIcon(":img/function"))
                # item.setIcon(
                #    0, ui_tools.colored_icon(":img/function", "#9FA8DA"))
                item.setToolTip(0, tooltip)
                item.setExpanded(self._get_expand(item))
                self.update_symbols_tree(
                    symbols['functions'][func]['functions'], parent=item)
        if 'classes' in symbols and symbols['classes']:
            classItem = ItemTree(parent, [translations.TR_CLASSES])
            classItem.isClickable = False
            classItem.isClass = True
            classItem.setExpanded(self._get_expand(classItem))
            classItem.setToolTip(
                0, TIP.format(len(symbols['classes']),
                              translations.TR_CLASSES))
            for claz in sorted(symbols['classes']):
                line_number = symbols['classes'][claz]['lineno']
                item = ItemTree(classItem, [claz], lineno=line_number)
                item.isClass = True
                tooltip = self.create_tooltip(claz, line_number)
                item.setToolTip(0, tooltip)
                # item.setIcon(0, ui_tools.colored_icon(":img/class", "#FFCC80"))
                # item.setIcon(0, ui_tools.get_icon('class', '#FFCC80'))
                item.setIcon(0, ui_tools.get_icon('class'))
                item.setExpanded(self._get_expand(item))
                self.update_symbols_tree(symbols['classes'][claz]['members'],
                                         parent=item)

    def _go_to_definition(self, item):
        """ Takes and item object and goes to definition on the editor """

        main_container = IDE.get_service('main_container')
        if item.isClickable and main_container:
            main_container.editor_go_to_line(item.lineno - 1)

    def create_tooltip(self, name, lineno):
        """Takes a name and line number and returns a tooltip"""
        doc = self.docstrings.get(lineno, None)
        if doc is None:
            doc = ''
        else:
            doc = '\n' + doc
        tooltip = name + doc
        return tooltip

    def _item_collapsed(self, item):
        """When item collapsed"""
        super(TreeSymbolsWidget, self).collapseItem(item)

        can_check = (not item.isClickable) or item.isClass or item.isMethod
        if can_check:
            n = self._get_unique_name(item)
            filename = self.actualSymbols[0]
            self.collapsedItems.setdefault(filename, [])
            if n not in self.collapsedItems[filename]:
                self.collapsedItems[filename].append(n)

    def _item_expanded(self, item):
        """When item expanded"""
        super(TreeSymbolsWidget, self).expandItem(item)

        n = self._get_unique_name(item)
        filename = self.actualSymbols[0]
        if n in self.collapsedItems.get(filename, []):
            self.collapsedItems[filename].remove(n)
            if not len(self.collapsedItems[filename]):
                # no more items, free space
                del self.collapsedItems[filename]

    def clean(self):
        """
        Reset the tree and reset attributes
        """
        self.tree.clear()
        self.collapsedItems = {}

    def reject(self):
        if self.parent() is None:
            self.dockWidget.emit(self)

    def closeEvent(self, event):
        """On Close event handling"""

        self.dockWidget.emit(self)
        # self.emit(SIGNAL("dockWidget(PyQt_PyObject)"), self)
        event.ignore()
Esempio n. 20
0
class BolumlemePencere(QWidget):
    def __init__(self, ebeveyn=None):
        super(BolumlemePencere, self).__init__(ebeveyn)
        self.ebeveyn = ebeveyn

        self.sistemDiski = ["", ""]
        self.takasDiski = ["", ""]
        self.seciliDisk = None
        self.diskler = parted.getAllDevices()
        disklerWidget = QWidget()
        disklerLayout = QHBoxLayout()
        self.disklerAcilirKutu = QComboBox()
        self.yenileButon = QPushButton(self.tr("Yenile"))
        self.yenileButon.pressed.connect(self.diskYenile)

        self.bolumListeKutu = QTreeWidget()
        self.bolumListeKutu.setColumnCount(4)
        self.bolumListeKutu.headerItem().setText(0, self.tr("bölüm"))
        self.bolumListeKutu.headerItem().setText(1, self.tr("kullanım"))
        self.bolumListeKutu.headerItem().setText(2, self.tr("boyut"))
        self.bolumListeKutu.headerItem().setText(3, self.tr("format"))
        # self.bolumListeKutu.headerItem().setText(4, self.tr("bayraklar"))
        self.bolumListeKutu.headerItem().setText(4, self.tr("bölüm numarası"))

        self.disklerAcilirKutu.currentIndexChanged.connect(self.diskDegisti)

        disklerLayout.addWidget(self.disklerAcilirKutu)
        disklerLayout.addWidget(self.yenileButon)
        disklerWidget.setLayout(disklerLayout)
        layout = QVBoxLayout()
        layout.addWidget(disklerWidget)
        layout.addWidget(self.bolumListeKutu)
        lejant = QLabel()
        lejant.setPixmap(QPixmap(":/gorseller/lejant.png"))
        lejant.setAlignment(Qt.AlignCenter)
        layout.addWidget(lejant)
        self.bolumListeKutu.itemClicked.connect(self.bolumSecildiFonk)
        self.bolumListeKutu.itemDoubleClicked.connect(self.bolumFormatSecFonk)

        opWidget = QWidget()
        opButonlar = QHBoxLayout()
        self.yeniBolumBtn = QPushButton(self.tr("Yeni Bölüm Ekle"))
        self.yeniBolumBtn.pressed.connect(self.bolumEkleFonk)
        self.bolumSilBtn = QPushButton(self.tr("Bölümü Sil"))
        self.bolumSilBtn.pressed.connect(self.bolumSilFonk)
        opButonlar.addWidget(self.yeniBolumBtn)
        opButonlar.addWidget(self.bolumSilBtn)
        opWidget.setLayout(opButonlar)
        layout.addWidget(opWidget)

        self.bolumSilBtn.setEnabled(False)
        self.setLayout(layout)
        self.diskYenile()

    def diskYenile(self):
        self.disklerAcilirKutu.clear()
        self.diskler = parted.getAllDevices()
        for disk in self.diskler:
            try:
                if parted.Disk(disk).type == "msdos":
                    self.disklerAcilirKutu.addItem("{} {} GB ({})".format(
                        disk.model, format(disk.getSize(unit="GB"), '.2f'),
                        disk.path),
                                                   userData=disk.path)
            except parted.DiskLabelException:
                disk = parted.freshDisk(disk, "msdos")
                # CDROM Aygıtları için
                try:
                    disk.commit()
                except parted.IOException:
                    pass
                else:
                    disk = disk.device
                    self.disklerAcilirKutu.addItem("{} {} GB ({})".format(
                        disk.model, format(disk.getSize(unit="GB"), '.2f'),
                        disk.path),
                                                   userData=disk.path)

    def diskDegisti(self):
        if self.disklerAcilirKutu.currentData():
            self.aygit = parted.getDevice(self.disklerAcilirKutu.currentData())
            self.ebeveyn.disk = parted.Disk(self.aygit)
            self.bolumListeYenile()

    def bolumListeYenile(self):
        self.extended = None
        self.bolumListeKutu.clear()
        for bolum in self.ebeveyn.disk.partitions:
            _bolum = self.bolumBilgi(bolum, "GB")
            if self.sistemDiski and bolum.path == self.sistemDiski[0]:
                if self.sistemDiski[1] == "evet":
                    item = self.treeWidgetItemOlustur(_bolum["yol"],
                                                      self.tr("Sistem Diski"),
                                                      _bolum["boyut"] + " GB",
                                                      "ext4",
                                                      _bolum["bayraklar"],
                                                      _bolum["no"])
                else:
                    item = self.treeWidgetItemOlustur(_bolum["yol"],
                                                      self.tr("Sistem Diski"),
                                                      _bolum["boyut"],
                                                      _bolum["dosyaSis"],
                                                      _bolum["bayraklar"],
                                                      _bolum["no"])
            elif self.takasDiski and bolum.path == self.takasDiski[0]:
                item = self.treeWidgetItemOlustur(_bolum["yol"],
                                                  self.tr("Takas Alanı"),
                                                  _bolum["boyut"],
                                                  self.tr("takas"),
                                                  _bolum["bayraklar"],
                                                  _bolum["no"])
            else:
                item = self.treeWidgetItemOlustur(_bolum["yol"], "",
                                                  _bolum["boyut"],
                                                  _bolum["dosyaSis"],
                                                  _bolum["bayraklar"],
                                                  _bolum["no"])

            if _bolum["tur"] == parted.PARTITION_NORMAL:
                item.setIcon(0, QIcon("gorseller/primary.xpm"))
            elif _bolum["tur"] == parted.PARTITION_EXTENDED:
                item.setIcon(0, QIcon("gorseller/extended.xpm"))
                self.extended = item
            elif _bolum["tur"] == parted.PARTITION_LOGICAL:
                item.setIcon(0, QIcon("gorseller/logical.xpm"))
                self.extended.addChild(item)
                self.extended.setExpanded(True)
            self.bolumListeKutu.addTopLevelItem(item)

        for bosBolum in self.ebeveyn.disk.getFreeSpacePartitions():
            _toplam = 0
            _bolum = self.bolumBilgi(bosBolum, "GB")
            if float(_bolum["boyut"]) > 1:
                if _bolum["tur"] == 5:
                    uzatilmisKalan = self.treeWidgetItemOlustur(
                        "", self.tr("Uzatılmış Bölüm Kalan"), _bolum["boyut"],
                        "", "", "ayrilmamis")
                    uzatilmisKalan.setIcon(0, QIcon(":/gorseller/blank.xpm"))
                    self.extended.addChild(uzatilmisKalan)
                    self.extended.setExpanded(True)
                if _bolum["tur"] == parted.PARTITION_FREESPACE:
                    _toplam = _toplam + float(_bolum["boyut"])
                ayrilmamis = self.treeWidgetItemOlustur(
                    "", self.tr("Ayrılmamış Bölüm"), _toplam, "", "",
                    "ayrilmamis")
                ayrilmamis.setIcon(0, QIcon(":/gorseller/blank.xpm"))
                self.bolumListeKutu.addTopLevelItem(ayrilmamis)
        self.bolumListeKutu.resizeColumnToContents(0)
        self.bolumListeKutu.resizeColumnToContents(1)
        self.bolumListeKutu.resizeColumnToContents(2)
        self.bolumListeKutu.resizeColumnToContents(3)

    def treeWidgetItemOlustur(self, bolum, kullanim, boyut, format, islev,
                              bolumno):
        item = QTreeWidgetItem()
        item.setText(0, str(bolum))
        item.setText(1, str(kullanim))
        item.setText(2, str(boyut) + " GB ")
        item.setText(3, str(format))
        # item.setText(4, str(islev))
        item.setText(4, str(bolumno))
        return item

    def bolumSecildiFonk(self, tiklanan):
        if tiklanan.text(4) != "ayrilmamis":
            self.bolumSilBtn.setEnabled(True)
        else:
            self.bolumSilBtn.setEnabled(False)

    def bolumFormatSecFonk(self, tiklanan):
        if tiklanan.text(4) != "ayrilmamis":
            self.seciliDisk = tiklanan
            diskOzellikPencere = diskOzellikleriSinif(self)
            diskOzellikPencere.exec_()
            if self.sistemDiski[0] != "":
                self.ebeveyn.kurparam["disk"]["bolum"] = self.sistemDiski[0]
                self.ebeveyn.kurparam["disk"]["format"] = self.sistemDiski[1]
            if self.takasDiski[0] != "":
                self.ebeveyn.kurparam["disk"]["takasbolum"] = self.takasDiski[
                    0]
            else:
                self.ebeveyn.kurparam["disk"]["takasbolum"] = ""

            if self.sistemDiski[0] == "":
                pass
            elif self.sistemDiski[0] != "" and self.takasDiski[0] == "":
                QMessageBox.information(
                    self, self.tr("Bilgi"),
                    self.
                    tr("Takas Alanı Belirtmediniz\nTakas alanı ram miktarınızın düşük olduğu durumlarda\nram yerine disk kullanarak işlemlerin devam etmesini sağlar."
                       ))
                self.ebeveyn.ileriDugme.setDisabled(False)
                self.bolumListeYenile()
            elif self.sistemDiski[0] != "" and self.takasDiski[0] != "":
                if self.sistemDiski[0] == self.takasDiski[0]:
                    QMessageBox.warning(
                        self, self.tr("Hata"), self.takasDiski[0] + self.
                        tr(" diskini hem sistem hem takas için seçtiniz\nAynı diski hem sistem hem takas olarak kullanmazsınız"
                           ))
                    self.ebeveyn.ileriDugme.setDisabled(True)
                else:
                    self.ebeveyn.ileriDugme.setDisabled(False)
                    self.bolumListeYenile()

    def bolumEkleFonk(self):
        if self._en_buyuk_bos_alan():
            alan = self._en_buyuk_bos_alan()
            birincilSayi = len(self.ebeveyn.disk.getPrimaryPartitions())
            uzatilmisSayi = ext_count = 1 if self.ebeveyn.disk.getExtendedPartition(
            ) else 0
            parts_avail = self.ebeveyn.disk.maxPrimaryPartitionCount - (
                birincilSayi + uzatilmisSayi)
            if not parts_avail and not ext_count:
                QMessageBox.warning(
                    self, self.tr("Uyarı"),
                    self.
                    tr("""Eğer dörtten fazla disk bölümü oluşturmak istiyorsanız birincil bölümlerden birini silip uzatılmış bölüm oluşturun. 
                                    Bu durumda oluşturduğunuz uzatılmış bölümleri işletim sistemi kurmak için kullanamayacağınızı aklınızda bulundurun."""
                       ))
            else:
                if parts_avail:
                    if not uzatilmisSayi and parts_avail > 1:
                        self.bolumOlustur(alan, parted.PARTITION_NORMAL)
                        self.bolumListeYenile()
                    elif parts_avail == 1:
                        self.bolumOlustur(alan, parted.PARTITION_EXTENDED)
                        self.bolumListeYenile()
                if uzatilmisSayi:
                    ext_part = self.ebeveyn.disk.getExtendedPartition()
                    try:
                        alan = ext_part.geometry.intersect(alan)
                    except ArithmeticError:
                        QMessageBox.critical(
                            self, self.tr("Hata"),
                            self.
                            tr("Yeni disk bölümü oluşturmak için yeterli alan yok ! Uzatılmış bölümün boyutunu arttırmayı deneyiniz."
                               ))
                    else:
                        self.bolumOlustur(alan, parted.PARTITION_LOGICAL)
                        self.bolumListeYenile()

    def bolumSilFonk(self):
        if self.bolumListeKutu.currentItem().text(4) != "ayrilmamis":
            bolumNo = int(self.bolumListeKutu.currentItem().text(4))
            for bolum in self.ebeveyn.disk.partitions:
                if bolum.number == bolumNo:
                    try:
                        self.ebeveyn.disk.deletePartition(bolum)
                        self.bolumListeYenile()
                    except parted.PartitionException:
                        QMessageBox.warning(
                            self, self.tr("Uyarı"),
                            self.
                            tr("Lütfen uzatılmış bölümleri silmeden önce mantıksal bölümleri siliniz."
                               ))
            self.bolumSilBtn.setDisabled(True)

    def bolumBilgi(self, bolum, birim):
        _bolum = {}
        _bolum["yol"] = bolum.path
        if birim == "GB":
            _bolum["boyut"] = format(bolum.getSize(unit=birim), '.2f')
        else:
            _bolum["boyut"] = bolum.getSize(unit=birim)
        _bolum["dosyaSis"] = "Bilinmeyen"

        if bolum.fileSystem:
            if bolum.fileSystem.type.startswith('linux-swap'):
                _bolum["dosyaSis"] = "takas"
            else:
                _bolum["dosyaSis"] = bolum.fileSystem.type
        try:
            _bolum["bayraklar"] = bolum.getFlagsAsString()
        except:
            pass
        _bolum["no"] = bolum.number
        _bolum["tur"] = bolum.type
        return _bolum

    def _en_buyuk_bos_alan(self):
        maks_boyut = -1
        alan = None
        alignment = self.aygit.optimumAlignment

        for _alan in self.ebeveyn.disk.getFreeSpaceRegions():
            if _alan.length > maks_boyut and _alan.length > alignment.grainSize:
                alan = _alan
                maks_boyut = _alan.length
        return alan

    def bolumOlustur(self, alan, bolumTur):
        if bolumTur == parted.PARTITION_NORMAL or bolumTur == parted.PARTITION_EXTENDED:
            for bosBolum in self.ebeveyn.disk.getFreeSpacePartitions():
                _bolum = self.bolumBilgi(bosBolum, "GB")
                if _bolum["tur"] == parted.PARTITION_FREESPACE:
                    maksBoyut = float(_bolum["boyut"])
        elif bolumTur == bolumTur == parted.PARTITION_LOGICAL:
            for bosBolum in self.ebeveyn.disk.getFreeSpacePartitions():
                _bolum = self.bolumBilgi(bosBolum, "GB")
                if _bolum["tur"] == 5:
                    maksBoyut = float(_bolum["boyut"])

        alignment = self.aygit.optimalAlignedConstraint
        constraint = self.aygit.getConstraint()
        data = {
            'start': constraint.startAlign.alignUp(alan, alan.start),
            'end': constraint.endAlign.alignDown(alan, alan.end),
        }

        boyut, ok = QInputDialog().getDouble(self,
                                             self.tr('Bölüm oluştur'),
                                             self.tr('GB cinsinden boyut:'),
                                             min=0.001,
                                             value=1,
                                             max=maksBoyut,
                                             decimals=3)

        if ok:
            data["end"] = int(data["start"]) + int(
                parted.sizeToSectors(float(boyut), "GiB",
                                     self.aygit.sectorSize))
            try:
                geometry = parted.Geometry(device=self.aygit,
                                           start=int(data["start"]),
                                           end=int(data["end"]))
                partition = parted.Partition(
                    disk=self.ebeveyn.disk,
                    type=bolumTur,
                    geometry=geometry,
                )

                self.ebeveyn.disk.addPartition(partition=partition,
                                               constraint=constraint)
            except (parted.PartitionException, parted.GeometryException,
                    parted.CreateException) as e:
                # GeometryException accounts for incorrect start/end values (e.g. start < end),
                # CreateException is raised e.g. when the partition doesn't fit on the disk.
                # PartedException is a generic error (e.g. start/end values out of range)
                raise RuntimeError(e.message)
Esempio n. 21
0
class MainWindow(QMainWindow):
    def __init__(self, screen_height, screen_width, version, parent=None):
        super(MainWindow, self).__init__(parent)

        self.screen_width = screen_width
        self.screen_height = screen_height
        self.version = version

        # basic main window settings
        self.resize(MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT)
        self.setWindowTitle('Linguistica {}'.format(self.version))

        # lexicon and lexicon tree
        self.lexicon = None
        self.lexicon_tree = None
        self.initialize_lexicon_tree()

        # set up major display, parameter window, then load main window
        self.majorDisplay = QWidget()
        self.parameterWindow = QWidget()
        self.load_main_window()

        # 'File' menu and actions
        select_corpus_action = self.create_action(text='&Select corpus...',
                                                  slot=self.corpus_dir_dialog,
                                                  tip='Select a corpus file',
                                                  shortcut='Ctrl+N')
        select_wordlist_action = self.create_action(text='&Select wordlist...',
                                                    slot=self.wordlist_dir_dialog,
                                                    tip='Select a wordlist file',
                                                    shortcut='Ctrl+W')
        run_file_action = self.create_action(text='&Run...',
                                             slot=self.run_file,
                                             tip='Run the input file',
                                             shortcut='Ctrl+D')
        parameters_action = self.create_action(text='&Parameters...',
                                               slot=self.parameters_dialog,
                                               tip='Change parameters',
                                               shortcut='Ctrl+P')

        file_menu = self.menuBar().addMenu('&File')
        file_menu.addAction(select_corpus_action)
        file_menu.addAction(select_wordlist_action)
        file_menu.addAction(run_file_action)
        file_menu.addAction(parameters_action)

        self.status = self.statusBar()
        self.status.setSizeGripEnabled(False)
        self.status.showMessage('No input file loaded. To select one: File --> '
                                'Select corpus... or Select wordlist...')

    def initialize_lexicon_tree(self):
        self.lexicon_tree = QTreeWidget()
        self.lexicon_tree.setEnabled(True)
        self.lexicon_tree.setMinimumWidth(TREEWIDGET_WIDTH_MIN)
        self.lexicon_tree.setMaximumWidth(TREEWIDGET_WIDTH_MAX)
        self.lexicon_tree.setMinimumHeight(TREEWIDGET_HEIGHT_MIN)
        self.lexicon_tree.setHeaderLabel('')
        self.lexicon_tree.setItemsExpandable(True)
        # noinspection PyUnresolvedReferences
        self.lexicon_tree.itemClicked.connect(self.tree_item_clicked)

    def create_action(self, text=None, slot=None, tip=None, shortcut=None):
        """
        This create actions for the File menu, things like
        Read Corpus, Rerun Corpus etc
        """
        action = QAction(text, self)
        if shortcut:
            action.setShortcut(shortcut)
        if tip:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot:
            # noinspection PyUnresolvedReferences
            action.triggered.connect(slot)
        if shortcut:
            # noinspection PyUnresolvedReferences
            QShortcut(QKeySequence(shortcut), self).activated.connect(slot)
        return action

    def _get_filename_from_dialog(self, ftype='input'):
        self.determine_last_file()
        if self.last_file_path and self.last_file_type == ftype:
            open_dir = self.last_file_path
        else:
            open_dir = os.getcwd()
        # noinspection PyTypeChecker,PyCallByClass
        fname = QFileDialog.getOpenFileName(self,
                                            'Select the {} file'.format(ftype),
                                            open_dir)
        process_all_gui_events()

        # HACK: fname is supposed to be a string (at least according to the
        # PyQt5 documentation), but for some reason fname is a tuple.
        # So we need this hack to make sure that fname is a string of a filename
        # -- Jackson Lee, 2015/06/22

        # update: it's turned out that this behavior is due to compatibility
        # between PyQt and PySide. The "tuple" behavior is in line with the
        # newer API2 for PyQt. (PyQt on python 3 uses API2 by default.)
        # more here: http://srinikom.github.io/pyside-bz-archive/343.html
        # so perhaps we keep our current hack for now?
        # -- Jackson Lee, 2015/08/24

        if fname and any(fname) and (type(fname) is tuple):
            return fname[0]
        else:
            # if this hack isn't needed somehow...
            return fname

    def corpus_dir_dialog(self):
        """
        Pop up the "open a file" dialog and ask for which corpus text file
        to use
        """
        self.corpus_filename = self._get_filename_from_dialog(ftype='corpus')

        process_all_gui_events()

        if type(self.corpus_filename) != str:
            return

        # note that self.corpus_filename is an absolute full path
        self.corpus_name = os.path.basename(self.corpus_filename)
        self.corpus_stem_name = Path(self.corpus_name).stem

        self.lexicon = read_corpus(self.corpus_filename)
        self.initialize_lexicon_tree()
        self.load_main_window(major_display=QWidget(),
                              parameter_window=QWidget())
        process_all_gui_events()

        self.status.clearMessage()
        self.status.showMessage(
            'Corpus selected: {}'.format(self.corpus_filename))

    def wordlist_dir_dialog(self):
        """
        Pop up the "open a file" dialog and ask for which corpus text file
        to use
        """
        self.corpus_filename = self._get_filename_from_dialog(ftype='wordlist')

        process_all_gui_events()

        if type(self.corpus_filename) != str:
            return

        # note that self.corpus_filename is an absolute full path
        self.corpus_name = os.path.basename(self.corpus_filename)
        self.corpus_stem_name = Path(self.corpus_name).stem

        self.lexicon = read_wordlist(self.corpus_filename)
        self.initialize_lexicon_tree()
        self.load_main_window(major_display=QWidget(),
                              parameter_window=QWidget())
        process_all_gui_events()

        self.status.clearMessage()
        self.status.showMessage(
            'Wordlist selected: {}'.format(self.corpus_filename))

    def parameters_dialog(self):
        if self.lexicon is None:
            warning = QMessageBox()
            warning.setIcon(QMessageBox.Warning)
            warning.setText('Parameters can only be accessed when an input '
                            'file is specified.')
            warning.setWindowTitle('No input file selected')
            warning.setStandardButtons(QMessageBox.Ok)
            warning.exec_()
            return

        process_all_gui_events()

        parameters = self.lexicon.parameters()
        dialog = QDialog()
        layout = QVBoxLayout()
        layout.addWidget(
            QLabel('Filename: {}'.format(Path(self.corpus_filename).name)))
        file_type = 'Wordlist' if self.lexicon.file_is_wordlist else 'Corpus'
        layout.addWidget(QLabel('Type: {}'.format(file_type)))

        grid = QGridLayout()
        self.parameter_spinboxes = [QSpinBox() for _ in range(len(parameters))]

        for i, parameter_name in enumerate(sorted(parameters.keys())):
            self.parameter_spinboxes[i].setObjectName(parameter_name)
            self.parameter_spinboxes[i].setRange(
                *PARAMETERS_RANGES[parameter_name])
            self.parameter_spinboxes[i].setValue(parameters[parameter_name])
            self.parameter_spinboxes[i].setSingleStep(1)
            # noinspection PyUnresolvedReferences
            self.parameter_spinboxes[i].valueChanged.connect(
                self.update_parameter)

            grid.addWidget(QLabel(parameter_name), i, 0)
            grid.addWidget(self.parameter_spinboxes[i], i, 1)
            grid.addWidget(QLabel(PARAMETERS_HINTS[parameter_name]), i, 2)

        layout.addLayout(grid)

        reset_button = QPushButton()
        reset_button.setText('&Reset')
        # noinspection PyUnresolvedReferences
        reset_button.clicked.connect(self.reset_parameters)

        spacer = QWidget()  # just for padding in tool_bar
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        tool_bar = QHBoxLayout()
        tool_bar.addWidget(spacer)  # so that the buttons are right-aligned
        tool_bar.addWidget(reset_button)

        layout.addLayout(tool_bar)

        dialog.setLayout(layout)
        dialog.setWindowTitle('Parameters')
        dialog.exec_()

    def reset_parameters(self):
        self.lexicon.use_default_parameters()

        for i, (_, value) in \
                enumerate(sorted(self.lexicon.parameters().items())):
            self.parameter_spinboxes[i].setValue(value)

    def update_parameter(self):
        for i in range(len(self.lexicon.parameters())):
            parameter_name, new_value = \
                self.parameter_spinboxes[i].objectName(), \
                self.parameter_spinboxes[i].value()
            self.lexicon.change_parameters(**{parameter_name: new_value})

    def update_progress(self, progress_text, target_percentage):
        """
        Update the progress dialog. This function is triggered by the
        "progress_signal" emitted from the linguistica component worker thread.
        """
        self.progressDialog.setLabelText(progress_text)
        self.progressDialog.setValue(target_percentage)
        process_all_gui_events()

    # noinspection PyProtectedMember
    def run_file(self):
        if self.lexicon is None:
            warning = QMessageBox()
            warning.setIcon(QMessageBox.Warning)
            warning.setText('No input file is selected.')
            warning.setWindowTitle('Error')
            warning.setStandardButtons(QMessageBox.Ok)
            warning.exec_()
            return

        self.status.clearMessage()
        self.status.showMessage('Running the file {} now...'
                                .format(self.corpus_name))

        print('\nInput file in use:\n{}\n'.format(self.corpus_filename),
              flush=True)

        # set up the Linguistica components worker
        # The worker is a QThread. We spawn this thread, and the linguistica
        # components run on this new thread but not the main thread for the GUI.
        # This makes the GUI still responsive
        # while the long and heavy running process of
        # the Linguistica components is ongoing.

        self.lxa_worker = LinguisticaWorker(self.lexicon)
        self.lxa_worker.progress_signal.connect(self.update_progress)

        # set up progress dialog

        process_all_gui_events()
        self.progressDialog = QProgressDialog()
        self.progressDialog.setRange(0, 100)  # it's like from 0% to 100%
        self.progressDialog.setLabelText('Initializing...')
        self.progressDialog.setValue(0)  # initialize as 0 (= 0%)
        self.progressDialog.setWindowTitle(
            'Processing {}'.format(self.corpus_name))
        self.progressDialog.setCancelButton(None)
        self.progressDialog.resize(400, 100)
        process_all_gui_events()

        self.progressDialog.show()

        # We disable the "cancel" button
        # Setting up a "cancel" mechanism may not be a good idea,
        # since it would probably involve killing the linguistica component
        # worker at *any* point of its processing.
        # This may have undesirable effects (e.g., freezing the GUI) -- BAD!

        # make sure all GUI stuff up to this point has been processed before
        # doing the real work of running the Lxa components
        process_all_gui_events()

        # Now the real work begins here!
        self.lxa_worker.start()

        process_all_gui_events()

        self.lexicon = self.lxa_worker.get_lexicon()

        print('\nAll Linguistica components run for the file', flush=True)
        self.status.clearMessage()
        self.status.showMessage('{} processed'.format(self.corpus_name))

        self.load_main_window(major_display=QWidget(),
                              parameter_window=QWidget())
        self.populate_lexicon_tree()
        self.update_last_file()
        process_all_gui_events()

        # display corpus name (in the tree header label)
        file_type = 'wordlist' if self.lexicon.file_is_wordlist else 'corpus'
        header_label = 'File: {}\nFile type: {}\n\n# word types: {:,}\n'.format(
            self.corpus_name, file_type, self.lexicon.number_of_word_types())
        if file_type == 'corpus':
            header_label += '# word tokens: {:,}\n'.format(
                self.lexicon.number_of_word_tokens())
        self.lexicon_tree.setHeaderLabel(header_label)

    @staticmethod
    def ensure_config_dir_exists():
        if not os.path.isdir(CONFIG_DIR):
            os.mkdir(CONFIG_DIR)

    def determine_last_file(self):
        self.last_file_path = None
        self.last_file_type = None
        self.last_file_encoding = None

        if not os.path.isfile(CONFIG_LAST_FILE):
            return

        with open(CONFIG_LAST_FILE, encoding='utf8') as f:
            config_last_file = json.load(f)

        self.last_file_path = config_last_file['last_file_path']
        self.last_file_type = config_last_file['last_file_type']
        self.last_file_encoding = config_last_file['last_file_encoding']

    def update_last_file(self):
        self.ensure_config_dir_exists()
        with open(CONFIG_LAST_FILE, 'w', encoding='utf8') as f:
            if self.lexicon.file_is_wordlist:
                file_type = 'wordlist'
            else:
                file_type = 'corpus'

            config = {'last_file_path': self.lexicon.file_abspath,
                      'last_file_type': file_type,
                      'last_file_encoding': self.lexicon.encoding,
                      }

            json.dump(config, f)

    def populate_lexicon_tree(self):
        self.lexicon_tree.clear()
        process_all_gui_events()

        # wordlist
        ancestor = QTreeWidgetItem(self.lexicon_tree, [WORDLIST])
        self.lexicon_tree.expandItem(ancestor)

        # word ngrams
        ancestor = QTreeWidgetItem(self.lexicon_tree, [WORD_NGRAMS])
        self.lexicon_tree.expandItem(ancestor)
        for item_str in [BIGRAMS, TRIGRAMS]:
            item = QTreeWidgetItem(ancestor, [item_str])
            self.lexicon_tree.expandItem(item)

        # signatures
        ancestor = QTreeWidgetItem(self.lexicon_tree, [SIGNATURES])
        self.lexicon_tree.expandItem(ancestor)
        for item in [SIGS_TO_STEMS, WORDS_TO_SIGS]:
            self.lexicon_tree.expandItem(QTreeWidgetItem(ancestor, [item]))

        # tries
        ancestor = QTreeWidgetItem(self.lexicon_tree, [TRIES])
        self.lexicon_tree.expandItem(ancestor)
        for item in [WORDS_AS_TRIES, SUCCESSORS, PREDECESSORS]:
            self.lexicon_tree.expandItem(QTreeWidgetItem(ancestor, [item]))

        # phonology
        ancestor = QTreeWidgetItem(self.lexicon_tree, [PHONOLOGY])
        self.lexicon_tree.expandItem(ancestor)
        for item in [PHONES, BIPHONES, TRIPHONES]:
            self.lexicon_tree.expandItem(QTreeWidgetItem(ancestor, [item]))

        # manifolds
        ancestor = QTreeWidgetItem(self.lexicon_tree, [MANIFOLDS])
        self.lexicon_tree.expandItem(ancestor)
        for item in [WORD_NEIGHBORS, VISUALIZED_GRAPH]:
            self.lexicon_tree.expandItem(QTreeWidgetItem(ancestor, [item]))

        self.status.clearMessage()
        self.status.showMessage('Navigation tree populated')
        print('Lexicon navigation tree populated', flush=True)

    def load_main_window(self, major_display=None, parameter_window=None):
        """
        Refresh the main window for the updated display content
        (most probably after a click or some event is triggered)
        """
        # get sizes of the three major PyQt objects
        major_display_size = self.majorDisplay.size()
        parameter_window_size = self.parameterWindow.size()
        lexicon_tree_size = self.lexicon_tree.size()

        if major_display:
            self.majorDisplay = major_display
        if parameter_window:
            self.parameterWindow = parameter_window

        # apply sizes to the major three objects
        self.majorDisplay.resize(major_display_size)
        self.parameterWindow.resize(parameter_window_size)
        self.lexicon_tree.resize(lexicon_tree_size)

        # set up:
        # 1) main splitter (b/w lexicon-tree+parameter window and major display)
        # 2) minor splitter (b/w lexicon-tree and parameter window)
        self.mainSplitter = QSplitter(Qt.Horizontal)
        self.mainSplitter.setHandleWidth(10)
        self.mainSplitter.setChildrenCollapsible(False)

        self.minorSplitter = QSplitter(Qt.Vertical)
        self.minorSplitter.setHandleWidth(10)
        self.minorSplitter.setChildrenCollapsible(False)

        self.minorSplitter.addWidget(self.lexicon_tree)
        self.minorSplitter.addWidget(self.parameterWindow)

        self.mainSplitter.addWidget(self.minorSplitter)
        self.mainSplitter.addWidget(self.majorDisplay)

        self.setCentralWidget(self.mainSplitter)

    def sig_to_stems_clicked(self, row):
        signature = self.sig_to_stems_major_table.item(row, 0).text()
        print(signature)
        signature = tuple(signature.split(SEP_SIG))

        stems = sorted(self.lexicon.signatures_to_stems()[signature])
        number_of_stems_per_column = 5

        # create a master list of sublists, where each sublist contains k stems
        # k = number_of_stems_per_column
        stem_rows = list()
        stem_row = list()

        for i, stem in enumerate(stems, 1):
            stem_row.append(stem)
            if not i % number_of_stems_per_column:
                stem_rows.append(stem_row)
                stem_row = list()
        if stem_row:
            stem_rows.append(stem_row)

        # set up the minor table as table widget
        sig_to_stems_minor_table = QTableWidget()
        sig_to_stems_minor_table.horizontalHeader().hide()
        sig_to_stems_minor_table.verticalHeader().hide()
        sig_to_stems_minor_table.clear()
        sig_to_stems_minor_table.setRowCount(len(stem_rows))
        sig_to_stems_minor_table.setColumnCount(number_of_stems_per_column)

        # fill in the minor table
        for row, stem_row in enumerate(stem_rows):
            for col, stem in enumerate(stem_row):
                item = QTableWidgetItem(stem)
                sig_to_stems_minor_table.setItem(row, col, item)

        sig_to_stems_minor_table.resizeColumnsToContents()

        minor_table_title = QLabel('{} (number of stems: {})'
                                   .format(SEP_SIG.join(signature), len(stems)))

        minor_table_widget_with_title = QWidget()
        layout = QVBoxLayout()
        layout.addWidget(minor_table_title)
        layout.addWidget(sig_to_stems_minor_table)
        minor_table_widget_with_title.setLayout(layout)

        new_display = QSplitter(Qt.Horizontal)
        new_display.setHandleWidth(10)
        new_display.setChildrenCollapsible(False)

        new_display.addWidget(self.sig_to_stems_major_table)
        new_display.addWidget(minor_table_widget_with_title)
        new_display_width = self.majorDisplay.width() / 2
        new_display.setSizes(
            [new_display_width * 0.4, new_display_width * 0.6])

        self.load_main_window(major_display=new_display)
        self.status.clearMessage()
        self.status.showMessage('{} selected'.format(signature))

    def unavailable_for_wordlist(self):
        self.load_main_window(major_display=QWidget(),
                              parameter_window=QWidget())
        self.status.showMessage('')
        warning = QMessageBox()
        warning.setIcon(QMessageBox.Warning)
        warning.setText('Unavailable for a wordlist')
        warning.setWindowTitle('Error')
        warning.setStandardButtons(QMessageBox.Ok)
        warning.exec_()

    def tree_item_clicked(self, item):
        """
        Trigger the appropriate action when something in the lexicon tree
        is clicked, and update the major display plus parameter window
        """
        item_str = item.text(0)

        if item_str in {WORD_NGRAMS, SIGNATURES, TRIES, PHONOLOGY, MANIFOLDS}:
            return

        print('loading', item_str, flush=True)

        self.status.clearMessage()
        self.status.showMessage('Loading {}...'.format(item_str))

        new_display = None
        new_parameter_window = None

        if item_str == WORDLIST:
            new_display = self.create_major_display_table(
                self.lexicon.word_phonology_dict().items(),
                key=lambda x: x[1].count, reverse=True,
                headers=['Word', 'Count', 'Frequency', 'Phones',
                         'Unigram plog', 'Avg unigram plog',
                         'Bigram plog', 'Avg bigram plog'],
                row_cell_functions=[
                    lambda x: x[0], lambda x: x[1].count,
                    lambda x: x[1].frequency,
                    lambda x: ' '.join(x[1].phones),
                    lambda x: x[1].unigram_plog,
                    lambda x: x[1].avg_unigram_plog,
                    lambda x: x[1].bigram_plog,
                    lambda x: x[1].avg_bigram_plog],
                cutoff=0)

        elif item_str == BIGRAMS:
            if self.lexicon.file_is_wordlist:
                self.unavailable_for_wordlist()
                return
            new_display = self.create_major_display_table(
                self.lexicon.word_bigram_counter().items(),
                key=lambda x: x[1], reverse=True,
                headers=['Bigram', 'Count'],
                row_cell_functions=[lambda x: SEP_NGRAM.join(x[0]),
                                    lambda x: x[1]],
                cutoff=2000)

        elif item_str == TRIGRAMS:
            if self.lexicon.file_is_wordlist:
                self.unavailable_for_wordlist()
                return
            new_display = self.create_major_display_table(
                self.lexicon.word_trigram_counter().items(),
                key=lambda x: x[1], reverse=True,
                headers=['Trigram', 'Count'],
                row_cell_functions=[lambda x: SEP_NGRAM.join(x[0]),
                                    lambda x: x[1]],
                cutoff=2000)

        elif item_str == SIGS_TO_STEMS:
            self.sig_to_stems_major_table = self.create_major_display_table(
                self.lexicon.signatures_to_stems().items(),
                key=lambda x: len(x[1]), reverse=True,
                headers=['Signature', 'Stem count', 'A few stems'],
                row_cell_functions=[lambda x: SEP_SIG.join(x[0]),
                                    lambda x: len(x[1]),
                                    lambda x: ', '.join(sorted(x[1])[:2]) +
                                              ', ...'],
                cutoff=0)
            # noinspection PyUnresolvedReferences
            self.sig_to_stems_major_table.cellClicked.connect(
                self.sig_to_stems_clicked)
            new_display = self.sig_to_stems_major_table

        elif item_str == WORDS_TO_SIGS:
            new_display = self.create_major_display_table(
                self.lexicon.words_to_signatures().items(),
                key=lambda x: len(x[1]), reverse=True,
                headers=['Word', 'Signature count', 'Signatures'],
                row_cell_functions=[lambda x: x[0],
                                    lambda x: len(x[1]),
                                    lambda x: ', '.join([SEP_SIG.join(sig)
                                                         for sig in
                                                         sorted(x[1])])],
                cutoff=2000)

        elif item_str == WORDS_AS_TRIES:
            words = self.lexicon.broken_words_left_to_right().keys()
            words_to_tries = dict()
            # key: word (str)
            # value: tuple of (str, str)
            # for left-to-right and right-to-left tries

            for word in words:
                l_r = ' '.join(self.lexicon.broken_words_left_to_right()[word])
                r_l = ' '.join(self.lexicon.broken_words_right_to_left()[word])
                words_to_tries[word] = (l_r, r_l)  # left-right, right-left

            new_display = self.create_major_display_table(
                words_to_tries.items(),
                key=lambda x: x[0], reverse=False,
                headers=['Word', 'Reversed word',
                         'Left-to-right trie', 'Right-to-left trie'],
                row_cell_functions=[lambda x: x[0], lambda x: x[0][::-1],
                                    lambda x: x[1][0], lambda x: x[1][1]],
                cutoff=0, set_text_alignment=[(3, Qt.AlignRight)])

        elif item_str == SUCCESSORS:
            new_display = self.create_major_display_table(
                self.lexicon.successors().items(),
                key=lambda x: len(x[1]), reverse=True,
                headers=['String', 'Successor count', 'Successors'],
                row_cell_functions=[lambda x: x[0],
                                    lambda x: len(x[1]),
                                    lambda x: ', '.join(sorted(x[1]))],
                cutoff=0)

        elif item_str == PREDECESSORS:
            new_display = self.create_major_display_table(
                self.lexicon.predecessors().items(),
                key=lambda x: len(x[1]), reverse=True,
                headers=['String', 'Predecessor count', 'Predecessors'],
                row_cell_functions=[lambda x: x[0],
                                    lambda x: len(x[1]),
                                    lambda x: ', '.join(sorted(x[1]))],
                cutoff=0)

        elif item_str == PHONES:
            new_display = self.create_major_display_table(
                self.lexicon.phone_dict().items(),
                key=lambda x: x[1].count, reverse=True,
                headers=['Phone', 'Count', 'Frequency', 'Plog'],
                row_cell_functions=[lambda x: x[0],
                                    lambda x: x[1].count,
                                    lambda x: x[1].frequency,
                                    lambda x: x[1].plog],
                cutoff=0)

        elif item_str == BIPHONES:
            new_display = self.create_major_display_table(
                self.lexicon.biphone_dict().items(),
                key=lambda x: x[1].count, reverse=True,
                headers=['Biphone', 'Count', 'Frequency',
                         'Mutual information (MI)', 'Weighted MI'],
                row_cell_functions=[lambda x: SEP_NGRAM.join(x[0]),
                                    lambda x: x[1].count,
                                    lambda x: x[1].frequency,
                                    lambda x: x[1].MI,
                                    lambda x: x[1].weighted_MI],
                cutoff=0)

        elif item_str == TRIPHONES:
            new_display = self.create_major_display_table(
                self.lexicon.phone_trigram_counter().items(),
                key=lambda x: x[1], reverse=True,
                headers=['Triphone', 'Count'],
                row_cell_functions=[lambda x: SEP_NGRAM.join(x[0]),
                                    lambda x: x[1]],
                cutoff=0)

        elif item_str == WORD_NEIGHBORS:
            if self.lexicon.file_is_wordlist:
                self.unavailable_for_wordlist()
                return
            word_to_freq = self.lexicon.word_unigram_counter()
            new_display = self.create_major_display_table(
                self.lexicon.words_to_neighbors().items(),
                key=lambda x: word_to_freq[x[0]], reverse=True,
                headers=['Word', 'Word count', 'Neighbors'],
                row_cell_functions=[lambda x: x[0],
                                    lambda x: word_to_freq[x[0]],
                                    lambda x: ' '.join(x[1])],
                cutoff=0)

        elif item_str == VISUALIZED_GRAPH:
            if self.lexicon.file_is_wordlist:
                self.unavailable_for_wordlist()
                return

            graph_width = self.screen_width - TREEWIDGET_WIDTH_MAX - 50
            graph_height = self.screen_height - 70
            html_name = 'show_manifold.html'

            manifold_name = '{}_manifold.json'.format(self.corpus_stem_name)
            manifold_filename = os.path.join(CONFIG_DIR, manifold_name)
            print('manifold_filename', manifold_filename)

            manifold_json_data = json_graph.node_link_data(
                self.lexicon.neighbor_graph())
            json.dump(manifold_json_data, open(manifold_filename, 'w'))

            viz_html = os.path.join(CONFIG_DIR, html_name)
            print('viz_html', viz_html)

            # write the show_manifold html file
            with open(viz_html, 'w') as f:
                print(SHOW_MANIFOLD_HTML.format(os.path.dirname(__file__),
                                                graph_width, graph_height,
                                                manifold_filename), file=f)

            url = Path(viz_html).as_uri()
            print('url:', url)

            new_display = QWebView()
            new_display.setUrl(QUrl(url))

        self.load_main_window(major_display=new_display,
                              parameter_window=new_parameter_window)

        self.status.clearMessage()
        self.status.showMessage('{} selected'.format(item_str))

    @staticmethod
    def create_major_display_table(input_iterable,
                                   key=lambda x: x, reverse=False,
                                   headers=None, row_cell_functions=None,
                                   cutoff=0,
                                   set_text_alignment=None):
        """
        This is a general function for creating a tabular display for the
        major display.
        """

        if not input_iterable:
            print('Warning: input is empty', flush=True)
            return

        if not hasattr(input_iterable, '__iter__'):
            print('Warning: input is not an iterable', flush=True)
            return

        number_of_headers = len(headers)
        number_of_columns = len(row_cell_functions)

        if number_of_headers != number_of_columns:
            print('headers and cell functions don\'t match', flush=True)
            return

        len_input = len(input_iterable)

        table_widget = QTableWidget()
        table_widget.clear()
        table_widget.setSortingEnabled(False)

        # set up row count
        if cutoff and cutoff < len_input:
            actual_cutoff = cutoff
        else:
            actual_cutoff = len_input

        table_widget.setRowCount(actual_cutoff)

        # set up column count and table headers
        table_widget.setColumnCount(number_of_headers)
        table_widget.setHorizontalHeaderLabels(headers)

        # fill in the table
        for row, x in enumerate(double_sorted(input_iterable, key=key,
                                              reverse=reverse)):
            for col, fn in enumerate(row_cell_functions):
                cell = fn(x)

                if isinstance(cell, (int, float)):
                    # cell is numeric
                    item = QTableWidgetItem()
                    item.setData(Qt.EditRole, cell)
                else:
                    # cell is not numeric
                    item = QTableWidgetItem(cell)

                if set_text_alignment:
                    for align_col, alignment in set_text_alignment:
                        if col == align_col:
                            item.setTextAlignment(alignment)

                table_widget.setItem(row, col, item)

            if not row < actual_cutoff:
                break

        table_widget.setSortingEnabled(True)
        table_widget.resizeColumnsToContents()

        return table_widget
Esempio n. 22
0
class capture(QWidget):
    filterApplied = pyqtSignal()

    def __init__(self):
        super().__init__()
        self.initUI()
        self.packet_list = []

    def initUI(self):
        mainLayout = QVBoxLayout(self)

        filter_layout = QHBoxLayout()
        filter_label = QLabel('Filter')
        self.filter_lineEdit = QLineEdit()
        filter_apply_btn = QPushButton('Apply')
        filter_layout.addWidget(filter_label)
        filter_layout.addWidget(self.filter_lineEdit)
        filter_layout.addWidget(filter_apply_btn)
        mainLayout.addLayout(filter_layout)
        self.filter = ''

        splitterMain = QSplitter(Qt.Vertical, self)
        self.QuickView = QTableWidget(splitterMain)
        #self.QuickView.setUniformItemSizes(True)
        self.QuickView.setColumnCount(6)
        self.QuickView.setHorizontalHeaderLabels(
            ['No.', 'Time', 'Source', 'Destination', 'Protocol', 'Size'])
        self.QuickView.setColumnWidth(0, 60)
        self.QuickView.verticalHeader().setVisible(False)
        self.QuickView.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeToContents)
        # self.QuickView.horizontalHeader().setStretchLastSection(True)
        self.QuickView.setShowGrid(False)
        self.QuickView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.QuickView.setSelectionMode(QTableWidget.ExtendedSelection)
        self.QuickView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # self.QuickView.setLayoutMode(QListView.Batched)
        # self.QuickView.setBatchSize(20)
        self.DetailView = QTreeWidget(splitterMain)
        self.DetailView.setColumnCount(2)
        self.DetailView.setHeaderLabels(['Item', 'Detail'])
        mainLayout.addWidget(splitterMain)

        bottomLayout = QHBoxLayout()
        self.start_btn = QPushButton('Start')
        self.stop_btn = QPushButton('Stop')
        self.restart_btn = QPushButton('Restart')
        self.clear_btn = QPushButton('Clear')
        self.intercept_CheckBox = QCheckBox('Intercept Packets')
        bottomLayout.addWidget(self.start_btn)
        bottomLayout.addWidget(self.stop_btn)
        bottomLayout.addWidget(self.restart_btn)
        bottomLayout.addWidget(self.clear_btn)
        bottomLayout.addWidget(self.intercept_CheckBox)
        bottomLayout.addStretch()
        self.stop_btn.setEnabled(False)
        self.restart_btn.setEnabled(False)
        mainLayout.addLayout(bottomLayout)

        self.start_btn.clicked.connect(self.start_sniff)
        # filter_apply_btn.clicked.connect(self.apply_filter)
        self.stop_btn.clicked.connect(self.stop_sniff)
        self.QuickView.currentItemChanged.connect(self.show_current_detail)
        self.restart_btn.clicked.connect(self.restart_sniff)
        self.clear_btn.clicked.connect(self.clear_widget)
        self.count = 0

    def start_sniff(self):
        self.cap_thread = capturethread()
        self.cap_thread.newPkt.connect(self.init_display)
        self.cap_thread.start()

        self.start_btn.setEnabled(False)
        self.stop_btn.setEnabled(True)
        self.restart_btn.setEnabled(False)

    def init_display(self, item_list, pkt):
        self.packet_list.append(pkt)
        self.QuickView.insertRow(self.QuickView.rowCount())

        for i in range(6):
            self.QuickView.setItem(self.QuickView.rowCount() - 1, i,
                                   item_list[i])

    # def apply_filter(self):
    #     filter_queue=CaptureQueue.get_filter()
    #     filter_queue.queue.clear()
    #     filter_queue.put(self.filter_lineEdit.text())
    #     if self.cap_thread.isRunning():
    #         self.cap_thread.set_stopper(True)
    #         self.cap_thread.wait()
    #     self.cap_thread.start()

    #     filter_pkt_queue=CaptureQueue.get_packet_to_filter()
    #     filter_pkt_queue.put(self.packet_list)
    #     filter_pkt_queue.put(self.filter_lineEdit.text())
    #     self.filter_thread=filterthread()
    #     self.filter_thread.start()
    #     self.filter_thread.filtered.connect(self.get_filtered_pkt)

    # def get_filtered_pkt(self,packetlist):
    #     pass

    # def formatString(self, tmp):
    #     self.final_dict = collections.OrderedDict()
    #     title_pattern = re.compile(r'###[ [a-zA-Z]+ ]###')  #abstract titles
    #     tmp_titles = title_pattern.findall(tmp)
    #     self.titles = []
    #     for title in tmp_titles:
    #         refine_pattern = re.compile(r'###\[ | \]###')
    #         self.titles.append(refine_pattern.sub('', title))
    #     #print(self.titles)

    #     content_split_pattern = title_pattern  #abstract contents
    #     tmp_content = re.split(content_split_pattern, tmp)
    #     self.contents = [i for i in tmp_content if i != '']
    #     #print(self.contents)

    #     for (title, content) in zip(self.titles, self.contents):
    #         tmp_dict = {}
    #         tmp_lists = re.split(r'\n', content)
    #         tmp_lists = [i.replace(' ', '') for i in tmp_lists if i != '']

    #         #print(tmp_lists)
    #         for i in tmp_lists:
    #             tmp_item = i.split('=')
    #             #print(tmp_item)
    #             if len(tmp_item) == 2:
    #                 tmp_dict[tmp_item[0]] = tmp_item[1]
    #         self.final_dict[title] = tmp_dict
    #     #print(self.final_dict)

    def buildTree(self):
        self.DetailView.clear()
        for title in self.packetDict.keys():
            tree_item = QTreeWidgetItem(self.DetailView)
            tree_item.setText(0, title)
            tree_item.setExpanded(True)
            detail_dic = self.packetDict[title]
            for i in detail_dic.keys():
                leaf = QTreeWidgetItem(tree_item, [i, str(detail_dic[i])])
                leaf.setToolTip(1, str(detail_dic[i]))
                tree_item.addChild(leaf)
            self.DetailView.addTopLevelItem(tree_item)

    def stop_sniff(self):
        self.cap_thread.set_stopper(True)

        self.start_btn.setEnabled(True)
        self.restart_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)

    def restart_sniff(self):
        self.pkt_queue = CaptureQueue.get_pkt()
        self.label_queue = CaptureQueue.get_label()
        with self.label_queue.mutex:
            self.label_queue.queue.clear()
        with self.pkt_queue.mutex:
            self.pkt_queue.queue.clear()
        self.packet_list.clear()
        self.QuickView.clearContents()
        self.DetailView.clear()
        self.start_sniff()

    def show_current_detail(self):
        if self.packet_list:
            pkt = self.packet_list[self.QuickView.currentRow()]
            # self.text = FakeOut()
            # old = sys.stdout
            # sys.stdout = self.text
            # packet.show2()
            # sys.stdout = old
            # tmp = self.text.str
            # self.formatString(tmp)
            self.packetDict = scapy2ordereddict.to_dict(pkt)
            self.buildTree()

    def clear_widget(self):
        self.pkt_queue = CaptureQueue.get_pkt()
        self.label_queue = CaptureQueue.get_label()
        with self.label_queue.mutex:
            self.label_queue.queue.clear()
        with self.pkt_queue.mutex:
            self.pkt_queue.queue.clear()
        self.packet_list.clear()
        self.QuickView.clearContents()
        self.DetailView.clear()
class Waterfall(QWidget, waterfall.Ui_Waterfall):
    
    general_settings_signal = QtCore.pyqtSignal(list) #send list of plotting params
    updated_rectangles_signal = QtCore.pyqtSignal(list) #send list of updated artists for redrawing

    def __init__(self, parent):
        super(Waterfall,self).__init__(parent)
        self.setupUi(self)
        
        self.test = {'a':'red','b':'green','c':'blue'}
        #Button functions
        self.btn_apply_general_settings.clicked.connect(self.send_settings)
        self.patient_tree = self.create_patient_tree()
        self.data_viewer_container.addWidget(self.patient_tree)
        self.get_settings()

    def get_settings(self):
        try:
            with shelve.open('WaterfallSettings') as shelfFile: 
                self.keys_and_colors = shelfFile['keys_and_colors']
                shelfFile.close()
        except:
            #default
            self.keys_and_colors = {'CR':,'PR':,'PD':,'SD':,''}

    def on_waterfall_data_signal(self,signal):
        self.waterfall_data = signal['waterfall_data'] #pandas dataframe
        
    def on_generated_rectangles_signal(self,signal):
        self.rectangles_received = signal[0]
        self.add_items() #display in table

    def send_settings(self,signal):
        self.list_general_settings = [
                                        self.plot_title.text(),
                                        self.x_label.text(),
                                        self.y_label.text(),
                                        self.twenty_percent_line.isChecked(),
                                        self.thirty_percent_line.isChecked(),
                                        self.zero_percent_line.isChecked(),
                                        self.display_responses_as_text.isChecked()
                                    ]
        self.general_settings_signal.emit(self.list_general_settings)

    def create_patient_tree(self):
            '''
            Create QTreeWidget populated with a patient's data for the DataEntry dialog.
            Assumes that self.temp_patient is the patient of interest and that the variable belongs to the dialog.
            '''
            self.tree = QTreeWidget()
            self.root = self.tree.invisibleRootItem()
            self.headers = [
                            'Patient #',
                            'Best response %',
                            'Response',
                            'Cancer',
                            'Color key',
                            ]
            self.headers_item = QTreeWidgetItem(self.headers)
            self.tree.setColumnCount(len(self.headers))
            self.tree.setHeaderItem(self.headers_item)
            self.root.setExpanded(True)
            self.tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
            self.tree.header().setStretchLastSection(False)
            return self.tree

    def add_items(self):
        '''
        Populate viewing tree
        '''
        self.tree.clear() #clear prior to entering items, prevent aggregation
        i=0
        for rect in self.rectangles_received:
            #populate editable tree with rect data
            self.rect_item = QTreeWidgetItem(self.root)
            self.rect_params = [
                                self.waterfall_data['Patient number'][i], 
                                rect.get_height(),
                                self.waterfall_data['Overall response'][i],
                                self.waterfall_data['Cancer'][i]
                                ]
            for col in range(0,4):
                self.rect_item.setText(col,str(self.rect_params[col]))
                self.rect_item.setTextAlignment(col,4)
            self.tree.setItemWidget(self.rect_item, 4, CustomCombo(self,self.test))
            self.rect_item.setFlags(self.rect_item.flags() | QtCore.Qt.ItemIsEditable)
            i+=1
        
    def on_updated_tree_item(self):
        #update the rectangle which was edited
        pass
Esempio n. 24
0
class CueListDialog(QDialog):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.setMinimumSize(400, 300)

        self._cues = []

        self.list = QTreeWidget(self)
        self.list.setSelectionMode(self.list.SingleSelection)
        self.list.setSelectionBehavior(self.list.SelectRows)
        self.list.setAlternatingRowColors(True)
        self.list.setIndentation(0)
        self.list.setHeaderLabels(['Index', 'Name'])
        self.list.header().setSectionResizeMode(QHeaderView.Fixed)
        self.list.header().setSectionResizeMode(1, QHeaderView.Stretch)
        self.list.header().setStretchLastSection(False)

        self.setLayout(QVBoxLayout())
        self.layout().addWidget(self.list)

        self.buttons = QDialogButtonBox(self)
        self.buttons.addButton(QDialogButtonBox.Cancel)
        self.buttons.addButton(QDialogButtonBox.Ok)
        self.layout().addWidget(self.buttons)

        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)

    def add_cue(self, cue):
        try:
            item = QTreeWidgetItem()
            item.setTextAlignment(0, Qt.AlignCenter)

            for n, prop in enumerate(['index', 'name']):
                item.setText(n, str(cue.properties().get(prop, 'Undefined')))

            self._cues.append(cue)
            self.list.addTopLevelItem(item)
        except Exception:
            pass

    def add_cues(self, cues):
        for cue in cues:
            self.add_cue(cue)

    def remove_cue(self, index):
        self.list.takeTopLevelItem(index)
        return self._cues.pop(index)

    def reset(self):
        self.list.clear()
        self._cues.clear()

    def selected_cues(self):
        cues = []
        for item in self.list.selectedItems():
            index = self.list.indexOfTopLevelItem(item)
            cues.append(self._cues[index])
        return cues
Esempio n. 25
0
class Waterfall(QWidget, waterfall.Ui_Waterfall):
    
    plot_settings_signal = QtCore.pyqtSignal(list) #send list of plotting params
    updated_rectangles_signal = QtCore.pyqtSignal(list) #send list of updated artists for redrawing

    def __init__(self, parent):
        super(Waterfall,self).__init__(parent)
        self.setupUi(self)
        
        self.get_settings()
        self.send_settings()

        #Button functions
        self.btn_apply_general_settings.clicked.connect(self.send_settings)
        self.btn_apply_keys_and_colors_settings.clicked.connect(self.send_settings)
        self.patient_tree = self.create_patient_tree()
        self.data_viewer_container.addWidget(self.patient_tree)
        self.btn_color_test.clicked.connect(self.get_color)
        
    def get_color(self):
        self.color = QColorDialog.getColor() #returns a color object
        print(color)

    def get_settings(self):
        try:
            with shelve.open('WaterfallSettings') as shelfFile: 
                self.keys_and_colors = shelfFile['keys_and_colors']
                shelfFile.close()
        except:
            #set and use default settings
            self.keys_and_colors = {
                                    'CR':'#03945D',
                                    'PR':'#B1EE97',
                                    'PD':'#FF6F69',
                                    'SD':'#707070'}
            with shelve.open('WaterfallSettings') as shelfFile:
                shelfFile['keys_and_colors'] = self.keys_and_colors
                shelfFile.close()
        
    def on_waterfall_data_signal(self,signal):
        self.waterfall_data = signal['waterfall_data'] #pandas dataframe
        
    def on_generated_rectangles_signal(self,signal):
        self.rectangles_received = signal[0]
        self.add_items() #display in table
        self.btn_apply_general_settings.setEnabled(True)
        self.btn_finalize_plot.setEnabled(True)
        self.btn_apply_keys_and_colors_settings.setEnabled(True)

    def send_settings(self):
        '''
        Emit both general plot settings, and color labeling settings. These are the settings to be used when the plot is created.
        '''
        self.general_settings = [
                                        self.plot_title.text(),
                                        self.x_label.text(),
                                        self.y_label.text(),
                                        [self.twenty_percent_line.isChecked(),
                                        self.thirty_percent_line.isChecked(),
                                        self.zero_percent_line.isChecked()],
                                        [self.display_responses_as_text.isChecked(),
                                        self.display_responses_as_color.isChecked(),
                                        self.display_no_responses.isChecked()],
                                        self.include_table.isChecked(),
                                        self.show_cancer_type.isChecked(),
                                        self.updated_color_coding
                                    ]
        self.plot_settings_signal.emit(self.general_settings)


    def get_updating_color_coding(self):
        

    def create_patient_tree(self):
            '''
            Create QTreeWidget populated with a patient's data for the DataEntry dialog.
            Assumes that self.temp_patient is the patient of interest and that the variable belongs to the dialog.
            '''
            self.tree = QTreeWidget()
            self.root = self.tree.invisibleRootItem()
            self.headers = [
                            'Patient #',
                            'Best response %',
                            'Response',
                            'Cancer',
                            'Color key',
                            ]
            self.headers_item = QTreeWidgetItem(self.headers)
            self.tree.setColumnCount(len(self.headers))
            self.tree.setHeaderItem(self.headers_item)
            self.root.setExpanded(True)
            self.tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
            self.tree.header().setStretchLastSection(False)
            return self.tree

    def add_items(self):
        '''
        Populate viewing tree
        '''
        self.tree.clear() #clear prior to entering items, prevent aggregation
        i=0
        for rect in self.rectangles_received:
            #populate editable tree with rect data
            self.rect_item = QTreeWidgetItem(self.root)
            self.rect_params = [
                                self.waterfall_data['Patient number'][i], 
                                rect.get_height(),
                                self.waterfall_data['Overall response'][i],
                                self.waterfall_data['Cancer'][i]
                                ]
            for col in range(0,4):
                self.rect_item.setText(col,str(self.rect_params[col]))
                self.rect_item.setTextAlignment(col,4)
            self.tree.setItemWidget(self.rect_item, 4, CustomCombo(self,self.keys_and_colors,self.waterfall_data['Overall response'][i]))
            self.rect_item.setFlags(self.rect_item.flags() | QtCore.Qt.ItemIsEditable)
            i+=1
        
    def on_updated_tree_item(self):
        #update the rectangle which was edited
        pass

class WaterfallPlotter(QWidget):

    generated_rectangles_signal = QtCore.pyqtSignal(list) #send list of rects for data display in tree

    def __init__(self,parent):
        super(WaterfallPlotter,self).__init__(parent)

        self.get_settings()
        self.settings_update = False

        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)

        self.toolbar = NavigationToolbar(self.canvas,self)

        self.btn_plot = QPushButton('Default Plot')
        self.btn_plot.clicked.connect(self.default_plot)

        self.layout = QVBoxLayout()
        self.layout.addWidget(self.toolbar)
        self.layout.addWidget(self.canvas)
        self.layout.addWidget(self.btn_plot)
        self.setLayout(self.layout)
        
    
    def on_waterfall_data_signal(self,signal):
        self.waterfall_data = signal['waterfall_data'] #pandas dataframe
        self.btn_plot.setEnabled(True)

    def get_settings(self):
        try:
            with shelve.open('WaterfallSettings') as shelfFile: 
                self.keys_and_colors = shelfFile['keys_and_colors']
                shelfFile.close()
        except:
            #set and use default settings
            self.keys_and_colors = {
                                    'CR':'#03945D',
                                    'PR':'#B1EE97',
                                    'PD':'#FF6F69',
                                    'SD':'#707070'}
            with shelve.open('WaterfallSettings') as shelfFile:
                shelfFile['keys_and_colors'] = self.keys_and_colors
                shelfFile.close()

    def on_general_settings_signal(self,signal):
        self.gen_settings = signal
        self.settings_update = True
        self.default_plot()
    
    def get_bar_colors(self,responses):
        return [self.keys_and_colors[x] for x in responses]

    def default_plot(self):
        '''
        Plot waterfall data
        '''            
        self.figure.clear()
        self.rect_locations = np.arange(len(self.waterfall_data['Best response percent change']))
        self.ax = self.figure.add_subplot(111)
        self.bar_colors = self.get_bar_colors(self.waterfall_data['Overall response'])

        if self.settings_update == False:
            self.ax.tick_params(
                            axis='x',          # changes apply to the x-axis
                            which='both',      # both major and minor ticks are affected
                            bottom='on',      # ticks along the bottom edge are off
                            top='on',         # ticks along the top edge are off
                            labelbottom='on'
                            ) # labels along the bottom edge are off
            self.ax.axhline(y=20, linestyle='--', c='k', alpha=0.5, lw=2.0, label='twenty_percent')
            self.ax.axhline(y=-30, linestyle='--', c='k', alpha=0.5, lw=2.0, label='thirty_percent')
            self.ax.axhline(y=0, c='k', alpha=1, lw=2.0, label='zero_percent')
            self.ax.grid(color = 'k', axis = 'y', alpha=0.25)
            self.rects = self.ax.bar(self.rect_locations, self.waterfall_data['Best response percent change'], color=self.bar_colors)

        else:
            #settings were updated, we received them and stored in variable self.gen_settings
            self.ax.set_title(self.gen_settings[0])
            self.ax.set_xlabel(self.gen_settings[1])
            self.ax.set_ylabel(self.gen_settings[2])
            if self.gen_settings[3][0]:
                self.ax.axhline(y=20, linestyle='--', c='k', alpha=0.5, lw=2.0, label='twenty_percent')
            if self.gen_settings[3][1]:
                self.ax.axhline(y=-30, linestyle='--', c='k', alpha=0.5, lw=2.0, label='thirty_percent')
            if self.gen_settings[3][2]:
                self.ax.axhline(y=0, c='k', alpha=1, lw=2.0, label='zero_percent')

            if self.gen_settings[4][0] and ~self.gen_settings[6]:
                #show responses as labels, default color bars
                #legend depends on user specified keys
                self.rects = self.ax.bar(self.rect_locations, self.waterfall_data['Best response percent change'])
                self.add_labels(self.ax, self.rects, self.waterfall_data, 1)
            elif self.gen_settings[4][1]:
                #color bars with response type
                self.rects = self.ax.bar(self.rect_locations, self.waterfall_data['Best response percent change'], color=self.bar_colors)
                self.patches = []
                for key in self.keys_and_colors.keys():
                    self.patches.append(mpatches.Patch(color = self.keys_and_colors[key],label=key))
                self.ax.legend(handles=self.patches)
            else:
                self.rects = self.ax.bar(self.rect_locations, self.waterfall_data['Best response percent change'])
            
            if self.gen_settings[5]:
                self.plot_table()
            
            if self.gen_settings[6] and ~self.gen_settings[4][0]:
                self.add_labels(self.ax, self.rects, self.waterfall_data, 0)
            
            if ~self.gen_settings[4][1]:
                #response not shown as color coding, custom color code the bars


        self.ax.grid(color = 'k', axis = 'y', alpha=0.25)
        self.canvas.draw()
        self.generated_rectangles_signal.emit([self.rects])
            
    def plot_table(self):
        rows = ['%s' % x for x in self.waterfall_data.keys()]
        rows = rows[4:] #skip first three, they are the 4 standard headers, rest are table rows
        columns = self.waterfall_data['Patient number'] #patient numbers
        cell_text = []
        for row in rows:
            cell_text_temp = []
            for col in range(len(columns)):
                cell_text_temp.append(self.waterfall_data[row][col])
            cell_text.append(cell_text_temp)
        the_table = self.ax.table(cellText=cell_text, rowLabels=rows, colLabels=columns, loc='bottom', cellLoc='center', colLoc='center')
        plt.subplots_adjust(bottom=0.15,left=0.5)
        self.ax.set_xlim(-0.5,len(columns)-0.5)
        self.ax.tick_params(
                        axis='x',          # changes apply to the x-axis
                        which='both',      # both major and minor ticks are affected
                        bottom='off',      # ticks along the bottom edge are off
                        top='off',         # ticks along the top edge are off
                        labelbottom='off'
                        ) # labels along the bottom edge are off
    
    def update_plot(self):
        '''
        TODO
        '''
        pass
                    
    def add_labels(self, ax, rects, waterfall_data, label_type):
        '''
        Add labels above/below bars. label_type == 1 --> display responses; == 0 --> display cancer type
        '''
        i = 0
        if label_type:
            for rect in rects:
                height = rect.get_height()
                if height >= 0:
                    valign = 'bottom'
                else:
                    valign = 'top'
                    
                ax.text(rect.get_x() + rect.get_width()/2., height,
                        '%s' % waterfall_data['Overall response'][i], ha='center', va=valign)
                i+=1
        else:
            for rect in rects:
                height = rect.get_height()
                if height >= 0:
                    valign = 'top'
                    hgt = -1
                else:
                    valign = 'bottom'
                    hgt = 1

                ax.text(rect.get_x() + rect.get_width()/2., hgt,
                        '%s' % waterfall_data['Cancer'][i], ha='center', va=valign, rotation='vertical')
                i+=1   
Esempio n. 26
0
class WidgetMP3Frames(QWidget):
    def __init__(self):
        super(WidgetMP3Frames, self).__init__()

        self.treeWidget = QTreeWidget()
        self.treeWidget.setHeaderLabel("Offset")

        self.gbox = QGroupBox("MP3 Frames")
        self.gbox.setMaximumWidth(100)
        self.gbox.setLayout(self.genlayout_gbox())

        self.chunks = []

        self.setLayout(self.genlayout())

    def genlayout(self):
        lay = QVBoxLayout()
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(self.gbox)
        return lay

    def genlayout_gbox(self):
        lay = QVBoxLayout()
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(self.treeWidget)
        return lay

    def setupWidget(self):

        self.treeWidget.clear()

        for offset in self.chunks:
            item = QTreeWidgetItem(self.treeWidget)
            item.setText(0, "%.X" % offset)
            self.treeWidget.addTopLevelItem(item)

    def scanChunks(self, fname: str) -> {}:
        self.chunks = []

        f = open(fname, "rb")
        fsize = os.fstat(f.fileno()).st_size
        offset = 0
        flagFirst = False

        # Scan all chunks
        while offset < fsize:

            bval = f.read(1)
            val = int(bval[0])

            if flagFirst:
                # if (val >> 5) == 7:
                if val == 0xFB:
                    self.chunks.append(offset - 1)
                flagFirst = False

            if not flagFirst:
                if val == 0xFF:
                    flagFirst = True

            offset += 1

        # Filter invalid
        for idx in range(len(self.chunks) - 1):
            offs1 = self.chunks[idx]
            offs2 = self.chunks[idx + 1]

            print("%X:%X  %i" % (offs1, offs2, offs2 - offs1))

        self.setupWidget()

        return {}
Esempio n. 27
0
class OTMainWindow(QWidget):
    def __init__(self, parent=None):
        super(OTMainWindow, self).__init__(parent, Qt.Window)
        self.setWindowTitle('OPC Python Tester')
        self.layout = QVBoxLayout()
        #
        self.tree = QTreeWidget(self)
        self.tree.setHeaderLabel('OPC server tree')
        self.tree_root = QTreeWidgetItem()
        self.tree_root.setText(0, 'not connected')
        self.tree.addTopLevelItem(self.tree_root)
        self.tree.itemDoubleClicked.connect(self.on_tree_item_double_clicked)
        #
        self.table = QTableWidget(self)
        self.table.setRowCount(0)
        self.table_column_labels = [
            'item_id', 'value', 'type', 'access', 'quality', 'timestamp']
        self.table.setColumnCount(len(self.table_column_labels))
        self.table.setHorizontalHeaderLabels(self.table_column_labels)
        self.table.horizontalHeader().setStretchLastSection(True)
        #
        self.splitter = QSplitter(Qt.Horizontal, self)
        self.splitter.setChildrenCollapsible(False)
        self.splitter.setHandleWidth(10)
        self.layout.addWidget(self.splitter)
        # final
        self.splitter.addWidget(self.tree)
        self.splitter.addWidget(self.table)
        self.splitter.setSizes([150, 300])
        self.setLayout(self.layout)

        # self.opcsrv = None
        self.cur_server_info = {}
        self.cur_comp_name = ''
        self.watched_itemids = []

        self.ssdialog = ServerSelectDialog(self)
        ssel_ret = self.ssdialog.exec_()
        if ssel_ret == QDialog.Accepted:
            self.do_connect(self.ssdialog.selected_server, self.ssdialog.selected_comp_name)
        else:
            print('Connection cancelled')

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.on_timer_timeout)
        self.timer.start(1000)  # every 1 second

    def do_connect(self, srv_info: dict, comp_name: str):
        print('Connecting to "{0}" ({1}) on comp: {2}...'.format(
            srv_info['desc'], srv_info['guid'], comp_name))
        self.opcsrv = opc_helper.opc_connect(srv_info['guid'], comp_name)
        if self.opcsrv is None:
            return
        self.cur_comp_name = comp_name
        self.cur_server_info = srv_info
        print(self.opcsrv.get_status())
        self.fill_tree()

    def fill_tree(self):
        self.tree.clear()
        if self.opcsrv is None:
            return
        self.tree_root = QTreeWidgetItem(self.tree)
        self.tree_root.setChildIndicatorPolicy(QTreeWidgetItem.DontShowIndicatorWhenChildless)
        root_title = '{0}'.format(self.cur_server_info['desc'])
        if self.cur_comp_name != '':
            root_title = '{0} ({1})'.format(self.cur_server_info['desc'], self.cur_comp_name)
        self.tree_root.setText(0, root_title)
        self.tree.addTopLevelItem(self.tree_root)
        server_tree = self.opcsrv.browse(flat=False)
        #
        for oitem in server_tree:
            self.fill_item(oitem, self.tree_root)

    def fill_item(self, item: dict, parent: QTreeWidgetItem):
        tree_item = QTreeWidgetItem()
        tree_item.setChildIndicatorPolicy(QTreeWidgetItem.DontShowIndicatorWhenChildless)
        tree_item.setText(0, item['name'])
        if item['children'] is None:
            # set userdata = item_id only if this IS a LEAF node
            tree_item.setData(0, Qt.UserRole, item['item_id'])  # column, role, data
        parent.addChild(tree_item)
        # recurse into children
        if item['children'] is not None:
            for oitem in item['children']:
                self.fill_item(oitem, tree_item)

    @pyqtSlot(QTreeWidgetItem, int)
    def on_tree_item_double_clicked(self, item: QTreeWidgetItem, column: int):
        # void	itemDoubleClicked(QTreeWidgetItem * item, int column)
        # virtual QVariant	data(int column, int role) const
        item_data = item.data(0, Qt.UserRole)
        if item_data is None:
            return
        item_id = str(item_data)
        print('Double click on [{0}]'.format(item_id))
        self.opcsrv.get_item(item_id)
        if item_id not in self.watched_itemids:
            self.watched_itemids.append(item_id)

    @pyqtSlot()
    def on_timer_timeout(self):
        num_items = len(self.watched_itemids)
        self.table.setRowCount(num_items)
        i = 0
        while i < num_items:
            item_id = self.watched_itemids[i]
            item_value = self.opcsrv.get_item(item_id)
            item_info = self.opcsrv.get_item_info(item_id)
            #
            twi = QTableWidgetItem(str(item_id))
            self.table.setItem(i, 0, twi)
            #
            twi = QTableWidgetItem(str(item_value))
            self.table.setItem(i, 1, twi)
            #
            twi = QTableWidgetItem(str(item_info['type']))
            self.table.setItem(i, 2, twi)
            #
            twi = QTableWidgetItem(str(item_info['access_rights']))
            self.table.setItem(i, 3, twi)
            #
            twi = QTableWidgetItem(str(item_info['quality']))
            self.table.setItem(i, 4, twi)
            #
            ts_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(item_info['timestamp']))
            twi = QTableWidgetItem(str(ts_str))
            self.table.setItem(i, 5, twi)
            #
            i += 1
Esempio n. 28
0
class DBogUI(object):
    def __init__(self):
        self.db_handler = MyDBHandler()
        self.sqlTranslator = SqlTranslator()
        self.page_size = 10
        self.former_table_data = []
        self.current_table_data = []
        self.changedTableData = []
        self.cell_editable_flg = 0
        self.mainWidget = None
        self.centerLayout = None
        self.stackedWidget = None
        self.statusbar = None
        self.data_page = None
        self.data_page_layout = None
        self.menubar = None
        self.dataPanel = None
        self.data_panel_layout = None
        self.sqlTextEdit = None
        self.table_widget = None
        self.btn_panel = None
        self.btn_panel_layout = None
        self.addLnBtn = None
        self.rmvLnBtn = None
        self.queryBtn = None
        self.editBtn = None
        self.saveBtn = None
        self.treePanel = None
        self.treePanelLayout = None
        self.tableTree = None
        self.translate_page = None
        self.translate_page_layout = None
        self.translate_up_panel = None
        self.translate_up_panel_layout = None
        self.src_sql_text_edit = None
        self.dest_sql_text_edit = None
        self.translate_down_panel = None
        self.translate_down_panel_layout = None
        self.from_sql_type_combobox = None
        self.to_sql_type_combobox = None
        self.translate_btn = None
        self.swap_btn = None
        self.clear_btn = None
        self.login_page = None
        self.usmLabel = None
        self.unmTextEdit = None
        self.pwdLabel = None
        self.pwdTextEdit = None
        self.hostLabel = None
        self.hostTextEdit = None
        self.dbNameLabel = None
        self.dbNameTextEdit = None
        self.cnntDbBtn = None
        self.load_cfg_btn = None
        self.error_widgets = []
        self.plain_color = None

    # setup UI
    def setup_ui(self, MainWindow):
        # UI MainWindow
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(900, 600)
        # center widgets
        self.mainWidget = QWidget(MainWindow)
        # 设置字体
        self.mainWidget.setFont(QFont("Roman times", 11))
        self.mainWidget.setObjectName("mainWidget")
        MainWindow.setCentralWidget(self.mainWidget)
        self.centerLayout = QVBoxLayout(self.mainWidget)
        # 设置stacked widget
        self.stackedWidget = QStackedWidget(self.mainWidget)
        self.centerLayout.addWidget(self.stackedWidget)
        # 设置data page
        self.build_data_page()
        # layout不同的比例区分大小
        self.data_page_layout.setStretchFactor(self.dataPanel, 4)
        self.data_page_layout.setStretchFactor(self.treePanel, 1)
        # 设置login面板
        self.build_login_page()
        # 设置translate面板
        self.build_translate_page()
        # 将三个面板,加入stackedWidget
        self.stackedWidget.addWidget(self.data_page)
        self.stackedWidget.addWidget(self.login_page)
        self.stackedWidget.addWidget(self.translate_page)
        # menu
        self.build_menu_bar(MainWindow)
        # status bar
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        # 刷新
        self.re_translate_ui(MainWindow)
        QMetaObject.connectSlotsByName(MainWindow)
        # refresh tree list
        self.db_handler.config(None, None, None, None)
        self.paint_tree()

    # data page
    def build_data_page(self):
        self.data_page = QWidget(self.mainWidget)
        self.centerLayout.addWidget(self.data_page)
        self.data_page_layout = QHBoxLayout(self.data_page)
        # 设置data面板
        self.build_tree_panel()
        # 设置data面板
        self.build_data_panel()
        # set event
        self.set_data_page_event()

    # menu
    def build_menu_bar(self, MainWindow):
        self.menubar = QMenuBar(MainWindow)
        self.menubar.setGeometry(QRect(0, 0, 400, 50))
        self.menubar.move(100, 100)
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        # 创建一个事件和一个特定图标
        show_data_action = QAction(QIcon('../resources/icon/database.png'), 'Operate', self)
        # show_data_action.setShortcut('Ctrl+Q')  # 设置事件的快捷方式
        show_data_action.setStatusTip('show data panel')  # 设置事件的状态提示
        show_data_action.triggered.connect(lambda: self.switch_page(0))  # 事件的触发
        login_menu = self.menubar.addMenu('&Data')  # 添加菜单file
        login_menu.addAction(show_data_action)  # 菜单添加事件
        # 创建一个事件和一个特定图标
        show_login_action = QAction(QIcon('../resources/icon/configure.png'), 'Config', self)
        # show_login_action.setShortcut('Ctrl+P')  # 设置事件的快捷方式
        show_login_action.setStatusTip('show login panel')  # 设置事件的状态提示
        show_login_action.triggered.connect(lambda: self.switch_page(1))  # 事件的触发
        login_menu = self.menubar.addMenu('&Login')  # 添加菜单file
        login_menu.addAction(show_login_action)  # 菜单添加事件
        # 创建一个事件和一个特定图标
        show_translate_action = QAction(QIcon('../resources/icon/translate.png'), 'Translate', self)
        # show_login_action.setShortcut('Ctrl+P')  # 设置事件的快捷方式
        show_translate_action.setStatusTip('show translate page')  # 设置事件的状态提示
        show_translate_action.triggered.connect(lambda: self.switch_page(2))  # 事件的触发
        translate_menu = self.menubar.addMenu('&Translate')  # 添加菜单file
        translate_menu.addAction(show_translate_action)  # 菜单添加事件

    # 设置data面板
    def build_data_panel(self):
        # data panel
        self.dataPanel = QWidget(self.data_page)
        self.data_page_layout.addWidget(self.dataPanel)
        self.data_panel_layout = QVBoxLayout(self.dataPanel)
        # SQL输入框
        self.sqlTextEdit = QTextEdit(self.dataPanel)
        self.sqlTextEdit.setObjectName("textEdit")
        # self.sqlTextEdit.setText("Enter your SQL here...")
        self.data_panel_layout.addWidget(self.sqlTextEdit)
        # 数据表格
        self.table_widget = TableWidget(self.dataPanel)
        self.table_widget.init_ui(0, 0, self.dataPanel)
        self.table_widget.set_page_controller(0)  # 表格设置页码控制
        self.table_widget.control_signal.connect(self.control_page)
        self.table_widget.setFixedSize(700, 400)
        self.data_panel_layout.addWidget(self.table_widget)
        # build inside panel
        self.build_btn_panel()
        self.data_panel_layout.setStretchFactor(self.sqlTextEdit, 2)
        self.data_panel_layout.setStretchFactor(self.table_widget, 6)
        self.data_panel_layout.setStretchFactor(self.btn_panel, 2)

    # button panel
    def build_btn_panel(self):
        # button panel
        self.btn_panel = QWidget(self.data_page)
        self.btn_panel.setFont(QFont("YaHei", 10))
        self.data_panel_layout.addWidget(self.btn_panel)
        self.btn_panel_layout = QHBoxLayout(self.btn_panel)
        # 表格操作:增加数据行按钮
        self.addLnBtn = QPushButton(self.btn_panel)
        self.addLnBtn.setMaximumSize(100, 80)
        self.addLnBtn.setObjectName("addLineButton")
        self.btn_panel_layout.addWidget(self.addLnBtn)
        # 表格操作:增加数据行按钮
        self.rmvLnBtn = QPushButton(self.btn_panel)
        self.rmvLnBtn.setObjectName("rmvLineButton")
        self.btn_panel_layout.addWidget(self.rmvLnBtn)
        # 查询按钮
        self.queryBtn = QPushButton(self.btn_panel)
        # self.queryBtn.setGeometry(QRect(150, 500, 100, 50))
        self.queryBtn.setObjectName("QueryButton")
        self.btn_panel_layout.addWidget(self.queryBtn)
        # 更新按钮
        self.editBtn = QPushButton(self.btn_panel)
        # self.editBtn.setGeometry(QRect(150, 500, 100, 50))
        self.editBtn.setObjectName("EditButton")
        self.btn_panel_layout.addWidget(self.editBtn)
        # 保存按钮
        self.saveBtn = QPushButton(self.btn_panel)
        # self.saveBtn.setGeometry(QRect(225, 500, 100, 50))
        self.saveBtn.setObjectName("SaveButton")
        self.btn_panel_layout.addWidget(self.saveBtn)
        # set partition / relative size
        self.btn_panel_layout.setStretchFactor(self.addLnBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.rmvLnBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.queryBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.editBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.saveBtn, 1)

    # left catalog tree panel
    def build_tree_panel(self):
        self.treePanel = QWidget(self.data_page)
        self.treePanel.setFont(QFont("SimHei", 11))
        self.treePanelLayout = QVBoxLayout(self.treePanel)
        # self.treePanel.setGeometry(QRect(0, 0, 100, 600))
        self.data_page_layout.addWidget(self.treePanel)
        # tableTree list
        self.tableTree = QTreeWidget(self.treePanel)
        self.treePanelLayout.addWidget(self.tableTree)
        # 设置列数
        self.tableTree.setColumnCount(1)
        self.tableTree.setColumnWidth(0, 100)
        # 设置头的标题
        self.tableTree.setHeaderLabel('Table List')
        # 绑定点击事件
        self.tableTree.clicked.connect(self.on_tree_clicked)

    # 加载树形菜单
    def paint_tree(self):
        if not CTools.isEmpty(self.db_handler.db_name):
            self.tableTree.clear()
            self.sqlTextEdit.setText("use %s;show tables;" % self.db_handler.db_name)
            table_lst = self.show_tables()
            # repaint tree nodes
            root = QTreeWidgetItem(self.tableTree)
            root.setText(0, self.db_handler.db_name)
            for i in range(len(table_lst)):
                table_name = table_lst[i]
                table_node = QTreeWidgetItem(root)
                table_node.setText(0, table_name)
            self.tableTree.addTopLevelItem(root)

    # data page
    def build_translate_page(self):
        self.translate_page = QWidget(self.mainWidget)
        self.centerLayout.addWidget(self.translate_page)
        self.translate_page_layout = QVBoxLayout(self.translate_page)
        self.build_translate_panel()

    # 设置data面板
    def build_translate_panel(self):
        # translate panel
        self.translate_up_panel = QWidget(self.translate_page)
        self.translate_page_layout.addWidget(self.translate_up_panel)
        # append widget
        self.translate_up_panel_layout = QHBoxLayout(self.translate_up_panel)
        # SQL输入框
        self.src_sql_text_edit = QTextEdit(self.translate_up_panel)
        self.src_sql_text_edit.setObjectName("src_sql_text_edit")
        self.src_sql_text_edit.setText("Enter source SQL to translate here...")
        self.translate_up_panel_layout.addWidget(self.src_sql_text_edit)
        # SQL输入框
        self.dest_sql_text_edit = QTextEdit(self.translate_up_panel)
        self.dest_sql_text_edit.setObjectName("dest_sql_text_edit")
        self.dest_sql_text_edit.setText("SQL translate result will be placed here...")
        self.dest_sql_text_edit.setReadOnly(True)
        self.translate_up_panel_layout.addWidget(self.dest_sql_text_edit)
        # translate panel
        self.translate_down_panel = QWidget(self.translate_page)
        self.translate_page_layout.addWidget(self.translate_down_panel)
        # append widget
        self.translate_down_panel_layout = QHBoxLayout(self.translate_down_panel)
        # select from sql type
        self.from_sql_type_combobox = QComboBox(self.translate_down_panel)
        self.from_sql_type_combobox.setMaximumSize(150, 120)
        self.from_sql_type_combobox.setObjectName("from_sql_type_combobox")
        self.from_sql_type_combobox.addItems(self.sqlTranslator.valid_from_dialect)
        self.translate_down_panel_layout.addWidget(self.from_sql_type_combobox)
        self.plain_color = self.from_sql_type_combobox.palette().color(QPalette.Background).toRgb()
        # select to sql type
        self.to_sql_type_combobox = QComboBox(self.translate_down_panel)
        self.to_sql_type_combobox.setMaximumSize(150, 120)
        self.to_sql_type_combobox.setObjectName("to_sql_type_combobox")
        # self.to_sql_type_combobox.addItems(['', 'mssql', 'sqlserver', 'mssqlserver', 'mysql', 'oracle', 'db2', 'db2udb'])
        self.to_sql_type_combobox.addItems(self.sqlTranslator.valid_to_dialect)
        self.translate_down_panel_layout.addWidget(self.to_sql_type_combobox)
        # translate按钮
        self.translate_btn = QPushButton(self.translate_down_panel)
        self.translate_btn.setMaximumSize(150, 120)
        self.translate_btn.setObjectName("Translate")
        self.translate_btn.setText("Translate")
        self.translate_down_panel_layout.addWidget(self.translate_btn)
        self.translate_btn.clicked.connect(self.on_translate)
        # 交换按钮
        self.swap_btn = QPushButton(self.translate_down_panel)
        self.swap_btn.setMaximumSize(150, 120)
        self.swap_btn.setObjectName("Swap")
        self.swap_btn.setText("Swap")
        self.translate_down_panel_layout.addWidget(self.swap_btn)
        self.swap_btn.clicked.connect(self.on_swap)
        # 清空按钮
        self.clear_btn = QPushButton(self.translate_down_panel)
        self.clear_btn.setMaximumSize(150, 120)
        self.clear_btn.setObjectName("Clear")
        self.clear_btn.setText("Clear")
        self.translate_down_panel_layout.addWidget(self.clear_btn)
        self.clear_btn.clicked.connect(self.on_clear)

    def on_translate(self):
        src_sql_str = self.src_sql_text_edit.toPlainText()
        from_type = self.from_sql_type_combobox.currentText()
        to_type = self.to_sql_type_combobox.currentText()
        error_msg = ""
        if not src_sql_str or src_sql_str == "Enter source SQL to translate here...":
            error_msg += "source SQL should not be empty\n"
            self.src_sql_text_edit.setTextBackgroundColor(QColor(255, 0, 0, 255))
            self.error_widgets.append(self.src_sql_text_edit)
        else:
            if self.src_sql_text_edit in self.error_widgets:
                self.error_widgets.remove(self.src_sql_text_edit)
        if not from_type:
            error_msg += "from SQL Type should not be empty\n"
            self.from_sql_type_combobox.setStyleSheet("QComboBox{color: rgb(255,0,0,255)}")
            self.error_widgets.append(self.from_sql_type_combobox)
        else:
            if self.from_sql_type_combobox in self.error_widgets:
                self.error_widgets.remove(self.from_sql_type_combobox)
        if not to_type:
            error_msg += "to SQL Type should not be empty\n"
            self.to_sql_type_combobox.setStyleSheet("QComboBox{color: rgb(255,0,0,255)}")
            self.error_widgets.append(self.to_sql_type_combobox)
        else:
            if self.to_sql_type_combobox in self.error_widgets:
                self.error_widgets.remove(self.to_sql_type_combobox)
        if self.error_widgets:
            QMessageBox.warning(None, "Warning", error_msg)
            return
        if self.src_sql_text_edit in self.error_widgets:
            self.src_sql_text_edit.setTextBackgroundColor(QColor(255, 255, 255, 255))
        if self.from_sql_type_combobox in self.error_widgets:
            self.from_sql_type_combobox.setStyleSheet("QComboBox{color: rgb%s}" % str(self.plain_color.getRgb()))
        if self.to_sql_type_combobox in self.error_widgets:
            self.to_sql_type_combobox.setStyleSheet("QComboBox{color: rgb%s}" % str(self.plain_color.getRgb()))
        dest_sql_str = self.sqlTranslator.web_translate(src_sql_str, from_type, to_type)
        self.dest_sql_text_edit.setText(dest_sql_str)

    def on_swap(self):
        # origin
        # from_sql_type_index = self.from_sql_type_combobox.currentIndex()
        # to_sql_type_index = self.to_sql_type_combobox.currentIndex()
        from_sql_type_text = self.from_sql_type_combobox.currentText()
        to_sql_type_text = self.to_sql_type_combobox.currentText()
        src_sql_str = self.src_sql_text_edit.toPlainText()
        dest_sql_str = self.dest_sql_text_edit.toPlainText()
        # change content
        self.from_sql_type_combobox.setCurrentIndex(
            self.sqlTranslator.get_opposite_sql_type(from_sql_type_text, 'from'))
        self.to_sql_type_combobox.setCurrentIndex(self.sqlTranslator.get_opposite_sql_type(to_sql_type_text, 'to'))
        self.dest_sql_text_edit.setText(src_sql_str)
        self.src_sql_text_edit.setText(dest_sql_str)

    def on_clear(self):
        self.from_sql_type_combobox.setCurrentIndex(0)
        self.to_sql_type_combobox.setCurrentIndex(0)
        self.dest_sql_text_edit.setText("")
        self.src_sql_text_edit.setText("")

    # show data after clicking the menu item in tree
    def on_tree_clicked(self):
        item = self.tableTree.currentItem()
        table_name = item.text(0)
        # no response when click database label
        if table_name != self.db_handler.db_name:
            cTool.logger.info("switch to data of table, name: %s" % table_name)
            self.sqlTextEdit.setText("select * from %s limit 0, %d;" % (table_name, self.page_size))
            self.do_query()

    # when press append record button
    def on_append_record(self, table_name, page_size):
        if self.is_cell_editable():
            self.sqlTextEdit.setText("select * from %s limit 0, %d;" % (table_name, page_size))
            self.do_query()
        else:
            self.warn_action("Table is not allowed to append record now")

    # when action is fobidden
    def warn_action(self, err_msg="illegal action"):
        cTool.logger.info("%s" % err_msg)
        QMessageBox.warning(None, "Warning", err_msg)

    # when press remove record button
    def on_remove_record(self):
        if self.is_cell_editable():
            cell_item_list = self.table_widget.table.selectedItems()
            count = len(cell_item_list)
            row_num_set = set()
            for x in range(0, count):
                table_item = cell_item_list[x]
                row_num_set.add(self.table_widget.table.row(table_item))
            selected_pk_set = set(
                self.former_table_data[row_num][self.db_handler.def_pk_index] for row_num in row_num_set)
            self.db_handler.delete_sql = self.db_handler.buildDeleteSQL(self.db_handler.def_table_name,
                                                                        self.db_handler.def_pk_name,
                                                                        selected_pk_set)
            # rmv_cnt = self.db_handler.modifyRecords(delete_sql)
            # print("updated count is %d for SQL %s" % (rmv_cnt, delete_sql))
            # self.do_query()
            self.former_table_data = [former_row_data for former_row_data in
                                      self.former_table_data if
                                      former_row_data[self.db_handler.def_pk_index] not in selected_pk_set]
            self.update_table_data(self.former_table_data, self.db_handler.def_field_name_lst, False)
        else:
            self.warn_action("Table is not allowed to delete record now")

    # 设置login面板
    def build_login_page(self):
        # login panel
        self.login_page = QWidget(self.stackedWidget)
        self.login_page.setGeometry(QRect(100, 100, 600, 600))
        # self.login_page_layout = QVBoxLayout(self.login_page)
        # username label
        self.usmLabel = QLabel(self.login_page)
        self.usmLabel.setGeometry(QRect(100, 100, 150, 50))
        self.usmLabel.setText("username ")
        # self.login_page_layout.addWidget(self.usmLabel)
        # username input
        self.unmTextEdit = QLineEdit(self.login_page)
        self.unmTextEdit.setText("")
        self.unmTextEdit.setGeometry(QRect(300, 100, 450, 50))
        # self.login_page_layout.addWidget(self.unmTextEdit)
        # password label
        self.pwdLabel = QLabel(self.login_page)
        self.pwdLabel.setGeometry(QRect(100, 150, 150, 50))
        self.pwdLabel.setText("password ")
        # self.login_page_layout.addWidget(self.pwdLabel)
        # password input
        self.pwdTextEdit = QLineEdit(self.login_page)
        self.pwdTextEdit.setText("")
        self.pwdTextEdit.setGeometry(QRect(300, 150, 450, 50))
        # self.login_page_layout.addWidget(self.pwdTextEdit)
        # host label
        self.hostLabel = QLabel(self.login_page)
        self.hostLabel.setGeometry(QRect(100, 200, 120, 50))
        self.hostLabel.setText("host address ")
        # self.login_page_layout.addWidget(self.hostLabel)
        # host input
        self.hostTextEdit = QLineEdit(self.login_page)
        self.hostTextEdit.setText("")
        self.hostTextEdit.setGeometry(QRect(300, 200, 450, 50))
        # self.login_page_layout.addWidget(self.hostTextEdit)
        # database name label
        self.dbNameLabel = QLabel(self.login_page)
        self.dbNameLabel.setGeometry(QRect(100, 250, 150, 50))
        self.dbNameLabel.setText("database name ")
        # self.login_page_layout.addWidget(self.dbNameLabel)
        # database name input
        self.dbNameTextEdit = QLineEdit(self.login_page)
        self.dbNameTextEdit.setText("")
        self.dbNameTextEdit.setGeometry(QRect(300, 250, 450, 50))
        # self.login_page_layout.addWidget(self.dbNameTextEdit)
        # confirm to connect database
        self.cnntDbBtn = QPushButton(self.login_page)
        self.cnntDbBtn.setObjectName("DbButton")
        self.cnntDbBtn.setGeometry(QRect(100, 350, 300, 50))
        # self.login_page_layout.addWidget(self.cnntDbBtn)
        # connect database with config file
        self.load_cfg_btn = QPushButton(self.login_page)
        self.load_cfg_btn.setObjectName("LoadConfigButton")
        self.load_cfg_btn.setGeometry(QRect(450, 350, 300, 50))
        # self.login_page_layout.addWidget(self.load_cfg_btn)
        # set event
        self.set_login_panel()

    # build login panel
    def set_login_panel(self):
        self.cnntDbBtn.clicked.connect(self.cnnt_db)
        self.load_cfg_btn.clicked.connect(self.re_config_db)

    # connect to database
    def cnnt_db(self):
        unmStr = self.unmTextEdit.text()
        pwdStr = self.pwdTextEdit.text()
        hostStr = self.hostTextEdit.text()
        dbNameStr = self.dbNameTextEdit.text()
        self.db_handler.config(hostStr, unmStr, pwdStr, dbNameStr)
        self.paint_tree()
        self.switch_page(0)

    # connect to database with configuration file
    def re_config_db(self):
        self.db_handler.config(mVars.host, mVars.username, mVars.password, mVars.database_name)

    # set label text of widget
    def re_translate_ui(self, MainWindow):
        _translate = QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "DBog"))
        self.setWindowIcon(QIcon("../resources/icon/ball.ico"))
        self.queryBtn.setText(_translate("MainWindow", "Query"))
        self.editBtn.setText(_translate("MainWindow", "Edit"))
        self.saveBtn.setText(_translate("MainWindow", "Save"))
        self.cnntDbBtn.setText(_translate("MainWindow", "ConnectByInputConfiguration"))
        self.load_cfg_btn.setText(_translate("MainWindow", "LoadConfigurationFile"))
        self.addLnBtn.setText(_translate("MainWindow", "AppendRecord"))
        self.rmvLnBtn.setText(_translate("MainWindow", "RemoveRecord"))

    # set event of data page
    def set_data_page_event(self):
        self.queryBtn.clicked.connect(self.do_query)
        self.editBtn.clicked.connect(self.do_edit)
        self.saveBtn.clicked.connect(self.do_save)
        self.addLnBtn.clicked.connect(lambda: self.on_append_record(self.db_handler.def_table_name, self.page_size - 1))
        self.rmvLnBtn.clicked.connect(self.on_remove_record)

    # switch to another page
    def switch_page(self, pnIndex):
        self.stackedWidget.setCurrentIndex(int(pnIndex))

    # execute query action
    def do_query(self):
        cmd_str = self.sqlTextEdit.toPlainText().replace(";", "")
        # 获取数据
        result_set = self.db_handler.get_all_result(cmd_str)
        # 获取表结构定义
        # db_filed_lst = self.db_handler.gen_alter(self.db_handler.def_table_name)
        # self.db_handler.field_name_lst = [db_filed["Field"] for db_filed in db_filed_lst]
        self.db_handler.gen_filed_name_list()
        # withdraw former table data / cast value None to empty string
        self.former_table_data = [[str(item) if item else "" for item in row] for row in result_set]
        # change button status to enable or not
        self.change_btn_status()
        # fill in table with empty line
        self.refresh_table(cmd_str)
        cTool.logger.info("result set for %s is %s" % (
            cmd_str, ','.join([str(row_data) for row_data in result_set])))

    # change button status to enable or not
    def change_btn_status(self):
        if self.is_cell_editable():
            self.addLnBtn.setEnabled(True)
            self.rmvLnBtn.setEnabled(True)
            self.saveBtn.setEnabled(True)
        else:
            self.addLnBtn.setEnabled(False)
            self.rmvLnBtn.setEnabled(False)
            self.saveBtn.setEnabled(False)

    # refresh table widget
    def refresh_table(self, cmd_str):
        row_cnt = len(self.former_table_data)
        if row_cnt > 0:
            col_cnt = len(self.former_table_data[0])
            while row_cnt < self.page_size:
                self.former_table_data.append(["" for i in range(col_cnt)])
                row_cnt = row_cnt + 1
        # count record
        page_cnt = self.count_record(cmd_str)
        # refresh table
        self.update_table_data(self.former_table_data, self.db_handler.def_field_name_lst, True)
        # refresh table page
        self.refresh_table_pager(page_cnt)

    # execute count of record
    def count_record(self, cmd_str):
        if "limit" in cmd_str:
            cmd_str = cmd_str.split("limit")[0]
        cnt_sql = "select count(1) from (%s) sub" % cmd_str
        ret_cnt = self.db_handler.get_single_result(cnt_sql)
        page_cnt = 0
        if ret_cnt and len(ret_cnt) > 0:
            ret_cnt = int(ret_cnt[0])
            page_cnt = int(ret_cnt / 10)
            if page_cnt % 10 != 0:
                page_cnt = page_cnt + 1
        return page_cnt

    # make new name of each field
    def rename_fields(self):
        ret_dict = self.count_list(self.db_handler.def_field_name_lst)
        for field_name in self.db_handler.def_field_name_lst:
            if ret_dict[field_name] > 1:
                field_name = "%s_%d" % (field_name, ret_dict[field_name])
                ret_dict[field_name] = ret_dict[field_name] - 1

    # statistic item count in a list
    def count_list(self, target_lst):
        ret_dict = {}
        for i in set(target_lst):
            ret_dict[i] = target_lst.count(i)
        return ret_dict

    # show table of current database
    def show_tables(self):
        tables_sql = "show tables;"
        self.sqlTextEdit.setText(tables_sql)
        return [tbl_name_tp[0] for tbl_name_tp in self.db_handler.get_all_result(tables_sql)]

    # execute update
    def do_edit(self):
        cmd_str = self.sqlTextEdit.toPlainText()
        row_count = self.db_handler.modifyRecords(cmd_str)
        info_msg = "Edit finish, effected row count: %d" % row_count
        cTool.logger.info(info_msg)
        QMessageBox.information(self, "Info", info_msg)

    # @decorator()
    # execute save action
    def do_save(self):
        # edit result set of multi tables is forbidden
        if self.is_cell_editable():
            # clear first
            self.current_table_data.clear()
            row_count = self.table_widget.table.rowCount()
            column_count = self.table_widget.table.columnCount()
            for i in range(0, row_count):
                curRowData = []
                for j in range(0, column_count):
                    cellValStr = self.table_widget.table.item(i, j).text()
                    curRowData.append(cellValStr)
                self.current_table_data.append(curRowData)
            # assemble SQL from data
            deleteSQL = self.db_handler.delete_sql
            if deleteSQL:
                # reset to empty string
                self.db_handler.delete_sql = ""
            else:
                dataLst = self.get_delete_data()
                deleteSQL = self.db_handler.buildDeleteSQL(self.db_handler.def_table_name, self.db_handler.def_pk_name,
                                                           dataLst) if dataLst else ""
            cTool.logger.info(deleteSQL)
            dataLst = self.get_insert_data()
            insertSQL = self.db_handler.buildInsertSQL(self.db_handler.def_table_name, dataLst) if dataLst else ""
            cTool.logger.info(insertSQL)
            rmCnt = self.db_handler.modifyRecords(deleteSQL) if deleteSQL else 0
            inCnt = self.db_handler.modifyRecords(insertSQL) if insertSQL else 0
            info_msg = "Save finish, effect record count removed: %d,inserted: %d" % (rmCnt, inCnt)
            cTool.logger.info(info_msg)
            QMessageBox.information(self, "Info", info_msg)
        else:
            self.warn_action("Table is not allowed to update now")

    # is table cell item editable
    def is_cell_editable(self):
        self.cell_editable_flg = QtCore.Qt.ItemIsEnabled if self.db_handler.is_combine_sql else QtCore.Qt.ItemIsEditable
        return self.cell_editable_flg == 2

    # withdraw delete data
    def get_delete_data(self):
        fmrSet = set(map(lambda x: ",".join(x), self.former_table_data))
        curSet = set(map(lambda x: ",".join(x), self.current_table_data))
        diffSet = fmrSet.difference(curSet)
        return list(map(lambda diffStr: diffStr.split(",")[self.db_handler.def_pk_index], diffSet))

    # withdraw insert data
    def get_insert_data(self):
        curSet = set(map(lambda x: ",".join(x), self.current_table_data))
        fmrSet = set(map(lambda x: ",".join(x), self.former_table_data))
        diffSet = curSet.difference(fmrSet)
        return list(map(lambda diffStr: diffStr.split(","), diffSet))

    # update/refresh table data
    def update_table_data(self, matrix_data, fd_name_list, update_header_flg):
        row_cnt = len(matrix_data)
        col_cnt = len(matrix_data[0]) if len(matrix_data) > 0 and len(matrix_data[0]) > 0 else 0
        # refresh table model struct
        self.clear_table_data()
        # if update_header_flg:
        self.refresh_table_header(fd_name_list, row_cnt, col_cnt)
        # refresh table data
        for row_num in range(0, row_cnt):
            for col_num in range(0, col_cnt):
                cell_data = str(matrix_data[row_num][col_num]) if matrix_data[row_num][col_num] else ""
                # 设置表格内容(行, 列) 文字
                self.table_widget.table.setItem(row_num, col_num, QTableWidgetItem(cell_data))

    # update/refresh table header
    def refresh_table_header(self, fd_name_list, row_cnt, col_cnt):
        self.table_widget.table.setRowCount(row_cnt)
        self.table_widget.table.setColumnCount(col_cnt)
        self.table_widget.table.setHorizontalHeaderLabels(fd_name_list)

    # update/refresh table pager
    def refresh_table_pager(self, page_count):
        self.table_widget.totalPageNum.setText(str(page_count))

    # clear table data
    def clear_table_data(self):
        self.table_widget.table.setRowCount(0)
        self.table_widget.table.setColumnCount(0)

    # config table item
    def touch_table_item(self):
        flag = QtCore.Qt.ItemIsEnabled if self.db_handler.is_combine_sql else QtCore.Qt.ItemIsEditable
        col_cnt = self.table_widget.table.colorCount()
        row_cnt = self.table_widget.table.rowCount()
        for col_num in range(0, col_cnt):
            for row_num in range(0, row_cnt):
                QTableWidgetItem(self.table_widget.table.item(row_cnt, col_cnt)).setFlags(flag)
                # 设置第二列不可编辑
                # self.table_widget.table.setItemDelegateForColumn(col_num, EmptyDelegate(self))
                # self.table_widget.table.setItemDelegateForColumn(col_num, QTableWidget.itemDelegate())

    # control page
    def control_page(self, signal):
        total_page = self.table_widget.show_total_page()
        if "home" == signal[0]:
            self.table_widget.curPage.setText("1")
        elif "pre" == signal[0]:
            if 1 == int(signal[1]):
                QMessageBox.information(self, "提示", "已经是第一页了", QMessageBox.Yes)
                return
            self.table_widget.curPage.setText(str(int(signal[1]) - 1))
        elif "next" == signal[0]:
            if total_page == int(signal[1]):
                QMessageBox.information(self, "提示", "已经是最后一页了", QMessageBox.Yes)
                return
            self.table_widget.curPage.setText(str(int(signal[1]) + 1))
        elif "final" == signal[0]:
            self.table_widget.curPage.setText(str(total_page))
        elif "confirm" == signal[0]:
            if total_page < int(signal[1]) or int(signal[1]) < 0:
                QMessageBox.information(self, "提示", "跳转页码超出范围", QMessageBox.Yes)
                return
            self.table_widget.curPage.setText(signal[1])
        self.change_table_content()  # 改变表格内容

    # change table pager and content
    def change_table_content(self):
        """根据当前页改变表格的内容"""
        cur_page = int(self.table_widget.curPage.text())
        # 每页默认十条数据
        cTool.logger.info("current page: %s" % cur_page)
        page_limit_sql = "limit %s, %s" % ((cur_page - 1) * 10, 10)
        query_sql = "select * from %s %s;" % (self.db_handler.def_table_name, page_limit_sql)
        self.sqlTextEdit.setText(query_sql)
        self.do_query()
Esempio n. 29
0
class select_param(QWidget):
	def init(self,treeview):
		self.dest_treeview=treeview
		
	def __init__(self):
		QWidget.__init__(self)
		self.win_list=windows()
		self.setFixedSize(400,700)
		self.main_vbox=QVBoxLayout()

		self.setWindowIcon(QIcon(os.path.join(get_image_file_path(),"scan.png")))

		self.setWindowTitle(_("Select simulation parameter (www.gpvdm.com)")) 


		self.tab = QTreeWidget()
		#self.tab.setHeaderItem("Scan items")


		self.main_vbox.addWidget(self.tab)

		self.hwidget=QWidget()

		okButton = QPushButton("OK")
		cancelButton = QPushButton("Cancel")

		hbox = QHBoxLayout()
		hbox.addStretch(1)
		hbox.addWidget(okButton)
		hbox.addWidget(cancelButton)

		self.hwidget.setLayout(hbox)

		self.main_vbox.addWidget(self.hwidget)

		self.setLayout(self.main_vbox)

		okButton.clicked.connect(self.tree_apply_click) 
		cancelButton.clicked.connect(self.close)

		#self.tab.itemSelectionChanged.connect(self.tree_apply_click)
		self.tab.header().close() 
		self.update()

		return

	def make_entry(self,root,text):
		depth=0
		pointer=root
		for depth in range(0,len(text)):
			found=False
			for i in range(0,pointer.childCount()):
				if pointer.child(i).text(0)==text[depth]:
					found=True
					pointer=pointer.child(i)
					break
			if found==False:
				pointer=QTreeWidgetItem(pointer, [text[depth]])
				


	def update(self):
		self.tab.clear()
		root = QTreeWidgetItem(self.tab, [_("Simulation parameters")])
		root.setExpanded(True)
		param_list=scan_items_get_list()
		i=0
		for item in range(0, len(param_list)):
			div_str=param_list[item].name.replace("\\", "/")
			div_str=div_str.split("/")
			piter=None
			self.make_entry(root,div_str)

	 

	def on_destroy(self):
		self.win_list.update(self,"scan_select")
		self.hide()
		return True

	def cal_path(self):
		getSelected = self.tab.selectedItems()
		if getSelected:
			item = getSelected[0]
#			getChildNode = baseNode.text(0)


			path = []
			while item is not None:
				path.append(str(item.text(0)))
				item = item.parent()

		ret="/".join(reversed(path))
		ret=ret.split('/', 1)[-1]
		ret=ret.replace("/", os.path.sep)
		return ret
	
	def tree_apply_click(self):
		index = self.dest_treeview.selectionModel().selectedRows()
		if len(index)>0:
			print("row=",index[0].row(),len(index))
			pos=index[0].row()
			#print(path,scan_items_get_file(path),scan_items_get_token(path))
			path=self.cal_path()
			file_name=scan_items_get_file(path)
			token=scan_items_get_token(path)
			print("adding",path,file_name,token)
			tab_set_value(self.dest_treeview,pos,0,file_name)
			tab_set_value(self.dest_treeview,pos,1,token)
			tab_set_value(self.dest_treeview,pos,2,path)

			self.close()
		else:
			error_dlg(self,"No row selected in the scan window, can't insert the selection")
Esempio n. 30
0
class materials_select(QWidget):
    def init(self, treeview):
        self.dest_treeview = treeview

    def set_save_function(self, save_function):
        self.save_function = save_function

    def __init__(self):
        QWidget.__init__(self)
        self.setFixedSize(500, 700)
        self.file_name_tab_pos = 2

        self.main_vbox = QVBoxLayout()
        self.save_function = None

        self.setWindowIcon(icon_get("scan"))

        self.setWindowTitle(_("Select material") + " (https://www.gpvdm.com)")

        self.tab = QTreeWidget()
        #self.tab.setHeaderItem("Scan items")

        self.font = QFont()
        #		self.font.setFamily('DejaVu Sans')
        #		self.font.setBold(True)
        #		self.font.setStyleHint(QFont.Monospace)
        #		self.font.setFixedPitch(True)
        self.font.setPointSize(int(20))

        self.tab.setFont(self.font)

        self.main_vbox.addWidget(self.tab)

        self.hwidget = QWidget()

        okButton = QPushButton(_("OK"))
        cancelButton = QPushButton(_("Cancel"))

        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(okButton)
        hbox.addWidget(cancelButton)

        self.hwidget.setLayout(hbox)

        self.main_vbox.addWidget(self.hwidget)

        self.setLayout(self.main_vbox)

        okButton.clicked.connect(self.tree_apply_click)
        cancelButton.clicked.connect(self.close)

        #self.tab.itemSelectionChanged.connect(self.tree_apply_click)
        self.tab.header().close()
        self.update()
        #return

    def make_entry(self, root, text):
        depth = 0
        pointer = root
        for depth in range(0, len(text)):
            found = False
            for i in range(0, pointer.childCount()):
                if pointer.child(i).text(0) == text[depth]:
                    found = True
                    pointer = pointer.child(i)
                    break
            if found == False:
                pointer = QTreeWidgetItem(pointer, [text[depth]])
                if depth == len(text) - 1:
                    pointer.setIcon(0, icon_get("organic_material"))
                else:
                    pointer.setIcon(0, icon_get("folder"))

    def update(self):
        self.tab.clear()
        root = QTreeWidgetItem(self.tab, [_("Materials")])
        root.setExpanded(True)
        param_list = find_materials()

        i = 0
        for item in range(0, len(param_list)):
            div_str = param_list[item].replace("\\", "/")
            div_str = div_str.split("/")
            piter = None
            self.make_entry(root, div_str)

    def on_destroy(self):
        self.hide()
        return True

    def cal_path(self):
        path = []
        getSelected = self.tab.selectedItems()
        if getSelected:
            item = getSelected[0]
            #			getChildNode = baseNode.text(0)

            while item is not None:
                path.append(str(item.text(0)))
                item = item.parent()

        ret = "/".join(reversed(path))
        ret = ret.split('/', 1)[-1]
        #ret=ret.replace("/", os.path.sep)
        return ret

    def tree_apply_click(self):
        getSelected = self.tab.selectedItems()
        if len(getSelected) == 0:
            error_dlg(self, _("You need to select a material"))
            return

        index = self.dest_treeview.selectionModel().selectedRows()
        if len(index) > 0:
            print("row=", index[0].row(), len(index))
            pos = index[0].row()

            path = self.cal_path()
            tab_set_value(self.dest_treeview, pos, self.file_name_tab_pos,
                          path)

            if self.save_function != None:
                self.save_function()

            self.close()
        else:
            error_dlg(
                self,
                _("No row selected in the layer editor, can't insert the selection"
                  ))
Esempio n. 31
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.dbm_obj = Dbm()
        self.curFile = ''
        self.textEdit = QTextEdit()
        self.sectionTreeWidget = QTreeWidget()

        self.notesListWidget = QListWidget()
        self.createHorizontalGroupBox()
        self.setCentralWidget(self.horizontalGroupBox)

        self.createActions()
        self.createMenus()
        # self.createToolBars()
        self.createStatusBar()
        self.readSettings()

        [self.hierarchy_dict, self.notebook_dict, self.section_dict, self.page_dict] = [{}, {}, {}, {}]

        self.setCurrentFile('')

        # For binding slots and signals
        self.fetchPageThread = FetchPage()
        self.fetchPageThread.setObjectName('fetchPageThread')
        self.syncAllThread = SyncAllThread()
        self.syncAllThread.setObjectName('syncAllThread')
        self.textEdit.document().contentsChanged.connect(self.documentWasModified)
        self.sectionTreeWidget.setObjectName("sectionTreeWidget")
        self.notesListWidget.setObjectName("notesListWidget")
        QMetaObject.connectSlotsByName(self)
        self.readDB()

    @pyqtSlot()
    def on_sectionTreeWidget_itemSelectionChanged(self):
        for x in self.sectionTreeWidget.selectedItems():
            if x.text(1) in self.section_dict.keys():
                self.populate_notes_list(x.parent().text(1), x.text(1))

    @pyqtSlot()
    def on_notesListWidget_itemSelectionChanged(self):
        for x in self.notesListWidget.selectedItems():
            self.fetchPageThread.fetchSignal.connect(self.on_fetchPageThread_fetchComplete)
            self.titleLabel.setText("Syncing")
            self.statusBar().showMessage("Syncing")
            # self.fetchPageThread.fetchSignal.connect(lambda:self.view.setHtml("<body>hello world</body>"))
            self.fetchPageThread.fetch(self.page_dict[x.data(1)])

    def on_fetchPageThread_fetchComplete(self, string):
        self.view.setHtml(string)
        self.titleLabel.setText(self.view.title())
        self.statusBar().showMessage("Page fetched")

    def on_syncAllThread_syncComplete(self, dbm):
        self.dbm_obj = dbm
        self.statusBar().showMessage("Sync complete")

    def createHorizontalGroupBox(self):
        self.horizontalGroupBox = QGroupBox()
        layout = QHBoxLayout()

        self.sectionTreeWidget.setHeaderHidden(1)
        layout.addWidget(self.sectionTreeWidget, 0)
        self.sectionTreeWidget.setStyleSheet("background-color: rgb(215,227,229)")
        self.notesListWidget.setWindowTitle('Notes')
        layout.addWidget(self.notesListWidget, 0)
        self.notesListWidget.setStyleSheet("QListWidget {background-color: rgb(196,226,233)}")

        subVBox = QGroupBox()
        vLayout = QVBoxLayout()

        self.titleLabel = QLabel()
        vLayout.addWidget(self.titleLabel, 0)

        self.view = QWebView()
        vLayout.addWidget(self.view, 1)

        subVBox.setLayout(vLayout)

        layout.addWidget(subVBox, 1)

        self.horizontalGroupBox.setLayout(layout)

    def closeEvent(self, event):
        if self.maybeSave():
            self.writeSettings()
            event.accept()
        else:
            event.ignore()

    def newFile(self):
        self.readDB()
        # if self.maybeSave():
        #     self.textEdit.clear()
        #     self.setCurrentFile('')

    def open(self):
        if self.maybeSave():
            fileName, _ = QFileDialog.getOpenFileName(self)
            if fileName:
                self.loadFile(fileName)

    def save(self):
        if self.curFile:
            return self.saveFile(self.curFile)

        return self.saveAs()

    def saveAs(self):
        fileName, _ = QFileDialog.getSaveFileName(self)
        if fileName:
            return self.saveFile(fileName)
        return False

    def on_sectionList_selection_changed(self):
        print('selected item index changed '),

    def populate_section_list(self, hierarchy, notebook_dict, section_dict):
        self.sectionTreeWidget.clear()
        for notebook_id in hierarchy.keys():
            notebook_sectionTreeWidget = QTreeWidgetItem(self.sectionTreeWidget,
                                                         [notebook_dict[notebook_id].name, notebook_id])
            for section_id in hierarchy[notebook_id].keys():
                QTreeWidgetItem(notebook_sectionTreeWidget, [section_dict[section_id].name, section_id])
        self.sectionTreeWidget.show()
        self.sectionTreeWidget.expandAll()

    def populate_notes_list(self, notebook_id, section_id):
        self.notesListWidget.clear()
        for page_id in self.hierarchy_dict[notebook_id][section_id]:
            item = QListWidgetItem(self.page_dict[page_id].title, self.notesListWidget)
            item.setData(1, QVariant(page_id))

    def readDB(self):

        self.dbm_obj.read()
        [self.hierarchy_dict, self.notebook_dict, self.section_dict, self.page_dict] = self.dbm_obj.get_hierarchy_dict()
        self.populate_section_list(self.hierarchy_dict, self.notebook_dict, self.section_dict)

    def sync(self):
        self.syncAllThread.syncCompleteSignal.connect(self.on_syncAllThread_syncComplete)
        self.statusBar().showMessage("Syncing")
        self.syncAllThread.sync()

    def about(self):
        QMessageBox.about(self, "About Application",
                          "The <b>Application</b> example demonstrates how to write "
                          "modern GUI applications using Qt, with a menu bar, "
                          "toolbars, and a status bar.")

    def documentWasModified(self):
        self.setWindowModified(self.textEdit.document().isModified())

    def createActions(self):
        root = QFileInfo(__file__).absolutePath()

        self.newAct = QAction(QIcon(root + '/images/new.png'), "&New", self,
                              shortcut=QKeySequence.New, statusTip="Create a new file",
                              triggered=self.newFile)

        self.openAct = QAction(QIcon(root + '/images/open.png'), "&Open...",
                               self, shortcut=QKeySequence.Open,
                               statusTip="Open an existing file", triggered=self.open)

        self.saveAct = QAction(QIcon(root + '/images/save.png'), "&Save", self,
                               shortcut=QKeySequence.Save,
                               statusTip="Save the document to disk", triggered=self.save)

        self.saveAsAct = QAction("Save &As...", self,
                                 shortcut=QKeySequence.SaveAs,
                                 statusTip="Save the document under a new name",
                                 triggered=self.saveAs)

        self.syncAct = QAction("S&ync", self,
                               statusTip="Sync everything",
                               triggered=self.sync)

        self.readDBAct = QAction("Read DB", self, statusTip="Read stored DB",
                                 triggered=self.readDB)

        self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q",
                               statusTip="Exit the application", triggered=self.close)

        self.cutAct = QAction(QIcon(root + '/images/cut.png'), "Cu&t", self,
                              shortcut=QKeySequence.Cut,
                              statusTip="Cut the current selection's contents to the clipboard",
                              triggered=self.textEdit.cut)

        self.copyAct = QAction(QIcon(root + '/images/copy.png'), "&Copy", self,
                               shortcut=QKeySequence.Copy,
                               statusTip="Copy the current selection's contents to the clipboard",
                               triggered=self.textEdit.copy)

        self.pasteAct = QAction(QIcon(root + '/images/paste.png'), "&Paste",
                                self, shortcut=QKeySequence.Paste,
                                statusTip="Paste the clipboard's contents into the current selection",
                                triggered=self.textEdit.paste)

        self.aboutAct = QAction("&About", self,
                                statusTip="Show the application's About box",
                                triggered=self.about)

        self.aboutQtAct = QAction("About &Qt", self,
                                  statusTip="Show the Qt library's About box",
                                  triggered=QApplication.instance().aboutQt)

        self.cutAct.setEnabled(False)
        self.copyAct.setEnabled(False)
        self.textEdit.copyAvailable.connect(self.cutAct.setEnabled)
        self.textEdit.copyAvailable.connect(self.copyAct.setEnabled)

    def createMenus(self):
        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenu.addAction(self.newAct)
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.saveAct)
        self.fileMenu.addAction(self.saveAsAct)
        self.fileMenu.addAction(self.syncAct)
        self.fileMenu.addAction(self.readDBAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.editMenu = self.menuBar().addMenu("&Edit")
        self.editMenu.addAction(self.cutAct)
        self.editMenu.addAction(self.copyAct)
        self.editMenu.addAction(self.pasteAct)

        self.menuBar().addSeparator()

        self.helpMenu = self.menuBar().addMenu("&Help")
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

    def createToolBars(self):
        self.fileToolBar = self.addToolBar("File")
        self.fileToolBar.addAction(self.newAct)
        self.fileToolBar.addAction(self.openAct)
        self.fileToolBar.addAction(self.saveAct)

        self.editToolBar = self.addToolBar("Edit")
        self.editToolBar.addAction(self.cutAct)
        self.editToolBar.addAction(self.copyAct)
        self.editToolBar.addAction(self.pasteAct)

    def createStatusBar(self):
        self.statusBar().showMessage("Ready")

    def readSettings(self):
        settings = QSettings("Trolltech", "Application Example")
        pos = settings.value("pos", QPoint(200, 200))
        size = settings.value("size", QSize(400, 400))
        self.resize(size)
        self.move(pos)

    def writeSettings(self):
        settings = QSettings("Trolltech", "Application Example")
        settings.setValue("pos", self.pos())
        settings.setValue("size", self.size())

    def maybeSave(self):
        if self.textEdit.document().isModified():
            ret = QMessageBox.warning(self, "Application",
                                      "The document has been modified.\nDo you want to save "
                                      "your changes?",
                                      QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)

            if ret == QMessageBox.Save:
                return self.save()

            if ret == QMessageBox.Cancel:
                return False

        return True

    def loadFile(self, fileName):
        file = QFile(fileName)
        if not file.open(QFile.ReadOnly | QFile.Text):
            QMessageBox.warning(self, "Application",
                                "Cannot read file %s:\n%s." % (fileName, file.errorString()))
            return

        inf = QTextStream(file)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.textEdit.setPlainText(inf.readAll())
        QApplication.restoreOverrideCursor()

        self.setCurrentFile(fileName)
        self.statusBar().showMessage("File loaded", 2000)

    def saveFile(self, fileName):
        file = QFile(fileName)
        if not file.open(QFile.WriteOnly | QFile.Text):
            QMessageBox.warning(self, "Application",
                                "Cannot write file %s:\n%s." % (fileName, file.errorString()))
            return False

        outf = QTextStream(file)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        outf << self.textEdit.toPlainText()
        QApplication.restoreOverrideCursor()

        self.setCurrentFile(fileName);
        self.statusBar().showMessage("File saved", 2000)
        return True

    def setCurrentFile(self, fileName):
        self.curFile = fileName
        self.textEdit.document().setModified(False)
        self.setWindowModified(False)

        if self.curFile:
            shownName = self.strippedName(self.curFile)
        else:
            shownName = 'OneNote for linux'

        self.setWindowTitle(shownName)

    def strippedName(self, fullFileName):
        return QFileInfo(fullFileName).fileName()

    def write_at_exit(self):
        self.dbm_obj.write([self.hierarchy_dict, self.notebook_dict, self.section_dict, self.page_dict])
Esempio n. 32
0
class SessionsManager(QDialog):
    """Session Manager, to load different configurations of ninja."""

    def __init__(self, parent=None):
        super(SessionsManager, self).__init__(parent, Qt.Dialog)
        self._ninja = parent
        self.setModal(True)
        self.setWindowTitle(translations.TR_SESSIONS_TITLE)
        self.setMinimumWidth(550)
        self.setMinimumHeight(450)
        self._manager = _SessionManager(parent)
        self._load_ui()

    def install(self):
        self._manager.load_sessions()

    def _load_ui(self):
        main_layout = QVBoxLayout(self)
        main_layout.addWidget(QLabel(translations.TR_SESSIONS_DIALOG_BODY))
        main_hbox = QHBoxLayout()
        # Session list
        session_layout = QVBoxLayout()
        self._session_list = QTreeWidget()
        self._session_list.setHeaderLabels(["Session", "Last Modified"])
        session_layout.addWidget(self._session_list)
        # Content frame
        content_frame = QFrame()
        content_frame.hide()
        frame_layout = QVBoxLayout(content_frame)
        content_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        session_layout.addWidget(content_frame)
        frame_layout.setContentsMargins(0, 0, 0, 0)
        self._content_list = QTreeWidget()
        self._content_list.setHeaderHidden(True)
        frame_layout.addWidget(self._content_list)
        # Separator line
        line_frame = QFrame()
        line_frame.setFrameStyle(QFrame.VLine | QFrame.Sunken)
        # Buttons
        btn_layout = QVBoxLayout()
        btn_create = QPushButton(translations.TR_SESSIONS_BTN_CREATE)
        btn_activate = QPushButton(translations.TR_SESSIONS_BTN_ACTIVATE)
        btn_update = QPushButton(translations.TR_SESSIONS_BTN_UPDATE)
        btn_delete = QPushButton(translations.TR_SESSIONS_BTN_DELETE)
        btn_details = QPushButton(translations.TR_SESSIONS_BTN_DETAILS)
        btn_details.setCheckable(True)
        # Add buttons to layout
        btn_layout.addWidget(btn_create)
        btn_layout.addWidget(btn_activate)
        btn_layout.addWidget(btn_update)
        btn_layout.addWidget(btn_delete)
        btn_layout.addStretch()
        btn_layout.addWidget(btn_details)
        # Add widgets and layouts to the main layout
        main_layout.addLayout(main_hbox)
        main_hbox.addLayout(session_layout)
        main_hbox.addWidget(line_frame)
        main_hbox.addLayout(btn_layout)
        main_hbox.setSizeConstraint(QLayout.SetFixedSize)
        btn_details.toggled[bool].connect(content_frame.setVisible)
        # Connections
        self._session_list.itemSelectionChanged.connect(
            self.load_session_content)
        btn_activate.clicked.connect(self.open_session)
        btn_update.clicked.connect(self.save_session)
        btn_create.clicked.connect(self.create_session)
        btn_delete.clicked.connect(self.delete_session)

    def __load_sessions(self):
        for session_name in self._manager.sessions:
            item = QTreeWidgetItem()
            item.setText(0, session_name)
            item.setText(1, "FIXME: manage this!")
            self._session_list.addTopLevelItem(item)
        self._session_list.setCurrentItem(
            self._session_list.topLevelItem(0))

    def load_session_content(self):
        """Load the selected session, replacing the current session."""
        item = self._session_list.currentItem()
        self._content_list.clear()
        if item is not None:
            key = item.text(0)
            files, projects = self._manager.get_session(key)
            files_parent = QTreeWidgetItem(
                self._content_list, [translations.TR_FILES])
            for ffile in files:
                QTreeWidgetItem(files_parent, [ffile[0]])
            projects_parent = QTreeWidgetItem(
                self._content_list, [translations.TR_PROJECT])
            for project in projects:
                QTreeWidgetItem(projects_parent, [project])

            files_parent.setExpanded(True)
            projects_parent.setExpanded(True)

    def create_session(self):
        """Create a new Session."""
        session_info = QInputDialog.getText(
            None, translations.TR_SESSIONS_CREATE_TITLE,
            translations.TR_SESSIONS_CREATE_BODY)
        if session_info[1]:
            session_name = session_info[0]
            if not session_name or session_name in settings.SESSIONS:
                QMessageBox.information(
                    self,
                    translations.TR_SESSIONS_MESSAGE_TITLE,
                    translations.TR_SESSIONS_MESSAGE_BODY)
                return
            self._manager.save_session_data(session_name)
        self.close()

    def save_session(self):
        """Save current session"""
        if self._session_list.currentItem():
            session_name = self._session_list.currentItem().text(0)
            self._manager.save_session_data(session_name)
            self._ninja.show_message(
                translations.TR_SESSIONS_UPDATED_NOTIF.format(session_name))
            self.load_session_content()

    def open_session(self):
        """Open a saved session"""
        if self._session_list.currentItem():
            session_name = self._session_list.currentItem().text(0)
            self._manager.load_session(session_name)
            self._manager.set_current_session(session_name)
            self.close()

    def delete_session(self):
        """Delete a session"""
        if self._session_list.currentItem():
            key = self._session_list.currentItem().text(0)
            self._manager.delete_session(key)
            self._session_list.takeTopLevelItem(
                self._session_list.currentIndex().row())

    @property
    def current_session(self):
        return self._manager.current_session()

    def showEvent(self, event):
        super().showEvent(event)
        self.__load_sessions()

    def hideEvent(self, event):
        super().hideEvent(event)
        self._session_list.clear()
Esempio n. 33
0
class JackConnectionsDialog(QDialog):
    def __init__(self, jack_client, parent=None, **kwargs):
        super().__init__(parent)

        self.resize(600, 400)

        self.setLayout(QGridLayout())
        # self.layout().setContentsMargins(0, 0, 0, 0)

        self.output_widget = QTreeWidget(self)
        self.output_widget.setHeaderLabels(['Output ports'])

        self.input_widget = QTreeWidget(self)
        self.input_widget.setHeaderLabels(['Input ports'])

        self.connections_widget = ConnectionsWidget(self.output_widget,
                                                    self.input_widget,
                                                    parent=self)
        self.output_widget.itemExpanded.connect(self.connections_widget.update)
        self.output_widget.itemCollapsed.connect(self.connections_widget.update)
        self.input_widget.itemExpanded.connect(self.connections_widget.update)
        self.input_widget.itemCollapsed.connect(self.connections_widget.update)

        self.input_widget.itemSelectionChanged.connect(
            self.__input_selection_changed)
        self.output_widget.itemSelectionChanged.connect(
            self.__output_selection_changed)

        self.layout().addWidget(self.output_widget, 0, 0)
        self.layout().addWidget(self.connections_widget, 0, 1)
        self.layout().addWidget(self.input_widget, 0, 2)

        self.layout().setColumnStretch(0, 2)
        self.layout().setColumnStretch(1, 1)
        self.layout().setColumnStretch(2, 2)

        self.connectButton = QPushButton('Connect', self)
        self.connectButton.clicked.connect(self.__disconnect_selected)
        self.connectButton.setEnabled(False)
        self.layout().addWidget(self.connectButton, 1, 1)

        self.dialogButtons = QDialogButtonBox(
            QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
        self.dialogButtons.accepted.connect(self.accept)
        self.dialogButtons.rejected.connect(self.reject)
        self.layout().addWidget(self.dialogButtons, 2, 0, 1, 3)

        self.__jack_client = jack_client
        self.__selected_in = None
        self.__selected_out = None

        self.connections = []
        self.update_graph()

    def set_connections(self, connections):
        self.connections = connections
        self.connections_widget.connections = self.connections
        self.connections_widget.update()

    def update_graph(self):
        input_ports = self.__jack_client.get_ports(is_audio=True, is_input=True)

        self.output_widget.clear()
        for port in range(8):
            self.output_widget.addTopLevelItem(
                QTreeWidgetItem(['output_' + str(port)]))

        self.input_widget.clear()
        clients = {}
        for port in input_ports:
            client_name = port.name[:port.name.index(':')]

            if client_name not in clients:
                clients[client_name] = ClientItem(client_name)
                self.input_widget.addTopLevelItem(clients[client_name])

            clients[client_name].add_port(port.name)

    def __input_selection_changed(self):
        if self.input_widget.selectedItems():
            self.__selected_in = self.input_widget.selectedItems()[0]
        else:
            self.__selected_in = None

        self.__check_selection()

    def __output_selection_changed(self):
        if self.output_widget.selectedItems():
            self.__selected_out = self.output_widget.selectedItems()[0]
        else:
            self.__selected_out = None

        self.__check_selection()

    def __check_selection(self):
        if self.__selected_in is not None and self.__selected_out is not None:
            output = self.output_widget.indexOfTopLevelItem(self.__selected_out)

            self.connectButton.clicked.disconnect()
            self.connectButton.setEnabled(True)

            if self.__selected_in.name in self.connections[output]:
                self.connectButton.setText('Disconnect')
                self.connectButton.clicked.connect(self.__disconnect_selected)
            else:
                self.connectButton.setText('Connect')
                self.connectButton.clicked.connect(self.__connect_selected)
        else:
            self.connectButton.setEnabled(False)

    def __connect_selected(self):
        output = self.output_widget.indexOfTopLevelItem(self.__selected_out)
        self.connections[output].append(self.__selected_in.name)
        self.connections_widget.update()
        self.__check_selection()

    def __disconnect_selected(self):
        output = self.output_widget.indexOfTopLevelItem(self.__selected_out)
        self.connections[output].remove(self.__selected_in.name)
        self.connections_widget.update()
        self.__check_selection()
Esempio n. 34
0
class SqlConnectionWidget(QWidget):
    """
    Class implementing a widget showing the SQL connections.
    
    @signal tableActivated(str) emitted after the entry for a table has been
        activated
    @signal schemaRequested(str) emitted when the schema display is requested
    @signal cleared() emitted after the connection tree has been cleared
    """
    tableActivated = pyqtSignal(str)
    schemaRequested = pyqtSignal(str)
    cleared = pyqtSignal()
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super(SqlConnectionWidget, self).__init__(parent)
        
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        
        self.__connectionTree = QTreeWidget(self)
        self.__connectionTree.setObjectName("connectionTree")
        self.__connectionTree.setHeaderLabels([self.tr("Database")])
        if qVersion() >= "5.0.0":
            self.__connectionTree.header().setSectionResizeMode(
                QHeaderView.Stretch)
        else:
            self.__connectionTree.header().setResizeMode(QHeaderView.Stretch)
        refreshAction = QAction(self.tr("Refresh"), self.__connectionTree)
        self.__schemaAction = QAction(
            self.tr("Show Schema"), self.__connectionTree)
        
        refreshAction.triggered.connect(self.refresh)
        self.__schemaAction.triggered.connect(self.showSchema)
        
        self.__connectionTree.addAction(refreshAction)
        self.__connectionTree.addAction(self.__schemaAction)
        self.__connectionTree.setContextMenuPolicy(Qt.ActionsContextMenu)
        
        layout.addWidget(self.__connectionTree)
        
        self.__activating = False
        
        self.__connectionTree.itemActivated.connect(self.__itemActivated)
        self.__connectionTree.currentItemChanged.connect(
            self.__currentItemChanged)
        
        self.__activeDb = ""
    
    def refresh(self):
        """
        Public slot to refresh the connection tree.
        """
        self.__connectionTree.clear()
        self.cleared.emit()
        
        connectionNames = QSqlDatabase.connectionNames()
        
        foundActiveDb = False
        for name in connectionNames:
            root = QTreeWidgetItem(self.__connectionTree)
            db = QSqlDatabase.database(name, False)
            root.setText(0, self.__dbCaption(db))
            if name == self.__activeDb:
                foundActiveDb = True
                self.__setActive(root)
            if db.isOpen():
                tables = db.tables()
                for table in tables:
                    itm = QTreeWidgetItem(root)
                    itm.setText(0, table)
        
        if not foundActiveDb and connectionNames:
            self.__activeDb = connectionNames[0]
            self.__setActive(self.__connectionTree.topLevelItem(0))
    
    def showSchema(self):
        """
        Public slot to show schema data of a database.
        """
        cItm = self.__connectionTree.currentItem()
        if cItm is None or cItm.parent() is None:
            return
        self.__setActive(cItm.parent())
        self.schemaRequested.emit(cItm.text(0))
    
    def __itemActivated(self, itm, column):
        """
        Private slot handling the activation of an item.
        
        @param itm reference to the item (QTreeWidgetItem)
        @param column column that was activated (integer)
        """
        if itm is None:
            return
        
        if not self.__activating:
            self.__activating = True
            if itm.parent() is None:
                self.__setActive(itm)
            else:
                self.__setActive(itm.parent())
                self.tableActivated.emit(itm.text(0))
            self.__activating = False
    
    def __currentItemChanged(self, current, previous):
        """
        Private slot handling a change of the current item.
        
        @param current reference to the new current item (QTreeWidgetItem)
        @param previous reference to the previous current item
            (QTreeWidgetItem)
        """
        self.__schemaAction.setEnabled(
            current is not None and current.parent() is not None)
    
    def __dbCaption(self, db):
        """
        Private method to assemble a string for the caption.
        
        @param db reference to the database object (QSqlDatabase)
        @return caption string (string)
        """
        nm = db.driverName()
        nm += ":"
        if db.userName():
            nm += db.userName()
            nm += "@"
        nm += db.databaseName()
        return nm
    
    def __setBold(self, itm, bold):
        """
        Private slot to set the font to bold.
        
        @param itm reference to the item to be changed (QTreeWidgetItem)
        @param bold flag indicating bold (boolean)
        """
        font = itm.font(0)
        font.setBold(bold)
        itm.setFont(0, font)
    
    def currentDatabase(self):
        """
        Public method to get the current database.
        
        @return reference to the current database (QSqlDatabase)
        """
        return QSqlDatabase.database(self.__activeDb)
    
    def __setActive(self, itm):
        """
        Private slot to set an item to active.
        
        @param itm reference to the item to set as the active item
            (QTreeWidgetItem)
        """
        for index in range(self.__connectionTree.topLevelItemCount()):
            if self.__connectionTree.topLevelItem(index).font(0).bold():
                self.__setBold(
                    self.__connectionTree.topLevelItem(index), False)
        
        if itm is None:
            return
        
        self.__setBold(itm, True)
        self.__activeDb = QSqlDatabase.connectionNames()[
            self.__connectionTree.indexOfTopLevelItem(itm)]
Esempio n. 35
0
class SessionsManager(QDialog):
    """Session Manager, to load different configurations of ninja."""
    def __init__(self, parent=None):
        super(SessionsManager, self).__init__(parent, Qt.Dialog)
        self._ninja = parent
        self.setModal(True)
        self.setWindowTitle(translations.TR_SESSIONS_TITLE)
        self.setMinimumWidth(550)
        self.setMinimumHeight(450)
        self._manager = _SessionManager(parent)
        self._load_ui()

    def install(self):
        self._manager.load_sessions()

    def _load_ui(self):
        main_layout = QVBoxLayout(self)
        main_layout.addWidget(QLabel(translations.TR_SESSIONS_DIALOG_BODY))
        main_hbox = QHBoxLayout()
        # Session list
        session_layout = QVBoxLayout()
        self._session_list = QTreeWidget()
        self._session_list.setHeaderLabels(["Session", "Last Modified"])
        session_layout.addWidget(self._session_list)
        # Content frame
        content_frame = QFrame()
        content_frame.hide()
        frame_layout = QVBoxLayout(content_frame)
        content_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        session_layout.addWidget(content_frame)
        frame_layout.setContentsMargins(0, 0, 0, 0)
        self._content_list = QTreeWidget()
        self._content_list.setHeaderHidden(True)
        frame_layout.addWidget(self._content_list)
        # Separator line
        line_frame = QFrame()
        line_frame.setFrameStyle(QFrame.VLine | QFrame.Sunken)
        # Buttons
        btn_layout = QVBoxLayout()
        btn_create = QPushButton(translations.TR_SESSIONS_BTN_CREATE)
        btn_activate = QPushButton(translations.TR_SESSIONS_BTN_ACTIVATE)
        btn_update = QPushButton(translations.TR_SESSIONS_BTN_UPDATE)
        btn_delete = QPushButton(translations.TR_SESSIONS_BTN_DELETE)
        btn_details = QPushButton(translations.TR_SESSIONS_BTN_DETAILS)
        btn_details.setCheckable(True)
        # Add buttons to layout
        btn_layout.addWidget(btn_create)
        btn_layout.addWidget(btn_activate)
        btn_layout.addWidget(btn_update)
        btn_layout.addWidget(btn_delete)
        btn_layout.addStretch()
        btn_layout.addWidget(btn_details)
        # Add widgets and layouts to the main layout
        main_layout.addLayout(main_hbox)
        main_hbox.addLayout(session_layout)
        main_hbox.addWidget(line_frame)
        main_hbox.addLayout(btn_layout)
        main_hbox.setSizeConstraint(QLayout.SetFixedSize)
        btn_details.toggled[bool].connect(content_frame.setVisible)
        # Connections
        self._session_list.itemSelectionChanged.connect(
            self.load_session_content)
        btn_activate.clicked.connect(self.open_session)
        btn_update.clicked.connect(self.save_session)
        btn_create.clicked.connect(self.create_session)
        btn_delete.clicked.connect(self.delete_session)

    def __load_sessions(self):
        for session_name in self._manager.sessions:
            item = QTreeWidgetItem()
            item.setText(0, session_name)
            item.setText(1, "FIXME: manage this!")
            self._session_list.addTopLevelItem(item)
        self._session_list.setCurrentItem(self._session_list.topLevelItem(0))

    def load_session_content(self):
        """Load the selected session, replacing the current session."""
        item = self._session_list.currentItem()
        self._content_list.clear()
        if item is not None:
            key = item.text(0)
            files, projects = self._manager.get_session(key)
            files_parent = QTreeWidgetItem(self._content_list,
                                           [translations.TR_FILES])
            for ffile in files:
                QTreeWidgetItem(files_parent, [ffile[0]])
            projects_parent = QTreeWidgetItem(self._content_list,
                                              [translations.TR_PROJECT])
            for project in projects:
                QTreeWidgetItem(projects_parent, [project])

            files_parent.setExpanded(True)
            projects_parent.setExpanded(True)

    def create_session(self):
        """Create a new Session."""
        session_info = QInputDialog.getText(
            None, translations.TR_SESSIONS_CREATE_TITLE,
            translations.TR_SESSIONS_CREATE_BODY)
        if session_info[1]:
            session_name = session_info[0]
            if not session_name or session_name in settings.SESSIONS:
                QMessageBox.information(self,
                                        translations.TR_SESSIONS_MESSAGE_TITLE,
                                        translations.TR_SESSIONS_MESSAGE_BODY)
                return
            self._manager.save_session_data(session_name)
        self.close()

    def save_session(self):
        """Save current session"""
        if self._session_list.currentItem():
            session_name = self._session_list.currentItem().text(0)
            self._manager.save_session_data(session_name)
            self._ninja.show_message(
                translations.TR_SESSIONS_UPDATED_NOTIF.format(session_name))
            self.load_session_content()

    def open_session(self):
        """Open a saved session"""
        if self._session_list.currentItem():
            session_name = self._session_list.currentItem().text(0)
            self._manager.load_session(session_name)
            self._manager.set_current_session(session_name)
            self.close()

    def delete_session(self):
        """Delete a session"""
        if self._session_list.currentItem():
            key = self._session_list.currentItem().text(0)
            self._manager.delete_session(key)
            self._session_list.takeTopLevelItem(
                self._session_list.currentIndex().row())

    @property
    def current_session(self):
        return self._manager.current_session()

    def showEvent(self, event):
        super().showEvent(event)
        self.__load_sessions()

    def hideEvent(self, event):
        super().hideEvent(event)
        self._session_list.clear()
Esempio n. 36
0
class FinalExamsTab:
    model: GuiModel

    def __init__(self, model):
        self.__final_exam_list = QTreeWidget()
        self.__final_exam_list.setHeaderLabels(["Date and time", "Room"])
        self.__final_exam_list.setHeaderLabels(["Date", "Time", "Day", "Room"])
        self.__final_exam_list.itemSelectionChanged.connect(
            self.__final_exam_selected)

        self.__final_exam_details = QTreeWidget()
        self.__final_exam_details.setColumnCount(3)
        self.__final_exam_details.setHeaderLabels(
            ["Student", "Points", "Grade"])
        self.__final_exam_details.itemActivated.connect(
            lambda item, column: file_double_clicked(self.model, item))
        self.__final_exam_details.itemSelectionChanged.connect(
            self.__final_exam_file_selected)

        self.__final_exam_file_details = FileDetailsWidget(model)

        details_splitter = QSplitter(Qt.Horizontal)
        details_splitter.addWidget(self.__final_exam_details)
        details_splitter.addWidget(self.__final_exam_file_details.widget)

        self.widget = QSplitter(Qt.Vertical)
        self.widget.addWidget(self.__final_exam_list)
        self.widget.addWidget(details_splitter)
        self.widget.setStretchFactor(0, 3)
        self.widget.setStretchFactor(1, 1)

        self.model = model
        self.__load_final_exam()

        self.model.subject_changed.connect(self.__subject_changed)

    def __subject_changed(self):
        self.__load_final_exam()

    def __load_final_exam(self):
        self.__final_exam_list.clear()
        self.__final_exam_details.clear()

        if self.model.subject is not None:
            for final_exam in self.model.subject.final_exams:
                final_exam_item = FinalExamItem([
                    final_exam.date_time.strftime("%d.%m.%Y"),
                    final_exam.date_time.strftime("%H:%M"),
                    final_exam.date_time.strftime("%A"), final_exam.room
                ], final_exam)
                self.__final_exam_list.addTopLevelItem(final_exam_item)

    def __final_exam_selected(self):
        self.__final_exam_details.clear()

        final_exam_items = self.__final_exam_list.selectedItems()
        if final_exam_items:
            final_exam: FinalExam = final_exam_items[0].final_exam

            for grade in final_exam.get_submissions():
                student_item = StudentItem([
                    f"{grade.student.surname} {grade.student.name}",
                    points_or_none(grade.points),
                    grade_or_none(grade.grade)
                ], grade.student)

                self.__final_exam_details.addTopLevelItem(student_item)
                add_file_items(grade.files, student_item)

            self.__final_exam_details.expandAll()

    def __final_exam_file_selected(self):
        self.__final_exam_file_details.master_selection_changed(
            self.__final_exam_details.selectedItems())
Esempio n. 37
0
class WidgetList(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.id_dict: Dict[int, Tuple[List[str], int]] = {}
        self.item_dict: DefaultDict[str, Set[tuple]] = defaultdict(set)
        self._init_ui()

    def _init_ui(self):
        layout = QVBoxLayout(self)
        self.splitter = QSplitter(orientation=Qt.Vertical, parent=self)

        self.label = QLabel('Number of widgets (not calculated)')
        self.update_button = QPushButton('Update')
        self.widget_list = QTreeWidget(self)
        self.differ_list = QListWidget(self)

        self.search_widget = QWidget(self)
        self.search_line = QLineEdit(self.search_widget)
        self.search_tree = QTreeWidget(self.search_widget)

        self.widget_list.setHeaderLabels(
            ['Class name', 'Object name', 'Number of children', 'id'])
        self.search_tree.setHeaderLabels(
            ['Class name', 'Object name', 'Number of children', 'id'])

        s_layout = QVBoxLayout(self.search_widget)
        s_layout.addWidget(self.search_line)
        s_layout.addWidget(self.search_tree)

        layout.addWidget(self.label)
        layout.addWidget(self.update_button)
        layout.addWidget(self.splitter)

        self.splitter.addWidget(self.widget_list)
        self.splitter.addWidget(self.differ_list)
        self.splitter.addWidget(self.search_widget)

        self.update_button.clicked.connect(self.update_widget_list)
        self.search_line.editingFinished.connect(self._update_search_list)

        self.differ_list.hide()

    @pyqtSlot(name='updateSearchList')
    def _update_search_list(self):
        search: str = self.search_line.text().lower()
        self.search_tree.clear()
        for key, value in self.item_dict.items():
            if key.lower().startswith(search):
                for name_list in value:
                    TreeWidgetItem(self.search_tree, name_list)

    def update_widget_list(self):
        self.widget_list.clear()
        self.differ_list.clear()
        self.item_dict.clear()

        count = 0
        new_id_dict: Dict[int, Tuple[list, int]] = {}

        for obj in QApplication.topLevelWidgets():
            count += self._update_list(obj, self.widget_list, new_id_dict)

        self.widget_list.update()
        self.label.setText(f'Number of widgets = {count}')
        self.widget_list.sortByColumn(2, Qt.DescendingOrder)

        for value in self.id_dict.values():
            QListWidgetItem(' '.join(value[0]), self.differ_list)

        self.id_dict = new_id_dict

    def _update_list(self, parent_obj: QObject, parent_node: QTreeWidget
                     or QTreeWidgetItem,
                     new_id_dict: Dict[int, Tuple[List[str], int]]):
        count = 0

        for obj in parent_obj.children():
            name_list = self._get_name_list(obj)
            item = TreeWidgetItem(parent_node, name_list)

            obj_count = 0

            for o in obj.children():
                obj_count += self._update_list(o, item, new_id_dict)

            item.setText(2, str(obj_count))

            name_list[2] = str(obj_count)
            self.item_dict[str(obj.__class__.__name__)].add(tuple(name_list))

            id_ = id(obj)
            new_id_dict[id_] = [item.text(i) for i in range(4)], obj_count

            _, prev_count = self.id_dict.pop(id_, (None, None))

            if prev_count is None:
                fr = 1
            elif prev_count + obj_count == 0:
                fr = 0
            else:
                fr = abs(prev_count - obj_count) / (prev_count + obj_count)
            item.setBackground(3, get_color(fr))

            count += 1 + obj_count

        return count

    @staticmethod
    def _get_name_list(obj: QObject):
        return [
            str(obj.__class__.__name__),
            obj.objectName(), '',
            str(id(obj))
        ]
Esempio n. 38
0
class tree(QWidget):
    '''Copied from Jiliang's Github:
        https://github.com/ligerliu/pyH5_GUI/blob/master/pyH5_GUI/NewTree.py
    '''
    (FILE, FILE_PATH, H5GROUP) = range(3)

    def __init__(self):
        super().__init__()
        self.title = 'Tree of h5 data'
        self.left = 10
        self.top = 10
        self.width = 720
        self.height = 640

        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.datalayout = QVBoxLayout()
        self.tree = QTreeWidget()
        header = QTreeWidgetItem(['File'])
        self.tree.setHeaderItem(header)
        self.datalayout.addWidget(self.tree)
        self.group_root = None

    def clear(self):
        self.tree.clear()

    def add_group(self, group):
        self.group_name = os.path.basename(group)
        self.group_file_path = group
        self.group_root = QTreeWidgetItem(
            self.tree, [self.group_name, self.group_file_path, ''])

    def add_file(self, h5file, group=None):
        self.h5_file_path = h5file
        self.f = h5py.File(h5file, 'r')
        self.filename = self.f.filename.split('/')[-1]

        if group is None:
            self.tree_root = QTreeWidgetItem(
                self.tree, [self.filename, self.h5_file_path, ''])
            self.add_branch(self.tree_root, self.f)
        else:
            #print('add group here')
            if self.group_root is None:
                self.add_group(group)
            hdf_branch = QTreeWidgetItem(
                [self.filename, self.h5_file_path, ''])
            print(self.filename, self.h5_file_path)
            self.group_root.addChild(hdf_branch)

            self.add_branch(hdf_branch, self.f)
        self.tree.setColumnWidth(0, 250)
        self.tree.setColumnWidth(1, 0)
        self.tree.setColumnWidth(2, 0)

    def add_branch(self, tree_root, h5file):
        for _ in h5file.keys():
            #print(_)
            branch = QTreeWidgetItem([
                str(h5file[_].name).split('/')[-1],
                str(self.h5_file_path),
                str(h5file[_].name)
            ])
            tree_root.addChild(branch)
            if isinstance(h5file[_], h5py.Group):
                self.add_branch(branch, h5file[_])

    @pyqtSlot(QTreeWidgetItem, int)
    def onItemClicked(self, item):
        print(self.filename, item.text(2))
Esempio n. 39
0
class DebugViewer(QWidget):
    """
    Class implementing a widget conatining various debug related views.
    
    The individual tabs contain the interpreter shell (optional),
    the filesystem browser (optional), the two variables viewers
    (global and local), a breakpoint viewer, a watch expression viewer and
    the exception logger. Additionally a list of all threads is shown.
    
    @signal sourceFile(string, int) emitted to open a source file at a line
    """
    sourceFile = pyqtSignal(str, int)
    
    def __init__(self, debugServer, docked, vm, parent=None,
                 embeddedShell=True, embeddedBrowser=True):
        """
        Constructor
        
        @param debugServer reference to the debug server object
        @param docked flag indicating a dock window
        @param vm reference to the viewmanager object
        @param parent parent widget (QWidget)
        @param embeddedShell flag indicating whether the shell should be
            included. This flag is set to False by those layouts, that have
            the interpreter shell in a separate window.
        @param embeddedBrowser flag indicating whether the file browser should
            be included. This flag is set to False by those layouts, that
            have the file browser in a separate window or embedded
            in the project browser instead.
        """
        super(DebugViewer, self).__init__(parent)
        
        self.debugServer = debugServer
        self.debugUI = None
        
        self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))
        
        self.__mainLayout = QVBoxLayout()
        self.__mainLayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.__mainLayout)
        
        self.__tabWidget = E5TabWidget()
        self.__mainLayout.addWidget(self.__tabWidget)
        
        self.embeddedShell = embeddedShell
        if embeddedShell:
            from QScintilla.Shell import ShellAssembly
            # add the interpreter shell
            self.shellAssembly = ShellAssembly(debugServer, vm, False)
            self.shell = self.shellAssembly.shell()
            index = self.__tabWidget.addTab(
                self.shellAssembly,
                UI.PixmapCache.getIcon("shell.png"), '')
            self.__tabWidget.setTabToolTip(index, self.shell.windowTitle())
        
        self.embeddedBrowser = embeddedBrowser
        if embeddedBrowser:
            from UI.Browser import Browser
            # add the browser
            self.browser = Browser()
            index = self.__tabWidget.addTab(
                self.browser,
                UI.PixmapCache.getIcon("browser.png"), '')
            self.__tabWidget.setTabToolTip(index, self.browser.windowTitle())
        
        from .VariablesViewer import VariablesViewer
        # add the global variables viewer
        self.glvWidget = QWidget()
        self.glvWidgetVLayout = QVBoxLayout(self.glvWidget)
        self.glvWidgetVLayout.setContentsMargins(0, 0, 0, 0)
        self.glvWidgetVLayout.setSpacing(3)
        self.glvWidget.setLayout(self.glvWidgetVLayout)
        
        self.globalsViewer = VariablesViewer(self.glvWidget, True)
        self.glvWidgetVLayout.addWidget(self.globalsViewer)
        
        self.glvWidgetHLayout = QHBoxLayout()
        self.glvWidgetHLayout.setContentsMargins(3, 3, 3, 3)
        
        self.globalsFilterEdit = QLineEdit(self.glvWidget)
        self.globalsFilterEdit.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.glvWidgetHLayout.addWidget(self.globalsFilterEdit)
        self.globalsFilterEdit.setToolTip(
            self.tr("Enter regular expression patterns separated by ';'"
                    " to define variable filters. "))
        self.globalsFilterEdit.setWhatsThis(
            self.tr("Enter regular expression patterns separated by ';'"
                    " to define variable filters. All variables and"
                    " class attributes matched by one of the expressions"
                    " are not shown in the list above."))
        
        self.setGlobalsFilterButton = QPushButton(
            self.tr('Set'), self.glvWidget)
        self.glvWidgetHLayout.addWidget(self.setGlobalsFilterButton)
        self.glvWidgetVLayout.addLayout(self.glvWidgetHLayout)
        
        index = self.__tabWidget.addTab(
            self.glvWidget,
            UI.PixmapCache.getIcon("globalVariables.png"), '')
        self.__tabWidget.setTabToolTip(index, self.globalsViewer.windowTitle())
        
        self.setGlobalsFilterButton.clicked.connect(
            self.__setGlobalsFilter)
        self.globalsFilterEdit.returnPressed.connect(self.__setGlobalsFilter)
        
        # add the local variables viewer
        self.lvWidget = QWidget()
        self.lvWidgetVLayout = QVBoxLayout(self.lvWidget)
        self.lvWidgetVLayout.setContentsMargins(0, 0, 0, 0)
        self.lvWidgetVLayout.setSpacing(3)
        self.lvWidget.setLayout(self.lvWidgetVLayout)
        
        self.lvWidgetHLayout1 = QHBoxLayout()
        self.lvWidgetHLayout1.setContentsMargins(3, 3, 3, 3)
        
        self.stackComboBox = QComboBox(self.lvWidget)
        self.stackComboBox.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.lvWidgetHLayout1.addWidget(self.stackComboBox)

        self.sourceButton = QPushButton(self.tr('Source'), self.lvWidget)
        self.lvWidgetHLayout1.addWidget(self.sourceButton)
        self.sourceButton.setEnabled(False)
        self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout1)

        self.localsViewer = VariablesViewer(self.lvWidget, False)
        self.lvWidgetVLayout.addWidget(self.localsViewer)
        
        self.lvWidgetHLayout2 = QHBoxLayout()
        self.lvWidgetHLayout2.setContentsMargins(3, 3, 3, 3)
        
        self.localsFilterEdit = QLineEdit(self.lvWidget)
        self.localsFilterEdit.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.lvWidgetHLayout2.addWidget(self.localsFilterEdit)
        self.localsFilterEdit.setToolTip(
            self.tr(
                "Enter regular expression patterns separated by ';' to define "
                "variable filters. "))
        self.localsFilterEdit.setWhatsThis(
            self.tr(
                "Enter regular expression patterns separated by ';' to define "
                "variable filters. All variables and class attributes matched"
                " by one of the expressions are not shown in the list above."))
        
        self.setLocalsFilterButton = QPushButton(
            self.tr('Set'), self.lvWidget)
        self.lvWidgetHLayout2.addWidget(self.setLocalsFilterButton)
        self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout2)
        
        index = self.__tabWidget.addTab(
            self.lvWidget,
            UI.PixmapCache.getIcon("localVariables.png"), '')
        self.__tabWidget.setTabToolTip(index, self.localsViewer.windowTitle())
        
        self.sourceButton.clicked.connect(self.__showSource)
        self.stackComboBox.currentIndexChanged[int].connect(
            self.__frameSelected)
        self.setLocalsFilterButton.clicked.connect(self.__setLocalsFilter)
        self.localsFilterEdit.returnPressed.connect(self.__setLocalsFilter)
        
        from .CallStackViewer import CallStackViewer
        # add the call stack viewer
        self.callStackViewer = CallStackViewer(self.debugServer)
        index = self.__tabWidget.addTab(
            self.callStackViewer,
            UI.PixmapCache.getIcon("step.png"), "")
        self.__tabWidget.setTabToolTip(
            index, self.callStackViewer.windowTitle())
        self.callStackViewer.sourceFile.connect(self.sourceFile)
        self.callStackViewer.frameSelected.connect(
            self.__callStackFrameSelected)
        
        from .CallTraceViewer import CallTraceViewer
        # add the call trace viewer
        self.callTraceViewer = CallTraceViewer(self.debugServer)
        index = self.__tabWidget.addTab(
            self.callTraceViewer,
            UI.PixmapCache.getIcon("callTrace.png"), "")
        self.__tabWidget.setTabToolTip(
            index, self.callTraceViewer.windowTitle())
        self.callTraceViewer.sourceFile.connect(self.sourceFile)
        
        from .BreakPointViewer import BreakPointViewer
        # add the breakpoint viewer
        self.breakpointViewer = BreakPointViewer()
        self.breakpointViewer.setModel(self.debugServer.getBreakPointModel())
        index = self.__tabWidget.addTab(
            self.breakpointViewer,
            UI.PixmapCache.getIcon("breakpoints.png"), '')
        self.__tabWidget.setTabToolTip(
            index, self.breakpointViewer.windowTitle())
        self.breakpointViewer.sourceFile.connect(self.sourceFile)
        
        from .WatchPointViewer import WatchPointViewer
        # add the watch expression viewer
        self.watchpointViewer = WatchPointViewer()
        self.watchpointViewer.setModel(self.debugServer.getWatchPointModel())
        index = self.__tabWidget.addTab(
            self.watchpointViewer,
            UI.PixmapCache.getIcon("watchpoints.png"), '')
        self.__tabWidget.setTabToolTip(
            index, self.watchpointViewer.windowTitle())
        
        from .ExceptionLogger import ExceptionLogger
        # add the exception logger
        self.exceptionLogger = ExceptionLogger()
        index = self.__tabWidget.addTab(
            self.exceptionLogger,
            UI.PixmapCache.getIcon("exceptions.png"), '')
        self.__tabWidget.setTabToolTip(
            index, self.exceptionLogger.windowTitle())
        
        if self.embeddedShell:
            self.__tabWidget.setCurrentWidget(self.shellAssembly)
        else:
            if self.embeddedBrowser:
                self.__tabWidget.setCurrentWidget(self.browser)
            else:
                self.__tabWidget.setCurrentWidget(self.glvWidget)
        
        # add the threads viewer
        self.__mainLayout.addWidget(QLabel(self.tr("Threads:")))
        self.__threadList = QTreeWidget()
        self.__threadList.setHeaderLabels(
            [self.tr("ID"), self.tr("Name"),
             self.tr("State"), ""])
        self.__threadList.setSortingEnabled(True)
        self.__mainLayout.addWidget(self.__threadList)
        
        self.__doThreadListUpdate = True
        
        self.__threadList.currentItemChanged.connect(self.__threadSelected)
        
        self.__mainLayout.setStretchFactor(self.__tabWidget, 5)
        self.__mainLayout.setStretchFactor(self.__threadList, 1)
        
        self.currPage = None
        self.currentStack = None
        self.framenr = 0
        
        self.debugServer.clientStack.connect(self.handleClientStack)
        
        self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode")
        self.sourceButton.setVisible(not self.__autoViewSource)
        
    def preferencesChanged(self):
        """
        Public slot to handle the preferencesChanged signal.
        """
        self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode")
        self.sourceButton.setVisible(not self.__autoViewSource)
        
    def setDebugger(self, debugUI):
        """
        Public method to set a reference to the Debug UI.
        
        @param debugUI reference to the DebugUI object (DebugUI)
        """
        self.debugUI = debugUI
        self.debugUI.clientStack.connect(self.handleClientStack)
        self.callStackViewer.setDebugger(debugUI)
        
    def handleResetUI(self):
        """
        Public method to reset the SBVviewer.
        """
        self.globalsViewer.handleResetUI()
        self.localsViewer.handleResetUI()
        self.__setGlobalsFilter()
        self.__setLocalsFilter()
        self.sourceButton.setEnabled(False)
        self.currentStack = None
        self.stackComboBox.clear()
        self.__threadList.clear()
        if self.embeddedShell:
            self.__tabWidget.setCurrentWidget(self.shellAssembly)
        else:
            if self.embeddedBrowser:
                self.__tabWidget.setCurrentWidget(self.browser)
            else:
                self.__tabWidget.setCurrentWidget(self.glvWidget)
        self.breakpointViewer.handleResetUI()
        
    def handleRawInput(self):
        """
        Public slot to handle the switch to the shell in raw input mode.
        """
        if self.embeddedShell:
            self.saveCurrentPage()
            self.__tabWidget.setCurrentWidget(self.shellAssembly)
        
    def initCallStackViewer(self, projectMode):
        """
        Public method to initialize the call stack viewer.
        
        @param projectMode flag indicating to enable the project mode (boolean)
        """
        self.callStackViewer.clear()
        self.callStackViewer.setProjectMode(projectMode)
        
    def isCallTraceEnabled(self):
        """
        Public method to get the state of the call trace function.
        
        @return flag indicating the state of the call trace function (boolean)
        """
        return self.callTraceViewer.isCallTraceEnabled()
        
    def clearCallTrace(self):
        """
        Public method to clear the recorded call trace.
        """
        self.callTraceViewer.clear()
        
    def setCallTraceToProjectMode(self, enabled):
        """
        Public slot to set the call trace viewer to project mode.
        
        In project mode the call trace info is shown with project relative
        path names.
        
        @param enabled flag indicating to enable the project mode (boolean)
        """
        self.callTraceViewer.setProjectMode(enabled)
        
    def showVariables(self, vlist, globals):
        """
        Public method to show the variables in the respective window.
        
        @param vlist list of variables to display
        @param globals flag indicating global/local state
        """
        if globals:
            self.globalsViewer.showVariables(vlist, self.framenr)
        else:
            self.localsViewer.showVariables(vlist, self.framenr)
            
    def showVariable(self, vlist, globals):
        """
        Public method to show the variables in the respective window.
        
        @param vlist list of variables to display
        @param globals flag indicating global/local state
        """
        if globals:
            self.globalsViewer.showVariable(vlist)
        else:
            self.localsViewer.showVariable(vlist)
            
    def showVariablesTab(self, globals):
        """
        Public method to make a variables tab visible.
        
        @param globals flag indicating global/local state
        """
        if globals:
            self.__tabWidget.setCurrentWidget(self.glvWidget)
        else:
            self.__tabWidget.setCurrentWidget(self.lvWidget)
        
    def saveCurrentPage(self):
        """
        Public slot to save the current page.
        """
        self.currPage = self.__tabWidget.currentWidget()
        
    def restoreCurrentPage(self):
        """
        Public slot to restore the previously saved page.
        """
        if self.currPage is not None:
            self.__tabWidget.setCurrentWidget(self.currPage)
            
    def handleClientStack(self, stack):
        """
        Public slot to show the call stack of the program being debugged.
        
        @param stack list of tuples with call stack data (file name,
            line number, function name, formatted argument/values list)
        """
        block = self.stackComboBox.blockSignals(True)
        self.framenr = 0
        self.stackComboBox.clear()
        self.currentStack = stack
        self.sourceButton.setEnabled(len(stack) > 0)
        for s in stack:
            # just show base filename to make it readable
            s = (os.path.basename(s[0]), s[1], s[2])
            self.stackComboBox.addItem('{0}:{1}:{2}'.format(*s))
        self.stackComboBox.blockSignals(block)
        
    def setVariablesFilter(self, globalsFilter, localsFilter):
        """
        Public slot to set the local variables filter.
        
        @param globalsFilter filter list for global variable types
            (list of int)
        @param localsFilter filter list for local variable types (list of int)
        """
        self.globalsFilter = globalsFilter
        self.localsFilter = localsFilter
        
    def __showSource(self):
        """
        Private slot to handle the source button press to show the selected
        file.
        """
        index = self.stackComboBox.currentIndex()
        if index > -1 and self.currentStack:
            s = self.currentStack[index]
            self.sourceFile.emit(s[0], int(s[1]))
        
    def __frameSelected(self, frmnr):
        """
        Private slot to handle the selection of a new stack frame number.
        
        @param frmnr frame number (0 is the current frame) (int)
        """
        self.framenr = frmnr
        self.debugServer.remoteClientVariables(0, self.localsFilter, frmnr)
        
        if self.__autoViewSource:
            self.__showSource()
        
    def __setGlobalsFilter(self):
        """
        Private slot to set the global variable filter.
        """
        filter = self.globalsFilterEdit.text()
        self.debugServer.remoteClientSetFilter(1, filter)
        self.debugServer.remoteClientVariables(2, self.globalsFilter)
        
    def __setLocalsFilter(self):
        """
        Private slot to set the local variable filter.
        """
        filter = self.localsFilterEdit.text()
        self.debugServer.remoteClientSetFilter(0, filter)
        if self.currentStack:
            self.debugServer.remoteClientVariables(
                0, self.localsFilter, self.framenr)
        
    def handleDebuggingStarted(self):
        """
        Public slot to handle the start of a debugging session.
        
        This slot sets the variables filter expressions.
        """
        self.__setGlobalsFilter()
        self.__setLocalsFilter()
        self.showVariablesTab(False)
        
    def currentWidget(self):
        """
        Public method to get a reference to the current widget.
        
        @return reference to the current widget (QWidget)
        """
        return self.__tabWidget.currentWidget()
        
    def setCurrentWidget(self, widget):
        """
        Public slot to set the current page based on the given widget.
        
        @param widget reference to the widget (QWidget)
        """
        self.__tabWidget.setCurrentWidget(widget)
        
    def showThreadList(self, currentID, threadList):
        """
        Public method to show the thread list.
        
        @param currentID id of the current thread (integer)
        @param threadList list of dictionaries containing the thread data
        """
        citm = None
        
        self.__threadList.clear()
        for thread in threadList:
            if thread['broken']:
                state = self.tr("waiting at breakpoint")
            else:
                state = self.tr("running")
            itm = QTreeWidgetItem(self.__threadList,
                                  ["{0:d}".format(thread['id']),
                                   thread['name'], state])
            if thread['id'] == currentID:
                citm = itm
        
        self.__threadList.header().resizeSections(QHeaderView.ResizeToContents)
        self.__threadList.header().setStretchLastSection(True)
        
        if citm:
            self.__doThreadListUpdate = False
            self.__threadList.setCurrentItem(citm)
            self.__doThreadListUpdate = True
        
    def __threadSelected(self, current, previous):
        """
        Private slot to handle the selection of a thread in the thread list.
        
        @param current reference to the new current item (QTreeWidgetItem)
        @param previous reference to the previous current item
            (QTreeWidgetItem)
        """
        if current is not None and self.__doThreadListUpdate:
            tid = int(current.text(0))
            self.debugServer.remoteSetThread(tid)
    
    def __callStackFrameSelected(self, frameNo):
        """
        Private slot to handle the selection of a call stack entry of the
        call stack viewer.
        
        @param frameNo frame number (index) of the selected entry (integer)
        """
        if frameNo >= 0:
            self.stackComboBox.setCurrentIndex(frameNo)
Esempio n. 40
0
class PackageEditor(QGridLayout):
    """ A Python package editor.  Note that this is a QLayout and not a
    QWidget.
    """

    # Emitted when the package has changed.
    package_changed = pyqtSignal()

    def __init__(self, show_root=False, scan="Scan", scan_whats_this='', whats_this=''):
        """ Initialise the editor. """

        super().__init__()

        self.package = None
        self.project = None

        self._show_root = show_root

        self._package_edit = QTreeWidget(whatsThis=whats_this)
        self._package_edit.header().hide()
        self._package_edit.itemChanged.connect(self._package_changed)
        self.addWidget(self._package_edit, 0, 0, 3, 1)

        self._scan_button = QPushButton(scan, whatsThis=scan_whats_this,
                clicked=self._scan, enabled=False)
        self.addWidget(self._scan_button, 0, 1)

        self._remove_button = QPushButton("Remove all",
                whatsThis="Remove all of the scanned directories and files.",
                clicked=self._remove_all, enabled=False)
        self.addWidget(self._remove_button, 0, 2)

        self._include_button = QPushButton("Include all",
                whatsThis="Select all of the scanned directories and files.",
                clicked=self._include_all, enabled=False)
        self.addWidget(self._include_button, 1, 1)

        self._exclude_button = QPushButton("Exclude all",
                whatsThis="Deselect all of the scanned directories and files.",
                clicked=self._exclude_all, enabled=False)
        self.addWidget(self._exclude_button, 1, 2)

        self._exclusions_edit = QTreeWidget(
                whatsThis="Any directory or file that matches any of the "
                        "these patterns will be automatically ignored when "
                        "scanning. Double-click on a pattern to edit or remove "
                        "it. Double-click below the last pattern in order to "
                        "add a new one.")
        self._exclusions_edit.setHeaderLabel("Exclusions")
        self._exclusions_edit.setEditTriggers(
                QTreeWidget.DoubleClicked|QTreeWidget.SelectedClicked|
                        QTreeWidget.EditKeyPressed)
        self._exclusions_edit.setRootIsDecorated(False)
        self._exclusions_edit.itemChanged.connect(self._exclusion_changed)

        self.addWidget(self._exclusions_edit, 2, 1, 1, 2)

    def configure(self, package, project):
        """ Configure the editor with the contents of the given package and
        project.
        """

        # Save the configuration.
        self.package = package
        self.project = project

        # Set the package itself.
        self._visualise()

        # Set the exclusions.
        self._exclusions_edit.clear()

        for exclude in package.exclusions:
            self._add_exclusion_item(exclude)

        # Add one to be edited to create a new entry.
        self._add_exclusion_item()

        self._scan_button.setEnabled(package is not None)

    def get_root_dir(self):
        """ Return the root directory to scan, or '' if there was an error or
        the user cancelled.
        """

        raise NotImplementedError

    def filter(self, name):
        """ See if a scanned name should be discarded. """

        # Include everything by default.
        return False

    def required(self, name):
        """ See if a scanned name is required. """

        # Nothing is required by default.
        return False

    def _add_exclusion_item(self, exclude=''):
        """ Add a QTreeWidgetItem that holds an exclusion. """

        itm = QTreeWidgetItem([exclude])

        itm.setFlags(
                Qt.ItemIsSelectable|Qt.ItemIsEditable|Qt.ItemIsEnabled|
                        Qt.ItemNeverHasChildren)

        self._exclusions_edit.addTopLevelItem(itm)

    def _exclusion_changed(self, itm, column):
        """ Invoked when an exclusion has changed. """

        exc_edit = self._exclusions_edit

        new_exc = itm.data(0, Qt.DisplayRole).strip()
        itm_index = exc_edit.indexOfTopLevelItem(itm)

        if new_exc != '':
            # See if we have added a new one.
            if itm_index == exc_edit.topLevelItemCount() - 1:
                self._add_exclusion_item()
        else:
            # It is empty so remove it.
            exc_edit.takeTopLevelItem(itm_index)

        # Save the new exclusions.
        self.package.exclusions = [
                exc_edit.topLevelItem(i).data(0, Qt.DisplayRole).strip()
                        for i in range(exc_edit.topLevelItemCount() - 1)]

        self.package_changed.emit()

    def _get_items(self):
        """ Return an iterator over the tree widget items. """

        it = QTreeWidgetItemIterator(self._package_edit)

        if self._show_root:
            it += 1

        itm = it.value()
        while itm is not None:
            yield itm
            it += 1
            itm = it.value()

    def _include_all(self, _):
        """ Invoked when the user clicks on the include all button. """

        for itm in self._get_items():
            itm.setCheckState(0, Qt.Checked)

    def _exclude_all(self, _):
        """ Invoked when the user clicks on the exclude all button. """

        for itm in self._get_items():
            if not itm.isDisabled():
                itm.setCheckState(0, Qt.Unchecked)
                itm.setExpanded(False)

    def _remove_all(self, _):
        """ Invoked when the use clicks on the remove all button. """

        blocked = self._package_edit.blockSignals(True)
        self._package_edit.clear()
        self._package_edit.blockSignals(blocked)

        self._enable_buttons()

        # This is a bit of a hack but is currently the only way to completely
        # remove the application package.
        if self._show_root:
            self.package.name = ''

        del self.package.contents[:]
        self.package_changed.emit()

    def _enable_buttons(self):
        """ Set the enabled state of those buttons that require content. """

        enable = (len(list(self._get_items())) != 0)

        self._remove_button.setEnabled(enable)
        self._include_button.setEnabled(enable)
        self._exclude_button.setEnabled(enable)

    def _scan(self, _):
        """ Invoked when the user clicks on the scan button. """

        project = self.project
        package = self.package

        # Get the root directory to scan.
        root = self.get_root_dir()
        if root == '':
            return

        # Save the included state of any existing contents so that they can be
        # restored after the scan.
        old_state = {}

        for itm in self._get_items():
            rel_path = [itm.data(0, Qt.DisplayRole)]

            parent = itm.parent()
            while parent is not None:
                rel_path.append(parent.data(0, Qt.DisplayRole))
                parent = parent.parent()

            rel_path.reverse()

            if self._show_root:
                rel_path = rel_path[1:]

            old_state['/'.join(rel_path)] = (itm.checkState(0) == Qt.Checked)

        # Walk the package.
        root_dir = QDir(root)
        if not root_dir.exists():
            QMessageBox.warning(self.parentWidget(), "Scan Directory",
                    "{0} is not a valid directory.".format(
                            QDir.toNativeSeparators(root)))
            return

        self._add_to_container(package, root_dir, [], old_state)
        self._visualise()

        self.package_changed.emit()

    def _add_to_container(self, container, content_dir, dir_stack, old_state):
        """ Add the files and directories of a package or sub-package to a
        container.
        """

        dir_contents = content_dir.entryInfoList(
                QDir.Files|QDir.Dirs|QDir.NoDotAndDotDot)

        # Make sure any filter is applied in a predictable order.
        dir_contents.sort(key=lambda fi: fi.fileName().lower()[1:] if fi.fileName().startswith('_') else fi.fileName().lower())

        dir_stack.append(content_dir.dirName())
        contents = []

        for content in dir_contents:
            name = content.fileName()

            # Apply any exclusions.
            for exc in self.package.exclusions:
                if fnmatch.fnmatch(name, exc):
                    name = None
                    break

            if name is None:
                continue

            # Apply any filter.
            if len(dir_stack) > 1:
                module_path = dir_stack[1:]
                module_path.append(name)
                path_name = '/'.join(module_path)
            else:
                path_name = name

            if self.filter(path_name):
                continue

            # See if we already know the included state.
            included = old_state.get(path_name, False)

            # Add the content.
            if content.isDir():
                qrc = QrcDirectory(name, included)

                self._add_to_container(qrc, QDir(content.canonicalFilePath()),
                        dir_stack, old_state)
            elif content.isFile():
                qrc = QrcFile(name, included)
            else:
                continue

            contents.append(qrc)

        container.contents = contents
        dir_stack.pop()

    def _visualise(self):
        """ Update the GUI with the package content. """

        blocked = self._package_edit.blockSignals(True)

        self._package_edit.clear()

        if self.package.name is not None:
            if self._show_root:
                parent = QTreeWidgetItem([':/' + self.package.name])
                self._package_edit.addTopLevelItem(parent)
                parent.setExpanded(True)
            else:
                parent = self._package_edit

            self._visualise_contents(self.package.contents, parent)

        self._package_edit.blockSignals(blocked)

        self._enable_buttons()

    def _visualise_contents(self, contents, parent):
        """ Visualise the contents for a parent. """

        p = parent
        while p is not None and isinstance(p, QTreeWidgetItem):
            p = p.parent()

        for content in contents:
            itm = QTreeWidgetItem(parent, [content.name])

            itm.setCheckState(0,
                    Qt.Checked if content.included else Qt.Unchecked)

            itm.setData(0, Qt.UserRole, content)

            if isinstance(content, QrcDirectory):
                self._visualise_contents(content.contents, itm)

    def _package_changed(self, itm, column):
        """ Invoked when part of the package changes. """

        if itm.checkState(0) == Qt.Checked:
            itm.data(0, Qt.UserRole).included = True
            itm.setExpanded(True)
        else:
            self._exclude(itm)
            itm.setExpanded(False)

        self.package_changed.emit()

    def _exclude(self, itm):
        """ Exclude an item and any children it may have. """

        for idx in range(itm.childCount()):
            self._exclude(itm.child(idx))

        itm.data(0, Qt.UserRole).included = False
        itm.setCheckState(0, Qt.Unchecked)
class OtherPackagesPage(QWidget):
    """ The GUI for the other packages page of a project. """

    # The page's label.
    label = "Other Packages"

    @property
    def project(self):
        """ The project property getter. """

        return self._project

    @project.setter
    def project(self, value):
        """ The project property setter. """

        if self._project != value:
            self._project = value
            self._package_delegate.set_project(value)
            self._update_page()

    def __init__(self):
        """ Initialise the page. """

        super().__init__()

        self._project = None

        # Create the page's GUI.
        layout = QHBoxLayout()

        self._package_selector = QTreeWidget(
                whatsThis="This shows a list of directories containing "
                        "additional Python packages which can be scanned when "
                        "selected. Double-click on a directory to edit or "
                        "remove it. Double-click below the last directory in "
                        "order to add a new one.")
        self._package_selector.setHeaderLabel("Packages Directory")
        self._package_selector.setEditTriggers(
                QTreeWidget.DoubleClicked|QTreeWidget.SelectedClicked|
                QTreeWidget.EditKeyPressed)
        self._package_selector.setRootIsDecorated(False)
        self._package_selector.currentItemChanged.connect(
                self._package_selector_changed)
        self._package_selector.itemChanged.connect(self._package_dir_changed)

        self._package_delegate = FilenameEditorDelegate("Packages Directory",
                directory=True)

        self._package_selector.setItemDelegateForColumn(0,
                self._package_delegate)

        layout.addWidget(self._package_selector)

        self._package_edit = _PackageDirectoryEditor()
        self._package_edit.package_changed.connect(self._package_changed)

        package_edit_gb = QGroupBox(self._package_edit.title)
        package_edit_gb.setLayout(self._package_edit)
        layout.addWidget(package_edit_gb)

        self.setLayout(layout)

    def _update_page(self):
        """ Update the page using the current project. """

        project = self.project

        self._package_selector.clear()

        for package in project.other_packages:
            self._add_package_dir(package)

        self._add_package_dir()

    def _add_package_dir(self, package=None):
        """ Add a QTreeWidgetItem that holds a package directory. """

        if package is None:
            package = QrcPackage()
            name = ''
        else:
            name = package.name

        itm = QTreeWidgetItem([name])
        itm.setData(0, Qt.UserRole, package)
        itm.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemNeverHasChildren|Qt.ItemIsEditable)

        self._package_selector.addTopLevelItem(itm)

    def _package_selector_changed(self, new, old):
        """ Invoked when the user selects a package directory. """

        if new is not None:
            self._package_edit.configure(new.data(0, Qt.UserRole),
                    self.project)

    def _package_dir_changed(self, itm, column):
        """ Invoked when the user edits a package directory name. """

        project = self.project

        selector = self._package_selector

        new_dir = itm.data(0, Qt.DisplayRole).strip()
        itm_index = selector.indexOfTopLevelItem(itm)

        if new_dir != '':
            itm.data(0, Qt.UserRole).name = project.path_to_user(new_dir)

            # See if we have added a new one.
            if itm_index == selector.topLevelItemCount() - 1:
                self._add_package_dir()
        else:
            # It is empty so remove it.
            selector.takeTopLevelItem(itm_index)

        # Save the new packages.
        project.other_packages = [
                selector.topLevelItem(i).data(0, Qt.UserRole)
                        for i in range(selector.topLevelItemCount() - 1)]

        self.project.modified = True

    def _package_changed(self):
        """ Invoked when the user edits a package contents. """

        self.project.modified = True
Esempio n. 42
0
class UIOpenPatientWindow(object):
    patient_info_initialized = QtCore.pyqtSignal(object)

    def setup_ui(self, open_patient_window_instance):
        if platform.system() == 'Darwin':
            self.stylesheet_path = "src/res/stylesheet.qss"
        else:
            self.stylesheet_path = "src/res/stylesheet-win-linux.qss"
        stylesheet = open(resource_path(self.stylesheet_path)).read()
        window_icon = QIcon()
        window_icon.addPixmap(
            QPixmap(resource_path("src/res/images/icon.ico")), QIcon.Normal,
            QIcon.Off)
        open_patient_window_instance.setObjectName("OpenPatientWindowInstance")
        open_patient_window_instance.setWindowIcon(window_icon)
        open_patient_window_instance.resize(840, 530)

        # Create a vertical box for containing the other elements and layouts
        self.open_patient_window_instance_vertical_box = QVBoxLayout()
        self.open_patient_window_instance_vertical_box.setObjectName(
            "OpenPatientWindowInstanceVerticalBox")

        # Create a label to prompt the user to enter the path to the directory that contains the DICOM files
        self.open_patient_directory_prompt = QLabel()
        self.open_patient_directory_prompt.setObjectName(
            "OpenPatientDirectoryPrompt")
        self.open_patient_directory_prompt.setAlignment(Qt.AlignLeft)
        self.open_patient_window_instance_vertical_box.addWidget(
            self.open_patient_directory_prompt)

        # Create a horizontal box to hold the input box for the directory and the choose button
        self.open_patient_directory_input_horizontal_box = QHBoxLayout()
        self.open_patient_directory_input_horizontal_box.setObjectName(
            "OpenPatientDirectoryInputHorizontalBox")
        # Create a textbox to contain the path to the directory that contains the DICOM files
        self.open_patient_directory_input_box = UIOpenPatientWindowDragAndDropEvent(
            self)

        self.open_patient_directory_input_box.setObjectName(
            "OpenPatientDirectoryInputBox")
        self.open_patient_directory_input_box.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding,
                        QSizePolicy.MinimumExpanding))
        self.open_patient_directory_input_box.returnPressed.connect(
            self.scan_directory_for_patient)
        self.open_patient_directory_input_horizontal_box.addWidget(
            self.open_patient_directory_input_box)

        # Create a choose button to open the file dialog
        self.open_patient_directory_choose_button = QPushButton()
        self.open_patient_directory_choose_button.setObjectName(
            "OpenPatientDirectoryChooseButton")
        self.open_patient_directory_choose_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding,
                        QSizePolicy.MinimumExpanding))
        self.open_patient_directory_choose_button.resize(
            self.open_patient_directory_choose_button.sizeHint().width(),
            self.open_patient_directory_input_box.height())
        self.open_patient_directory_choose_button.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.open_patient_directory_input_horizontal_box.addWidget(
            self.open_patient_directory_choose_button)
        self.open_patient_directory_choose_button.clicked.connect(
            self.choose_button_clicked)
        # Create a widget to hold the input fields
        self.open_patient_directory_input_widget = QWidget()
        self.open_patient_directory_input_horizontal_box.setStretch(0, 4)
        self.open_patient_directory_input_widget.setLayout(
            self.open_patient_directory_input_horizontal_box)
        self.open_patient_window_instance_vertical_box.addWidget(
            self.open_patient_directory_input_widget)

        # Create a horizontal box to hold the stop button and direction to the user on where to select the patient
        self.open_patient_appear_prompt_and_stop_horizontal_box = QHBoxLayout()
        self.open_patient_appear_prompt_and_stop_horizontal_box.setObjectName(
            "OpenPatientAppearPromptAndStopHorizontalBox")
        # Create a label to show direction on where the files will appear
        self.open_patient_directory_appear_prompt = QLabel()
        self.open_patient_directory_appear_prompt.setObjectName(
            "OpenPatientDirectoryAppearPrompt")
        self.open_patient_directory_appear_prompt.setAlignment(Qt.AlignLeft)
        self.open_patient_appear_prompt_and_stop_horizontal_box.addWidget(
            self.open_patient_directory_appear_prompt)
        self.open_patient_appear_prompt_and_stop_horizontal_box.addStretch(1)
        # Create a button to stop searching
        self.open_patient_window_stop_button = QPushButton()
        self.open_patient_window_stop_button.setObjectName(
            "OpenPatientWindowStopButton")
        self.open_patient_window_stop_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding,
                        QSizePolicy.MinimumExpanding))
        self.open_patient_window_stop_button.resize(
            self.open_patient_window_stop_button.sizeHint().width(),
            self.open_patient_window_stop_button.sizeHint().height())
        self.open_patient_window_stop_button.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.open_patient_window_stop_button.clicked.connect(
            self.stop_button_clicked)
        self.open_patient_window_stop_button.setProperty(
            "QPushButtonClass", "fail-button")
        self.open_patient_window_stop_button.setVisible(
            False)  # Button doesn't show until a search commences
        self.open_patient_appear_prompt_and_stop_horizontal_box.addWidget(
            self.open_patient_window_stop_button)
        # Create a widget to hold the layout
        self.open_patient_appear_prompt_and_stop_widget = QWidget()
        self.open_patient_appear_prompt_and_stop_widget.setLayout(
            self.open_patient_appear_prompt_and_stop_horizontal_box)
        self.open_patient_window_instance_vertical_box.addWidget(
            self.open_patient_appear_prompt_and_stop_widget)

        # Create a tree view list to list out all patients in the directory selected above
        self.open_patient_window_patients_tree = QTreeWidget()
        self.open_patient_window_patients_tree.setObjectName(
            "OpenPatientWindowPatientsTree")
        self.open_patient_window_patients_tree.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding,
                        QSizePolicy.MinimumExpanding))
        self.open_patient_window_patients_tree.resize(
            self.open_patient_window_patients_tree.sizeHint().width(),
            self.open_patient_window_patients_tree.sizeHint().height())
        self.open_patient_window_patients_tree.setHeaderHidden(True)
        self.open_patient_window_patients_tree.setHeaderLabels([""])
        self.open_patient_window_instance_vertical_box.addWidget(
            self.open_patient_window_patients_tree)

        # Create a label to show what would happen if they select the patient
        self.open_patient_directory_result_label = QtWidgets.QLabel()
        self.open_patient_directory_result_label.setObjectName(
            "OpenPatientDirectoryResultLabel")
        self.open_patient_directory_result_label.setAlignment(Qt.AlignLeft)
        self.open_patient_window_instance_vertical_box.addWidget(
            self.open_patient_directory_result_label)

        # Create a horizontal box to hold the Cancel and Open button
        self.open_patient_window_patient_open_actions_horizontal_box = QHBoxLayout(
        )
        self.open_patient_window_patient_open_actions_horizontal_box.setObjectName(
            "OpenPatientWindowPatientOpenActionsHorizontalBox")
        self.open_patient_window_patient_open_actions_horizontal_box.addStretch(
            1)
        # Add a button to go back/exit from the application
        self.open_patient_window_exit_button = QPushButton()
        self.open_patient_window_exit_button.setObjectName(
            "OpenPatientWindowExitButton")
        self.open_patient_window_exit_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding,
                        QSizePolicy.MinimumExpanding))
        self.open_patient_window_exit_button.resize(
            self.open_patient_window_stop_button.sizeHint().width(),
            self.open_patient_window_stop_button.sizeHint().height())
        self.open_patient_window_exit_button.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.open_patient_window_exit_button.clicked.connect(
            self.exit_button_clicked)
        self.open_patient_window_exit_button.setProperty(
            "QPushButtonClass", "fail-button")
        self.open_patient_window_patient_open_actions_horizontal_box.addWidget(
            self.open_patient_window_exit_button)

        # Add a button to confirm opening of the patient
        self.open_patient_window_confirm_button = QPushButton()
        self.open_patient_window_confirm_button.setObjectName(
            "OpenPatientWindowConfirmButton")
        self.open_patient_window_confirm_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding,
                        QSizePolicy.MinimumExpanding))
        self.open_patient_window_confirm_button.resize(
            self.open_patient_window_confirm_button.sizeHint().width(),
            self.open_patient_window_confirm_button.sizeHint().height())
        self.open_patient_window_confirm_button.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.open_patient_window_confirm_button.clicked.connect(
            self.confirm_button_clicked)
        self.open_patient_window_confirm_button.setProperty(
            "QPushButtonClass", "success-button")
        self.open_patient_window_patient_open_actions_horizontal_box.addWidget(
            self.open_patient_window_confirm_button)

        # Create a widget to house all of the actions button for open patient window
        self.open_patient_window_patient_open_actions_widget = QWidget()
        self.open_patient_window_patient_open_actions_widget.setLayout(
            self.open_patient_window_patient_open_actions_horizontal_box)
        self.open_patient_window_instance_vertical_box.addWidget(
            self.open_patient_window_patient_open_actions_widget)

        # Set the vertical box fourth element, the tree view, to stretch out as far as possible
        self.open_patient_window_instance_vertical_box.setStretch(
            3, 4)  # Stretch the treeview out as far as possible
        self.open_patient_window_instance_central_widget = QWidget()
        self.open_patient_window_instance_central_widget.setObjectName(
            "OpenPatientWindowInstanceCentralWidget")
        self.open_patient_window_instance_central_widget.setLayout(
            self.open_patient_window_instance_vertical_box)

        # Create threadpool for multithreading
        self.threadpool = QThreadPool()
        print("Multithreading with maximum %d threads" %
              self.threadpool.maxThreadCount())
        # Create interrupt event for stopping the directory search
        self.interrupt_flag = threading.Event()

        # Bind all texts into the buttons and labels
        self.retranslate_ui(open_patient_window_instance)
        # Set the central widget, ready for display
        open_patient_window_instance.setCentralWidget(
            self.open_patient_window_instance_central_widget)

        # Set the current stylesheet to the instance and connect it back to the caller through slot
        open_patient_window_instance.setStyleSheet(stylesheet)
        QtCore.QMetaObject.connectSlotsByName(open_patient_window_instance)

    def retranslate_ui(self, open_patient_window_instance):
        _translate = QtCore.QCoreApplication.translate
        open_patient_window_instance.setWindowTitle(
            _translate("OpenPatientWindowInstance",
                       "OnkoDICOM - Select Patient"))
        self.open_patient_directory_prompt.setText(
            _translate(
                "OpenPatientWindowInstance",
                "Choose the path of the folder containing DICOM files to load Patient's details:"
            ))
        self.open_patient_directory_input_box.setPlaceholderText(
            _translate(
                "OpenPatientWindowInstance",
                "Enter DICOM Files Path (For example, C:\path\\to\your\DICOM\Files)"
            ))
        self.open_patient_directory_choose_button.setText(
            _translate("OpenPatientWindowInstance", "Choose"))
        self.open_patient_directory_appear_prompt.setText(
            _translate(
                "OpenPatientWindowInstance",
                "Patient File directory shown below once file path chosen. Please select the file(s) you want to open:"
            ))
        self.open_patient_directory_result_label.setText(
            "The selected directory(s) above will be opened in the OnkoDICOM program."
        )
        self.open_patient_window_stop_button.setText(
            _translate("OpenPatientWindowInstance", "Stop Search"))
        self.open_patient_window_exit_button.setText(
            _translate("OpenPatientWindowInstance", "Exit"))
        self.open_patient_window_confirm_button.setText(
            _translate("OpenPatientWindowInstance", "Confirm"))

    def exit_button_clicked(self):
        QCoreApplication.exit(0)

    def scan_directory_for_patient(self):
        self.filepath = self.open_patient_directory_input_box.text()
        # Proceed if a folder was selected
        if self.filepath != "":
            # Update the QTreeWidget to reflect data being loaded
            # First, clear the widget of any existing data
            self.open_patient_window_patients_tree.clear()

            # Next, update the tree widget
            self.open_patient_window_patients_tree.addTopLevelItem(
                QTreeWidgetItem(["Loading selected directory..."]))

            # The choose button is disabled until the thread finishes executing
            self.open_patient_directory_choose_button.setEnabled(False)

            # Reveals the Stop Search button for the duration of the search
            self.open_patient_window_stop_button.setVisible(True)

            # The interrupt flag is then un-set if a previous search has been stopped.
            self.interrupt_flag.clear()

            # Then, create a new thread that will load the selected folder
            worker = Worker(DICOMDirectorySearch.get_dicom_structure,
                            self.filepath,
                            self.interrupt_flag,
                            progress_callback=True)
            worker.signals.result.connect(self.on_search_complete)
            worker.signals.progress.connect(self.search_progress)

            # Execute the thread
            self.threadpool.start(worker)

    def choose_button_clicked(self):
        """
        Executes when the choose button is clicked.
        Gets filepath from the user and loads all files and subdirectories.
        """
        # Get folder path from pop up dialog box
        self.filepath = QtWidgets.QFileDialog.getExistingDirectory(
            None, 'Select patient folder...', '')
        self.open_patient_directory_input_box.setText(self.filepath)
        self.scan_directory_for_patient()

    def stop_button_clicked(self):
        self.interrupt_flag.set()

    def search_progress(self, progress_update):
        """
        Current progress of the file search.
        """
        self.open_patient_window_patients_tree.clear()
        self.open_patient_window_patients_tree.addTopLevelItem(
            QTreeWidgetItem([
                "Loading selected directory... (%s files searched)" %
                progress_update
            ]))

    def on_search_complete(self, dicom_structure):
        """
        Executes once the directory search is complete.
        :param dicom_structure: DICOMStructure object constructed by the directory search.
        """
        self.open_patient_directory_choose_button.setEnabled(True)
        self.open_patient_window_stop_button.setVisible(False)
        self.open_patient_window_patients_tree.clear()

        if dicom_structure is None:  # dicom_structure will be None if function was interrupted.
            return

        for patient_item in dicom_structure.get_tree_items_list():
            self.open_patient_window_patients_tree.addTopLevelItem(
                patient_item)

        if len(dicom_structure.patients) == 0:
            QMessageBox.about(self, "No files found",
                              "Selected directory contains no DICOM files.")

    def confirm_button_clicked(self):
        """
        Begins loading of the selected files.
        """
        selected_files = []
        for item in self.get_checked_leaves():
            selected_files += item.dicom_object.get_files()

        if len(selected_files) > 0:
            self.progress_window = ProgressWindow(
                self,
                QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
            self.progress_window.signal_loaded.connect(self.on_loaded)
            self.progress_window.signal_error.connect(self.on_loading_error)

            self.progress_window.start_loading(selected_files)
            self.progress_window.exec_()
        else:
            QMessageBox.about(self, "Unable to open selection",
                              "No files selected.")

    def on_loaded(self, results):
        """
        Executes when the progress bar finishes loaded the selected files.
        """
        if results[0] is True:  # Will be NoneType if loading was interrupted.
            self.patient_info_initialized.emit(
                results[1])  # Emits the progress window.

    def on_loading_error(self, error_code):
        """
        Error handling for progress window.
        """
        if error_code == 0:
            QMessageBox.about(
                self.progress_window, "Unable to open selection",
                "Selected files cannot be opened as they are not a DICOM-RT set."
            )
            self.progress_window.close()
        elif error_code == 1:
            QMessageBox.about(
                self.progress_window, "Unable to open selection",
                "Selected files cannot be opened as they contain unsupported DICOM classes."
            )
            self.progress_window.close()

    def get_checked_leaves(self):
        """
        :return: A list of all QTreeWidgetItems in the QTreeWidget that are both leaves and checked.
        """
        checked_items = []

        def recurse(parent_item: QTreeWidgetItem):
            for i in range(parent_item.childCount()):
                child = parent_item.child(i)
                grand_children = child.childCount()
                if grand_children > 0:
                    recurse(child)
                else:
                    if child.checkState(0) == Qt.Checked:
                        checked_items.append(child)

        recurse(self.open_patient_window_patients_tree.invisibleRootItem())
        return checked_items
class BookmarksWindow(QDialog):
    """
    A simple UI for showing bookmarks and navigating to them.

    FIXME: For now, this window is tied to a particular lane.
           If your project has more than one lane, then each one
           will have it's own bookmark window, which is kinda dumb.
    """
    def __init__(self, parent, topLevelOperatorView):
        super(BookmarksWindow, self).__init__(parent)
        self.setWindowTitle("Bookmarks")
        self.topLevelOperatorView = topLevelOperatorView
        self.bookmark_tree = QTreeWidget(self)
        self.bookmark_tree.setHeaderLabels( ["Location", "Notes"] )
        self.bookmark_tree.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Preferred )
        self.bookmark_tree.setColumnWidth(0, 200)
        self.bookmark_tree.setColumnWidth(1, 300)

        self.note_edit = QLineEdit(self)
        self.add_bookmark_button = QPushButton("Add Bookmark", self, clicked=self.add_bookmark)

        geometry = self.geometry()
        geometry.setSize( QSize(520, 520) )
        self.setGeometry(geometry)
        
        layout = QVBoxLayout()
        layout.addWidget(self.bookmark_tree)
        layout.addWidget(self.note_edit)
        layout.addWidget(self.add_bookmark_button)
        self.setLayout(layout)

        self._load_bookmarks()
        
        self.bookmark_tree.setContextMenuPolicy( Qt.CustomContextMenu )
        self.bookmark_tree.customContextMenuRequested.connect( self.showContextMenu )
        
        self.bookmark_tree.itemDoubleClicked.connect(self._handle_doubleclick)

    def _handle_doubleclick(self, item, col):
        """
        Navigate to the bookmark
        """
        data = item.data(0, Qt.UserRole).toPyObject()
        if data is None:
            return

        (coord, notes) = data
        axes = self.topLevelOperatorView.InputImages.meta.getAxisKeys()
        axes = axes[:-1] # drop channel
        axes = sorted(axes)
        assert len(axes) == len(coord)
        tagged_coord = dict(list(zip(axes, coord)))
        tagged_location = OrderedDict(list(zip('txyzc', (0,0,0,0,0))))
        tagged_location.update(tagged_coord)
        t = list(tagged_location.values())[0]
        coord3d = list(tagged_location.values())[1:4]
        
        self.parent().editor.posModel.time = t
        self.parent().editor.navCtrl.panSlicingViews( coord3d, [0,1,2] )
        self.parent().editor.posModel.slicingPos = coord3d

    def showContextMenu(self, pos):
        item = self.bookmark_tree.itemAt(pos)
        data = item.data(0, Qt.UserRole).toPyObject()
        if data is None:
            return
        
        def delete_bookmark():
            (coord, notes) = data
            bookmarks = list(self.topLevelOperatorView.Bookmarks.value)
            i = bookmarks.index((coord, notes))
            bookmarks.pop(i)
            self.topLevelOperatorView.Bookmarks.setValue(bookmarks)
            self._load_bookmarks()

        menu = QMenu(parent=self)
        menu.addAction( QAction("Delete", menu, triggered=delete_bookmark) )
        globalPos = self.bookmark_tree.viewport().mapToGlobal( pos )
        menu.exec_( globalPos )
        #selection = menu.exec_( globalPos )
        #if selection is removeLanesAction:
        #    self.removeLanesRequested.emit( self._selectedLanes )

    def add_bookmark(self):
        coord_txyzc = self.parent().editor.posModel.slicingPos5D
        tagged_coord_txyzc = dict( list(zip('txyzc', coord_txyzc)) )
        axes = self.topLevelOperatorView.InputImages.meta.getAxisKeys()
        axes = axes[:-1] # drop channel
        axes = sorted(axes)
        coord = tuple(tagged_coord_txyzc[c] for c in axes)

        notes = str(self.note_edit.text())
        bookmarks = list(self.topLevelOperatorView.Bookmarks.value)
        bookmarks.append((coord, notes))
        self.topLevelOperatorView.Bookmarks.setValue(bookmarks)
        
        self._load_bookmarks()
    
    def _load_bookmarks(self):
        self.bookmark_tree.clear()
        lane_index = self.topLevelOperatorView.current_view_index()
        lane_nickname = self.topLevelOperatorView.InputImages.meta.nickname or "Lane {}".format(lane_index)
        bookmarks = self.topLevelOperatorView.Bookmarks.value
        group_item = QTreeWidgetItem( self.bookmark_tree, [lane_nickname] )

        for coord, notes in bookmarks:
            item = QTreeWidgetItem( group_item, [] )
            item.setText(0, str(coord))
            item.setData(0, Qt.UserRole, (coord, notes))
            item.setText(1, notes)

        self.bookmark_tree.expandAll()
Esempio n. 44
0
File: main.py Progetto: eqs/koala
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        # Initialize main-widgets

        # 画像をスライドするボタンの設定
        self.prevButton = QPushButton('&Prev')
        self.prevButton.clicked.connect(self.showPrevImage)
        self.prevButton.setShortcut(QtCore.Qt.Key_Left)

        self.nextButton = QPushButton('&Next')
        self.nextButton.clicked.connect(self.showNextImage)
        self.nextButton.setShortcut(QtCore.Qt.Key_Right)

        buttonLayout = QGridLayout()
        buttonLayout.addWidget(self.prevButton, 0, 0)
        buttonLayout.addWidget(self.nextButton, 0, 1)

        # 画像を表示するラベルの設定
        self.pictureLabel = QLabel()
        self.pictureLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.putPixmap('startimage.png')

        self.indexLabel = QLabel('')
        self.indexLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.pathLabel = QLabel('')
        self.pathLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.classLabel = QLabel('')
        self.classLabel.setAlignment(QtCore.Qt.AlignCenter)
        classLabelFont = QFont()
        classLabelFont.setPointSize(32)
        self.classLabel.setFont(classLabelFont)

        pictureLayout = QGridLayout()
        pictureLayout.addWidget(self.indexLabel, 0, 0)
        pictureLayout.addWidget(self.pictureLabel, 1, 0)
        pictureLayout.addWidget(self.pathLabel, 2, 0)
        pictureLayout.addWidget(self.classLabel, 3, 0)

        # アノテーションをするためのボタンの設定
        annotationButtonLayout = QGridLayout()
        for k, conf in enumerate(BUTTON_CONFIG):
            button = QPushButton('&{0} : {1}'.format(conf['class'], conf['key']))
            button.clicked.connect(self.anotateClass)
            button.setShortcut(conf['key'])
            annotationButtonLayout.addWidget(button, 0, k)

        # 画像のパス一覧を表示するためのリスト
        self.treeWidget = QTreeWidget()
        self.treeWidget.setColumnCount(2)
        self.treeWidget.setHeaderLabels(['#', 'filepath', 'class', 'corrected'])
        self.treeWidget.itemSelectionChanged.connect(self.showImageFromTree)

        mainLayout = QVBoxLayout()
        mainLayout.addLayout(pictureLayout)
        mainLayout.addLayout(annotationButtonLayout)
        mainLayout.addLayout(buttonLayout)

        topLayout = QHBoxLayout()
        topLayout.addWidget(self.treeWidget)
        topLayout.addLayout(mainLayout)

        topWidget = QWidget()
        topWidget.setLayout(topLayout)
        self.setCentralWidget(topWidget)
        self.setWindowTitle('koala')

        # Initialize menu bar and status bar
        openAction = QAction('&Open', self)
        openAction.setShortcut('Ctrl+O')
        openAction.setStatusTip('Open annotation file')
        openAction.triggered.connect(self.openAnnotationFile)

        saveAction = QAction('&Save', self)
        saveAction.setShortcut('Ctrl+S')
        saveAction.setStatusTip('Save annotation file')
        saveAction.triggered.connect(self.saveAnnotationFile)

        newSaveAction = QAction('&Save As', self)
        newSaveAction.setShortcut('Ctrl+Shift+S')
        newSaveAction.setStatusTip('Save annotation as new file')
        newSaveAction.triggered.connect(self.newSaveAnnotationFile)

        addImageAction = QAction('&Add Image', self)
        addImageAction.setShortcut('Ctrl+I')
        addImageAction.setStatusTip('Add image files')
        addImageAction.triggered.connect(self.addImageFile)

        self.statusBar()

        menuBar = self.menuBar()
        fileMenu = menuBar.addMenu('&File')
        fileMenu.addAction(openAction)
        fileMenu.addAction(saveAction)
        fileMenu.addAction(newSaveAction)
        fileMenu.addAction(addImageAction)

        # Initialize image annotation information list
        self.imageDataList = []
        self.imageIndex = 0

        # 開いているファイルのパス
        self.openingFilePath = ''

    def putPixmap(self, filepath):
        # Pixmapをパスから読み込んでラベルにセットする
        pixmap = QPixmap(filepath)
        self.pictureLabel.setPixmap(pixmap.scaled(256, 256, QtCore.Qt.KeepAspectRatio))
        self.pictureLabel.show()

    def anotateClass(self):
        # 押されたボタンの情報から,ラベリングを行う
        if len(self.imageDataList) > 0:
            text = self.sender().text()
            dataClass = text[1:text.index(':')-1].strip()
            # ラベルの変更があったときだけデータの変更を行う
            if self.imageDataList[self.imageIndex]['class'] != dataClass:
                self.imageDataList[self.imageIndex]['class'] = dataClass
                self.imageDataList[self.imageIndex]['corrected'] = not self.imageDataList[self.imageIndex]['corrected']
            self.updateDataInformation()

            # Update Image List
            root = self.treeWidget.invisibleRootItem()
            item = root.child(self.imageIndex)
            item.setText(2, self.imageDataList[self.imageIndex]['class'])
            item.setBackground(2, QColor(CLASS_TO_COLOR[self.imageDataList[self.imageIndex]['class']]))
            item.setText(3, str(self.imageDataList[self.imageIndex]['corrected']))


    def updateDataInformation(self):
        # 現在選択しているデータのパスとクラスをラベルに表示する
        if len(self.imageDataList) > 0:
            self.indexLabel.setText('{0} / {1}'.format(self.imageIndex+1, len(self.imageDataList)))
            self.pathLabel.setText(self.imageDataList[self.imageIndex]['filepath'])
            self.classLabel.setText(self.imageDataList[self.imageIndex]['class'])
            self.classLabel.setStyleSheet('color : {0}'.format(CLASS_TO_COLOR[self.imageDataList[self.imageIndex]['class']]))


    def showPrevImage(self):
        if len(self.imageDataList) > 0:
            self.imageIndex = (self.imageIndex - 1) % len(self.imageDataList)
            self.putPixmap(self.imageDataList[self.imageIndex]['filepath'])
            self.updateDataInformation()

    def showNextImage(self):
        if len(self.imageDataList) > 0:
            self.imageIndex = (self.imageIndex + 1) % len(self.imageDataList)
            self.putPixmap(self.imageDataList[self.imageIndex]['filepath'])
            self.updateDataInformation()

    def showIntendedImage(self, idx):
        """ 番号で指定した画像を表示する """
        if len(self.imageDataList) > 0 and 0 <= idx <= len(self.imageDataList):
            self.imageIndex = idx
            self.putPixmap(self.imageDataList[self.imageIndex]['filepath'])
            self.updateDataInformation()


    def showImageFromTree(self):
        """ TreeWidget上でアイテムが指定されたらその番号の画像にジャンプする """
        indices = self.treeWidget.selectedIndexes()
        if indices:
            selectedIndex = indices[0].row()
            self.showIntendedImage(selectedIndex)

    def openAnnotationFile(self):
        # アノテーションの情報を読み込む
        openFilePath = QFileDialog.getOpenFileName(parent=self, filter='*.json')[0]
        # キャンセルされなければ読み込み
        if len(openFilePath) > 0:
            with open(openFilePath, 'r') as f:
                self.imageDataList = json.load(f)
                self.imageIndex = 0
                self.putPixmap(self.imageDataList[self.imageIndex]['filepath'])
                self.updateDataInformation()
                self.updateImageList()

                self.openingFilePath = openFilePath
                self.setWindowTitle('koala - {0}'.format(self.openingFilePath))


    def saveAnnotationFile(self):
        # 新規のファイルなら保存のダイアログを出す
        if self.openingFilePath == '':
            # アノテーションの情報を保存する
            saveFilePath = QFileDialog.getSaveFileName(parent=self, filter='*.json')[0]
            # キャンセルされなければ保存
            if len(saveFilePath) > 0:
                with open(saveFilePath, 'w') as f:
                    json.dump(self.imageDataList, f, ensure_ascii=False, indent=4)
                    self.openingFilePath = saveFilePath
                    self.setWindowTitle('koala - {0}'.format(self.openingFilePath))
                    self.statusBar().showMessage('New annotation file is saved as "{0}"'.format(self.openingFilePath))
        else:
            with open(self.openingFilePath, 'w') as f:
                json.dump(self.imageDataList, f, ensure_ascii=False, indent=4)
                self.statusBar().showMessage('Annotation file is saved.'.format(self.openingFilePath))

    def newSaveAnnotationFile(self): # 名前をつけて保存
        saveFilePath = QFileDialog.getSaveFileName(parent=self, filter='*.json')[0]
        # キャンセルされなければ保存
        if len(saveFilePath) > 0:
            with open(saveFilePath, 'w') as f:
                json.dump(self.imageDataList, f, ensure_ascii=False, indent=4)
                self.openingFilePath = saveFilePath
                self.setWindowTitle('koala - {0}'.format(self.openingFilePath))
                self.statusBar().showMessage('New annotation file is saved as "{0}"'.format(self.openingFilePath))

    def addImageFile(self):
        # Open file dialog for adding images
        selectedImagePathList = QFileDialog.getOpenFileNames(parent=self, filter='*.png *.jpg *.jpeg *.bmp')[0]
        # Image path list
        imagePathList = [imageData['filepath'] for imageData in self.imageDataList]
        # Add selected images to imagePathList
        for imagePath in selectedImagePathList:
            # まだ追加されていない画像ならリストに追加する
            if not (imagePath in imagePathList):
                self.imageDataList.append({'#' : len(self.imageDataList),
                                           'filepath' : imagePath,
                                           'class' : None,
                                           'corrected' : False})

        # 画像があるなら開く
        if len(self.imageDataList) > 0:
            self.imageIndex = len(self.imageDataList) - 1
            self.putPixmap(self.imageDataList[self.imageIndex]['filepath'])
            self.updateDataInformation()
            self.updateImageList()

    def updateImageList(self):
        self.treeWidget.clear()
        for idx, imageData in enumerate(self.imageDataList):
            treeWidgetItem = QTreeWidgetItem(['{0:7d}'.format(idx + 1),
                                              imageData['filepath'],
                                              imageData['class'],
                                              str(imageData['corrected'])])
            treeWidgetItem.setBackground(2, QColor(CLASS_TO_COLOR[imageData['class']]))
            self.treeWidget.addTopLevelItem(treeWidgetItem)