Exemplo n.º 1
0
    def init_menu_bar(self):
        menu_bar = self.menuBar()
        file_menu = menu_bar.addMenu(_('File'))
        options_menu = menu_bar.addMenu(_('Options'))
        manga_menu = QMenu(_('Manga DB'), self)
        file_menu.addMenu(manga_menu)

        save_act = QAction(_('Save '), self)
        save_act.triggered.connect(self.save_sites)
        manga_menu.addAction(save_act)

        imp_act = QAction(_('Import'), self)
        imp_act.triggered.connect(self.import_db)
        manga_menu.addAction(imp_act)

        relocate_act = QAction(_('Relocate'), self)
        relocate_act.triggered.connect(self.relocate_db)
        manga_menu.addAction(relocate_act)

        aot_menu = QAction(_('Stay on top'), self)
        aot_menu.setCheckable(True)
        aot_menu.triggered.connect(
            lambda: self.change_sot(aot_menu.isChecked()))
        options_menu.addAction(aot_menu)
        aot_menu.setChecked(self.config.sot)

        dark_mode = QAction(_('Dark mode'), self)
        dark_mode.setCheckable(True)
        dark_mode.triggered.connect(
            lambda: self.redraw_palette(dark_mode.isChecked()))
        options_menu.addAction(dark_mode)
        dark_mode.setChecked(self.config.dark_mode)
Exemplo n.º 2
0
class ObjectViewSelectionToggle(object):
    def __init__(self, name, menuparent):
        self.name = name
        self.menuparent = menuparent

        self.action_view_toggle = QAction("{0} visible".format(name),
                                          menuparent)
        self.action_select_toggle = QAction("{0} selectable".format(name),
                                            menuparent)
        self.action_view_toggle.setCheckable(True)
        self.action_view_toggle.setChecked(True)
        self.action_select_toggle.setCheckable(True)
        self.action_select_toggle.setChecked(True)

        self.action_view_toggle.triggered.connect(self.handle_view_toggle)
        self.action_select_toggle.triggered.connect(self.handle_select_toggle)

        menuparent.addAction(self.action_view_toggle)
        menuparent.addAction(self.action_select_toggle)

    def handle_view_toggle(self, val):
        if not val:
            self.action_select_toggle.setChecked(False)
        else:
            self.action_select_toggle.setChecked(True)

    def handle_select_toggle(self, val):
        if val:
            self.action_view_toggle.setChecked(True)

    def is_visible(self):
        return self.action_view_toggle.isChecked()

    def is_selectable(self):
        return self.action_select_toggle.isChecked()
Exemplo n.º 3
0
class Example(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        grid = QGridLayout()
        self.setLayout(grid)

        self.txtBox = QLineEdit()
        self.menu = QMenu()
        self.btn = QPushButton("Dropdown")
        self.cmbox = CheckableComboBox()
        self.btn2 = QPushButton("Test checked")

        self.Act1 = QAction("Action 1", self.menu)
        self.Act1.setCheckable(True)
        self.Act2 = QAction("Action 2", self.menu)
        self.Act2.setCheckable(True)

        self.menu.addAction(self.Act1)
        self.menu.addAction(self.Act2)
        self.btn.setMenu(self.menu)

        grid.addWidget(self.txtBox, 0, 0, 1, 3)
        grid.addWidget(self.btn, 1, 0)
        grid.addWidget(self.cmbox, 1, 1)
        grid.addWidget(self.btn2, 1, 2)

        for i in range(3, 5):
            self.cmbox.addItem("Action " + str(i))

        self.btn2.clicked.connect(self.test_checked)
        self.cmbox.view().pressed.connect(self.handle_item_pressed)  # WIP

        self.move(300, 150)
        self.setMinimumWidth(800)
        self.setWindowTitle('checkComboBox')
        self.show()

    def test_checked(self, attr1):
        print(self.Act1.isChecked())
        print(self.Act2.isChecked())
        print(self.cmbox.isChecked(0))
        print(self.cmbox.isChecked(1))

        self.txtBox.setText("Action 1 is: " + str(self.Act1.isChecked()) +
                            " and Action 2 is: " + str(self.Act2.isChecked()) +
                            " and Action 3 is: " + str(self.cmbox.isChecked(0)) +
                            " and Action 4 is: " + str(self.cmbox.isChecked(1)))

    # TODO - Fix for chkbox and not only text.
    def handle_item_pressed(self, index):
        item = self.cmbox.model().itemFromIndex(index)
        print(item.checkState())
Exemplo n.º 4
0
    def init_menu_bar(self):
        menu_bar = self.menuBar()
        file_menu = menu_bar.addMenu(_('File'))
        entry_menu = menu_bar.addMenu(_('Entry'))
        filter_menu = menu_bar.addMenu(_('Filter'))
        view_menu = menu_bar.addMenu(_('View'))

        redraw_on_release_grid_action = QAction(_('Refresh on slider release'),
                                                self)
        redraw_on_release_grid_action.setCheckable(True)
        redraw_on_release_grid_action.setChecked(True)
        view_menu.addAction(redraw_on_release_grid_action)
        redraw_on_release_grid_action.triggered.connect(
            lambda: self.change_redraw_on_release(redraw_on_release_grid_action
                                                  .isChecked()))
        redraw_on_release_grid_action.setChecked(self.config.redraw_on_release)
        self.change_redraw_on_release(self.config.redraw_on_release)

        imp_act = QAction(_('Import db'), self)
        imp_act.triggered.connect(self.import_db)
        file_menu.addAction(imp_act)

        exp_act = QAction(_('Export db'), self)
        exp_act.triggered.connect(self.export_db)
        file_menu.addAction(exp_act)

        aot_menu = QAction(_('Stay on top'), self)
        aot_menu.setCheckable(True)
        aot_menu.triggered.connect(
            lambda: self.change_sot(aot_menu.isChecked()))
        view_menu.addAction(aot_menu)
        aot_menu.setChecked(self.config.stay_on_top)

        dark_mode = QAction(_('Dark mode'), self)
        dark_mode.setCheckable(True)
        dark_mode.triggered.connect(
            lambda: self.redraw_palette(dark_mode.isChecked()))
        view_menu.addAction(dark_mode)
        dark_mode.setChecked(self.config.dark_mode)

        new_entry = QAction(_('Add new entry'), self)
        new_entry.triggered.connect(self.add_new_entry)
        entry_menu.addAction(new_entry)

        filter_menu.addSection('Entry type')
        for entry_type in EntryType:
            self.add_filter(entry_type.value, filter_menu,
                            self.filter_type_changed)

        filter_menu.addSeparator()
        filter_menu.addSection('Progress status')
        for entry_status in ProgressStatus:
            self.add_filter(entry_status.value, filter_menu,
                            self.filter_status_changed)
Exemplo n.º 5
0
    def fnProcesaSeleccionRats(self, action: QAction):
        def llenarTabla(msg):
            self.fillTableWidget(self.pandasUtils.tempDf)
            self.overlay.killAndHide()

        self.ratsSeleccionados[str(action.data())] = action.isChecked()
        print(f"Fn procesa seleccion Rat {action} {self.ratsSeleccionados}")
        if (sum([1 for rat, v in self.ratsSeleccionados.items()
                 if v is True]) == 0):
            action.setChecked(True)
            self.ratsSeleccionados[str(action.data())] = action.isChecked()
        self.fnMuestraCantidadEnRats()
        self.fnAplicaFiltrosDfOk(llenarTabla)
Exemplo n.º 6
0
 def add_filter(self, name, filter_menu, function):
     a = QAction(_(name), self)
     a.setCheckable(True)
     a.triggered.connect(lambda: function(name, a.isChecked()))
     filter_menu.addAction(a)
     a.setChecked(not getattr(self.config, name.lower()))
     a.trigger()
Exemplo n.º 7
0
class MenuBarWidget(QMenuBar):
    def __init__(self, mainWidget):
        super().__init__()
        self.mainWidget: MainWidget = mainWidget

        self.file = self.addMenu("File")
        self.audio = self.addMenu("Audio")

        self.configDialog = QAction('Settings', self)
        self.configDialog.triggered.connect(ConfigDialog.showDialog)

        self.autoPlay = QAction('Autoplay disorder')
        self.autoPlay.setCheckable(True)
        self.autoPlay.setChecked(False)
        self.autoPlay.triggered.connect(self.changeBtn)

        self.playAudio = QAction('Play disorder')
        self.playAudio.triggered.connect(self.playSample)

        self.file.addAction(self.configDialog)
        self.audio.addAction(self.autoPlay)
        self.audio.addAction(self.playAudio)

    def playSample(self):
        QSound.play(ConfigControl.get_disorderPath())

    def changeBtn(self):
        if self.autoPlay.isChecked():
            self.mainWidget.playDisorder.setDisabled(1)
        else:
            self.mainWidget.playDisorder.setEnabled(1)
Exemplo n.º 8
0
class MenuBarWidget(QMenuBar):
    def __init__(self, mainWidget):
        super().__init__()

        self.mainWidget: MainWidget = mainWidget

        self.file = self.addMenu("File")
        self.edit = self.addMenu("Edit")
        self.show = self.addMenu("Show")

        self.showFileTreeAct = QAction('Show file tree',
                                       shortcut="Ctrl+D",
                                       enabled=True,
                                       checkable=True,
                                       triggered=self.showFileTree)
        self.setDirect = QAction('Select main directory',
                                 triggered=self.setActiveDirectory)

        self.file.addAction(self.setDirect)
        self.show.addAction(self.showFileTreeAct)

    def showFileTree(self):
        showFiles = self.showFileTreeAct.isChecked()
        self.mainWidget.tree.setHidden(not showFiles)

    def setActiveDirectory(self):
        directory = str(
            QFileDialog.getExistingDirectory(self, "Select Directory"))

        self.mainWidget.model.setRootPath(directory)
        tmpIdx = self.mainWidget.model.index(directory)
        self.mainWidget.tree.setRootIndex(tmpIdx)
Exemplo n.º 9
0
class ribbon_optics_files(QToolBar):
    def __init__(self):
        QToolBar.__init__(self)

        self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.setIconSize(QSize(42, 42))

        self.run = play(self,
                        "optics_ribbon_run",
                        run_text=wrap_text(_("Run optical simulation"), 5))
        self.addAction(self.run)

        self.fx_box = mode_selector()
        self.fx_box.show_all = True
        self.fx_box.update()
        self.addWidget(self.fx_box)

        self.spectrum = tb_spectrum()
        self.addWidget(self.spectrum)

        self.configwindow = QAction(icon_get("preferences-system"),
                                    _("Configure"), self)
        self.addAction(self.configwindow)

        self.optical_filter = QAction(icon_get("optical_filter"),
                                      _("Optical\nFilter"), self)
        self.optical_filter.setCheckable(True)
        self.optical_filter.triggered.connect(self.callback_filter_clicked)

        self.menu_optical_filter = QMenu(self)
        self.optical_filter.setMenu(self.menu_optical_filter)

        self.filter_edit = QAction(_("Edit"), self)
        self.filter_edit.triggered.connect(self.callback_filter_window)
        self.menu_optical_filter.addAction(self.filter_edit)
        self.addAction(self.optical_filter)

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

        self.help = QAction(icon_get("help"), _("Help"), self)
        self.addAction(self.help)

    def callback_filter_clicked(self):
        f = inp()
        f.load(os.path.join(get_sim_path(), "filter.inp"))
        enabled = f.set_token("#filter_enabled",
                              str(self.optical_filter.isChecked()))
        f.save()

    def callback_filter_window(self):
        widget = tab_class(os.path.join(get_sim_path(), "filter.inp"))
        widget.setWindowIcon(icon_get("filter_wheel"))

        widget.setWindowTitle(_("Filter editor") + " (https://www.gpvdm.com)")

        widget.show()
Exemplo n.º 10
0
    def setUpBotAction(self):
        botSetting = QAction('&Bot', self)
        #botSetting.triggered.connect(self.joinChannel)
        botSetting.triggered.connect(self.openBotSetting)
        self.fileMenu.addAction(botSetting)

        botOnAndOff = QAction('&Bot Running', self)
        botOnAndOff.setCheckable(True)
        #change later
        botOnAndOff.setChecked(False)
        self.botRunning = botOnAndOff.isChecked()
        botOnAndOff.triggered.connect(self.initializeBot)
        self.fileMenu.addAction(botOnAndOff)
Exemplo n.º 11
0
class ToolsMenu(QMenu):
    """Menu with file actions."""

    def __init__(self, parent, diagram, title='&Settings'):
        """Initializes the class."""
        super(ToolsMenu, self).__init__(parent)
        self.setTitle(title)
        self._diagram = diagram
        self.parent = parent

        self._selmode_action = QAction('&Selection Mode', self)
        self._selmode_action.setCheckable(True)
        self._selmode_action.setStatusTip('On/off selection mode')
        self._selmode_action.triggered.connect(self._select_mode)
        self.addAction(self._selmode_action)
        self.addSeparator()
        
        self._operation_mode_group = QActionGroup(self, exclusive=True)
        self._operation_mode_group.triggered.connect(self._select_operation)

        self._point_op_action = QAction('&Point Operations', self)
        self._point_op_action.setCheckable(True)
        self._point_op_action.setChecked(True) 
        self._point_op_action.setData(OperatinState.point)
        self._operation_mode_group.addAction(self._point_op_action)
        self._point_op_action.setStatusTip('Set point operation mode')
        self.addAction(self._point_op_action)

        self._line_op_action = QAction('&Line Operations', self)
        self._line_op_action.setCheckable(True)
        self._line_op_action.setData(OperatinState.line)
        self._operation_mode_group.addAction(self._line_op_action)        
        self._line_op_action.setStatusTip('Set line operation mode')
        self.addAction(self._line_op_action)

    def _select_mode(self):
        """set diagram mode"""
        state = self._selmode_action.isChecked()
        self._diagram.set_select_mode(state)
        self._enable_operation(not state)
        
    def _select_operation(self):
        """set diagram operation"""
        action = self._operation_mode_group.checkedAction()
        self._diagram.set_operation_state(action.data())
        
    def _enable_operation(self, enable):
        """enable operations items"""
        self._line_op_action.setEnabled(enable)
        self._point_op_action.setEnabled(enable)
Exemplo n.º 12
0
 def __init__(self, parent):
     super().__init__(parent)
     self.parent, unitMeasure, rulerSize, colors, zoom= parent, QMenu(gettext("Unit Measure"), self), QMenu(gettext("Ruler Size"), self), QMenu(gettext("Colors"), self), QMenu(gettext("Zoom"), self)
     self.addAction(QAction(parent.orientation[1 if not parent.oH else 0], self, triggered= partial(parent.changeOrientation, 1 if not parent.oH else 0)))
     for menu in [ [ zoom, parent.zooms, parent.changeMode, parent.zoom ], [ unitMeasure, parent.unitMeasure, parent.changeUnitMeasure, parent.cUM ], [ rulerSize, parent.rulerSize, parent.changeRulerSize, parent.sXY ], [ colors, parent.colors, parent.changeRulerColor, parent.defaultColors ] ]:
         for i, item in enumerate(menu[1]):
             item= QAction(item[0][0], self, triggered= partial(menu[2], i))
             if type(menu[3]) == type(list()) and i == len(menu[1]) - 1:
                 menu[0].addSeparator()
                 item.setEnabled(False if menu[1] == menu[3] else True)
             else:
                 item.setCheckable(True)
                 item.setChecked(True if i == menu[3] else False)
                 item.setEnabled(not item.isChecked())
             menu[0].addAction(item)
         if menu[0] in [ unitMeasure, colors ]:
             menu[0].setEnabled(not bool(parent.zoom))
         self.addMenu(menu[0])
     self.addSeparator()
     self.addAction(QAction(parent.about, self, triggered= self.AboutDialog(parent).exec_))
     self.addSeparator()
     self.addAction(QAction(gettext("Exit"), self, triggered= parent.close))
Exemplo n.º 13
0
class ImageViewer(QMainWindow):
    def __init__(self):
        super(ImageViewer, self).__init__()

        self.printer = QPrinter()
        self.scaleFactor = 0.0

        self.imageLabel = QLabel()
        self.imageLabel.setBackgroundRole(QPalette.Base)
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)

        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.imageLabel)
        self.setCentralWidget(self.scrollArea)

        self.createActions()
        self.createMenus()

        self.setWindowTitle("Image Viewer")
        self.resize(500, 400)

    def open(self):
        fileName, _ = QFileDialog.getOpenFileName(self, "Open File",
                                                  QDir.currentPath())
        if fileName:
            image = QImage(fileName)
            if image.isNull():
                QMessageBox.information(self, "Image Viewer",
                                        "Cannot load %s." % fileName)
                return

            self.imageLabel.setPixmap(QPixmap.fromImage(image))
            self.scaleFactor = 1.0

            self.printAct.setEnabled(True)
            self.fitToWindowAct.setEnabled(True)
            self.updateActions()

            if not self.fitToWindowAct.isChecked():
                self.imageLabel.adjustSize()

    def print_(self):
        dialog = QPrintDialog(self.printer, self)
        if dialog.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.imageLabel.pixmap().size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(),
                                size.height())
            painter.setWindow(self.imageLabel.pixmap().rect())
            painter.drawPixmap(0, 0, self.imageLabel.pixmap())

    def zoomIn(self):
        self.scaleImage(1.25)

    def zoomOut(self):
        self.scaleImage(0.8)

    def normalSize(self):
        self.imageLabel.adjustSize()
        self.scaleFactor = 1.0

    def fitToWindow(self):
        fitToWindow = self.fitToWindowAct.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.normalSize()

        self.updateActions()

    def about(self):
        QMessageBox.about(
            self, "About Image Viewer",
            "<p>The <b>Image Viewer</b> example shows how to combine "
            "QLabel and QScrollArea to display an image. QLabel is "
            "typically used for displaying text, but it can also display "
            "an image. QScrollArea provides a scrolling view around "
            "another widget. If the child widget exceeds the size of the "
            "frame, QScrollArea automatically provides scroll bars.</p>"
            "<p>The example demonstrates how QLabel's ability to scale "
            "its contents (QLabel.scaledContents), and QScrollArea's "
            "ability to automatically resize its contents "
            "(QScrollArea.widgetResizable), can be used to implement "
            "zooming and scaling features.</p>"
            "<p>In addition the example shows how to use QPainter to "
            "print an image.</p>")

    def createActions(self):
        self.openAct = QAction("&Open...",
                               self,
                               shortcut="Ctrl+O",
                               triggered=self.open)

        self.printAct = QAction("&Print...",
                                self,
                                shortcut="Ctrl+P",
                                enabled=False,
                                triggered=self.print_)

        self.exitAct = QAction("E&xit",
                               self,
                               shortcut="Ctrl+Q",
                               triggered=self.close)

        self.zoomInAct = QAction("Zoom &In (25%)",
                                 self,
                                 shortcut="Ctrl++",
                                 enabled=False,
                                 triggered=self.zoomIn)

        self.zoomOutAct = QAction("Zoom &Out (25%)",
                                  self,
                                  shortcut="Ctrl+-",
                                  enabled=False,
                                  triggered=self.zoomOut)

        self.normalSizeAct = QAction("&Normal Size",
                                     self,
                                     shortcut="Ctrl+S",
                                     enabled=False,
                                     triggered=self.normalSize)

        self.fitToWindowAct = QAction("&Fit to Window",
                                      self,
                                      enabled=False,
                                      checkable=True,
                                      shortcut="Ctrl+F",
                                      triggered=self.fitToWindow)

        self.aboutAct = QAction("&About", self, triggered=self.about)

        self.aboutQtAct = QAction("About &Qt",
                                  self,
                                  triggered=QApplication.instance().aboutQt)

    def createMenus(self):
        self.fileMenu = QMenu("&File", self)
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.printAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.fitToWindowAct)

        self.helpMenu = QMenu("&Help", self)
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

        self.menuBar().addMenu(self.fileMenu)
        self.menuBar().addMenu(self.viewMenu)
        self.menuBar().addMenu(self.helpMenu)

    def updateActions(self):
        self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())

    def scaleImage(self, factor):
        self.scaleFactor *= factor
        self.imageLabel.resize(self.scaleFactor *
                               self.imageLabel.pixmap().size())

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)

        self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
        self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)

    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(
            int(factor * scrollBar.value() +
                ((factor - 1) * scrollBar.pageStep() / 2)))
Exemplo n.º 14
0
class Florodoro(QWidget):

    def parseArguments(self):
        parser = argparse.ArgumentParser(
            description="A pomodoro timer that grows procedurally generated trees and flowers while you're studying.",
        )

        parser.add_argument(
            "-d",
            "--debug",
            action="store_true",
            help="run the app in debug mode",
        )

        return parser.parse_args()

    def __init__(self):
        super().__init__()

        arguments = self.parseArguments()

        self.DEBUG = arguments.debug

        os.chdir(os.path.dirname(os.path.realpath(__file__)))

        self.MIN_WIDTH = 600
        self.MIN_HEIGHT = 350

        self.setMinimumWidth(self.MIN_WIDTH)
        self.setMinimumHeight(self.MIN_HEIGHT)

        self.ROOT_FOLDER = os.path.expanduser("~/.florodoro/")

        self.HISTORY_FILE_PATH = self.ROOT_FOLDER + "history" + ("" if not self.DEBUG else "-debug") + ".yaml"
        self.CONFIGURATION_FILE_PATH = self.ROOT_FOLDER + "config" + ("" if not self.DEBUG else "-debug") + ".yaml"

        self.history = History(self.HISTORY_FILE_PATH)

        self.SOUNDS_FOLDER = "sounds/"
        self.PLANTS_FOLDER = "plants/"
        self.IMAGE_FOLDER = "images/"

        self.TEXT_COLOR = self.palette().text().color()
        self.BREAK_COLOR = "#B37700"

        self.APP_NAME = "Florodoro"

        self.STUDY_ICON = qtawesome.icon('fa5s.book', color=self.TEXT_COLOR)
        self.BREAK_ICON = qtawesome.icon('fa5s.coffee', color=self.BREAK_COLOR)
        self.CONTINUE_ICON = qtawesome.icon('fa5s.play', color=self.TEXT_COLOR)
        self.PAUSE_ICON = qtawesome.icon('fa5s.pause', color=self.TEXT_COLOR)
        self.RESET_ICON = qtawesome.icon('fa5s.undo', color=self.TEXT_COLOR)

        self.PLANTS = [GreenTree, DoubleGreenTree, OrangeTree, CircularFlower]
        self.PLANT_NAMES = ["Spruce", "Double spruce", "Maple", "Flower"]

        self.MAX_PLANT_AGE = 90  # maximum number of minutes to make the plant optimal in size

        self.WIDGET_SPACING = 10

        self.MAX_TIME = 180
        self.STEP = 5

        self.INITIAL_TEXT = "Start!"

        self.menuBar = QMenuBar(self)
        self.presets_menu = self.menuBar.addMenu('&Presets')

        self.presets = {
            "Classic": (25, 5, 4),
            "Extended": (45, 12, 2),
            "Sitcomodoro": (65, 25, 1),
        }

        for name in self.presets:
            study_time, break_time, cycles = self.presets[name]

            self.presets_menu.addAction(
                QAction(f"{name} ({study_time} : {break_time} : {cycles})", self,
                        triggered=partial(self.load_preset, study_time, break_time, cycles)))

        self.DEFAULT_PRESET = "Classic"

        self.options_menu = self.menuBar.addMenu('&Options')

        self.notify_menu = self.options_menu.addMenu("&Notify")

        self.sound_action = QAction("&Sound", self, checkable=True, checked=not self.DEBUG,
                                    triggered=lambda _: self.volume_slider.setDisabled(
                                        not self.sound_action.isChecked()))

        self.notify_menu.addAction(self.sound_action)

        self.volume_slider = QSlider(Qt.Horizontal, minimum=0, maximum=100, value=85)
        slider_action = QWidgetAction(self)
        slider_action.setDefaultWidget(SpacedQWidget(self.volume_slider))
        self.notify_menu.addAction(slider_action)

        self.popup_action = QAction("&Pop-up", self, checkable=True, checked=True)
        self.notify_menu.addAction(self.popup_action)

        self.menuBar.addAction(
            QAction(
                "&Statistics",
                self,
                triggered=lambda: self.statistics.show() if self.statistics.isHidden() else self.statistics.hide()
            )
        )

        self.menuBar.addAction(
            QAction(
                "&About",
                self,
                triggered=lambda: QMessageBox.information(
                    self,
                    "About",
                    "This application was created by Tomáš Sláma. It is heavily inspired by the Android app Forest, "
                    "but with all of the plants generated procedurally. It's <a href='https://github.com/xiaoxiae/Florodoro'>open source</a> and licensed "
                    "under MIT, so do as you please with the code and anything else related to the project.",
                ),
            )
        )

        self.plant_menu = self.options_menu.addMenu("&Plants")

        self.overstudy_action = QAction("Overstudy", self, checkable=True)
        self.options_menu.addAction(self.overstudy_action)

        self.plant_images = []
        self.plant_checkboxes = []

        # dynamically create widgets for each plant
        for plant, name in zip(self.PLANTS, self.PLANT_NAMES):
            self.plant_images.append(tempfile.NamedTemporaryFile(suffix=".svg"))
            tmp = plant()
            tmp.set_max_age(1)
            tmp.set_age(1)
            tmp.save(self.plant_images[-1].name, 200, 200)

            setattr(self.__class__, name,
                    QAction(self, icon=QIcon(self.plant_images[-1].name), text=name, checkable=True, checked=True))

            action = getattr(self.__class__, name)

            self.plant_menu.addAction(action)
            self.plant_checkboxes.append(action)

        # the current plant that we're growing
        # if set to none, no plant is growing
        self.plant = None

        self.menuBar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)

        main_vertical_layout = QVBoxLayout(self)
        main_vertical_layout.setContentsMargins(0, 0, 0, 0)
        main_vertical_layout.setSpacing(0)
        main_vertical_layout.addWidget(self.menuBar)

        self.canvas = Canvas(self)

        self.statistics = Statistics(self.history)

        font = self.font()
        font.setPointSize(100)

        self.main_label = QLabel(self, alignment=Qt.AlignCenter)
        self.main_label.setFont(font)
        self.main_label.setText(self.INITIAL_TEXT)

        font.setPointSize(26)
        self.cycle_label = QLabel(self)
        self.cycle_label.setAlignment(Qt.AlignTop)
        self.cycle_label.setMargin(20)
        self.cycle_label.setFont(font)

        main_horizontal_layout = QHBoxLayout(self)

        self.study_time_spinbox = QSpinBox(self, prefix="Study for: ", suffix="min.", minimum=1, maximum=self.MAX_TIME,
                                           singleStep=self.STEP)

        self.break_time_spinbox = QSpinBox(self, prefix="Break for: ", suffix="min.", minimum=1, maximum=self.MAX_TIME,
                                           singleStep=self.STEP,
                                           styleSheet=f'color:{self.BREAK_COLOR};')

        self.cycles_spinbox = QSpinBox(self, prefix="Cycles: ", minimum=1, value=1)

        # keep track of remaining number of cycles and the starting number of cycles
        self.remaining_cycles = 0
        self.total_cycles = 0

        # whether we're currently studying
        self.is_study_ongoing = False

        # whether we notified the user already during overstudy
        self.already_notified_during_overstudy = False

        stacked_layout = QStackedLayout(self, stackingMode=QStackedLayout.StackAll)
        stacked_layout.addWidget(self.main_label)
        stacked_layout.addWidget(self.cycle_label)
        stacked_layout.addWidget(self.canvas)

        main_vertical_layout.addLayout(stacked_layout)

        self.setStyleSheet("")

        self.study_button = QPushButton(self, clicked=self.start, icon=self.STUDY_ICON)
        self.break_button = QPushButton(self, clicked=self.start_break, icon=self.BREAK_ICON)
        self.pause_button = QPushButton(self, clicked=self.toggle_pause, icon=self.PAUSE_ICON)
        self.reset_button = QPushButton(self, clicked=self.reset, icon=self.RESET_ICON)

        main_horizontal_layout.addWidget(self.study_time_spinbox)
        main_horizontal_layout.addWidget(self.break_time_spinbox)
        main_horizontal_layout.addWidget(self.cycles_spinbox)
        main_horizontal_layout.addWidget(self.study_button)
        main_horizontal_layout.addWidget(self.break_button)
        main_horizontal_layout.addWidget(self.pause_button)
        main_horizontal_layout.addWidget(self.reset_button)

        main_vertical_layout.addLayout(main_horizontal_layout)

        self.setLayout(main_vertical_layout)

        self.study_timer_frequency = 1 / 60 * 1000
        self.study_timer = QTimer(self, interval=int(self.study_timer_frequency), timeout=self.decrease_remaining_time)

        self.player = QMediaPlayer(self)

        self.setWindowIcon(QIcon(self.IMAGE_FOLDER + "icon.svg"))
        self.setWindowTitle(self.APP_NAME)

        # set initial UI state
        self.reset()

        # a list of name, getter and setter things to load/save when the app opens/closes
        # also dynamically get settings for selecting/unselecting plants
        self.CONFIGURATION_ATTRIBUTES = [("study-time", self.study_time_spinbox.value,
                                          self.study_time_spinbox.setValue),
                                         ("break-time", self.break_time_spinbox.value,
                                          self.break_time_spinbox.setValue),
                                         ("cycles", self.cycles_spinbox.value, self.cycles_spinbox.setValue),
                                         ("sound", self.sound_action.isChecked, self.sound_action.setChecked),
                                         ("sound-volume", self.volume_slider.value, self.volume_slider.setValue),
                                         ("pop-ups", self.popup_action.isChecked, self.popup_action.setChecked),
                                         ("overstudy", self.overstudy_action.isChecked,
                                          self.overstudy_action.setChecked)] + \
                                        [(name.lower(), getattr(self.__class__, name).isChecked,
                                          getattr(self.__class__, name).setChecked) for _, name in
                                         zip(self.PLANTS, self.PLANT_NAMES)]
        # load the default preset
        self.load_preset(*self.presets[self.DEFAULT_PRESET])

        self.load_settings()
        self.show()

    def load_settings(self):
        """Loads the settings file (if it exists)."""
        if os.path.exists(self.CONFIGURATION_FILE_PATH):
            with open(self.CONFIGURATION_FILE_PATH) as file:
                configuration = yaml.load(file, Loader=yaml.FullLoader)

                # don't crash if config is broken
                if not isinstance(configuration, dict):
                    return

                for key in configuration:
                    for name, _, setter in self.CONFIGURATION_ATTRIBUTES:
                        if key == name:
                            setter(configuration[key])

    def save_settings(self):
        """Saves the settings file (if it exists)."""
        if not os.path.exists(self.ROOT_FOLDER):
            os.mkdir(self.ROOT_FOLDER)

        with open(self.CONFIGURATION_FILE_PATH, 'w') as file:
            configuration = {}

            for name, getter, _ in self.CONFIGURATION_ATTRIBUTES:
                configuration[name] = getter()

            file.write(yaml.dump(configuration))

    def closeEvent(self, event):
        """Called when the app is being closed. Overridden to also save Florodoro settings."""
        self.save_settings()
        super().closeEvent(event)

    def load_preset(self, study_value: int, break_value: int, cycles: int):
        """Load a pomodoro preset."""
        self.study_time_spinbox.setValue(study_value)
        self.break_time_spinbox.setValue(break_value)
        self.cycles_spinbox.setValue(cycles)

    def keyPressEvent(self, event: QKeyEvent) -> None:
        """Debug-related keyboard controls."""
        if self.DEBUG:
            if event.key() == Qt.Key_Escape:
                possible_plants = [plant for i, plant in enumerate(self.PLANTS) if self.plant_checkboxes[i].isChecked()]

                if len(possible_plants) != 0:
                    self.plant = choice(possible_plants)()
                    self.canvas.set_drawable(self.plant)
                    self.plant.set_max_age(1)
                    self.plant.set_age(1)
                    self.canvas.update()

    def start_break(self):
        """Starts the break, instead of the study."""
        # if we're overstudying, this can be pressed when studying, so save that we did so
        if self.overstudy_action.isChecked() and self.is_study_ongoing:
            self.save_study(ignore_remainder=False)

        self.start(do_break=True)

    def start(self, do_break=False):
        """The function for starting either the study or break timer (depending on do_break)."""
        self.study_button.setEnabled(do_break)
        self.break_button.setEnabled(self.overstudy_action.isChecked() and not do_break)
        self.reset_button.setDisabled(False)

        self.pause_button.setDisabled(False)
        self.pause_button.setIcon(self.PAUSE_ICON)

        # if we're initially starting to do cycles, reset their count
        # don't reset on break, because we could be doing a standalone break
        if self.remaining_cycles == 0 and not do_break:
            self.remaining_cycles = self.cycles_spinbox.value()
            self.total_cycles = self.remaining_cycles
        else:
            # if we're not studing and are about to when there is still leftover time, we're ending the break quicker than intended
            # therefore, reduce cycles by 1, since they would have been if the timer were to run out during break
            if not self.is_study_ongoing and not do_break and self.get_leftover_time() / self.total_time > 0:
                self.remaining_cycles -= 1

                if self.remaining_cycles == 0:
                    self.reset()
                    return

        # set depending on whether we are currently studying or not
        self.is_study_ongoing = not do_break
        self.already_notified_during_overstudy = False

        self.main_label.setStyleSheet('' if not do_break else f'color:{self.BREAK_COLOR};')

        # the total time to study for (spinboxes are minutes)
        # since it's rounded down and it looks better to start at the exact time, 0.99 is added
        self.total_time = (self.study_time_spinbox if not do_break else self.break_time_spinbox).value() * 60 + 0.99
        self.ending_time = datetime.now() + timedelta(minutes=self.total_time / 60)

        # so it's displayed immediately
        self.update_time_label(self.total_time)
        self.update_cycles_label()

        # don't start showing canvas and growing the plant when we're not studying
        if not do_break:
            possible_plants = [plant for i, plant in enumerate(self.PLANTS) if self.plant_checkboxes[i].isChecked()]

            if len(possible_plants) != 0:
                self.plant = choice(possible_plants)()
                self.canvas.set_drawable(self.plant)
                self.plant.set_max_age(min(1, (self.total_time / 60) / self.MAX_PLANT_AGE))
                self.plant.set_age(0)
            else:
                self.plant = None

        self.study_timer.stop()  # it could be running - we could be currently in a break
        self.study_timer.start()

    def toggle_pause(self):
        """Called when the pause button is pressed. Either stops the timer or starts it again, while also doing stuff
        to the pause icons."""
        # stop the timer, if it's running
        if self.study_timer.isActive():
            self.study_timer.stop()
            self.pause_button.setIcon(self.CONTINUE_ICON)
            self.pause_time = datetime.now()

        # if not, resume
        else:
            self.ending_time += datetime.now() - self.pause_time
            self.study_timer.start()
            self.pause_button.setIcon(self.PAUSE_ICON)

    def reset(self):
        """Reset the UI."""
        self.study_timer.stop()
        self.pause_button.setIcon(self.PAUSE_ICON)

        self.main_label.setStyleSheet('')
        self.study_button.setDisabled(False)
        self.break_button.setDisabled(False)
        self.pause_button.setDisabled(True)
        self.reset_button.setDisabled(True)

        if self.plant is not None:
            self.plant.set_age(0)

        self.remaining_cycles = 0

        self.main_label.setText(self.INITIAL_TEXT)
        self.cycle_label.setText('')

    def update_time_label(self, time):
        """Update the text of the time label, given some time in seconds."""
        sign = -1 if time < 0 else 1

        time = abs(time)

        hours = int(time // 3600)
        minutes = int((time // 60) % 60)
        seconds = int(time % 60)

        # smooth timer: hide minutes/hours if there are none
        result = "-" if sign == -1 else ""
        if hours == 0:
            if minutes == 0:
                result += str(seconds)
            else:
                result += str(minutes) + QTime(0, 0, seconds).toString(":ss")
        else:
            result += str(hours) + QTime(0, minutes, seconds).toString(":mm:ss")

        self.main_label.setText(result)

    def play_sound(self, name: str):
        """Play a file from the sound directory. Extension is not included, will be added automatically."""
        for file in os.listdir(self.SOUNDS_FOLDER):
            # if the file starts with the provided name and only contains an extension after, try to play it
            if file.startswith(name) and file[len(name):][0] == ".":
                path = QDir.current().absoluteFilePath(self.SOUNDS_FOLDER + file)
                url = QUrl.fromLocalFile(path)
                content = QMediaContent(url)
                self.player.setMedia(content)
                self.player.setVolume(self.volume_slider.value())
                self.player.play()

    def show_notification(self, message: str):
        """Show the specified notification using plyer."""
        notification.notify(self.APP_NAME, message, self.APP_NAME, os.path.abspath(self.IMAGE_FOLDER + "icon.svg"))

    def update_cycles_label(self):
        """Update the cycles label, if we're currently studying and it wouldn't be 1/1."""
        if self.total_cycles != 1 and self.is_study_ongoing:
            self.cycle_label.setText(f"{self.total_cycles - self.remaining_cycles + 1}/{self.total_cycles}")

    def get_leftover_time(self):
        """Return time until the timer runs out."""
        return (self.ending_time - datetime.now()).total_seconds()

    def decrease_remaining_time(self):
        """Decrease the remaining time by the timer frequency. Updates clock/plant growth."""
        if self.DEBUG:
            self.ending_time -= timedelta(seconds=30)

        self.update_time_label(self.get_leftover_time())

        if self.get_leftover_time() <= 0:
            if self.is_study_ongoing:
                # only notify once per study, since this would be called all the time during overstudy
                if not self.already_notified_during_overstudy:
                    if self.sound_action.isChecked():
                        self.play_sound("study_done")

                    if self.popup_action.isChecked():
                        self.show_notification("Studying finished, take a break!")

                    self.already_notified_during_overstudy = True

                if not self.overstudy_action.isChecked():
                    self.save_study()  # save before break!
                    self.start_break()
            else:
                self.history.add_break(datetime.now(), self.total_time // 60)
                self.statistics.refresh()

                if self.sound_action.isChecked():
                    self.play_sound("break_done")

                if self.popup_action.isChecked():
                    self.show_notification("Break is over!")

                self.remaining_cycles -= 1
                if self.remaining_cycles <= 0:  # <=, because we could have just started a simple break
                    self.reset()
                else:
                    self.start()
                    self.update_cycles_label()

        else:
            # if there is leftover time and we haven't finished studying, grow the plant
            if self.is_study_ongoing:
                if self.plant is not None:
                    self.plant.set_age(1 - (self.get_leftover_time() / self.total_time))

                self.canvas.update()

    def save_study(self, ignore_remainder=True):
        """Save the record of the current study to the history file. By default, ignore the leftover time, since it
        will be a tiny number."""
        date = datetime.now()

        duration = (self.total_time - self.get_leftover_time()) / 60

        if ignore_remainder:
            duration = float(int(duration))

        self.history.add_study(date, duration, self.plant)

        self.statistics.move()  # move to the last plant
        self.statistics.refresh()
Exemplo n.º 15
0
class PlotterWindow(QMainWindow):
    def __init__(self, get_transfer_callback):
        super(PlotterWindow, self).__init__()
        self.setWindowTitle('UAVCAN Plotter')
        self.setWindowIcon(get_app_icon())

        self._active_data_types = set()

        self._get_transfer = get_transfer_callback

        self._update_timer = QTimer(self)
        self._update_timer.setSingleShot(False)
        self._update_timer.timeout.connect(self._update)
        self._update_timer.start(50)

        self._base_time = time.monotonic()

        self._plot_containers = []

        #
        # Control menu
        #
        control_menu = self.menuBar().addMenu('&Control')

        self._stop_action = QAction(get_icon('stop'), '&Stop Updates', self)
        self._stop_action.setStatusTip('While stopped, all new data will be discarded')
        self._stop_action.setShortcut(QKeySequence('Ctrl+Shift+S'))
        self._stop_action.setCheckable(True)
        self._stop_action.toggled.connect(self._on_stop_toggled)
        control_menu.addAction(self._stop_action)

        self._pause_action = QAction(get_icon('pause'), '&Pause Updates', self)
        self._pause_action.setStatusTip('While paused, new data will be accumulated in memory '
                                        'to be processed once un-paused')
        self._pause_action.setShortcut(QKeySequence('Ctrl+Shift+P'))
        self._pause_action.setCheckable(True)
        self._pause_action.toggled.connect(self._on_pause_toggled)
        control_menu.addAction(self._pause_action)

        control_menu.addSeparator()

        self._reset_time_action = QAction(get_icon('history'), '&Reset', self)
        self._reset_time_action.setStatusTip('Base time will be reset; all plots will be reset')
        self._reset_time_action.setShortcut(QKeySequence('Ctrl+Shift+R'))
        self._reset_time_action.triggered.connect(self._do_reset)
        control_menu.addAction(self._reset_time_action)

        #
        # New Plot menu
        #
        plot_menu = self.menuBar().addMenu('&New Plot')
        for idx, pl_name in enumerate(PLOT_AREAS.keys()):
            new_plot_action = QAction('Add ' + pl_name, self)
            new_plot_action.setStatusTip('Add new plot window')
            new_plot_action.setShortcut(QKeySequence('Ctrl+Alt+' + str(idx)))
            new_plot_action.triggered.connect(partial(self._do_add_new_plot, pl_name))
            plot_menu.addAction(new_plot_action)

        #
        # Window stuff
        #
        self.statusBar().showMessage('Use the "New Plot" menu to add plots')
        self.setCentralWidget(None)
        self.resize(600, 400)

    def _on_stop_toggled(self, checked):
        self._pause_action.setChecked(False)
        self.statusBar().showMessage('Stopped' if checked else 'Un-stopped')

    def _on_pause_toggled(self, checked):
        self.statusBar().showMessage('Paused' if checked else 'Un-paused')

    def _do_add_new_plot(self, plot_area_name):
        def remove():
            self._plot_containers.remove(plc)

        plc = PlotContainerWidget(self, PLOT_AREAS[plot_area_name], self._active_data_types)
        plc.on_close = remove
        self._plot_containers.append(plc)

        docks = [
            Qt.LeftDockWidgetArea,
            Qt.LeftDockWidgetArea,
            Qt.RightDockWidgetArea,
            Qt.RightDockWidgetArea,
        ]
        dock_to = docks[(len(self._plot_containers) - 1) % len(docks)]
        self.addDockWidget(dock_to, plc)

        if len(self._plot_containers) > 1:
            self.statusBar().showMessage('Drag plots by the header to rearrange or detach them')

    def _do_reset(self):
        self._base_time = time.monotonic()

        for plc in self._plot_containers:
            try:
                plc.reset()
            except Exception:
                logger.error('Failed to reset plot container', exc_info=True)

        logger.info('Reset done, new time base %r', self._base_time)

    def _update(self):
        if self._stop_action.isChecked():
            while self._get_transfer() is not None:     # Discarding everything
                pass
            return

        if not self._pause_action.isChecked():
            while True:
                tr = self._get_transfer()
                if not tr:
                    break

                self._active_data_types.add(tr.data_type_name)

                for plc in self._plot_containers:
                    try:
                        plc.process_transfer(tr.ts_mono - self._base_time, tr)
                    except Exception:
                        logger.error('Plot container failed to process a transfer', exc_info=True)

        for plc in self._plot_containers:
            try:
                plc.update()
            except Exception:
                logger.error('Plot container failed to update', exc_info=True)
Exemplo n.º 16
0
class ToolBox(QMainWindow):
    def __init__(self, parent=None, view_manager=AbstractViewManager):
        QMainWindow.__init__(self, parent)
        self.manager = view_manager
        self.controller = view_manager.get_controller()
        self.favorites = self.controller.get_favorites()
        self.favorites.add_listener(self.on_fav_changed)
        self.ui = Ui_ToolBox()
        self.ui.setupUi(self)
        self.setWindowFlags(QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint)
        self.edit = None

        self.move(40, 985)
        self.bind_all()

        self.action_thumb_filter = QAction("Show thumbnail page",
                                           self,
                                           checkable=True,
                                           triggered=self.url_combo_load)
        self.action_thumb_filter.setChecked(True)

        self.action_pix_filter = QAction("Show pictures page",
                                         self,
                                         checkable=True,
                                         triggered=self.url_combo_load)
        self.action_pix_filter.setChecked(True)

        self.action_video_filter = QAction("Show video page",
                                           self,
                                           checkable=True,
                                           triggered=self.url_combo_load)
        self.action_video_filter.setChecked(True)

        menu = QMenu(self)
        menu.addAction(self.action_thumb_filter)
        menu.addAction(self.action_pix_filter)
        menu.addAction(self.action_video_filter)
        self.ui.bn_filter.setMenu(menu)

        self.curr_urls = list()
        self.category_combo_load()

    def bind_all(self):
        self.ui.combo_category.currentIndexChanged.connect(self.url_combo_load)
        self.ui.bn_go.clicked.connect(lambda: self.controller.goto_url(
            self.curr_urls[self.ui.combo_url.currentIndex()].url))
        self.ui.bn_edit.clicked.connect(self.favorite_edit)
        self.ui.bn_add_thumb.clicked.connect(self.add_thumb)
        self.ui.bn_add_page.clicked.connect(self.add_full)
        self.ui.bn_config.clicked.connect(self.manager.show_config_dialog)

    def panic(self):
        self.hide()
        if self.edit is not None:
            self.edit.hide()

    def add_thumb(self):
        self.controller.add_thumb_page_to_fav(
            self.ui.combo_category.currentText())

    def add_full(self):
        self.controller.add_full_page_to_fav(
            self.ui.combo_category.currentText())

    def favorite_edit(self):
        curr_record = self.curr_urls[self.ui.combo_url.currentIndex()]
        self.edit = FavoriteChangeDialog(None, curr_record, self.favorites)
        # self.edit.addAction(self.panic_action)
        self.edit.show()

    def on_fav_changed(self, record):
        if record is None:
            save_cat_index = self.ui.combo_category.currentIndex()
            save_url_index = self.ui.combo_url.currentIndex()

            self.category_combo_load()
            self.ui.combo_category.setCurrentIndex(save_cat_index)

            self.url_combo_load()
            self.ui.combo_url.setCurrentIndex(save_url_index)
        else:
            self.category_combo_load()
            self.ui.combo_category.setCurrentText(record.category)
            self.url_combo_load()
            self.ui.combo_url.setCurrentText(record.combo_view)

    def category_combo_load(self):
        self.ui.combo_category.clear()
        self.ui.combo_category.addItems(self.favorites.get_categories())

    # def connect_to(self, tool_button):
    #     self.tb = tool_button
    #     tool_button.clicked.connect(self.on_clicked_show)
    #
    # def on_clicked_show(self):
    #     if self.isHidden():
    #         self.show()
    #     else:
    #         self.hide()

    def url_combo_load(self):
        fav_list = self.favorites.get(self.ui.combo_category.currentText())
        self.ui.combo_url.clear()
        self.curr_urls = list()
        for item in fav_list:
            if self.url_filter(item):
                self.ui.combo_url.addItem(item.combo_view)
                self.curr_urls.append(item)

    def url_filter(self, item):
        if self.action_thumb_filter.isChecked():
            if item.is_thumb(): return True
        if self.action_pix_filter.isChecked():
            if item.is_pix(): return True
        if self.action_video_filter.isChecked():
            if item.is_video(): return True
        return False

    def addAction(self, action):
        super().addAction(action)
        self.panic_action = action
Exemplo n.º 17
0
class MainWindow(QMainWindow):
    def __init__(self, url):
        super(MainWindow, self).__init__()

        self.progress = 0

        fd = QFile(":/jquery.min.js")

        if fd.open(QIODevice.ReadOnly | QFile.Text):
            self.jQuery = QTextStream(fd).readAll()
            fd.close()
        else:
            self.jQuery = ''

        QNetworkProxyFactory.setUseSystemConfiguration(True)

        self.view = QWebView(self)
        self.view.load(url)
        self.view.loadFinished.connect(self.adjustLocation)
        self.view.titleChanged.connect(self.adjustTitle)
        self.view.loadProgress.connect(self.setProgress)
        self.view.loadFinished.connect(self.finishLoading)

        self.locationEdit = QLineEdit(self)
        self.locationEdit.setSizePolicy(QSizePolicy.Expanding,
                self.locationEdit.sizePolicy().verticalPolicy())
        self.locationEdit.returnPressed.connect(self.changeLocation)

        toolBar = self.addToolBar("Navigation")
        toolBar.addAction(self.view.pageAction(QWebPage.Back))
        toolBar.addAction(self.view.pageAction(QWebPage.Forward))
        toolBar.addAction(self.view.pageAction(QWebPage.Reload))
        toolBar.addAction(self.view.pageAction(QWebPage.Stop))
        toolBar.addWidget(self.locationEdit)

        viewMenu = self.menuBar().addMenu("&View")
        viewSourceAction = QAction("Page Source", self)
        viewSourceAction.triggered.connect(self.viewSource)
        viewMenu.addAction(viewSourceAction)

        effectMenu = self.menuBar().addMenu("&Effect")
        effectMenu.addAction("Highlight all links", self.highlightAllLinks)

        self.rotateAction = QAction(
                self.style().standardIcon(QStyle.SP_FileDialogDetailedView),
                "Turn images upside down", self, checkable=True,
                toggled=self.rotateImages)
        effectMenu.addAction(self.rotateAction)

        toolsMenu = self.menuBar().addMenu("&Tools")
        toolsMenu.addAction("Remove GIF images", self.removeGifImages)
        toolsMenu.addAction("Remove all inline frames",
                self.removeInlineFrames)
        toolsMenu.addAction("Remove all object elements",
                self.removeObjectElements)
        toolsMenu.addAction("Remove all embedded elements",
                self.removeEmbeddedElements)
        self.setCentralWidget(self.view)

    def viewSource(self):
        accessManager = self.view.page().networkAccessManager()
        request = QNetworkRequest(self.view.url())
        reply = accessManager.get(request)
        reply.finished.connect(self.slotSourceDownloaded)

    def slotSourceDownloaded(self):
        reply = self.sender()
        self.textEdit = QTextEdit()
        self.textEdit.setAttribute(Qt.WA_DeleteOnClose)
        self.textEdit.show()
        self.textEdit.setPlainText(QTextStream(reply).readAll())
        self.textEdit.resize(600, 400)
        reply.deleteLater()

    def adjustLocation(self):
        self.locationEdit.setText(self.view.url().toString())

    def changeLocation(self):
        url = QUrl.fromUserInput(self.locationEdit.text())
        self.view.load(url)
        self.view.setFocus()

    def adjustTitle(self):
        if 0 < self.progress < 100:
            self.setWindowTitle("%s (%s%%)" % (self.view.title(), self.progress))
        else:
            self.setWindowTitle(self.view.title())

    def setProgress(self, p):
        self.progress = p
        self.adjustTitle()

    def finishLoading(self):
        self.progress = 100
        self.adjustTitle()
        self.view.page().mainFrame().evaluateJavaScript(self.jQuery)
        self.rotateImages(self.rotateAction.isChecked())

    def highlightAllLinks(self):
        code = """$('a').each(
                    function () {
                        $(this).css('background-color', 'yellow') 
                    } 
                  )"""
        self.view.page().mainFrame().evaluateJavaScript(code)

    def rotateImages(self, invert):
        if invert:
            code = """
                $('img').each(
                    function () {
                        $(this).css('-webkit-transition', '-webkit-transform 2s'); 
                        $(this).css('-webkit-transform', 'rotate(180deg)') 
                    } 
                )"""
        else:
            code = """
                $('img').each(
                    function () { 
                        $(this).css('-webkit-transition', '-webkit-transform 2s'); 
                        $(this).css('-webkit-transform', 'rotate(0deg)') 
                    } 
                )"""

        self.view.page().mainFrame().evaluateJavaScript(code)

    def removeGifImages(self):
        code = "$('[src*=gif]').remove()"
        self.view.page().mainFrame().evaluateJavaScript(code)

    def removeInlineFrames(self):
        code = "$('iframe').remove()"
        self.view.page().mainFrame().evaluateJavaScript(code)

    def removeObjectElements(self):
        code = "$('object').remove()"
        self.view.page().mainFrame().evaluateJavaScript(code)

    def removeEmbeddedElements(self):
        code = "$('embed').remove()"
        self.view.page().mainFrame().evaluateJavaScript(code)
class ImageViewer(QMainWindow):
    def __init__(self):
        super(ImageViewer, self).__init__()

        self.printer = QPrinter()
        self.scaleFactor = 0.0
        self.directory=""

        self.imageLabel = QLabel()
        self.imageLabel.setBackgroundRole(QPalette.Base)
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)

        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.imageLabel)
        self.setCentralWidget(self.scrollArea)

        self.createActions()
        self.createMenus()
        
        self.setWindowTitle("IOA Image Viewer")
        self.resize(800, 600)

    def openImg(self,fileName):
        self.imageLabel = QLabel()
        self.imageLabel.setBackgroundRole(QPalette.Base)
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)
        image = QImage(fileName)
        self.imageLabel.setPixmap(QPixmap.fromImage(image))
        self.scaleFactor = 1.0
        self.printAct.setEnabled(True)
        self.fitToWindowAct.setEnabled(True)
        self.updateActions()

    def open(self):
        fileName, _ = QFileDialog.getOpenFileName(self, "Open File",
                QDir.currentPath())
        self.directory=fileName
        print(fileName)
        if fileName:
            image = QImage(fileName)
            if image.isNull():
                QMessageBox.information(self, "Image Viewer",
                        "Cannot load %s." % fileName)
                return

            self.imageLabel.setPixmap(QPixmap.fromImage(image))
            self.scaleFactor = 1.0

            self.printAct.setEnabled(True)
            self.fitToWindowAct.setEnabled(True)
            self.updateActions()

            if not self.fitToWindowAct.isChecked():
                self.imageLabel.adjustSize()

    def print_(self):
        dialog = QPrintDialog(self.printer, self)
        if dialog.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.imageLabel.pixmap().size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
            painter.setWindow(self.imageLabel.pixmap().rect())
            painter.drawPixmap(0, 0, self.imageLabel.pixmap())

    def zoomIn(self):
        self.scaleImage(1.25)

    def zoomOut(self):
        self.scaleImage(0.8)

    def normalSize(self):
        self.imageLabel.adjustSize()
        self.scaleFactor = 1.0

    def fitToWindow(self):
        fitToWindow = self.fitToWindowAct.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.normalSize()
        self.updateActions()

    def faceDetect(self):
        try:
            face_cascade = cv2.CascadeClassifier(r"FaceRcognition-master\haarcascade_frontalface_default.xml")
            img = cv2.imread(self.directory)
            grey_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            
            faces = face_cascade.detectMultiScale(grey_img,
            scaleFactor=1.05,
            minNeighbors=5)
            c=0
            #crop=[]
            #print(type(faces))
            #print(faces)
            for x, y, w, h in faces:
                #crop.append(img[y:y+h,x:x+w])
                #crimg=cv2.circle(img, (x+int(h/2), y+int(w/2)), int(h/2), (0, 255, 0), 3)
                img = cv2.rectangle(img, (x,y), (x+w, y+w), (0,255,0), 2)
                c=c+1
            #a=0
            #print(c)
            # for cimg in crop:
            #     name="face detected image"+str(c)
            #     cv2.imshow(name, cimg)
            #     c+=1
            #self.imageLabel.setPixmap(img)
            #print(QDir.currentPath)
            cv2.imwrite("Detected.jpg",img)
            #print(os.getcwd())
            cdr=str(os.getcwd())+'\Detected.jpg'
            #pic = cdr[:-2]+"Detected.jpg'"
            image = QImage(cdr)
            if image.isNull():
                QMessageBox.information(self, "IOA Image Viewer",
                        "Cannot load %s." % cdr)
                return

            self.imageLabel.setPixmap(QPixmap.fromImage(image))
            # cv2.imshow("face detected image", img)
            # cv2.waitKey(0)
            # cv2.destroyAllWindows()
        except:
            pass
    
    def imgResize(self):
        try:
            img = cv2.imread(self.directory,1)
            oheight, owidth = img.shape[0:2]
            iheight, hokPressed = QInputDialog.getInt(self, "Get Height","Height :", 0, 0, int(oheight), 1)
            iwidth , okPressed = QInputDialog.getInt(self, "Get Width","Width :", 0, 0, int(owidth), 1)
            resizedImg = cv2.resize(img,(int(iwidth),int(iheight)))
            print(iheight)
            print(iwidth)
            cv2.imshow("Resized.jpg",resizedImg)
            cv2.imwrite("Resized.jpg",resizedImg)
            cdr=str(QDir.currentPath())+'/Resized.jpg'
            print(cdr)
            self.openImg(cdr)
            # cdr=str(os.getcwd())+'\\Resized.jpg'
            # Rimage = QImage(cdr)
            # if Rimage.isNull():
            #     QMessageBox.information(self, "IOA Image Viewer",
            #             "Cannot load %s." % cdr)
            #     return
            # self.imageLabel.setPixmap(QPixmap.fromImage(Rimage))
            # self.scaleFactor = 1.0
            # self.imageLabel.setScaledContents(True)
        except:
            pass


            # scale_percent = 100 # percent of original size
            # width = int(img.shape[1] * scale_percent / 100)
            # height = int(img.shape[0] * scale_percent / 100)
            # dim = (width, height)
            # resized = cv2.resize(img, dim)

    
    def imgRotate(self):
        try:
            colorImage  = Image.open(self.directory)
            transposed  = colorImage.transpose(Image.ROTATE_90)
            transposed = transposed.save('Rotated.jpg')
            cdr=str(os.getcwd())+'\\Rotated.jpg'
            image = QImage(cdr)
            if image.isNull():
                QMessageBox.information(self, "IOA Image Viewer",
                        "Cannot load %s." % cdr)
                return

            self.imageLabel.setPixmap(QPixmap.fromImage(image))
        except:
            pass

    def imgCropFace(self):
        try:
            img = cv2.imread(self.directory)
            face_cascade = cv2.CascadeClassifier(r"FaceRcognition-master\haarcascade_frontalface_default.xml")
            grey_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            faces = face_cascade.detectMultiScale(grey_img,
            scaleFactor=1.05,
            minNeighbors=5)
            c=0
            for x,y,w,h in faces:
                img = cv2.rectangle(img,(x,y),(x+w, y+w),(0,255,0),1)
                cropped = img[y:y+h, x:x+w]
            cv2.imwrite("Rotated.jpg",cropped)
            cdr=str(os.getcwd())+'\\Rotated.jpg'
            image = QImage(cdr)
            if image.isNull():
                QMessageBox.information(self, "IOA Image Viewer",
                        "Cannot load %s." % cdr)
                return
            self.imageLabel.setPixmap(QPixmap.fromImage(image))
        except:
            pass


    # def imgBlur(self):
    #     img = cv2.imread(self.directory)
    #     blur_image = cv2.GaussianBlur(img, (7,7), 0) 
        #scale 1,2,3,4,5



    # def imgEdge(self):
    #     edge_img = cv2.Canny(img,100,200)



    #def imgDenoise(self):
        # result = cv2.fastNlMeansDenoisingColored(img,None,20,10,7,21)

    def about(self):
        QMessageBox.about(self, "About Image Viewer",
                "<p>The <b>Image Viewer</b> example shows how to combine "
                "QLabel and QScrollArea to display an image. QLabel is "
                "typically used for displaying text, but it can also display "
                "an image. QScrollArea provides a scrolling view around "
                "another widget. If the child widget exceeds the size of the "
                "frame, QScrollArea automatically provides scroll bars.</p>"
                "<p>The example demonstrates how QLabel's ability to scale "
                "its contents (QLabel.scaledContents), and QScrollArea's "
                "ability to automatically resize its contents "
                "(QScrollArea.widgetResizable), can be used to implement "
                "zooming and scaling features.</p>"
                "<p>In addition the example shows how to use QPainter to "
                "print an image.</p>")

    def createActions(self):
        self.openAct = QAction("&Open...", self, shortcut="Ctrl+O",
                triggered=self.open)

        self.printAct = QAction("&Print...", self, shortcut="Ctrl+P",
                enabled=False, triggered=self.print_)

        self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q",
                triggered=self.close)

        self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++",
                enabled=False, triggered=self.zoomIn)

        self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-",
                enabled=False, triggered=self.zoomOut)

        self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S",
                enabled=False, triggered=self.normalSize)

        self.fitToWindowAct = QAction("&Fit to Window", self, enabled=False,
                checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow)

        self.aboutAct = QAction("&About", self, triggered=self.about)

        self.faceDetectAct = QAction("Face Detect",self,triggered=self.faceDetect,enabled=False)


        self.resizeAct = QAction("Resize",self,triggered=self.imgResize,enabled=False)

        self.rotateAct = QAction("Rotate", self,triggered=self.imgRotate, enabled=False)

    def createMenus(self):
        self.fileMenu = QMenu("&File", self)
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.printAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.fitToWindowAct)

        self.optionsMenu = QMenu("&Options", self)
        self.optionsMenu.addAction(self.faceDetectAct)
        self.optionsMenu.addAction(self.resizeAct)
        self.optionsMenu.addAction(self.rotateAct)

        self.helpMenu = QMenu("&Help", self)
        self.helpMenu.addAction(self.aboutAct)

        self.menuBar().addMenu(self.fileMenu)
        self.menuBar().addMenu(self.viewMenu)
        self.menuBar().addMenu(self.optionsMenu)
        self.menuBar().addMenu(self.helpMenu)
        

    def updateActions(self):
        self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.resizeAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.faceDetectAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.rotateAct.setEnabled(not self.fitToWindowAct.isChecked())

    def scaleImage(self, factor):
        self.scaleFactor *= factor
        self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size())

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)

        self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
        self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)

    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(int(factor * scrollBar.value()
                                + ((factor - 1) * scrollBar.pageStep()/2)))
Exemplo n.º 19
0
class EditorBar(QToolBar):
    saveDocAsSignal = pyqtSignal()
    spellSignal = pyqtSignal(bool)
    whiteSpaceSignal = pyqtSignal(bool)
    boldSignal = pyqtSignal(bool)
    italicSignal = pyqtSignal(bool)
    underlineSignal = pyqtSignal(bool)
    strikethroughSignal = pyqtSignal(bool)
    subscriptSignal = pyqtSignal(bool)
    superscriptSignal = pyqtSignal(bool)

    def __init__(self, parent = None):
        QToolBar.__init__(self,  parent)
        self.setWindowTitle('EditorBar')
        self.setIconSize(QSize(16, 16))
        self.createActions()

    def createActions(self):
        self.settingsAction = QAction(self.tr("Settings"), self)
        self.settingsAction.setIcon(QIcon(":/icons/icons/configure.png"))
        self.settingsAction.triggered.connect(self.settings)
        self.addAction(self.settingsAction)

        self.saveDocAsAction = QAction(self.tr("Save As"), self)
        self.saveDocAsAction.triggered.connect(self.SaveDocumentAs)
        self.saveDocAsAction.setIcon(QIcon(":/icons/icons/filesave.png"))
        self.addAction(self.saveDocAsAction)

        self.spellAction = QAction(self.tr("Spellchecking"), self)
        self.spellAction.setIcon(
            QIcon(":/icons/icons/tools-check-spelling.png"))
        self.spellAction.setCheckable(True)
        self.spellAction.setChecked(settings.get('editor:spell'))
        self.spellAction.toggled.connect(self.spell)
        self.insertSeparator(self.spellAction)
        self.addAction(self.spellAction)

        self.whiteSpaceAction = QAction(self.tr("Show whitespace"), self)
        self.whiteSpaceAction.setIcon(
            QIcon(":/icons/icons/whiteSpace.png"))
        self.whiteSpaceAction.setCheckable(True)
        self.whiteSpaceAction.setChecked(settings.get('editor:whiteSpace'))
        self.whiteSpaceAction.toggled.connect(self.whiteSpace)
        self.addAction(self.whiteSpaceAction)

        self.BoldAction = QAction(
                QIcon(":/icons/icons/format-text-bold.png"),
                self.tr("&Bold"), self,
                shortcut=Qt.CTRL + Qt.Key_B,
                triggered=self.bold, checkable=True)
        self.addAction(self.BoldAction)

        self.ItalicAction = QAction(self.tr("Italic"), self)
        self.ItalicAction.setIcon(
            QIcon(":/icons/icons/format-text-italic.png"))
        self.ItalicAction.setShortcut(Qt.CTRL + Qt.Key_I)
        self.ItalicAction.setCheckable(True)
        self.ItalicAction.triggered.connect(self.italic)
        self.addAction(self.ItalicAction)

        self.UnderlineAction = QAction(self.tr("Underline"), self)
        self.UnderlineAction.setIcon(
            QIcon(":/icons/icons/format-text-underline.png"))
        self.UnderlineAction.setCheckable(True)
        self.UnderlineAction.setShortcut(Qt.CTRL + Qt.Key_U)
        self.UnderlineAction.triggered.connect(self.underline)
        self.addAction(self.UnderlineAction)

        self.StrikethroughAction = QAction(self.tr("Strikethrough"), self)
        self.StrikethroughAction.setIcon(
            QIcon(":/icons/icons/format-text-strikethrough.png"))
        self.StrikethroughAction.setCheckable(True)
        self.StrikethroughAction.triggered.connect(self.strikethrough)
        self.addAction(self.StrikethroughAction)

        self.SubscriptAction = QAction(self.tr("Subscript"), self)
        self.SubscriptAction.setIcon(
            QIcon(":/icons/icons/format-text-subscript.png"))
        self.SubscriptAction.setCheckable(True)
        self.SubscriptAction.triggered.connect(self.subscript)
        self.addAction(self.SubscriptAction)

        self.SuperscriptAction = QAction(self.tr("Superscript"), self)
        self.SuperscriptAction.setIcon(
            QIcon(":/icons/icons/format-text-superscript.png"))
        self.SuperscriptAction.setCheckable(True)
        self.SuperscriptAction.triggered.connect(self.superscript)
        self.addAction(self.SuperscriptAction)

    def settings(self):
        lectorSettings = Settings(self, 1)
#        QObject.connect(lectorSettings, SIGNAL('accepted()'),
#                    self.updateTextEditor)
        lectorSettings.settingAccepted.connect(self.resetSpell)
        lectorSettings.show()

    def SaveDocumentAs(self):
        self.saveDocAsSignal.emit()

    def spell(self):
        state = self.spellAction.isChecked()
        self.spellSignal.emit(state)

    def resetSpell(self):
        '''
        Turn off and on spellcheckig to use correct dictionary
        '''
        state = self.spellAction.isChecked()
        if state:
            self.spellSignal.emit(False)
            self.spellSignal.emit(state)

    def whiteSpace(self):
        state = self.whiteSpaceAction.isChecked()
        self.whiteSpaceSignal.emit(state)

    def toggleFormat(self, CharFormat):
        font = CharFormat.font()
        self.BoldAction.setChecked(font.bold())
        self.ItalicAction.setChecked(font.italic())
        self.UnderlineAction.setChecked(font.underline())
        self.StrikethroughAction.setChecked(CharFormat.fontStrikeOut())
        if CharFormat.verticalAlignment() == \
                QTextCharFormat.AlignSuperScript:
            self.SubscriptAction.setChecked(False)
            self.SuperscriptAction.setChecked(True)
        elif CharFormat.verticalAlignment() == \
                QTextCharFormat.AlignSubScript:
            self.SubscriptAction.setChecked(True)
            self.SuperscriptAction.setChecked(False)
        else:
            self.SubscriptAction.setChecked(False)
            self.SuperscriptAction.setChecked(False)

    def bold(self):
        state = self.BoldAction.isChecked()
        self.boldSignal.emit(state)

    def italic(self):
        state = self.ItalicAction.isChecked()
        self.italicSignal.emit(state)

    def underline(self):
        state = self.UnderlineAction.isChecked()
        self.underlineSignal.emit(state)

    def strikethrough(self):
        state = self.StrikethroughAction.isChecked()
        self.strikethroughSignal.emit(state)

    def subscript(self):
        state = self.SubscriptAction.isChecked()
        self.subscriptSignal.emit(state)

    def superscript(self):
        state = self.SuperscriptAction.isChecked()
        self.superscriptSignal.emit(state)
Exemplo n.º 20
0
class UserAgentMenu(QMenu):
    """
    Class implementing a menu to select the user agent string.
    """
    def __init__(self, title, url=None, parent=None):
        """
        Constructor
        
        @param title title of the menu (string)
        @param url URL to set user agent for (QUrl)
        @param parent reference to the parent widget (QWidget)
        """
        super(UserAgentMenu, self).__init__(title, parent)
        
        self.__manager = None
        self.__url = url
        if self.__url:
            if self.__url.isValid():
                import Helpviewer.HelpWindow
                self.__manager = \
                    Helpviewer.HelpWindow.HelpWindow.userAgentsManager()
            else:
                self.__url = None
        
        self.aboutToShow.connect(self.__populateMenu)
    
    def __populateMenu(self):
        """
        Private slot to populate the menu.
        """
        self.aboutToShow.disconnect(self.__populateMenu)
        
        self.__actionGroup = QActionGroup(self)
        
        # add default action
        self.__defaultUserAgent = QAction(self)
        self.__defaultUserAgent.setText(self.tr("Default"))
        self.__defaultUserAgent.setCheckable(True)
        self.__defaultUserAgent.triggered.connect(
            self.__switchToDefaultUserAgent)
        if self.__url:
            self.__defaultUserAgent.setChecked(
                self.__manager.userAgentForUrl(self.__url) == "")
        else:
            from Helpviewer.HelpBrowserWV import HelpWebPage
            self.__defaultUserAgent.setChecked(HelpWebPage().userAgent() == "")
        self.addAction(self.__defaultUserAgent)
        self.__actionGroup.addAction(self.__defaultUserAgent)
        isChecked = self.__defaultUserAgent.isChecked()
        
        # add default extra user agents
        isChecked = self.__addDefaultActions() or isChecked
        
        # add other action
        self.addSeparator()
        self.__otherUserAgent = QAction(self)
        self.__otherUserAgent.setText(self.tr("Other..."))
        self.__otherUserAgent.setCheckable(True)
        self.__otherUserAgent.triggered.connect(
            self.__switchToOtherUserAgent)
        self.addAction(self.__otherUserAgent)
        self.__actionGroup.addAction(self.__otherUserAgent)
        self.__otherUserAgent.setChecked(not isChecked)
    
    def __switchToDefaultUserAgent(self):
        """
        Private slot to set the default user agent.
        """
        if self.__url:
            self.__manager.removeUserAgent(self.__url.host())
        else:
            from Helpviewer.HelpBrowserWV import HelpWebPage
            HelpWebPage().setUserAgent("")
    
    def __switchToOtherUserAgent(self):
        """
        Private slot to set a custom user agent string.
        """
        from Helpviewer.HelpBrowserWV import HelpWebPage
        userAgent, ok = QInputDialog.getText(
            self,
            self.tr("Custom user agent"),
            self.tr("User agent:"),
            QLineEdit.Normal,
            HelpWebPage().userAgent(resolveEmpty=True))
        if ok:
            if self.__url:
                self.__manager.setUserAgentForUrl(self.__url, userAgent)
            else:
                HelpWebPage().setUserAgent(userAgent)
    
    def __changeUserAgent(self):
        """
        Private slot to change the user agent.
        """
        act = self.sender()
        if self.__url:
            self.__manager.setUserAgentForUrl(self.__url, act.data())
        else:
            from Helpviewer.HelpBrowserWV import HelpWebPage
            HelpWebPage().setUserAgent(act.data())
    
    def __addDefaultActions(self):
        """
        Private slot to add the default user agent entries.
        
        @return flag indicating that a user agent entry is checked (boolean)
        """
        from . import UserAgentDefaults_rc              # __IGNORE_WARNING__
        defaultUserAgents = QFile(":/UserAgentDefaults.xml")
        defaultUserAgents.open(QIODevice.ReadOnly)
        
        menuStack = []
        isChecked = False
        
        if self.__url:
            currentUserAgentString = self.__manager.userAgentForUrl(self.__url)
        else:
            from Helpviewer.HelpBrowserWV import HelpWebPage
            currentUserAgentString = HelpWebPage().userAgent()
        xml = QXmlStreamReader(defaultUserAgents)
        while not xml.atEnd():
            xml.readNext()
            if xml.isStartElement() and xml.name() == "separator":
                if menuStack:
                    menuStack[-1].addSeparator()
                else:
                    self.addSeparator()
                continue
            
            if xml.isStartElement() and xml.name() == "useragent":
                attributes = xml.attributes()
                title = attributes.value("description")
                userAgent = attributes.value("useragent")
                
                act = QAction(self)
                act.setText(title)
                act.setData(userAgent)
                act.setToolTip(userAgent)
                act.setCheckable(True)
                act.setChecked(userAgent == currentUserAgentString)
                act.triggered.connect(self.__changeUserAgent)
                if menuStack:
                    menuStack[-1].addAction(act)
                else:
                    self.addAction(act)
                self.__actionGroup.addAction(act)
                isChecked = isChecked or act.isChecked()
            
            if xml.isStartElement() and xml.name() == "useragentmenu":
                attributes = xml.attributes()
                title = attributes.value("title")
                if title == "v_a_r_i_o_u_s":
                    title = self.tr("Various")
                
                menu = QMenu(self)
                menu.setTitle(title)
                self.addMenu(menu)
                menuStack.append(menu)
            
            if xml.isEndElement() and xml.name() == "useragentmenu":
                menuStack.pop()
        
        if xml.hasError():
            E5MessageBox.critical(
                self,
                self.tr("Parsing default user agents"),
                self.tr(
                    """<p>Error parsing default user agents.</p><p>{0}</p>""")
                .format(xml.errorString()))
        
        return isChecked
Exemplo n.º 21
0
class SimulationGui(QMainWindow):
    """
    class for the graphical user interface
    """
    # TODO enable closing plot docks by right-clicking their name

    runSimulation = pyqtSignal()
    stopSimulation = pyqtSignal()
    playbackTimeChanged = pyqtSignal()
    regimeFinished = pyqtSignal()
    finishedRegimeBatch = pyqtSignal(bool)

    def __init__(self):
        # constructor of the base class
        QMainWindow.__init__(self)

        QCoreApplication.setOrganizationName("RST")
        QCoreApplication.setOrganizationDomain("https://tu-dresden.de/rst")
        QCoreApplication.setApplicationVersion(
            pkg_resources.require("PyMoskito")[0].version)
        QCoreApplication.setApplicationName(globals()["__package__"])

        # load settings
        self._settings = QSettings()
        self._read_settings()

        # initialize logger
        self._logger = logging.getLogger(self.__class__.__name__)

        # Create Simulation Backend
        self.guiProgress = None
        self.cmdProgress = None
        self.sim = SimulatorInteractor(self)
        self.runSimulation.connect(self.sim.run_simulation)
        self.stopSimulation.connect(self.sim.stop_simulation)
        self.sim.simulation_finalized.connect(self.new_simulation_data)
        self.currentDataset = None
        self.interpolator = None

        # sim setup viewer
        self.targetView = SimulatorView(self)
        self.targetView.setModel(self.sim.target_model)
        self.targetView.expanded.connect(self.target_view_changed)
        self.targetView.collapsed.connect(self.target_view_changed)

        # sim results viewer
        self.result_view = QTreeView()

        # the docking area allows to rearrange the user interface at runtime
        self.area = pg.dockarea.DockArea()

        # Window properties
        icon_size = QSize(25, 25)
        self.setCentralWidget(self.area)
        self.resize(1000, 700)
        self.setWindowTitle("PyMoskito")
        res_path = get_resource("mosquito.png")
        icon = QIcon(res_path)
        self.setWindowIcon(icon)

        # create docks
        self.propertyDock = pg.dockarea.Dock("Properties")
        self.animationDock = pg.dockarea.Dock("Animation")
        self.regimeDock = pg.dockarea.Dock("Regimes")
        self.dataDock = pg.dockarea.Dock("Data")
        self.logDock = pg.dockarea.Dock("Log")
        self.plotDockPlaceholder = pg.dockarea.Dock("Placeholder")

        # arrange docks
        self.area.addDock(self.animationDock, "right")
        self.area.addDock(self.regimeDock, "left", self.animationDock)
        self.area.addDock(self.propertyDock, "bottom", self.regimeDock)
        self.area.addDock(self.dataDock, "bottom", self.propertyDock)
        self.area.addDock(self.plotDockPlaceholder, "bottom", self.animationDock)
        self.area.addDock(self.logDock, "bottom", self.dataDock)
        self.non_plotting_docks = list(self.area.findAll()[1].keys())

        # add widgets to the docks
        self.propertyDock.addWidget(self.targetView)

        if not vtk_available:
            self._logger.error("loading vtk failed with:{}".format(vtk_error_msg))

        # check if there is a registered visualizer
        available_vis = get_registered_visualizers()
        self._logger.info("found visualizers: {}".format(
            [name for cls, name in available_vis]))
        if available_vis:
            # instantiate the first visualizer
            self._logger.info("loading visualizer '{}'".format(available_vis[0][1]))
            self.animationLayout = QVBoxLayout()

            if issubclass(available_vis[0][0], MplVisualizer):
                self.animationWidget = QWidget()
                self.visualizer = available_vis[0][0](self.animationWidget,
                                                      self.animationLayout)
                self.animationDock.addWidget(self.animationWidget)
            elif issubclass(available_vis[0][0], VtkVisualizer):
                if vtk_available:
                    # vtk window
                    self.animationFrame = QFrame()
                    self.vtkWidget = QVTKRenderWindowInteractor(
                        self.animationFrame)
                    self.animationLayout.addWidget(self.vtkWidget)
                    self.animationFrame.setLayout(self.animationLayout)
                    self.animationDock.addWidget(self.animationFrame)
                    self.vtk_renderer = vtkRenderer()
                    self.vtkWidget.GetRenderWindow().AddRenderer(
                        self.vtk_renderer)
                    self.visualizer = available_vis[0][0](self.vtk_renderer)
                    self.vtkWidget.Initialize()
                else:
                    self._logger.warning("visualizer depends on vtk which is "
                                         "not available on this system!")
            elif available_vis:
                raise NotImplementedError
        else:
            self.visualizer = None

        # regime window
        self.regime_list = QListWidget(self)
        self.regime_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.regimeDock.addWidget(self.regime_list)
        self.regime_list.itemDoubleClicked.connect(self.regime_dclicked)
        self._regimes = []
        self.regime_file_name = ""

        self.actDeleteRegimes = QAction(self.regime_list)
        self.actDeleteRegimes.setText("&Delete Selected Regimes")
        # TODO shortcut works always, not only with focus on the regime list
        # self.actDeleteRegimes.setShortcutContext(Qt.WindowShortcut)
        self.actDeleteRegimes.setShortcut(QKeySequence(Qt.Key_Delete))
        self.actDeleteRegimes.triggered.connect(self.remove_regime_items)

        self.actSave = QAction(self)
        self.actSave.setText('Save Results As')
        self.actSave.setIcon(QIcon(get_resource("save.png")))
        self.actSave.setDisabled(True)
        self.actSave.setShortcut(QKeySequence.Save)
        self.actSave.triggered.connect(self.export_simulation_data)

        self.actLoadRegimes = QAction(self)
        self.actLoadRegimes.setText("Load Regimes from File")
        self.actLoadRegimes.setIcon(QIcon(get_resource("load.png")))
        self.actLoadRegimes.setDisabled(False)
        self.actLoadRegimes.setShortcut(QKeySequence.Open)
        self.actLoadRegimes.triggered.connect(self.load_regime_dialog)

        self.actExitOnBatchCompletion = QAction(self)
        self.actExitOnBatchCompletion.setText("&Exit On Batch Completion")
        self.actExitOnBatchCompletion.setCheckable(True)
        self.actExitOnBatchCompletion.setChecked(
            self._settings.value("control/exit_on_batch_completion") == "True"
        )
        self.actExitOnBatchCompletion.changed.connect(
            self.update_exit_on_batch_completion_setting)

        # regime management
        self.runningBatch = False
        self._current_regime_index = None
        self._current_regime_name = None
        self._regimes = []

        self.regimeFinished.connect(self.run_next_regime)
        self.finishedRegimeBatch.connect(self.regime_batch_finished)

        # data window
        self.dataList = QListWidget(self)
        self.dataDock.addWidget(self.dataList)
        self.dataList.itemDoubleClicked.connect(self.create_plot)

        # actions for simulation control
        self.actSimulateCurrent = QAction(self)
        self.actSimulateCurrent.setText("&Simulate Current Regime")
        self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png")))
        self.actSimulateCurrent.setShortcut(QKeySequence("F5"))
        self.actSimulateCurrent.triggered.connect(self.start_simulation)

        self.actSimulateAll = QAction(self)
        self.actSimulateAll.setText("Simulate &All Regimes")
        self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png")))
        self.actSimulateAll.setShortcut(QKeySequence("F6"))
        self.actSimulateAll.setDisabled(True)
        self.actSimulateAll.triggered.connect(self.start_regime_execution)

        # actions for animation control
        self.actAutoPlay = QAction(self)
        self.actAutoPlay.setText("&Autoplay Simulation")
        self.actAutoPlay.setCheckable(True)
        self.actAutoPlay.setChecked(
            self._settings.value("control/autoplay_animation") == "True"
        )
        self.actAutoPlay.changed.connect(self.update_autoplay_setting)

        self.actPlayPause = QAction(self)
        self.actPlayPause.setText("Play Animation")
        self.actPlayPause.setIcon(QIcon(get_resource("play.png")))
        self.actPlayPause.setDisabled(True)
        self.actPlayPause.setShortcut(QKeySequence(Qt.Key_Space))
        self.actPlayPause.triggered.connect(self.play_animation)

        self.actStop = QAction(self)
        self.actStop.setText("Stop")
        self.actStop.setIcon(QIcon(get_resource("stop.png")))
        self.actStop.setDisabled(True)
        self.actStop.triggered.connect(self.stop_animation)

        self.actSlow = QAction(self)
        self.actSlow.setText("Slowest")
        self.actSlow.setIcon(QIcon(get_resource("slow.png")))
        self.actSlow.setDisabled(False)
        self.actSlow.triggered.connect(self.set_slowest_playback_speed)

        self.actFast = QAction(self)
        self.actFast.setText("Fastest")
        self.actFast.setIcon(QIcon(get_resource("fast.png")))
        self.actFast.setDisabled(False)
        self.actFast.triggered.connect(self.set_fastest_playback_speed)

        self.speedControl = QSlider(Qt.Horizontal, self)
        self.speedControl.setMaximumSize(200, 25)
        self.speedControl.setTickPosition(QSlider.TicksBothSides)
        self.speedControl.setDisabled(False)
        self.speedControl.setMinimum(0)
        self.speedControl.setMaximum(12)
        self.speedControl.setValue(6)
        self.speedControl.setTickInterval(6)
        self.speedControl.setSingleStep(2)
        self.speedControl.setPageStep(3)
        self.speedControl.valueChanged.connect(self.update_playback_speed)

        self.timeSlider = QSlider(Qt.Horizontal, self)
        self.timeSlider.setMinimum(0)
        self.timeSliderRange = 1000
        self.timeSlider.setMaximum(self.timeSliderRange)
        self.timeSlider.setTickInterval(1)
        self.timeSlider.setTracking(True)
        self.timeSlider.setDisabled(True)
        self.timeSlider.valueChanged.connect(self.update_playback_time)

        self.playbackTime = .0
        self.playbackGain = 1
        self.currentStepSize = .0
        self.currentEndTime = .0
        self.playbackTimer = QTimer()
        self.playbackTimer.timeout.connect(self.increment_playback_time)
        self.playbackTimeChanged.connect(self.update_gui)
        self.playbackTimeout = 33  # in [ms] -> 30 fps

        self.actResetCamera = QAction(self)
        self.actResetCamera.setText("Reset Camera")
        self.actResetCamera.setIcon(QIcon(get_resource("reset_camera.png")))
        self.actResetCamera.setDisabled(True)
        if available_vis:
            self.actResetCamera.setEnabled(self.visualizer.can_reset_view)
        self.actResetCamera.triggered.connect(self.reset_camera_clicked)

        # postprocessing
        self.actPostprocessing = QAction(self)
        self.actPostprocessing.setText("Launch Postprocessor")
        self.actPostprocessing.setIcon(QIcon(get_resource("processing.png")))
        self.actPostprocessing.setDisabled(False)
        self.actPostprocessing.triggered.connect(self.postprocessing_clicked)
        self.actPostprocessing.setShortcut(QKeySequence("F7"))

        self.postprocessor = None

        # toolbar
        self.toolbarSim = QToolBar("Simulation")
        self.toolbarSim.setContextMenuPolicy(Qt.PreventContextMenu)
        self.toolbarSim.setMovable(False)
        self.toolbarSim.setIconSize(icon_size)
        self.addToolBar(self.toolbarSim)
        self.toolbarSim.addAction(self.actLoadRegimes)
        self.toolbarSim.addAction(self.actSave)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actSimulateCurrent)
        self.toolbarSim.addAction(self.actSimulateAll)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actPlayPause)
        self.toolbarSim.addAction(self.actStop)
        self.toolbarSim.addWidget(self.timeSlider)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actSlow)
        self.toolbarSim.addWidget(self.speedControl)
        self.toolbarSim.addAction(self.actFast)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actPostprocessing)
        self.toolbarSim.addAction(self.actResetCamera)
        self.postprocessor = None

        # log dock
        self.logBox = QPlainTextEdit(self)
        self.logBox.setReadOnly(True)
        self.logDock.addWidget(self.logBox)

        # init logger for logging box
        self.textLogger = PlainTextLogger(logging.INFO)
        self.textLogger.set_target_cb(self.logBox.appendPlainText)
        logging.getLogger().addHandler(self.textLogger)

        # menu bar
        fileMenu = self.menuBar().addMenu("&File")
        fileMenu.addAction(self.actLoadRegimes)
        fileMenu.addAction(self.actSave)
        fileMenu.addAction("&Quit", self.close)

        editMenu = self.menuBar().addMenu("&Edit")
        editMenu.addAction(self.actDeleteRegimes)

        simMenu = self.menuBar().addMenu("&Simulation")
        simMenu.addAction(self.actSimulateCurrent)
        simMenu.addAction(self.actSimulateAll)
        simMenu.addAction(self.actExitOnBatchCompletion)
        simMenu.addAction(self.actPostprocessing)

        animMenu = self.menuBar().addMenu("&Animation")
        animMenu.addAction(self.actPlayPause)
        animMenu.addAction("&Increase Playback Speed",
                           self.increment_playback_speed,
                           QKeySequence(Qt.CTRL + Qt.Key_Plus))
        animMenu.addAction("&Decrease Playback Speed",
                           self.decrement_playback_speed,
                           QKeySequence(Qt.CTRL + Qt.Key_Minus))
        animMenu.addAction("&Reset Playback Speed",
                           self.reset_playback_speed,
                           QKeySequence(Qt.CTRL + Qt.Key_0))
        animMenu.addAction(self.actAutoPlay)
        animMenu.addAction(self.actResetCamera)

        helpMenu = self.menuBar().addMenu("&Help")
        helpMenu.addAction("&Online Documentation", self.show_online_docs)
        helpMenu.addAction("&About", self.show_info)

        # status bar
        self.status = QStatusBar(self)
        self.setStatusBar(self.status)
        self.statusLabel = QLabel("Ready.")
        self.statusBar().addPermanentWidget(self.statusLabel)
        self.timeLabel = QLabel("current time: 0.0")
        self.statusBar().addPermanentWidget(self.timeLabel)

        self._logger.info("Simulation GUI is up and running.")

    def _read_settings(self):

        # add default settings if none are present
        if not self._settings.contains("path/simulation_results"):
            self._settings.setValue("path/simulation_results",
                                    os.path.join(os.path.curdir,
                                                 "results",
                                                 "simulation"))
        if not self._settings.contains("path/postprocessing_results"):
            self._settings.setValue("path/postprocessing_results",
                                    os.path.join(os.path.curdir,
                                                 "results",
                                                 "postprocessing"))
        if not self._settings.contains("path/metaprocessing_results"):
            self._settings.setValue("path/metaprocessing_results",
                                    os.path.join(os.path.curdir,
                                                 "results",
                                                 "metaprocessing"))

        if not self._settings.contains("control/autoplay_animation"):
            self._settings.setValue("control/autoplay_animation", "False")

        if not self._settings.contains("control/exit_on_batch_completion"):
            self._settings.setValue("control/exit_on_batch_completion", "False")

    def _write_settings(self):
        """ Store the application state. """
        pass

    @pyqtSlot()
    def update_autoplay_setting(self):
        self._settings.setValue("control/autoplay_animation",
                                str(self.actAutoPlay.isChecked()))

    @pyqtSlot()
    def update_exit_on_batch_completion_setting(self, state=None):
        if state is None:
            state = self.actExitOnBatchCompletion.isChecked()
        self._settings.setValue("control/exit_on_batch_completion", str(state))

    def set_visualizer(self, vis):
        self.visualizer = vis
        self.vtkWidget.Initialize()

    @pyqtSlot()
    def play_animation(self):
        """
        play the animation
        """
        self._logger.debug("Starting Playback")

        # if we are at the end, start from the beginning
        if self.playbackTime == self.currentEndTime:
            self.timeSlider.setValue(0)

        self.actPlayPause.setText("Pause Animation")
        self.actPlayPause.setIcon(QIcon(get_resource("pause.png")))
        self.actPlayPause.triggered.disconnect(self.play_animation)
        self.actPlayPause.triggered.connect(self.pause_animation)
        self.playbackTimer.start(self.playbackTimeout)

    @pyqtSlot()
    def pause_animation(self):
        """
        pause the animation
        """
        self._logger.debug("Pausing Playback")
        self.playbackTimer.stop()
        self.actPlayPause.setText("Play Animation")
        self.actPlayPause.setIcon(QIcon(get_resource("play.png")))
        self.actPlayPause.triggered.disconnect(self.pause_animation)
        self.actPlayPause.triggered.connect(self.play_animation)

    def stop_animation(self):
        """
        Stop the animation if it is running and reset the playback time.
        """
        self._logger.debug("Stopping Playback")
        if self.actPlayPause.text() == "Pause Animation":
            # animation is playing -> stop it
            self.playbackTimer.stop()
            self.actPlayPause.setText("Play Animation")
            self.actPlayPause.setIcon(QIcon(get_resource("play.png")))
            self.actPlayPause.triggered.disconnect(self.pause_animation)
            self.actPlayPause.triggered.connect(self.play_animation)

        self.timeSlider.setValue(0)

    @pyqtSlot()
    def start_simulation(self):
        """
        start the simulation and disable start button
        """
        if self._current_regime_index is None:
            regime_name = ""
        else:
            regime_name = str(self.regime_list.item(
                self._current_regime_index).text())

        self.statusLabel.setText("simulating {}".format(regime_name))
        self._logger.info("Simulating: {}".format(regime_name))

        self.actSimulateCurrent.setIcon(QIcon(
            get_resource("stop_simulation.png")))
        self.actSimulateCurrent.setText("Abort &Simulation")
        self.actSimulateCurrent.triggered.disconnect(self.start_simulation)
        self.actSimulateCurrent.triggered.connect(self.stop_simulation)

        if not self.runningBatch:
            self.actSimulateAll.setDisabled(True)

        self.guiProgress = QProgressBar(self)
        self.sim.simulationProgressChanged.connect(self.guiProgress.setValue)
        self.statusBar().addWidget(self.guiProgress)
        self.runSimulation.emit()

    @pyqtSlot()
    def stop_simulation(self):
        self.stopSimulation.emit()

    def export_simulation_data(self, ok):
        """
        Query the user for a custom name and export the current simulation
        results.

        :param ok: unused parameter from QAction.triggered() Signal
        """
        self._save_data()

    def _save_data(self, file_path=None):
        """
        Save the current simulation results.

        If *fie_name* is given, the result will be saved to the specified
        location, making automated exporting easier.

        Args:
            file_path(str): Absolute path of the target file. If `None` the
                use will be asked for a storage location.
        """
        regime_name = self._regimes[self._current_regime_index]["Name"]

        if file_path is None:
            # get default path
            path = self._settings.value("path/simulation_results")

            # create canonic file name
            suggestion = self._simfile_name(regime_name)
        else:
            path = os.path.dirname(file_path)
            suggestion = os.path.basename(file_path)

        # check if path exists otherwise create it
        if not os.path.isdir(path):
            box = QMessageBox()
            box.setText("Export Folder does not exist yet.")
            box.setInformativeText("Do you want to create it? \n"
                                   "{}".format(os.path.abspath(path)))
            box.setStandardButtons(QMessageBox.Ok | QMessageBox.No)
            box.setDefaultButton(QMessageBox.Ok)
            ret = box.exec_()
            if ret == QMessageBox.Ok:
                os.makedirs(path)
            else:
                path = os.path.abspath(os.path.curdir)
                file_path = None

        # If no path was given, present the default and let the user choose
        if file_path is None:
            dialog = QFileDialog(self)
            dialog.setAcceptMode(QFileDialog.AcceptSave)
            dialog.setFileMode(QFileDialog.AnyFile)
            dialog.setDirectory(path)
            dialog.setNameFilter("PyMoskito Results (*.pmr)")
            dialog.selectFile(suggestion)

            if dialog.exec_():
                file_path = dialog.selectedFiles()[0]
            else:
                self._logger.warning("Export Aborted")
                return -1

        # ask whether this should act as new default
        path = os.path.abspath(os.path.dirname(file_path))
        if path != self._settings.value("path/simulation_results"):
            box = QMessageBox()
            box.setText("Use this path as new default?")
            box.setInformativeText("{}".format(path))
            box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            box.setDefaultButton(QMessageBox.Yes)
            ret = box.exec_()
            if ret == QMessageBox.Yes:
                self._settings.setValue("path/simulation_results", path)

        self.currentDataset.update({"regime name": regime_name})
        with open(file_path, "wb") as f:
            pickle.dump(self.currentDataset, f, protocol=4)

        self.statusLabel.setText("results saved to {}".format(file_path))
        self._logger.info("results saved to {}".format(file_path))

    def _simfile_name(self, regime_name):
        """ Create a canonical name for a simulation result file
        """
        suggestion = (time.strftime("%Y%m%d-%H%M%S")
                      + "_" + regime_name + ".pmr")
        return suggestion

    def load_regime_dialog(self):
        regime_path = os.path.join(os.curdir)

        dialog = QFileDialog(self)
        dialog.setFileMode(QFileDialog.ExistingFile)
        dialog.setDirectory(regime_path)
        dialog.setNameFilter("Simulation Regime files (*.sreg)")

        if dialog.exec_():
            file = dialog.selectedFiles()[0]
            self.load_regimes_from_file(file)

    def load_regimes_from_file(self, file_name):
        """
        load simulation regime from file
        :param file_name:
        """
        self.regime_file_name = os.path.split(file_name)[-1][:-5]
        self._logger.info("loading regime file: {0}".format(self.regime_file_name))
        with open(file_name.encode(), "r") as f:
            self._regimes += yaml.load(f)

        self._update_regime_list()

        if self._regimes:
            self.actSimulateAll.setDisabled(False)

        self._logger.info("loaded {} regimes".format(len(self._regimes)))
        self.statusBar().showMessage("loaded {} regimes.".format(len(self._regimes)), 1000)
        return

    def _update_regime_list(self):
        self.regime_list.clear()
        for reg in self._regimes:
            self._logger.debug("adding '{}' to regime list".format(reg["Name"]))
            self.regime_list.addItem(reg["Name"])

    def remove_regime_items(self):
        if self.regime_list.currentRow() >= 0:
            # flag all selected files as invalid
            items = self.regime_list.selectedItems()
            for item in items:
                del self._regimes[self.regime_list.row(item)]
                self.regime_list.takeItem(self.regime_list.row(item))

    @pyqtSlot(QListWidgetItem)
    def regime_dclicked(self, item):
        """
        Apply the selected regime to the current target.
        """
        self.apply_regime_by_name(str(item.text()))

    def apply_regime_by_name(self, regime_name):
        """
        Apply the regime given by `regime_name` und update the regime index.

        Returns:
            bool: `True` if successful, `False` if errors occurred.
        """
        # get regime idx
        try:
            idx = list(map(itemgetter("Name"), self._regimes)).index(regime_name)
        except ValueError as e:
            self._logger.error("apply_regime_by_name(): Error no regime called "
                               "'{0}'".format(regime_name))
            return False

        # apply
        return self._apply_regime_by_idx(idx)

    def _apply_regime_by_idx(self, index=0):
        """
        Apply the given regime.

        Args:
            index(int): Index of the regime in the `RegimeList` .

        Returns:
            bool: `True` if successful, `False` if errors occurred.
        """
        if index >= len(self._regimes):
            self._logger.error("applyRegime: index error! ({})".format(index))
            return False

        reg_name = self._regimes[index]["Name"]
        self.statusBar().showMessage("regime {} applied.".format(reg_name),
                                     1000)
        self._logger.info("applying regime '{}'".format(reg_name))

        self._current_regime_index = index
        self._current_regime_name = reg_name

        return self.sim.set_regime(self._regimes[index])

    @pyqtSlot()
    def start_regime_execution(self):
        """
        Simulate all regimes in the regime list.
        """
        self.actSimulateAll.setText("Stop Simulating &All Regimes")
        self.actSimulateAll.setIcon(QIcon(get_resource("stop_batch.png")))
        self.actSimulateAll.triggered.disconnect(self.start_regime_execution)
        self.actSimulateAll.triggered.connect(self.stop_regime_excecution)

        self.runningBatch = True
        self._current_regime_index = -1
        self.regimeFinished.emit()

    def run_next_regime(self):
        """
        Execute the next regime in the regime batch.
        """
        # are we finished?
        if self._current_regime_index == len(self._regimes) - 1:
            self.finishedRegimeBatch.emit(True)
            return

        suc = self._apply_regime_by_idx(self._current_regime_index + 1)
        if not suc:
            self.finishedRegimeBatch.emit(False)
            return

        self.start_simulation()

    @pyqtSlot()
    def stop_regime_excecution(self):
        """ Stop the batch process.
        """
        self.stopSimulation.emit()
        self.finishedRegimeBatch.emit(False)

    def regime_batch_finished(self, status):
        self.runningBatch = False
        self.actSimulateAll.setDisabled(False)
        self.actSave.setDisabled(True)

        self.actSimulateAll.setText("Simulate &All Regimes")
        self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png")))
        self.actSimulateAll.triggered.disconnect(self.stop_regime_excecution)
        self.actSimulateAll.triggered.connect(self.start_regime_execution)

        if status:
            self.statusLabel.setText("All regimes have been simulated")
            self._logger.info("All Regimes have been simulated")
        else:
            self._logger.error("Batch simulation has been aborted")

        if self._settings.value("control/exit_on_batch_completion") == "True":
            self._logger.info("Shutting down SimulationGUI")
            self.close()

    @pyqtSlot(str, dict, name="new_simulation_data")
    def new_simulation_data(self, status, data):
        """
        Slot to be called when the simulation interface has completed the
        current job and new data is available.

        Args:
            status (str): Status of the simulation, either
                - `finished` : Simulation has been finished successfully or
                - `failed` : Simulation has failed.
            data (dict): Dictionary, holding the simulation data.
        """
        self._logger.info("Simulation {}".format(status))
        self.statusLabel.setText("Simulation {}".format(status))

        self.actSimulateCurrent.setText("&Simulate Current Regime")
        self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png")))
        self.actSimulateCurrent.triggered.disconnect(self.stop_simulation)
        self.actSimulateCurrent.triggered.connect(self.start_simulation)

        self.actPlayPause.setDisabled(False)
        self.actStop.setDisabled(False)
        self.actSave.setDisabled(False)
        self.speedControl.setDisabled(False)
        self.timeSlider.setDisabled(False)

        self.sim.simulationProgressChanged.disconnect(self.guiProgress.setValue)
        self.statusBar().removeWidget(self.guiProgress)

        self.stop_animation()

        self.currentDataset = data
        if data:
            self._read_results()
            self._update_data_list()
            self._update_plots()

        if self._settings.value("control/autoplay_animation") == "True":
            self.actPlayPause.trigger()

        if self.runningBatch:
            regime_name = self._regimes[self._current_regime_index]["Name"]
            file_name = self._simfile_name(regime_name)
            self._save_data(os.path.join(
                self._settings.value("path/simulation_results"),
                file_name))
            self.regimeFinished.emit()
        else:
            self.actSimulateAll.setDisabled(False)

    def _read_results(self):
        state = self.currentDataset["results"]["Solver"]
        self.interpolator = interp1d(self.currentDataset["results"]["time"],
                                     state,
                                     axis=0,
                                     bounds_error=False,
                                     fill_value=(state[0], state[-1]))
        self.currentStepSize = 1.0/self.currentDataset["simulation"][
            "measure rate"]
        self.currentEndTime = self.currentDataset["simulation"]["end time"]
        self.validData = True

    def increment_playback_speed(self):
        self.speedControl.setValue(self.speedControl.value()
                                   + self.speedControl.singleStep())

    def decrement_playback_speed(self):
        self.speedControl.setValue(self.speedControl.value()
                                   - self.speedControl.singleStep())

    def reset_playback_speed(self):
        self.speedControl.setValue((self.speedControl.maximum()
                                    - self.speedControl.minimum())/2)

    def set_slowest_playback_speed(self):
        self.speedControl.setValue(self.speedControl.minimum())

    def set_fastest_playback_speed(self):
        self.speedControl.setValue(self.speedControl.maximum())

    def update_playback_speed(self, val):
        """
        adjust playback time to slider value

        :param val:
        """
        maximum = self.speedControl.maximum()
        self.playbackGain = 10**(3.0 * (val - maximum / 2) / maximum)

    @pyqtSlot()
    def increment_playback_time(self):
        """
        go one time step forward in playback
        """
        if self.playbackTime == self.currentEndTime:
            self.pause_animation()
            return

        increment = self.playbackGain * self.playbackTimeout / 1000
        self.playbackTime = min(self.currentEndTime,
                                self.playbackTime + increment)
        pos = int(self.playbackTime / self.currentEndTime
                  * self.timeSliderRange)
        self.timeSlider.blockSignals(True)
        self.timeSlider.setValue(pos)
        self.timeSlider.blockSignals(False)
        self.playbackTimeChanged.emit()

    def update_playback_time(self):
        """
        adjust playback time to slider value
        """
        self.playbackTime = self.timeSlider.value()/self.timeSliderRange*self.currentEndTime
        self.playbackTimeChanged.emit()
        return

    def update_gui(self):
        """
        updates the graphical user interface, including:
            - timestamp
            - visualisation
            - time cursor in diagrams
        """
        if not self.validData:
            return

        self.timeLabel.setText("current time: %4f" % self.playbackTime)

        # update time cursor in plots
        self._update_time_cursors()

        # update state of rendering
        if self.visualizer:
            state = self.interpolator(self.playbackTime)
            self.visualizer.update_scene(state)
            if isinstance(self.visualizer, MplVisualizer):
                pass
            elif isinstance(self.visualizer, VtkVisualizer):
                self.vtkWidget.GetRenderWindow().Render()

    def _update_data_list(self):
        self.dataList.clear()
        for module_name, results in self.currentDataset["results"].items():
            if not isinstance(results, np.ndarray):
                continue
            if len(results.shape) == 1:
                self.dataList.insertItem(0, module_name)
            elif len(results.shape) == 2:
                for col in range(results.shape[1]):
                    self.dataList.insertItem(
                        0,
                        self._build_entry_name(module_name, (col, ))
                    )
            elif len(results.shape) == 3:
                for col in range(results.shape[1]):
                    for der in range(results.shape[2]):
                        self.dataList.insertItem(
                            0,
                            self._build_entry_name(module_name, (col, der))
                        )

    def _build_entry_name(self, module_name, idx):
        """
        Construct an identifier for a given entry of a module.
        Args:
            module_name (str): name of the module the entry belongs to.
            idx (tuple): Index of the entry.

        Returns:
            str: Identifier to use for display.
        """
        # save the user from defining 1d entries via tuples
        if len(idx) == 1:
            m_idx = idx[0]
        else:
            m_idx = idx

        mod_settings = self.currentDataset["modules"]
        info = mod_settings.get(module_name, {}).get("output_info", None)
        if info:
            if m_idx in info:
                return ".".join([module_name, info[m_idx]["Name"]])

        return ".".join([module_name] + [str(i) for i in idx])

    def _get_index_from_suffix(self, module_name, suffix):
        info = self.currentDataset["modules"].get(module_name, {}).get(
            "output_info", None)
        idx = next((i for i in info if info[i]["Name"] == suffix), None)
        return idx

    def _get_units(self, entry):
        """
        Return the unit that corresponds to a given entry.

        If no information is available, None is returned.

        Args:
            entry (str): Name of the entry. This can either be "Model.a.b" where
                a and b are numbers or if information is available "Model.Signal"
                where signal is the name of that part.

        Returns:

        """
        args = entry.split(".")
        module_name = args.pop(0)
        info = self.currentDataset["modules"].get(module_name, {}).get(
            "output_info", None)
        if info is None:
            return None

        if len(args) == 1:
            try:
                idx = int(args[0])
            except ValueError:
                idx = next((i for i in info if info[i]["Name"] == args[0]),
                           None)
        else:
            idx = (int(a) for a in args)

        return info[idx]["Unit"]

    def create_plot(self, item):
        """
        Creates a plot widget based on the given item.

        If a plot for this item is already open no new plot is created but the
        existing one is raised up again.

        Args:
            item(Qt.ListItem): Item to plot.
        """
        title = str(item.text())
        if title in self.non_plotting_docks:
            self._logger.error("Title '{}' not allowed for a plot window since"
                               "it would shadow on of the reserved "
                               "names".format(title))

        # check if plot has already been opened
        if title in self.area.findAll()[1]:
            self.area.docks[title].raiseDock()
            return

        # collect data
        data = self._get_data_by_name(title)
        t = self.currentDataset["results"]["time"]
        unit = self._get_units(title)
        if "." in title:
            name = title.split(".")[1]
        else:
            name = title

        # create plot widget
        widget = pg.PlotWidget(title=title)
        widget.showGrid(True, True)
        widget.plot(x=t, y=data)
        widget.getPlotItem().getAxis("bottom").setLabel(text="Time", units="s")
        widget.getPlotItem().getAxis("left").setLabel(text=name, units=unit)

        # add a time line
        time_line = pg.InfiniteLine(self.playbackTime,
                                    angle=90,
                                    movable=False,
                                    pen=pg.mkPen("#FF0000", width=2.0))
        widget.getPlotItem().addItem(time_line)

        # create dock container and add it to dock area
        dock = pg.dockarea.Dock(title, closable=True)
        dock.addWidget(widget)
        self.area.addDock(dock, "above", self.plotDockPlaceholder)

    def _get_data_by_name(self, name):
        tmp = name.split(".")
        module_name = tmp[0]
        if len(tmp) == 1:
            data = np.array(self.currentDataset["results"][module_name])
        elif len(tmp) == 2:
            try:
                idx = int(tmp[1])
            except ValueError:
                idx = self._get_index_from_suffix(module_name, tmp[1])
            finally:
                data = self.currentDataset["results"][module_name][..., idx]
        elif len(tmp) == 3:
            idx = int(tmp[1])
            der = int(tmp[2])
            data = self.currentDataset["results"][module_name][..., idx, der]
        else:
            raise ValueError("Format not supported")

        return data

    def _update_time_cursors(self):
        """
        Update the time lines of all plot windows
        """
        for title, dock in self.area.findAll()[1].items():
            if title in self.non_plotting_docks:
                continue
            for widget in dock.widgets:
                for item in widget.getPlotItem().items:
                    if isinstance(item, pg.InfiniteLine):
                        item.setValue(self.playbackTime)

    def _update_plots(self):
        """
        Update the data in all plot windows
        """
        for title, dock in self.area.findAll()[1].items():
            if title in self.non_plotting_docks:
                continue

            if not self.dataList.findItems(dock.name(), Qt.MatchExactly):
                # no data for this plot -> remove it
                dock.close()
                continue

            for widget in dock.widgets:
                for item in widget.getPlotItem().items:
                    if isinstance(item, pg.PlotDataItem):
                        x_data = self.currentDataset["results"]["time"]
                        y_data = self._get_data_by_name(dock.name())
                        item.setData(x=x_data, y=y_data)

    @pyqtSlot(QModelIndex)
    def target_view_changed(self, index):
        self.targetView.resizeColumnToContents(0)

    def postprocessing_clicked(self):
        """
        starts the post- and metaprocessing application
        """
        self._logger.info("launching postprocessor")
        self.statusBar().showMessage("launching postprocessor", 1000)
        if self.postprocessor is None:
            self.postprocessor = PostProcessor()

        self.postprocessor.show()

    def reset_camera_clicked(self):
        """
        reset camera in vtk window
        """
        self.visualizer.reset_camera()
        self.vtkWidget.GetRenderWindow().Render()

    def show_info(self):
        icon_lic = open(get_resource("license.txt"), "r").read()
        text = "This application was build using PyMoskito ver. {} .<br />" \
               "PyMoskito is free software distributed under GPLv3. <br />" \
               "It is developed by members of the " \
               "<a href=\'https://tu-dresden.de/ing/elektrotechnik/rst'>" \
               "Institute of Control Theory</a>" \
               " at the <a href=\'https://tu-dresden.de'>" \
               "Dresden University of Technology</a>. <br />" \
               "".format(pkg_resources.require("PyMoskito")[0].version) \
               + "<br />" + icon_lic
        box = QMessageBox.about(self, "PyMoskito", text)

    def show_online_docs(self):
        webbrowser.open("https://pymoskito.readthedocs.org")

    def closeEvent(self, QCloseEvent):
        self._logger.info("Close Event received, shutting down.")
        logging.getLogger().removeHandler(self.textLogger)
        super().closeEvent(QCloseEvent)
Exemplo n.º 22
0
 def __addDefaultActions(self):
     """
     Private slot to add the default user agent entries.
     
     @return flag indicating that a user agent entry is checked (boolean)
     """
     from . import UserAgentDefaults_rc              # __IGNORE_WARNING__
     defaultUserAgents = QFile(":/UserAgentDefaults.xml")
     defaultUserAgents.open(QIODevice.ReadOnly)
     
     menuStack = []
     isChecked = False
     
     if self.__url:
         currentUserAgentString = self.__manager.userAgentForUrl(self.__url)
     else:
         from Helpviewer.HelpBrowserWV import HelpWebPage
         currentUserAgentString = HelpWebPage().userAgent()
     xml = QXmlStreamReader(defaultUserAgents)
     while not xml.atEnd():
         xml.readNext()
         if xml.isStartElement() and xml.name() == "separator":
             if menuStack:
                 menuStack[-1].addSeparator()
             else:
                 self.addSeparator()
             continue
         
         if xml.isStartElement() and xml.name() == "useragent":
             attributes = xml.attributes()
             title = attributes.value("description")
             userAgent = attributes.value("useragent")
             
             act = QAction(self)
             act.setText(title)
             act.setData(userAgent)
             act.setToolTip(userAgent)
             act.setCheckable(True)
             act.setChecked(userAgent == currentUserAgentString)
             act.triggered.connect(self.__changeUserAgent)
             if menuStack:
                 menuStack[-1].addAction(act)
             else:
                 self.addAction(act)
             self.__actionGroup.addAction(act)
             isChecked = isChecked or act.isChecked()
         
         if xml.isStartElement() and xml.name() == "useragentmenu":
             attributes = xml.attributes()
             title = attributes.value("title")
             if title == "v_a_r_i_o_u_s":
                 title = self.tr("Various")
             
             menu = QMenu(self)
             menu.setTitle(title)
             self.addMenu(menu)
             menuStack.append(menu)
         
         if xml.isEndElement() and xml.name() == "useragentmenu":
             menuStack.pop()
     
     if xml.hasError():
         E5MessageBox.critical(
             self,
             self.tr("Parsing default user agents"),
             self.tr(
                 """<p>Error parsing default user agents.</p><p>{0}</p>""")
             .format(xml.errorString()))
     
     return isChecked
Exemplo n.º 23
0
class storylineView(QWidget, Ui_storylineView):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setupUi(self)
        self._mdlPlots = None
        self.scene = QGraphicsScene()
        self.view.setScene(self.scene)

        self.reloadTimer = QTimer()
        self.reloadTimer.timeout.connect(self.refresh)
        self.reloadTimer.setSingleShot(True)
        self.reloadTimer.setInterval(500)

        self.btnRefresh.clicked.connect(self.refresh)
        self.sldTxtSize.sliderMoved.connect(self.reloadTimer.start)

        self.generateMenu()

    def generateMenu(self):
        m = QMenu()

        self.actPlots = QAction(self.tr("Show Plots"), m)
        self.actPlots.setCheckable(True)
        self.actPlots.setChecked(True)
        self.actPlots.toggled.connect(self.reloadTimer.start)
        m.addAction(self.actPlots)

        self.actCharacters = QAction(self.tr("Show Characters"), m)
        self.actCharacters.setCheckable(True)
        self.actCharacters.setChecked(False)
        self.actCharacters.toggled.connect(self.reloadTimer.start)
        m.addAction(self.actCharacters)

        self.btnSettings.setMenu(m)

    def setModels(self, mdlOutline, mdlCharacter, mdlPlots):
        self._mdlPlots = mdlPlots
        # self._mdlPlots.dataChanged.connect(self.refresh)
        # self._mdlPlots.rowsInserted.connect(self.refresh)

        self._mdlOutline = mdlOutline
        self._mdlOutline.dataChanged.connect(self.updateMaybe)

        self._mdlCharacter = mdlCharacter
        self._mdlCharacter.dataChanged.connect(self.reloadTimer.start)

    def updateMaybe(self, topLeft, bottomRight):
        if topLeft.column() <= Outline.notes.value <= bottomRight.column():
            self.reloadTimer.start

    def plotReferences(self):
        "Returns a list of plot references"
        if not self._mdlPlots:
            pass

        plotsID = self._mdlPlots.getPlotsByImportance()
        r = []
        for importance in plotsID:
            for ID in importance:
                ref = references.plotReference(ID)
                r.append(ref)

        return r

    def charactersReferences(self):
        "Returns a list of character references"
        if not self._mdlCharacter:
            pass

        chars = self._mdlCharacter.getCharactersByImportance()
        r = []
        for importance in chars:
            for c in importance:
                ref = references.characterReference(c.ID())
                r.append(ref)

        return r

    def refresh(self):
        if not self._mdlPlots or not self._mdlOutline or not self._mdlCharacter:
            return

        if not self.isVisible():
            return

        LINE_HEIGHT = 18
        SPACING = 3
        TEXT_WIDTH = self.sldTxtSize.value()
        CIRCLE_WIDTH = 10
        LEVEL_HEIGHT = 12

        s = self.scene
        s.clear()

        # Get Max Level (max depth)
        root = self._mdlOutline.rootItem
        def maxLevel(item, level=0, max=0):
            if level > max:
                max = level
            for c in item.children():
                m = maxLevel(c, level + 1)
                if m > max:
                    max = m
            return max

        MAX_LEVEL = maxLevel(root)

        # Get the list of tracked items (array of references)
        trackedItems = []

        if self.actPlots.isChecked():
            trackedItems += self.plotReferences()

        if self.actCharacters.isChecked():
            trackedItems += self.charactersReferences()

        ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING )

        fm = QFontMetrics(s.font())
        max_name = 0
        for ref in trackedItems:
            name = references.title(ref)
            max_name = max(fm.width(name), max_name)

        TITLE_WIDTH = max_name + 2 * SPACING

        # Add Folders and Texts
        outline = OutlineRect(0, 0, 0, ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT)
        s.addItem(outline)
        outline.setPos(TITLE_WIDTH + SPACING, 0)

        refCircles = [] # a list of all references, to be added later on the lines

        # A Function to add a rect with centered elided text
        def addRectText(x, w, parent, text="", level=0, tooltip=""):
            deltaH = LEVEL_HEIGHT if level else 0
            r = OutlineRect(0, 0, w, parent.rect().height()-deltaH, parent, title=text)
            r.setPos(x, deltaH)

            txt = QGraphicsSimpleTextItem(text, r)
            f = txt.font()
            f.setPointSize(8)
            fm = QFontMetricsF(f)
            elidedText = fm.elidedText(text, Qt.ElideMiddle, w)
            txt.setFont(f)
            txt.setText(elidedText)
            txt.setPos(r.boundingRect().center() - txt.boundingRect().center())
            txt.setY(0)
            return r

        # A function to returns an item's width, by counting its children
        def itemWidth(item):
            if item.isFolder():
                r = 0
                for c in item.children():
                    r += itemWidth(c)
                return r or TEXT_WIDTH
            else:
                return TEXT_WIDTH

        def listItems(item, rect, level=0):
            delta = 0
            for child in item.children():
                w = itemWidth(child)

                if child.isFolder():
                    parent = addRectText(delta, w, rect, child.title(), level, tooltip=child.title())
                    parent.setToolTip(references.tooltip(references.textReference(child.ID())))
                    listItems(child, parent, level + 1)

                else:
                    rectChild = addRectText(delta, TEXT_WIDTH, rect, "", level, tooltip=child.title())
                    rectChild.setToolTip(references.tooltip(references.textReference(child.ID())))
                    
                    # Find tracked references in that scene (or parent folders)
                    for ref in trackedItems:

                        result = []

                        # Tests if POV
                        scenePOV = False  # Will hold true of character is POV of the current text, not containing folder
                        if references.type(ref) == references.CharacterLetter:
                            ID = references.ID(ref)
                            c = child
                            while c:
                                if c.POV() == ID:
                                    result.append(c.ID())
                                    if c == child: scenePOV = True
                                c = c.parent()

                        # Search in notes/references
                        c = child
                        while c:
                            result += references.findReferencesTo(ref, c, recursive=False)
                            c = c.parent()

                        if result:
                            ref2 = result[0]
                            
                            # Create a RefCircle with the reference
                            c = RefCircle(TEXT_WIDTH / 2, - CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2, important=scenePOV)
                            
                            # Store it, with the position of that item, to display it on the line later on
                            refCircles.append((ref, c, rect.mapToItem(outline, rectChild.pos())))

                delta += w

        listItems(root, outline)

        OUTLINE_WIDTH = itemWidth(root)

        # Add Tracked items
        i = 0
        itemsRect = s.addRect(0, 0, 0, 0)
        itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING)

        # Set of colors for plots (as long as they don't have their own colors)
        colors = [
            "#D97777", "#AE5F8C", "#D9A377", "#FFC2C2", "#FFDEC2", "#D2A0BC",
            "#7B0F0F", "#7B400F", "#620C3D", "#AA3939", "#AA6C39", "#882D61",
            "#4C0000", "#4C2200", "#3D0022",
        ]

        for ref in trackedItems:
            if references.type(ref) == references.CharacterLetter:
                color = self._mdlCharacter.getCharacterByID(references.ID(ref)).color()
            else:
                color = QColor(colors[i % len(colors)])

            # Rect
            r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect)
            r.setPen(QPen(Qt.NoPen))
            r.setBrush(QBrush(color))
            r.setPos(0, i * LINE_HEIGHT + i * SPACING)
            r.setToolTip(references.tooltip(ref))
            i += 1

            # Text
            name = references.title(ref)
            txt = QGraphicsSimpleTextItem(name, r)
            txt.setPos(r.boundingRect().center() - txt.boundingRect().center())

            # Line
            line = PlotLine(0, 0,
                            OUTLINE_WIDTH + SPACING, 0)
            line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y())
            s.addItem(line)
            line.setPen(QPen(color, 5))
            line.setToolTip(references.tooltip(ref))

            # We add the circles / references to text, on the line
            for ref2, circle, pos in refCircles:
                if ref2 == ref:
                    circle.setParentItem(line)
                    circle.setPos(pos.x(), 0)

        # self.view.fitInView(0, 0, TOTAL_WIDTH, i * LINE_HEIGHT, Qt.KeepAspectRatioByExpanding) # KeepAspectRatio
        self.view.setSceneRect(0, 0, 0, 0)
Exemplo n.º 24
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.settingsTree = SettingsTree()
        self.setCentralWidget(self.settingsTree)

        self.locationDialog = None

        self.createActions()
        self.createMenus()

        self.autoRefreshAct.setChecked(True)
        self.fallbacksAct.setChecked(True)

        self.setWindowTitle("Settings Editor")
        self.resize(500, 600)

    def openSettings(self):
        if self.locationDialog is None:
            self.locationDialog = LocationDialog(self)

        if self.locationDialog.exec_():
            settings = QSettings(self.locationDialog.format(),
                                        self.locationDialog.scope(),
                                        self.locationDialog.organization(),
                                        self.locationDialog.application())
            self.setSettingsObject(settings)
            self.fallbacksAct.setEnabled(True)

    def openIniFile(self):
        fileName, _ = QFileDialog.getOpenFileName(self, "Open INI File", '',
                "INI Files (*.ini *.conf)")

        if fileName:
            settings = QSettings(fileName, QSettings.IniFormat)
            self.setSettingsObject(settings)
            self.fallbacksAct.setEnabled(False)

    def openPropertyList(self):
        fileName, _ = QFileDialog.getOpenFileName(self, "Open Property List",
                '', "Property List Files (*.plist)")

        if fileName:
            settings = QSettings(fileName, QSettings.NativeFormat)
            self.setSettingsObject(settings)
            self.fallbacksAct.setEnabled(False)

    def openRegistryPath(self):
        path, ok = QInputDialog.getText(self, "Open Registry Path",
                "Enter the path in the Windows registry:", QLineEdit.Normal,
                'HKEY_CURRENT_USER\\')

        if ok and path != '':
            settings = QSettings(path, QSettings.NativeFormat)
            self.setSettingsObject(settings)
            self.fallbacksAct.setEnabled(False)

    def about(self):
        QMessageBox.about(self, "About Settings Editor",
                "The <b>Settings Editor</b> example shows how to access "
                "application settings using Qt.")

    def createActions(self):
        self.openSettingsAct = QAction("&Open Application Settings...", self,
                shortcut="Ctrl+O", triggered=self.openSettings)

        self.openIniFileAct = QAction("Open I&NI File...", self,
                shortcut="Ctrl+N", triggered=self.openIniFile)

        self.openPropertyListAct = QAction("Open Mac &Property List...", self,
                shortcut="Ctrl+P", triggered=self.openPropertyList)
        if sys.platform != 'darwin':
            self.openPropertyListAct.setEnabled(False)

        self.openRegistryPathAct = QAction("Open Windows &Registry Path...",
                self, shortcut="Ctrl+G", triggered=self.openRegistryPath)
        if sys.platform != 'win32':
            self.openRegistryPathAct.setEnabled(False)

        self.refreshAct = QAction("&Refresh", self, shortcut="Ctrl+R",
                enabled=False, triggered=self.settingsTree.refresh)

        self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q",
                triggered=self.close)

        self.autoRefreshAct = QAction("&Auto-Refresh", self, shortcut="Ctrl+A",
                checkable=True, enabled=False)
        self.autoRefreshAct.triggered.connect(self.settingsTree.setAutoRefresh)
        self.autoRefreshAct.triggered.connect(self.refreshAct.setDisabled)

        self.fallbacksAct = QAction("&Fallbacks", self, shortcut="Ctrl+F",
                checkable=True, enabled=False,
                triggered=self.settingsTree.setFallbacksEnabled)

        self.aboutAct = QAction("&About", self, triggered=self.about)

        self.aboutQtAct = QAction("About &Qt", self,
                triggered=QApplication.instance().aboutQt)

    def createMenus(self):
        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenu.addAction(self.openSettingsAct)
        self.fileMenu.addAction(self.openIniFileAct)
        self.fileMenu.addAction(self.openPropertyListAct)
        self.fileMenu.addAction(self.openRegistryPathAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.refreshAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.optionsMenu = self.menuBar().addMenu("&Options")
        self.optionsMenu.addAction(self.autoRefreshAct)
        self.optionsMenu.addAction(self.fallbacksAct)

        self.menuBar().addSeparator()

        self.helpMenu = self.menuBar().addMenu("&Help")
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

    def setSettingsObject(self, settings):
        settings.setFallbacksEnabled(self.fallbacksAct.isChecked())
        self.settingsTree.setSettingsObject(settings)

        self.refreshAct.setEnabled(True)
        self.autoRefreshAct.setEnabled(True)

        niceName = settings.fileName()
        niceName.replace('\\', '/')
        niceName = niceName.split('/')[-1]

        if not settings.isWritable():
            niceName += " (read only)"

        self.setWindowTitle("%s - Settings Editor" % niceName)
Exemplo n.º 25
0
class MainWindow(QMainWindow):
    def __init__(self, parent):
        QMainWindow.__init__(self)

        self.printer = QPrinter()
        self.load_img = self.load_img_fit
        self.reload_img = self.reload_auto
        self.open_new = parent.open_win
        self.exit = parent.closeAllWindows
        self.scene = QGraphicsScene()
        self.img_view = ImageView(self)
        self.img_view.setScene(self.scene)
        self.setCentralWidget(self.img_view)

        self.create_actions()
        self.create_menu()
        self.create_dict()
        self.slides_next = True

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showMenu)

        self.read_prefs()
        self.read_list = parent.read_list
        self.write_list = parent.write_list
        self.pics_dir = os.path.expanduser('~/Pictures') or QDir.currentPath()
        self.resize(700, 500)

    def create_actions(self):
        self.open_act = QAction('&Open', self, shortcut='Ctrl+O')
        self.open_act.triggered.connect(self.open)
        self.open_new_act = QAction('Open new window', self, shortcut='Ctrl+Shift+O')
        self.open_new_act.triggered.connect(partial(self.open, True))
        self.reload_act = QAction('&Reload image', self, shortcut='Ctrl+R')
        self.reload_act.triggered.connect(self.reload_img)
        self.print_act = QAction('&Print', self, shortcut='Ctrl+P')
        self.print_act.triggered.connect(self.print_img)
        self.save_act = QAction('&Save image', self, shortcut='Ctrl+S')
        self.save_act.triggered.connect(self.save_img)
        self.close_act = QAction('Close window', self, shortcut='Ctrl+W')
        self.close_act.triggered.connect(self.close)
        self.exit_act = QAction('E&xit', self, shortcut='Ctrl+Q')
        self.exit_act.triggered.connect(self.exit)
        self.fulls_act = QAction('Fullscreen', self, shortcut='F11', checkable=True)
        self.fulls_act.triggered.connect(self.toggle_fs)
        self.ss_act = QAction('Slideshow', self, shortcut='F5', checkable=True)
        self.ss_act.triggered.connect(self.toggle_slideshow)
        self.ss_next_act = QAction('Next / Random image', self, checkable=True)
        self.ss_next_act.triggered.connect(self.set_slide_type)
        self.ss_next_act.setChecked(True)
        self.next_act = QAction('Next image', self, shortcut='Right')
        self.next_act.triggered.connect(self.go_next_img)
        self.prev_act = QAction('Previous image', self, shortcut='Left')
        self.prev_act.triggered.connect(self.go_prev_img)
        self.rotleft_act = QAction('Rotate left', self, shortcut='Ctrl+Left')
        self.rotleft_act.triggered.connect(partial(self.img_rotate, 270))
        self.rotright_act = QAction('Rotate right', self, shortcut='Ctrl+Right')
        self.rotright_act.triggered.connect(partial(self.img_rotate, 90))
        self.fliph_act = QAction('Flip image horizontally', self, shortcut='Ctrl+H')
        self.fliph_act.triggered.connect(partial(self.img_flip, -1, 1))
        self.flipv_act = QAction('Flip image vertically', self, shortcut='Ctrl+V')
        self.flipv_act.triggered.connect(partial(self.img_flip, 1, -1))
        self.resize_act = QAction('Resize image', self, triggered=self.resize_img)
        self.crop_act = QAction('Crop image', self, triggered=self.crop_img)
        self.zin_act = QAction('Zoom &In', self, shortcut='Up')
        self.zin_act.triggered.connect(partial(self.img_view.zoom, 1.1))
        self.zout_act = QAction('Zoom &Out', self, shortcut='Down')
        self.zout_act.triggered.connect(partial(self.img_view.zoom, 1 / 1.1))
        self.fit_win_act = QAction('Best &fit', self, checkable=True, shortcut='F',
                triggered=self.zoom_default)
        self.fit_win_act.setChecked(True)
        self.prefs_act = QAction('Preferences', self, triggered=self.set_prefs)
        self.props_act = QAction('Properties', self, triggered=self.get_props)
        self.help_act = QAction('&Help', self, shortcut='F1', triggered=self.help_page)
        self.about_act = QAction('&About', self, triggered=self.about_cm)
        self.aboutQt_act = QAction('About &Qt', self,
                triggered=qApp.aboutQt)

    def create_menu(self):
        self.popup = QMenu(self)
        main_acts = [self.open_act, self.open_new_act, self.reload_act, self.print_act, self.save_act]
        edit_acts1 = [self.rotleft_act, self.rotright_act, self.fliph_act, self.flipv_act]
        edit_acts2 = [self.resize_act, self.crop_act]
        view_acts = [self.next_act, self.prev_act, self.zin_act, self.zout_act, self.fit_win_act, self.fulls_act, self.ss_act, self.ss_next_act]
        help_acts = [self.help_act, self.about_act, self.aboutQt_act]
        end_acts = [self.prefs_act, self.props_act, self.close_act, self.exit_act]
        for act in main_acts:
            self.popup.addAction(act)
        edit_menu = QMenu(self.popup)
        edit_menu.setTitle('&Edit')
        for act in edit_acts1:
            edit_menu.addAction(act)
        edit_menu.addSeparator()
        for act in edit_acts2:
            edit_menu.addAction(act)
        self.popup.addMenu(edit_menu)
        view_menu = QMenu(self.popup)
        view_menu.setTitle('&View')
        for act in view_acts:
            view_menu.addAction(act)
        self.popup.addMenu(view_menu)
        help_menu = QMenu(self.popup)
        help_menu.setTitle('&Help')
        for act in help_acts:
            help_menu.addAction(act)
        self.popup.addMenu(help_menu)
        for act in end_acts:
            self.popup.addAction(act)

        self.action_list = main_acts + edit_acts1 + edit_acts2 + view_acts + help_acts + end_acts
        for act in self.action_list:
            self.addAction(act)

    def showMenu(self, pos):
        self.popup.popup(self.mapToGlobal(pos))
 
    def create_dict(self):
        """Create a dictionary to handle auto-orientation."""
        self.orient_dict = {None: self.load_img,
                '1': self.load_img,
                '2': partial(self.img_flip, -1, 1),
                '3': partial(self.img_rotate, 180),
                '4': partial(self.img_flip, -1, 1),
                '5': self.img_rotate_fliph,
                '6': partial(self.img_rotate, 90),
                '7': self.img_rotate_flipv,
                '8': partial(self.img_rotate, 270)}

    def read_prefs(self):
        """Parse the preferences from the config file, or set default values."""
        try:
            conf = preferences.Config()
            values = conf.read_config()
            self.auto_orient = values[0]
            self.slide_delay = values[1]
            self.quality = values[2]
        except:
            self.auto_orient = True
            self.slide_delay = 5
            self.quality = 90
        self.reload_img = self.reload_auto if self.auto_orient else self.reload_nonauto

    def set_prefs(self):
        """Write preferences to the config file."""
        dialog = preferences.PrefsDialog(self)
        if dialog.exec_() == QDialog.Accepted:
            self.auto_orient = dialog.auto_orient
            self.slide_delay = dialog.delay_spinb.value()
            self.quality = dialog.qual_spinb.value()
            conf = preferences.Config()
            conf.write_config(self.auto_orient, self.slide_delay, self.quality)
        self.reload_img = self.reload_auto if self.auto_orient else self.reload_nonauto

    def open(self, new_win=False):
        fname = QFileDialog.getOpenFileName(self, 'Open File', self.pics_dir)[0]
        if fname:
            if fname.lower().endswith(self.read_list):
                if new_win:
                    self.open_new(fname)
                else:
                    self.open_img(fname)
            else:
                QMessageBox.information(self, 'Error', 'Cannot load {} images.'.format(fname.rsplit('.', 1)[1]))

    def open_img(self, fname):
        self.fname = fname
        self.reload_img()
        dirname = os.path.dirname(self.fname)
        self.set_img_list(dirname)
        self.img_index = self.filelist.index(self.fname)

    def set_img_list(self, dirname):
        """Create a list of readable images from the current directory."""
        filelist = os.listdir(dirname)
        self.filelist = [os.path.join(dirname, fname) for fname in filelist
                        if fname.lower().endswith(self.read_list)]
        self.filelist.sort()
        self.last_file = len(self.filelist) - 1

    def get_img(self):
        """Get image from fname and create pixmap."""
        image = QImage(self.fname)
        self.pixmap = QPixmap.fromImage(image)
        self.setWindowTitle(self.fname.rsplit('/', 1)[1])

    def reload_auto(self):
        """Load a new image with auto-orientation."""
        self.get_img()
        try:
            orient = GExiv2.Metadata(self.fname)['Exif.Image.Orientation']
            self.orient_dict[orient]()
        except:
            self.load_img()

    def reload_nonauto(self):
        """Load a new image without auto-orientation."""
        self.get_img()
        self.load_img()

    def load_img_fit(self):
        """Load the image to fit the window."""
        self.scene.clear()
        self.scene.addPixmap(self.pixmap)
        self.scene.setSceneRect(0, 0, self.pixmap.width(), self.pixmap.height())
        self.img_view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)

    def load_img_1to1(self):
        """Load the image at its original size."""
        self.scene.clear()
        self.img_view.resetTransform()
        self.scene.addPixmap(self.pixmap)
        self.scene.setSceneRect(0, 0, self.pixmap.width(), self.pixmap.height())
        pixitem = QGraphicsPixmapItem(self.pixmap)
        self.img_view.centerOn(pixitem)

    def go_next_img(self):
        self.img_index = self.img_index + 1 if self.img_index < self.last_file else 0
        self.fname = self.filelist[self.img_index]
        self.reload_img()

    def go_prev_img(self):
        self.img_index = self.img_index - 1 if self.img_index else self.last_file
        self.fname = self.filelist[self.img_index]
        self.reload_img()

    def zoom_default(self):
        """Toggle best fit / original size loading."""
        if self.fit_win_act.isChecked():
            self.load_img = self.load_img_fit
            self.create_dict()
            self.load_img()
        else:
            self.load_img = self.load_img_1to1
            self.create_dict()
            self.load_img()

    def img_rotate(self, angle):
        self.pixmap = self.pixmap.transformed(QTransform().rotate(angle))
        self.load_img()

    def img_flip(self, x, y):
        self.pixmap = self.pixmap.transformed(QTransform().scale(x, y))
        self.load_img()

    def img_rotate_fliph(self):
        self.img_rotate(90)
        self.img_flip(-1, 1)

    def img_rotate_flipv(self):
        self.img_rotate(90)
        self.img_flip(1, -1)

    def resize_img(self):
        dialog = editimage.ResizeDialog(self, self.pixmap.width(), self.pixmap.height())
        if dialog.exec_() == QDialog.Accepted:
            width = dialog.get_width.value()
            height = dialog.get_height.value()
            self.pixmap = self.pixmap.scaled(width, height, Qt.IgnoreAspectRatio,
                    Qt.SmoothTransformation)
            self.save_img()

    def crop_img(self):
        self.img_view.setup_crop(self.pixmap.width(), self.pixmap.height())
        dialog = editimage.CropDialog(self, self.pixmap.width(), self.pixmap.height())
        if dialog.exec_() == QDialog.Accepted:
            coords = self.img_view.get_coords()
            self.pixmap = self.pixmap.copy(*coords)
            self.load_img()
        self.img_view.rband.hide()

    def toggle_fs(self):
        if self.fulls_act.isChecked():
            self.showFullScreen()
        else:
            self.showNormal()

    def toggle_slideshow(self):
        if self.ss_act.isChecked():
            self.showFullScreen()
            self.start_ss()
        else:
            self.toggle_fs()
            self.timer.stop()
            self.ss_timer.stop()

    def start_ss(self):
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_img)
        self.timer.start(self.slide_delay * 1000)
        self.ss_timer = QTimer()
        self.ss_timer.timeout.connect(self.update_img)
        self.ss_timer.start(60000)

    def update_img(self):
        if self.slides_next:
            self.go_next_img()
        else:
            self.fname = random.choice(self.filelist)
            self.reload_img()

    def set_slide_type(self):
        self.slides_next = self.ss_next_act.isChecked()

    def save_img(self):
        fname = QFileDialog.getSaveFileName(self, 'Save your image', self.fname)[0]
        if fname:
            if fname.lower().endswith(self.write_list):
                keep_exif = QMessageBox.question(self, 'Save exif data',
                        'Do you want to save the picture metadata?', QMessageBox.Yes |
                        QMessageBox.No, QMessageBox.Yes)
                if keep_exif == QMessageBox.Yes:
                    self.pixmap.save(fname, None, self.quality)
                    exif = GExiv2.Metadata(self.fname)
                    if exif:
                        saved_exif = GExiv2.Metadata(fname)
                        for tag in exif.get_exif_tags():
                            saved_exif[tag] = exif[tag]
                        saved_exif.set_orientation(GExiv2.Orientation.NORMAL)
                        saved_exif.save_file()
            else:
                QMessageBox.information(self, 'Error', 'Cannot save {} images.'.format(fname.rsplit('.', 1)[1]))

    def print_img(self):
        dialog = QPrintDialog(self.printer, self)
        if dialog.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            if self.pixmap.width() > self.pixmap.height():
                self.pixmap = self.pixmap.transformed(QTransform().rotate(90))
            size = self.pixmap.size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
            painter.setWindow(self.pixmap.rect())
            painter.drawPixmap(0, 0, self.pixmap)

    def resizeEvent(self, event=None):
        if self.fit_win_act.isChecked():
            try:
                self.load_img()
            except:
                pass

    def get_props(self):
        """Get the properties of the current image."""
        image = QImage(self.fname)
        preferences.PropsDialog(self, self.fname.rsplit('/', 1)[1], image.width(), image.height())

    def help_page(self):
        preferences.HelpDialog(self)

    def about_cm(self):
        about_message = 'Version: 0.3.9\nAuthor: David Whitlock\nLicense: GPLv3'
        QMessageBox.about(self, 'About Cheesemaker', about_message)
Exemplo n.º 26
0
class revisions(QWidget, Ui_revisions):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setupUi(self)
        self.splitter.setStretchFactor(0, 5)
        self.splitter.setStretchFactor(1, 70)

        self.listDelegate = listCompleterDelegate(self)
        self.list.setItemDelegate(self.listDelegate)
        self.list.itemClicked.connect(self.showDiff)
        self.list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.list.customContextMenuRequested.connect(self.popupMenu)
        self.btnDelete.setEnabled(False)
        self.btnDelete.clicked.connect(self.delete)
        self.btnRestore.clicked.connect(self.restore)
        self.btnRestore.setEnabled(False)

        # self.list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.updateTimer = QTimer()
        self.updateTimer.setSingleShot(True)
        self.updateTimer.setInterval(500)
        self.updateTimer.timeout.connect(self.update)
        self.updateTimer.stop()

        self.menu = QMenu(self)
        self.actGroup = QActionGroup(self)

        self.actShowDiff = QAction(self.tr("Show modifications"), self.menu)
        self.actShowDiff.setCheckable(True)
        self.actShowDiff.setChecked(True)
        self.actShowDiff.triggered.connect(self.showDiff)
        self.menu.addAction(self.actShowDiff)
        self.actGroup.addAction(self.actShowDiff)

        self.actShowVersion = QAction(self.tr("Show ancient version"),
                                      self.menu)
        self.actShowVersion.setCheckable(True)
        self.actShowVersion.setChecked(False)
        self.actShowVersion.triggered.connect(self.showDiff)
        self.menu.addAction(self.actShowVersion)
        self.actGroup.addAction(self.actShowVersion)

        self.menu.addSeparator()
        self.actShowSpaces = QAction(self.tr("Show spaces"), self.menu)
        self.actShowSpaces.setCheckable(True)
        self.actShowSpaces.setChecked(False)
        self.actShowSpaces.triggered.connect(self.showDiff)
        self.menu.addAction(self.actShowSpaces)

        self.actDiffOnly = QAction(self.tr("Show modifications only"),
                                   self.menu)
        self.actDiffOnly.setCheckable(True)
        self.actDiffOnly.setChecked(True)
        self.actDiffOnly.triggered.connect(self.showDiff)
        self.menu.addAction(self.actDiffOnly)
        self.btnOptions.setMenu(self.menu)

        self._model = None
        self._index = None

    def setModel(self, model):
        self._model = model
        self._model.dataChanged.connect(self.updateMaybe)

    def setCurrentModelIndex(self, index):
        self._index = index
        self.view.setText("")
        self.update()

    def updateMaybe(self, topLeft, bottomRight):
        if self._index and \
                                topLeft.column() <= Outline.revisions.value <= bottomRight.column() and \
                                topLeft.row() <= self._index.row() <= bottomRight.row():
            # self.update()
            self.updateTimer.start()

    def update(self):
        self.list.clear()
        item = self._index.internalPointer()
        rev = item.revisions()
        # Sort revisions
        rev = sorted(rev, key=lambda x: x[0], reverse=True)
        for r in rev:
            timestamp = datetime.datetime.fromtimestamp(
                r[0]).strftime('%Y-%m-%d %H:%M:%S')
            readable = self.readableDelta(r[0])
            i = QListWidgetItem(readable)
            i.setData(Qt.UserRole, r[0])
            i.setData(Qt.UserRole + 1, timestamp)
            self.list.addItem(i)

    def readableDelta(self, timestamp):
        now = datetime.datetime.now()
        delta = now - datetime.datetime.fromtimestamp(timestamp)
        if delta.days > 365:
            return self.tr("{} years ago").format(str(int(delta.days / 365)))
        elif delta.days > 30:
            return self.tr("{} months ago").format(str(int(delta.days / 30.5)))
        elif delta.days > 0:
            return self.tr("{} days ago").format(str(delta.days))
        if delta.days == 1:
            return self.tr("1 day ago")
        elif delta.seconds > 60 * 60:
            return self.tr("{} hours ago").format(
                str(int(delta.seconds / 60 / 60)))
        elif delta.seconds > 60:
            return self.tr("{} minutes ago").format(
                str(int(delta.seconds / 60)))
        else:
            return self.tr("{} seconds ago").format(str(delta.seconds))

    def showDiff(self):
        # UI stuff
        self.actShowSpaces.setEnabled(self.actShowDiff.isChecked())
        self.actDiffOnly.setEnabled(self.actShowDiff.isChecked())

        # FIXME: Errors in line number
        i = self.list.currentItem()

        if not i:
            self.btnDelete.setEnabled(False)
            self.btnRestore.setEnabled(False)
            return

        self.btnDelete.setEnabled(True)
        self.btnRestore.setEnabled(True)

        ts = i.data(Qt.UserRole)
        item = self._index.internalPointer()

        textNow = item.text()
        textBefore = [r[1] for r in item.revisions() if r[0] == ts][0]

        if self.actShowVersion.isChecked():
            self.view.setText(textBefore)
            return

        textNow = textNow.splitlines()
        textBefore = textBefore.splitlines()

        d = difflib.Differ()
        diff = list(d.compare(textBefore, textNow))

        if self.actShowSpaces.isChecked():
            _format = lambda x: x.replace(" ", "␣ ")
        else:
            _format = lambda x: x

        extra = "<br>"
        diff = [d for d in diff if d and not d[:2] == "? "]
        mydiff = ""
        skip = False
        for n, l in enumerate(diff):
            l = diff[n]
            op = l[:2]
            txt = l[2:]
            op2 = diff[n + 1][:2] if n + 1 < len(diff) else None
            txt2 = diff[n + 1][2:] if n + 1 < len(diff) else None

            if skip:
                skip = False
                continue

            # Same line
            if op == "  " and not self.actDiffOnly.isChecked():
                mydiff += "{}{}".format(txt, extra)

            elif op == "- " and op2 == "+ ":
                if self.actDiffOnly.isChecked():
                    mydiff += "<br><span style='color: blue;'>{}</span><br>".format(
                        self.tr("Line {}:").format(str(n)))
                s = difflib.SequenceMatcher(None, txt, txt2, autojunk=True)
                newline = ""
                for tag, i1, i2, j1, j2 in s.get_opcodes():
                    if tag == "equal":
                        newline += txt[i1:i2]
                    elif tag == "delete":
                        newline += "<span style='color:red; background:yellow;'>{}</span>".format(
                            _format(txt[i1:i2]))
                    elif tag == "insert":
                        newline += "<span style='color:green; background:yellow;'>{}</span>".format(
                            _format(txt2[j1:j2]))
                    elif tag == "replace":
                        newline += "<span style='color:red; background:yellow;'>{}</span>".format(
                            _format(txt[i1:i2]))
                        newline += "<span style='color:green; background:yellow;'>{}</span>".format(
                            _format(txt2[j1:j2]))

                # Few ugly tweaks for html diffs
                newline = re.sub(
                    r"(<span style='color.*?><span.*?>)</span>(.*)<span style='color:.*?>(</span></span>)",
                    "\\1\\2\\3", newline)
                newline = re.sub(
                    r"<p align=\"<span style='color:red; background:yellow;'>cen</span><span style='color:green; background:yellow;'>righ</span>t<span style='color:red; background:yellow;'>er</span>\" style=\" -qt-block-indent:0; -qt-user-state:0; \">(.*?)</p>",
                    "<p align=\"right\"><span style='color:green; background:yellow;'>\\1</span></p>",
                    newline)
                newline = re.sub(
                    r"<p align=\"<span style='color:green; background:yellow;'>cente</span>r<span style='color:red; background:yellow;'>ight</span>\" style=\" -qt-block-indent:0; -qt-user-state:0; \">(.*)</p>",
                    "<p align=\"center\"><span style='color:green; background:yellow;'>\\1</span></p>",
                    newline)
                newline = re.sub(r"<p(<span.*?>)(.*?)(</span>)(.*?)>(.*?)</p>",
                                 "<p\\2\\4>\\1\\5\\3</p>", newline)

                mydiff += newline + extra
                skip = True
            elif op == "- ":
                if self.actDiffOnly.isChecked():
                    mydiff += "<br>{}:<br>".format(str(n))
                mydiff += "<span style='color:red;'>{}</span>{}".format(
                    txt, extra)
            elif op == "+ ":
                if self.actDiffOnly.isChecked():
                    mydiff += "<br>{}:<br>".format(str(n))
                mydiff += "<span style='color:green;'>{}</span>{}".format(
                    txt, extra)

        self.view.setText(mydiff)

    def restore(self):
        i = self.list.currentItem()
        if not i:
            return
        ts = i.data(Qt.UserRole)
        item = self._index.internalPointer()
        textBefore = [r[1] for r in item.revisions() if r[0] == ts][0]
        index = self._index.sibling(self._index.row(), Outline.text.value)
        self._index.model().setData(index, textBefore)
        # item.setData(Outline.text.value, textBefore)

    def delete(self):
        i = self.list.currentItem()
        if not i:
            return
        ts = i.data(Qt.UserRole)
        self._index.internalPointer().deleteRevision(ts)

    def clearAll(self):
        self._index.internalPointer().clearAllRevisions()

    def saveState(self):
        return [
            self.actShowDiff.isChecked(),
            self.actShowVersion.isChecked(),
            self.actShowSpaces.isChecked(),
            self.actDiffOnly.isChecked(),
        ]

    def popupMenu(self, pos):
        i = self.list.itemAt(pos)
        m = QMenu(self)
        if i:
            m.addAction(self.tr("Restore")).triggered.connect(self.restore)
            m.addAction(self.tr("Delete")).triggered.connect(self.delete)
            m.addSeparator()
        if self.list.count():
            m.addAction(self.tr("Clear all")).triggered.connect(self.clearAll)

        m.popup(self.list.mapToGlobal(pos))

    def restoreState(self, state):
        self.actShowDiff.setChecked(state[0])
        self.actShowVersion.setChecked(state[1])
        self.actShowSpaces.setChecked(state[2])
        self.actDiffOnly.setChecked(state[3])
        self.actShowSpaces.setEnabled(self.actShowDiff.isChecked())
        self.actDiffOnly.setEnabled(self.actShowDiff.isChecked())
Exemplo n.º 27
0
class PDFAreaSelectorDlg(QDialog):

    def __init__(self, parent=None):
        super().__init__(parent)

        self._parent = parent

        self.setWindowTitle("Area selection")
        self.scaleFactor = 1
        self.setModal(True)

        self.controlBarWgt         = QWidget(self)
        self.nextPageBtn         = QPushButton("Next page (Ctrl+right arrow)")
        self.previousPageBtn     = QPushButton("Previous page (Ctrl+left arrow)")
        self.noPageTxt             = QLabel("1")
        self.noPageTxt.setStyleSheet("border: 1px solid grey")
        self.noPageTxt.setFixedWidth(40)

        self.controlBarWgt.setLayout(QHBoxLayout())

        self.nextPageBtn.clicked.connect(self.nextPage)
        self.previousPageBtn.clicked.connect(self.previousPage)

        self.controlBarWgt.layout().addWidget(self.previousPageBtn)
        self.controlBarWgt.layout().addWidget(self.noPageTxt)
        self.controlBarWgt.layout().addWidget(self.nextPageBtn)

        self.imageLabel = ImageWidget()
        self.imageLabel.setBackgroundRole(QPalette.Base)
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)

        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.imageLabel)

        self.createActions()
        self.createMenus()

        self.setLayout(QVBoxLayout())
        self.layout().addWidget(self.menuBar)
        self.layout().addWidget(self.controlBarWgt)
        self.layout().addWidget(self.scrollArea)

        self.imageLabel.areaSelected.connect(self.resendSelectedEvent)

        self.loadImage()

    @pyqtSlot(float, float, float, float, QPixmap)
    def resendSelectedEvent(self, x, y, width, height, image):
        self._parent.x = x
        self._parent.y = y
        self._parent.width = width
        self._parent.height = height
        self._parent.image = image

        self._parent.areaSelected.emit()
        self.close()


    def loadImage(self):
        image = QImage.fromData(self._parent.pages[self._parent.currentPageInd],"PNG")

        self.imageLabel.setPixmap(QPixmap.fromImage(image))
        self.fitToWindowAct.setEnabled(True)
        self.updateActions()

        if not self.fitToWindowAct.isChecked():
            self.imageLabel.adjustSize()

        self.setWindowFilePath(self._parent.fileName)
        return True


    def zoomIn(self):
        self.scaleImage(1.25)


    def zoomOut(self):
        self.scaleImage(0.8)


    def normalSize(self):
        self.imageLabel.adjustSize()
        self.scaleFactor = 1.0

    def fitToWindow(self):
        fitToWindow = self.fitToWindowAct.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        if not fitToWindow :
            self.normalSize()
        self.updateActions()



    def createActions(self):

        self.exitAct = QAction("E&xit", self)
        self.exitAct.setShortcut("Ctrl+Q")
        self.exitAct.triggered.connect(self.close)

        self.zoomInAct = QAction("Zoom &In (25%)", self)
        self.zoomInAct.setShortcut("Ctrl++")
        self.zoomInAct.setEnabled(False)
        self.zoomInAct.triggered.connect(self.zoomIn)

        self.zoomOutAct = QAction("Zoom &Out (25%)", self)
        self.zoomOutAct.setShortcut("Ctrl+-")
        self.zoomOutAct.setEnabled(False)
        self.zoomOutAct.triggered.connect(self.zoomOut)

        self.normalSizeAct = QAction("&Normal Size", self)
        self.normalSizeAct.setShortcut("Ctrl+S")
        self.normalSizeAct.setEnabled(False)
        self.normalSizeAct.triggered.connect(self.normalSize)

        self.fitToWindowAct = QAction("&Fit to Window", self)
        self.fitToWindowAct.setEnabled(False)
        self.fitToWindowAct.setCheckable(True)
        self.fitToWindowAct.setShortcut("Ctrl+F")
        self.fitToWindowAct.triggered.connect(self.fitToWindow)



        self.nextPageAct = QAction("&Next page", self)
        self.nextPageAct.setEnabled(True)
        self.nextPageAct.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Right))
        self.nextPageAct.triggered.connect(self.nextPage)


        self.previousPageAct = QAction("&Previous page", self)
        self.previousPageAct.setEnabled(True)
        self.previousPageAct.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Left))
        self.previousPageAct.triggered.connect(self.previousPage)



    def createMenus(self):

        self.menuBar = QMenuBar(self)

        self.fileMenu = QMenu("&File", self)
        self.fileMenu.addAction(self.exitAct)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.fitToWindowAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.nextPageAct)
        self.viewMenu.addAction(self.previousPageAct)

        self.menuBar.addMenu(self.fileMenu)
        self.menuBar.addMenu(self.viewMenu)



    def updateActions(self):

        self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())



    def scaleImage(self, factor):
        assert(self.imageLabel.pixmap())
        self.scaleFactor *= factor
        self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size())

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)

        self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
        self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)


    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(int(factor * scrollBar.value()
                                + ((factor - 1) * scrollBar.pageStep()/2)))



    def nextPage(self):
        if self._parent.currentPageInd < len(self._parent.pages)-1:
            self._parent.currentPageInd += 1
            self.loadImage()
            self.noPageTxt.setText(str(self._parent.currentPageInd+1))

    def previousPage(self):
        if self._parent.currentPageInd > 0:
            self._parent.currentPageInd -= 1
            self.loadImage()
            self.noPageTxt.setText(str(self._parent.currentPageInd+1))
Exemplo n.º 28
0
class UserAgentMenu(QMenu):
    """
    Class implementing a menu to select the user agent string.
    """
    def __init__(self, title, url=None, parent=None):
        """
        Constructor
        
        @param title title of the menu (string)
        @param url URL to set user agent for (QUrl)
        @param parent reference to the parent widget (QWidget)
        """
        super(UserAgentMenu, self).__init__(title, parent)

        self.__manager = None
        self.__url = url
        if self.__url:
            if self.__url.isValid():
                import Helpviewer.HelpWindow
                self.__manager = \
                    Helpviewer.HelpWindow.HelpWindow.userAgentsManager()
            else:
                self.__url = None

        self.aboutToShow.connect(self.__populateMenu)

    def __populateMenu(self):
        """
        Private slot to populate the menu.
        """
        self.aboutToShow.disconnect(self.__populateMenu)

        self.__actionGroup = QActionGroup(self)

        # add default action
        self.__defaultUserAgent = QAction(self)
        self.__defaultUserAgent.setText(self.tr("Default"))
        self.__defaultUserAgent.setCheckable(True)
        self.__defaultUserAgent.triggered.connect(
            self.__switchToDefaultUserAgent)
        if self.__url:
            self.__defaultUserAgent.setChecked(
                self.__manager.userAgentForUrl(self.__url) == "")
        else:
            from Helpviewer.HelpBrowserWV import HelpWebPage
            self.__defaultUserAgent.setChecked(HelpWebPage().userAgent() == "")
        self.addAction(self.__defaultUserAgent)
        self.__actionGroup.addAction(self.__defaultUserAgent)
        isChecked = self.__defaultUserAgent.isChecked()

        # add default extra user agents
        isChecked = self.__addDefaultActions() or isChecked

        # add other action
        self.addSeparator()
        self.__otherUserAgent = QAction(self)
        self.__otherUserAgent.setText(self.tr("Other..."))
        self.__otherUserAgent.setCheckable(True)
        self.__otherUserAgent.triggered.connect(self.__switchToOtherUserAgent)
        self.addAction(self.__otherUserAgent)
        self.__actionGroup.addAction(self.__otherUserAgent)
        self.__otherUserAgent.setChecked(not isChecked)

    def __switchToDefaultUserAgent(self):
        """
        Private slot to set the default user agent.
        """
        if self.__url:
            self.__manager.removeUserAgent(self.__url.host())
        else:
            from Helpviewer.HelpBrowserWV import HelpWebPage
            HelpWebPage().setUserAgent("")

    def __switchToOtherUserAgent(self):
        """
        Private slot to set a custom user agent string.
        """
        from Helpviewer.HelpBrowserWV import HelpWebPage
        userAgent, ok = QInputDialog.getText(
            self, self.tr("Custom user agent"), self.tr("User agent:"),
            QLineEdit.Normal,
            HelpWebPage().userAgent(resolveEmpty=True))
        if ok:
            if self.__url:
                self.__manager.setUserAgentForUrl(self.__url, userAgent)
            else:
                HelpWebPage().setUserAgent(userAgent)

    def __changeUserAgent(self):
        """
        Private slot to change the user agent.
        """
        act = self.sender()
        if self.__url:
            self.__manager.setUserAgentForUrl(self.__url, act.data())
        else:
            from Helpviewer.HelpBrowserWV import HelpWebPage
            HelpWebPage().setUserAgent(act.data())

    def __addDefaultActions(self):
        """
        Private slot to add the default user agent entries.
        
        @return flag indicating that a user agent entry is checked (boolean)
        """
        from . import UserAgentDefaults_rc  # __IGNORE_WARNING__
        defaultUserAgents = QFile(":/UserAgentDefaults.xml")
        defaultUserAgents.open(QIODevice.ReadOnly)

        menuStack = []
        isChecked = False

        if self.__url:
            currentUserAgentString = self.__manager.userAgentForUrl(self.__url)
        else:
            from Helpviewer.HelpBrowserWV import HelpWebPage
            currentUserAgentString = HelpWebPage().userAgent()
        xml = QXmlStreamReader(defaultUserAgents)
        while not xml.atEnd():
            xml.readNext()
            if xml.isStartElement() and xml.name() == "separator":
                if menuStack:
                    menuStack[-1].addSeparator()
                else:
                    self.addSeparator()
                continue

            if xml.isStartElement() and xml.name() == "useragent":
                attributes = xml.attributes()
                title = attributes.value("description")
                userAgent = attributes.value("useragent")

                act = QAction(self)
                act.setText(title)
                act.setData(userAgent)
                act.setToolTip(userAgent)
                act.setCheckable(True)
                act.setChecked(userAgent == currentUserAgentString)
                act.triggered.connect(self.__changeUserAgent)
                if menuStack:
                    menuStack[-1].addAction(act)
                else:
                    self.addAction(act)
                self.__actionGroup.addAction(act)
                isChecked = isChecked or act.isChecked()

            if xml.isStartElement() and xml.name() == "useragentmenu":
                attributes = xml.attributes()
                title = attributes.value("title")
                if title == "v_a_r_i_o_u_s":
                    title = self.tr("Various")

                menu = QMenu(self)
                menu.setTitle(title)
                self.addMenu(menu)
                menuStack.append(menu)

            if xml.isEndElement() and xml.name() == "useragentmenu":
                menuStack.pop()

        if xml.hasError():
            E5MessageBox.critical(
                self, self.tr("Parsing default user agents"),
                self.tr(
                    """<p>Error parsing default user agents.</p><p>{0}</p>""").
                format(xml.errorString()))

        return isChecked
Exemplo n.º 29
0
class revisions(QWidget, Ui_revisions):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setupUi(self)
        self.splitter.setStretchFactor(0, 5)
        self.splitter.setStretchFactor(1, 70)

        self.listDelegate = listCompleterDelegate(self)
        self.list.setItemDelegate(self.listDelegate)
        self.list.itemClicked.connect(self.showDiff)
        self.list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.list.customContextMenuRequested.connect(self.popupMenu)
        self.btnDelete.setEnabled(False)
        self.btnDelete.clicked.connect(self.delete)
        self.btnRestore.clicked.connect(self.restore)
        self.btnRestore.setEnabled(False)

        # self.list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.updateTimer = QTimer()
        self.updateTimer.setSingleShot(True)
        self.updateTimer.setInterval(500)
        self.updateTimer.timeout.connect(self.update)
        self.updateTimer.stop()

        self.menu = QMenu(self)
        self.actGroup = QActionGroup(self)

        self.actShowDiff = QAction(self.tr("Show modifications"), self.menu)
        self.actShowDiff.setCheckable(True)
        self.actShowDiff.setChecked(True)
        self.actShowDiff.triggered.connect(self.showDiff)
        self.menu.addAction(self.actShowDiff)
        self.actGroup.addAction(self.actShowDiff)

        self.actShowVersion = QAction(self.tr("Show ancient version"), self.menu)
        self.actShowVersion.setCheckable(True)
        self.actShowVersion.setChecked(False)
        self.actShowVersion.triggered.connect(self.showDiff)
        self.menu.addAction(self.actShowVersion)
        self.actGroup.addAction(self.actShowVersion)

        self.menu.addSeparator()
        self.actShowSpaces = QAction(self.tr("Show spaces"), self.menu)
        self.actShowSpaces.setCheckable(True)
        self.actShowSpaces.setChecked(False)
        self.actShowSpaces.triggered.connect(self.showDiff)
        self.menu.addAction(self.actShowSpaces)

        self.actDiffOnly = QAction(self.tr("Show modifications only"), self.menu)
        self.actDiffOnly.setCheckable(True)
        self.actDiffOnly.setChecked(True)
        self.actDiffOnly.triggered.connect(self.showDiff)
        self.menu.addAction(self.actDiffOnly)
        self.btnOptions.setMenu(self.menu)

        self._model = None
        self._index = None

    def setModel(self, model):
        self._model = model
        self._model.dataChanged.connect(self.updateMaybe)

    def setCurrentModelIndex(self, index):
        self._index = index
        self.view.setText("")
        self.update()

    def updateMaybe(self, topLeft, bottomRight):
        if self._index and \
                                topLeft.column() <= Outline.revisions <= bottomRight.column() and \
                                topLeft.row() <= self._index.row() <= bottomRight.row():
            # self.update()
            self.updateTimer.start()

    def update(self):
        self.list.clear()
        item = self._index.internalPointer()
        rev = item.revisions()
        # Sort revisions
        rev = sorted(rev, key=lambda x: x[0], reverse=True)
        for r in rev:
            timestamp = datetime.datetime.fromtimestamp(r[0]).strftime('%Y-%m-%d %H:%M:%S')
            readable = self.readableDelta(r[0])
            i = QListWidgetItem(readable)
            i.setData(Qt.UserRole, r[0])
            i.setData(Qt.UserRole + 1, timestamp)
            self.list.addItem(i)

    def readableDelta(self, timestamp):
        now = datetime.datetime.now()
        delta = now - datetime.datetime.fromtimestamp(timestamp)
        if delta.days > 365:
            return self.tr("{} years ago").format(str(int(delta.days / 365)))
        elif delta.days > 30:
            return self.tr("{} months ago").format(str(int(delta.days / 30.5)))
        elif delta.days > 0:
            return self.tr("{} days ago").format(str(delta.days))
        if delta.days == 1:
            return self.tr("1 day ago")
        elif delta.seconds > 60 * 60:
            return self.tr("{} hours ago").format(str(int(delta.seconds / 60 / 60)))
        elif delta.seconds > 60:
            return self.tr("{} minutes ago").format(str(int(delta.seconds / 60)))
        else:
            return self.tr("{} seconds ago").format(str(delta.seconds))

    def showDiff(self):
        # UI stuff
        self.actShowSpaces.setEnabled(self.actShowDiff.isChecked())
        self.actDiffOnly.setEnabled(self.actShowDiff.isChecked())

        # FIXME: Errors in line number
        i = self.list.currentItem()

        if not i:
            self.btnDelete.setEnabled(False)
            self.btnRestore.setEnabled(False)
            return

        self.btnDelete.setEnabled(True)
        self.btnRestore.setEnabled(True)

        ts = i.data(Qt.UserRole)
        item = self._index.internalPointer()

        textNow = item.text()
        textBefore = [r[1] for r in item.revisions() if r[0] == ts][0]

        if self.actShowVersion.isChecked():
            self.view.setText(textBefore)
            return

        textNow = textNow.splitlines()
        textBefore = textBefore.splitlines()

        d = difflib.Differ()
        diff = list(d.compare(textBefore, textNow))

        if self.actShowSpaces.isChecked():
            _format = lambda x: x.replace(" ", "␣ ")
        else:
            _format = lambda x: x

        extra = "<br>"
        diff = [d for d in diff if d and not d[:2] == "? "]
        mydiff = ""
        skip = False
        for n, l in enumerate(diff):
            l = diff[n]
            op = l[:2]
            txt = l[2:]
            op2 = diff[n + 1][:2] if n + 1 < len(diff) else None
            txt2 = diff[n + 1][2:] if n + 1 < len(diff) else None

            if skip:
                skip = False
                continue

            # Same line
            if op == "  " and not self.actDiffOnly.isChecked():
                mydiff += "{}{}".format(txt, extra)

            elif op == "- " and op2 == "+ ":
                if self.actDiffOnly.isChecked():
                    mydiff += "<br><span style='color: blue;'>{}</span><br>".format(
                            self.tr("Line {}:").format(str(n)))
                s = difflib.SequenceMatcher(None, txt, txt2, autojunk=True)
                newline = ""
                for tag, i1, i2, j1, j2 in s.get_opcodes():
                    if tag == "equal":
                        newline += txt[i1:i2]
                    elif tag == "delete":
                        newline += "<span style='color:red; background:yellow;'>{}</span>".format(_format(txt[i1:i2]))
                    elif tag == "insert":
                        newline += "<span style='color:green; background:yellow;'>{}</span>".format(
                            _format(txt2[j1:j2]))
                    elif tag == "replace":
                        newline += "<span style='color:red; background:yellow;'>{}</span>".format(_format(txt[i1:i2]))
                        newline += "<span style='color:green; background:yellow;'>{}</span>".format(
                            _format(txt2[j1:j2]))

                # Few ugly tweaks for html diffs
                newline = re.sub(r"(<span style='color.*?><span.*?>)</span>(.*)<span style='color:.*?>(</span></span>)",
                                 "\\1\\2\\3", newline)
                newline = re.sub(
                    r"<p align=\"<span style='color:red; background:yellow;'>cen</span><span style='color:green; background:yellow;'>righ</span>t<span style='color:red; background:yellow;'>er</span>\" style=\" -qt-block-indent:0; -qt-user-state:0; \">(.*?)</p>",
                    "<p align=\"right\"><span style='color:green; background:yellow;'>\\1</span></p>", newline)
                newline = re.sub(
                    r"<p align=\"<span style='color:green; background:yellow;'>cente</span>r<span style='color:red; background:yellow;'>ight</span>\" style=\" -qt-block-indent:0; -qt-user-state:0; \">(.*)</p>",
                    "<p align=\"center\"><span style='color:green; background:yellow;'>\\1</span></p>", newline)
                newline = re.sub(r"<p(<span.*?>)(.*?)(</span>)(.*?)>(.*?)</p>",
                                 "<p\\2\\4>\\1\\5\\3</p>", newline)

                mydiff += newline + extra
                skip = True
            elif op == "- ":
                if self.actDiffOnly.isChecked():
                    mydiff += "<br>{}:<br>".format(str(n))
                mydiff += "<span style='color:red;'>{}</span>{}".format(txt, extra)
            elif op == "+ ":
                if self.actDiffOnly.isChecked():
                    mydiff += "<br>{}:<br>".format(str(n))
                mydiff += "<span style='color:green;'>{}</span>{}".format(txt, extra)

        self.view.setText(mydiff)

    def restore(self):
        i = self.list.currentItem()
        if not i:
            return
        ts = i.data(Qt.UserRole)
        item = self._index.internalPointer()
        textBefore = [r[1] for r in item.revisions() if r[0] == ts][0]
        index = self._index.sibling(self._index.row(), Outline.text)
        self._index.model().setData(index, textBefore)
        # item.setData(Outline.text, textBefore)

    def delete(self):
        i = self.list.currentItem()
        if not i:
            return
        ts = i.data(Qt.UserRole)
        self._index.internalPointer().deleteRevision(ts)

    def clearAll(self):
        self._index.internalPointer().clearAllRevisions()

    def saveState(self):
        return [
            self.actShowDiff.isChecked(),
            self.actShowVersion.isChecked(),
            self.actShowSpaces.isChecked(),
            self.actDiffOnly.isChecked(),
        ]

    def popupMenu(self, pos):
        i = self.list.itemAt(pos)
        m = QMenu(self)
        if i:
            m.addAction(self.tr("Restore")).triggered.connect(self.restore)
            m.addAction(self.tr("Delete")).triggered.connect(self.delete)
            m.addSeparator()
        if self.list.count():
            m.addAction(self.tr("Clear all")).triggered.connect(self.clearAll)

        m.popup(self.list.mapToGlobal(pos))

    def restoreState(self, state):
        self.actShowDiff.setChecked(state[0])
        self.actShowVersion.setChecked(state[1])
        self.actShowSpaces.setChecked(state[2])
        self.actDiffOnly.setChecked(state[3])
        self.actShowSpaces.setEnabled(self.actShowDiff.isChecked())
        self.actDiffOnly.setEnabled(self.actShowDiff.isChecked())
Exemplo n.º 30
0
class GUI(Ui_mainWindow):
    def __init__(self, window):
        super().__init__()
        self.setupUi(window)
        self.window = window
        self.settings = Settings()
        self.window.closeEvent = self.closeEvent
        self.server = Server()
        self.downman = DownloadManager()
        self.browser = Browser()
        # snapshot updater is to be started on exchange connect
        self.xchgClient = ExchangeClient()
        self.lastKnownDir = "/tmp"
        self.destPrefix = ''
        self.userlist = None
        self.di_list = []
        self.addEventListeners()
        self.browserTable.setColumnHidden(0, True)
        self.userListTable.setColumnHidden(0, True)
        self.tabWidget.setCurrentIndex(0)
        self.urlFrame.setVisible(False)
        self.window.setWindowIcon(QIcon(":/images/favicon.ico"))
        self.window.setWindowTitle("21Lane")
        self.makeMenuBar()
        self.setupSystemTray()
        self.loadSettings()
        self.window.show()

    def makeMenuBar(self):
        self.menuBar = QMenuBar(self.window)
        self.fileMenu = QMenu("File")
        self.menuBar.addMenu(self.fileMenu)
        self.exitAction = QAction("Exit", self.window)
        self.exitAction.triggered.connect(self.closeEvent)
        self.minimizeToTrayAction = QAction("Minimize to Tray", self.window)
        self.minimizeToTrayAction.setCheckable(True)
        self.minimizeToTrayAction.setChecked(True)
        self.fileMenu.addAction(self.minimizeToTrayAction)
        self.fileMenu.addAction(self.exitAction)
        self.window.layout().setMenuBar(self.menuBar)

    def loadSettings(self):
        success = self.settings.load()
        self.publicNameInput.setText(self.settings.configDic["publicName"])
        self.port.setValue(self.settings.configDic["port"])
        self.sharedLocationInput.setText(self.settings.configDic["sharedDir"])
        self.downloadLocationInput.setText(
            self.settings.configDic["downloadDir"])
        self.speedLimitSlider.setValue(self.settings.configDic["speedLimit"])
        self.speedLimitSpin.setValue(self.settings.configDic["speedLimit"])
        self.exchangeURLInput.setText(self.settings.configDic["exchangeURL"])
        if success:
            self.toggleShare()
            self.tabWidget.setCurrentIndex(1)
            self.reloadUsersBtn.click()

    def keyPressedEvent(self, event):
        if event.key() == Qt.Key_Escape:
            event.ignore()

    def closeEvent(self, event):
        if (self.window.sender()
                == None) and (self.minimizeToTrayAction.isChecked()):
            self.showWindow(False)
            self.activateAction.setChecked(False)
            event.ignore()
            return
        if self.server.isRunning():
            self.server.stopServer()
        if self.xchgClient.isRunning():
            print('attempting to shut down exchange client')
            self.xchgClient.quit()
            print('asked to end xchgclient politely')
            if not self.xchgClient.wait(1):
                print('forced closure of xchgclient required')
                self.xchgClient.terminate()
            print('xchgclient forcefully closed')
        if self.downman.running:
            self.downman.stopDownloader()
        qApp.exit()

    def showMessage(self, maintext=None, subtext=None):
        QMessageBox.information(self.window, maintext, subtext, QMessageBox.Ok,
                                QMessageBox.Ok)

    def getPathFromDialog(self):
        return QFileDialog.getExistingDirectory(self.window, "Select folder",
                                                self.lastKnownDir,
                                                QFileDialog.ShowDirsOnly)

    def addEventListeners(self):
        self.speedLimitSlider.valueChanged[int].connect(self.updateSpeedLimit)
        self.speedLimitSpin.valueChanged[int].connect(self.updateSpeedLimit)
        self.sharedLocationBtn.clicked.connect(self.showDirectorySelector)
        self.downloadLocationBtn.clicked.connect(self.showDirectorySelector)
        self.toggleShareBtn.setText("Start Sharing")
        self.toggleShareBtn.clicked.connect(self.toggleShare)
        self.server.ftp_handler.stats.clientConnect.connect(
            self.statClientConnected)
        self.server.ftp_handler.stats.clientDisconnect.connect(
            self.statClientDisconnected)
        self.server.ftp_handler.stats.fileTransfer[int].connect(
            self.statFileTransferred)
        self.reloadUsersBtn.clicked.connect(self.loadUsers)
        self.browserInput.returnPressed.connect(self.browserGoBtn.click)
        self.browserGoBtn.clicked.connect(self.loadBrowserTable)
        self.browserHomeBtn.clicked.connect(self.loadBrowserTable)
        self.browserPrevBtn.clicked.connect(self.handleBackBtnClick)
        self.userListTable.doubleClicked.connect(self.showBrowser)
        self.browserTable.doubleClicked.connect(self.handleFileSelection)
        self.developerLink.linkActivated.connect(xdg_open)
        self.projectLink.linkActivated.connect(xdg_open)

    def showDirectorySelector(self, event):
        if self.window.sender() is self.downloadLocationBtn:
            self.downloadLocationInput.setText(self.getPathFromDialog())
            self.destPrefix = self.downloadLocationInput.text()
        elif self.window.sender() is self.sharedLocationBtn:
            self.sharedLocationInput.setText(self.getPathFromDialog())

    def updateSpeedLimit(self, value):
        self.speedLimitSlider.setValue(value)
        self.speedLimitSpin.setValue(value)

    def statClientConnected(self):
        self.server.connected += 1
        self.stats_connected.setText(str(self.server.connected))

    def statClientDisconnected(self):
        self.server.connected -= 1
        self.stats_connected.setText(str(self.server.connected))

    def statFileTransferred(self, filesize):
        self.server.bytesTransferred += filesize
        self.server.filesTransferred += 1
        self.stats_files.setText(str(self.server.filesTransferred))
        self.stats_bytes.setText(toHumanReadable(self.server.bytesTransferred))

    def toggleShare(self):
        try:
            if  (not self.publicNameInput.text()) or \
                (not self.port.value()) or \
                (not self.sharedLocationInput.text()):
                raise FormIncompleteError
            if self.xchgClient.isRunning():
                self.xchgClient.quit()
                if not self.xchgClient.wait(1):
                    self.xchgClient.terminate()
            if self.server.isRunning():
                self.server.stopServer()
                self.toggleShareBtn.setText("Start Sharing")
                self.toggleShareBtn.setIcon(QIcon(":/images/failed.svg"))
                self.urlFrame.setVisible(False)
            else:
                self.server.setPort(self.port.value())
                self.server.setSharedDirectory(self.sharedLocationInput.text())
                self.server.start()
                if not self.exchangeURLInput.text():
                    self.xchgClient.updateInfo(self.publicNameInput.text(),
                                               None, self.port.value())
                else:
                    self.xchgClient.updateInfo(self.publicNameInput.text(),
                                               self.exchangeURLInput.text(),
                                               self.port.value())
                self.xchgClient.updateDir(self.sharedLocationInput.text())
                self.settings.update(self.publicNameInput.text(), self.port.value(), \
                    self.sharedLocationInput.text(), self.downloadLocationInput.text(), self.speedLimitSlider.value(), self.exchangeURLInput.text())
                self.toggleShareBtn.setText("Stop Sharing")
                self.toggleShareBtn.setIcon(QIcon(":/images/complete.svg"))
                addresses = getAllAddresses()
                print(addresses)
                if len(addresses) != 0:
                    lblstr = "<html><body>"
                    current = 0
                    end = len(addresses) - 1
                    for addr in addresses:
                        hyperlink = 'ftp://' + addr + ':' + str(
                            self.server.port)
                        lblstr += "<a href=\'" + hyperlink + "\'>" + hyperlink + "</a>"
                        if current != (end - 1):
                            lblstr += '<br>'
                        current += 1
                    lblstr += "</body></html>"
                    self.urlLabel.setText(lblstr)
                    self.urlFrame.setVisible(True)
                    print(self.urlFrame.isVisible(), 'url frame is visivle')
        except FileNotFoundError:
            self.showMessage("Don't fool me", "Shared location doesn't exist")
        except PortUnavailableError:
            self.showMessage("Port unavailable",
                             "Please select some other port number")
        except FormIncompleteError:
            self.showMessage("Form incomplete",
                             "Please fill in proper values!")

    def loadUsers(self):
        userlist = self.xchgClient.getUserList()
        self.userlist = userlist
        if not userlist:
            self.showMessage("Sorry", "Cannot retrieve list of users")
            return
        table = self.userListTable
        table.clearContents()
        table.setRowCount(len(userlist))
        for i, entry in enumerate(userlist):
            table.setItem(i, 0, QTableWidgetItem(str(i)))
            table.setItem(
                i, 1,
                QTableWidgetItem(toHumanReadable(int(entry["sharedSize"]))))
            table.setItem(i, 2, QTableWidgetItem(entry["publicName"]))

    def showBrowser(self):
        if not self.userlist:
            return
        current = self.userListTable.selectedItems()[0]
        index = int(self.userListTable.item(current.row(), 0).text())
        user = self.userlist[index]
        self.browser.update(user["ip"], int(user["port"]))
        self.tabWidget.setCurrentIndex(2)
        self.browserInput.setText("/")
        self.browserGoBtn.click()

    def loadBrowserTable(self):
        pwd = self.browserInput.text()
        filelist = []
        try:
            if not self.browser.pathExists(self.browser.host,
                                           self.browser.port, pwd):
                self.showMessage("Error", "The path does not exist!")
                return
            self.browser.historyStack.append(pwd)
            filelist = self.browser.getFileList(self.browser.host,
                                                self.browser.port, pwd)
            self.browser.filelist = filelist
        except ConnectionRefusedError:
            self.showMessage(
                "Offline",
                "The remote machine cannot be contacted!\nBetter luck next time."
            )
            self.tabWidget.setCurrentIndex(1)
        table = self.browserTable
        table.clearContents()
        table.setRowCount(len(filelist))
        for i, file in enumerate(filelist):
            table.setItem(i, 0, QTableWidgetItem(str(i)))
            table.setItem(i, 1,
                          QTableWidgetItem(QIcon(":images/download.png"), ""))
            table.setItem(i, 2,
                          QTableWidgetItem(toHumanReadable(file["filesize"])))
            if file["isDir"]:
                table.setItem(
                    i, 3, QTableWidgetItem(QIcon(":/images/folder.png"), ""))
            else:
                table.setItem(
                    i, 3, QTableWidgetItem(guess_mime(file["filename"])[0]))
            table.setItem(i, 4, QTableWidgetItem(file["filename"]))

    def handleBackBtnClick(self):
        if not self.userlist:
            self.showMessage(
                "Confused",
                "What should I load? \nFind someone from list of connected users."
            )
            return
        if len(self.browser.historyStack) < 2:
            self.showMessage("Sorry", "Hey, there's no looking back!")
            return
        self.browser.historyStack.pop()
        prev = self.browser.historyStack.pop()
        self.browserInput.setText(prev)
        self.loadBrowserTable()

    def handleFileSelection(self):
        if not self.browser.filelist:
            return
        current = self.browserTable.selectedItems()[0]
        index = int(self.browserTable.item(current.row(), 0).text())
        file = self.browser.filelist[index]
        print("index", index, file["pathname"], current.text())
        if file["isDir"] and current.column() is not 1:
            pwd = join_path(self.browserInput.text(), file["pathname"])
            self.browserInput.setText(pwd)
            self.browserGoBtn.click()
            return
        # a download is to be handled
        print("downloading directory", file["filename"])
        # decide it is a file or directory
        if not self.destPrefix:
            destDir = join_path(self.getPathFromDialog())
        else:
            destDir = self.destPrefix
        meta = None
        if file["isDir"]:
            meta = self.browser.getRecursiveFileList(self.browser.host,
                                                     self.browser.port,
                                                     file["pathname"])
            filelist = self.browser.recfilelist
        else:
            meta = {"totalFiles": 1, "totalSize": file["filesize"]}
            filelist = [file]
        signal = DownloadItemUpdater()
        diui = self.createDownloadItemBox(file["filename"], meta["totalSize"])
        dilist = []
        for item in filelist:
            di = DownloadItem(item["filename"], self.browser.host,
                              self.browser.port, item["pathname"],
                              join_path(destDir, item["filename"]),
                              item["filesize"], signal)
            di.updateGuiComponents(diui)
            dilist.append(di)

        # create callbacks for gui events
        def cancelCallback():
            for di in dilist:
                di.cancel()
            diui["cancelBtn"].setIcon(QIcon(":/images/reload.png"))
            diui["cancelBtn"].clicked.disconnect()
            diui["cancelBtn"].clicked.connect(retryCallback)

        def updateProgressCallback(progress):
            sum = 0
            for di in dilist:
                sum += di.completed
            diui["progress"].setValue(sum)
            diui["completion"].setText(toHumanReadable(sum))

        def retryCallback():
            print("retrying")
            di.completed = 0
            self.downman.addItem(di)
            diui["cancelBtn"].clicked.disconnect()
            diui["cancelBtn"].clicked.connect(cancelCallback)
            diui["cancelBtn"].setIcon(QIcon(":/images/cancel.png"))

        def errorCallback():
            diui["completion"].setText("Failed")
            cancelCallback()

        def completeCallback():
            diui["completion"].setText("Completed")
            diui["cancelBtn"].clicked.disconnect()
            diui["cancelBtn"].setToolTip("Open")
            diui["cancelBtn"].setIcon(QIcon(":/images/open.png"))
            diui["cancelBtn"].clicked.connect(openFile)

        def openFile():
            xdg_open(join_path(destDir, file["filename"]))

        def openDir():
            xdg_open(destDir)

        diui["cancelBtn"].clicked.connect(cancelCallback)
        signal.progress[int].connect(updateProgressCallback)
        signal.error.connect(errorCallback)
        signal.complete.connect(completeCallback)
        diui["openDestBtn"].clicked.connect(openDir)
        self.downloadsLayout.insertWidget(0, diui["widget"])
        for entry in dilist:
            self.downman.addItem(entry)
        # print ("downloading", di.filename, di.filesize, "to")

    def createDownloadItemBox(self, filename, filesize):
        diui = {}
        frame = QFrame(self.window)
        frame.setFrameStyle(QFrame.StyledPanel)
        layout = QHBoxLayout()
        filesize = filesize if not filesize is 0 else 1
        diui["layout"] = layout
        diui["widget"] = frame
        diui["filename"] = QLabel(filename)
        diui["filename"].setToolTip(filename)
        diui["filename"].setMaximumWidth(30)
        diui["filesize"] = QLabel(toHumanReadable(filesize))
        diui["filesize"].setAlignment(Qt.AlignCenter)
        diui["progress"] = QProgressBar()
        diui["progress"].setRange(0, filesize)
        diui["completion"] = QLabel("Waiting...")
        diui["completion"].setAlignment(Qt.AlignCenter)
        diui["cancelBtn"] = QPushButton(QIcon(":/images/cancel.png"), '')
        diui["openDestBtn"] = QPushButton(QIcon(":/images/folder.png"), '')
        diui["cancelBtn"].setToolTip("Cancel download")
        diui["openDestBtn"].setToolTip("Open folder")
        diui["layout"].addWidget(diui["filename"])
        diui["layout"].addWidget(diui["filesize"])
        diui["layout"].addWidget(diui["progress"])
        diui["layout"].addWidget(diui["completion"])
        diui["layout"].addWidget(diui["openDestBtn"])
        diui["layout"].addWidget(diui["cancelBtn"])
        diui["layout"].setSpacing(0)
        layout.setContentsMargins(5, 2, 5, 2)
        layout.setSpacing(6)
        layout.setStretch(0, 3)
        layout.setStretch(1, 2)
        layout.setStretch(2, 5)
        layout.setStretch(3, 2)
        layout.setStretch(4, 1)
        layout.setStretch(5, 1)
        frame.setLayout(layout)
        return diui

    def showWindow(self, checked):
        self.window.setVisible(checked)

    def setupSystemTray(self):
        self.activateAction = QAction("Show", self.window)
        self.activateAction.setCheckable(True)
        self.activateAction.setChecked(True)
        self.activateAction.triggered.connect(self.showWindow)
        self.quitAction = QAction("Quit", self.window)
        self.quitAction.triggered.connect(self.closeEvent)
        self.trayIconMenu = QMenu(self.window)
        self.trayIconMenu.addAction(self.activateAction)
        self.trayIconMenu.addAction(self.quitAction)
        self.trayIcon = QSystemTrayIcon(QIcon(":/images/icon.ico"),
                                        self.window)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.show()
Exemplo n.º 31
0
class PackTreeView(TreeView):
    EMPTY_VIEW_MSG = "Drop AAS files here"
    EMPTY_VIEW_ICON = OPEN_DRAG_ICON

    def __init__(self, parent=None):
        super(PackTreeView, self).__init__(parent,
                                           emptyViewMsg=self.EMPTY_VIEW_MSG,
                                           emptyViewIcon=self.EMPTY_VIEW_ICON)
        PackTreeView.__instance = self
        self.recentFilesSeparator = None
        self.setAcceptDrops(True)
        self.setExpandsOnDoubleClick(False)

    # noinspection PyArgumentList
    def initActions(self):
        super(PackTreeView, self).initActions()
        self.addAct.setText("Add package")
        self.addAct.setEnabled(True)

        self.newPackAct = QAction(NEW_PACK_ICON,
                                  "&New AAS file",
                                  self,
                                  statusTip="Create new AAS file",
                                  triggered=self.newPackWithDialog,
                                  enabled=True)

        self.defNewFileTypeFilter = ""
        self.defNewFileTypeActs = []
        for typ in FILE_TYPE_FILTERS.keys():
            act = QAction(
                typ,
                self,
                checkable=True,
                statusTip=f"Choose {typ} as standard initialisation file type",
                triggered=self.toggleDefNewFileType)
            self.defNewFileTypeActs.append(act)

        self.openPackAct = QAction(OPEN_ICON,
                                   "&Open AAS file",
                                   self,
                                   shortcut=SC_OPEN,
                                   statusTip="Open AAS file",
                                   triggered=self.openPackWithDialog,
                                   enabled=True)

        # Recent files actions
        self.recentFileActs = []
        for i in range(MAX_RECENT_FILES):
            recentFileAct = QAction("",
                                    self,
                                    statusTip=f"Open recent file",
                                    triggered=self.openRecentSlot,
                                    visible=False)
            self.recentFileActs.append(recentFileAct)

        self.saveAct = QAction(SAVE_ICON,
                               "Save",
                               self,
                               statusTip="Save current file",
                               triggered=lambda: self.savePack(),
                               enabled=False)

        self.saveAsAct = QAction("Save As...",
                                 self,
                                 statusTip="Save current file as..",
                                 triggered=lambda: self.savePackAsWithDialog(),
                                 enabled=False)

        self.saveAllAct = QAction(SAVE_ALL_ICON,
                                  "&Save All",
                                  self,
                                  shortcut=SC_SAVE_ALL,
                                  statusTip="Save all files",
                                  triggered=self.saveAll,
                                  enabled=True)

        self.closeAct = QAction("Close AAS file",
                                self,
                                statusTip="Close current file",
                                triggered=self.closeFileWithDialog,
                                enabled=False)

        self.closeAllAct = QAction("Close all",
                                   self,
                                   statusTip="Close all files",
                                   triggered=self.closeAllFilesWithDialog,
                                   enabled=False)

        self.autoScrollToSrcAct = QAction(
            "Autoscroll to source",
            self,
            # icon=AUTOSCROLL_TO_SRC_ICON,
            toolTip="Autoscroll to source",
            statusTip="Autoscroll to source",
            checkable=True)
        self.autoScrollToSrcAct.toggle()
        self.autoScrollFromSrcAct = QAction(
            "Autoscroll from source",
            self,
            # icon=AUTOSCROLL_FROM_SRC_ICON,
            toolTip="Autoscroll from source",
            statusTip="Autoscroll from source",
            checkable=True)
        self.autoScrollFromSrcAct.toggle()

        self.shellViewAct = QAction(VIEW_ICON,
                                    "Shell view",
                                    self,
                                    toolTip="Shell view",
                                    statusTip="Change to shell view",
                                    triggered=self.onShellViewPushed,
                                    checkable=True)

    def toggleDefNewFileType(self):
        #FIXME refactor
        action = self.sender()
        if action:
            typ = action.text()
            self.defNewFileTypeFilter = FILE_TYPE_FILTERS[typ]

    def onShellViewPushed(self):
        checked = self.shellViewAct.isChecked()

        packs = self.sourceModel().match(QModelIndex(), TYPE_ROLE, Package)
        if checked:
            for pack in packs:
                CLASSES_INFO[AssetAdministrationShell][PACKVIEW_ATTRS_INFO] = {
                    "asset": {},
                    "submodel": {}
                }
                self.sourceModel().update(pack)

            rowsToHide = []
            for attr in ("submodels", "assets", "concept_descriptions",
                         "others"):
                rowsToHide.extend(self.model().match(QModelIndex(),
                                                     Qt.DisplayRole, attr))
            for row in rowsToHide:
                self.setRowHidden(row.row(), row.parent(), True)
        else:
            CLASSES_INFO[AssetAdministrationShell][PACKVIEW_ATTRS_INFO] = {}
            for pack in packs:
                self.sourceModel().update(pack)

    # noinspection PyUnresolvedReferences
    def initMenu(self):
        super(PackTreeView, self).initMenu()
        self.attrsMenu.addSeparator()
        self.attrsMenu.addAction(self.openPackAct)
        self.attrsMenu.addAction(self.saveAct)
        self.attrsMenu.addAction(self.saveAsAct)
        self.attrsMenu.addAction(self.saveAllAct)
        self.attrsMenu.addAction(self.closeAct)
        self.attrsMenu.addAction(self.closeAllAct)

        self.openInCurrTabAct.triggered.connect(
            lambda: self.openInCurrTabClicked.emit(self.currentIndex()))
        self.openInNewTabAct.triggered.connect(
            lambda: self.openInNewTabClicked.emit(self.currentIndex()))
        self.openInBackgroundAct.triggered.connect(
            lambda: self.openInBgTabClicked.emit(self.currentIndex()))
        self.openInNewWindowAct.triggered.connect(
            lambda: self.openInNewWindowClicked.emit(self.currentIndex()))

    def updateActions(self, index: QModelIndex):
        super(PackTreeView, self).updateActions(index)

        if index.isValid():
            self.openInCurrTabAct.setEnabled(True)
            self.openInNewTabAct.setEnabled(True)
            self.openInBackgroundAct.setEnabled(True)
            self.openInNewWindowAct.setEnabled(True)

        # update save and close actions
        self.saveAct.setEnabled(self.isSaveOk())
        self.saveAct.setText(f"Save {index.data(PACKAGE_ROLE)}")
        self.saveAct.setToolTip(f"Save {index.data(PACKAGE_ROLE)}")
        self.saveAsAct.setEnabled(self.isSaveOk())
        self.saveAllAct.setEnabled(self.isSaveAllOk())
        self.closeAct.setEnabled(self.isCloseOk())
        self.closeAllAct.setEnabled(self.isCloseAllOk())

        # update add action
        if not index.isValid():
            self.addAct.setEnabled(True)
            self.addAct.setText("Add package")

    def onDelClear(self):
        index = self.currentIndex()
        if isinstance(index.data(OBJECT_ROLE), Package):
            self.closeAct.trigger()
        else:
            super(PackTreeView, self).onDelClear()

    def isPasteOk(self, index: QModelIndex) -> bool:
        if not self.treeObjClipboard or not index.isValid():
            return False

        if super(PackTreeView, self).isPasteOk(index):
            return True

        obj2paste = self.treeObjClipboard[0]
        currObj = index.data(OBJECT_ROLE)

        if ClassesInfo.addType(type(currObj)) and isinstance(
                obj2paste, ClassesInfo.addType(type(currObj))):
            return True
        return False

    def isSaveOk(self) -> bool:
        pack = self.currentIndex().data(PACKAGE_ROLE)
        return True if pack else False

    def isCloseOk(self) -> bool:
        pack = self.currentIndex().data(PACKAGE_ROLE)
        return True if pack else False

    def isSaveAllOk(self) -> bool:
        return True if self.model().data(QModelIndex(),
                                         OPENED_PACKS_ROLE) else False

    def isCloseAllOk(self) -> bool:
        return True if self.model().data(QModelIndex(),
                                         OPENED_PACKS_ROLE) else False

    def onAddAct(self, objVal=None, parent: QModelIndex = None):
        parent = parent if parent else self.currentIndex()
        name = parent.data(NAME_ROLE)
        parentObj = parent.data(OBJECT_ROLE)

        if objVal:
            kwargs = {"parent": parent, "rmDefParams": False, "objVal": objVal}
        else:
            kwargs = {"parent": parent, "rmDefParams": True}

        try:
            if not parent.isValid():
                self.newPackWithDialog()
            elif name in Package.addableAttrs():
                self.addItemWithDialog(objType=ClassesInfo.addType(
                    Package, name),
                                       **kwargs)
            elif ClassesInfo.addType(type(parentObj)):
                self.addItemWithDialog(objType=ClassesInfo.addType(
                    type(parentObj)),
                                       **kwargs)
            else:
                raise TypeError("Parent type is not extendable:",
                                type(parent.data(OBJECT_ROLE)))
        except Exception as e:
            print(e)
            QMessageBox.critical(self, "Error", str(e))

    def addItemWithDialog(self,
                          parent: QModelIndex,
                          objType,
                          objVal=None,
                          title="",
                          rmDefParams=False):
        if objType is Package:
            self.newPackWithDialog()
            return
        elif objType is StoredFile:
            self.addFileWithDialog(parent)
            return
        super(PackTreeView, self).addItemWithDialog(parent, objType, objVal,
                                                    title, rmDefParams)

    def newPackWithDialog(self):
        saved = False
        file = 'new_aas_file.aasx'

        while not saved:
            file = QFileDialog.getSaveFileName(
                self,
                'Create new AAS File',
                file,
                filter=FILTER_AAS_FILES,
                initialFilter=self.defNewFileTypeFilter,
                options=FILE_DIALOG_OPTIONS)[0]
            if file:
                pack = Package()
                saved = self.savePack(pack, file)
                if saved:
                    self.model().setData(QModelIndex(), pack, ADD_ITEM_ROLE)
            else:
                # cancel pressed
                return

    def openPackWithDialog(self):
        opened = False
        file = ""
        while not opened:
            file = QFileDialog.getOpenFileName(self,
                                               "Open AAS file",
                                               file,
                                               filter=FILTER_AAS_FILES,
                                               options=FILE_DIALOG_OPTIONS)[0]
            if file:
                opened = self.openPack(file)
            else:
                # cancel pressed
                return

    def addFileWithDialog(self, parent: QModelIndex):
        opened = False
        file = ""
        while not opened:
            file = QFileDialog.getOpenFileName(self,
                                               "Add file",
                                               file,
                                               options=FILE_DIALOG_OPTIONS)[0]
            if file:
                storedFile = StoredFile(filePath=file)
                opened = self.model().setData(parent, storedFile,
                                              ADD_ITEM_ROLE)
            else:
                # cancel pressed
                return

    def openPack(self, file: str) -> bool:
        try:
            pack = Package(file)
            absFile = pack.file.absolute().as_posix()
            self.updateRecentFiles(absFile)
        except Exception as e:
            self.removeFromRecentFiles(file)
            QMessageBox.critical(self, "Error",
                                 f"Package {file} couldn't be opened: {e}")
        else:
            openedPacks = self.model().data(QModelIndex(), OPENED_FILES_ROLE)
            if Path(file).absolute() in openedPacks:
                QMessageBox.critical(self, "Error",
                                     f"Package {file} is already opened")
            else:
                self.model().setData(QModelIndex(), pack, ADD_ITEM_ROLE)
                return True
        return False

    def savePack(self, pack: Package = None, file: str = None) -> bool:
        pack = self.currentIndex().data(PACKAGE_ROLE) if pack is None else pack
        try:
            pack.write(file)
            self.updateRecentFiles(pack.file.absolute().as_posix())
            return True
        except (TypeError, ValueError) as e:
            QMessageBox.critical(self, "Error",
                                 f"Package couldn't be saved: {file}: {e}")
        except AttributeError as e:
            QMessageBox.critical(self, "Error",
                                 f"No chosen package to save: {e}")
        return False

    def savePackAsWithDialog(self, pack: Package = None) -> bool:
        pack = self.currentIndex().data(PACKAGE_ROLE) if pack is None else pack
        saved = False
        file = pack.file.as_posix()
        while not saved:
            try:
                file = QFileDialog.getSaveFileName(
                    self,
                    'Save AAS File',
                    file,
                    filter=FILTER_AAS_FILES,
                    options=FILE_DIALOG_OPTIONS)[0]
            except AttributeError as e:
                QMessageBox.critical(self, "Error",
                                     f"No chosen package to save: {e}")
            else:
                if file:
                    saved = self.savePack(pack, file)
                else:
                    # cancel pressed
                    return

    def saveAll(self):
        for pack in self.model().data(QModelIndex(), OPENED_PACKS_ROLE):
            self.savePack(pack)

    def closeFileWithDialog(self):
        pack = self.currentIndex().data(PACKAGE_ROLE)
        try:
            packItem, = self.model().match(QModelIndex(),
                                           OBJECT_ROLE,
                                           pack,
                                           hits=1)
        except ValueError:
            QMessageBox.critical(self, "Not found error",
                                 f"The file to close is not found: {pack}")
        if packItem.isValid():
            try:
                dialog = QMessageBox(
                    QMessageBox.NoIcon,
                    f"Close {pack}",
                    f"Do you want to save your changes in {pack} before closing?",
                    standardButtons=QMessageBox.Save | QMessageBox.Cancel
                    | QMessageBox.Discard)
                dialog.setDefaultButton = QMessageBox.Save
                dialog.button(QMessageBox.Save).setText("&Save&Close")
                res = dialog.exec()
                if res == QMessageBox.Save:
                    self.savePack()
                    self.closeFile(packItem)
                elif res == QMessageBox.Discard:
                    self.closeFile(packItem)
            except AttributeError as e:
                QMessageBox.critical(self, "Error",
                                     f"No chosen package to close: {e}")

    def closeAllFilesWithDialog(self):
        dialog = QMessageBox(
            QMessageBox.NoIcon,
            f"Close all AAS files",
            f"Do you want to save your changes before closing? ",
            standardButtons=QMessageBox.Save | QMessageBox.Cancel
            | QMessageBox.Discard)
        dialog.setDefaultButton = QMessageBox.Save
        dialog.button(QMessageBox.Save).setText("&Save and Close All")
        res = dialog.exec()
        if res == QMessageBox.Save:
            for pack in self.model().data(QModelIndex(), OPENED_PACKS_ROLE):
                self.savePack(pack)
                packItem, = self.model().match(QModelIndex(),
                                               OBJECT_ROLE,
                                               pack,
                                               hits=1)
                self.closeFile(packItem)
        elif res == QMessageBox.Discard:
            for pack in self.model().data(QModelIndex(), OPENED_PACKS_ROLE):
                packItem, = self.model().match(QModelIndex(),
                                               OBJECT_ROLE,
                                               pack,
                                               hits=1)
                self.closeFile(packItem)

    def closeFile(self, packItem: QModelIndex):
        self.model().setData(packItem, NOT_GIVEN, CLEAR_ROW_ROLE)

    def openRecentSlot(self):
        action = self.sender()
        if action:
            self.openPack(action.data())

    def updateRecentFiles(self, file: str):
        self.removeFromRecentFiles(file)
        settings = QSettings(ACPLT, APPLICATION_NAME)
        files = settings.value('recentFiles', [])
        files.insert(0, file)
        del files[MAX_RECENT_FILES:]
        settings.setValue('recentFiles', files)

    def removeFromRecentFiles(self, file: str):
        settings = QSettings(ACPLT, APPLICATION_NAME)
        files = settings.value('recentFiles', [])
        try:
            files.remove(file)
        except ValueError:
            pass
        except AttributeError:
            files = []
        settings.setValue('recentFiles', files)

    def updateRecentFileActs(self):
        settings = QSettings(ACPLT, APPLICATION_NAME)
        files = settings.value('recentFiles', [])
        try:
            files = files[:MAX_RECENT_FILES]
        except TypeError:
            files = []

        for i, file in enumerate(files):
            if len(file) < 30:
                self.recentFileActs[i].setText(file)
            else:
                self.recentFileActs[i].setText(f"..{file[len(file)-30:]}")
            self.recentFileActs[i].setData(file)
            self.recentFileActs[i].setVisible(True)

        for i in range(len(files), MAX_RECENT_FILES):
            self.recentFileActs[i].setVisible(False)

        self.recentFilesSeparator.setVisible(bool(files))

    def dragEnterEvent(self, event: QDragEnterEvent):
        if event.mimeData().hasUrls:
            event.accept()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()

    def dropEvent(self, e: QDropEvent) -> None:
        for url in e.mimeData().urls():
            file = str(url.toLocalFile())
            self.openPack(file)
Exemplo n.º 32
0
class WebBrowser(QMainWindow):

    adjTitle = pyqtSignal(str)
    setProg = pyqtSignal(int)
    finishLoad = pyqtSignal(str)

    def __init__(self, url, parent=None):
        super(WebBrowser, self).__init__(parent)

        self.progress = 0

        fd = QFile(":/jquery.min.js")

        if fd.open(QIODevice.ReadOnly | QFile.Text):
            self.jQuery = QTextStream(fd).readAll()
            fd.close()
        else:
            self.jQuery = ''

        QNetworkProxyFactory.setUseSystemConfiguration(True)

        self.view = QWebEngineView(self)
        self.view.load(QUrl(url))
        self.view.loadFinished.connect(self.adjustLocation)
        self.view.titleChanged.connect(self.adjustTitle)
        self.view.loadProgress.connect(self.setProgress)
        self.view.loadFinished.connect(self.finishLoading)

        self.locationEdit = QLineEdit(self)
        self.locationEdit.setSizePolicy(
            QSizePolicy.Expanding,
            self.locationEdit.sizePolicy().verticalPolicy())
        self.locationEdit.returnPressed.connect(self.changeLocation)

        toolBar = self.addToolBar("Navigation")
        toolBar.addAction(self.view.pageAction(QWebEnginePage.Back))
        toolBar.addAction(self.view.pageAction(QWebEnginePage.Forward))
        toolBar.addAction(self.view.pageAction(QWebEnginePage.Reload))
        toolBar.addAction(self.view.pageAction(QWebEnginePage.Stop))
        toolBar.addWidget(self.locationEdit)

        viewMenu = self.menuBar().addMenu("&View")
        viewSourceAction = QAction("Page Source", self)
        viewSourceAction.triggered.connect(self.viewSource)
        viewMenu.addAction(viewSourceAction)

        effectMenu = self.menuBar().addMenu("&Effect")
        effectMenu.addAction("Highlight all links", self.highlightAllLinks)

        self.rotateAction = QAction(self.style().standardIcon(
            QStyle.SP_FileDialogDetailedView),
                                    "Turn images upside down",
                                    self,
                                    checkable=True,
                                    toggled=self.rotateImages)
        effectMenu.addAction(self.rotateAction)

        toolsMenu = self.menuBar().addMenu("&Tools")
        toolsMenu.addAction("Remove GIF images", self.removeGifImages)
        toolsMenu.addAction("Remove all inline frames",
                            self.removeInlineFrames)
        toolsMenu.addAction("Remove all object elements",
                            self.removeObjectElements)
        toolsMenu.addAction("Remove all embedded elements",
                            self.removeEmbeddedElements)
        self.setCentralWidget(self.view)

    def viewSource(self):
        accessManager = self.view.page().networkAccessManager()
        request = QNetworkRequest(self.view.url())
        reply = accessManager.get(request)
        reply.finished.connect(self.slotSourceDownloaded)

    def slotSourceDownloaded(self):
        reply = self.sender()
        self.textEdit = QTextEdit()
        self.textEdit.setAttribute(Qt.WA_DeleteOnClose)
        self.textEdit.show()
        self.textEdit.setPlainText(QTextStream(reply).readAll())
        self.textEdit.resize(600, 400)
        reply.deleteLater()

    def adjustLocation(self):
        self.locationEdit.setText(self.view.url().toString())

    def changeLocation(self):
        url = QUrl.fromUserInput(self.locationEdit.text())
        self.view.load(url)
        self.view.setFocus()

    def adjustTitle(self):
        if 0 < self.progress < 100:
            self.setWindowTitle("{0} ({1}%%)".format(self.view.title(),
                                                     self.progress))
        else:
            self.setWindowTitle(self.view.title())

    def setProgress(self, p):
        self.progress = p
        self.setProg.emit(self.progress)
        self.adjustTitle()

    def finishLoading(self):
        self.progress = 100
        self.adjustTitle()
        # self.view.page().mainFrame().evaluateJavaScript(self.jQuery)
        self.rotateImages(self.rotateAction.isChecked())

    def highlightAllLinks(self):
        code = """$('a').each(
                    function () {
                        $(this).css('background-color', 'yellow') 
                    } 
                  )"""
        self.view.page().javaScriptPrompt(code)

    def rotateImages(self, invert):
        if invert:
            code = """
                $('img').each(
                    function () {
                        $(this).css('-webkit-transition', '-webkit-transform 2s'); 
                        $(this).css('-webkit-transform', 'rotate(180deg)') 
                    } 
                )"""
        else:
            code = """
                $('img').each(
                    function () { 
                        $(this).css('-webkit-transition', '-webkit-transform 2s'); 
                        $(this).css('-webkit-transform', 'rotate(0deg)') 
                    } 
                )"""

        # self.view.page().mainFrame().evaluateJavaScript(code)

    def removeGifImages(self):
        code = "$('[src*=gif]').remove()"
        self.view.page().runJavaScript(code)

    def removeInlineFrames(self):
        code = "$('iframe').remove()"
        self.view.page().runJavaScript(code)

    def removeObjectElements(self):
        code = "$('object').remove()"
        self.view.page().runJavaScript(code)

    def removeEmbeddedElements(self):
        code = "$('embed').remove()"
        self.view.page().runJavaScript(code)
Exemplo n.º 33
0
class SimpleRichText(QTextEdit):

    # pylint: disable=method-hidden

    def __init__(self, focusin, focusout):
        QTextEdit.__init__(self)
        self.focusin = focusin
        self.focusout = focusout
        self.createActions()

        #self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)

    def focusOutEvent ( self, event ):
        self.focusout()

    def focusInEvent ( self, event ):
        self.focusin()


    def closeEvent(self, event):
        event.accept()

    def createActions(self):
        self.boldAct = QAction(self.tr("&Bold"), self)
        self.boldAct.setCheckable(True)
        self.boldAct.setShortcut(self.tr("Ctrl+B"))
        self.boldAct.setStatusTip(self.tr("Make the text bold"))
        self.boldAct.triggered.connect(self.setBold)
        ### self.connect(self.boldAct, SIGNAL("triggered()"), self.setBold)
        self.addAction(self.boldAct)

        boldFont = self.boldAct.font()
        boldFont.setBold(True)
        self.boldAct.setFont(boldFont)

        self.italicAct = QAction(self.tr("&Italic"), self)
        self.italicAct.setCheckable(True)
        self.italicAct.setShortcut(self.tr("Ctrl+I"))
        self.italicAct.setStatusTip(self.tr("Make the text italic"))
        self.italicAct.triggered.connect(self.setItalic)
        ### self.connect(self.italicAct, SIGNAL("triggered()"), self.setItalic)
        self.addAction(self.italicAct)

    def setBold(self):
        format = QTextCharFormat()
        if self.boldAct.isChecked():
            weight = QFont.Bold
        else:
            weight = QFont.Normal
        format.setFontWeight(weight)
        self.setFormat(format)

    def setItalic(self):
        format = QTextCharFormat()
        #format.setFontItalic(self.__italic.isChecked())
        format.setFontItalic(self.italicAct.isChecked())
        self.setFormat(format)

    def setUnderline(self):
        format = QTextCharFormat()
        format.setFontUnderline(self.__underline.isChecked())
        self.setFormat(format)

    def setFormat(self, format):
        self.textCursor().mergeCharFormat(format)
        self.mergeCurrentCharFormat(format)

    def bold(self):
        print("bold")

    def italic(self):
        print("italic")
Exemplo n.º 34
0
class QImageViewer(QMainWindow):
    def __init__(self):
        super().__init__()

        # gets all the images from given url
        self.url = "/home/emsee/Documents/Manga/Terror Man/chapter_1"
        image_list = sorted_nicely(
            [os.path.join(self.url, file) for file in os.listdir(self.url)])
        self.files_it = iter(image_list)
        pprint.pprint(image_list)

        self.printer = QPrinter()
        self.scaleFactor = 0.0

        self.imageLabel = [QLabel() for i in image_list]
        pprint.pprint(self.imageLabel)

        if len(image_list) == len(self.imageLabel):
            print("THESE TWO ARE EQUAL")

        map(lambda obj: obj.setBackgroundRole(QPalette.Base), self.imageLabel)
        map(
            lambda obj: obj.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.
                                          Ignored), self.imageLabel)
        map(lambda obj: obj.setScaledContents(True), self.imageLabel)
        # self.imageLabel.setBackgroundRole(QPalette.Base)
        # self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        # self.imageLabel.setScaledContents(True)

        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        # for i in self.imageLabel:
        #     self.scrollArea.setWidget(i)
        self.scrollArea.setVisible(False)

        self.setCentralWidget(self.scrollArea)

        #test2 shit
        content_widget = QWidget()
        self.scrollArea.setWidget(content_widget)
        self._lay = QVBoxLayout(content_widget)

        # print(type(self.url))
        # print(type(self.url[0]))
        self.count = 0

        for url in image_list:
            print("I am here: " + str(self.count))
            self.open(url)

        self.createActions()
        self.createMenus()

        self.setWindowTitle("Image Viewer")
        self.resize(800, 600)

    def open(self, url):
        print("IN THE OPEN FUNCTION")
        # options = QFileDialog.Options()
        # fileName = QFileDialog.getOpenFileName(self, "Open File", QDir.currentPath())
        # fileName, _ = QFileDialog.getOpenFileName(self, 'QFileDialog.getOpenFileName()', '',
        #                                           'Images (*.png *.jpeg *.jpg *.bmp *.gif)', options=options)

        fileName = url

        if fileName:
            image = QImage(fileName)
            if image.isNull():
                QMessageBox.information(self, "Image Viewer",
                                        "Cannot load %s." % fileName)
                return

            self.imageLabel[self.count].setPixmap(QPixmap.fromImage(image))
            # map(lambda obj: obj.setPixmap(QPixmap.fromImage(image)), self.imageLabel)
            # map(lambda obj: self._lay.addWidget(obj), self.imageLabel)
            self._lay.addWidget(self.imageLabel[self.count])
            self.count += 1
            self.scaleFactor = 1.0

            self.scrollArea.setVisible(True)
            self.printAct.setEnabled(True)
            self.fitToWindowAct.setEnabled(True)
            self.updateActions()

            if not self.fitToWindowAct.isChecked():
                self.imageLabel[self.count].adjustSize()
                # map(lambda obj: obj.imageLabel.adjustSize(), self.imageLabel)

    def print_(self):
        dialog = QPrintDialog(self.printer, self)
        if dialog.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.imageLabel.pixmap().size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(),
                                size.height())
            painter.setWindow(self.imageLabel.pixmap().rect())
            painter.drawPixmap(0, 0, self.imageLabel.pixmap())

    def zoomIn(self):
        self.scaleImage(1.25)

    def zoomOut(self):
        self.scaleImage(0.8)

    def normalSize(self):
        # self.imageLabel.adjustSize()
        map(lambda obj: obj.imageLabel.adjustSize(), self.imageLabel)
        self.scaleFactor = 1.0

    def fitToWindow(self):
        fitToWindow = self.fitToWindowAct.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.normalSize()

        self.updateActions()

    def about(self):
        QMessageBox.about(
            self, "About Image Viewer",
            "<p>The <b>Image Viewer</b> example shows how to combine "
            "QLabel and QScrollArea to display an image. QLabel is "
            "typically used for displaying text, but it can also display "
            "an image. QScrollArea provides a scrolling view around "
            "another widget. If the child widget exceeds the size of the "
            "frame, QScrollArea automatically provides scroll bars.</p>"
            "<p>The example demonstrates how QLabel's ability to scale "
            "its contents (QLabel.scaledContents), and QScrollArea's "
            "ability to automatically resize its contents "
            "(QScrollArea.widgetResizable), can be used to implement "
            "zooming and scaling features.</p>"
            "<p>In addition the example shows how to use QPainter to "
            "print an image.</p>")

    def createActions(self):
        self.openAct = QAction("&Open...",
                               self,
                               shortcut="Ctrl+O",
                               triggered=self.open)
        self.printAct = QAction("&Print...",
                                self,
                                shortcut="Ctrl+P",
                                enabled=False,
                                triggered=self.print_)
        self.exitAct = QAction("E&xit",
                               self,
                               shortcut="Ctrl+Q",
                               triggered=self.close)
        self.zoomInAct = QAction("Zoom &In (25%)",
                                 self,
                                 shortcut="Ctrl++",
                                 enabled=False,
                                 triggered=self.zoomIn)
        self.zoomOutAct = QAction("Zoom &Out (25%)",
                                  self,
                                  shortcut="Ctrl+-",
                                  enabled=False,
                                  triggered=self.zoomOut)
        self.normalSizeAct = QAction("&Normal Size",
                                     self,
                                     shortcut="Ctrl+S",
                                     enabled=False,
                                     triggered=self.normalSize)
        self.fitToWindowAct = QAction("&Fit to Window",
                                      self,
                                      enabled=False,
                                      checkable=True,
                                      shortcut="Ctrl+F",
                                      triggered=self.fitToWindow)
        self.aboutAct = QAction("&About", self, triggered=self.about)
        self.aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt)

    def createMenus(self):
        self.fileMenu = QMenu("&File", self)
        self.fileMenu.addAction(self.openAct)
        # self.fileMenu.addAction(self.printAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.fitToWindowAct)

        self.helpMenu = QMenu("&Help", self)
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

        self.menuBar().addMenu(self.fileMenu)
        self.menuBar().addMenu(self.viewMenu)
        self.menuBar().addMenu(self.helpMenu)

    def updateActions(self):
        self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())

    def scaleImage(self, factor):
        self.scaleFactor *= factor
        x = [map(lambda obj: obj.imageLabel.pixmap().size(), self.imageLabel)]
        for i in x:
            map(lambda obj: obj.imageLabel.resize(self.scaleFactor * x[i]),
                self.imageLabel)
        # self.imageLabel.resize(self.scaleFactor * x)

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)

        self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
        self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)

    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(
            int(factor * scrollBar.value() +
                ((factor - 1) * scrollBar.pageStep() / 2)))
Exemplo n.º 35
0
 def toggle_column(self, action: QtWidgets.QAction) -> None:
     column: AssEventsModelColumn = action.data()
     self.horizontalHeader().setSectionHidden(
         column.value, not action.isChecked()
     )
Exemplo n.º 36
0
class TextEdit(QMainWindow):
    def __init__(self, fileName=None, parent=None):
        super(TextEdit, self).__init__(parent)

        self.setWindowIcon(QIcon(':/images/logo.png'))
        self.setToolButtonStyle(Qt.ToolButtonFollowStyle)
        self.setupFileActions()
        self.setupEditActions()
        self.setupTextActions()

        helpMenu = QMenu("Help", self)
        self.menuBar().addMenu(helpMenu)
        helpMenu.addAction("About", self.about)
        helpMenu.addAction("About &Qt", QApplication.instance().aboutQt)
 
        self.textEdit = QTextEdit(self)
        self.textEdit.currentCharFormatChanged.connect(
                self.currentCharFormatChanged)
        self.textEdit.cursorPositionChanged.connect(self.cursorPositionChanged)
        self.setCentralWidget(self.textEdit)
        self.textEdit.setFocus()
        self.setCurrentFileName()
        self.fontChanged(self.textEdit.font())
        self.colorChanged(self.textEdit.textColor())
        self.alignmentChanged(self.textEdit.alignment())
        self.textEdit.document().modificationChanged.connect(
                self.actionSave.setEnabled)
        self.textEdit.document().modificationChanged.connect(
                self.setWindowModified)
        self.textEdit.document().undoAvailable.connect(
                self.actionUndo.setEnabled)
        self.textEdit.document().redoAvailable.connect(
                self.actionRedo.setEnabled)
        self.setWindowModified(self.textEdit.document().isModified())
        self.actionSave.setEnabled(self.textEdit.document().isModified())
        self.actionUndo.setEnabled(self.textEdit.document().isUndoAvailable())
        self.actionRedo.setEnabled(self.textEdit.document().isRedoAvailable())
        self.actionUndo.triggered.connect(self.textEdit.undo)
        self.actionRedo.triggered.connect(self.textEdit.redo)
        self.actionCut.setEnabled(False)
        self.actionCopy.setEnabled(False)
        self.actionCut.triggered.connect(self.textEdit.cut)
        self.actionCopy.triggered.connect(self.textEdit.copy)
        self.actionPaste.triggered.connect(self.textEdit.paste)
        self.textEdit.copyAvailable.connect(self.actionCut.setEnabled)
        self.textEdit.copyAvailable.connect(self.actionCopy.setEnabled)
        QApplication.clipboard().dataChanged.connect(self.clipboardDataChanged)

        if fileName is None:
            fileName = ':/example.html'

        if not self.load(fileName):
            self.fileNew()

    def closeEvent(self, e):
        if self.maybeSave():
            e.accept()
        else:
            e.ignore()

    def setupFileActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("File Actions")
        self.addToolBar(tb)

        menu = QMenu("&File", self)
        self.menuBar().addMenu(menu)

        self.actionNew = QAction(
                QIcon.fromTheme('document-new',
                        QIcon(rsrcPath + '/filenew.png')),
                "&New", self, priority=QAction.LowPriority,
                shortcut=QKeySequence.New, triggered=self.fileNew)
        tb.addAction(self.actionNew)
        menu.addAction(self.actionNew)

        self.actionOpen = QAction(
                QIcon.fromTheme('document-open',
                        QIcon(rsrcPath + '/fileopen.png')),
                "&Open...", self, shortcut=QKeySequence.Open,
                triggered=self.fileOpen)
        tb.addAction(self.actionOpen)
        menu.addAction(self.actionOpen)
        menu.addSeparator()

        self.actionSave = QAction(
                QIcon.fromTheme('document-save',
                        QIcon(rsrcPath + '/filesave.png')),
                "&Save", self, shortcut=QKeySequence.Save,
                triggered=self.fileSave, enabled=False)
        tb.addAction(self.actionSave)
        menu.addAction(self.actionSave)

        self.actionSaveAs = QAction("Save &As...", self,
                priority=QAction.LowPriority,
                shortcut=Qt.CTRL + Qt.SHIFT + Qt.Key_S,
                triggered=self.fileSaveAs)
        menu.addAction(self.actionSaveAs)
        menu.addSeparator()
 
        self.actionPrint = QAction(
                QIcon.fromTheme('document-print',
                        QIcon(rsrcPath + '/fileprint.png')),
                "&Print...", self, priority=QAction.LowPriority,
                shortcut=QKeySequence.Print, triggered=self.filePrint)
        tb.addAction(self.actionPrint)
        menu.addAction(self.actionPrint)

        self.actionPrintPreview = QAction(
                QIcon.fromTheme('fileprint',
                        QIcon(rsrcPath + '/fileprint.png')),
                "Print Preview...", self,
                shortcut=Qt.CTRL + Qt.SHIFT + Qt.Key_P,
                triggered=self.filePrintPreview)
        menu.addAction(self.actionPrintPreview)

        self.actionPrintPdf = QAction(
                QIcon.fromTheme('exportpdf',
                        QIcon(rsrcPath + '/exportpdf.png')),
                "&Export PDF...", self, priority=QAction.LowPriority,
                shortcut=Qt.CTRL + Qt.Key_D,
                triggered=self.filePrintPdf)
        tb.addAction(self.actionPrintPdf)
        menu.addAction(self.actionPrintPdf)
        menu.addSeparator()

        self.actionQuit = QAction("&Quit", self, shortcut=QKeySequence.Quit,
                triggered=self.close)
        menu.addAction(self.actionQuit)

    def setupEditActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("Edit Actions")
        self.addToolBar(tb)

        menu = QMenu("&Edit", self)
        self.menuBar().addMenu(menu)

        self.actionUndo = QAction(
                QIcon.fromTheme('edit-undo',
                        QIcon(rsrcPath + '/editundo.png')),
                "&Undo", self, shortcut=QKeySequence.Undo)
        tb.addAction(self.actionUndo)
        menu.addAction(self.actionUndo)

        self.actionRedo = QAction(
                QIcon.fromTheme('edit-redo',
                        QIcon(rsrcPath + '/editredo.png')),
                "&Redo", self, priority=QAction.LowPriority,
                shortcut=QKeySequence.Redo)
        tb.addAction(self.actionRedo)
        menu.addAction(self.actionRedo)
        menu.addSeparator()

        self.actionCut = QAction(
                QIcon.fromTheme('edit-cut', QIcon(rsrcPath + '/editcut.png')),
                "Cu&t", self, priority=QAction.LowPriority,
                shortcut=QKeySequence.Cut)
        tb.addAction(self.actionCut)
        menu.addAction(self.actionCut)

        self.actionCopy = QAction(
                QIcon.fromTheme('edit-copy',
                        QIcon(rsrcPath + '/editcopy.png')),
                "&Copy", self, priority=QAction.LowPriority,
                shortcut=QKeySequence.Copy)
        tb.addAction(self.actionCopy)
        menu.addAction(self.actionCopy)

        self.actionPaste = QAction(
                QIcon.fromTheme('edit-paste',
                        QIcon(rsrcPath + '/editpaste.png')),
                "&Paste", self, priority=QAction.LowPriority,
                shortcut=QKeySequence.Paste,
                enabled=(len(QApplication.clipboard().text()) != 0))
        tb.addAction(self.actionPaste)
        menu.addAction(self.actionPaste)

    def setupTextActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("Format Actions")
        self.addToolBar(tb)

        menu = QMenu("F&ormat", self)
        self.menuBar().addMenu(menu)

        self.actionTextBold = QAction(
                QIcon.fromTheme('format-text-bold',
                        QIcon(rsrcPath + '/textbold.png')),
                "&Bold", self, priority=QAction.LowPriority,
                shortcut=Qt.CTRL + Qt.Key_B, triggered=self.textBold,
                checkable=True)
        bold = QFont()
        bold.setBold(True)
        self.actionTextBold.setFont(bold)
        tb.addAction(self.actionTextBold)
        menu.addAction(self.actionTextBold)

        self.actionTextItalic = QAction(
                QIcon.fromTheme('format-text-italic',
                        QIcon(rsrcPath + '/textitalic.png')),
                "&Italic", self, priority=QAction.LowPriority,
                shortcut=Qt.CTRL + Qt.Key_I, triggered=self.textItalic,
                checkable=True)
        italic = QFont()
        italic.setItalic(True)
        self.actionTextItalic.setFont(italic)
        tb.addAction(self.actionTextItalic)
        menu.addAction(self.actionTextItalic)

        self.actionTextUnderline = QAction(
                QIcon.fromTheme('format-text-underline',
                        QIcon(rsrcPath + '/textunder.png')),
                "&Underline", self, priority=QAction.LowPriority,
                shortcut=Qt.CTRL + Qt.Key_U, triggered=self.textUnderline,
                checkable=True)
        underline = QFont()
        underline.setUnderline(True)
        self.actionTextUnderline.setFont(underline)
        tb.addAction(self.actionTextUnderline)
        menu.addAction(self.actionTextUnderline)

        menu.addSeparator()

        grp = QActionGroup(self, triggered=self.textAlign)

        # Make sure the alignLeft is always left of the alignRight.
        if QApplication.isLeftToRight():
            self.actionAlignLeft = QAction(
                    QIcon.fromTheme('format-justify-left',
                            QIcon(rsrcPath + '/textleft.png')),
                    "&Left", grp)
            self.actionAlignCenter = QAction(
                    QIcon.fromTheme('format-justify-center',
                            QIcon(rsrcPath + '/textcenter.png')),
                    "C&enter", grp)
            self.actionAlignRight = QAction(
                    QIcon.fromTheme('format-justify-right',
                            QIcon(rsrcPath + '/textright.png')),
                    "&Right", grp)
        else:
            self.actionAlignRight = QAction(
                    QIcon.fromTheme('format-justify-right',
                            QIcon(rsrcPath + '/textright.png')),
                    "&Right", grp)
            self.actionAlignCenter = QAction(
                    QIcon.fromTheme('format-justify-center',
                            QIcon(rsrcPath + '/textcenter.png')),
                    "C&enter", grp)
            self.actionAlignLeft = QAction(
                    QIcon.fromTheme('format-justify-left',
                            QIcon(rsrcPath + '/textleft.png')),
                    "&Left", grp)
 
        self.actionAlignJustify = QAction(
                QIcon.fromTheme('format-justify-fill',
                        QIcon(rsrcPath + '/textjustify.png')),
                "&Justify", grp)

        self.actionAlignLeft.setShortcut(Qt.CTRL + Qt.Key_L)
        self.actionAlignLeft.setCheckable(True)
        self.actionAlignLeft.setPriority(QAction.LowPriority)

        self.actionAlignCenter.setShortcut(Qt.CTRL + Qt.Key_E)
        self.actionAlignCenter.setCheckable(True)
        self.actionAlignCenter.setPriority(QAction.LowPriority)

        self.actionAlignRight.setShortcut(Qt.CTRL + Qt.Key_R)
        self.actionAlignRight.setCheckable(True)
        self.actionAlignRight.setPriority(QAction.LowPriority)

        self.actionAlignJustify.setShortcut(Qt.CTRL + Qt.Key_J)
        self.actionAlignJustify.setCheckable(True)
        self.actionAlignJustify.setPriority(QAction.LowPriority)

        tb.addActions(grp.actions())
        menu.addActions(grp.actions())
        menu.addSeparator()

        pix = QPixmap(16, 16)
        pix.fill(Qt.black)
        self.actionTextColor = QAction(QIcon(pix), "&Color...", self,
                triggered=self.textColor)
        tb.addAction(self.actionTextColor)
        menu.addAction(self.actionTextColor)

        tb = QToolBar(self)
        tb.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
        tb.setWindowTitle("Format Actions")
        self.addToolBarBreak(Qt.TopToolBarArea)
        self.addToolBar(tb)

        comboStyle = QComboBox(tb)
        tb.addWidget(comboStyle)
        comboStyle.addItem("Standard")
        comboStyle.addItem("Bullet List (Disc)")
        comboStyle.addItem("Bullet List (Circle)")
        comboStyle.addItem("Bullet List (Square)")
        comboStyle.addItem("Ordered List (Decimal)")
        comboStyle.addItem("Ordered List (Alpha lower)")
        comboStyle.addItem("Ordered List (Alpha upper)")
        comboStyle.addItem("Ordered List (Roman lower)")
        comboStyle.addItem("Ordered List (Roman upper)")
        comboStyle.activated.connect(self.textStyle)

        self.comboFont = QFontComboBox(tb)
        tb.addWidget(self.comboFont)
        self.comboFont.activated[str].connect(self.textFamily)

        self.comboSize = QComboBox(tb)
        self.comboSize.setObjectName("comboSize")
        tb.addWidget(self.comboSize)
        self.comboSize.setEditable(True)

        db = QFontDatabase()
        for size in db.standardSizes():
            self.comboSize.addItem("%s" % (size))

        self.comboSize.activated[str].connect(self.textSize)
        self.comboSize.setCurrentIndex(
                self.comboSize.findText(
                        "%s" % (QApplication.font().pointSize())))

    def load(self, f):
        if not QFile.exists(f):
            return False

        fh = QFile(f)
        if not fh.open(QFile.ReadOnly):
            return False

        data = fh.readAll()
        codec = QTextCodec.codecForHtml(data)
        unistr = codec.toUnicode(data)

        if Qt.mightBeRichText(unistr):
            self.textEdit.setHtml(unistr)
        else:
            self.textEdit.setPlainText(unistr)

        self.setCurrentFileName(f)
        return True

    def maybeSave(self):
        if not self.textEdit.document().isModified():
            return True

        if self.fileName.startswith(':/'):
            return True

        ret = QMessageBox.warning(self, "Application",
                "The document has been modified.\n"
                "Do you want to save your changes?",
                QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)

        if ret == QMessageBox.Save:
            return self.fileSave()

        if ret == QMessageBox.Cancel:
            return False

        return True

    def setCurrentFileName(self, fileName=''):
        self.fileName = fileName
        self.textEdit.document().setModified(False)

        if not fileName:
            shownName = 'untitled.txt'
        else:
            shownName = QFileInfo(fileName).fileName()

        self.setWindowTitle(self.tr("%s[*] - %s" % (shownName, "Rich Text")))
        self.setWindowModified(False)

    def fileNew(self):
        if self.maybeSave():
            self.textEdit.clear()
            self.setCurrentFileName()

    def fileOpen(self):
        fn, _ = QFileDialog.getOpenFileName(self, "Open File...", None,
                "HTML-Files (*.htm *.html);;All Files (*)")

        if fn:
            self.load(fn)

    def fileSave(self):
        if not self.fileName:
            return self.fileSaveAs()

        writer = QTextDocumentWriter(self.fileName)
        success = writer.write(self.textEdit.document())
        if success:
            self.textEdit.document().setModified(False)

        return success

    def fileSaveAs(self):
        fn, _ = QFileDialog.getSaveFileName(self, "Save as...", None,
                "ODF files (*.odt);;HTML-Files (*.htm *.html);;All Files (*)")

        if not fn:
            return False

        lfn = fn.lower()
        if not lfn.endswith(('.odt', '.htm', '.html')):
            # The default.
            fn += '.odt'

        self.setCurrentFileName(fn)
        return self.fileSave()

    def filePrint(self):
        printer = QPrinter(QPrinter.HighResolution)
        dlg = QPrintDialog(printer, self)

        if self.textEdit.textCursor().hasSelection():
            dlg.addEnabledOption(QPrintDialog.PrintSelection)

        dlg.setWindowTitle("Print Document")

        if dlg.exec_() == QPrintDialog.Accepted:
            self.textEdit.print_(printer)

        del dlg

    def filePrintPreview(self):
        printer = QPrinter(QPrinter.HighResolution)
        preview = QPrintPreviewDialog(printer, self)
        preview.paintRequested.connect(self.printPreview)
        preview.exec_()

    def printPreview(self, printer):
        self.textEdit.print_(printer)

    def filePrintPdf(self):
        fn, _ = QFileDialog.getSaveFileName(self, "Export PDF", None,
                "PDF files (*.pdf);;All Files (*)")

        if fn:
            if QFileInfo(fn).suffix().isEmpty():
                fn += '.pdf'

            printer = QPrinter(QPrinter.HighResolution)
            printer.setOutputFormat(QPrinter.PdfFormat)
            printer.setOutputFileName(fn)
            self.textEdit.document().print_(printer)

    def textBold(self):
        fmt = QTextCharFormat()
        fmt.setFontWeight(self.actionTextBold.isChecked() and QFont.Bold or QFont.Normal)
        self.mergeFormatOnWordOrSelection(fmt)

    def textUnderline(self):
        fmt = QTextCharFormat()
        fmt.setFontUnderline(self.actionTextUnderline.isChecked())
        self.mergeFormatOnWordOrSelection(fmt)

    def textItalic(self):
        fmt = QTextCharFormat()
        fmt.setFontItalic(self.actionTextItalic.isChecked())
        self.mergeFormatOnWordOrSelection(fmt)

    def textFamily(self, family):
        fmt = QTextCharFormat()
        fmt.setFontFamily(family)
        self.mergeFormatOnWordOrSelection(fmt)

    def textSize(self, pointSize):
        pointSize = float(pointSize)
        if pointSize > 0:
            fmt = QTextCharFormat()
            fmt.setFontPointSize(pointSize)
            self.mergeFormatOnWordOrSelection(fmt)

    def textStyle(self, styleIndex):
        cursor = self.textEdit.textCursor()
        if styleIndex:
            styleDict = {
                1: QTextListFormat.ListDisc,
                2: QTextListFormat.ListCircle,
                3: QTextListFormat.ListSquare,
                4: QTextListFormat.ListDecimal,
                5: QTextListFormat.ListLowerAlpha,
                6: QTextListFormat.ListUpperAlpha,
                7: QTextListFormat.ListLowerRoman,
                8: QTextListFormat.ListUpperRoman,
            }

            style = styleDict.get(styleIndex, QTextListFormat.ListDisc)
            cursor.beginEditBlock()
            blockFmt = cursor.blockFormat()
            listFmt = QTextListFormat()

            if cursor.currentList():
                listFmt = cursor.currentList().format()
            else:
                listFmt.setIndent(blockFmt.indent() + 1)
                blockFmt.setIndent(0)
                cursor.setBlockFormat(blockFmt)

            listFmt.setStyle(style)
            cursor.createList(listFmt)
            cursor.endEditBlock()
        else:
            bfmt = QTextBlockFormat()
            bfmt.setObjectIndex(-1)
            cursor.mergeBlockFormat(bfmt)

    def textColor(self):
        col = QColorDialog.getColor(self.textEdit.textColor(), self)
        if not col.isValid():
            return

        fmt = QTextCharFormat()
        fmt.setForeground(col)
        self.mergeFormatOnWordOrSelection(fmt)
        self.colorChanged(col)

    def textAlign(self, action):
        if action == self.actionAlignLeft:
            self.textEdit.setAlignment(Qt.AlignLeft | Qt.AlignAbsolute)
        elif action == self.actionAlignCenter:
            self.textEdit.setAlignment(Qt.AlignHCenter)
        elif action == self.actionAlignRight:
            self.textEdit.setAlignment(Qt.AlignRight | Qt.AlignAbsolute)
        elif action == self.actionAlignJustify:
            self.textEdit.setAlignment(Qt.AlignJustify)

    def currentCharFormatChanged(self, format):
        self.fontChanged(format.font())
        self.colorChanged(format.foreground().color())

    def cursorPositionChanged(self):
        self.alignmentChanged(self.textEdit.alignment())

    def clipboardDataChanged(self):
        self.actionPaste.setEnabled(len(QApplication.clipboard().text()) != 0)

    def about(self):
        QMessageBox.about(self, "About", 
                "This example demonstrates Qt's rich text editing facilities "
                "in action, providing an example document for you to "
                "experiment with.")

    def mergeFormatOnWordOrSelection(self, format):
        cursor = self.textEdit.textCursor()
        if not cursor.hasSelection():
            cursor.select(QTextCursor.WordUnderCursor)

        cursor.mergeCharFormat(format)
        self.textEdit.mergeCurrentCharFormat(format)

    def fontChanged(self, font):
        self.comboFont.setCurrentIndex(
                self.comboFont.findText(QFontInfo(font).family()))
        self.comboSize.setCurrentIndex(
                self.comboSize.findText("%s" % font.pointSize()))
        self.actionTextBold.setChecked(font.bold())
        self.actionTextItalic.setChecked(font.italic())
        self.actionTextUnderline.setChecked(font.underline())

    def colorChanged(self, color):
        pix = QPixmap(16, 16)
        pix.fill(color)
        self.actionTextColor.setIcon(QIcon(pix))

    def alignmentChanged(self, alignment):
        if alignment & Qt.AlignLeft:
            self.actionAlignLeft.setChecked(True)
        elif alignment & Qt.AlignHCenter:
            self.actionAlignCenter.setChecked(True)
        elif alignment & Qt.AlignRight:
            self.actionAlignRight.setChecked(True)
        elif alignment & Qt.AlignJustify:
            self.actionAlignJustify.setChecked(True)
Exemplo n.º 37
0
class MainWindow(QMainWindow):
    """Главное окно программы. Управляет интерфейсом и событиями."""
    def __init__(self, core):
        """Инициализация главного окна.

        core - ядро программы, содержащее логику."""
        super().__init__()
        self.core = core
        self.core.logged.connect(self.onLogged)
        self.initUI()
        self.command()

    def initUI(self):
        """Инициализировать графический интерфейс."""
        self.textEdit = QTextEdit()

        self.actionFileStart = QAction('&Start', self)
        self.actionFileStart.triggered.connect(self.onActionFileStartTriggered)
        self.actionFileStop = QAction('&Stop', self)
        self.actionFileStop.triggered.connect(self.onActionFileStopTriggered)
        self.actionFileStop.setDisabled(True)

        actionFileSave = QAction('&Save config', self)
        actionFileSave.triggered.connect(self.core.save)
        actionFileDraw = QAction('&Save chart', self)
        actionFileDraw.triggered.connect(self.core.draw)

        actionFileLogClear = QAction('&Clear', self)
        actionFileLogClear.triggered.connect(self.textEdit.clear)

        actionFileExit = QAction('&Exit', self)
        actionFileExit.triggered.connect(self.close)

        self.actionViewInfo = QAction('&Extended', self)
        self.actionViewInfo.setCheckable(True)
        self.actionViewInfo.setChecked(True)
        self.actionViewInfo.changed.connect(self.changeRow)

        actionConfigPeriod = QAction('&Period...', self)
        actionConfigPeriod.triggered.connect(self.actionConfigPeriodTriggered)

        actionConfigSensorsAdd = QAction('&Add...', self)
        actionConfigSensorsAdd.triggered.connect(
            self.actionConfigSensorsAddTriggered)

        actionConfigSensorsDel = QAction('&Delete...', self)
        actionConfigSensorsDel.triggered.connect(
            self.actionConfigSensorsDelTriggered)

        actionConfigPathAddresses = QAction('&Adressess...', self)
        actionConfigPathAddresses.triggered.connect(
            self.actionConfigPathAddressesTriggered)

        actionConfigPathSensors = QAction('&Sensors...', self)
        actionConfigPathSensors.triggered.connect(
            self.actionConfigPathSensorsTriggered)

        actionConfigPathData = QAction('&Data...', self)
        actionConfigPathData.triggered.connect(
            self.actionConfigPathDataTriggered)

        actionHelpHelp = QAction('&Help...', self)
        actionHelpHelp.triggered.connect(self.actionHelpHelpTriggered)
        actionHelpAbout = QAction('&About...', self)
        actionHelpAbout.triggered.connect(self.onAbout)

        menuFile = self.menuBar().addMenu('&File')
        menuFile.addAction(self.actionFileStart)
        menuFile.addAction(self.actionFileStop)
        menuFile.addSeparator()

        menuFileOpen = menuFile.addMenu('&Open')
        menuFileSave = menuFile.addMenu('&Save')
        menuFileSave.addAction(actionFileSave)
        menuFileSave.addAction(actionFileDraw)
        menuFile.addSeparator()

        menuFile.addAction(actionFileLogClear)
        menuFile.addSeparator()
        menuFile.addAction(actionFileExit)

        menuView = self.menuBar().addMenu('&View')
        menuView.addAction(self.actionViewInfo)

        menuConfig = self.menuBar().addMenu('&Settings')
        menuConfig.addAction(actionConfigPeriod)
        menuConfigSensors = menuConfig.addMenu('&Sensors')
        menuConfigSensors.addAction(actionConfigSensorsAdd)
        menuConfigSensors.addAction(actionConfigSensorsDel)
        menuConfigPath = menuConfig.addMenu('&Path')
        menuConfigPath.addAction(actionConfigPathAddresses)
        menuConfigPath.addAction(actionConfigPathSensors)
        menuConfigPath.addAction(actionConfigPathData)

        menuHelp = self.menuBar().addMenu('&Help')
        menuHelp.addAction(actionHelpHelp)
        menuHelp.addAction(actionHelpAbout)

        self.textEditList = deque(maxlen=100)
        self.textEdit.setVerticalScrollBarPolicy(2)
        self.textEdit.setToolTip("Action log.")
        self.textEdit.setReadOnly(True)

        self.isItemSave = False
        self.table = QTableWidget(0, 3)
        self.table.setToolTip("Temperature sensors.")
        self.table.setFixedWidth(342)
        self.table.setVerticalScrollBarPolicy(1)
        self.table.setHorizontalScrollBarPolicy(1)
        self.table.setColumnWidth(0, 50)
        self.table.setColumnWidth(1, 150)
        self.table.setColumnWidth(2, 120)
        self.table.setAlternatingRowColors(True)
        self.table.setSelectionMode(QTableWidget.SingleSelection)
        self.table.setSelectionBehavior(QTableWidget.SelectRows)
        self.table.setHorizontalHeaderLabels(['Val', 'Name', 'Address'])
        self.table.verticalHeader().setFixedWidth(20)
        self.table.verticalHeader().setDefaultAlignment(Qt.AlignRight)
        self.table.setEditTriggers(self.table.NoEditTriggers)

        grid = QGridLayout()
        grid.setSpacing(3)
        grid.setContentsMargins(3, 3, 3, 3)
        grid.addWidget(self.textEdit, 0, 0, 1, 1)
        grid.addWidget(self.table, 0, 1, 1, 1)
        widget = QWidget()
        widget.setLayout(grid)
        self.setCentralWidget(widget)

        self.setGeometry(300, 200, 600, 400)
        self.setWindowFlags(Qt.MSWindowsFixedSizeDialogHint)
        self.statusBar().setSizeGripEnabled(False)
        self.setWindowTitle('Observer')
        self.show()
        self.core.dataAdded.connect(self.onDataAdded)
        self.statusBar().showMessage('Application is runnig.')

    def command(self):
        """Обработка командой строки при запуске."""
        if len(sys.argv) > 1:
            if sys.argv[1] == '-s':
                if len(sys.argv) > 2:
                    if sys.argv[2].isdigit():
                        self.core.period = sys.argv[2]
                    self.actionFileStart.trigger()

    def closeEvent(self, event):
        """Событие закрытия программы."""
        self.core.stop()
        super().closeEvent(event)

    def onActionFileStartTriggered(self):
        """Действие нажатия Файл -> Старт.
        Запускает мониторинг."""
        self.actionFileStop.setEnabled(True)
        self.actionFileStart.setDisabled(True)
        self.core.start()

    def onActionFileStopTriggered(self):
        """Действие нажатия Файл -> Стоп.
        Остановить мониторинг."""
        self.actionFileStart.setEnabled(True)
        self.actionFileStop.setDisabled(True)
        self.core.stop()

    def actionConfigSensorsAddTriggered(self):
        """Действие нажатия Настройки -> Сенсоры -> Добавить.
        Открыть файл с настройками датчиков."""
        if os.path.exists(self.core.pathSensors):
            answer = QMessageBox.question(
                self, 'Add sensors',
                'Open file "{}/{}" to add sensors in external editor?'.format(
                    self.core.pathSensors, 'temperature'))
            if answer == QMessageBox.Yes:
                try:
                    os.startfile('{}\\{}'.format(self.core.pathSensors,
                                                 'temperature'))
                except Exception:
                    QMessageBox.warning(
                        self, 'Add sensors', 'File "{}/{}" don`t open!'.format(
                            self.core.pathSensors, 'temperature'))
        else:
            QMessageBox.warning(
                self, 'Add sensors',
                '"{}" path does not exist'.format(self.core.pathSensors))

    def actionConfigSensorsDelTriggered(self):
        """Действие нажатия Настройки -> Сенсоры -> Удалить.
        Открыть файл с настройками датчиков."""
        if os.path.exists(self.core.pathSensors):
            answer = QMessageBox.question(
                self, 'Delete sensors',
                'Open "{}/{}" to delete sensors in external editor?'.format(
                    self.core.pathSensors, 'temperature'))
            if answer == QMessageBox.Yes:
                try:
                    os.startfile('{}\\{}'.format(self.core.pathSensors,
                                                 'temperature'))
                except Exception:
                    QMessageBox.warning(
                        self, 'Delete sensors',
                        'File "{}/{}" don`t open!'.format(
                            self.core.pathSensors, 'temperature'))
        else:
            QMessageBox.warning(
                self, 'Delete sensors',
                '"{}" path does not exist'.format(self.core.pathSensors))

    def actionConfigPeriodTriggered(self):
        """Действие нажатия Настройки -> Период.
        Открыть окно настройки периода опроса датчиков."""
        period, ok = QInputDialog.getInt(self, 'Request period',
                                         'Enter request period:',
                                         int(self.core.period), 10, 3600)
        if ok:
            self.core.period = str(period)
            self.core.configSave()

    def actionConfigPathAddressesTriggered(self):
        """Действие нажатия Настройки -> Путь -> Адреса.
        Открывает окно выбора пути к файлу настройки адресов."""
        path = QFileDialog.getOpenFileName(self, 'Addresses path',
                                           self.core.pathAddresses, '')
        if path[0] != '':
            self.core.pathAddresses = path[0]
            self.core.configSave()

    def actionConfigPathSensorsTriggered(self):
        """Действие нажатия Настройки -> Путь -> Датчики.
        Открывает окно выбора пути к файлу настройки датчиков."""
        path = QFileDialog.getExistingDirectory(self, 'Sensors directory',
                                                self.core.pathSensors)
        if path != '':
            self.core.pathSensors = path
            self.core.configSave()

    def actionConfigPathDataTriggered(self):
        """Действие нажатия Настройки -> Путь -> Данные.
        Открывает окно выбора пути к папке, содержащей данные."""
        path = QFileDialog.getExistingDirectory(self, 'Data directory',
                                                self.core.pathData)
        if path != '':
            self.core.pathData = path
            self.core.configSave()

    def changeRow(self):
        """Изменение отображения информации в колонках."""
        if self.actionViewInfo.isChecked():
            self.table.setColumnHidden(1, False)
            self.table.setColumnHidden(2, False)
            self.table.setFixedWidth(self.table.width() + 270)
            self.setFixedWidth(self.width() + 270)
        else:
            self.table.setColumnHidden(1, True)
            self.table.setColumnHidden(2, True)
            self.table.setFixedWidth(self.table.width() - 270)
            self.setFixedWidth(self.width() - 270)

    def checkItemSave(self, item):
        """Проверка изменения ячейки имени датчика."""
        if item.column() == 1:
            self.isItemSave = True
        else:
            self.isItemSave = False

    def itemSave(self, item):
        """Сохранение имени в списке."""
        if self.isItemSave:
            if item.column() == 1:
                if item.tableWidget() == self.table:
                    self.isItemSave = False
                    self.statusBar().showMessage(item.text() + " save")
                    temp = self.table.item(item.row(), 2)
                    key = temp.text()
                    self.core.sensors[key].name = item.text()
        self.table.clearSelection()

    def onLogged(self, log, modes):
        """Событие логирования.
        
        log - передаваемое сообщение;
        
        modes - содержит один или несколько режимов отображения лога:
        
        l - вывод в текстовое поле;

        s - вывод в статус бар;

        f - запись в файл."""
        if 'l' in modes:
            self.textEditList.appendleft(log)
            self.textEdit.clear()
            self.textEdit.setText('\n'.join(self.textEditList))

        if 's' in modes:
            self.statusBar().showMessage(log)

        if 'f' in modes:
            directory = '{0}\\{1}\\{2}\\{3}'.format(
                self.core.pathData, self.core.currentDate.strftime('%Y'),
                self.core.currentDate.strftime('%m'),
                self.core.currentDate.strftime('%d'))
            os.makedirs(directory, 0o777, True)
            with open(
                    '{0}\\{1}.log'.format(
                        directory, self.core.currentDate.strftime('%Y.%m.%d')),
                    'a') as file:
                file.write(log + '\n')

    def onDataAdded(self):
        """Событие добавления данных."""
        self.table.setRowCount(len(self.core.sensors))
        if self.table.rowCount() <= 20:
            self.table.setMinimumHeight(25 + self.table.rowCount() * 23)
        i = 0
        for key in self.core.sensors.keys():
            if self.core.sensors[key].value is not None:
                self.table.setRowHeight(i, 23)
                self.table.setItem(
                    i, 0, QTableWidgetItem(self.core.sensors[key].value))
                self.table.setItem(
                    i, 1, QTableWidgetItem(self.core.sensors[key].name))
                self.table.setItem(i, 2, QTableWidgetItem(key))
                i += 1
        self.table.setRowCount(i)
        self.table.sortItems(1)

    def onAbout(self):
        """Действие нажатия Помощь -> О программе."""
        text = '<h2>{0}</h2>'\
               '<p>Client program for monitoring the condition<br>'\
               'of the premises of the Kharkov Radar.<br>'\
               'Source code is available on '\
               '<a href="https://github.com/StanislavMain/ObserverClient">'\
               'GitHub</a>.</p><p><b>Stanislav Hnatiuk</b><br>'\
               'Institute of Ionosphere\n<br>Ukraine, Kharkiv.<br><br>'\
               'Contacts with me:<br>'\
               '<a href="https://t.me/stanmain">Telegram</a><br>'\
               '<a href="https://github.com/StanislavMain">GitHub</a><br>'\
               '<br><b>All rights reserved.</b></p>'.format(self.windowTitle())
        QMessageBox.about(self, 'About the program', text)

    def actionHelpHelpTriggered(self):
        """Действие нажатия Помощь -> Помощь."""
        text = '<p><b>Console parametrs</b><br>'\
               'usage: main [options]<br>'\
               'where options have next key:<br>'\
               '-s [seconds]: start monitoring with a period of [seconds]</p>'\
               '<p><b>Code and name</b><br>'\
               'Write your address and sensors in the appropriate files:<br>'\
               '{}<br>{}/*</p>'\
               '<p><b>Data</b><br>'\
               'Data is located in "{}"<br>'\
               '"/Year/Month/Day/Y.M.D.csv" : data file<br>'\
               '"/Year/Month/Day/Y.M.D.png" : chart file<br>'\
               '"/Year/Month/Day/Y.M.D.log" : log file<br>'\
               '</p>'.format(
                   self.core.pathAddresses,
                   self.core.pathSensors,
                   self.core.pathData
                )
        QMessageBox.information(self, 'Help', text)
Exemplo n.º 38
0
class ImageViewer(QMainWindow):
    def __init__(self):
        super(ImageViewer, self).__init__()

        self.printer = QPrinter()
        self.scaleFactor = 0.0

        self.imageLabel = QLabel()
        self.imageLabel.setBackgroundRole(QPalette.Base)
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)

        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.imageLabel)
        self.setCentralWidget(self.scrollArea)

        self.createActions()
        self.createMenus()

        self.setWindowTitle("Image Viewer")
        self.resize(500, 400)

    def open(self):
        fileName, _ = QFileDialog.getOpenFileName(self, "Open File",
                QDir.currentPath())
        if fileName:
            image = QImage(fileName)
            if image.isNull():
                QMessageBox.information(self, "Image Viewer",
                        "Cannot load %s." % fileName)
                return

            self.imageLabel.setPixmap(QPixmap.fromImage(image))
            self.scaleFactor = 1.0

            self.printAct.setEnabled(True)
            self.fitToWindowAct.setEnabled(True)
            self.updateActions()

            if not self.fitToWindowAct.isChecked():
                self.imageLabel.adjustSize()

    def print_(self):
        dialog = QPrintDialog(self.printer, self)
        if dialog.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.imageLabel.pixmap().size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
            painter.setWindow(self.imageLabel.pixmap().rect())
            painter.drawPixmap(0, 0, self.imageLabel.pixmap())

    def zoomIn(self):
        self.scaleImage(1.25)

    def zoomOut(self):
        self.scaleImage(0.8)

    def normalSize(self):
        self.imageLabel.adjustSize()
        self.scaleFactor = 1.0

    def fitToWindow(self):
        fitToWindow = self.fitToWindowAct.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.normalSize()

        self.updateActions()

    def about(self):
        QMessageBox.about(self, "About Image Viewer",
                "<p>The <b>Image Viewer</b> example shows how to combine "
                "QLabel and QScrollArea to display an image. QLabel is "
                "typically used for displaying text, but it can also display "
                "an image. QScrollArea provides a scrolling view around "
                "another widget. If the child widget exceeds the size of the "
                "frame, QScrollArea automatically provides scroll bars.</p>"
                "<p>The example demonstrates how QLabel's ability to scale "
                "its contents (QLabel.scaledContents), and QScrollArea's "
                "ability to automatically resize its contents "
                "(QScrollArea.widgetResizable), can be used to implement "
                "zooming and scaling features.</p>"
                "<p>In addition the example shows how to use QPainter to "
                "print an image.</p>")

    def createActions(self):
        self.openAct = QAction("&Open...", self, shortcut="Ctrl+O",
                triggered=self.open)

        self.printAct = QAction("&Print...", self, shortcut="Ctrl+P",
                enabled=False, triggered=self.print_)

        self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q",
                triggered=self.close)

        self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++",
                enabled=False, triggered=self.zoomIn)

        self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-",
                enabled=False, triggered=self.zoomOut)

        self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S",
                enabled=False, triggered=self.normalSize)

        self.fitToWindowAct = QAction("&Fit to Window", self, enabled=False,
                checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow)

        self.aboutAct = QAction("&About", self, triggered=self.about)

        self.aboutQtAct = QAction("About &Qt", self,
                triggered=QApplication.instance().aboutQt)

    def createMenus(self):
        self.fileMenu = QMenu("&File", self)
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.printAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.fitToWindowAct)

        self.helpMenu = QMenu("&Help", self)
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

        self.menuBar().addMenu(self.fileMenu)
        self.menuBar().addMenu(self.viewMenu)
        self.menuBar().addMenu(self.helpMenu)

    def updateActions(self):
        self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())

    def scaleImage(self, factor):
        self.scaleFactor *= factor
        self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size())

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)

        self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
        self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)

    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(int(factor * scrollBar.value()
                                + ((factor - 1) * scrollBar.pageStep()/2)))
Exemplo n.º 39
0
    def __addDefaultActions(self):
        """
        Private slot to add the default user agent entries.
        
        @return flag indicating that a user agent entry is checked (boolean)
        """
        from . import UserAgentDefaults_rc  # __IGNORE_WARNING__
        defaultUserAgents = QFile(":/UserAgentDefaults.xml")
        defaultUserAgents.open(QIODevice.ReadOnly)

        menuStack = []
        isChecked = False

        if self.__url:
            currentUserAgentString = self.__manager.userAgentForUrl(self.__url)
        else:
            from Helpviewer.HelpBrowserWV import HelpWebPage
            currentUserAgentString = HelpWebPage().userAgent()
        xml = QXmlStreamReader(defaultUserAgents)
        while not xml.atEnd():
            xml.readNext()
            if xml.isStartElement() and xml.name() == "separator":
                if menuStack:
                    menuStack[-1].addSeparator()
                else:
                    self.addSeparator()
                continue

            if xml.isStartElement() and xml.name() == "useragent":
                attributes = xml.attributes()
                title = attributes.value("description")
                userAgent = attributes.value("useragent")

                act = QAction(self)
                act.setText(title)
                act.setData(userAgent)
                act.setToolTip(userAgent)
                act.setCheckable(True)
                act.setChecked(userAgent == currentUserAgentString)
                act.triggered.connect(self.__changeUserAgent)
                if menuStack:
                    menuStack[-1].addAction(act)
                else:
                    self.addAction(act)
                self.__actionGroup.addAction(act)
                isChecked = isChecked or act.isChecked()

            if xml.isStartElement() and xml.name() == "useragentmenu":
                attributes = xml.attributes()
                title = attributes.value("title")
                if title == "v_a_r_i_o_u_s":
                    title = self.tr("Various")

                menu = QMenu(self)
                menu.setTitle(title)
                self.addMenu(menu)
                menuStack.append(menu)

            if xml.isEndElement() and xml.name() == "useragentmenu":
                menuStack.pop()

        if xml.hasError():
            E5MessageBox.critical(
                self, self.tr("Parsing default user agents"),
                self.tr(
                    """<p>Error parsing default user agents.</p><p>{0}</p>""").
                format(xml.errorString()))

        return isChecked
Exemplo n.º 40
0
class MusicPlayer(QMainWindow):
    """MusicPlayer houses all of elements that directly interact with the main window."""

    def __init__(self, parent=None):
        """Initialize the QMainWindow widget.

        The window title, window icon, and window size are initialized here as well
        as the following widgets: QMediaPlayer, QMediaPlaylist, QMediaContent, QMenuBar,
        QToolBar, QLabel, QPixmap, QSlider, QDockWidget, QListWidget, QWidget, and
        QVBoxLayout. The connect signals for relavant widgets are also initialized.
        """
        super(MusicPlayer, self).__init__(parent)
        self.setWindowTitle('Mosaic')

        window_icon = utilities.resource_filename('mosaic.images', 'icon.png')
        self.setWindowIcon(QIcon(window_icon))
        self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63)

        # Initiates Qt objects to be used by MusicPlayer
        self.player = QMediaPlayer()
        self.playlist = QMediaPlaylist()
        self.playlist_location = defaults.Settings().playlist_path
        self.content = QMediaContent()
        self.menu = self.menuBar()
        self.toolbar = QToolBar()
        self.art = QLabel()
        self.pixmap = QPixmap()
        self.slider = QSlider(Qt.Horizontal)
        self.duration_label = QLabel()
        self.playlist_dock = QDockWidget('Playlist', self)
        self.library_dock = QDockWidget('Media Library', self)
        self.playlist_view = QListWidget()
        self.library_view = library.MediaLibraryView()
        self.library_model = library.MediaLibraryModel()
        self.preferences = configuration.PreferencesDialog()
        self.widget = QWidget()
        self.layout = QVBoxLayout(self.widget)
        self.duration = 0
        self.playlist_dock_state = None
        self.library_dock_state = None

        # Sets QWidget() as the central widget of the main window
        self.setCentralWidget(self.widget)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.art.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)

        # Initiates the playlist dock widget and the library dock widget
        self.addDockWidget(defaults.Settings().dock_position, self.playlist_dock)
        self.playlist_dock.setWidget(self.playlist_view)
        self.playlist_dock.setVisible(defaults.Settings().playlist_on_start)
        self.playlist_dock.setFeatures(QDockWidget.DockWidgetClosable)

        self.addDockWidget(defaults.Settings().dock_position, self.library_dock)
        self.library_dock.setWidget(self.library_view)
        self.library_dock.setVisible(defaults.Settings().media_library_on_start)
        self.library_dock.setFeatures(QDockWidget.DockWidgetClosable)
        self.tabifyDockWidget(self.playlist_dock, self.library_dock)

        # Sets the range of the playback slider and sets the playback mode as looping
        self.slider.setRange(0, self.player.duration() / 1000)
        self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)

        # OSX system menu bar causes conflicts with PyQt5 menu bar
        if sys.platform == 'darwin':
            self.menu.setNativeMenuBar(False)

        # Initiates Settings in the defaults module to give access to settings.toml
        defaults.Settings()

        # Signals that connect to other methods when they're called
        self.player.metaDataChanged.connect(self.display_meta_data)
        self.slider.sliderMoved.connect(self.seek)
        self.player.durationChanged.connect(self.song_duration)
        self.player.positionChanged.connect(self.song_position)
        self.player.stateChanged.connect(self.set_state)
        self.playlist_view.itemActivated.connect(self.activate_playlist_item)
        self.library_view.activated.connect(self.open_media_library)
        self.playlist.currentIndexChanged.connect(self.change_index)
        self.playlist.mediaInserted.connect(self.initialize_playlist)
        self.playlist_dock.visibilityChanged.connect(self.dock_visiblity_change)
        self.library_dock.visibilityChanged.connect(self.dock_visiblity_change)
        self.preferences.dialog_media_library.media_library_line.textChanged.connect(self.change_media_library_path)
        self.preferences.dialog_view_options.dropdown_box.currentIndexChanged.connect(self.change_window_size)
        self.art.mousePressEvent = self.press_playback

        # Creating the menu controls, media controls, and window size of the music player
        self.menu_controls()
        self.media_controls()
        self.load_saved_playlist()

    def menu_controls(self):
        """Initiate the menu bar and add it to the QMainWindow widget."""
        self.file = self.menu.addMenu('File')
        self.edit = self.menu.addMenu('Edit')
        self.playback = self.menu.addMenu('Playback')
        self.view = self.menu.addMenu('View')
        self.help_ = self.menu.addMenu('Help')

        self.file_menu()
        self.edit_menu()
        self.playback_menu()
        self.view_menu()
        self.help_menu()

    def media_controls(self):
        """Create the bottom toolbar and controls used for media playback."""
        self.addToolBar(Qt.BottomToolBarArea, self.toolbar)
        self.toolbar.setMovable(False)

        play_icon = utilities.resource_filename('mosaic.images', 'md_play.png')
        self.play_action = QAction(QIcon(play_icon), 'Play', self)
        self.play_action.triggered.connect(self.player.play)

        stop_icon = utilities.resource_filename('mosaic.images', 'md_stop.png')
        self.stop_action = QAction(QIcon(stop_icon), 'Stop', self)
        self.stop_action.triggered.connect(self.player.stop)

        previous_icon = utilities.resource_filename('mosaic.images', 'md_previous.png')
        self.previous_action = QAction(QIcon(previous_icon), 'Previous', self)
        self.previous_action.triggered.connect(self.previous)

        next_icon = utilities.resource_filename('mosaic.images', 'md_next.png')
        self.next_action = QAction(QIcon(next_icon), 'Next', self)
        self.next_action.triggered.connect(self.playlist.next)

        repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png')
        self.repeat_action = QAction(QIcon(repeat_icon), 'Repeat', self)
        self.repeat_action.setShortcut('R')
        self.repeat_action.triggered.connect(self.repeat_song)

        self.toolbar.addAction(self.play_action)
        self.toolbar.addAction(self.stop_action)
        self.toolbar.addAction(self.previous_action)
        self.toolbar.addAction(self.next_action)
        self.toolbar.addAction(self.repeat_action)
        self.toolbar.addWidget(self.slider)
        self.toolbar.addWidget(self.duration_label)

    def file_menu(self):
        """Add a file menu to the menu bar.

        The file menu houses the Open File, Open Multiple Files, Open Playlist,
        Open Directory, and Exit Application menu items.
        """
        self.open_action = QAction('Open File', self)
        self.open_action.setShortcut('O')
        self.open_action.triggered.connect(self.open_file)

        self.open_multiple_files_action = QAction('Open Multiple Files', self)
        self.open_multiple_files_action.setShortcut('M')
        self.open_multiple_files_action.triggered.connect(self.open_multiple_files)

        self.open_playlist_action = QAction('Open Playlist', self)
        self.open_playlist_action.setShortcut('CTRL+P')
        self.open_playlist_action.triggered.connect(self.open_playlist)

        self.open_directory_action = QAction('Open Directory', self)
        self.open_directory_action.setShortcut('D')
        self.open_directory_action.triggered.connect(self.open_directory)

        self.save_playlist_action = QAction('Save Playlist', self)
        self.save_playlist_action.setShortcut('CTRL+S')
        self.save_playlist_action.triggered.connect(self.save_playlist)

        self.exit_action = QAction('Quit', self)
        self.exit_action.setShortcut('CTRL+Q')
        self.exit_action.triggered.connect(self.closeEvent)

        self.file.addAction(self.open_action)
        self.file.addAction(self.open_multiple_files_action)
        self.file.addAction(self.open_playlist_action)
        self.file.addAction(self.open_directory_action)
        self.file.addSeparator()
        self.file.addAction(self.save_playlist_action)
        self.file.addSeparator()
        self.file.addAction(self.exit_action)

    def edit_menu(self):
        """Add an edit menu to the menu bar.

        The edit menu houses the preferences item that opens a preferences dialog
        that allows the user to customize features of the music player.
        """
        self.preferences_action = QAction('Preferences', self)
        self.preferences_action.setShortcut('CTRL+SHIFT+P')
        self.preferences_action.triggered.connect(lambda: self.preferences.exec_())

        self.edit.addAction(self.preferences_action)

    def playback_menu(self):
        """Add a playback menu to the menu bar.

        The playback menu houses
        """
        self.play_playback_action = QAction('Play', self)
        self.play_playback_action.setShortcut('P')
        self.play_playback_action.triggered.connect(self.player.play)

        self.stop_playback_action = QAction('Stop', self)
        self.stop_playback_action.setShortcut('S')
        self.stop_playback_action.triggered.connect(self.player.stop)

        self.previous_playback_action = QAction('Previous', self)
        self.previous_playback_action.setShortcut('B')
        self.previous_playback_action.triggered.connect(self.previous)

        self.next_playback_action = QAction('Next', self)
        self.next_playback_action.setShortcut('N')
        self.next_playback_action.triggered.connect(self.playlist.next)

        self.playback.addAction(self.play_playback_action)
        self.playback.addAction(self.stop_playback_action)
        self.playback.addAction(self.previous_playback_action)
        self.playback.addAction(self.next_playback_action)

    def view_menu(self):
        """Add a view menu to the menu bar.

        The view menu houses the Playlist, Media Library, Minimalist View, and Media
        Information menu items. The Playlist item toggles the playlist dock into and
        out of view. The Media Library items toggles the media library dock into and
        out of view. The Minimalist View item resizes the window and shows only the
        menu bar and player controls. The Media Information item opens a dialog that
        shows information relevant to the currently playing song.
        """
        self.dock_action = self.playlist_dock.toggleViewAction()
        self.dock_action.setShortcut('CTRL+ALT+P')

        self.library_dock_action = self.library_dock.toggleViewAction()
        self.library_dock_action.setShortcut('CTRL+ALT+L')

        self.minimalist_view_action = QAction('Minimalist View', self)
        self.minimalist_view_action.setShortcut('CTRL+ALT+M')
        self.minimalist_view_action.setCheckable(True)
        self.minimalist_view_action.triggered.connect(self.minimalist_view)

        self.view_media_info_action = QAction('Media Information', self)
        self.view_media_info_action.setShortcut('CTRL+SHIFT+M')
        self.view_media_info_action.triggered.connect(self.media_information_dialog)

        self.view.addAction(self.dock_action)
        self.view.addAction(self.library_dock_action)
        self.view.addSeparator()
        self.view.addAction(self.minimalist_view_action)
        self.view.addSeparator()
        self.view.addAction(self.view_media_info_action)

    def help_menu(self):
        """Add a help menu to the menu bar.

        The help menu houses the about dialog that shows the user information
        related to the application.
        """
        self.about_action = QAction('About', self)
        self.about_action.setShortcut('H')
        self.about_action.triggered.connect(lambda: about.AboutDialog().exec_())

        self.help_.addAction(self.about_action)

    def open_file(self):
        """Open the selected file and add it to a new playlist."""
        filename, success = QFileDialog.getOpenFileName(self, 'Open File', '', 'Audio (*.mp3 *.flac)', '', QFileDialog.ReadOnly)

        if success:
            file_info = QFileInfo(filename).fileName()
            playlist_item = QListWidgetItem(file_info)
            self.playlist.clear()
            self.playlist_view.clear()
            self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(filename)))
            self.player.setPlaylist(self.playlist)
            playlist_item.setToolTip(file_info)
            self.playlist_view.addItem(playlist_item)
            self.playlist_view.setCurrentRow(0)
            self.player.play()

    def open_multiple_files(self):
        """Open the selected files and add them to a new playlist."""
        filenames, success = QFileDialog.getOpenFileNames(self, 'Open Multiple Files', '', 'Audio (*.mp3 *.flac)', '', QFileDialog.ReadOnly)

        if success:
            self.playlist.clear()
            self.playlist_view.clear()
            for file in natsort.natsorted(filenames, alg=natsort.ns.PATH):
                file_info = QFileInfo(file).fileName()
                playlist_item = QListWidgetItem(file_info)
                self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file)))
                self.player.setPlaylist(self.playlist)
                playlist_item.setToolTip(file_info)
                self.playlist_view.addItem(playlist_item)
                self.playlist_view.setCurrentRow(0)
                self.player.play()

    def open_playlist(self):
        """Load an M3U or PLS file into a new playlist."""
        playlist, success = QFileDialog.getOpenFileName(self, 'Open Playlist', '', 'Playlist (*.m3u *.pls)', '', QFileDialog.ReadOnly)

        if success:
            playlist = QUrl.fromLocalFile(playlist)
            self.playlist.clear()
            self.playlist_view.clear()
            self.playlist.load(playlist)
            self.player.setPlaylist(self.playlist)

            for song_index in range(self.playlist.mediaCount()):
                file_info = self.playlist.media(song_index).canonicalUrl().fileName()
                playlist_item = QListWidgetItem(file_info)
                playlist_item.setToolTip(file_info)
                self.playlist_view.addItem(playlist_item)

            self.playlist_view.setCurrentRow(0)
            self.player.play()

    def save_playlist(self):
        """Save the media in the playlist dock as a new M3U playlist."""
        playlist, success = QFileDialog.getSaveFileName(self, 'Save Playlist', '', 'Playlist (*.m3u)', '')
        if success:
            saved_playlist = "{}.m3u" .format(playlist)
            self.playlist.save(QUrl().fromLocalFile(saved_playlist), "m3u")

    def load_saved_playlist(self):
        """Load the saved playlist if user setting permits."""
        saved_playlist = "{}/.m3u" .format(self.playlist_location)
        if os.path.exists(saved_playlist):
            playlist = QUrl().fromLocalFile(saved_playlist)
            self.playlist.load(playlist)
            self.player.setPlaylist(self.playlist)

            for song_index in range(self.playlist.mediaCount()):
                file_info = self.playlist.media(song_index).canonicalUrl().fileName()
                playlist_item = QListWidgetItem(file_info)
                playlist_item.setToolTip(file_info)
                self.playlist_view.addItem(playlist_item)

            self.playlist_view.setCurrentRow(0)

    def open_directory(self):
        """Open the selected directory and add the files within to an empty playlist."""
        directory = QFileDialog.getExistingDirectory(self, 'Open Directory', '', QFileDialog.ReadOnly)

        if directory:
            self.playlist.clear()
            self.playlist_view.clear()
            for dirpath, __, files in os.walk(directory):
                for filename in natsort.natsorted(files, alg=natsort.ns.PATH):
                    file = os.path.join(dirpath, filename)
                    if filename.endswith(('mp3', 'flac')):
                        self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file)))
                        playlist_item = QListWidgetItem(filename)
                        playlist_item.setToolTip(filename)
                        self.playlist_view.addItem(playlist_item)

            self.player.setPlaylist(self.playlist)
            self.playlist_view.setCurrentRow(0)
            self.player.play()

    def open_media_library(self, index):
        """Open a directory or file from the media library into an empty playlist."""
        self.playlist.clear()
        self.playlist_view.clear()

        if self.library_model.fileName(index).endswith(('mp3', 'flac')):
            self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(self.library_model.filePath(index))))
            self.playlist_view.addItem(self.library_model.fileName(index))

        elif self.library_model.isDir(index):
            directory = self.library_model.filePath(index)
            for dirpath, __, files in os.walk(directory):
                for filename in natsort.natsorted(files, alg=natsort.ns.PATH):
                    file = os.path.join(dirpath, filename)
                    if filename.endswith(('mp3', 'flac')):
                        self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file)))
                        playlist_item = QListWidgetItem(filename)
                        playlist_item.setToolTip(filename)
                        self.playlist_view.addItem(playlist_item)

        self.player.setPlaylist(self.playlist)
        self.player.play()

    def display_meta_data(self):
        """Display the current song's metadata in the main window.

        If the current song contains metadata, its cover art is extracted and shown in
        the main window while the track number, artist, album, and track title are shown
        in the window title.
        """
        if self.player.isMetaDataAvailable():
            file_path = self.player.currentMedia().canonicalUrl().toLocalFile()
            (album, artist, title, track_number, *__, artwork) = metadata.metadata(file_path)

            try:
                self.pixmap.loadFromData(artwork)
            except TypeError:
                self.pixmap = QPixmap(artwork)

            meta_data = '{} - {} - {} - {}' .format(track_number, artist, album, title)

            self.setWindowTitle(meta_data)
            self.art.setScaledContents(True)
            self.art.setPixmap(self.pixmap)
            self.layout.addWidget(self.art)

    def initialize_playlist(self, start):
        """Display playlist and reset playback mode when media inserted into playlist."""
        if start == 0:
            if self.library_dock.isVisible():
                self.playlist_dock.setVisible(True)
                self.playlist_dock.show()
                self.playlist_dock.raise_()

            if self.playlist.playbackMode() != QMediaPlaylist.Sequential:
                self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
                repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png')
                self.repeat_action.setIcon(QIcon(repeat_icon))

    def press_playback(self, event):
        """Change the playback of the player on cover art mouse event.

        When the cover art is clicked, the player will play the media if the player is
        either paused or stopped. If the media is playing, the media is set
        to pause.
        """
        if event.button() == 1 and configuration.Playback().cover_art_playback.isChecked():
            if (self.player.state() == QMediaPlayer.StoppedState or
                    self.player.state() == QMediaPlayer.PausedState):
                self.player.play()
            elif self.player.state() == QMediaPlayer.PlayingState:
                self.player.pause()

    def seek(self, seconds):
        """Set the position of the song to the position dragged to by the user."""
        self.player.setPosition(seconds * 1000)

    def song_duration(self, duration):
        """Set the slider to the duration of the currently played media."""
        duration /= 1000
        self.duration = duration
        self.slider.setMaximum(duration)

    def song_position(self, progress):
        """Move the horizontal slider in sync with the duration of the song.

        The progress is relayed to update_duration() in order
        to display the time label next to the slider.
        """
        progress /= 1000

        if not self.slider.isSliderDown():
            self.slider.setValue(progress)

        self.update_duration(progress)

    def update_duration(self, current_duration):
        """Calculate the time played and the length of the song.

        Both of these times are sent to duration_label() in order to display the
        times on the toolbar.
        """
        duration = self.duration

        if current_duration or duration:
            time_played = QTime((current_duration / 3600) % 60, (current_duration / 60) % 60,
                                (current_duration % 60), (current_duration * 1000) % 1000)
            song_length = QTime((duration / 3600) % 60, (duration / 60) % 60, (duration % 60),
                                (duration * 1000) % 1000)

            if duration > 3600:
                time_format = "hh:mm:ss"
            else:
                time_format = "mm:ss"

            time_display = "{} / {}" .format(time_played.toString(time_format), song_length.toString(time_format))

        else:
            time_display = ""

        self.duration_label.setText(time_display)

    def set_state(self, state):
        """Change the icon in the toolbar in relation to the state of the player.

        The play icon changes to the pause icon when a song is playing and
        the pause icon changes back to the play icon when either paused or
        stopped.
        """
        if self.player.state() == QMediaPlayer.PlayingState:
            pause_icon = utilities.resource_filename('mosaic.images', 'md_pause.png')
            self.play_action.setIcon(QIcon(pause_icon))
            self.play_action.triggered.connect(self.player.pause)

        elif (self.player.state() == QMediaPlayer.PausedState or self.player.state() == QMediaPlayer.StoppedState):
            self.play_action.triggered.connect(self.player.play)
            play_icon = utilities.resource_filename('mosaic.images', 'md_play.png')
            self.play_action.setIcon(QIcon(play_icon))

    def previous(self):
        """Move to the previous song in the playlist.

        Moves to the previous song in the playlist if the current song is less
        than five seconds in. Otherwise, restarts the current song.
        """
        if self.player.position() <= 5000:
            self.playlist.previous()
        else:
            self.player.setPosition(0)

    def repeat_song(self):
        """Set the current media to repeat and change the repeat icon accordingly.

        There are four playback modes: repeat none, repeat all, repeat once, and shuffle.
        Clicking the repeat button cycles through each playback mode.
        """
        if self.playlist.playbackMode() == QMediaPlaylist.Sequential:
            self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
            repeat_on_icon = utilities.resource_filename('mosaic.images', 'md_repeat_all.png')
            self.repeat_action.setIcon(QIcon(repeat_on_icon))

        elif self.playlist.playbackMode() == QMediaPlaylist.Loop:
            self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop)
            repeat_on_icon = utilities.resource_filename('mosaic.images', 'md_repeat_once.png')
            self.repeat_action.setIcon(QIcon(repeat_on_icon))

        elif self.playlist.playbackMode() == QMediaPlaylist.CurrentItemInLoop:
            self.playlist.setPlaybackMode(QMediaPlaylist.Random)
            repeat_icon = utilities.resource_filename('mosaic.images', 'md_shuffle.png')
            self.repeat_action.setIcon(QIcon(repeat_icon))

        elif self.playlist.playbackMode() == QMediaPlaylist.Random:
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
            repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png')
            self.repeat_action.setIcon(QIcon(repeat_icon))

    def activate_playlist_item(self, item):
        """Set the active media to the playlist item dobule-clicked on by the user."""
        current_index = self.playlist_view.row(item)
        if self.playlist.currentIndex() != current_index:
            self.playlist.setCurrentIndex(current_index)

        if self.player.state() != QMediaPlayer.PlayingState:
            self.player.play()

    def change_index(self, row):
        """Highlight the row in the playlist of the active media."""
        self.playlist_view.setCurrentRow(row)

    def minimalist_view(self):
        """Resize the window to only show the menu bar and audio controls."""
        if self.minimalist_view_action.isChecked():

            if self.playlist_dock.isVisible():
                self.playlist_dock_state = True
            if self.library_dock.isVisible():
                self.library_dock_state = True

            self.library_dock.close()
            self.playlist_dock.close()

            QTimer.singleShot(10, lambda: self.resize(500, 0))

        else:
            self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63)

            if self.library_dock_state:
                self.library_dock.setVisible(True)

            if self.playlist_dock_state:
                self.playlist_dock.setVisible(True)

    def dock_visiblity_change(self, visible):
        """Change the size of the main window when the docks are toggled."""
        if visible and self.playlist_dock.isVisible() and not self.library_dock.isVisible():
            self.resize(defaults.Settings().window_size + self.playlist_dock.width() + 6,
                        self.height())

        elif visible and not self.playlist_dock.isVisible() and self.library_dock.isVisible():
            self.resize(defaults.Settings().window_size + self.library_dock.width() + 6,
                        self.height())

        elif visible and self.playlist_dock.isVisible() and self.library_dock.isVisible():
            self.resize(defaults.Settings().window_size + self.library_dock.width() + 6,
                        self.height())

        elif (not visible and not self.playlist_dock.isVisible() and not
                self.library_dock.isVisible()):
            self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63)

    def media_information_dialog(self):
        """Show a dialog of the current song's metadata."""
        if self.player.isMetaDataAvailable():
            file_path = self.player.currentMedia().canonicalUrl().toLocalFile()
        else:
            file_path = None
        dialog = information.InformationDialog(file_path)
        dialog.exec_()

    def change_window_size(self):
        """Change the window size of the music player."""
        self.playlist_dock.close()
        self.library_dock.close()
        self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63)

    def change_media_library_path(self, path):
        """Change the media library path to the new path selected in the preferences dialog."""
        self.library_model.setRootPath(path)
        self.library_view.setModel(self.library_model)
        self.library_view.setRootIndex(self.library_model.index(path))

    def closeEvent(self, event):
        """Override the PyQt close event in order to handle save playlist on close."""
        playlist = "{}/.m3u" .format(self.playlist_location)
        if defaults.Settings().save_playlist_on_close:
            self.playlist.save(QUrl().fromLocalFile(playlist), "m3u")
        else:
            if os.path.exists(playlist):
                os.remove(playlist)
        QApplication.quit()
Exemplo n.º 41
0
class ImageViewer(QMainWindow):
    def __init__(self):
        super(ImageViewer, self).__init__()

        self.printer = QPrinter()
        self.scaleFactor = 0.0

        self.imageLabel = QLabel()
        self.imageLabel.setBackgroundRole(QPalette.Base)
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)
        self.imageLabel.setAlignment(Qt.AlignCenter)

        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.imageLabel)
        self.setCentralWidget(self.scrollArea)
        self.scrollArea.setAlignment(Qt.AlignCenter)

        self.createActions()
        self.createMenus()

        self.setWindowTitle("Image Viewer")
        self.resize(500, 400)
        self.setWindowIcon(QIcon('./res/Sketch-Icon.png'))

    def open(self):
        # 打开图片后根据weight重新调整大小
        fileName, _ = QFileDialog.getOpenFileName(self, "Open File",
                QDir.currentPath())
        if fileName:
            # 如果是webp格式用PIL
            # 如果是psd用PSDtool
            # gif格式?
            fileType = fileName.split('.')[-1]
            if fileType == 'gif':
                #QMessageBox.information(self, "file type", "type:%s"%(fileType))
                self.movie = QMovie(fileName)
                self.movie.setCacheMode(QMovie.CacheAll)
                self.movie.setSpeed(100)
                self.imageLabel.setMovie(self.movie)
                self.movie.start()
                if not self.fitToWindowAct.isChecked():
                    self.imageLabel.adjustSize()

            elif fileType == 'psd':
                img = PSDImage.load(fileName)
                img = img.as_PIL()
                img.save('./res/temp.jpg')
                image = QImage('./res/temp.jpg')
                if image.isNull():
                    QMessageBox.information(self, "Image Viewer",
                            "Cannot load %s." % (fileName))
                    return
                self.imageLabel.setPixmap(QPixmap.fromImage(image))
                self.scaleFactor = 1.0

                self.printAct.setEnabled(True)
                self.fitToWindowAct.setEnabled(True)
                self.updateActions()

                if not self.fitToWindowAct.isChecked():
                    self.imageLabel.adjustSize()

            elif fileType == 'svg':
                svg = QtSvg.QSvgRenderer(fileName)
                img = QImage(svg.defaultSize().width(), svg.defaultSize().height(), QImage.Format_ARGB32)
                p = QPainter(img)
                svg.render(p)
                img.save('./res/temp.png')
                p.end()
                image = QImage('./res/temp.png')
                if image.isNull():
                    QMessageBox.information(self, "Image Viewer",
                            "Cannot load %s." % (fileName))
                    return

                self.imageLabel.setPixmap(QPixmap.fromImage(image))
                self.scaleFactor = 1.0

                self.printAct.setEnabled(True)
                self.fitToWindowAct.setEnabled(True)
                self.updateActions()

                if not self.fitToWindowAct.isChecked():
                    self.imageLabel.adjustSize()

            else:
                image = QImage(fileName)
                if image.isNull():
                    QMessageBox.information(self, "Image Viewer",
                            "Cannot load %s." % (fileName))
                    return
                self.imageLabel.setPixmap(QPixmap.fromImage(image))
                self.scaleFactor = 1.0

                self.printAct.setEnabled(True)
                self.fitToWindowAct.setEnabled(True)
                self.updateActions()

                if not self.fitToWindowAct.isChecked():
                    self.imageLabel.adjustSize()


    def print_(self):
        dialog = QPrintDialog(self.printer, self)
        if dialog.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.imageLabel.pixmap().size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
            painter.setWindow(self.imageLabel.pixmap().rect())
            painter.drawPixmap(0, 0, self.imageLabel.pixmap())

    def zoomIn(self):
        self.scaleImage(1.25)

    def zoomOut(self):
        self.scaleImage(0.8)

    def normalSize(self):
        self.imageLabel.adjustSize()
        self.scaleFactor = 1.0

    def fitToWindow(self):
        fitToWindow = self.fitToWindowAct.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.normalSize()

        self.updateActions()

    def about(self):
        QMessageBox.about(self, "About Image Viewer",
                "<p>The <b>Image Viewer</b> example shows how to combine "
                "QLabel and QScrollArea to display an image. QLabel is "
                "typically used for displaying text, but it can also display "
                "an image. QScrollArea provides a scrolling view around "
                "another widget. If the child widget exceeds the size of the "
                "frame, QScrollArea automatically provides scroll bars.</p>"
                "<p>The example demonstrates how QLabel's ability to scale "
                "its contents (QLabel.scaledContents), and QScrollArea's "
                "ability to automatically resize its contents "
                "(QScrollArea.widgetResizable), can be used to implement "
                "zooming and scaling features.</p>"
                "<p>In addition the example shows how to use QPainter to "
                "print an image.</p>")


    def rgbColor(self):
        pass


    def hexColor(self):
        pass

    def createActions(self):
        self.openAct = QAction("&Open...", self, shortcut="Ctrl+O",
                triggered=self.open)

        self.printAct = QAction("&Print...", self, shortcut="Ctrl+P",
                enabled=False, triggered=self.print_)

        self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q",
                triggered=self.close)

        self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++",
                enabled=False, triggered=self.zoomIn)

        self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-",
                enabled=False, triggered=self.zoomOut)

        self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S",
                enabled=False, triggered=self.normalSize)

        self.fitToWindowAct = QAction("&Fit to Window", self, enabled=False,
                checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow)

        self.rgbColor = QAction("&RGB Color", self, enabled=False,
                checkable=True, triggered=self.rgbColor)

        self.hexColor = QAction("&HEX Color", self, enabled=False,
                checkable=True, triggered=self.hexColor)

        self.aboutAct = QAction("&About", self, triggered=self.about)

        self.aboutQtAct = QAction("About &Qt", self,
                triggered=QApplication.instance().aboutQt)

    def createMenus(self):
        self.fileMenu = QMenu("&File", self)
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.printAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.fitToWindowAct)

        self.pickColor = QMenu("&PickColor", self)
        self.pickColor.addAction(self.rgbColor)
        self.pickColor.addAction(self.hexColor)

        self.helpMenu = QMenu("&Help", self)
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

        self.menuBar().addMenu(self.fileMenu)
        self.menuBar().addMenu(self.viewMenu)
        self.menuBar().addMenu(self.pickColor)
        self.menuBar().addMenu(self.helpMenu)


    def updateActions(self):
        self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())

    def scaleImage(self, factor):
        self.scaleFactor *= factor
        self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size())

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)

        self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
        self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)

    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(int(factor * scrollBar.value()
                                + ((factor - 1) * scrollBar.pageStep()/2)))
Exemplo n.º 42
0
class Actions(QObject):

    def __init__(self, controller: 'Controller') -> None:
        super().__init__()

        self.controller = controller

        self.make_actions()

    def make_actions(self) -> None:
        self.save_as = QAction(QIcon.fromTheme('save-as'), 'Save Filelist As', self)
        self.save_as.setShortcut('Ctrl+s')
        self.save_as.setStatusTip('Save the current file selection')
        self.save_as.triggered.connect(self.controller.save_as)

        def on_debug(enabled):
            if enabled:
                logging.getLogger().setLevel(logging.DEBUG)
            else:
                logging.getLogger().setLevel(logging.ERROR)

        self.debug = QAction(QIcon.fromTheme('media-record'), '&Debug', self, checkable=True)
        self.debug.setStatusTip('Debug application')
        self.debug.setChecked(logging.getLogger().getEffectiveLevel() == logging.DEBUG)
        self.debug.triggered.connect(lambda: on_debug(self.debug.isChecked()))

        self.exit = QAction(QIcon.fromTheme('window-close'), '&Exit', self)
        self.exit.setShortcut('Ctrl+W')
        self.exit.setStatusTip('Close Window')
        self.exit.triggered.connect(self.controller.close_window)

        self.home = QAction(QIcon.fromTheme('go-home'), '&Go to Home', self)
        self.home.setStatusTip('Go to the Home directory')
        self.home.triggered.connect(self.controller.go_home)

        self.undo = QAction(QIcon.fromTheme('undo'), '&Undo', self)
        self.undo.setShortcut('Ctrl+Z')
        self.undo.setStatusTip('Undo the last action')

        self.redo = QAction(QIcon.fromTheme('redo'), '&Redo', self)
        self.redo.setShortcut('Ctrl+Y')
        self.redo.setStatusTip('Redo the last action')

        self.edit_copy = QAction(QIcon.fromTheme('edit-copy'), '&Copy', self)
        self.edit_copy.setShortcut('Ctrl+C')
        self.edit_copy.setStatusTip('Copy Selected Files')
        self.edit_copy.triggered.connect(self.controller.on_edit_copy)

        self.edit_cut = QAction(QIcon.fromTheme('edit-cut'), 'Cu&t', self)
        self.edit_cut.setShortcut('Ctrl+X')
        self.edit_cut.setStatusTip('Cut Selected Files')
        self.edit_cut.triggered.connect(self.controller.on_edit_cut)

        self.edit_paste = QAction(QIcon.fromTheme('edit-paste'), '&Paste', self)
        self.edit_paste.setShortcut('Ctrl+V')
        self.edit_paste.setStatusTip('Paste Files')
        self.edit_paste.triggered.connect(self.controller.on_edit_paste)

        self.edit_delete = QAction(QIcon.fromTheme('edit-delete'), '&Delete', self)
        self.edit_delete.setStatusTip('Delete Selected Files')

        self.edit_select_all = QAction(QIcon.fromTheme('edit-select-all'), '&Select All', self)
        self.edit_select_all.setShortcut('Ctrl+A')
        self.edit_select_all.setStatusTip('Select All')
        self.edit_select_all.triggered.connect(self.controller.select_all)

        self.zoom_in = QAction(QIcon.fromTheme('zoom-in'), "Zoom &In", self)
        self.zoom_in.triggered.connect(self.controller.zoom_in)
        self.zoom_in.setShortcut('Ctrl+=')
        self.zoom_out = QAction(QIcon.fromTheme('zoom-out'), "Zoom &Out", self)
        self.zoom_out.triggered.connect(self.controller.zoom_out)
        self.zoom_out.setShortcut('Ctrl+-')

        self.lod_in = QAction(QIcon.fromTheme('zoom-in'), "Level of Detail &In", self)
        self.lod_in.triggered.connect(self.controller.more_details)
        self.lod_in.setShortcut('Alt+=')
        self.lod_out = QAction(QIcon.fromTheme('zoom-out'), "Level of Detail &Out", self)
        self.lod_out.triggered.connect(self.controller.less_details)
        self.lod_out.setShortcut('Alt+-')

        self.crop_thumbnails = QAction(QIcon.fromTheme('zoom-fit-best'), "Crop Thumbnails", self, checkable=True)
        self.crop_thumbnails.triggered.connect(
            lambda: self.controller.set_crop_thumbnails(self.crop_thumbnails.isChecked()))

        self.new_window = QAction(QIcon.fromTheme('window-new'), "New Window", self)
        self.new_window.triggered.connect(lambda x: self.controller.new_controller(clone=True))
        self.new_window.setShortcut('Ctrl+N')

        self.parent_directory = QAction(self.controller.app.qapp.style().standardIcon(QStyle.SP_FileDialogToParent),
                                        "Parent Directory")
        self.parent_directory.triggered.connect(self.controller.parent_directory)

        self.back = QAction(QIcon.fromTheme('back'), 'Go &back', self)
        self.back.setShortcut('Alt+Left')
        self.back.setStatusTip('Go back in history')
        self.back.setEnabled(False)
        self.back.triggered.connect(self.controller.go_back)

        self.forward = QAction(QIcon.fromTheme('forward'), 'Go &forward', self)
        self.forward.setShortcut('Alt+Right')
        self.forward.setStatusTip('Go forward in history')
        self.forward.setEnabled(False)
        self.forward.triggered.connect(self.controller.go_forward)

        self.search = QAction(QIcon.fromTheme('system-search'), 'Search', self)
        self.search.setShortcut('F3')
        self.search.setStatusTip('Search for files')
        self.search.triggered.connect(self.controller.show_search)

        self.reload = QAction(QIcon.fromTheme('reload'), 'Reload', self)
        self.reload.setShortcut('F5')
        self.reload.setStatusTip('Reload the View')
        self.reload.triggered.connect(self.controller.reload)

        self.rename = QAction(QIcon.fromTheme('rename'), 'Rename', self)
        self.rename.setShortcut('F2')
        self.rename.setStatusTip('Rename the current file')
        self.rename.triggered.connect(lambda checked: self.controller.show_rename_dialog())

        self.reload_thumbnails = QAction(QIcon.fromTheme('edit-delete'), 'Reload Thumbnails', self)
        self.reload_thumbnails.setStatusTip('Reload Thumbnails')
        self.reload_thumbnails.triggered.connect(self.controller.reload_thumbnails)

        self.reload_metadata = QAction(QIcon.fromTheme('edit-delete'), 'Reload MetaData', self)
        self.reload_metadata.setStatusTip('Reload MetaData')
        self.reload_metadata.triggered.connect(self.controller.reload_metadata)

        self.make_directory_thumbnails = QAction(QIcon.fromTheme('folder'), 'Make Directory Thumbnails', self)
        self.make_directory_thumbnails.setStatusTip('Make Directory Thumbnails')
        self.make_directory_thumbnails.triggered.connect(self.controller.make_directory_thumbnails)

        self.prepare = QAction(QIcon.fromTheme('media-playback-start'), 'Load Thumbnails', self)
        self.prepare.setShortcut('F6')
        self.prepare.setStatusTip('Load Thumbnails')
        self.prepare.triggered.connect(self.controller.prepare)

        self.view_detail_view = QAction("Detail View", self, checkable=True)
        self.view_icon_view = QAction("Icon View", self, checkable=True)
        self.view_small_icon_view = QAction("Small Icon View", self, checkable=True)

        self.view_icon_view = QAction(QIcon.fromTheme("view-grid-symbolic"),
                                      "Icon View")
        self.view_icon_view.triggered.connect(self.controller.view_icon_view)

        self.view_small_icon_view = QAction(QIcon.fromTheme("view-list-symbolic"),
                                            "Small Icon View")
        self.view_small_icon_view.triggered.connect(self.controller.view_small_icon_view)

        self.view_detail_view = QAction(QIcon.fromTheme("view-more-horizontal-symbolic"),
                                        "Detail View")
        self.view_detail_view.triggered.connect(self.controller.view_detail_view)

        self.view_group = QActionGroup(self)
        self.view_group.addAction(self.view_detail_view)
        self.view_group.addAction(self.view_icon_view)
        self.view_group.addAction(self.view_small_icon_view)

        self.show_hidden = QAction(QIcon.fromTheme('camera-photo'), "Show Hidden", self, checkable=True)
        self.show_hidden.triggered.connect(self.controller.show_hidden)
        self.show_hidden.setShortcut('Ctrl+H')
        self.show_hidden.setChecked(settings.value("globals/show_hidden", False, bool))

        self.show_filtered = QAction(QIcon.fromTheme('camera-photo'), "Show Filtered", self, checkable=True)
        self.show_filtered.triggered.connect(self.controller.show_filtered)

        self.show_abspath = QAction("Show AbsPath", self, checkable=True)
        self.show_abspath.triggered.connect(self.controller.show_abspath)

        self.show_basename = QAction("Show Basename", self, checkable=True)
        self.show_basename.triggered.connect(self.controller.show_basename)

        self.path_options_group = QActionGroup(self)
        self.path_options_group.addAction(self.show_abspath)
        self.path_options_group.addAction(self.show_basename)

        self.toggle_timegaps = QAction("Show Time Gaps", self, checkable=True)
        self.toggle_timegaps.triggered.connect(self.controller.toggle_timegaps)

        # Sorting Options
        self.sort_directories_first = QAction("Directories First", checkable=True)
        self.sort_directories_first.triggered.connect(
            lambda: self.controller._sorter.set_directories_first(self.sort_directories_first.isChecked()))
        self.sort_directories_first.setChecked(True)

        self.sort_reversed = QAction("Reverse Sort", checkable=True)
        self.sort_reversed.triggered.connect(
            lambda: self.controller.set_sort_reversed(self.sort_reversed.isChecked()))

        self.sort_by_name = QAction("Sort by Name", checkable=True)
        self.sort_by_name.triggered.connect(lambda:
                                            self.controller.set_sort_key_func(
                                                lambda x: numeric_sort_key(x.basename().lower())))
        self.sort_by_name.setChecked(True)

        self.sort_by_size = QAction("Sort by Size", checkable=True)
        self.sort_by_size.triggered.connect(lambda: self.controller.set_sort_key_func(FileInfo.size))

        self.sort_by_ext = QAction("Sort by Extension", checkable=True)
        self.sort_by_ext.triggered.connect(lambda: self.controller.set_sort_key_func(FileInfo.ext))

        self.sort_by_date = QAction("Sort by Date", checkable=True)
        self.sort_by_date.triggered.connect(lambda: self.controller.set_sort_key_func(FileInfo.mtime))

        def framerate_key(fileinfo):
            metadata = fileinfo.metadata()
            return metadata.get('framerate', 0)

        self.sort_by_framerate = QAction("Sort by Framerate", checkable=True)
        self.sort_by_framerate.triggered.connect(lambda: self.controller.set_sort_key_func(framerate_key))

        def aspect_ratio_key(fileinfo):
            metadata = fileinfo.metadata()
            if 'width' in metadata and 'height' in metadata and metadata['height'] != 0:
                return metadata['width'] / metadata['height']
            else:
                return 0

        self.sort_by_aspect_ratio = QAction("Sort by Aspect Ratio", checkable=True)
        self.sort_by_aspect_ratio.triggered.connect(lambda: self.controller.set_sort_key_func(aspect_ratio_key))

        def area_key(fileinfo):
            metadata = fileinfo.metadata()
            if 'width' in metadata and 'height' in metadata:
                return metadata['width'] * metadata['height']
            else:
                return 0

        self.sort_by_area = QAction("Sort by Area", checkable=True)
        self.sort_by_area.triggered.connect(lambda: self.controller.set_sort_key_func(area_key))

        def duration_key(fileinfo):
            metadata = fileinfo.metadata()
            if 'duration' in metadata:
                return metadata['duration']
            else:
                return 0

        self.sort_by_duration = QAction("Sort by Duration", checkable=True)
        self.sort_by_duration.triggered.connect(lambda: self.controller.set_sort_key_func(duration_key))

        self.sort_by_user = QAction("Sort by User", checkable=True)
        self.sort_by_group = QAction("Sort by Group", checkable=True)
        self.sort_by_permission = QAction("Sort by Permission", checkable=True)
        self.sort_by_random = QAction("Random Shuffle", checkable=True)
        # self.sort_by_random.triggered.connect(lambda: self.controller.set_sort_key_func(None))
        self.sort_by_random.triggered.connect(lambda: print("sort_by_random: not implemented"))

        self.sort_group = QActionGroup(self)
        self.sort_group.addAction(self.sort_by_name)
        self.sort_group.addAction(self.sort_by_size)
        self.sort_group.addAction(self.sort_by_ext)
        self.sort_group.addAction(self.sort_by_date)
        self.sort_group.addAction(self.sort_by_area)
        self.sort_group.addAction(self.sort_by_duration)
        self.sort_group.addAction(self.sort_by_aspect_ratio)
        self.sort_group.addAction(self.sort_by_framerate)
        self.sort_group.addAction(self.sort_by_user)
        self.sort_group.addAction(self.sort_by_group)
        self.sort_group.addAction(self.sort_by_permission)
        self.sort_group.addAction(self.sort_by_random)

        self.group_by_none = QAction("Don't Group", checkable=True)
        self.group_by_none.triggered.connect(self.controller.set_grouper_by_none)
        self.group_by_none.setChecked(True)

        self.group_by_day = QAction("Group by Day", checkable=True)
        self.group_by_day.triggered.connect(self.controller.set_grouper_by_day)

        self.group_by_directory = QAction("Group by Directory", checkable=True)
        self.group_by_directory.triggered.connect(self.controller.set_grouper_by_directory)

        self.group_by_duration = QAction("Group by Duration", checkable=True)
        self.group_by_duration.triggered.connect(self.controller.set_grouper_by_duration)

        self.group_group = QActionGroup(self)
        self.group_group.addAction(self.group_by_none)
        self.group_group.addAction(self.group_by_day)
        self.group_group.addAction(self.group_by_directory)
        self.group_group.addAction(self.group_by_duration)

        self.about = QAction(QIcon.fromTheme('help-about'), 'About dt-fileview', self)
        self.about.setStatusTip('Show About dialog')

        self.about.triggered.connect(self.controller.show_about_dialog)

        self.show_preferences = QAction(QIcon.fromTheme("preferences-system"), "Preferencs...")
        self.show_preferences.triggered.connect(self.controller.show_preferences)

        def on_filter_pin(checked) -> None:
            # FIXME: Could use icon state for this
            if checked:
                self.filter_pin.setIcon(QIcon.fromTheme("remmina-pin-down"))
            else:
                self.filter_pin.setIcon(QIcon.fromTheme("remmina-pin-up"))

        self.filter_pin = QAction(QIcon.fromTheme("remmina-pin-up"), "Pin the filter", checkable=True)
        self.filter_pin.triggered.connect(self.controller.set_filter_pin)
        # self.filter_pin.setToolTip("Pin the filter")

        self.filter_pin.triggered.connect(on_filter_pin)
class MainWindow(QMainWindow):
    def __init__(self):

        super().__init__()  #constructs an object from parent class
        self.data_list = []
        self.initUI()  #calls for initializing method

        #initializes titles that will be plotted later
        self.title = "Plot title"
        self.xtitle = "x axis"
        self.ytitle = "y axis"

    def initUI(self):  #initializing method for main window

        self.setGeometry(
            300, 300, 1050, 1000
        )  #first numbers are coordinates on the window, latter ones size
        self.setWindowTitle('Data Visualization programme')
        self.center()  #calls for method center
        self.statusBar()
        self.create_menubar()
        self.grid_button()  #creates checkable grid button to menubar
        self.fitline_button()  #creates button for fitting a line to points
        self.givetitle_button(
        )  #creates button for user to change the title of the plot
        self.giveaxis_button(
        )  #creates button for user to change the names of the axis
        self.show()

    #METHODS:

    #centers the window on user's screen
    def center(self):

        qr = self.frameGeometry()
        print(qr)
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    #creates menubar and adds file menu to it:
    def create_menubar(self):

        self.menubar = self.menuBar()  #creates top menubar
        fileMenu = self.menubar.addMenu(
            '&File')  #creates filemenu and adds it to menubar
        openfileact = QAction("Open file",
                              self)  #creates "Open file" action to file menu
        openfileact.triggered.connect(
            self.showDialog
        )  #goes to method showDialog(), when user selects "Open file
        fileMenu.addAction(openfileact)

    #Opens file selection window to user and calls for method create_data_objects() of the ReadFile class to read data from the file:
    def showDialog(self):

        dialog = QFileDialog()
        dialog.setNameFilter('Text files (*.txt *.csv) ')
        path = dialog.getOpenFileName(self, 'Open file', '/home',
                                      'Text files (*.txt *.csv) ')

        try:

            with open(path[0]) as fname:
                self.data_list = ReadFile.create_data_objects(
                    fname)  #reads data from file to data_list

                #randomly chooces plotting colours that each data set
                self.colour_list = []
                for i in range(len(self.data_list)):
                    self.colour_list.append(self.get_color())

        except OSError:

            print("Could not open {}".format(path), file=sys.stderr)

    #randomly selects colours for all the data sets plotted
    def get_color(self):

        red = random.randrange(0, 255)
        green = random.randrange(0, 255)
        blue = random.randrange(0, 255)

        return QColor(red, green, blue)

    #Draws the data points:
    def drawPoints(self, qp):

        for i in range(self.how_many_datasets):
            pen = QPen(self.colour_list[i], 14, Qt.SolidLine, Qt.RoundCap)
            qp.setPen(pen)

            for b in range(self.data_list[i].data_length):

                x0 = self.data_list[i].x[b]
                y0 = self.data_list[i].y[b]

                x0 = (
                    x0 + self.normalizer_x
                ) * self.scaling_x + self.margin_x  #here we normalize and scale the x coordinate to be able to plot it
                y0 = (self.height() - 2 * self.margin_y) - (
                    y0 + self.normalizer_y) * self.scaling_y + self.margin_y
                #for y coordinate we also have to substract y from the (heigth - margins) in order to start the drawing from lower left corner instead of upper left corner.
                #On Qt, (0,0) coordinate is on upper left corner and we change it to be in lower left corner for the data.

                qp.drawPoint(x0, y0)

    # scales the data for nice plotting according to the min and max values in data sets:
    def scale(self):
        """
		idea:
				1.find min and max values in all the data sets that will be plottet at the same time
				2.calculate the distance between the min and max values
				3.compare the distance of the min and max data values to the plotting window's length minus some margins
				--> Like this we find scaling number that the data needs to be multiplied with to get nice full screen plot. 
				--> Scaling keeps the relative distances between datapoints but makes them spread to entire alvailable plotting area.

		"""
        #initialize values
        max_values_x = []
        min_values_x = []
        max_values_y = []
        min_values_y = []
        how_many_datasets = len(self.data_list)

        #find min and max values on data
        for i in range(how_many_datasets):

            max_values_x.append(max(self.data_list[i].x))
            min_values_x.append(min(self.data_list[i].x))
            max_values_y.append(max(self.data_list[i].y))
            min_values_y.append(min(self.data_list[i].y))
        self.max_y = max(max_values_y)
        self.max_x = max(max_values_x)
        self.min_y = min(min_values_y)
        self.min_x = min(min_values_x)

        #calculate max distances on data
        self.width_x = abs(self.max_x - self.min_x)
        self.heigth_y = abs(self.max_y - self.min_y)

        #chooce margin sizes
        self.margin_x = 0.13 * self.width()
        self.margin_y = 0.13 * self.height()

        #determine scaling factor to get nice plot
        self.scaling_x = (self.width() - 2 * self.margin_x) / self.width_x
        self.scaling_y = (self.height() - 2 * self.margin_y) / self.heigth_y

        #we will normalize the data to start from zero
        self.normalizer_x = 0
        self.normalizer_y = 0

        if (self.min_x < 0):
            self.normalizer_x = abs(self.min_x)

        if (self.min_y < 0):
            self.normalizer_y = abs(self.min_y)

        if (self.min_x > 0):
            self.normalizer_x = -1 * self.min_x

        if (self.min_y > 0):
            self.normalizer_y = -1 * self.min_y

    #creates figure settings menu and checkable grid button to it:
    def grid_button(self):

        self.plotmenu = self.menubar.addMenu(
            '&Figure settings')  #adds Figure settings menu to menubar
        self.gridact = QAction('Grid', self, checkable=True)
        self.gridact.setStatusTip('Set grid on the figure')
        self.gridact.setChecked(True)  #first put grid on
        self.gridact.triggered.connect(
            self.repaint)  #repaints window if user wants grid on/off
        self.plotmenu.addAction(self.gridact)

    #creates figure settings menu and checkable fitting line button to it:
    def fitline_button(self):

        self.fitact = QAction('Fit line', self, checkable=True)
        self.fitact.setStatusTip('Draw line between points on the figure')
        self.fitact.setChecked(True)
        self.fitact.triggered.connect(
            self.repaint)  #repaints window if user wants line on/off
        self.plotmenu.addAction(self.fitact)

    #when paintEvent is generated, the window is repainted:
    def paintEvent(self, e):

        qp = QPainter()  #Creates a QPainter object that does the painting
        qp.begin(self)

        self.how_many_datasets = len(self.data_list)
        if (self.how_many_datasets !=
                0):  #repaints only if we have data on the list
            self.scale()
            self.drawPoints(qp)
            self.drawGrid(qp)

            if (self.fitact.isChecked()):
                self.fitline(qp)

        qp.end()

    #Draws the grid and number axis:
    def drawGrid(self, qp):
        """
		idea: 
		 1.divides the plot into n blocs depending how thick grid we want. 
		 2. starts plotting the horizontal and vertical grid lines from up to down. 
		 Coordinates of the lines are again skaled and for y coordinate we have to substract y from the (heigth - margins) in order to get the maximun value up and lower value down.


		"""

        #select how many grids we want in horizontal and vertical directions
        number_of_blocs = 5

        #calculates the width and height of one grid square
        dist_between_xlines = self.width_x * self.scaling_x / number_of_blocs
        dist_between_ylines = self.heigth_y * self.scaling_y / number_of_blocs

        for i in range(number_of_blocs + 1):

            #sets color for grid lines
            color = QColor(Qt.black)
            pen = QPen(color, 1, Qt.SolidLine)
            qp.setPen(pen)

            # calculates the coordinates for horizontal lines
            xmin = (self.min_x +
                    self.normalizer_x) * self.scaling_x + self.margin_x
            xmax = (self.max_x +
                    self.normalizer_x) * self.scaling_x + self.margin_x
            yline = ((self.height() - 2 * self.margin_y) -
                     (self.max_y + self.normalizer_y) * self.scaling_y +
                     self.margin_y + dist_between_ylines * i)

            #calculates the coordinates for vertical lines
            ymin = (self.height() -
                    2 * self.margin_y) - (self.max_y + self.normalizer_y
                                          ) * self.scaling_y + self.margin_y
            ymax = (self.height() -
                    2 * self.margin_y) - (self.min_y + self.normalizer_y
                                          ) * self.scaling_y + self.margin_y
            xline = ((self.min_x + self.normalizer_x) * self.scaling_x +
                     self.margin_x + dist_between_xlines * i)

            #creates plottable QLineF objects from coordinates
            lineHorizontal = QLineF(xmin, yline, xmax, yline)
            lineVertical = QLineF(xline, ymin, xline, ymax)

            #draws grid only if user has checked it from the menu
            if (self.gridact.isChecked()):
                qp.drawLine(lineHorizontal)
                qp.drawLine(lineVertical)

            #draws the axis numbers:
            #to find out the real data number we need to remove all the scaling factors.
            textpointx = QPointF(xline, (ymax + 0.02 * ymax))
            textpointy = QPointF((xmin - 0.3 * xmin), yline)

            #finds the x number on data (strips all the scaling)
            xnumbertowrite = xline - self.margin_x
            xnumbertowrite /= self.scaling_x
            xnumbertowrite -= self.normalizer_x

            #finds y number on data (strips all the scaling)
            ynumbertowrite = yline - self.margin_y
            ynumbertowrite /= self.scaling_y
            ynumbertowrite -= self.normalizer_y

            #removes more scaling numbers if there is negative data to get the real y number
            if (self.min_y < 0):

                if (ynumbertowrite < 0):
                    ynumbertowrite = self.heigth_y - abs(ynumbertowrite)
                else:
                    ynumbertowrite = self.max_y - abs(
                        self.min_y) - ynumbertowrite

            else:

                ynumbertowrite = self.max_y - abs(self.min_y) - ynumbertowrite

            #draws the real x and y values
            qp.drawText(textpointx, str(round(xnumbertowrite, 2)))
            qp.drawText(textpointy, str(round(ynumbertowrite, 2)))

        #makes some of the values global for later use when we draw titles and axis names.
        self.xmin = xmin
        self.ymin = ymin
        self.xmax = xmax
        self.ymax = ymax
        self.dist_between_xlines = dist_between_xlines
        self.dist_between_ylines = dist_between_ylines
        self.dist_between_xlines = dist_between_xlines

        #calls for method to draw the title of the plot
        self.draw_title(qp, self.title, self.xtitle, self.ytitle)

        #calls for method to draw the info box of the plot
        self.create_plot_info_box(qp)

    #fits a line between plotted points when user checks "Fit line" from figure settings menu.
    def fitline(self, qp):
        """
		idea of this method:
		The x and y data is not sorted from min to max value. However, in order to fit the line we need to now the indexes of data points that have x values next to each others. (To combine these points to form the line.)
		To do this we copy the datasets and modify the copied datas in following way:

		1. copy x and y data sets for local variables to be able to modify them only for this line fitting purpose
		2. find the index of min x value. (The corresponding y coordinate has the same index.)
		3. delete the current min x value and its corresponding y value to find out the next smallest value. 
		(The next smallest value is the new min x value after deletation)
		4. draw line between these adjacent points.
		5.repeat until the end of data.
		"""
        #draws lines between all data sets
        for i in range(self.how_many_datasets):

            #set pen for each line with rigth colour
            pen = QPen(self.colour_list[i], 1, Qt.SolidLine)
            qp.setPen(pen)

            #copies data sets so that they can be edited
            xdata = self.data_list[i].x.copy()
            ydata = self.data_list[i].y.copy()

            for b in range(self.data_list[i].data_length - 1):

                #find min x value and index corresponding to it
                ind0 = xdata.index(min(xdata))
                x0 = xdata[ind0]
                y0 = ydata[ind0]

                x0 = (
                    x0 + self.normalizer_x
                ) * self.scaling_x + self.margin_x  #here we normalize and scale the x coordinate to be able to plot it
                y0 = (self.height() - 2 * self.margin_y) - (
                    y0 + self.normalizer_y) * self.scaling_y + self.margin_y
                #for y coordinate we also have to substract y from the (heigth - margins) in order to start the drawing from lower left corner instead of upper left corner.
                #(On Qt, (0,0) coordinate is on upper left corner and we change it to be in lower left corner for the data.

                #delete min x value and y value corresponding to it to be able to find the second smallest
                del xdata[ind0]
                del ydata[ind0]
                ind1 = xdata.index(min(xdata))
                x1 = xdata[ind1]
                y1 = ydata[ind1]

                #scale datavalues for plotting
                x1 = (x1 + self.normalizer_x) * self.scaling_x + self.margin_x
                y1 = (self.height() - 2 * self.margin_y) - (
                    y1 + self.normalizer_y) * self.scaling_y + self.margin_y

                #draw line
                line = QLineF(x0, y0, x1, y1)
                qp.drawLine(line)

    #creates button for changing the title of the plot and adds it to plotmenu
    def givetitle_button(self):

        self.titleact = QAction('Edit title', self)
        self.titleact.setStatusTip('Give a title to the figure')
        self.titleact.triggered.connect(self.edit_title)
        self.plotmenu.addAction(self.titleact)

    #reads new title name from the user and calls for repaint.
    def edit_title(self):

        text, ok = QInputDialog.getText(self, 'Enter new title', 'Title:')

        if ok:
            self.title = str(text)

        self.xtitle = self.xtitle
        self.ytitle = self.ytitle
        self.repaint()

    #creates button for changing the names of the axis of the plot and adds it to plotmenu
    def giveaxis_button(self):

        self.axisact = QAction('Edit axis names', self)
        self.axisact.setStatusTip('Give names to the axis of the figure')
        self.axisact.triggered.connect(self.edit_axis)
        self.plotmenu.addAction(self.axisact)

    #reads axis names from the user and calls for repaint
    def edit_axis(self):

        text, ok = QInputDialog.getText(
            self, 'Enter new axis names',
            'Enter new axis names in following format: x-name , y.name')

        if ok:
            xname, yname = text.split(",")
            self.xtitle = str(xname)
            self.ytitle = str(yname)

        self.title = self.title
        self.repaint()

    #draws axis names and title name.
    def draw_title(self, qp, plot_title, x_title, y_title):

        #sets pen for title
        color = QColor(Qt.black)
        pen = QPen(color, 1, Qt.SolidLine)
        qp.setFont(QFont('Decorative', 20))
        qp.setPen(pen)

        #sets coordinates for x axis name:
        textx = QPointF(self.width() / 1.4, (self.ymax + 0.06 * self.ymax))
        textpointxaxis = QPointF(self.width() / 1.35,
                                 (self.ymax + 0.06 * self.ymax))

        #sets coordinates for y axis name:
        texty = QPointF((self.xmin - 0.3 * self.xmin),
                        self.ymin - 0.4 * self.ymin)
        textpointyaxis = QPointF((self.xmin - 0.15 * self.xmin),
                                 self.ymin - 0.4 * self.ymin)

        #sets coordinates for title:
        textpointitle = QPointF((self.width() / 2.3),
                                self.ymin - 0.2 * self.ymin)

        #draws title
        qp.drawText(textpointitle, plot_title)

        #sets pen smaller for axis names and draws them
        qp.setFont(QFont('Decorative', 10))
        qp.setPen(pen)
        qp.drawText(textx, "x:")
        qp.drawText(texty, "y:")
        qp.drawText(textpointxaxis, x_title)
        qp.drawText(textpointyaxis, y_title)

    #creates the info box that includes the name and colour of all the datasets plotted
    def create_plot_info_box(self, qp):

        #selects the colour of the box and its outer lines
        color = QColor(Qt.black)
        pen = QPen(color, 1, Qt.SolidLine)
        qp.setBrush(Qt.white)
        qp.setPen(pen)

        #selects the height and width reserved for the name of one data set
        text_height = self.dist_between_ylines / 6
        text_width = self.dist_between_xlines

        #draws the info box. First two parameters are coordinates and two latter are height and width of the box.
        #The height is determined by how many datasets there are and how many names needs to be written.
        qp.drawRect((self.xmax - 0.2 * self.xmax),
                    (self.ymax - 0.2 * self.ymax), text_width,
                    text_height * len(self.data_list) +
                    self.dist_between_ylines * 0.14)

        for i in range(len(self.data_list)):

            #draw the name of every dataset
            color = QColor(Qt.black)
            pen = QPen(color, 1, Qt.SolidLine)
            qp.setFont(QFont('Decorative', 10))
            qp.setPen(pen)
            textpoint = QPointF(
                (self.xmax - 0.16 * self.xmax), (self.ymax - 0.2 * self.ymax) +
                i * text_height + self.dist_between_ylines * 0.14)
            qp.drawText(textpoint, self.data_list[i].data_title)

            #draw matching point of every dataset
            pen = QPen(self.colour_list[i], 14, Qt.SolidLine, Qt.RoundCap)
            qp.setPen(pen)
            pointcoordinates = QPointF(
                (self.xmax - 0.18 * self.xmax),
                (self.ymax - 0.205 * self.ymax) + i * text_height +
                self.dist_between_ylines * 0.14)
            qp.drawPoint(pointcoordinates)

    #Verifies closing of the window:
    def closeEvent(self, event):

        #here we modify closeEvent -method's event handler to verify exit.
        reply = QMessageBox.question(self, 'Exit programme',
                                     "Are you sure you want to exit?",
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)

        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
Exemplo n.º 44
0
    def init_menubar(self):
        """Generates the main window menu bar."""

        # Creates the actions for the main menu
        ### 'File' menu
        exit_action = QAction('E&xit', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.setStatusTip('Exit smtracker')
        exit_action.triggered.connect(qApp.exit)

        export_action = QAction('&Export...', self)
        export_action.setShortcut('Ctrl+E')
        export_action.setStatusTip('Export table as HTML file')
        export_action.triggered.connect(self.export_html)

        open_action = QAction('&Open...', self)
        open_action.setShortcut('Ctrl+O')
        open_action.setStatusTip('Open a Stats.xml file')
        open_action.triggered.connect(self.open_file)

        ### 'Options' menu
        icons_action = QAction('Enable &icons', self)
        icons_action.setCheckable(True)
        icons_action.setChecked(self.icons_enabled)
        icons_action.triggered.connect(lambda: self.toggle_icons(icons_action.isChecked()))

        ### 'About' menu
        about_action = QAction('&About smtracker...', self)
        about_action.triggered.connect(self.about_box)

        qt_action = QAction('About &Qt...', self)
        qt_action.triggered.connect(QApplication.aboutQt)

        # Creates the menu bar and starts adding items to it
        menubar = self.menuBar()
        file_menu = menubar.addMenu('&File')
        file_menu.addAction(open_action)

        # Create the profile submenu and add the machine profile item
        profile_menu = file_menu.addMenu('Open &profile')
        mp_action = profile_menu.addAction('Machine Profile')

        # Define the location for profiles
        profile_folder, mp_folder = parse.get_profile_location()

        # Check if the machine profile exists
        if os.path.isfile(mp_folder + "Stats.xml") is True:
            no_mp = False
            mp_action.setStatusTip('Open this machine\'s profile')
            machine_profile = etree.parse(mp_folder + "Stats.xml").getroot()
            mp_action.triggered.connect(lambda: self.set_stats(machine_profile))
        else:
            no_mp = True
            mp_action.setEnabled(False)

        # Check if there's any local profiles
        if os.path.isdir(profile_folder) is True:
            no_lp = False
            profile_menu.addSeparator()
            for profile in os.listdir(profile_folder):
                tempstats = etree.parse(profile_folder + profile + "/Stats.xml").getroot()
                tempname = parse.get_profile_name(tempstats)
                action = profile_menu.addAction(tempname)
                function = functools.partial(self.set_stats, tempstats)
                action.triggered.connect(function)
        else:
            no_lp = True

        # If there are no profiles at all, disable profile menu
        if no_mp is True and no_lp is True:
            profile_menu.setEnabled(False)

        # Add the rest of the actions to the menubar
        file_menu.addAction(export_action)
        file_menu.addAction(exit_action)

        options_menu = menubar.addMenu('&Options')
        options_menu.addAction(icons_action)

        about_menu = menubar.addMenu('&About')
        about_menu.addAction(about_action)
        about_menu.addAction(qt_action)
Exemplo n.º 45
0
class MainWindow(QMainWindow):
    def __init__(self, parent):
        QMainWindow.__init__(self)

        self.printer = QPrinter()
        self.load_img = self.load_img_fit
        self.reload_img = self.reload_auto
        self.open_new = parent.open_win
        self.scene = QGraphicsScene()
        self.img_view = ImageView(self)
        self.img_view.setScene(self.scene)
        self.setCentralWidget(self.img_view)

        self.create_actions()
        self.create_menu()
        self.create_dict()
        self.create_toolbar()
        self.slides_next = True

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showMenu)

        self.read_prefs()
        self.read_list = parent.read_list
        self.write_list = parent.write_list
        self.pics_dir = os.path.expanduser('~/Pictures') or QDir.currentPath()
        self.resize(700, 500)

    def create_actions(self):
        self.open_act = QAction('&Open', self, shortcut='Ctrl+O')
        self.open_act.triggered.connect(self.open)
        self.open_new_act = QAction('Open new window',
                                    self,
                                    shortcut='Ctrl+Shift+O')
        self.open_new_act.triggered.connect(partial(self.open, True))
        self.reload_act = QAction('&Reload image', self, shortcut='Ctrl+R')
        self.reload_act.triggered.connect(self.reload_img)
        self.print_act = QAction('&Print', self, shortcut='Ctrl+P')
        self.print_act.triggered.connect(self.print_img)
        self.save_act = QAction('&Save image', self, shortcut='Ctrl+S')
        self.save_act.triggered.connect(self.save_img_as)
        self.close_act = QAction('Close window', self, shortcut='Ctrl+W')
        self.close_act.triggered.connect(self.close)
        self.exit_act = QAction('E&xit', self, shortcut='Ctrl+Q')
        self.exit_act.triggered.connect(self.exit)
        self.fulls_act = QAction('Fullscreen',
                                 self,
                                 shortcut='F11',
                                 checkable=True)
        self.fulls_act.triggered.connect(self.toggle_fs)
        self.ss_act = QAction('Slideshow', self, shortcut='F5', checkable=True)
        self.ss_act.triggered.connect(self.toggle_slideshow)
        self.ss_next_act = QAction('Next / Random image', self, checkable=True)
        self.ss_next_act.triggered.connect(self.set_slide_type)
        self.ss_next_act.setChecked(True)
        self.next_act = QAction('Next image', self, shortcut='Right')
        self.next_act.triggered.connect(self.go_next_img)
        self.prev_act = QAction('Previous image', self, shortcut='Left')
        self.prev_act.triggered.connect(self.go_prev_img)
        self.rotleft_act = QAction('Rotate left', self, shortcut='Ctrl+Left')
        self.rotleft_act.triggered.connect(partial(self.img_rotate, 270))
        self.rotright_act = QAction('Rotate right',
                                    self,
                                    shortcut='Ctrl+Right')
        self.rotright_act.triggered.connect(partial(self.img_rotate, 90))
        self.fliph_act = QAction('Flip image horizontally',
                                 self,
                                 shortcut='Ctrl+H')
        self.fliph_act.triggered.connect(partial(self.img_flip, -1, 1))
        self.flipv_act = QAction('Flip image vertically',
                                 self,
                                 shortcut='Ctrl+V')
        self.flipv_act.triggered.connect(partial(self.img_flip, 1, -1))
        self.resize_act = QAction('Resize image',
                                  self,
                                  triggered=self.resize_img)
        self.crop_act = QAction('Crop image', self, triggered=self.crop_img)
        self.zin_act = QAction('Zoom &In', self, shortcut='Up')
        self.zin_act.triggered.connect(partial(self.img_view.zoom, 1.1))
        self.zout_act = QAction('Zoom &Out', self, shortcut='Down')
        self.zout_act.triggered.connect(partial(self.img_view.zoom, 1 / 1.1))
        self.fit_win_act = QAction('Best &fit',
                                   self,
                                   checkable=True,
                                   shortcut='F',
                                   triggered=self.zoom_default)
        self.fit_win_act.setChecked(True)
        self.prefs_act = QAction('Preferences', self, triggered=self.set_prefs)
        self.props_act = QAction('Properties', self, triggered=self.get_props)
        self.help_act = QAction('&Help',
                                self,
                                shortcut='F1',
                                triggered=self.help_page)
        self.about_act = QAction('&About', self, triggered=self.about_cm)
        self.aboutQt_act = QAction('About &Qt', self, triggered=qApp.aboutQt)

    def create_menu(self):
        self.popup = QMenu(self)
        main_acts = [
            self.open_act, self.open_new_act, self.reload_act, self.print_act,
            self.save_act
        ]
        edit_acts1 = [
            self.rotleft_act, self.rotright_act, self.fliph_act, self.flipv_act
        ]
        edit_acts2 = [self.resize_act, self.crop_act]
        view_acts = [
            self.next_act, self.prev_act, self.zin_act, self.zout_act,
            self.fit_win_act, self.fulls_act, self.ss_act, self.ss_next_act
        ]
        help_acts = [self.help_act, self.about_act, self.aboutQt_act]
        end_acts = [
            self.prefs_act, self.props_act, self.close_act, self.exit_act
        ]
        for act in main_acts:
            self.popup.addAction(act)
        edit_menu = QMenu(self.popup)
        edit_menu.setTitle('&Edit')
        for act in edit_acts1:
            edit_menu.addAction(act)
        edit_menu.addSeparator()
        for act in edit_acts2:
            edit_menu.addAction(act)
        self.popup.addMenu(edit_menu)
        view_menu = QMenu(self.popup)
        view_menu.setTitle('&View')
        for act in view_acts:
            view_menu.addAction(act)
        self.popup.addMenu(view_menu)
        help_menu = QMenu(self.popup)
        help_menu.setTitle('&Help')
        for act in help_acts:
            help_menu.addAction(act)
        self.popup.addMenu(help_menu)
        for act in end_acts:
            self.popup.addAction(act)

        self.action_list = main_acts + edit_acts1 + edit_acts2 + view_acts + help_acts + end_acts
        for act in self.action_list:
            self.addAction(act)

    def showMenu(self, pos):
        self.popup.popup(self.mapToGlobal(pos))

    def create_dict(self):
        """Create a dictionary to handle auto-orientation."""
        self.orient_dict = {
            None: self.load_img,
            '1': self.load_img,
            '2': partial(self.img_flip, -1, 1),
            '3': partial(self.img_rotate, 180),
            '4': partial(self.img_flip, -1, 1),
            '5': self.img_rotate_fliph,
            '6': partial(self.img_rotate, 90),
            '7': self.img_rotate_flipv,
            '8': partial(self.img_rotate, 270)
        }

    def create_toolbar(self):
        script_dir = os.path.dirname(os.path.realpath(__file__))

        def icon(icon_name):
            return QIcon(os.path.join(script_dir, "assets", icon_name))

        def add_action(description, icon_file, function):
            action = QAction(icon(icon_file), description, self)
            action.triggered.connect(function)
            self.toolbar.addAction(action)

        self.toolbar = self.addToolBar("File")
        add_action("save", "save.png", self.save_img)
        add_action("crop", "crop.png", self.crop_img)
        add_action("resize", "resize.png", self.resize_img)
        add_action("save important", "star.png",
                   lambda: self.save_img(rating=100))
        add_action("save non important", "hollow_star.png",
                   lambda: self.save_img(rating=0))

    def read_prefs(self):
        """Parse the preferences from the config file, or set default values."""
        try:
            conf = preferences.Config()
            values = conf.read_config()
            self.auto_orient = values[0]
            self.slide_delay = values[1]
            self.quality = values[2]
        except:
            self.auto_orient = True
            self.slide_delay = 5
            self.quality = 90
        self.reload_img = self.reload_auto if self.auto_orient else self.reload_nonauto

    def set_prefs(self):
        """Write preferences to the config file."""
        dialog = preferences.PrefsDialog(self)
        if dialog.exec_() == QDialog.Accepted:
            self.auto_orient = dialog.auto_orient
            self.slide_delay = dialog.delay_spinb.value()
            self.quality = dialog.qual_spinb.value()
            conf = preferences.Config()
            conf.write_config(self.auto_orient, self.slide_delay, self.quality)
        self.reload_img = self.reload_auto if self.auto_orient else self.reload_nonauto

    def open(self, new_win=False):
        fname = QFileDialog.getOpenFileName(self, 'Open File',
                                            self.pics_dir)[0]
        if fname:
            if fname.lower().endswith(self.read_list):
                if new_win:
                    self.open_new(fname)
                else:
                    self.open_img(fname)
            else:
                QMessageBox.information(
                    self, 'Error',
                    'Cannot load {} images.'.format(fname.rsplit('.', 1)[1]))

    def open_img(self, fname):
        self.fname = fname
        self.reload_img()
        dirname = os.path.dirname(self.fname)
        self.set_img_list(dirname)
        self.img_index = self.filelist.index(self.fname)

    def set_img_list(self, dirname):
        """Create a list of readable images from the current directory."""
        filelist = os.listdir(dirname)
        self.filelist = [
            os.path.join(dirname, fname) for fname in filelist
            if fname.lower().endswith(self.read_list)
        ]
        self.filelist.sort()
        self.last_file = len(self.filelist) - 1

    def set_title(self):
        file_name = self.fname.rsplit('/', 1)[1]
        size = " [%d x %d]" % (self.pixmap.width(), self.pixmap.height())
        self.setWindowTitle(file_name + size)

    def get_img(self):
        """Get image from fname and create pixmap."""
        image = QImage(self.fname)
        self.pixmap = QPixmap.fromImage(image)

    def reload_auto(self):
        """Load a new image with auto-orientation."""
        self.get_img()
        try:
            orient = GExiv2.Metadata(self.fname)['Exif.Image.Orientation']
            self.orient_dict[orient]()
        except:
            self.load_img()

    def reload_nonauto(self):
        """Load a new image without auto-orientation."""
        self.get_img()
        self.load_img()

    def load_img_fit(self):
        """Load the image to fit the window."""
        self.scene.clear()
        self.scene.addPixmap(self.pixmap)
        self.scene.setSceneRect(0, 0, self.pixmap.width(),
                                self.pixmap.height())
        self.img_view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)
        self.set_title()

    def load_img_1to1(self):
        """Load the image at its original size."""
        self.scene.clear()
        self.img_view.resetTransform()
        self.scene.addPixmap(self.pixmap)
        self.scene.setSceneRect(0, 0, self.pixmap.width(),
                                self.pixmap.height())
        pixitem = QGraphicsPixmapItem(self.pixmap)
        self.img_view.centerOn(pixitem)
        self.set_title()

    def go_next_img(self):
        self.img_index = self.img_index + 1 if self.img_index < self.last_file else 0
        self.fname = self.filelist[self.img_index]
        self.reload_img()

    def go_prev_img(self):
        self.img_index = self.img_index - 1 if self.img_index else self.last_file
        self.fname = self.filelist[self.img_index]
        self.reload_img()

    def zoom_default(self):
        """Toggle best fit / original size loading."""
        if self.fit_win_act.isChecked():
            self.load_img = self.load_img_fit
            self.create_dict()
            self.load_img()
        else:
            self.load_img = self.load_img_1to1
            self.create_dict()
            self.load_img()

    def img_rotate(self, angle):
        self.pixmap = self.pixmap.transformed(QTransform().rotate(angle))
        self.load_img()

    def img_flip(self, x, y):
        self.pixmap = self.pixmap.transformed(QTransform().scale(x, y))
        self.load_img()

    def img_rotate_fliph(self):
        self.img_rotate(90)
        self.img_flip(-1, 1)

    def img_rotate_flipv(self):
        self.img_rotate(90)
        self.img_flip(1, -1)

    def resize_img(self):
        dialog = editimage.ResizeDialog(self, self.pixmap.width(),
                                        self.pixmap.height())
        if dialog.exec_() == QDialog.Accepted:
            width = dialog.get_width.value()
            height = dialog.get_height.value()
            self.pixmap = self.pixmap.scaled(width, height,
                                             Qt.IgnoreAspectRatio,
                                             Qt.SmoothTransformation)
            self.load_img()

    def crop_img(self):
        def callback(coords):
            self.pixmap = self.pixmap.copy(*coords)
            self.load_img()

        self.img_view.crop(callback)

    def toggle_fs(self):
        if self.fulls_act.isChecked():
            self.showFullScreen()
        else:
            self.showNormal()

    def toggle_slideshow(self):
        if self.ss_act.isChecked():
            self.showFullScreen()
            self.start_ss()
        else:
            self.toggle_fs()
            self.timer.stop()
            self.ss_timer.stop()

    def start_ss(self):
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_img)
        self.timer.start(self.slide_delay * 1000)
        self.ss_timer = QTimer()
        self.ss_timer.timeout.connect(self.update_img)
        self.ss_timer.start(60000)

    def update_img(self):
        if self.slides_next:
            self.go_next_img()
        else:
            self.fname = random.choice(self.filelist)
            self.reload_img()

    def set_slide_type(self):
        self.slides_next = self.ss_next_act.isChecked()

    def save_img_as(self):
        fname = QFileDialog.getSaveFileName(self, 'Save your image',
                                            self.fname)[0]
        if fname:
            if fname.lower().endswith(self.write_list):
                keep_exif = QMessageBox.question(
                    self, 'Save exif data',
                    'Do you want to save the picture metadata?',
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
                if keep_exif == QMessageBox.Yes:
                    exif = GExiv2.Metadata(self.fname)
                    self.pixmap.save(fname, None, self.quality)
                    if exif:
                        saved_exif = GExiv2.Metadata(fname)
                        for tag in exif.get_exif_tags():
                            saved_exif[tag] = exif[tag]
                        saved_exif.set_orientation(GExiv2.Orientation.NORMAL)
                        saved_exif.save_file()
            else:
                QMessageBox.information(
                    self, 'Error',
                    'Cannot save {} images.'.format(fname.rsplit('.', 1)[1]))

    def save_img(self, rating=None):
        exif = GExiv2.Metadata(self.fname)
        self.pixmap.save(self.fname, None, self.quality)
        if rating is not None:
            exif["Exif.Image.Rating"] = str(rating)
        if exif:
            exif.save_file()

    def print_img(self):
        dialog = QPrintDialog(self.printer, self)
        if dialog.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            if self.pixmap.width() > self.pixmap.height():
                self.pixmap = self.pixmap.transformed(QTransform().rotate(90))
            size = self.pixmap.size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(),
                                size.height())
            painter.setWindow(self.pixmap.rect())
            painter.drawPixmap(0, 0, self.pixmap)

    def resizeEvent(self, event=None):
        if self.fit_win_act.isChecked():
            try:
                self.load_img()
            except:
                pass

    def get_props(self):
        """Get the properties of the current image."""
        image = QImage(self.fname)
        preferences.PropsDialog(self,
                                self.fname.rsplit('/', 1)[1], image.width(),
                                image.height())

    def help_page(self):
        preferences.HelpDialog(self)

    def about_cm(self):
        about_message = 'Version: 0.3.9\nAuthor: David Whitlock\nLicense: GPLv3'
        QMessageBox.about(self, 'About Cheesemaker', about_message)

    def exit(self):
        QCoreApplication.quit()
Exemplo n.º 46
0
class ImageViewer(QMainWindow):
    def __init__(self):
        super(ImageViewer, self).__init__()

        self.printer = QPrinter()
        self.scaleFactor = 0.0

        self.imageLabel = QLabel()
        self.imageLabel.setBackgroundRole(QPalette.Base)
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)

        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.imageLabel)
        self.setCentralWidget(self.scrollArea)

        self.createActions()
        self.createMenus()
        self.initUI()

        self.setWindowTitle("Encrypter Pictures")
        self.resize(500, 400)

    def initUI(self):

        encrypt = QAction(QIcon(CURRENT_DIR + '/encrypt.png'), 'Encrypt', self)
        encrypt.setShortcut('Ctrl+D')
        encrypt.triggered.connect(self.buttonClicked)

        open_file= QAction(QIcon(CURRENT_DIR + '/open.png'), 'Exit', self)
        open_file.triggered.connect(self.open)

        exitAction = QAction(QIcon(CURRENT_DIR + '/exit24.png'), 'Exit', self)
        exitAction.triggered.connect(qApp.quit)

        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(open_file)
        self.toolbar.addAction(encrypt)
        self.toolbar.addAction(exitAction)

        self.statusBar()

    def buttonClicked(self):
        try:
            self.statusBar().showMessage("Encrypting: " + self.fileName)
            self.output_path = cript(self.fileName)
            self.show_image()
            self.statusBar().showMessage("Salve on: " + self.output_path)
        except AttributeError:
            self.statusBar().showMessage("Select an image")

    def show_image(self):
        """docstring for show_image"""
        if self.output_path:
            image = QImage(self.output_path)
            if image.isNull():
                QMessageBox.information(self, "Image Viewer",
                        "Cannot load %s." % self.output_path)
                return

            self.imageLabel.setPixmap(QPixmap.fromImage(image))
            self.scaleFactor = 1.0

            self.printAct.setEnabled(True)
            self.fitToWindowAct.setEnabled(True)
            self.updateActions()

            if not self.fitToWindowAct.isChecked():
                self.imageLabel.adjustSize()
    def open(self):
        self.fileName, _ = QFileDialog.getOpenFileName(self, "Open File",
                QDir.currentPath())
        if self.fileName:
            image = QImage(self.fileName)
            if image.isNull():
                QMessageBox.information(self, "Image Viewer",
                        "Cannot load %s." % self.fileName)
                return

            self.imageLabel.setPixmap(QPixmap.fromImage(image))
            self.scaleFactor = 1.0

            self.printAct.setEnabled(True)
            self.fitToWindowAct.setEnabled(True)
            self.updateActions()

            if not self.fitToWindowAct.isChecked():
                self.imageLabel.adjustSize()

    def print_(self):
        dialog = QPrintDialog(self.printer, self)
        if dialog.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.imageLabel.pixmap().size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(
                rect.x(),
                rect.y(),
                size.width(),
                size.height()
            )
            painter.setWindow(self.imageLabel.pixmap().rect())
            painter.drawPixmap(0, 0, self.imageLabel.pixmap())

    def zoomIn(self):
        self.scaleImage(1.25)

    def zoomOut(self):
        self.scaleImage(0.8)

    def normalSize(self):
        self.imageLabel.adjustSize()
        self.scaleFactor = 1.0

    def fitToWindow(self):
        fitToWindow = self.fitToWindowAct.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.normalSize()

        self.updateActions()

    def about(self):
        QMessageBox.about(self, "About Encrypter Pictures",
                "<p>The <b>Encrypter Pictures</b> is a software to encrypt and"
                "decrypting any given image")

    def createActions(self):
        self.openAct = QAction(
            QIcon(CURRENT_DIR + '/open.png'),
            "&Open...", self, shortcut="Ctrl+O",
            triggered=self.open
        )

        self.printAct = QAction(
            QIcon(CURRENT_DIR + '/print.png'),
            "&Print...", self, shortcut="Ctrl+P",
            enabled=False, triggered=self.print_
        )

        self.exitAct = QAction(
            QIcon(CURRENT_DIR + '/exit24.png'),
            "E&xit", self, shortcut="Ctrl+Q",
            triggered=self.close
        )

        self.zoomInAct = QAction(
            QIcon(CURRENT_DIR + '/zoom_in.png'),
            "Zoom &In (25%)", self, shortcut="Ctrl++",
            enabled=False, triggered=self.zoomIn
        )

        self.zoomOutAct = QAction(
            QIcon(CURRENT_DIR + '/zoom_out.png'),
            "Zoom &Out (25%)", self, shortcut="Ctrl+-",
            enabled=False, triggered=self.zoomOut
        )

        self.normalSizeAct = QAction(
            QIcon(CURRENT_DIR + '/zoom.png'),
            "&Normal Size", self, shortcut="Ctrl+S",
            enabled=False, triggered=self.normalSize
        )

        self.fitToWindowAct = QAction(
            QIcon(CURRENT_DIR + '/expand.png'),
            "&Fit to Window",
            self,
            enabled=False,
            checkable=True,
            shortcut="Ctrl+F",
            triggered=self.fitToWindow
        )

        self.aboutAct = QAction(
            QIcon(CURRENT_DIR + '/info.png'),
            "&About", self, triggered=self.about
        )

        self.aboutQtAct = QAction(
            QIcon(CURRENT_DIR + '/pyqt.png'),
            "About &Qt", self,
            triggered=QApplication.instance().aboutQt
        )

    def createMenus(self):
        self.fileMenu = QMenu("&File", self)
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.printAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.fitToWindowAct)

        self.helpMenu = QMenu("&Help", self)
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

        self.menuBar().addMenu(self.fileMenu)
        self.menuBar().addMenu(self.viewMenu)
        self.menuBar().addMenu(self.helpMenu)

    def updateActions(self):
        self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())

    def scaleImage(self, factor):
        self.scaleFactor *= factor
        self.imageLabel.resize(self.scaleFactor
                               * self.imageLabel.pixmap().size())

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)

        self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
        self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)

    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(int(factor * scrollBar.value()
                                + ((factor - 1) * scrollBar.pageStep()/2)))
Exemplo n.º 47
0
class ImageViewer(QWidget):
    def __init__(self):

        super(ImageViewer, self).__init__()

        pal = QPalette()
        pal.setColor(QPalette.Background, Qt.lightGray)

        self.factor = 3.0

        self.config = Config()
        self.currentRep = ""

        self.createActions()
        self.createToolbarMenus()
        #self.createMenus()

        self.browserFile()
        self.imgqLabel()
        self.boxSliders()

        self.verticalLayout = QVBoxLayout(self)
        self.horizontalLayout = QHBoxLayout(self)

        self.textInfo = QTextEdit()

        self.textInfoTop = QTextEdit()
        self.textInfoTop.setEnabled(True)
        self.textInfoTop.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Ignored)
        self.textInfoTop.setFontPointSize(11)
        self.textInfoTop.setStyleSheet("background-color: lightgray")
        #self.textInfoTop.adjustSize()
        self.textInfoTop.setText('Welcome to IRMaGe')

        self.tableJson = QTableWidget()
        self.tableJson.setColumnCount(2)
        self.tableJson.setColumnWidth(0, 150)
        self.tableJson.setColumnWidth(1, 400)
        self.tableJson.setSizeAdjustPolicy(
            QAbstractScrollArea.AdjustToContents)

        self.tableJson.setHorizontalHeaderLabels(['Keys', 'Values'])
        #self.tableJson.setBackgroundRole(QPalette.Light)

        self.scrollText = QScrollArea()
        self.scrollText.setBackgroundRole(QPalette.Dark)
        self.scrollText.setWidget(self.textInfoTop)
        self.scrollText.setWidgetResizable(True)
        #=======================================================================
        # self.adjustScrollBar(self.scrollText.horizontalScrollBar(), 1.0)
        # self.adjustScrollBar(self.scrollText.verticalScrollBar(), 2.0)
        #=======================================================================
        self.scrollTable = QScrollArea()
        self.scrollTable.setBackgroundRole(QPalette.Dark)
        self.scrollTable.setWidget(self.tableJson)
        self.scrollTable.setWidgetResizable(True)
        #=======================================================================
        # self.adjustScrollBar(self.scrollTable.horizontalScrollBar(), 2.0)
        # self.adjustScrollBar(self.scrollTable.verticalScrollBar(), 2.0)
        #=======================================================================

        self.headerTabData = [
            'Data', 'PatientName', 'StudyName', 'DateCreation', 'PatientSex',
            'PatientWeight', 'ProtocolName', 'SequenceName'
        ]

        self.tableData = TableDataBrower(self)
        self.tableData.setColumnCount(8)
        self.tableData.setRowCount(10)
        self.tableData.setColumnWidth(0, 200)
        self.tableData.setHorizontalHeaderLabels(self.headerTabData)
        self.tableData.setBackgroundRole(QPalette.Light)
        self.tableData.setSizeAdjustPolicy(
            QAbstractScrollArea.AdjustToContents)
        self.tableData.verticalHeader().hide()

        self.scrollBrowser = QScrollArea()
        self.scrollBrowser.setBackgroundRole(QPalette.Dark)
        self.scrollBrowser.setWidget(self.tableData)
        self.scrollBrowser.setWidgetResizable(True)

        self.splitter0 = QSplitter(Qt.Vertical)
        self.splitter0.addWidget(self.scrollText)
        self.splitter0.addWidget(self.scrollTable)

        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.imageLabel)
        self.scrollArea.setWidgetResizable(False)
        self.scrollArea.setAlignment(Qt.AlignCenter)

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), 0.8)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), 1.0)

        self.splitter1 = QSplitter(Qt.Horizontal)
        self.splitter1.addWidget(self.splitter0)
        self.splitter1.addWidget(self.scrollArea)
        self.splitter1.addWidget(self.layoutSlide)

        self.splitter3 = QSplitter(Qt.Horizontal)
        self.splitter3.addWidget(self.browser)
        self.splitter3.addWidget(self.scrollBrowser)

        self.splitter2 = QSplitter(Qt.Vertical)
        self.splitter2.addWidget(self.splitter1)
        self.splitter2.addWidget(self.splitter3)
        self.splitter2.setHandleWidth(15)
        #=======================================================================
        # self.splitter2.
        #=======================================================================

        self.verticalLayout.addWidget(self.menuToolBar)
        self.verticalLayout.addWidget(self.splitter2)

        self.setWindowTitle("MRImage Viewer (IRMaGe)")
        self.resize(800, 600)

        self.setAutoFillBackground(True)
        self.setPalette(pal)

    def changeSel(self):
        print('Tab changed')

    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(
            int(factor * scrollBar.value() +
                ((factor - 1) * scrollBar.pageStep() / 2)))

    def imgqLabel(self):
        QLabel.__init__(self)
        image = QImage('sources_images/LogoIRMaGe.png')
        self.scaleFactor = 1.0
        self.imageLabel = QLabel()
        self.imageLabel.setBackgroundRole(QPalette.Base)
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)
        self.imageLabel.setPixmap(QPixmap.fromImage(image))
        self.scaleFactor *= self.factor
        self.imageLabel.adjustSize()
        self.imageLabel.resize(self.scaleFactor *
                               self.imageLabel.pixmap().size())

    def open(self, filePath):
        self.img = nib.load(filePath)
        self.textInfoTop.setText('File : ' + filePath + '\n')
        self.textInfoTop.append('Dim : ' + str(self.img.shape) + '\n')
        self.enableSliders()
        self.a1.setValue(0)
        self.a2.setValue(0)
        self.a3.setValue(0)
        self.c2.setMaximum(self.img.shape[0])
        self.c2.setMinimum(-self.img.shape[0])
        self.c3.setMaximum(self.img.shape[1])
        self.c3.setMinimum(-self.img.shape[1])
        self.navigImage()
        self.fitToWindowAct.setEnabled(True)
        self.fitToWindow()

    def openJson(self, pathJson, fileName):
        with open(pathJson, 'r') as stream:
            try:
                json_object = json.load(stream)
                data = json.dumps(json_object, indent=0, sort_keys=True)
                data = json.loads(data)
                rowPosition = 0
                self.tableJson.setRowCount(0)

                i = 0
                for keyd in self.headerTabData:
                    try:
                        val = str(data[keyd])
                        val = val.replace('[', '')
                        val = val.replace(']', '')
                    except:
                        val = ''
                    #===========================================================
                    # self.tableData.insertRow(i)
                    # self.tableData.setItem(0,i,QTableWidgetItem(val))
                    # i+=1
                    #===========================================================
                #===============================================================
                # self.tableData.setItem(0,0,QTableWidgetItem(fileName))
                # self.tableData.selectRow(0)
                #===============================================================
                for keys in data:
                    stringValue = str(data[keys])
                    stringValue = stringValue.replace('[', '')
                    stringValue = stringValue.replace(']', '')
                    self.tableJson.insertRow(rowPosition)
                    self.tableJson.setItem(rowPosition, 0,
                                           QTableWidgetItem(keys))
                    self.tableJson.setItem(rowPosition, 1,
                                           QTableWidgetItem(stringValue))
                    rowPosition += 1
                self.tableJson.resizeColumnsToContents()
            except json.JSONDecodeError as exc:
                itemError = 'Error Json format'
                self.tableJson.setRowCount(0)
                self.tableJson.insertRow(0)
                self.tableJson.setItem(0, 0, QTableWidgetItem(itemError))
                print(exc)

    def jsonParser(self, pathJson):
        with open(pathJson, 'r') as stream:
            try:
                json_object = json.load(stream)
                listTag = json.dumps(json_object, indent=0, sort_keys=True)
                listTag = json.loads(listTag)
            except json.JSONDecodeError as exc:
                itemError = 'Error Json format'
        return listTag

    def tableDataFill(self, pathRepertory):
        files = [f for f in fnmatch.filter(os.listdir(pathRepertory), '*.nii')]
        self.tableData.setRowCount(0)
        j = 0
        for f in files:
            base = os.path.splitext(f)[0]
            g = os.path.join(pathRepertory, base + ".json")
            self.tableData.insertRow(j)
            if os.path.isfile(g):
                data = self.jsonParser(g)
                i = 0
                for keyw in self.headerTabData:
                    try:
                        val = str(data[keyw])
                        val = val.replace('[', '')
                        val = val.replace(']', '')
                    except:
                        val = ''
                    self.tableData.setItem(j, i, QTableWidgetItem(val))
                    i += 1
            else:
                self.tableData.setItem(j, 1,
                                       QTableWidgetItem('No json file found'))
            self.tableData.setItem(j, 0, QTableWidgetItem(f))
            self.tableData.resizeColumnsToContents()
            j += 1

    def indexImage(self):
        sl1 = self.a1.value()
        sl2 = self.a2.value()
        sl3 = self.a3.value()
        if len(self.img.shape) == 3:
            x = self.img.get_data()[:, :, sl1].copy()
            self.a1.setMaximum(self.img.shape[2] - 1)
            self.a2.setMaximum(0)
            self.a3.setMaximum(0)
        if len(self.img.shape) == 4:
            x = self.img.get_data()[:, :, sl1, sl2].copy()
            self.a1.setMaximum(self.img.shape[2] - 1)
            self.a2.setMaximum(self.img.shape[3] - 1)
            self.a3.setMaximum(0)
        if len(self.img.shape) == 5:
            x = self.img.get_data()[:, :, sl1, sl2, sl3].copy()
            self.a1.setMaximum(self.img.shape[2] - 1)
            self.a2.setMaximum(self.img.shape[3] - 1)
            self.a3.setMaximum(self.img.shape[4] - 1)
        x = rotate(x, -90, reshape=False)
        x = np.uint8((x - x.min()) / x.ptp() * 255.0)
        self.x = x

############################ Slice controls  #########################################

    def boxSliders(self):
        self.k1 = QLabel('Slider 1    ')
        self.k2 = QLabel('Slider 2')
        self.k3 = QLabel('Slider 3')

        self.a1 = self.createSlider(0, 0, 0)
        self.a2 = self.createSlider(0, 0, 0)
        self.a3 = self.createSlider(0, 0, 0)

        self.a1.valueChanged.connect(self.changePosValue)
        self.a2.valueChanged.connect(self.changePosValue)
        self.a3.valueChanged.connect(self.changePosValue)

        self.txta1 = self.createFieldValue()
        self.txta2 = self.createFieldValue()
        self.txta3 = self.createFieldValue()

        self.controlsGroup = QGroupBox('Slice Controls')
        gridCtrl = QGridLayout()
        gridCtrl.addWidget(self.k1, 0, 0)
        gridCtrl.addWidget(self.a1, 0, 1)
        gridCtrl.addWidget(self.txta1, 0, 2)
        gridCtrl.addWidget(self.k2, 1, 0)
        gridCtrl.addWidget(self.a2, 1, 1)
        gridCtrl.addWidget(self.txta2, 1, 2)
        gridCtrl.addWidget(self.k3, 2, 0)
        gridCtrl.addWidget(self.a3, 2, 1)
        gridCtrl.addWidget(self.txta3, 2, 2)
        self.controlsGroup.setLayout(gridCtrl)

        ############################ brightness and contrast  ################################
        self.txtb1 = self.createFieldValue()
        self.txtb2 = self.createFieldValue()
        self.txtb3 = self.createFieldValue()
        self.txtb4 = self.createFieldValue()

        self.l1 = QLabel('Brightness    ')
        self.b1 = self.createSlider(101, 0, 50)
        self.l2 = QLabel('Contrast')
        self.b2 = self.createSlider(101, 0, 50)
        self.l3 = QLabel('Sharpness')
        self.b3 = self.createSlider(101, 0, 50)
        self.l4 = QLabel('Color')
        self.b4 = self.createSlider(101, 0, 50)

        self.b1.valueChanged.connect(self.changeContValue)
        self.b2.valueChanged.connect(self.changeContValue)
        self.b3.valueChanged.connect(self.changeContValue)
        self.b4.valueChanged.connect(self.changeContValue)

        self.txtb1.setText(str(0))
        self.txtb2.setText(str(0))
        self.txtb3.setText(str(0))
        self.txtb4.setText(str(0))

        self.buttonResetContrast = QPushButton('reset', self)
        self.buttonResetContrast.setToolTip('Reset all values')
        self.buttonResetContrast.setEnabled(False)
        self.buttonResetContrast.clicked.connect(self.resetValuesContrast)

        self.contrastGroup = QGroupBox('Brightness and Contrast')
        gridCont = QGridLayout()
        gridCont.addWidget(self.l1, 0, 0)
        gridCont.addWidget(self.b1, 0, 1)
        gridCont.addWidget(self.txtb1, 0, 2)
        gridCont.addWidget(self.l2, 1, 0)
        gridCont.addWidget(self.b2, 1, 1)
        gridCont.addWidget(self.txtb2, 1, 2)
        gridCont.addWidget(self.l3, 2, 0)
        gridCont.addWidget(self.b3, 2, 1)
        gridCont.addWidget(self.txtb3, 2, 2)
        gridCont.addWidget(self.l4, 3, 0)
        gridCont.addWidget(self.b4, 3, 1)
        gridCont.addWidget(self.txtb4, 3, 2)
        gridCont.addWidget(self.buttonResetContrast, 4, 2)
        self.contrastGroup.setLayout(gridCont)

        ############################ Transformation  #########################################
        self.txtc1 = self.createFieldValue()
        self.txtc2 = self.createFieldValue()
        self.txtc3 = self.createFieldValue()
        self.txtc4 = self.createFieldValue()

        self.m1 = QLabel('Rotation')
        self.c1 = self.createSlider(180, -180, 0)
        self.m2 = QLabel('Translate X    ')
        self.c2 = self.createSlider(1, -1, 0)
        self.m3 = QLabel('Translate Y    ')
        self.c3 = self.createSlider(1, -1, 0)
        self.m4 = QLabel('Resize')
        self.c4 = self.createSlider(10, 0, 0)

        self.c1.valueChanged.connect(self.changeTransValue)
        self.c2.valueChanged.connect(self.changeTransValue)
        self.c3.valueChanged.connect(self.changeTransValue)
        self.c4.valueChanged.connect(self.changeTransValue)

        self.txtc1.setText(str(0))
        self.txtc2.setText(str(0))
        self.txtc3.setText(str(0))
        self.txtc4.setText(str(0))

        self.buttonResetTransform = QPushButton('reset', self)
        self.buttonResetTransform.setToolTip('Reset all values')
        self.buttonResetTransform.setEnabled(False)
        self.buttonResetTransform.clicked.connect(self.resetValuesTransform)

        self.transformationGroup = QGroupBox('Transformations')
        gridTransf = QGridLayout()
        gridTransf.addWidget(self.m1, 0, 0)
        gridTransf.addWidget(self.c1, 0, 1)
        gridTransf.addWidget(self.txtc1, 0, 2)
        gridTransf.addWidget(self.m2, 1, 0)
        gridTransf.addWidget(self.c2, 1, 1)
        gridTransf.addWidget(self.txtc2, 1, 2)
        gridTransf.addWidget(self.m3, 2, 0)
        gridTransf.addWidget(self.c3, 2, 1)
        gridTransf.addWidget(self.txtc3, 2, 2)
        gridTransf.addWidget(self.m4, 3, 0)
        gridTransf.addWidget(self.c4, 3, 1)
        gridTransf.addWidget(self.txtc4, 3, 2)
        gridTransf.addWidget(self.buttonResetTransform, 4, 2)
        self.transformationGroup.setLayout(gridTransf)

        ####################################################################################
        self.layoutSliders = QVBoxLayout()
        self.layoutSliders.addWidget(self.controlsGroup)
        self.layoutSliders.addWidget(self.contrastGroup)
        self.layoutSliders.addWidget(self.transformationGroup)

        self.layoutSlide = QWidget()
        self.layoutSlide.setLayout(self.layoutSliders)

    def createSlider(self, maxm=0, minm=0, pos=0):
        slider = QSlider(Qt.Horizontal)
        slider.setFocusPolicy(Qt.StrongFocus)
        #slider.setTickPosition(QSlider.TicksBothSides)
        slider.setTickInterval(1)
        #slider.setSingleStep(1)
        slider.setMaximum(maxm)
        slider.setMinimum(minm)
        slider.setValue(pos)
        slider.setEnabled(False)
        return slider

    def createFieldValue(self):
        fieldValue = QLineEdit()
        fieldValue.setEnabled(False)
        fieldValue.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        return fieldValue

    def displayPosValue(self):
        self.txta1.setText(
            str(self.a1.value() + 1) + ' / ' + str(self.a1.maximum() + 1))
        self.txta2.setText(
            str(self.a2.value() + 1) + ' / ' + str(self.a2.maximum() + 1))
        self.txta3.setText(
            str(self.a3.value() + 1) + ' / ' + str(self.a3.maximum() + 1))

    def changePosValue(self):
        self.navigImage()

    def navigImage(self):
        self.indexImage()
        self.displayPosValue()
        w, h = self.x.shape
        image = QImage(self.x.data, w, h, QImage.Format_Indexed8)
        self.pixm = QPixmap.fromImage(image)
        self.imageLabel.setPixmap(self.pixm)
        self.imageLabel.adjustSize()
        self.imageLabel.resize(self.scaleFactor *
                               self.imageLabel.pixmap().size())
        self.filter()

    def changeContValue(self):
        self.txtb1.setText(str(self.b1.value() - 50))
        self.txtb2.setText(str(self.b2.value() - 50))
        self.txtb3.setText(str(self.b3.value() - 50))
        self.txtb4.setText(str(self.b4.value() - 50))
        self.filter()

    def changeTransValue(self):
        self.txtc1.setText(str(self.c1.value()))
        self.txtc2.setText(str(self.c2.value()))
        self.txtc3.setText(str(self.c3.value()))
        self.txtc4.setText(str(self.c4.value()))
        self.filter()

    def filter(self):
        img = Image.fromarray(self.x, 'L')

        brightness = ImageEnhance.Brightness(img)
        newImg = brightness.enhance(1.2 * (self.b1.value() + 1) / 50.0)

        contrast = ImageEnhance.Contrast(newImg)
        newImg = contrast.enhance((self.b2.value() + 1) / 50.0)

        sharpness = ImageEnhance.Sharpness(newImg)
        newImg = sharpness.enhance(2.0 * (self.b3.value() + 1) / 50.0)

        color = ImageEnhance.Color(newImg)
        newImg = color.enhance((self.b4.value() + 1) / 50.0)

        newImg = newImg.rotate(self.c1.value())

        newImg = newImg.transform(
            img.size, Image.AFFINE,
            (1, 0, self.c2.value(), 0, 1, self.c3.value()))

        size1 = int(img.size[0] * (self.c4.value() + 1))
        size2 = int(img.size[1] * (self.c4.value() + 1))

        newImg = newImg.resize((size1, size2), Image.ANTIALIAS)

        self.pixm = QPixmap.fromImage(newImg.toqimage())
        self.imageLabel.setPixmap(self.pixm)
        self.imageLabel.adjustSize()
        self.imageLabel.resize(self.scaleFactor *
                               self.imageLabel.pixmap().size())

    def resetValuesContrast(self):
        self.b1.setSliderPosition(50)
        self.b2.setSliderPosition(50)
        self.b3.setSliderPosition(50)
        self.b4.setSliderPosition(50)
        self.changeContValue()

    def resetValuesTransform(self):
        self.c1.setSliderPosition(0)
        self.c2.setSliderPosition(0)
        self.c3.setSliderPosition(0)
        self.c4.setSliderPosition(0)
        self.changeTransValue()

    def enableSliders(self):
        self.a1.setEnabled(True)
        self.a2.setEnabled(True)
        self.a3.setEnabled(True)
        self.b1.setEnabled(True)
        self.b2.setEnabled(True)
        self.b3.setEnabled(True)
        self.b4.setEnabled(True)
        self.c1.setEnabled(True)
        self.c2.setEnabled(True)
        self.c3.setEnabled(True)
        self.c4.setEnabled(True)
        self.buttonResetContrast.setEnabled(True)
        self.buttonResetTransform.setEnabled(True)

####################################################################################

    def browserFile(self):

        global Browser, Model

        self.browser = QTreeView()

        model = QFileSystemModel()
        model.setNameFilters(['*.nii'])
        model.setNameFilterDisables(False)
        model.setReadOnly(True)

        self.browser.setModel(model)
        self.browser.expandAll()
        self.browser.setColumnWidth(0, 400)

        self.browser.selectionModel().selectionChanged.connect(self.select)

        Browser = self.browser
        Model = model

        #=======================================================================
        # self.browser.doubleClicked.connect(self.selection)
        #self.browser.clicked.connect(self.selection)
        #=======================================================================

    def select(self, signal):
        file_path = self.browser.model().filePath(signal.indexes()[0])
        shortName, fileExt = os.path.splitext(file_path)
        filePath, fileName = os.path.split(file_path)
        self.textInfo.setText(filePath)
        blackColor = QColor(0, 0, 0)

        if os.path.isfile(file_path):
            if fileExt == ".nii":
                if self.currentRep != filePath:
                    self.tableDataFill(filePath)
                    self.currentRep = filePath
                self.open(file_path)
                self.tableData.selectRow(
                    self.tableData.findItems(fileName,
                                             Qt.MatchExactly)[0].row())
                if os.path.isfile(shortName + '.json'):
                    greenColor = QColor(50, 150, 100)
                    self.textInfoTop.setTextColor(greenColor)
                    self.textInfoTop.append('Json file exists ' + '\n')
                    self.openJson(shortName + '.json', fileName)
                else:
                    redColor = QColor(255, 0, 0)
                    self.textInfoTop.setTextColor(redColor)
                    self.textInfoTop.append('Json file doesn\'t exist' + '\n')
                    self.tableJson.setRowCount(0)
        else:
            self.tableData.setRowCount(0)
            self.currentRep = filePath

        self.textInfoTop.setTextColor(blackColor)
        self.scrollText.setWidgetResizable(True)


####################################################################################

    def createMenus(self):
        self.fileMenu = QMenu("&File", self)
        self.fileMenu.addAction(self.exitAct)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.fitToWindowAct)
        self.viewMenu.addSeparator()

        self.helpMenu = QMenu("&Help", self)
        self.helpMenu.addAction(self.aboutAct)

        self.menuBar = QMenuBar()

        self.menuBar.addMenu(self.fileMenu)
        self.menuBar.addMenu(self.viewMenu)
        self.menuBar.addMenu(self.helpMenu)

    def createToolbarMenus(self):
        self.menuToolBar = QToolBar()

        viewMenu = QToolButton()
        viewMenu.setText('View')
        viewMenu.setPopupMode(QToolButton.MenuButtonPopup)
        aMenu = QMenu()
        aMenu.addAction(self.zoomInAct)
        aMenu.addAction(self.zoomOutAct)
        aMenu.addAction(self.normalSizeAct)
        aMenu.addSeparator()
        aMenu.addAction(self.fitToWindowAct)
        viewMenu.setMenu(aMenu)

        helpMenu = QToolButton()
        helpMenu.setText('Help')
        helpMenu.setPopupMode(QToolButton.MenuButtonPopup)
        bMenu = QMenu()
        helpMenu.setMenu(bMenu)

        self.menuToolBar.addWidget(viewMenu)
        self.menuToolBar.addWidget(helpMenu)

    def createActions(self):
        self.exitAct = QAction("Exit",
                               self,
                               shortcut="Ctrl+Q",
                               triggered=self.close)

        self.zoomInAct = QAction("Zoom In (25%)",
                                 self,
                                 shortcut="Ctrl++",
                                 enabled=False,
                                 triggered=self.zoomIn)

        self.zoomOutAct = QAction("Zoom Out (25%)",
                                  self,
                                  shortcut="Ctrl+-",
                                  enabled=False,
                                  triggered=self.zoomOut)

        self.normalSizeAct = QAction("Normal Size",
                                     self,
                                     shortcut="Ctrl+S",
                                     enabled=False,
                                     triggered=self.normalSize)

        self.fitToWindowAct = QAction("Fit to Window",
                                      self,
                                      enabled=False,
                                      checkable=True,
                                      shortcut="Ctrl+F",
                                      triggered=self.fitToWindow)

    def zoomIn(self):
        self.factor = 1.25
        self.scaleImage(self.factor)

    def zoomOut(self):
        self.factor = 0.8
        self.scaleImage(self.factor)

    def normalSize(self):
        self.imageLabel.adjustSize()
        self.scaleFactor = 1.0

    def fitToWindow(self):
        fitToWindow = self.fitToWindowAct.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        self.scrollText.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.normalSize()

        self.updateActions()

    def updateActions(self):
        self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())

    def scaleImage(self, factor):
        self.scaleFactor *= factor
        self.imageLabel.resize(self.scaleFactor *
                               self.imageLabel.pixmap().size())

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)

        self.zoomInAct.setEnabled(self.scaleFactor < 5.0)
        self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)

    def close(self):
        self.close()
Exemplo n.º 48
0
class ObjectView(QListView):
    """Shows cropped object images either in a grid or expanded
    """
    def __init__(self, parent=None):
        super(ObjectView, self).__init__(parent)

        # Items are shown either in a grid or with a single item expanded
        # When more than one item is selected, view changes to grid.

        self.setItemDelegate(CropDelegate(self))
        self.setFlow(self.LeftToRight)
        self.setWrapping(True)
        self.setResizeMode(self.Adjust)
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)

        # Activating an item toggles the expanded state
        self.activated.connect(self.toggle_expanded)

        colour_scheme_choice().colour_scheme_changed.connect(self.colour_scheme_changed)

        self._create_actions()

    def _create_actions(self):
        group = QActionGroup(self)
        self.grid_action = QAction(
            '&Grid', self, shortcut='ctrl+G', triggered=self.show_grid,
            checkable=True, icon=load_icon(':/icons/show_grid.png')
        )
        self.grid_action.setChecked(True)
        group.addAction(self.grid_action)
        self.expanded_action = QAction(
            '&Expanded', self, shortcut='ctrl+E', triggered=self.show_expanded,
            checkable=True, icon=load_icon(':/icons/show_expanded.png')
        )
        group.addAction(self.expanded_action)

    def colour_scheme_changed(self):
        """Slot for colour_scheme_changed signal
        """
        self.update()

    def selectionChanged(self, selected, deselected):
        """QAbstractItemView slot
        """
        debug_print('ObjectView.selectionChanged')

        # Grid view unless exactly one item selected
        if (self.expanded_action.isChecked() and
                1 != len(self.selectionModel().selectedIndexes())):
            self.grid_action.trigger()

        super(ObjectView, self).selectionChanged(selected, deselected)

    def show_grid(self, checked=False):
        """Shows the list as a grid of squares
        """
        debug_print('ObjectView.show_grid')
        self._refresh()

    def show_expanded(self, checked=False):
        """Shows the first item of the selection expanded to fill the viewport.
        If the selection is empty, the first item in the list is selected.
        """
        debug_print('ObjectView.show_expanded')

        # Select a single item
        sm = self.selectionModel()
        selected = sm.selectedIndexes()
        if len(selected) > 1:
            sm.select(selected[0], QItemSelectionModel.ClearAndSelect)
        elif not selected:
            sm.select(self.model().index(0, 0), QItemSelectionModel.Select)

        self._refresh()

    def toggle_expanded(self, index):
        """Selects 'index' and toggles the expanded state
        """
        debug_print('ObjectView.toggle_expanded')
        self.selectionModel().select(index, QItemSelectionModel.Select)
        if self.expanded_action.isChecked():
            self.grid_action.trigger()
        else:
            self.expanded_action.trigger()

    def _refresh(self):
        debug_print('ObjectView._refresh')
        self.scheduleDelayedItemsLayout()
        selected = self.selectionModel().selectedIndexes()
        if selected:
            self.scrollTo(selected[0])

    def keyPressEvent(self, event):
        """QAbstractItemView virtual
        """
        if event.key() in (Qt.Key_Return, Qt.Key_Enter):
            # This logic reimplemented from QAbstractItemView::keyPressEvent,
            # in src/gui/itemviews/qabstractitemview.cpp - make 'Enter' and
            # 'Return' keys toggle the 'Expanded' / 'Grid' state on Mac OS X
            if self.state() != QListView.EditingState or self.hasFocus():
                if self.currentIndex().isValid():
                    self.activated.emit(self.currentIndex())
                event.ignore()
        else:
            super(ObjectView, self).keyPressEvent(event)
Exemplo n.º 49
0
class SimpleRichText(QTextEdit):

    # pylint: disable=method-hidden

    def __init__(self, focusin, focusout):
        QTextEdit.__init__(self)
        self.focusin = focusin
        self.focusout = focusout
        self.createActions()

        #self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)

    def focusOutEvent(self, event):
        self.focusout()

    def focusInEvent(self, event):
        self.focusin()

    def closeEvent(self, event):
        event.accept()

    def createActions(self):
        self.boldAct = QAction(self.tr("&Bold"), self)
        self.boldAct.setCheckable(True)
        self.boldAct.setShortcut(self.tr("Ctrl+B"))
        self.boldAct.setStatusTip(self.tr("Make the text bold"))
        self.boldAct.triggered.connect(self.setBold)
        ### self.connect(self.boldAct, SIGNAL("triggered()"), self.setBold)
        self.addAction(self.boldAct)

        boldFont = self.boldAct.font()
        boldFont.setBold(True)
        self.boldAct.setFont(boldFont)

        self.italicAct = QAction(self.tr("&Italic"), self)
        self.italicAct.setCheckable(True)
        self.italicAct.setShortcut(self.tr("Ctrl+I"))
        self.italicAct.setStatusTip(self.tr("Make the text italic"))
        self.italicAct.triggered.connect(self.setItalic)
        ### self.connect(self.italicAct, SIGNAL("triggered()"), self.setItalic)
        self.addAction(self.italicAct)

    def setBold(self):
        format = QTextCharFormat()
        if self.boldAct.isChecked():
            weight = QFont.Bold
        else:
            weight = QFont.Normal
        format.setFontWeight(weight)
        self.setFormat(format)

    def setItalic(self):
        format = QTextCharFormat()
        #format.setFontItalic(self.__italic.isChecked())
        format.setFontItalic(self.italicAct.isChecked())
        self.setFormat(format)

    def setUnderline(self):
        format = QTextCharFormat()
        format.setFontUnderline(self.__underline.isChecked())
        self.setFormat(format)

    def setFormat(self, format):
        self.textCursor().mergeCharFormat(format)
        self.mergeCurrentCharFormat(format)

    def bold(self):
        print("bold")

    def italic(self):
        print("italic")
Exemplo n.º 50
0
class MainWindow(QMainWindow):
    InsertTextButton = 10
    items = {-2: "source", -3: "channel", -4: "sink"}

    def __init__(self):
        import _diagramscene_rc

        super(MainWindow, self).__init__()

        self.config_manipulations = FlumeConfig(self)
        properties_generator.dump_props()

        self.create_actions()
        self.create_menus()
        self.create_tool_box()
        self.clicked_button_id = 0

        self.scene = DiagramScene(self.item_menu)
        self.scene.setSceneRect(QRectF(0, 0, 5000, 5000))

        self.scene.itemInserted.connect(self.item_inserted)
        self.scene.textInserted.connect(self.text_inserted)
        self.scene.itemSelected.connect(self.item_selected)

        self.create_tool_bars()
        # self.scene.enable_grid()

        layout = QHBoxLayout()
        layout.addWidget(self.tool_box)
        self.view = QGraphicsView(self.scene)
        self.view.centerOn(0, 0)
        layout.addWidget(self.view)

        self.widget = QWidget()
        self.widget.setLayout(layout)

        self.setCentralWidget(self.widget)
        self.setWindowTitle("The Flume Illustrator")

    # noinspection PyAttributeOutsideInit,PyArgumentList
    def create_actions(self):

        self.to_front_action = QAction(QIcon(':/images/bringtofront.png'),
                                       "Bring to &Front", self, shortcut="Ctrl+F",
                                       statusTip="Bring item to front", triggered=self.bring_to_front)
        self.send_back_action = QAction(QIcon(':/images/sendtoback.png'),
                                        "Send to &Back", self, shortcut="Ctrl+B",
                                        statusTip="Send item to back", triggered=self.send_to_back)
        self.bold_action = QAction(QIcon(':/images/bold.png'),
                                   "Bold", self, checkable=True, shortcut="Ctrl+B",
                                   triggered=self.handle_font_change)
        self.italic_action = QAction(QIcon(':/images/italic.png'),
                                     "Italic", self, checkable=True, shortcut="Ctrl+I",
                                     triggered=self.handle_font_change)
        self.underline_action = QAction(QIcon(':/images/underline.png'),
                                        "Underline", self, checkable=True, shortcut="Ctrl+U",
                                        triggered=self.handle_font_change)

        self.delete_action = QAction(QIcon(':/images/delete.png'),
                                     "Delete", self, shortcut="Delete", statusTip='Delete item from diagram',
                                     triggered=self.delete_item)
        self.exit_action = QAction("Exit", self, shortcut="Ctrl+X",
                                   statusTip="Quit program", triggered=self.close)
        self.about_action = QAction("About", self, shortcut="Ctrl+B",
                                    triggered=self.about)
        self.load_config_action = QAction("Load", self, shortcut="Ctrl+O",
                                          statusTip="Load config file", triggered=self.config_manipulations.load_config)

        self.enable_grid_action = QAction("Enable grid", self, checkable=True, triggered=self.enable_grid)

    # noinspection PyAttributeOutsideInit
    def create_menus(self):
        self.file_menu = self.menuBar().addMenu("File")
        self.file_menu.addAction(self.load_config_action)
        self.file_menu.addAction(self.exit_action)

        self.item_menu = self.menuBar().addMenu("Item")
        self.item_menu.addAction(self.delete_action)
        self.item_menu.addSeparator()
        self.item_menu.addAction(self.to_front_action)
        self.item_menu.addAction(self.send_back_action)

        self.about_menu = self.menuBar().addMenu("Help")
        self.about_menu.addAction(self.about_action)

    # noinspection PyAttributeOutsideInit,PyUnresolvedReferences
    def create_tool_box(self):
        self.button_group = QButtonGroup()
        self.button_group.setExclusive(False)
        self.button_group.buttonClicked[int].connect(self.button_group_clicked)

        layout = QGridLayout()
        layout.addWidget(self.create_cell_widget("Source", "source"), 0, 0)
        layout.addWidget(self.create_cell_widget("Channel", "channel"), 0, 1)
        layout.addWidget(self.create_cell_widget("Sink", "sink"), 1, 0)

        text_button = QToolButton()
        text_button.setCheckable(True)
        self.button_group.addButton(text_button, self.InsertTextButton)
        text_button.setIcon(QIcon(QPixmap(':/images/textpointer.png').scaled(30, 30)))
        text_button.setIconSize(QSize(50, 50))

        text_layout = QGridLayout()
        text_layout.addWidget(text_button, 0, 0, Qt.AlignHCenter)
        text_layout.addWidget(QLabel("Text"), 1, 0, Qt.AlignCenter)
        text_widget = QWidget()
        text_widget.setLayout(text_layout)
        layout.addWidget(text_widget, 1, 1)

        layout.setRowStretch(3, 10)
        layout.setColumnStretch(2, 10)

        item_widget = QWidget()
        item_widget.setLayout(layout)

        self.tool_box = QToolBox()
        self.tool_box.setSizePolicy(QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Ignored))
        self.tool_box.setMinimumWidth(item_widget.sizeHint().width())
        self.tool_box.addItem(item_widget, "Basic Flume Items")

    # noinspection PyAttributeOutsideInit,PyUnresolvedReferences
    def create_tool_bars(self):

        self.edit_tool_bar = self.addToolBar("Edit")
        self.edit_tool_bar.addAction(self.delete_action)
        self.edit_tool_bar.addAction(self.to_front_action)
        self.edit_tool_bar.addAction(self.send_back_action)

        self.edit_tool_bar.addAction(self.enable_grid_action)

        self.font_combo = QFontComboBox()
        self.font_combo.currentFontChanged.connect(self.current_font_changed)

        self.font_size_combo = QComboBox()
        self.font_size_combo.setEditable(True)
        for i in range(8, 30, 2):
            self.font_size_combo.addItem(str(i))
        validator = QIntValidator(2, 64, self)
        self.font_size_combo.setValidator(validator)
        self.font_size_combo.currentIndexChanged.connect(self.font_size_changed)

        self.font_color_tool_button = QToolButton()
        self.font_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
        self.font_color_tool_button.setMenu(
            self.create_color_menu(self.text_color_changed, Qt.black))
        self.text_action = self.font_color_tool_button.menu().defaultAction()
        self.font_color_tool_button.setIcon(
            self.create_color_tool_button_icon(':/images/textpointer.png',
                                               Qt.black))
        self.font_color_tool_button.setAutoFillBackground(True)
        self.font_color_tool_button.clicked.connect(self.text_button_triggered)

        self.fill_color_tool_button = QToolButton()
        self.fill_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
        self.fill_color_tool_button.setMenu(
            self.create_color_menu(self.item_color_changed, Qt.white))
        self.fillAction = self.fill_color_tool_button.menu().defaultAction()
        self.fill_color_tool_button.setIcon(
            self.create_color_tool_button_icon(':/images/floodfill.png',
                                               Qt.white))
        self.fill_color_tool_button.clicked.connect(self.fill_button_triggered)

        self.line_color_tool_button = QToolButton()
        self.line_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
        self.line_color_tool_button.setMenu(
            self.create_color_menu(self.line_color_changed, Qt.black))
        self.lineAction = self.line_color_tool_button.menu().defaultAction()
        self.line_color_tool_button.setIcon(
            self.create_color_tool_button_icon(':/images/linecolor.png',
                                               Qt.black))
        self.line_color_tool_button.clicked.connect(self.line_button_triggered)

        self.text_tool_bar = self.addToolBar("Font")
        self.text_tool_bar.addWidget(self.font_combo)
        self.text_tool_bar.addWidget(self.font_size_combo)
        self.text_tool_bar.addAction(self.bold_action)
        self.text_tool_bar.addAction(self.italic_action)
        self.text_tool_bar.addAction(self.underline_action)

        self.color_tool_bar = self.addToolBar("Color")
        self.color_tool_bar.addWidget(self.font_color_tool_button)
        self.color_tool_bar.addWidget(self.fill_color_tool_button)
        self.color_tool_bar.addWidget(self.line_color_tool_button)

        self.loading_tool_bar = self.addToolBar("Load")
        self.loading_tool_bar.addAction(self.load_config_action)

        pointer_button = QToolButton()
        pointer_button.setCheckable(True)
        pointer_button.setChecked(True)
        pointer_button.setIcon(QIcon(":/images/pointer.png"))
        line_pointer_button = QToolButton()
        line_pointer_button.setCheckable(True)
        line_pointer_button.setIcon(QIcon(":/images/linepointer.png"))

        self.pointer_type_group = QButtonGroup()
        self.pointer_type_group.addButton(pointer_button, DiagramScene.MoveItem)
        self.pointer_type_group.addButton(line_pointer_button, DiagramScene.InsertLine)
        self.pointer_type_group.buttonClicked[int].connect(self.pointer_group_clicked)

        self.scene_scale_combo = QComboBox()
        self.scene_scale_combo.addItems(["50%", "75%", "100%", "125%", "150%"])
        self.scene_scale_combo.setCurrentIndex(2)
        self.scene_scale_combo.currentIndexChanged[str].connect(self.scene_scale_changed)

        self.pointer_tool_bar = self.addToolBar("Pointer type")
        self.pointer_tool_bar.addWidget(pointer_button)
        self.pointer_tool_bar.addWidget(line_pointer_button)
        self.pointer_tool_bar.addWidget(self.scene_scale_combo)

    def button_group_clicked(self, button_id):
        buttons = self.button_group.buttons()
        self.clicked_button_id = button_id
        for button in buttons:
            if self.button_group.button(button_id) != button:
                button.setChecked(False)
        if button_id == self.InsertTextButton:
            self.scene.set_mode(DiagramScene.InsertText)
        else:
            self.scene.set_item_type(self.items[button_id])
            self.scene.set_mode(DiagramScene.InsertItem)

    def delete_item(self):
        for item in self.scene.selectedItems():
            if isinstance(item, FlumeDiagramItem):
                item.remove_arrows()
            self.scene.removeItem(item)

    # noinspection PyTypeChecker,PyCallByClass
    def about(self):

        # noinspection PyArgumentList
        QMessageBox.about(self, "About Flume Illustrator", "The Flume illustrator shows config-file details")

    def pointer_group_clicked(self):
        self.scene.set_mode(self.pointer_type_group.checkedId())

    def bring_to_front(self):
        if not self.scene.selectedItems():
            return

        selected_item = self.scene.selectedItems()[0]
        overlap_items = selected_item.collidingItems()

        z_value = 0
        for item in overlap_items:
            if item.zValue() >= z_value and isinstance(item, FlumeDiagramItem):
                z_value = item.zValue() + 0.1
        selected_item.setZValue(z_value)

    def send_to_back(self):
        if not self.scene.selectedItems():
            return

        selected_item = self.scene.selectedItems()[0]
        overlap_items = selected_item.collidingItems()

        z_value = 0
        for item in overlap_items:
            if item.zValue() <= z_value and isinstance(item, FlumeDiagramItem):
                z_value = item.zValue() - 0.1
        selected_item.setZValue(z_value)

    def scene_scale_changed(self, scale):
        new_scale = float(scale[:scale.index("%")]) / 100
        old_transform = self.view.transform()
        self.view.resetTransform()
        self.view.translate(old_transform.dx(), old_transform.dy())
        self.view.scale(new_scale, new_scale)

    def item_inserted(self, diagram_type):
        self.pointer_type_group.button(DiagramScene.MoveItem).setChecked(True)
        self.scene.set_mode(self.scene.DefaultMode)
        self.button_group.button(self.clicked_button_id).setChecked(False)

    def text_inserted(self, item):
        self.button_group.button(self.InsertTextButton).setChecked(False)
        self.scene.set_mode(self.pointer_type_group.checkedId())

    def current_font_changed(self, font):
        self.handle_font_change()

    def font_size_changed(self, font=None):
        self.handle_font_change()

    def text_color_changed(self):
        self.text_action = self.sender()
        self.font_color_tool_button.setIcon(
            self.create_color_tool_button_icon(':/images/textpointer.png',
                                               QColor(self.text_action.data())))
        self.text_button_triggered()

    def item_color_changed(self):
        self.fillAction = self.sender()
        self.fill_color_tool_button.setIcon(
            self.create_color_tool_button_icon(':/images/floodfill.png',
                                               QColor(self.fillAction.data())))
        self.fill_button_triggered()

    def line_color_changed(self):
        self.lineAction = self.sender()
        self.line_color_tool_button.setIcon(
            self.create_color_tool_button_icon(':/images/linecolor.png',
                                               QColor(self.lineAction.data())))
        self.line_button_triggered()

    def text_button_triggered(self):
        self.scene.set_text_color(QColor(self.text_action.data()))

    def fill_button_triggered(self):
        self.scene.set_item_color(QColor(self.fillAction.data()))

    def line_button_triggered(self):
        self.scene.set_line_color(QColor(self.lineAction.data()))

    def handle_font_change(self):
        font = self.font_combo.currentFont()
        font.setPointSize(int(self.font_size_combo.currentText()))
        if self.bold_action.isChecked():
            font.setWeight(QFont.Bold)
        else:
            font.setWeight(QFont.Normal)
        font.setItalic(self.italic_action.isChecked())
        font.setUnderline(self.underline_action.isChecked())

        self.scene.setFont(font)

    def item_selected(self, item):
        font = item.font()
        self.font_combo.setCurrentFont(font)
        self.font_size_combo.setEditText(str(font.pointSize()))
        self.bold_action.setChecked(font.weight() == QFont.Bold)
        self.italic_action.setChecked(font.italic())
        self.underline_action.setChecked(font.underline())

    def create_cell_widget(self, text, diagram_type):
        item = FlumeObject(diagram_type, "")
        icon = QIcon(item.pictogram.image())

        button = QToolButton()
        button.setIcon(icon)
        button.setIconSize(QSize(50, 50))
        button.setCheckable(True)
        self.button_group.addButton(button)  # , diagram_type

        layout = QGridLayout()
        layout.addWidget(button, 0, 0, Qt.AlignHCenter)
        layout.addWidget(QLabel(text), 1, 0, Qt.AlignHCenter)

        widget = QWidget()
        widget.setLayout(layout)

        return widget

    # noinspection PyArgumentList
    def create_color_menu(self, slot, default_color):
        colors = [Qt.black, Qt.white, Qt.red, Qt.blue, Qt.yellow]
        names = ["black", "white", "red", "blue", "yellow"]

        color_menu = QMenu(self)
        for color, name in zip(colors, names):
            action = QAction(self.create_color_icon(color), name, self,
                             triggered=slot)
            action.setData(QColor(color))
            color_menu.addAction(action)
            if color == default_color:
                color_menu.setDefaultAction(action)
        return color_menu

    @staticmethod
    def create_color_tool_button_icon(image_file, color):
        pixmap = QPixmap(50, 80)
        pixmap.fill(Qt.transparent)
        painter = QPainter(pixmap)
        image = QPixmap(image_file)
        target = QRect(0, 0, 50, 60)
        source = QRect(0, 0, 42, 42)
        painter.fillRect(QRect(0, 60, 50, 80), color)
        painter.drawPixmap(target, image, source)
        painter.end()

        return QIcon(pixmap)

    @staticmethod
    def create_color_icon(color):
        pixmap = QPixmap(20, 20)
        painter = QPainter(pixmap)
        painter.setPen(Qt.NoPen)
        painter.fillRect(QRect(0, 0, 20, 20), color)
        painter.end()

        return QIcon(pixmap)

    def enable_grid(self):
        if self.enable_grid_action.isChecked():
            color = Qt.black
        else:
            color = Qt.white
        for i in range(50):
            for j in range(50):
                self.scene.addEllipse(i * 100, j * 100, 2, 2, QPen(color))