Esempio n. 1
0
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.setWindowTitle("My Awesome App")

        label = QLabel("THIS IS AWESOME!!!")
        label.setAlignment(Qt.AlignCenter)

        self.setCentralWidget(label)

        toolbar = QToolBar("My main toolbar")
        self.addToolBar(toolbar)

        # Andriod Button
        button_action = QAction(QIcon("android.png"), "Your button", self)
        button_action.setStatusTip('This is your button')
        button_action.triggered.connect(self.onMyToolBarButtonClick)
        button_action.setCheckable(True)
        toolbar.addAction(button_action)

        toolbar.addSeparator()
        #Second Button
        button_action2 = QAction(QIcon('android.png'), 'Your button2', self)
        button_action2.setStatusTip('This is your button 2')
        button_action2.triggered.connect(self.onMyToolBarButtonClick)
        button_action2.setCheckable(True)
        toolbar.addAction(button_action2)

        #CheckBox
        toolbar.addWidget(QLabel('Hello'))
        toolbar.addWidget(QCheckBox())

        self.setStatusBar(QStatusBar(self))
Esempio n. 2
0
    def toolbar(self) -> QToolBar:
        toolbar = QToolBar(self)
        toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        label = QLabel("Blend:")
        toolbar.addWidget(label)

        blend_combo_box = QComboBox()
        modes_list = [
            o for o in getmembers(QPainter)
            if o[0].startswith('CompositionMode_')
        ]
        blend_combo_box.addItems(
            [f[0].replace('CompositionMode_', '') for f in modes_list])
        blend_combo_box.setCurrentText('Screen')
        blend_combo_box.currentTextChanged.connect(
            self._blend_current_text_changed)
        toolbar.addWidget(blend_combo_box)

        toolbar.addSeparator()

        show_scale_bar_action = QAction(QIcon(":/icons/icons8-ruler-16.png"),
                                        "Scale Bar", self)
        show_scale_bar_action.triggered.connect(self._show_scale_bar)
        show_scale_bar_action.setCheckable(True)
        toolbar.addAction(show_scale_bar_action)

        show_mask_action = QAction(QIcon(":/icons/icons8-ruler-16.png"),
                                   "Mask", self)
        show_mask_action.triggered.connect(self._show_mask)
        show_mask_action.setCheckable(True)
        toolbar.addAction(show_mask_action)

        return toolbar
Esempio n. 3
0
    def __init__(self, *args, **kwargs):
        super(MainWindow01, self).__init__(*args, **kwargs)
        
        self.setWindowTitle("My Awesome App")
        
        label = QLabel("THIS IS AWESOME!!!")
        label.setAlignment(Qt.AlignCenter)
        
        self.setCentralWidget(label)
        
        toolbar = QToolBar("My main toolbar")
        self.addToolBar(toolbar)
        
        button_action = QAction(QIcon("rocket.png"), "Your button", self)
        button_action.setStatusTip("This is your button")
        button_action.triggered.connect(self.onMyToolBarButtonClick)
        button_action.setCheckable(True)
        toolbar.addAction(button_action)

        toolbar.addSeparator()
        
        button_action2 = QAction("Your button2", self)
        button_action2.setStatusTip("This is your button2")
        button_action2.triggered.connect(self.onMyToolBarButtonClick)
        button_action2.setCheckable(True)
        toolbar.addAction(button_action2)
        
        toolbar.addWidget(QLabel("Hello"))
        toolbar.addWidget(QCheckBox())

        self.setStatusBar(QStatusBar(self))
Esempio n. 4
0
    def init_ui(self):
        """Layout and main functionalities"""

        path_icon = str(
            os.path.dirname(os.path.abspath(__file__))) + "\\..\\icon\\"

        # Canvas
        self.plot_canvas = FigureCanvas(self.fig)
        self.plot_canvas.draw()

        # Matplotlib toolbar
        plot_toolbar = NavigationToolbar(self.plot_canvas, self)

        # Custom Toolbar
        action_toolbar = QToolBar(self)
        # - Actions -
        refresh_act = QAction(QIcon(path_icon+"refresh.png"), 'Refresh', self)
        refresh_act.triggered.connect(self.refresh_plot)
        close_act = QAction(QIcon(path_icon+"close.png"), 'Close', self)
        close_act.triggered.connect(self.hide)
        # - Format -
        action_toolbar.addAction(refresh_act)
        action_toolbar.addSeparator()
        action_toolbar.addAction(close_act)

        # Layout
        # - For the Widget
        v_plot = QVBoxLayout()
        v_plot.addWidget(self.plot_canvas)
        v_plot.addWidget(plot_toolbar)
        v_plot.addWidget(action_toolbar)
        self.setLayout(v_plot)
Esempio n. 5
0
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setWindowTitle('My Awesome App !!!!!!')

        label = QLabel('This is a PyQt5 window !')
        label.setAlignment(Qt.AlignCenter)
        self.setCentralWidget(label)

        toolbar = QToolBar('My main toolbar')
        toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        toolbar.setIconSize(QSize(16, 16))
        self.addToolBar(toolbar)

        button_action = QAction(QIcon('.\\bug.png'), "Your button", self)
        button_action.setStatusTip('This is your button')
        button_action.triggered.connect(self.onMyToolbarButtonClick)
        button_action.setCheckable(True)
        toolbar.addAction(button_action)

        toolbar.addSeparator()

        button_action2 = QAction(QIcon('.\\bug.png'), "Your button 2", self)
        button_action2.setStatusTip('This is your button 2')
        button_action2.triggered.connect(self.onMyToolbarButtonClick)
        button_action2.setCheckable(True)
        toolbar.addAction(button_action2)

        toolbar.addWidget(QLabel('Hello'))
        toolbar.addWidget(QCheckBox())

        self.setStatusBar(QStatusBar(self))
Esempio n. 6
0
class ToolBarAction(QWidgetAction):
    def __init__(self, *args, **kwargs):
        super(ToolBarAction, self).__init__(*args, **kwargs)
        self._toolBar = QToolBar(
            styleSheet="QToolBar {background: transparent; border: 0;}")
        self._toolBar.setIconSize(QSize(16, 16))
        self.setDefaultWidget(self._toolBar)

    def toolBar(self):
        return self._toolBar

    def addAction(self, action):
        self._toolBar.addAction(action)
        if action.shortcut().toString() > "":
            action.setToolTip(action.text().replace("&", "") + "<br>" +
                              action.shortcut().toString())

    def widgetForAction(self, *args, **kwargs):
        return self._toolBar.widgetForAction(*args, **kwargs)

    def addWidget(self, *args, **kwargs):
        self._toolBar.addWidget(*args, **kwargs)

    def addSeparator(self):
        self._toolBar.addSeparator()
Esempio n. 7
0
    def setupToolbar(self):
        tb = QToolBar()
        self.verticalLayout.insertWidget(0, tb)

        tb.addAction(self.actionReplyToAll)
        self.actionReplyToAll.triggered.connect(self._compose)
        tb.addAction(self.actionReplyToSender)
        self.actionReplyToSender.triggered.connect(self._compose)
        tb.addAction(self.actionForward)
        self.actionForward.triggered.connect(self._compose)

        tb.addSeparator()
        tb.addAction(self.actionFlagMessage)
        self.actionFlagMessage.triggered.connect(self._flagMessages)

        tb.addAction(self.actionTagMessage)
        self.actionTagMessage.triggered.connect(self.openTagEditor)

        tagMenu = QMenu('Tags')
        tagMenu.aboutToShow.connect(self._prepareTagMenu)
        self.actionTagMessage.setMenu(tagMenu)
        tb.addSeparator()

        tb.addAction(self.actionSelectAll)
        self.actionSelectAll.triggered.connect(self.messagesTree.selectAll)
        tb.addAction(self.actionDeleteMessage)
        self.actionDeleteMessage.triggered.connect(
            self._deleteSelectedMessages)

        self.updateToolbarState()
Esempio n. 8
0
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_QWFormDoc()
        self.ui.setupUi(self)

        self.__curFile = ""
        locToolBar = QToolBar("文档", self)
        locToolBar.addAction(self.ui.qAction1)
        locToolBar.addAction(self.ui.qAction5)
        locToolBar.addSeparator()
        locToolBar.addAction(self.ui.qAction2)
        locToolBar.addAction(self.ui.qAction3)
        locToolBar.addAction(self.ui.qAction4)
        locToolBar.addAction(self.ui.qAction7)
        locToolBar.addAction(self.ui.qAction8)
        locToolBar.addSeparator()
        locToolBar.addAction(self.ui.qAction6)
        locToolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        Layout = QVBoxLayout()
        Layout.addWidget(locToolBar)
        Layout.addWidget(self.ui.qPlainTextEdit)
        Layout.setContentsMargins(2, 2, 2, 2)
        Layout.setSpacing(2)
        self.setLayout(Layout)
        self.setAutoFillBackground(True)
Esempio n. 9
0
    def __init__(self):
        super().__init__()
        self.setWindowTitle("My Awesome App")

        label = QLabel("THIS IS AWESOME!!!")
        label.setAlignment(Qt.AlignCenter)

        self.setCentralWidget(label)

        toolbar = QToolBar("My Main toolbar")
        toolbar.setIconSize(QSize(16, 16))
        self.addToolBar(toolbar)

        button_action = QAction(QIcon("../../icons/bug.png"), "Your button",
                                self)
        button_action.setStatusTip("This is your button")
        button_action.triggered.connect(self.onMyToolBarButtonClick)
        button_action.setCheckable(True)  # True, False Toggle 됨
        toolbar.addAction(button_action)

        toolbar.addSeparator()

        button_action2 = QAction(QIcon("../../icons/bug.png"), "Your button2",
                                 self)
        button_action2.setStatusTip("This is your button2")
        button_action2.triggered.connect(self.onMyToolBarButtonClick)
        button_action2.setCheckable(True)
        toolbar.addAction(button_action2)

        toolbar.addWidget(QLabel("Hello"))
        toolbar.addWidget(QCheckBox())

        self.setStatusBar(QStatusBar(self))
Esempio n. 10
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setWindowTitle("My GUI App")

        label = QLabel("This is a PyQt5 window")
        label.setAlignment(Qt.AlignCenter)

        self.setCentralWidget(label)

        toolbar = QToolBar("My Main Toolbar")
        toolbar.setIconSize(QSize(16, 16))
        self.addToolBar(toolbar)

        buttonAction = QAction(QIcon(path.join(iconsPath, "bug.png")),
                               "Your button", self)
        buttonAction.setStatusTip("This is your button")
        buttonAction.triggered.connect(self.onMyToolBarButtonClick)
        buttonAction.setCheckable(True)
        toolbar.addAction(buttonAction)

        toolbar.addSeparator()

        buttonAction2 = QAction(QIcon(path.join(iconsPath, "bug.png")),
                                "Your button 2", self)
        buttonAction2.setStatusTip("This is your second button")
        buttonAction2.triggered.connect(self.onMyToolBarButtonClick)
        buttonAction2.setCheckable(True)
        toolbar.addAction(buttonAction2)

        toolbar.addWidget(QLabel("Hello"))
        toolbar.addWidget(QCheckBox())

        self.setStatusBar(QStatusBar(self))
Esempio n. 11
0
    def run(self):
        toolbar = QToolBar()
        toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        toolbar.setIconSize(QSize(42, 42))

        toolbar.addSeparator()

        self.tb_configure = QAction(icon_get("preferences-system"),
                                    wrap_text(_("Configure"), 4), self)
        toolbar.addAction(self.tb_configure)

        toolbar.addSeparator()

        self.play_one = play(self,
                             "fit_ribbon_one",
                             run_text=wrap_text(_("One itteration"), 2))

        toolbar.addAction(self.play_one)

        self.play = play(self,
                         "fit_ribbon_run",
                         play_icon="forward",
                         run_text=wrap_text(_("Run fit"), 2))

        toolbar.addAction(self.play)

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

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

        toolbar.addAction(self.help)

        return toolbar
Esempio n. 12
0
    def createGroup(self):
        self.bottomMenu = QFrame()

        self.searchText = QLineEdit(self)
        self.searchText.setStatusTip('Text to search for')
        self.replaceText = QLineEdit(self)
        self.replaceText.setStatusTip('Replace search text with this text')
        toolBar = QToolBar()
        # Create new action
        undoAction = QAction(QIcon.fromTheme('edit-undo'), 'Undo', self)
        undoAction.setStatusTip('Undo')
        undoAction.triggered.connect(self.undoCall)
        toolBar.addAction(undoAction)

        # create redo action
        redoAction = QAction(QIcon.fromTheme('edit-redo'), 'Redo', self)
        redoAction.setStatusTip('Redo')
        redoAction.triggered.connect(self.redoCall)
        toolBar.addAction(redoAction)

        toolBar.addSeparator()

        # create replace action
        replaceAction = QAction(QIcon.fromTheme('edit-find-replace'),
                                'Replace', self)
        replaceAction.setStatusTip('Replace text')
        replaceAction.triggered.connect(self.replaceCall)
        toolBar.addAction(replaceAction)

        # create find action
        findAction = QAction(QIcon.fromTheme('edit-find'), 'Find', self)
        findAction.setStatusTip('Find next occurrence of text')
        findAction.triggered.connect(self.findCall)
        toolBar.addAction(findAction)

        # create next action
        previousAction = QAction(QIcon.fromTheme('go-previous'),
                                 'Find Previous', self)
        previousAction.setStatusTip('Find previous occurrence of text')
        previousAction.triggered.connect(self.previousCall)
        toolBar.addAction(previousAction)

        toolBar.addSeparator()

        # create case action
        caseAction = QAction(QIcon.fromTheme('edit-case'), 'Aa', self)
        caseAction.setStatusTip('Toggle between any case and match case')
        caseAction.setCheckable(1)
        caseAction.triggered.connect(self.caseCall)
        toolBar.addAction(caseAction)

        box = QHBoxLayout()
        box.addWidget(toolBar)
        box.addWidget(self.searchText)
        box.addWidget(self.replaceText)
        box.addStretch(1)
        self.bottomMenu.setLayout(box)

        return self.bottomMenu
Esempio n. 13
0
    def simulations(self):
        toolbar = QToolBar()
        toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        toolbar.setIconSize(QSize(42, 42))

        self.tb_simulate = QAction(icon_get("build_play2"),
                                   wrap_text(_("Run scan"), 2), self)
        toolbar.addAction(self.tb_simulate)

        self.tb_stop = QAction(icon_get("media-playback-pause"),
                               wrap_text(_("Stop"), 3), self)
        toolbar.addAction(self.tb_stop)

        toolbar.addSeparator()

        self.tb_plot = QAction(icon_get("plot"), wrap_text(_("Plot"), 4), self)
        toolbar.addAction(self.tb_plot)

        self.tb_plot_time = QAction(icon_get("plot_time"),
                                    wrap_text(_("Time domain plot"), 6), self)
        toolbar.addAction(self.tb_plot_time)

        self.box_widget = QWidget()
        self.box = QVBoxLayout()
        self.box_widget.setLayout(self.box)
        self.box_tb0 = QToolBar()
        self.box_tb0.setIconSize(QSize(32, 32))
        self.box.addWidget(self.box_tb0)
        self.box_tb1 = QToolBar()
        self.box_tb1.setIconSize(QSize(32, 32))
        self.box.addWidget(self.box_tb1)

        self.tb_build = QAction(icon_get("cog"), wrap_text(_("Build scan"), 2),
                                self)
        self.box_tb0.addAction(self.tb_build)

        self.tb_rerun = QAction(icon_get("play-green"),
                                wrap_text(_("Rerun"), 2), self)
        self.box_tb0.addAction(self.tb_rerun)

        self.tb_zip = QAction(icon_get("package-x-generic"),
                              wrap_text(_("Archive simulations"), 2), self)
        self.box_tb0.addAction(self.tb_zip)

        self.tb_clean = QAction(icon_get("clean"),
                                wrap_text(_("Clean simulation"), 4), self)
        self.box_tb1.addAction(self.tb_clean)

        self.tb_run_all = QAction(icon_get("forward2"),
                                  wrap_text(_("Run all scans"), 3), self)
        self.box_tb1.addAction(self.tb_run_all)

        self.tb_notes = QAction(icon_get("text-x-generic"),
                                wrap_text(_("Notes"), 3), self)
        toolbar.addAction(self.tb_notes)

        toolbar.addWidget(self.box_widget)

        return toolbar
Esempio n. 14
0
def ToolBar(*action_list):
    toolbar = QToolBar()
    for action in action_list:
        if action is None:
            toolbar.addSeparator()
        else:
            toolbar.addWidget(ToolButton(action))
    return toolbar
Esempio n. 15
0
    def createToolbar(self) -> QToolBar:
        toolbar = QToolBar()

        import os
        print(os.system("pwd"))

        action = toolbar.addAction('load_hg')
        action.setIcon(QtGui.QIcon(str(Path('Resources/load.png'))))
        action.setText("Загрузить гиперграф")
        action.triggered.connect(self.centralWidget().loadHypergraphSlot)

        action = toolbar.addAction('save_hg')
        action.setIcon(QtGui.QIcon(str(Path('Resources/save.png'))))
        action.setText("Сохранить гиперграф")
        action.triggered.connect(self.centralWidget().saveHypergraphSlot)

        action = toolbar.addAction('new_scene')
        action.setIcon(QtGui.QIcon(str(Path('Resources/new.png'))))
        action.setText("Новый гиперграф")
        action.triggered.connect(self.centralWidget().resetSceneSlot)

        toolbar.addSeparator()

        action = toolbar.addAction('add_vertex')
        action.setIcon(QtGui.QIcon(str(Path('Resources/new_vertex.png'))))
        action.setText("Добавить вершину")
        action.triggered.connect(
            self.centralWidget().addVertexForCurrentSceneSlot)

        action = toolbar.addAction('remove_vertex')
        action.setIcon(QtGui.QIcon(str(Path('Resources/remove_vertex.png'))))
        action.setText("Удалить вершину")
        action.triggered.connect(
            self.centralWidget().removeVertexForCurrentSceneSlot)

        action = toolbar.addAction('add_edge')
        action.setIcon(QtGui.QIcon(str(Path('Resources/new_edge.png'))))
        action.setText("Добавить ребро")
        action.triggered.connect(
            self.centralWidget().addEdgeForCurrentSceneSlot)

        action = toolbar.addAction('remove_edge')
        action.setIcon(QtGui.QIcon(str(Path('Resources/remove_edge.png'))))
        action.setText("Удалить ребро ребро")
        action.triggered.connect(
            self.centralWidget().removeEdgeForCurrentSceneSlot)

        toolbar.addSeparator()

        action = toolbar.addAction('contraction_player')
        action.setIcon(QtGui.QIcon(str(Path('Resources/contraction.png'))))
        action.setText("Стягивание гиперграфа")
        action.triggered.connect(
            self.centralWidget().startContractionPlayerSlot)

        return toolbar
Esempio n. 16
0
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        dia = QtWidgets.QDialog()
        layout = QtWidgets.QVBoxLayout()
        # self.browser = QWebEngineView()
        # self.setStyleSheet("background-color : white;")
        # self.showFullScreen()
        # layout1=QtWidgets.QHBoxLayout()

        self.setWindowTitle("My Awesome App")
        self.setWindowIcon(QtGui.QIcon(os.path.join('ges12.jpg')))

        self.label4 = QLabel("ok")
        self.label4.setAlignment(Qt.AlignTop)
        self.label4.linkActivated.connect(self.link)
        self.label4.setText('<a href="http://google.com/">Google</a>')

        self.label = QLabel("Welcome To My New Desktop Application")
        self.label.setAlignment(Qt.AlignCenter)
        self.label2 = QLabel("LOGIN / REGISTER")
        self.label2.setAlignment(Qt.AlignCenter)
        self.label.setFont(QFont("Arial", 20))
        self.label2.setFont(QFont("Arial", 20))
        self.line = QLineEdit()
        self.line1 = QLineEdit()

        # self.setCentralWidget(self.browser)
        navtb = QToolBar("Navigation")
        navtb.setIconSize(QSize(20, 20))
        self.addToolBar(navtb)

        self.urlbar = QLineEdit()
        navtb.addSeparator()
        navtb.addWidget(self.urlbar)

        # self.label.setAlignment(Qt.AlignCenter)

        self.button = QPushButton("Login")
        self.button.setStatusTip("Next Page")
        # self.button.clicked.connect(self.do)#
        self.b1 = QPushButton("Cancel")
        layout.addWidget(self.label4)

        layout.addWidget(self.label)
        layout.addWidget(self.label2)
        layout.addWidget(self.line)
        layout.addWidget(self.line1)
        layout.addWidget(self.button)
        layout.addWidget(self.b1)

        # self.setCentralWidget(label)
        dia.setLayout(layout)

        self.setCentralWidget(dia)
Esempio n. 17
0
    def initUI(self):

        path_icon = str(
            os.path.dirname(os.path.abspath(__file__))) + \
                    "\\..\\icon\\"

        # Canvas
        self.plotCanvas = FigureCanvas(self.fig)
        self.plotCanvas.draw()

        # Matplotlib toolbar
        plotToolbar = NavigationToolbar(self.plotCanvas, self)

        # Custom Toolbar
        actionToolbar = QToolBar(self)
        # - Labels -
        rollingLabel = QLabel("Moving window: ")
        averageLabel = QLabel("  Average time: ")
        # - Spin Box -
        self.rolling = QSpinBox(self)
        self.rolling.setMinimum(0)
        self.rolling.setMaximum(1000)
        self.rolling.setValue(0)
        self.rolling.setToolTip(
            "Size of the moving window.\n"
            "This is the number of observations used for calculating"
            " the statistic.\n0 = Auto")
        self.average = QComboBox(self)
        self.average.addItems(
            ["None", "Minutely", "Hourly", "Daily", "Weekly"])
        self.average.setToolTip("")
        # - Actions -
        applyAct = QAction(QIcon(path_icon+"apply.png"),
                           'Apply', self)
        applyAct.triggered.connect(self.refreshPlot)
        closeAct = QAction(QIcon(path_icon+"close.png"),
                           'Close', self)
        closeAct.triggered.connect(self.hide)
        # - Format -
        actionToolbar.addWidget(rollingLabel)
        actionToolbar.addWidget(self.rolling)
        actionToolbar.addWidget(averageLabel)
        actionToolbar.addWidget(self.average)
        actionToolbar.addAction(applyAct)
        actionToolbar.addSeparator()
        actionToolbar.addAction(closeAct)

        # Layout
        # - For the Widget
        vPlot = QVBoxLayout()
        vPlot.addWidget(self.plotCanvas)
        vPlot.addWidget(plotToolbar)
        vPlot.addWidget(actionToolbar)
        self.setLayout(vPlot)
Esempio n. 18
0
    def _createToolBars(self):
        # Using a title
        fileToolBar = self.addToolBar("File")

        # Using a QToolBar object
        editToolBar = QToolBar("Edit", self)
        self.addToolBar(editToolBar)
        editToolBar.addSeparator()
        self.fontSizeSpinBox = QSpinBox()
        self.fontSizeSpinBox.setFocusPolicy(Qt.NoFocus)
        editToolBar.addWidget(self.fontSizeSpinBox)
Esempio n. 19
0
    def createGroup(self):
        self.bottomMenu = QFrame()

        self.searchText = QLineEdit(self)
        self.replaceText = QLineEdit(self)

        toolBar = QToolBar()
        # Create new action
        undoAction = QAction(QIcon.fromTheme('edit-undo'), 'Undo', self)
        undoAction.setStatusTip('Undo')
        undoAction.triggered.connect(self.undoCall)
        toolBar.addAction(undoAction)

        # create redo action
        redoAction = QAction(QIcon.fromTheme('edit-redo'), 'Redo', self)
        redoAction.setStatusTip('Undo')
        redoAction.triggered.connect(self.redoCall)
        toolBar.addAction(redoAction)

        toolBar.addSeparator()

        # create replace action
        replaceAction = QAction(QIcon.fromTheme('edit-find-replace'),
                                'Replace', self)
        replaceAction.triggered.connect(self.replaceCall)
        toolBar.addAction(replaceAction)

        # create find action
        findAction = QAction(QIcon.fromTheme('edit-find'), 'Find', self)
        findAction.triggered.connect(self.findCall)
        toolBar.addAction(findAction)

        # create next action
        nextAction = QAction(QIcon.fromTheme('go-previous'), 'Find Previous',
                             self)
        nextAction.triggered.connect(self.nextCall)
        toolBar.addAction(nextAction)

        toolBar.addSeparator()

        # create case action
        caseAction = QAction(QIcon.fromTheme('edit-case'), 'Aa', self)
        caseAction.setCheckable(1)
        caseAction.triggered.connect(self.caseCall)
        toolBar.addAction(caseAction)

        box = QHBoxLayout()
        box.addWidget(toolBar)
        box.addWidget(self.searchText)
        box.addWidget(self.replaceText)
        box.addStretch(1)
        self.bottomMenu.setLayout(box)

        return self.bottomMenu
Esempio n. 20
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 设置窗口标题
        self.setWindowTitle('Rhodes no Workers Desktop')
        # 设置窗口图标
        self.setWindowIcon(QIcon('src/favicon.png'))

        # 设置浏览器
        self.browser = QWebEngineView()

        # 添加导航栏
        navigation_bar = QToolBar('Navigation')
        # 设定图标的大小
        navigation_bar.setIconSize(QSize(20, 20))
        self.addToolBar(navigation_bar)

        # 添加前进、后退、停止加载和刷新的按钮
        back_button = QAction(QIcon('src/back.png'), 'Back', self)
        next_button = QAction(QIcon('src/forward.png'), 'Forward', self)
        stop_button = QAction(QIcon('src/stop.png'), 'Stop', self)
        reload_button = QAction(QIcon('src/reload.png'), 'Reload', self)

        home_button = QPushButton('  HomePage  ', self)
        honor_button = QPushButton('  ArkHonor  ', self)
        honor_button.setStyleSheet("QPushButton{background: lightpink;}")
        story_button = QPushButton('  ArkStory  ', self)
        story_button.setStyleSheet("QPushButton{background: lightblue;}")

        back_button.triggered.connect(self.browser.back)
        next_button.triggered.connect(self.browser.forward)
        stop_button.triggered.connect(self.browser.stop)
        reload_button.triggered.connect(self.browser.reload)

        home_button.clicked.connect(self.homeClick)
        honor_button.clicked.connect(self.honorClick)
        story_button.clicked.connect(self.storyClick)

        # 将按钮添加到导航栏上
        navigation_bar.addAction(back_button)
        navigation_bar.addAction(next_button)
        navigation_bar.addAction(stop_button)
        navigation_bar.addAction(reload_button)

        navigation_bar.addSeparator()

        navigation_bar.addWidget(home_button)
        navigation_bar.addWidget(honor_button)
        navigation_bar.addWidget(story_button)

        # 指定打开界面的 URL
        self.browser.setUrl(QUrl(self.homeUrl))
        # 添加浏览器到窗口中
        self.setCentralWidget(self.browser)
        self.setStyleSheet(self.button_style)
Esempio n. 21
0
    def __init__(self, parent=None):
        super().__init__(parent)
        self.mOpacityLabel = QLabel()
        self.mOpacitySlider = QSlider(Qt.Horizontal)
        self.mLayerView = LayerView()
        self.mMapDocument = None
        self.mUpdatingSlider = False
        self.mChangingLayerOpacity = False
        self.mUpdatingSlider = False

        self.setObjectName("layerDock")
        widget = QWidget(self)
        layout = QVBoxLayout(widget)
        layout.setContentsMargins(5, 5, 5, 5)
        opacityLayout = QHBoxLayout()
        self.mOpacitySlider.setRange(0, 100)
        self.mOpacitySlider.setEnabled(False)
        opacityLayout.addWidget(self.mOpacityLabel)
        opacityLayout.addWidget(self.mOpacitySlider)
        self.mOpacityLabel.setBuddy(self.mOpacitySlider)
        handler = MapDocumentActionHandler.instance()
        newLayerMenu = QMenu(self)
        newLayerMenu.addAction(handler.actionAddTileLayer())
        newLayerMenu.addAction(handler.actionAddObjectGroup())
        newLayerMenu.addAction(handler.actionAddImageLayer())
        newIcon = QIcon(":/images/16x16/document-new.png")
        newLayerButton = QToolButton()
        newLayerButton.setPopupMode(QToolButton.InstantPopup)
        newLayerButton.setMenu(newLayerMenu)
        newLayerButton.setIcon(newIcon)
        Utils.setThemeIcon(newLayerButton, "document-new")
        buttonContainer = QToolBar()
        buttonContainer.setFloatable(False)
        buttonContainer.setMovable(False)
        buttonContainer.setIconSize(QSize(16, 16))
        buttonContainer.addWidget(newLayerButton)
        buttonContainer.addAction(handler.actionMoveLayerUp())
        buttonContainer.addAction(handler.actionMoveLayerDown())
        buttonContainer.addAction(handler.actionDuplicateLayer())
        buttonContainer.addAction(handler.actionRemoveLayer())
        buttonContainer.addSeparator()
        buttonContainer.addAction(handler.actionToggleOtherLayers())
        listAndToolBar = QVBoxLayout()
        listAndToolBar.setSpacing(0)
        listAndToolBar.addWidget(self.mLayerView)
        listAndToolBar.addWidget(buttonContainer)
        layout.addLayout(opacityLayout)
        layout.addLayout(listAndToolBar)
        self.setWidget(widget)
        self.retranslateUi()
        self.mOpacitySlider.valueChanged.connect(self.sliderValueChanged)
        self.updateOpacitySlider()
Esempio n. 22
0
    def __init__(self, parent = None):
        super().__init__(parent)
        self.mOpacityLabel = QLabel()
        self.mOpacitySlider = QSlider(Qt.Horizontal)
        self.mLayerView = LayerView()
        self.mMapDocument = None
        self.mUpdatingSlider = False
        self.mChangingLayerOpacity = False
        self.mUpdatingSlider = False

        self.setObjectName("layerDock")
        widget = QWidget(self)
        layout = QVBoxLayout(widget)
        layout.setContentsMargins(5, 5, 5, 5)
        opacityLayout = QHBoxLayout()
        self.mOpacitySlider.setRange(0, 100)
        self.mOpacitySlider.setEnabled(False)
        opacityLayout.addWidget(self.mOpacityLabel)
        opacityLayout.addWidget(self.mOpacitySlider)
        self.mOpacityLabel.setBuddy(self.mOpacitySlider)
        handler = MapDocumentActionHandler.instance()
        newLayerMenu = QMenu(self)
        newLayerMenu.addAction(handler.actionAddTileLayer())
        newLayerMenu.addAction(handler.actionAddObjectGroup())
        newLayerMenu.addAction(handler.actionAddImageLayer())
        newIcon = QIcon(":/images/16x16/document-new.png")
        newLayerButton = QToolButton()
        newLayerButton.setPopupMode(QToolButton.InstantPopup)
        newLayerButton.setMenu(newLayerMenu)
        newLayerButton.setIcon(newIcon)
        Utils.setThemeIcon(newLayerButton, "document-new")
        buttonContainer = QToolBar()
        buttonContainer.setFloatable(False)
        buttonContainer.setMovable(False)
        buttonContainer.setIconSize(QSize(16, 16))
        buttonContainer.addWidget(newLayerButton)
        buttonContainer.addAction(handler.actionMoveLayerUp())
        buttonContainer.addAction(handler.actionMoveLayerDown())
        buttonContainer.addAction(handler.actionDuplicateLayer())
        buttonContainer.addAction(handler.actionRemoveLayer())
        buttonContainer.addSeparator()
        buttonContainer.addAction(handler.actionToggleOtherLayers())
        listAndToolBar = QVBoxLayout()
        listAndToolBar.setSpacing(0)
        listAndToolBar.addWidget(self.mLayerView)
        listAndToolBar.addWidget(buttonContainer)
        layout.addLayout(opacityLayout)
        layout.addLayout(listAndToolBar)
        self.setWidget(widget)
        self.retranslateUi()
        self.mOpacitySlider.valueChanged.connect(self.sliderValueChanged)
        self.updateOpacitySlider()
Esempio n. 23
0
    def createGroup(self):
        self.bottomMenu = QFrame()

        self.searchText = QLineEdit(self)
        self.replaceText = QLineEdit(self)

        toolBar = QToolBar()
        # Create new action
        undoAction = QAction(QIcon.fromTheme('edit-undo'), 'Undo', self)        
        undoAction.setStatusTip('Undo')
        undoAction.triggered.connect(self.undoCall)
        toolBar.addAction(undoAction)

        # create redo action
        redoAction = QAction(QIcon.fromTheme('edit-redo'), 'Redo', self)        
        redoAction.setStatusTip('Undo')
        redoAction.triggered.connect(self.redoCall)
        toolBar.addAction(redoAction)

        toolBar.addSeparator()

        # create replace action
        replaceAction = QAction(QIcon.fromTheme('edit-find-replace'), 'Replace', self)        
        replaceAction.triggered.connect(self.replaceCall)
        toolBar.addAction(replaceAction)

        # create find action
        findAction = QAction(QIcon.fromTheme('edit-find'), 'Find', self)        
        findAction.triggered.connect(self.findCall)
        toolBar.addAction(findAction)

        # create next action
        nextAction = QAction(QIcon.fromTheme('go-previous'), 'Find Previous', self)        
        nextAction.triggered.connect(self.nextCall)
        toolBar.addAction(nextAction)

        toolBar.addSeparator()

        # create case action
        caseAction = QAction(QIcon.fromTheme('edit-case'), 'Aa', self)  
        caseAction.setCheckable(1)      
        caseAction.triggered.connect(self.caseCall)
        toolBar.addAction(caseAction)

        box = QHBoxLayout()
        box.addWidget(toolBar)
        box.addWidget(self.searchText)
        box.addWidget(self.replaceText)
        box.addStretch(1)
        self.bottomMenu.setLayout(box)

        return self.bottomMenu
Esempio n. 24
0
def toolbar(groups, actions, controls=None):
    texts = [str(action.text()) for action in actions]
    if controls:
        controls = dict([('widget-' + z, v) for z, v in controls.items()])
    toolbar = QToolBar('Toolbar')
    for name, actionlist in groups:
        for action in actionlist:
            if action in texts:
                toolbar.addAction(actions[texts.index(action)])
            elif action in controls:
                toolbar.addWidget(controls[action])
        toolbar.addSeparator()
    return toolbar
Esempio n. 25
0
    def _defineToolBar(self) -> None:
        toolBar = QToolBar(self)
        toolBar.setMovable(False)

        # add actions
        toolBar.addAction(self.actionAdd)
        toolBar.addAction(self.actionRemove)
        toolBar.addSeparator()
        toolBar.addAction(self.actionRemoveAll)
        toolBar.addSeparator()
        toolBar.addAction(self.actionSave)

        self.addToolBar(toolBar)
Esempio n. 26
0
    def createToolBar(self):
        tool_bar = QToolBar("Toolbar")
        # You should set the size of the icons in the toolbar using the setIconSize() method
        # with QSize() to avoid extra padding
        # when PyQt tries to figure out the arrangement by itself.
        tool_bar.setIconSize(QSize(24,24))
        self.addToolBar(tool_bar)

        tool_bar.addAction(self.open_act)
        tool_bar.addAction(self.save_act)
        tool_bar.addAction(self.print_act)
        tool_bar.addSeparator()
        tool_bar.addAction(self.exit_act)
Esempio n. 27
0
    def __createToolbar(self):
        self.buttons = {}

        toolbar = QToolBar("bottom toolbar")
        toolbar.setOrientation(Qt.Horizontal)
        toolbar.setAllowedAreas(Qt.BottomToolBarArea)
        toolbar.setIconSize(QSize(32, 32))

        button1 = QPushButton("Show All")
        toolbar.addWidget(button1)
        toolbar.addSeparator()

        button2 = QPushButton("Play")
        toolbar.addWidget(button2)
        toolbar.addSeparator()

        button3 = QPushButton("Stop")
        toolbar.addWidget(button3)
        toolbar.addSeparator()

        button4 = QPushButton("Record")
        toolbar.addWidget(button4)
        toolbar.addSeparator()

        self.addToolBar(Qt.BottomToolBarArea, toolbar)
Esempio n. 28
0
 def init_topmenu(self):
     topmenu = QToolBar()
     Sync_Button = QPushButton("Sync")
     Sync_Button.clicked.connect(self.do_sync)
     Last_Updated = QLabel("Last Updated:")
     settings = QPushButton("Settings")
     settings.clicked.connect(self.popup_settings)
     topmenu.addWidget(Sync_Button)
     topmenu.addSeparator()
     topmenu.addWidget(Last_Updated)
     topmenu.addWidget(settings)
     # Do not let the user drag the toolbar around
     topmenu.setMovable(False)
     return topmenu
Esempio n. 29
0
 def createToolBar(self):
     """
     Create toolbar for photo editor GUI
     """
     tool_bar = QToolBar("Photo Editor Toolbar")
     tool_bar.setIconSize(QSize(24, 24))
     self.addToolBar(tool_bar)
     # Add actions to toolbar
     tool_bar.addAction(self.open_act)
     tool_bar.addAction(self.save_act)
     tool_bar.addAction(self.print_act)
     tool_bar.addAction(self.clear_act)
     tool_bar.addSeparator()
     tool_bar.addAction(self.exit_act)
Esempio n. 30
0
    def init_bar(self) -> None:
        """
        Init function bar, set to right side of widget
        :return: 
        """
        toolbar = QToolBar()
        toolbar.setObjectName('MapToolbar')
        self.mainWindow.addToolBar(Qt.RightToolBarArea, toolbar)

        # ------------------------------- Open map ----------------------------------
        self.openMap_action = QAction(QIcon('resources/icons/openMap.png'), TR().tr('Open_map'), self.mainWindow,
                                      triggered=self.open_map_action, enabled=False)
        toolbar.addAction(self.openMap_action)

        # ------------------------------- Zoom in ----------------------------------
        self.zoomIn_action = QAction(QIcon('resources/icons/zoomIn.png'), TR().tr('Zoom_in'), self.mainWindow,
                                     triggered=self.zoom_in_action, shortcut='Ctrl++', enabled=False)
        toolbar.addAction(self.zoomIn_action)

        # ------------------------------- Zoom out ----------------------------------
        self.zoomOut_action = QAction(QIcon('resources/icons/zoomOut.png'), TR().tr('Zoom_out'), self.mainWindow,
                                      triggered=self.zoom_out_action, shortcut='Ctrl++', enabled=False)
        toolbar.addAction(self.zoomOut_action)

        # ------------------------------- Edit info  ----------------------------------
        self.info_action = QAction(QIcon('resources/icons/scroll.png'), TR().tr('Edit_info'), self.mainWindow,
                                   triggered=self.edit_info_action, shortcut='Ctrl+P', enabled=False)
        toolbar.addAction(self.info_action)

        toolbar.addSeparator()

        # ------------------------------- Add monster ----------------------------------
        self.addMonster_action = QAction(QIcon('resources/icons/addMonster.png'), TR().tr('Add_monster'), self.mainWindow,
                                         triggered=self.add_monster_action, shortcut='Ctrl+M', enabled=False)
        toolbar.addAction(self.addMonster_action)

        # ------------------------------- Add item ----------------------------------
        self.addItem_action = QAction(QIcon('resources/icons/addItem.png'), TR().tr('Add_item'), self.mainWindow,
                                      triggered=self.add_item_action, enabled=False)
        toolbar.addAction(self.addItem_action)

        # ------------------------------- Add room ----------------------------------
        self.addRoom_action = QAction(QIcon('resources/icons/addRoom.png'), TR().tr('Add_room'), self.mainWindow,
                                      triggered=self.add_room_action, enabled=False)
        toolbar.addAction(self.addRoom_action)

        # ------------------------------- Add object ----------------------------------
        self.addObject_action = QAction(QIcon('resources/icons/addObject.png'), TR().tr('Add_object_map'), self.mainWindow,
                                        triggered=self.add_object_action, enabled=False)
        toolbar.addAction(self.addObject_action)
Esempio n. 31
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # 设置浏览器
        self.browser = HJWebEngineView()
        layout = QVBoxLayout()
        layout.addWidget(self.browser)
        layout.addStretch(1)
        # 添加浏览器到窗口中
        self.setLayout(layout)

        # 使用QToolBar创建导航栏,并使用QAction创建按钮
        # 添加导航栏
        navigation_bar = QToolBar('Navigation')
        # 设定图标的大小
        navigation_bar.setIconSize(QSize(16, 16))
        # 添加导航栏到窗口中
        layout.addWidget(navigation_bar)

        # QAction类提供了抽象的用户界面action,这些action可以被放置在窗口部件中
        # 添加前进、后退、停止加载和刷新的按钮
        back_button = QAction(QIcon('icons/previous.png'), 'Back', self)
        next_button = QAction(QIcon('icons/next.png'), 'Forward', self)
        stop_button = QAction(QIcon('icons/stop_cross.png'), 'stop', self)
        reload_button = QAction(QIcon('icons/reload.png'), 'reload', self)

        back_button.triggered.connect(self.browser.back)
        next_button.triggered.connect(self.browser.forward)
        stop_button.triggered.connect(self.browser.stop)
        reload_button.triggered.connect(self.browser.reload)

        # 将按钮添加到导航栏上
        navigation_bar.addAction(back_button)
        navigation_bar.addAction(next_button)
        navigation_bar.addAction(stop_button)
        navigation_bar.addAction(reload_button)

        # 添加URL地址栏
        self.urlbar = QLineEdit()
        # 让地址栏能响应回车按键信号
        # self.urlbar.returnPressed.connect(self.navigate_to_url)
        # 不允许编辑
        self.urlbar.setReadOnly(True)

        navigation_bar.addSeparator()
        navigation_bar.addWidget(self.urlbar)

        # 让浏览器相应url地址的变化
        self.browser.urlChanged.connect(self.update_urlbar)
Esempio n. 32
0
 def _createToolBar(self):
     tools = QToolBar()
     Geffe = tools.addAction(Window.actions[0], self._change_lfsr3_state)
     Geffe.setToolTip("Geffe Random Number Generator")
     Geffe.setFont(Window.font_toolbar)
     tools.addSeparator()
     StopGo = tools.addAction(Window.actions[1], self._change_lfsr3_state)
     StopGo.setToolTip("Stop&Go Random Number Generator")
     StopGo.setFont(Window.font_toolbar)
     tools.addSeparator()
     Shrinking = tools.addAction(Window.actions[2],
                                 self._change_lfsr3_state)
     Shrinking.setToolTip("Shrinking Random Number Generator")
     Shrinking.setFont(Window.font_toolbar)
     self.addToolBar(tools)
Esempio n. 33
0
    def setup_toolbar(self):
        toolbar = QToolBar("Main Toolbar")
        toolbar.setObjectName("main-toolbar")

        toolbar.addAction(self.start_listening_action)
        toolbar.addAction(self.stop_listening_action)
        toolbar.addSeparator()
        toolbar.addAction(self.stop_debug_action)
        toolbar.addAction(self.detach_debug_action)
        toolbar.addSeparator()
        toolbar.addAction(self.run_debug_action)
        toolbar.addAction(self.step_over_action)
        toolbar.addAction(self.step_into_action)
        toolbar.addAction(self.step_out_action)

        self.addToolBar(toolbar)
Esempio n. 34
0
class ToolBarAction(QWidgetAction):
    def __init__(self, *args, **kwargs):
        super(ToolBarAction, self).__init__(*args, **kwargs)
        self._toolBar = QToolBar(styleSheet="QToolBar {background: transparent; border: 0;}")
        self._toolBar.setIconSize(QSize(16, 16))
        self.setDefaultWidget(self._toolBar)
    def toolBar(self):
        return self._toolBar
    def addAction(self, action):
        self._toolBar.addAction(action)
        if action.shortcut().toString() > "":
            action.setToolTip(action.text().replace("&", "") + "<br>" + action.shortcut().toString())
    def widgetForAction(self, *args, **kwargs):
        return self._toolBar.widgetForAction(*args, **kwargs)
    def addWidget(self, *args, **kwargs):
        self._toolBar.addWidget(*args, **kwargs)
    def addSeparator(self):
        self._toolBar.addSeparator()
Esempio n. 35
0
    def toolbarWidget(self):
        self.brushSizeWidget = BrushSizeWidget(self.brush_size, max_size=70)
        self.clearAction = QAction(QIcon("assets/clear.png"), "Clear", self)
        self.saveAction = QAction(QIcon("assets/save.png"), "Save", self)
        self.removeAction = QAction(QIcon("assets/remove.png"), "Remove", self)
        self.previewAction = QAction(QIcon("assets/preview.png"), "Preview", self)

        toolbar = QToolBar()
        toolbar.setIconSize(QSize(30, 30))
        toolbar.addWidget(self.brushSizeWidget)
        toolbar.addSeparator()
        toolbar.addAction(self.clearAction)
        toolbar.addAction(self.saveAction)
        toolbar.addAction(self.removeAction)
        toolbar.addAction(self.removeAction)
        toolbar.addAction(self.previewAction)
        toolbar.addSeparator()
        toolbar.addWidget(self.sizeWidget())
        toolbar.addWidget(self.prefixWidget())
        toolbar.addWidget(self.toolbarSpacer())
        return toolbar
Esempio n. 36
0
 def initToolBar(self):
     """
     Public method to populate a toolbar with our actions.
     
     @return the populated toolBar (QToolBar)
     """
     toolBar = QToolBar(self.tr("Graphics"), self)
     toolBar.setIconSize(UI.Config.ToolBarIconSize)
     toolBar.addAction(self.deleteShapeAct)
     toolBar.addSeparator()
     toolBar.addAction(self.alignLeftAct)
     toolBar.addAction(self.alignHCenterAct)
     toolBar.addAction(self.alignRightAct)
     toolBar.addAction(self.alignTopAct)
     toolBar.addAction(self.alignVCenterAct)
     toolBar.addAction(self.alignBottomAct)
     toolBar.addSeparator()
     toolBar.addAction(self.incWidthAct)
     toolBar.addAction(self.incHeightAct)
     toolBar.addAction(self.decWidthAct)
     toolBar.addAction(self.decHeightAct)
     toolBar.addAction(self.setSizeAct)
     toolBar.addSeparator()
     toolBar.addAction(self.rescanAct)
     toolBar.addAction(self.relayoutAct)
     
     return toolBar
Esempio n. 37
0
    def __init__(self):
        super(MainWindow, self).__init__()
        # remove close & maximize window buttons
        #self.setWindowFlags(Qt.CustomizeWindowHint|Qt.WindowMinimizeButtonHint)
        self.setMinimumSize(500, 666)
        #self.setMaximumSize(1000,666)
        self.mdiArea = QMdiArea()
        self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.setCentralWidget(self.mdiArea)

        self.mdiArea.subWindowActivated.connect(self.updateMenus)
        self.mdiArea.setViewMode(QMdiArea.TabbedView)

        self.windowMapper = QSignalMapper(self)
        self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow)

        self.child = None

        self.createActions()
        self.createMenus()
        self.createStatusBar()
        self.updateMenus()
        self.readSettings()
        self.setWindowTitle("LEKTURE")

        mytoolbar = QToolBar()
        #self.toolbar = self.addToolBar()
        mytoolbar.addAction(self.newAct)
        mytoolbar.addAction(self.openAct)
        mytoolbar.addAction(self.saveAct)
        mytoolbar.addAction(self.saveAsAct)
        mytoolbar.addSeparator()
        mytoolbar.addAction(self.outputsAct)
        mytoolbar.addAction(self.scenarioAct)
        self.scenarioAct.setVisible(False)
        mytoolbar.setMovable(False)
        mytoolbar.setFixedWidth(60)
        self.addToolBar(Qt.LeftToolBarArea, mytoolbar)
Esempio n. 38
0
 def initBasicToolbar(self, ui, toolbarManager):
     """
     Public slot to initialize the basic VCS toolbar.
     
     @param ui reference to the main window (UserInterface)
     @param toolbarManager reference to a toolbar manager object
         (E5ToolBarManager)
     @return the toolbar generated (QToolBar)
     """
     tb = QToolBar(self.tr("VCS"), ui)
     tb.setIconSize(UI.Config.ToolBarIconSize)
     tb.setObjectName("VersionControlToolbar")
     tb.setToolTip(self.tr('VCS'))
     
     tb.addAction(self.vcsNewAct)
     tb.addAction(self.vcsExportAct)
     tb.addSeparator()
     tb.addAction(self.vcsAddAct)
     
     toolbarManager.addToolBar(tb, tb.windowTitle())
     
     return tb
Esempio n. 39
0
 def initToolbar(self, toolbarManager):
     """
     Public slot to initialize the multi project toolbar.
     
     @param toolbarManager reference to a toolbar manager object
         (E5ToolBarManager)
     @return the toolbar generated (QToolBar)
     """
     tb = QToolBar(self.tr("Multiproject"), self.ui)
     tb.setIconSize(UI.Config.ToolBarIconSize)
     tb.setObjectName("MultiProjectToolbar")
     tb.setToolTip(self.tr('Multiproject'))
     
     tb.addActions(self.actGrp1.actions())
     tb.addAction(self.closeAct)
     tb.addSeparator()
     tb.addAction(self.saveAct)
     tb.addAction(self.saveasAct)
     
     toolbarManager.addToolBar(tb, tb.windowTitle())
     toolbarManager.addAction(self.addProjectAct, tb.windowTitle())
     toolbarManager.addAction(self.propsAct, tb.windowTitle())
     
     return tb
Esempio n. 40
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)
Esempio n. 41
0
class ReTextWindow(QMainWindow):
	def __init__(self, parent=None):
		QMainWindow.__init__(self, parent)
		self.resize(950, 700)
		screenRect = QDesktopWidget().screenGeometry()
		if globalSettings.windowGeometry:
			self.restoreGeometry(globalSettings.windowGeometry)
		else:
			self.move((screenRect.width()-self.width())/2, (screenRect.height()-self.height())/2)
		if not screenRect.contains(self.geometry()):
			self.showMaximized()
		if globalSettings.iconTheme:
			QIcon.setThemeName(globalSettings.iconTheme)
		if QIcon.themeName() in ('hicolor', ''):
			if not QFile.exists(icon_path + 'document-new.png'):
				QIcon.setThemeName(get_icon_theme())
		if QFile.exists(icon_path+'retext.png'):
			self.setWindowIcon(QIcon(icon_path+'retext.png'))
		elif QFile.exists('/usr/share/pixmaps/retext.png'):
			self.setWindowIcon(QIcon('/usr/share/pixmaps/retext.png'))
		else:
			self.setWindowIcon(QIcon.fromTheme('retext',
				QIcon.fromTheme('accessories-text-editor')))
		self.tabWidget = QTabWidget(self)
		self.initTabWidget()
		self.setCentralWidget(self.tabWidget)
		self.tabWidget.currentChanged.connect(self.changeIndex)
		self.tabWidget.tabCloseRequested.connect(self.closeTab)
		toolBar = QToolBar(self.tr('File toolbar'), self)
		self.addToolBar(Qt.TopToolBarArea, toolBar)
		self.editBar = QToolBar(self.tr('Edit toolbar'), self)
		self.addToolBar(Qt.TopToolBarArea, self.editBar)
		self.searchBar = QToolBar(self.tr('Search toolbar'), self)
		self.addToolBar(Qt.BottomToolBarArea, self.searchBar)
		toolBar.setVisible(not globalSettings.hideToolBar)
		self.editBar.setVisible(not globalSettings.hideToolBar)
		self.actionNew = self.act(self.tr('New'), 'document-new',
			self.createNew, shct=QKeySequence.New)
		self.actionNew.setPriority(QAction.LowPriority)
		self.actionOpen = self.act(self.tr('Open'), 'document-open',
			self.openFile, shct=QKeySequence.Open)
		self.actionOpen.setPriority(QAction.LowPriority)
		self.actionSetEncoding = self.act(self.tr('Set encoding'),
			trig=self.showEncodingDialog)
		self.actionSetEncoding.setEnabled(False)
		self.actionReload = self.act(self.tr('Reload'), 'view-refresh',
			lambda: self.currentTab.readTextFromFile())
		self.actionReload.setEnabled(False)
		self.actionSave = self.act(self.tr('Save'), 'document-save',
			self.saveFile, shct=QKeySequence.Save)
		self.actionSave.setEnabled(False)
		self.actionSave.setPriority(QAction.LowPriority)
		self.actionSaveAs = self.act(self.tr('Save as'), 'document-save-as',
			self.saveFileAs, shct=QKeySequence.SaveAs)
		self.actionNextTab = self.act(self.tr('Next tab'), 'go-next',
			lambda: self.switchTab(1), shct=Qt.CTRL+Qt.Key_PageDown)
		self.actionPrevTab = self.act(self.tr('Previous tab'), 'go-previous',
			lambda: self.switchTab(-1), shct=Qt.CTRL+Qt.Key_PageUp)
		self.actionPrint = self.act(self.tr('Print'), 'document-print',
			self.printFile, shct=QKeySequence.Print)
		self.actionPrint.setPriority(QAction.LowPriority)
		self.actionPrintPreview = self.act(self.tr('Print preview'), 'document-print-preview',
			self.printPreview)
		self.actionViewHtml = self.act(self.tr('View HTML code'), 'text-html', self.viewHtml)
		self.actionChangeEditorFont = self.act(self.tr('Change editor font'),
			trig=self.changeEditorFont)
		self.actionChangePreviewFont = self.act(self.tr('Change preview font'),
			trig=self.changePreviewFont)
		self.actionSearch = self.act(self.tr('Find text'), 'edit-find', shct=QKeySequence.Find)
		self.actionSearch.setCheckable(True)
		self.actionSearch.triggered[bool].connect(self.searchBar.setVisible)
		self.searchBar.visibilityChanged.connect(self.searchBarVisibilityChanged)
		self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E,
			trigbool=self.preview)
		if QIcon.hasThemeIcon('document-preview'):
			self.actionPreview.setIcon(QIcon.fromTheme('document-preview'))
		elif QIcon.hasThemeIcon('preview-file'):
			self.actionPreview.setIcon(QIcon.fromTheme('preview-file'))
		elif QIcon.hasThemeIcon('x-office-document'):
			self.actionPreview.setIcon(QIcon.fromTheme('x-office-document'))
		else:
			self.actionPreview.setIcon(QIcon(icon_path+'document-preview.png'))
		self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.Key_L,
		trigbool=self.enableLivePreview)
		menuPreview = QMenu()
		menuPreview.addAction(self.actionLivePreview)
		self.actionPreview.setMenu(menuPreview)
		self.actionTableMode = self.act(self.tr('Table mode'), shct=Qt.CTRL+Qt.Key_T,
			trigbool=lambda x: self.currentTab.editBox.enableTableMode(x))
		if ReTextFakeVimHandler:
			self.actionFakeVimMode = self.act(self.tr('FakeVim mode'),
				shct=Qt.CTRL+Qt.ALT+Qt.Key_V, trigbool=self.enableFakeVimMode)
			if globalSettings.useFakeVim:
				self.actionFakeVimMode.setChecked(True)
				self.enableFakeVimMode(True)
		self.actionFullScreen = self.act(self.tr('Fullscreen mode'), 'view-fullscreen',
			shct=Qt.Key_F11, trigbool=self.enableFullScreen)
		self.actionFullScreen.setPriority(QAction.LowPriority)
		self.actionConfig = self.act(self.tr('Preferences'), icon='preferences-system',
			trig=self.openConfigDialog)
		self.actionConfig.setMenuRole(QAction.PreferencesRole)
		self.actionSaveHtml = self.act('HTML', 'text-html', self.saveFileHtml)
		self.actionPdf = self.act('PDF', 'application-pdf', self.savePdf)
		self.actionOdf = self.act('ODT', 'x-office-document', self.saveOdf)
		self.getExportExtensionsList()
		self.actionQuit = self.act(self.tr('Quit'), 'application-exit', shct=QKeySequence.Quit)
		self.actionQuit.setMenuRole(QAction.QuitRole)
		self.actionQuit.triggered.connect(self.close)
		self.actionUndo = self.act(self.tr('Undo'), 'edit-undo',
			lambda: self.currentTab.editBox.undo(), shct=QKeySequence.Undo)
		self.actionRedo = self.act(self.tr('Redo'), 'edit-redo',
			lambda: self.currentTab.editBox.redo(), shct=QKeySequence.Redo)
		self.actionCopy = self.act(self.tr('Copy'), 'edit-copy',
			lambda: self.currentTab.editBox.copy(), shct=QKeySequence.Copy)
		self.actionCut = self.act(self.tr('Cut'), 'edit-cut',
			lambda: self.currentTab.editBox.cut(), shct=QKeySequence.Cut)
		self.actionPaste = self.act(self.tr('Paste'), 'edit-paste',
			lambda: self.currentTab.editBox.paste(), shct=QKeySequence.Paste)
		self.actionUndo.setEnabled(False)
		self.actionRedo.setEnabled(False)
		self.actionCopy.setEnabled(False)
		self.actionCut.setEnabled(False)
		qApp = QApplication.instance()
		qApp.clipboard().dataChanged.connect(self.clipboardDataChanged)
		self.clipboardDataChanged()
		if enchant_available:
			self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSpellCheck)
			self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale)
		self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit)
		self.actionWebKit.setChecked(globalSettings.useWebKit)
		self.actionShow = self.act(self.tr('Show directory'), 'system-file-manager', self.showInDir)
		self.actionFind = self.act(self.tr('Next'), 'go-next', self.find,
			shct=QKeySequence.FindNext)
		self.actionFindPrev = self.act(self.tr('Previous'), 'go-previous',
			lambda: self.find(back=True), shct=QKeySequence.FindPrevious)
		self.actionCloseSearch = self.act(self.tr('Close'), 'window-close',
			lambda: self.searchBar.setVisible(False))
		self.actionCloseSearch.setPriority(QAction.LowPriority)
		self.actionHelp = self.act(self.tr('Get help online'), 'help-contents', self.openHelp)
		self.aboutWindowTitle = self.tr('About ReText')
		self.actionAbout = self.act(self.aboutWindowTitle, 'help-about', self.aboutDialog)
		self.actionAbout.setMenuRole(QAction.AboutRole)
		self.actionAboutQt = self.act(self.tr('About Qt'))
		self.actionAboutQt.setMenuRole(QAction.AboutQtRole)
		self.actionAboutQt.triggered.connect(qApp.aboutQt)
		availableMarkups = markups.get_available_markups()
		if not availableMarkups:
			print('Warning: no markups are available!')
		self.defaultMarkup = availableMarkups[0] if availableMarkups else None
		if globalSettings.defaultMarkup:
			mc = markups.find_markup_class_by_name(globalSettings.defaultMarkup)
			if mc and mc.available():
				self.defaultMarkup = mc
		if len(availableMarkups) > 1:
			self.chooseGroup = QActionGroup(self)
			markupActions = []
			for markup in availableMarkups:
				markupAction = self.act(markup.name, trigbool=self.markupFunction(markup))
				if markup == self.defaultMarkup:
					markupAction.setChecked(True)
				self.chooseGroup.addAction(markupAction)
				markupActions.append(markupAction)
		self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold,
			trig=lambda: self.insertFormatting('bold'))
		self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic,
			trig=lambda: self.insertFormatting('italic'))
		self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline,
			trig=lambda: self.insertFormatting('underline'))
		self.usefulTags = ('header', 'italic', 'bold', 'underline', 'numbering',
			'bullets', 'image', 'link', 'inline code', 'code block', 'blockquote')
		self.usefulChars = ('deg', 'divide', 'dollar', 'hellip', 'laquo', 'larr',
			'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo',
			'rarr', 'rsquo', 'times')
		self.formattingBox = QComboBox(self.editBar)
		self.formattingBox.addItem(self.tr('Formatting'))
		self.formattingBox.addItems(self.usefulTags)
		self.formattingBox.activated[str].connect(self.insertFormatting)
		self.symbolBox = QComboBox(self.editBar)
		self.symbolBox.addItem(self.tr('Symbols'))
		self.symbolBox.addItems(self.usefulChars)
		self.symbolBox.activated.connect(self.insertSymbol)
		self.updateStyleSheet()
		menubar = self.menuBar()
		menuFile = menubar.addMenu(self.tr('File'))
		menuEdit = menubar.addMenu(self.tr('Edit'))
		menuHelp = menubar.addMenu(self.tr('Help'))
		menuFile.addAction(self.actionNew)
		menuFile.addAction(self.actionOpen)
		self.menuRecentFiles = menuFile.addMenu(self.tr('Open recent'))
		self.menuRecentFiles.aboutToShow.connect(self.updateRecentFiles)
		menuFile.addAction(self.actionShow)
		menuFile.addAction(self.actionSetEncoding)
		menuFile.addAction(self.actionReload)
		menuFile.addSeparator()
		menuFile.addAction(self.actionSave)
		menuFile.addAction(self.actionSaveAs)
		menuFile.addSeparator()
		menuFile.addAction(self.actionNextTab)
		menuFile.addAction(self.actionPrevTab)
		menuFile.addSeparator()
		menuExport = menuFile.addMenu(self.tr('Export'))
		menuExport.addAction(self.actionSaveHtml)
		menuExport.addAction(self.actionOdf)
		menuExport.addAction(self.actionPdf)
		if self.extensionActions:
			menuExport.addSeparator()
			for action, mimetype in self.extensionActions:
				menuExport.addAction(action)
			menuExport.aboutToShow.connect(self.updateExtensionsVisibility)
		menuFile.addAction(self.actionPrint)
		menuFile.addAction(self.actionPrintPreview)
		menuFile.addSeparator()
		menuFile.addAction(self.actionQuit)
		menuEdit.addAction(self.actionUndo)
		menuEdit.addAction(self.actionRedo)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionCut)
		menuEdit.addAction(self.actionCopy)
		menuEdit.addAction(self.actionPaste)
		menuEdit.addSeparator()
		if enchant_available:
			menuSC = menuEdit.addMenu(self.tr('Spell check'))
			menuSC.addAction(self.actionEnableSC)
			menuSC.addAction(self.actionSetLocale)
		menuEdit.addAction(self.actionSearch)
		menuEdit.addAction(self.actionChangeEditorFont)
		menuEdit.addAction(self.actionChangePreviewFont)
		menuEdit.addSeparator()
		if len(availableMarkups) > 1:
			self.menuMode = menuEdit.addMenu(self.tr('Default markup'))
			for markupAction in markupActions:
				self.menuMode.addAction(markupAction)
		menuFormat = menuEdit.addMenu(self.tr('Formatting'))
		menuFormat.addAction(self.actionBold)
		menuFormat.addAction(self.actionItalic)
		menuFormat.addAction(self.actionUnderline)
		menuEdit.addAction(self.actionWebKit)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionViewHtml)
		menuEdit.addAction(self.actionPreview)
		menuEdit.addAction(self.actionTableMode)
		if ReTextFakeVimHandler:
			menuEdit.addAction(self.actionFakeVimMode)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionFullScreen)
		menuEdit.addAction(self.actionConfig)
		menuHelp.addAction(self.actionHelp)
		menuHelp.addSeparator()
		menuHelp.addAction(self.actionAbout)
		menuHelp.addAction(self.actionAboutQt)
		toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
		toolBar.addAction(self.actionNew)
		toolBar.addSeparator()
		toolBar.addAction(self.actionOpen)
		toolBar.addAction(self.actionSave)
		toolBar.addAction(self.actionPrint)
		toolBar.addSeparator()
		toolBar.addAction(self.actionPreview)
		toolBar.addAction(self.actionFullScreen)
		self.editBar.addAction(self.actionUndo)
		self.editBar.addAction(self.actionRedo)
		self.editBar.addSeparator()
		self.editBar.addAction(self.actionCut)
		self.editBar.addAction(self.actionCopy)
		self.editBar.addAction(self.actionPaste)
		self.editBar.addSeparator()
		self.editBar.addWidget(self.formattingBox)
		self.editBar.addWidget(self.symbolBox)
		self.searchEdit = QLineEdit(self.searchBar)
		self.searchEdit.setPlaceholderText(self.tr('Search'))
		self.searchEdit.returnPressed.connect(self.find)
		self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar)
		self.searchBar.addWidget(self.searchEdit)
		self.searchBar.addSeparator()
		self.searchBar.addWidget(self.csBox)
		self.searchBar.addAction(self.actionFindPrev)
		self.searchBar.addAction(self.actionFind)
		self.searchBar.addAction(self.actionCloseSearch)
		self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
		self.searchBar.setVisible(False)
		self.autoSaveEnabled = globalSettings.autoSave
		if self.autoSaveEnabled:
			timer = QTimer(self)
			timer.start(60000)
			timer.timeout.connect(self.saveAll)
		self.ind = None
		if enchant_available:
			self.sl = globalSettings.spellCheckLocale
			if self.sl:
				try:
					enchant.Dict(self.sl)
				except Exception as e:
					print(e, file=sys.stderr)
					self.sl = None
			if globalSettings.spellCheck:
				self.actionEnableSC.setChecked(True)
		self.fileSystemWatcher = QFileSystemWatcher()
		self.fileSystemWatcher.fileChanged.connect(self.fileChanged)

	def iterateTabs(self):
		for i in range(self.tabWidget.count()):
			yield self.tabWidget.widget(i).tab

	def updateStyleSheet(self):
		if globalSettings.styleSheet:
			sheetfile = QFile(globalSettings.styleSheet)
			sheetfile.open(QIODevice.ReadOnly)
			self.ss = QTextStream(sheetfile).readAll()
			sheetfile.close()
		else:
			palette = QApplication.palette()
			self.ss = 'html { color: %s; }\n' % palette.color(QPalette.WindowText).name()
			self.ss += 'td, th { border: 1px solid #c3c3c3; padding: 0 3px 0 3px; }\n'
			self.ss += 'table { border-collapse: collapse; }\n'

	def initTabWidget(self):
		def dragEnterEvent(e):
			e.acceptProposedAction()
		def dropEvent(e):
			fn = bytes(e.mimeData().data('text/plain')).decode().rstrip()
			if fn.startswith('file:'):
				fn = QUrl(fn).toLocalFile()
			self.openFileWrapper(fn)
		self.tabWidget.setTabsClosable(True)
		self.tabWidget.setAcceptDrops(True)
		self.tabWidget.setMovable(True)
		self.tabWidget.dragEnterEvent = dragEnterEvent
		self.tabWidget.dropEvent = dropEvent

	def act(self, name, icon=None, trig=None, trigbool=None, shct=None):
		if not isinstance(shct, QKeySequence):
			shct = QKeySequence(shct)
		if icon:
			action = QAction(self.actIcon(icon), name, self)
		else:
			action = QAction(name, self)
		if trig:
			action.triggered.connect(trig)
		elif trigbool:
			action.setCheckable(True)
			action.triggered[bool].connect(trigbool)
		if shct:
			action.setShortcut(shct)
		return action

	def actIcon(self, name):
		return QIcon.fromTheme(name, QIcon(icon_path+name+'.png'))

	def printError(self):
		import traceback
		print('Exception occured while parsing document:', file=sys.stderr)
		traceback.print_exc()

	def createTab(self, fileName):
		self.currentTab = ReTextTab(self, fileName,
			previewState=int(globalSettings.livePreviewByDefault))
		self.tabWidget.addTab(self.currentTab.getSplitter(), self.tr("New document"))

	def closeTab(self, ind):
		if self.maybeSave(ind):
			if self.tabWidget.count() == 1:
				self.createTab("")
			currentWidget = self.tabWidget.widget(ind)
			if currentWidget.tab.fileName:
				self.fileSystemWatcher.removePath(currentWidget.tab.fileName)
			del currentWidget.tab
			self.tabWidget.removeTab(ind)

	def docTypeChanged(self):
		markupClass = self.currentTab.getMarkupClass()
		if type(self.currentTab.markup) != markupClass:
			self.currentTab.setMarkupClass(markupClass)
			self.currentTab.updatePreviewBox()
		dtMarkdown = (markupClass == markups.MarkdownMarkup)
		dtMkdOrReST = dtMarkdown or (markupClass == markups.ReStructuredTextMarkup)
		self.formattingBox.setEnabled(dtMarkdown)
		self.symbolBox.setEnabled(dtMarkdown)
		self.actionUnderline.setEnabled(dtMarkdown)
		self.actionBold.setEnabled(dtMkdOrReST)
		self.actionItalic.setEnabled(dtMkdOrReST)
		canReload = bool(self.currentTab.fileName) and not self.autoSaveActive()
		self.actionSetEncoding.setEnabled(canReload)
		self.actionReload.setEnabled(canReload)

	def changeIndex(self, ind):
		self.currentTab = self.tabWidget.currentWidget().tab
		editBox = self.currentTab.editBox
		previewState = self.currentTab.previewState
		self.actionUndo.setEnabled(editBox.document().isUndoAvailable())
		self.actionRedo.setEnabled(editBox.document().isRedoAvailable())
		self.actionCopy.setEnabled(editBox.textCursor().hasSelection())
		self.actionCut.setEnabled(editBox.textCursor().hasSelection())
		self.actionPreview.setChecked(previewState >= PreviewLive)
		self.actionLivePreview.setChecked(previewState == PreviewLive)
		self.actionTableMode.setChecked(editBox.tableModeEnabled)
		self.editBar.setEnabled(previewState < PreviewNormal)
		self.ind = ind
		if self.currentTab.fileName:
			self.setCurrentFile()
		else:
			self.setWindowTitle(self.tr('New document') + '[*]')
			self.docTypeChanged()
		self.modificationChanged(editBox.document().isModified())
		editBox.setFocus(Qt.OtherFocusReason)

	def changeEditorFont(self):
		font, ok = QFontDialog.getFont(globalSettings.editorFont, self)
		if ok:
			globalSettings.editorFont = font
			for tab in self.iterateTabs():
				tab.editBox.updateFont()

	def changePreviewFont(self):
		font, ok = QFontDialog.getFont(globalSettings.font, self)
		if ok:
			globalSettings.font = font
			for tab in self.iterateTabs():
				tab.updatePreviewBox()

	def preview(self, viewmode):
		self.currentTab.previewState = viewmode * 2
		self.actionLivePreview.setChecked(False)
		self.editBar.setDisabled(viewmode)
		self.currentTab.updateBoxesVisibility()
		if viewmode:
			self.currentTab.updatePreviewBox()

	def enableLivePreview(self, livemode):
		self.currentTab.previewState = int(livemode)
		self.actionPreview.setChecked(livemode)
		self.editBar.setEnabled(True)
		self.currentTab.updateBoxesVisibility()
		if livemode:
			self.currentTab.updatePreviewBox()

	def enableWebKit(self, enable):
		globalSettings.useWebKit = enable
		for i in range(self.tabWidget.count()):
			splitter = self.tabWidget.widget(i)
			tab = splitter.tab
			tab.previewBox.disconnectExternalSignals()
			tab.previewBox.setParent(None)
			tab.previewBox.deleteLater()
			tab.previewBox = tab.createPreviewBox(tab.editBox)
			tab.previewBox.setMinimumWidth(125)
			splitter.addWidget(tab.previewBox)
			splitter.setSizes((50, 50))
			tab.updatePreviewBox()
			tab.updateBoxesVisibility()

	def enableCopy(self, copymode):
		self.actionCopy.setEnabled(copymode)
		self.actionCut.setEnabled(copymode)

	def enableFullScreen(self, yes):
		if yes:
			self.showFullScreen()
		else:
			self.showNormal()

	def openConfigDialog(self):
		dlg = ConfigDialog(self)
		dlg.setWindowTitle(self.tr('Preferences'))
		dlg.show()

	def enableFakeVimMode(self, yes):
		globalSettings.useFakeVim = yes
		if yes:
			FakeVimMode.init(self)
			for tab in self.iterateTabs():
				tab.installFakeVimHandler()
		else:
			FakeVimMode.exit(self)

	def enableSpellCheck(self, yes):
		if yes:
			self.setAllDictionaries(enchant.Dict(self.sl or None))
		else:
			self.setAllDictionaries(None)
		globalSettings.spellCheck = yes

	def setAllDictionaries(self, dictionary):
		for tab in self.iterateTabs():
			hl = tab.highlighter
			hl.dictionary = dictionary
			hl.rehighlight()

	def changeLocale(self):
		if self.sl:
			localedlg = LocaleDialog(self, defaultText=self.sl)
		else:
			localedlg = LocaleDialog(self)
		if localedlg.exec() != QDialog.Accepted:
			return
		sl = localedlg.localeEdit.text()
		setdefault = localedlg.checkBox.isChecked()
		if sl:
			try:
				sl = str(sl)
				enchant.Dict(sl)
			except Exception as e:
				QMessageBox.warning(self, '', str(e))
			else:
				self.sl = sl
				self.enableSpellCheck(self.actionEnableSC.isChecked())
		else:
			self.sl = None
			self.enableSpellCheck(self.actionEnableSC.isChecked())
		if setdefault:
			globalSettings.spellCheckLocale = sl

	def searchBarVisibilityChanged(self, visible):
		self.actionSearch.setChecked(visible)
		if visible:
			self.searchEdit.setFocus(Qt.ShortcutFocusReason)

	def find(self, back=False):
		flags = QTextDocument.FindFlags()
		if back:
			flags |= QTextDocument.FindBackward
		if self.csBox.isChecked():
			flags |= QTextDocument.FindCaseSensitively
		text = self.searchEdit.text()
		editBox = self.currentTab.editBox
		cursor = editBox.textCursor()
		newCursor = editBox.document().find(text, cursor, flags)
		if not newCursor.isNull():
			editBox.setTextCursor(newCursor)
			return self.setSearchEditColor(True)
		cursor.movePosition(QTextCursor.End if back else QTextCursor.Start)
		newCursor = editBox.document().find(text, cursor, flags)
		if not newCursor.isNull():
			editBox.setTextCursor(newCursor)
			return self.setSearchEditColor(True)
		self.setSearchEditColor(False)

	def setSearchEditColor(self, found):
		palette = self.searchEdit.palette()
		palette.setColor(QPalette.Active, QPalette.Base,
		                 Qt.white if found else QColor(255, 102, 102))
		self.searchEdit.setPalette(palette)

	def showInDir(self):
		if self.currentTab.fileName:
			path = QFileInfo(self.currentTab.fileName).path()
			QDesktopServices.openUrl(QUrl.fromLocalFile(path))
		else:
			QMessageBox.warning(self, '', self.tr("Please, save the file somewhere."))

	def setCurrentFile(self):
		self.setWindowTitle("")
		self.tabWidget.setTabText(self.ind, self.currentTab.getDocumentTitle(baseName=True))
		self.tabWidget.setTabToolTip(self.ind, self.currentTab.fileName or '')
		self.setWindowFilePath(self.currentTab.fileName)
		files = readListFromSettings("recentFileList")
		while self.currentTab.fileName in files:
			files.remove(self.currentTab.fileName)
		files.insert(0, self.currentTab.fileName)
		if len(files) > 10:
			del files[10:]
		writeListToSettings("recentFileList", files)
		QDir.setCurrent(QFileInfo(self.currentTab.fileName).dir().path())
		self.docTypeChanged()

	def createNew(self, text=None):
		self.createTab("")
		self.ind = self.tabWidget.count()-1
		self.tabWidget.setCurrentIndex(self.ind)
		if text:
			self.currentTab.editBox.textCursor().insertText(text)

	def switchTab(self, shift=1):
		self.tabWidget.setCurrentIndex((self.ind + shift) % self.tabWidget.count())

	def updateRecentFiles(self):
		self.menuRecentFiles.clear()
		self.recentFilesActions = []
		filesOld = readListFromSettings("recentFileList")
		files = []
		for f in filesOld:
			if QFile.exists(f):
				files.append(f)
				self.recentFilesActions.append(self.act(f, trig=self.openFunction(f)))
		writeListToSettings("recentFileList", files)
		for action in self.recentFilesActions:
			self.menuRecentFiles.addAction(action)

	def markupFunction(self, markup):
		return lambda: self.setDefaultMarkup(markup)

	def openFunction(self, fileName):
		return lambda: self.openFileWrapper(fileName)

	def extensionFunction(self, data):
		return lambda: \
		self.runExtensionCommand(data['Exec'], data['FileFilter'], data['DefaultExtension'])

	def getExportExtensionsList(self):
		extensions = []
		for extsprefix in datadirs:
			extsdir = QDir(extsprefix+'/export-extensions/')
			if extsdir.exists():
				for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'],
				QDir.Files | QDir.Readable):
					extensions.append(self.readExtension(fileInfo.filePath()))
		locale = QLocale.system().name()
		self.extensionActions = []
		for extension in extensions:
			try:
				if ('Name[%s]' % locale) in extension:
					name = extension['Name[%s]' % locale]
				elif ('Name[%s]' % locale.split('_')[0]) in extension:
					name = extension['Name[%s]' % locale.split('_')[0]]
				else:
					name = extension['Name']
				data = {}
				for prop in ('FileFilter', 'DefaultExtension', 'Exec'):
					if 'X-ReText-'+prop in extension:
						data[prop] = extension['X-ReText-'+prop]
					elif prop in extension:
						data[prop] = extension[prop]
					else:
						data[prop] = ''
				action = self.act(name, trig=self.extensionFunction(data))
				if 'Icon' in extension:
					action.setIcon(self.actIcon(extension['Icon']))
				mimetype = extension['MimeType'] if 'MimeType' in extension else None
			except KeyError:
				print('Failed to parse extension: Name is required', file=sys.stderr)
			else:
				self.extensionActions.append((action, mimetype))

	def updateExtensionsVisibility(self):
		markupClass = self.currentTab.getMarkupClass()
		for action in self.extensionActions:
			if markupClass is None:
				action[0].setEnabled(False)
				continue
			mimetype = action[1]
			if mimetype == None:
				enabled = True
			elif markupClass == markups.MarkdownMarkup:
				enabled = (mimetype in ("text/x-retext-markdown", "text/x-markdown"))
			elif markupClass == markups.ReStructuredTextMarkup:
				enabled = (mimetype in ("text/x-retext-rst", "text/x-rst"))
			else:
				enabled = False
			action[0].setEnabled(enabled)

	def readExtension(self, fileName):
		extFile = QFile(fileName)
		extFile.open(QIODevice.ReadOnly)
		extension = {}
		stream = QTextStream(extFile)
		while not stream.atEnd():
			line = stream.readLine()
			if '=' in line:
				index = line.index('=')
				extension[line[:index].rstrip()] = line[index+1:].lstrip()
		extFile.close()
		return extension

	def openFile(self):
		supportedExtensions = ['.txt']
		for markup in markups.get_all_markups():
			supportedExtensions += markup.file_extensions
		fileFilter = ' (' + str.join(' ', ['*'+ext for ext in supportedExtensions]) + ');;'
		fileNames = QFileDialog.getOpenFileNames(self,
			self.tr("Select one or several files to open"), "",
			self.tr("Supported files") + fileFilter + self.tr("All files (*)"))
		for fileName in fileNames[0]:
			self.openFileWrapper(fileName)

	def openFileWrapper(self, fileName):
		if not fileName:
			return
		fileName = QFileInfo(fileName).canonicalFilePath()
		exists = False
		for i, tab in enumerate(self.iterateTabs()):
			if tab.fileName == fileName:
				exists = True
				ex = i
		if exists:
			self.tabWidget.setCurrentIndex(ex)
		elif QFile.exists(fileName):
			noEmptyTab = (
				(self.ind is None) or
				self.currentTab.fileName or
				self.currentTab.editBox.toPlainText() or
				self.currentTab.editBox.document().isModified()
			)
			if noEmptyTab:
				self.createTab(fileName)
				self.ind = self.tabWidget.count()-1
				self.tabWidget.setCurrentIndex(self.ind)
			if fileName:
				self.fileSystemWatcher.addPath(fileName)
			self.currentTab.fileName = fileName
			self.currentTab.readTextFromFile()
			editBox = self.currentTab.editBox
			self.setCurrentFile()
			self.setWindowModified(editBox.document().isModified())

	def showEncodingDialog(self):
		if not self.maybeSave(self.ind):
			return
		encoding, ok = QInputDialog.getItem(self, '',
			self.tr('Select file encoding from the list:'),
			[bytes(b).decode() for b in QTextCodec.availableCodecs()],
			0, False)
		if ok:
			self.currentTab.readTextFromFile(encoding)

	def saveFile(self):
		self.saveFileMain(dlg=False)

	def saveFileAs(self):
		self.saveFileMain(dlg=True)

	def saveAll(self):
		for tab in self.iterateTabs():
			if tab.fileName and QFileInfo(tab.fileName).isWritable():
				tab.saveTextToFile()
				tab.editBox.document().setModified(False)

	def saveFileMain(self, dlg):
		if (not self.currentTab.fileName) or dlg:
			markupClass = self.currentTab.getMarkupClass()
			if (markupClass is None) or not hasattr(markupClass, 'default_extension'):
				defaultExt = self.tr("Plain text (*.txt)")
				ext = ".txt"
			else:
				defaultExt = self.tr('%s files',
					'Example of final string: Markdown files') \
					% markupClass.name + ' (' + str.join(' ',
					('*'+extension for extension in markupClass.file_extensions)) + ')'
				if markupClass == markups.MarkdownMarkup:
					ext = globalSettings.markdownDefaultFileExtension
				elif markupClass == markups.ReStructuredTextMarkup:
					ext = globalSettings.restDefaultFileExtension
				else:
					ext = markupClass.default_extension
			newFileName = QFileDialog.getSaveFileName(self,
				self.tr("Save file"), "", defaultExt)[0]
			if newFileName:
				if not QFileInfo(newFileName).suffix():
					newFileName += ext
				if self.currentTab.fileName:
					self.fileSystemWatcher.removePath(self.currentTab.fileName)
				self.currentTab.fileName = newFileName
				self.actionSetEncoding.setDisabled(self.autoSaveActive())
		if self.currentTab.fileName:
			if self.currentTab.saveTextToFile():
				self.setCurrentFile()
				self.currentTab.editBox.document().setModified(False)
				self.setWindowModified(False)
				return True
			else:
				QMessageBox.warning(self, '',
				self.tr("Cannot save to file because it is read-only!"))
		return False

	def saveHtml(self, fileName):
		if not QFileInfo(fileName).suffix():
			fileName += ".html"
		try:
			htmltext = self.currentTab.getHtml(includeStyleSheet=False,
				webenv=True)
		except Exception:
			return self.printError()
		htmlFile = QFile(fileName)
		htmlFile.open(QIODevice.WriteOnly)
		html = QTextStream(htmlFile)
		if globalSettings.defaultCodec:
			html.setCodec(globalSettings.defaultCodec)
		html << htmltext
		htmlFile.close()

	def textDocument(self):
		td = QTextDocument()
		td.setMetaInformation(QTextDocument.DocumentTitle,
		                      self.currentTab.getDocumentTitle())
		if self.ss:
			td.setDefaultStyleSheet(self.ss)
		td.setHtml(self.currentTab.getHtml())
		td.setDefaultFont(globalSettings.font)
		return td

	def saveOdf(self):
		try:
			document = self.textDocument()
		except Exception:
			return self.printError()
		fileName = QFileDialog.getSaveFileName(self,
			self.tr("Export document to ODT"), "",
			self.tr("OpenDocument text files (*.odt)"))[0]
		if not QFileInfo(fileName).suffix():
			fileName += ".odt"
		writer = QTextDocumentWriter(fileName)
		writer.setFormat(b"odf")
		writer.write(document)

	def saveFileHtml(self):
		fileName = QFileDialog.getSaveFileName(self,
			self.tr("Save file"), "",
			self.tr("HTML files (*.html *.htm)"))[0]
		if fileName:
			self.saveHtml(fileName)

	def getDocumentForPrint(self):
		if globalSettings.useWebKit:
			return self.currentTab.previewBox
		try:
			return self.textDocument()
		except Exception:
			self.printError()

	def standardPrinter(self):
		printer = QPrinter(QPrinter.HighResolution)
		printer.setDocName(self.currentTab.getDocumentTitle())
		printer.setCreator('ReText %s' % app_version)
		return printer

	def savePdf(self):
		self.currentTab.updatePreviewBox()
		fileName = QFileDialog.getSaveFileName(self,
			self.tr("Export document to PDF"),
			"", self.tr("PDF files (*.pdf)"))[0]
		if fileName:
			if not QFileInfo(fileName).suffix():
				fileName += ".pdf"
			printer = self.standardPrinter()
			printer.setOutputFormat(QPrinter.PdfFormat)
			printer.setOutputFileName(fileName)
			document = self.getDocumentForPrint()
			if document != None:
				document.print(printer)

	def printFile(self):
		self.currentTab.updatePreviewBox()
		printer = self.standardPrinter()
		dlg = QPrintDialog(printer, self)
		dlg.setWindowTitle(self.tr("Print document"))
		if (dlg.exec() == QDialog.Accepted):
			document = self.getDocumentForPrint()
			if document != None:
				document.print(printer)

	def printPreview(self):
		document = self.getDocumentForPrint()
		if document == None:
			return
		printer = self.standardPrinter()
		preview = QPrintPreviewDialog(printer, self)
		preview.paintRequested.connect(document.print)
		preview.exec()

	def runExtensionCommand(self, command, filefilter, defaultext):
		of = ('%of' in command)
		html = ('%html' in command)
		if of:
			if defaultext and not filefilter:
				filefilter = '*'+defaultext
			fileName = QFileDialog.getSaveFileName(self,
				self.tr('Export document'), '', filefilter)[0]
			if not fileName:
				return
			if defaultext and not QFileInfo(fileName).suffix():
				fileName += defaultext
		basename = '.%s.retext-temp' % self.currentTab.getDocumentTitle(baseName=True)
		if html:
			tmpname = basename+'.html'
			self.saveHtml(tmpname)
		else:
			tmpname = basename + self.currentTab.getMarkupClass().default_extension
			self.currentTab.saveTextToFile(fileName=tmpname, addToWatcher=False)
		command = command.replace('%of', '"out'+defaultext+'"')
		command = command.replace('%html' if html else '%if', '"'+tmpname+'"')
		try:
			Popen(str(command), shell=True).wait()
		except Exception as error:
			errorstr = str(error)
			QMessageBox.warning(self, '', self.tr('Failed to execute the command:')
			+ '\n' + errorstr)
		QFile(tmpname).remove()
		if of:
			QFile('out'+defaultext).rename(fileName)

	def autoSaveActive(self, ind=None):
		tab = self.currentTab if ind is None else self.tabWidget.widget(ind).tab
		return (self.autoSaveEnabled and tab.fileName and
			QFileInfo(tab.fileName).isWritable())

	def modificationChanged(self, changed):
		if self.autoSaveActive():
			changed = False
		self.actionSave.setEnabled(changed)
		self.setWindowModified(changed)

	def clipboardDataChanged(self):
		mimeData = QApplication.instance().clipboard().mimeData()
		if mimeData is not None:
			self.actionPaste.setEnabled(mimeData.hasText())

	def insertFormatting(self, formatting):
		cursor = self.currentTab.editBox.textCursor()
		text = cursor.selectedText()
		moveCursorTo = None

		def c(cursor):
			nonlocal moveCursorTo
			moveCursorTo = cursor.position()

		def ensurenl(cursor):
			if not cursor.atBlockStart():
				cursor.insertText('\n\n')

		toinsert = {
			'header': (ensurenl, '# ', text),
			'italic': ('*', text, c, '*'),
			'bold': ('**', text, c, '**'),
			'underline': ('<u>', text, c, '</u>'),
			'numbering': (ensurenl, ' 1. ', text),
			'bullets': (ensurenl, '  * ', text),
			'image': ('![', text or self.tr('Alt text'), c, '](', self.tr('URL'), ')'),
			'link': ('[', text or self.tr('Link text'), c, '](', self.tr('URL'), ')'),
			'inline code': ('`', text, c, '`'),
			'code block': (ensurenl, '    ', text),
			'blockquote': (ensurenl, '> ', text),
		}

		if formatting not in toinsert:
			return

		cursor.beginEditBlock()
		for token in toinsert[formatting]:
			if callable(token):
				token(cursor)
			else:
				cursor.insertText(token)
		cursor.endEditBlock()

		self.formattingBox.setCurrentIndex(0)
		# Bring back the focus on the editor
		self.currentTab.editBox.setFocus(Qt.OtherFocusReason)

		if moveCursorTo:
			cursor.setPosition(moveCursorTo)
			self.currentTab.editBox.setTextCursor(cursor)

	def insertSymbol(self, num):
		if num:
			self.currentTab.editBox.insertPlainText('&'+self.usefulChars[num-1]+';')
		self.symbolBox.setCurrentIndex(0)

	def fileChanged(self, fileName):
		ind = None
		for testind, tab in enumerate(self.iterateTabs()):
			if tab.fileName == fileName:
				ind = testind
		if ind is None:
			self.fileSystemWatcher.removePath(fileName)
		self.tabWidget.setCurrentIndex(ind)
		if not QFile.exists(fileName):
			self.currentTab.editBox.document().setModified(True)
			QMessageBox.warning(self, '', self.tr(
				'This file has been deleted by other application.\n'
				'Please make sure you save the file before exit.'))
		elif not self.currentTab.editBox.document().isModified():
			# File was not modified in ReText, reload silently
			self.currentTab.readTextFromFile()
			self.currentTab.updatePreviewBox()
		else:
			text = self.tr(
				'This document has been modified by other application.\n'
				'Do you want to reload the file (this will discard all '
				'your changes)?\n')
			if self.autoSaveEnabled:
				text += self.tr(
					'If you choose to not reload the file, auto save mode will '
					'be disabled for this session to prevent data loss.')
			messageBox = QMessageBox(QMessageBox.Warning, '', text)
			reloadButton = messageBox.addButton(self.tr('Reload'), QMessageBox.YesRole)
			messageBox.addButton(QMessageBox.Cancel)
			messageBox.exec()
			if messageBox.clickedButton() is reloadButton:
				self.currentTab.readTextFromFile()
				self.currentTab.updatePreviewBox()
			else:
				self.autoSaveEnabled = False
				self.currentTab.editBox.document().setModified(True)
		if fileName not in self.fileSystemWatcher.files():
			# https://github.com/retext-project/retext/issues/137
			self.fileSystemWatcher.addPath(fileName)

	def maybeSave(self, ind):
		tab = self.tabWidget.widget(ind).tab
		if self.autoSaveActive(ind):
			tab.saveTextToFile()
			return True
		if not tab.editBox.document().isModified():
			return True
		self.tabWidget.setCurrentIndex(ind)
		ret = QMessageBox.warning(self, '',
			self.tr("The document has been modified.\nDo you want to save your changes?"),
			QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
		if ret == QMessageBox.Save:
			return self.saveFileMain(False)
		elif ret == QMessageBox.Cancel:
			return False
		return True

	def closeEvent(self, closeevent):
		for ind in range(self.tabWidget.count()):
			if not self.maybeSave(ind):
				return closeevent.ignore()
		if globalSettings.saveWindowGeometry and not self.isMaximized():
			globalSettings.windowGeometry = self.saveGeometry()
		closeevent.accept()

	def viewHtml(self):
		htmlDlg = HtmlDialog(self)
		try:
			htmltext = self.currentTab.getHtml(includeStyleSheet=False)
		except Exception:
			return self.printError()
		winTitle = self.currentTab.getDocumentTitle(baseName=True)
		htmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+")")
		htmlDlg.textEdit.setPlainText(htmltext.rstrip())
		htmlDlg.hl.rehighlight()
		htmlDlg.show()
		htmlDlg.raise_()
		htmlDlg.activateWindow()

	def openHelp(self):
		QDesktopServices.openUrl(QUrl('https://github.com/retext-project/retext/wiki'))

	def aboutDialog(self):
		QMessageBox.about(self, self.aboutWindowTitle,
		'<p><b>' + (self.tr('ReText %s (using PyMarkups %s)') % (app_version, markups.__version__))
		+'</b></p>' + self.tr('Simple but powerful editor'
		' for Markdown and reStructuredText')
		+'</p><p>'+self.tr('Author: Dmitry Shachnev, 2011').replace('2011', '2011–2016')
		+'<br><a href="https://github.com/retext-project/retext">'+self.tr('Website')
		+'</a> | <a href="http://daringfireball.net/projects/markdown/syntax">'
		+self.tr('Markdown syntax')
		+'</a> | <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">'
		+self.tr('reStructuredText syntax')+'</a></p>')

	def setDefaultMarkup(self, markupClass):
		self.defaultMarkup = markupClass
		defaultName = markups.get_available_markups()[0].name
		writeToSettings('defaultMarkup', markupClass.name, defaultName)
		for tab in self.iterateTabs():
			if not tab.fileName:
				tab.setMarkupClass(markupClass)
				tab.updatePreviewBox()
		self.docTypeChanged()
Esempio n. 42
0
	def __init__(self, parent=None):
		QMainWindow.__init__(self, parent)
		self.resize(950, 700)
		screenRect = QDesktopWidget().screenGeometry()
		if globalSettings.windowGeometry:
			self.restoreGeometry(globalSettings.windowGeometry)
		else:
			self.move((screenRect.width()-self.width())/2, (screenRect.height()-self.height())/2)
		if not screenRect.contains(self.geometry()):
			self.showMaximized()
		if globalSettings.iconTheme:
			QIcon.setThemeName(globalSettings.iconTheme)
		if QIcon.themeName() in ('hicolor', ''):
			if not QFile.exists(icon_path + 'document-new.png'):
				QIcon.setThemeName(get_icon_theme())
		if QFile.exists(icon_path+'retext.png'):
			self.setWindowIcon(QIcon(icon_path+'retext.png'))
		elif QFile.exists('/usr/share/pixmaps/retext.png'):
			self.setWindowIcon(QIcon('/usr/share/pixmaps/retext.png'))
		else:
			self.setWindowIcon(QIcon.fromTheme('retext',
				QIcon.fromTheme('accessories-text-editor')))
		self.tabWidget = QTabWidget(self)
		self.initTabWidget()
		self.setCentralWidget(self.tabWidget)
		self.tabWidget.currentChanged.connect(self.changeIndex)
		self.tabWidget.tabCloseRequested.connect(self.closeTab)
		toolBar = QToolBar(self.tr('File toolbar'), self)
		self.addToolBar(Qt.TopToolBarArea, toolBar)
		self.editBar = QToolBar(self.tr('Edit toolbar'), self)
		self.addToolBar(Qt.TopToolBarArea, self.editBar)
		self.searchBar = QToolBar(self.tr('Search toolbar'), self)
		self.addToolBar(Qt.BottomToolBarArea, self.searchBar)
		toolBar.setVisible(not globalSettings.hideToolBar)
		self.editBar.setVisible(not globalSettings.hideToolBar)
		self.actionNew = self.act(self.tr('New'), 'document-new',
			self.createNew, shct=QKeySequence.New)
		self.actionNew.setPriority(QAction.LowPriority)
		self.actionOpen = self.act(self.tr('Open'), 'document-open',
			self.openFile, shct=QKeySequence.Open)
		self.actionOpen.setPriority(QAction.LowPriority)
		self.actionSetEncoding = self.act(self.tr('Set encoding'),
			trig=self.showEncodingDialog)
		self.actionSetEncoding.setEnabled(False)
		self.actionReload = self.act(self.tr('Reload'), 'view-refresh',
			lambda: self.currentTab.readTextFromFile())
		self.actionReload.setEnabled(False)
		self.actionSave = self.act(self.tr('Save'), 'document-save',
			self.saveFile, shct=QKeySequence.Save)
		self.actionSave.setEnabled(False)
		self.actionSave.setPriority(QAction.LowPriority)
		self.actionSaveAs = self.act(self.tr('Save as'), 'document-save-as',
			self.saveFileAs, shct=QKeySequence.SaveAs)
		self.actionNextTab = self.act(self.tr('Next tab'), 'go-next',
			lambda: self.switchTab(1), shct=Qt.CTRL+Qt.Key_PageDown)
		self.actionPrevTab = self.act(self.tr('Previous tab'), 'go-previous',
			lambda: self.switchTab(-1), shct=Qt.CTRL+Qt.Key_PageUp)
		self.actionPrint = self.act(self.tr('Print'), 'document-print',
			self.printFile, shct=QKeySequence.Print)
		self.actionPrint.setPriority(QAction.LowPriority)
		self.actionPrintPreview = self.act(self.tr('Print preview'), 'document-print-preview',
			self.printPreview)
		self.actionViewHtml = self.act(self.tr('View HTML code'), 'text-html', self.viewHtml)
		self.actionChangeEditorFont = self.act(self.tr('Change editor font'),
			trig=self.changeEditorFont)
		self.actionChangePreviewFont = self.act(self.tr('Change preview font'),
			trig=self.changePreviewFont)
		self.actionSearch = self.act(self.tr('Find text'), 'edit-find', shct=QKeySequence.Find)
		self.actionSearch.setCheckable(True)
		self.actionSearch.triggered[bool].connect(self.searchBar.setVisible)
		self.searchBar.visibilityChanged.connect(self.searchBarVisibilityChanged)
		self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E,
			trigbool=self.preview)
		if QIcon.hasThemeIcon('document-preview'):
			self.actionPreview.setIcon(QIcon.fromTheme('document-preview'))
		elif QIcon.hasThemeIcon('preview-file'):
			self.actionPreview.setIcon(QIcon.fromTheme('preview-file'))
		elif QIcon.hasThemeIcon('x-office-document'):
			self.actionPreview.setIcon(QIcon.fromTheme('x-office-document'))
		else:
			self.actionPreview.setIcon(QIcon(icon_path+'document-preview.png'))
		self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.Key_L,
		trigbool=self.enableLivePreview)
		menuPreview = QMenu()
		menuPreview.addAction(self.actionLivePreview)
		self.actionPreview.setMenu(menuPreview)
		self.actionTableMode = self.act(self.tr('Table mode'), shct=Qt.CTRL+Qt.Key_T,
			trigbool=lambda x: self.currentTab.editBox.enableTableMode(x))
		if ReTextFakeVimHandler:
			self.actionFakeVimMode = self.act(self.tr('FakeVim mode'),
				shct=Qt.CTRL+Qt.ALT+Qt.Key_V, trigbool=self.enableFakeVimMode)
			if globalSettings.useFakeVim:
				self.actionFakeVimMode.setChecked(True)
				self.enableFakeVimMode(True)
		self.actionFullScreen = self.act(self.tr('Fullscreen mode'), 'view-fullscreen',
			shct=Qt.Key_F11, trigbool=self.enableFullScreen)
		self.actionFullScreen.setPriority(QAction.LowPriority)
		self.actionConfig = self.act(self.tr('Preferences'), icon='preferences-system',
			trig=self.openConfigDialog)
		self.actionConfig.setMenuRole(QAction.PreferencesRole)
		self.actionSaveHtml = self.act('HTML', 'text-html', self.saveFileHtml)
		self.actionPdf = self.act('PDF', 'application-pdf', self.savePdf)
		self.actionOdf = self.act('ODT', 'x-office-document', self.saveOdf)
		self.getExportExtensionsList()
		self.actionQuit = self.act(self.tr('Quit'), 'application-exit', shct=QKeySequence.Quit)
		self.actionQuit.setMenuRole(QAction.QuitRole)
		self.actionQuit.triggered.connect(self.close)
		self.actionUndo = self.act(self.tr('Undo'), 'edit-undo',
			lambda: self.currentTab.editBox.undo(), shct=QKeySequence.Undo)
		self.actionRedo = self.act(self.tr('Redo'), 'edit-redo',
			lambda: self.currentTab.editBox.redo(), shct=QKeySequence.Redo)
		self.actionCopy = self.act(self.tr('Copy'), 'edit-copy',
			lambda: self.currentTab.editBox.copy(), shct=QKeySequence.Copy)
		self.actionCut = self.act(self.tr('Cut'), 'edit-cut',
			lambda: self.currentTab.editBox.cut(), shct=QKeySequence.Cut)
		self.actionPaste = self.act(self.tr('Paste'), 'edit-paste',
			lambda: self.currentTab.editBox.paste(), shct=QKeySequence.Paste)
		self.actionUndo.setEnabled(False)
		self.actionRedo.setEnabled(False)
		self.actionCopy.setEnabled(False)
		self.actionCut.setEnabled(False)
		qApp = QApplication.instance()
		qApp.clipboard().dataChanged.connect(self.clipboardDataChanged)
		self.clipboardDataChanged()
		if enchant_available:
			self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSpellCheck)
			self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale)
		self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit)
		self.actionWebKit.setChecked(globalSettings.useWebKit)
		self.actionShow = self.act(self.tr('Show directory'), 'system-file-manager', self.showInDir)
		self.actionFind = self.act(self.tr('Next'), 'go-next', self.find,
			shct=QKeySequence.FindNext)
		self.actionFindPrev = self.act(self.tr('Previous'), 'go-previous',
			lambda: self.find(back=True), shct=QKeySequence.FindPrevious)
		self.actionCloseSearch = self.act(self.tr('Close'), 'window-close',
			lambda: self.searchBar.setVisible(False))
		self.actionCloseSearch.setPriority(QAction.LowPriority)
		self.actionHelp = self.act(self.tr('Get help online'), 'help-contents', self.openHelp)
		self.aboutWindowTitle = self.tr('About ReText')
		self.actionAbout = self.act(self.aboutWindowTitle, 'help-about', self.aboutDialog)
		self.actionAbout.setMenuRole(QAction.AboutRole)
		self.actionAboutQt = self.act(self.tr('About Qt'))
		self.actionAboutQt.setMenuRole(QAction.AboutQtRole)
		self.actionAboutQt.triggered.connect(qApp.aboutQt)
		availableMarkups = markups.get_available_markups()
		if not availableMarkups:
			print('Warning: no markups are available!')
		self.defaultMarkup = availableMarkups[0] if availableMarkups else None
		if globalSettings.defaultMarkup:
			mc = markups.find_markup_class_by_name(globalSettings.defaultMarkup)
			if mc and mc.available():
				self.defaultMarkup = mc
		if len(availableMarkups) > 1:
			self.chooseGroup = QActionGroup(self)
			markupActions = []
			for markup in availableMarkups:
				markupAction = self.act(markup.name, trigbool=self.markupFunction(markup))
				if markup == self.defaultMarkup:
					markupAction.setChecked(True)
				self.chooseGroup.addAction(markupAction)
				markupActions.append(markupAction)
		self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold,
			trig=lambda: self.insertFormatting('bold'))
		self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic,
			trig=lambda: self.insertFormatting('italic'))
		self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline,
			trig=lambda: self.insertFormatting('underline'))
		self.usefulTags = ('header', 'italic', 'bold', 'underline', 'numbering',
			'bullets', 'image', 'link', 'inline code', 'code block', 'blockquote')
		self.usefulChars = ('deg', 'divide', 'dollar', 'hellip', 'laquo', 'larr',
			'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo',
			'rarr', 'rsquo', 'times')
		self.formattingBox = QComboBox(self.editBar)
		self.formattingBox.addItem(self.tr('Formatting'))
		self.formattingBox.addItems(self.usefulTags)
		self.formattingBox.activated[str].connect(self.insertFormatting)
		self.symbolBox = QComboBox(self.editBar)
		self.symbolBox.addItem(self.tr('Symbols'))
		self.symbolBox.addItems(self.usefulChars)
		self.symbolBox.activated.connect(self.insertSymbol)
		self.updateStyleSheet()
		menubar = self.menuBar()
		menuFile = menubar.addMenu(self.tr('File'))
		menuEdit = menubar.addMenu(self.tr('Edit'))
		menuHelp = menubar.addMenu(self.tr('Help'))
		menuFile.addAction(self.actionNew)
		menuFile.addAction(self.actionOpen)
		self.menuRecentFiles = menuFile.addMenu(self.tr('Open recent'))
		self.menuRecentFiles.aboutToShow.connect(self.updateRecentFiles)
		menuFile.addAction(self.actionShow)
		menuFile.addAction(self.actionSetEncoding)
		menuFile.addAction(self.actionReload)
		menuFile.addSeparator()
		menuFile.addAction(self.actionSave)
		menuFile.addAction(self.actionSaveAs)
		menuFile.addSeparator()
		menuFile.addAction(self.actionNextTab)
		menuFile.addAction(self.actionPrevTab)
		menuFile.addSeparator()
		menuExport = menuFile.addMenu(self.tr('Export'))
		menuExport.addAction(self.actionSaveHtml)
		menuExport.addAction(self.actionOdf)
		menuExport.addAction(self.actionPdf)
		if self.extensionActions:
			menuExport.addSeparator()
			for action, mimetype in self.extensionActions:
				menuExport.addAction(action)
			menuExport.aboutToShow.connect(self.updateExtensionsVisibility)
		menuFile.addAction(self.actionPrint)
		menuFile.addAction(self.actionPrintPreview)
		menuFile.addSeparator()
		menuFile.addAction(self.actionQuit)
		menuEdit.addAction(self.actionUndo)
		menuEdit.addAction(self.actionRedo)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionCut)
		menuEdit.addAction(self.actionCopy)
		menuEdit.addAction(self.actionPaste)
		menuEdit.addSeparator()
		if enchant_available:
			menuSC = menuEdit.addMenu(self.tr('Spell check'))
			menuSC.addAction(self.actionEnableSC)
			menuSC.addAction(self.actionSetLocale)
		menuEdit.addAction(self.actionSearch)
		menuEdit.addAction(self.actionChangeEditorFont)
		menuEdit.addAction(self.actionChangePreviewFont)
		menuEdit.addSeparator()
		if len(availableMarkups) > 1:
			self.menuMode = menuEdit.addMenu(self.tr('Default markup'))
			for markupAction in markupActions:
				self.menuMode.addAction(markupAction)
		menuFormat = menuEdit.addMenu(self.tr('Formatting'))
		menuFormat.addAction(self.actionBold)
		menuFormat.addAction(self.actionItalic)
		menuFormat.addAction(self.actionUnderline)
		menuEdit.addAction(self.actionWebKit)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionViewHtml)
		menuEdit.addAction(self.actionPreview)
		menuEdit.addAction(self.actionTableMode)
		if ReTextFakeVimHandler:
			menuEdit.addAction(self.actionFakeVimMode)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionFullScreen)
		menuEdit.addAction(self.actionConfig)
		menuHelp.addAction(self.actionHelp)
		menuHelp.addSeparator()
		menuHelp.addAction(self.actionAbout)
		menuHelp.addAction(self.actionAboutQt)
		toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
		toolBar.addAction(self.actionNew)
		toolBar.addSeparator()
		toolBar.addAction(self.actionOpen)
		toolBar.addAction(self.actionSave)
		toolBar.addAction(self.actionPrint)
		toolBar.addSeparator()
		toolBar.addAction(self.actionPreview)
		toolBar.addAction(self.actionFullScreen)
		self.editBar.addAction(self.actionUndo)
		self.editBar.addAction(self.actionRedo)
		self.editBar.addSeparator()
		self.editBar.addAction(self.actionCut)
		self.editBar.addAction(self.actionCopy)
		self.editBar.addAction(self.actionPaste)
		self.editBar.addSeparator()
		self.editBar.addWidget(self.formattingBox)
		self.editBar.addWidget(self.symbolBox)
		self.searchEdit = QLineEdit(self.searchBar)
		self.searchEdit.setPlaceholderText(self.tr('Search'))
		self.searchEdit.returnPressed.connect(self.find)
		self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar)
		self.searchBar.addWidget(self.searchEdit)
		self.searchBar.addSeparator()
		self.searchBar.addWidget(self.csBox)
		self.searchBar.addAction(self.actionFindPrev)
		self.searchBar.addAction(self.actionFind)
		self.searchBar.addAction(self.actionCloseSearch)
		self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
		self.searchBar.setVisible(False)
		self.autoSaveEnabled = globalSettings.autoSave
		if self.autoSaveEnabled:
			timer = QTimer(self)
			timer.start(60000)
			timer.timeout.connect(self.saveAll)
		self.ind = None
		if enchant_available:
			self.sl = globalSettings.spellCheckLocale
			if self.sl:
				try:
					enchant.Dict(self.sl)
				except Exception as e:
					print(e, file=sys.stderr)
					self.sl = None
			if globalSettings.spellCheck:
				self.actionEnableSC.setChecked(True)
		self.fileSystemWatcher = QFileSystemWatcher()
		self.fileSystemWatcher.fileChanged.connect(self.fileChanged)
Esempio n. 43
0
class PlotsMath(QWidget):
    
    F_X = Qt.UserRole + 1
    FITS_SPECTRUM = Qt.UserRole + 2
    
    def __init__(self, settings, database, project=None):
        super(PlotsMath, self).__init__()
        self.ui = Ui_PlotsMath()
        self.ui.setupUi(self)
        self.settings = settings
        self.project=project
        self.plot = QtCommons.nestWidget(self.ui.plot, QMathPlotWidget())
        self.reference_dialog = ReferenceSpectraDialog(database)
        self.reference_dialog.fits_picked.connect(self.open_fits)
        self.toolbar = QToolBar('Instrument Response Toolbar')
        open_btn = QtCommons.addToolbarPopup(self.toolbar, text="Open...", icon_file=':/new_open_20')
        open_file_action = open_btn.menu().addAction('FITS file')
        open_btn.menu().addAction('Reference library', self.reference_dialog.show)
        self.blackbody_menu = blackbody.BlackBodyAction(self.blackbody, open_btn.menu())
        
        if project:
            save_result = QtCommons.addToolbarPopup(self.toolbar, text='Save', icon_file=':/save_20')
            save_result.menu().addAction('As File', lambda: QtCommons.save_file('Save Operation Result...', FITS_EXTS, lambda f: self.save(f[0]), project.path))
            save_result.menu().addAction('As Instrument Response', self.save_project_instrument_response)
            open_file_action.triggered.connect(lambda: QtCommons.open_file('Open FITS Spectrum',FITS_EXTS, lambda f: self.open_fits(f[0]), project.path))
        else:
            open_file_action.triggered.connect(lambda: open_file_sticky('Open FITS Spectrum',FITS_EXTS, lambda f: self.open_fits(f[0]), self.settings, CALIBRATED_PROFILE, [RAW_PROFILE]))
            self.toolbar.addAction(QIcon(':/save_20'), 'Save', lambda: save_file_sticky('Save Operation Result...', 'FITS file (.fit)', lambda f: self.save(f[0]), self.settings, MATH_OPERATION, [CALIBRATED_PROFILE]))
            
        self.toolbar.addAction('Set operand', self.set_operand)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.ui.actionZoom)
        self.ui.actionZoom.triggered.connect(self.start_zoom)
        self.toolbar.addAction(self.ui.actionReset_Zoom)
        self.ui.actionReset_Zoom.triggered.connect(self.reset_zoom)
        self.toolbar.addSeparator()
        self.operands_model = QStandardItemModel()
        self.ui.operands_listview.setModel(self.operands_model)
        remove_btn = QtCommons.addToolbarPopup(self.toolbar, text='Remove...')
        remove_btn.menu().addAction(self.ui.actionSelectPointsToRemove)
        remove_btn.menu().addAction("Before point", lambda: spectrum_trim_dialog(self.spectrum, 'before', self.plot.axes, lambda: self.draw(), self, before_removal=self.undo.save_undo))
        remove_btn.menu().addAction("After point", lambda: spectrum_trim_dialog(self.spectrum, 'after', self.plot.axes, lambda: self.draw(), self, before_removal=self.undo.save_undo))
        self.ui.clear_operands.clicked.connect(self.operands_model.clear)
        self.ui.remove_operand.clicked.connect(lambda: self.operands_model.removeRows(self.ui.operands_listview.selectionModel().selectedRows()[0].row(), 1))
            
        self.operands_model.rowsInserted.connect(lambda: self.ui.clear_operands.setEnabled(self.operands_model.rowCount() > 0) )
        self.operands_model.rowsRemoved.connect(lambda: self.ui.clear_operands.setEnabled(self.operands_model.rowCount() > 0) )
        self.ui.operands_listview.selectionModel().selectionChanged.connect(lambda s, u: self.ui.remove_operand.setEnabled(len(s)))
        self.ui.actionSelectPointsToRemove.triggered.connect(self.pick_rm_points)
        self.undo = Undo(None, self.draw)
        self.undo.add_actions(self.toolbar)
        self.ui.spline_factor.valueChanged.connect(self.factor_valueChanged)
        self.ui.spline_degrees.valueChanged.connect(lambda v: self.draw())
        self.ui.spline_factor_auto.toggled.connect(lambda v: self.draw())
        self.ui.spline_factor_auto.toggled.connect(lambda v: self.ui.spline_factor.setEnabled(not v))
        self.ui.execute.clicked.connect(self.execute_operation)
        self.plot.figure.tight_layout()
        
    def blackbody(self, blackbody):
        self.spectrum = blackbody.spectrum()
        self.spectrum_name = "Blackbody radiation for {0}".format(blackbody.kelvin)
        self.undo.set_spectrum(self.spectrum)
        self.spectrum.normalize_to_max()
        self.draw()
        

    def open_fits(self, filename):
        fits_file = fits.open(filename)
        fits_spectrum = FitsSpectrum(fits_file)
        self.spectrum_name = fits_spectrum.name()
        self.spectrum = fits_spectrum.spectrum
        self.undo.set_spectrum(self.spectrum)
        self.spectrum.normalize_to_max()
        if self.spectrum.dispersion() <0.4:
            print("dispersion too high ({}), reducing spectrum resolution".format(self.spectrum.dispersion()))
            self.spectrum.resample(self.spectrum.dispersion() / 0.4)
        self.draw()

    @pyqtSlot(float)
    def factor_valueChanged(self, f):
        self.draw()
        
    def pick_rm_points(self):
        self.plot.rm_element('zoom')
        self.plot.add_span_selector('pick_rm_points', lambda min,max: self.rm_points(min,max+1),direction='horizontal')
        
        
    def start_zoom(self):
        self.plot.rm_element('pick_rm_points')
        self.plot.select_zoom()
        
    def draw(self):
        self.ui.spline_degrees_value.setText("{}".format(self.ui.spline_degrees.value()))
        spline_factor = self.ui.spline_factor.value() if not self.ui.spline_factor_auto.isChecked() else None
        spline = UnivariateSpline(self.spectrum.wavelengths, self.spectrum.fluxes, k=self.ui.spline_degrees.value(), s=spline_factor)
        self.f_x = lambda x: spline(x)
        self.plot.plot(None, self.spectrum.wavelengths, self.spectrum.fluxes, '--', self.spectrum.wavelengths, spline(self.spectrum.wavelengths), '-')
        self.plot.figure.tight_layout()
        self.plot.figure.canvas.draw()
        
    def rm_points(self, wmin, wmax):
        self.undo.save_undo()
        x_min = self.spectrum.wavelength_index(max(self.spectrum.wavelengths[0], wmin))
        x_max = self.spectrum.wavelength_index(min(self.spectrum.wavelengths[-1], wmax))
        m=(self.spectrum.fluxes[x_max]-self.spectrum.fluxes[x_min])/(x_max-x_min)
        q = self.spectrum.fluxes[x_min]
        
        f = lambda x: x * m + q
        self.spectrum.fluxes[x_min:x_max] = np.fromfunction(f, self.spectrum.fluxes[x_min:x_max].shape)
        self.draw()
        
    def trim(self, direction):
        point = QInputDialog.getInt(None, 'Trim curve', 'Enter wavelength for trimming', self.spectrum.wavelengths[0] if direction == 'before' else self.spectrum.wavelengths[-1], self.spectrum.wavelengths[0], self.spectrum.wavelengths[-1])
        if not point[1]:
            return
        self.undo.save_undo()
        if direction == 'before':
            self.spectrum.cut(start=self.spectrum.wavelength_index(point[0]))
        else:
            self.spectrum.cut(end=self.spectrum.wavelength_index(point[0]))
        self.reset_zoom()
        self.draw()
    
    def set_operand(self):
        item = QStandardItem(self.spectrum_name)
        item.setData(self.f_x, PlotsMath.F_X)
        item.setData(self.spectrum, PlotsMath.FITS_SPECTRUM)
        self.operands_model.appendRow(item)
        
    def execute_operation(self):
        max_wavelengths = lambda operands: np.arange(max([o[0].wavelengths[0] for o in operands]), min([o[0].wavelengths[-1] for o in operands]))
        datasets = lambda operands, wavelengths: [PlotsMath.__data(o[1], wavelengths) for o in operands]
        operands = [(self.operands_model.item(a).data(PlotsMath.FITS_SPECTRUM), self.operands_model.item(a).data(PlotsMath.F_X)) for a in np.arange(self.operands_model.rowCount())]
        
        
        def divide(operands):
            if len(operands) > 2:
                print("Division supports only 2 operands, truncating")
            wavelengths = max_wavelengths(operands[0:2])
            datas = datasets(operands[0:2], wavelengths)
            return (wavelengths, datas[0]/datas[1])
        
        def mean(operands):
            wavelengths = max_wavelengths(operands)
            mean_data = np.zeros(wavelengths.shape)
            for data in datasets(operands, wavelengths):
                mean_data += data
            return (wavelengths, mean_data/len(wavelengths))
        
        operations = { 0: divide, 1: mean }
        try:
            wavelengths, data = operations[self.ui.operation_type.currentIndex()](operands)
            self.spectrum = Spectrum(data, wavelengths)
            
            self.spectrum.normalize_to_max()
            self.undo.set_spectrum(self.spectrum)
            self.ui.spline_degrees.setValue(5)
            self.ui.spline_factor.setValue(0)
            self.ui.spline_factor_auto.setChecked(False)
            self.draw()
        except IndexError:
            QMessageBox.warning(None, "Error", "Datasets are not compatible. Maybe you need to calibrate better, or use a different reference file")

    def save_project_instrument_response(self):
        name = QInputDialog.getText(self, 'Enter Name', 'Enter new instrument response name for saving')
        if name[1]:
            self.project.add_file(Project.INSTRUMENT_RESPONSES, lambda f: self.save(f), bare_name=name[0])

    def save(self, filename):
        hdu = fits.PrimaryHDU( PlotsMath.__data(self.f_x, self.spectrum.wavelengths))
        #hdu = fits.PrimaryHDU( self.spectrum.fluxes)
        fits_file = fits.HDUList([hdu])
        hdu.header['CRPIX1'] = 1
        hdu.header['CRVAL1'] = self.spectrum.wavelengths[0]
        hdu.header['CDELT1'] = self.spectrum.dispersion()
        hdu.writeto(filename, clobber=True)

    def reset_zoom(self):
        self.plot.reset_zoom(self.spectrum.wavelengths, self.spectrum.fluxes.min(), self.spectrum.fluxes.max())

    def __data(f_x, xrange):
        return np.fromfunction(lambda x: f_x(x+xrange[0]), xrange.shape)
Esempio n. 44
0
class MainWindow(QMainWindow, ui_window.Ui_Window):
    emulator_found = QtCore.pyqtSignal(dict)
    emulators_loaded = QtCore.pyqtSignal()

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)

        self.emulators = { }

        self.settings = QSettings('SanderTheDragon', 'Qtendo')

        self.ui_create()
        self.ui_connect()
        self.settings_load()



    def showEvent(self, ev):
        QMainWindow.showEvent(self, ev)

        self.statusBar.showMsg('Searching for emulators', 1000)
        Thread(target=self.find_emulators, daemon=True).start()


    def closeEvent(self, ev):
        QMainWindow.closeEvent(self, ev)

        self.settings_save()



    def ui_create(self):
        #Add toolbar
        self.toolBar = QToolBar()
        self.toolBar.addAction(self.actionPageEmulation)
        self.toolBar.setFloatable(False)
        self.toolBar.setMovable(False)
        self.toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.gridLayout.addWidget(self.toolBar, 1, 0)

        #Add a second toolbar on emulation page
        self.toolBarEmulation = QToolBar()
        self.toolBarEmulation.setFloatable(False)
        self.toolBarEmulation.setMovable(False)
        self.toolBarEmulation.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.pageEmulationLayout.addWidget(self.toolBarEmulation, 0, 0)

        #Add progress bar to status bar
        self.taskProgress = QProgressBar()
        self.taskProgress.setVal = lambda x: ( self.taskProgress.setVisible(True), self.taskProgress.setValue(x) )
        self.taskProgress.setVal(0)
        self.taskProgress.setTextVisible(False)
        self.statusBar.addPermanentWidget(self.taskProgress)

        #Also print messages to terminal
        self.statusBar.showMsg = lambda msg, timeout: ( logging.info(msg), self.statusBar.showMessage(msg, timeout) )

        #Styling
        self.setStyleSheet('QToolButton { padding-right: -3px; }')


    def ui_connect(self):
        #Menu actions
        self.actionQuit.triggered.connect(QCoreApplication.quit)
        self.actionSettings.triggered.connect(lambda: settings.SettingsDialog(parent=self).exec_())
        self.actionAbout.triggered.connect(lambda: about.AboutDialog(parent=self).exec_())

        #Toolbar actions
        self.actionPageEmulation.triggered.connect(lambda: self.stackedWidget.setCurrentIndex(0))

        #Other signals
        self.emulator_found.connect(self.add_emulator)
        self.emulators_loaded.connect(self.reset_status)


    def settings_load(self):
        if self.settings.value('qtendo/window/restore', True, type=bool):
            self.restoreGeometry(self.settings.value('qtendo/window/geometry', type=QByteArray))


    def settings_save(self):
        if self.settings.value('qtendo/window/restore', True, type=bool):
            self.settings.setValue('qtendo/window/geometry', self.saveGeometry())



    def change_emulator(self, index):
        current = self.stackedWidgetEmulation.currentIndex()

        if current != index:
            emulator = self.emulators[list(self.emulators.keys())[current]]
            emulator['action'].setIcon(QIcon(emulator['action'].icon().pixmap(QSize(24, 24), QIcon.Disabled)))

            self.stackedWidgetEmulation.setCurrentIndex(index)
            emulator = self.emulators[list(self.emulators.keys())[index]]
            emulator['action'].setIcon(QIcon(':' + emulator['icon']))


    def add_emulator(self, emulator):
        if len(emulator['path']) > 0:
            self.statusBar.showMsg('Found ' + emulator['name'], 1000)
        else:
            self.statusBar.showMsg('Failed to find ' + emulator['name'], 1000)

        self.emulators[emulator['name']] = emulator

        i = self.stackedWidgetEmulation.count()
        emulator['action'] = QAction()

        emulator['action'].setIcon(QIcon(':' + emulator['icon']))
        if i > 0:
            self.toolBarEmulation.addSeparator()
            emulator['action'].setIcon(QIcon(emulator['action'].icon().pixmap(QSize(24, 24), QIcon.Disabled)))

        emulator['action'].setIconText(emulator['name'])
        emulator['action'].triggered.connect(lambda checked, index=i: self.change_emulator(index))

        self.toolBarEmulation.addAction(emulator['action'])
        self.stackedWidgetEmulation.insertWidget(i, emulator['widget'](emulator))

        if len(self.settings.value('emulation/emulator/' + emulator['name'].lower().replace(' ', '_') + '/path', emulator['path'], type=str)) == 0:
            self.toolBarEmulation.widgetForAction(emulator['action']).setStyleSheet('color: ' + QApplication.palette().color(QPalette.Disabled, QPalette.WindowText).name() + ';')

        emulator['reload_settings'] = self.reload_settings

        self.taskProgress.setVal(int((100.0 / float(emulator_count)) * float(i + 1)))


    def reset_status(self):
        self.statusBar.clearMessage()
        self.taskProgress.setValue(0)
        self.taskProgress.setVisible(False)


    def reload_settings(self):
        emulator = self.emulators[list(self.emulators.keys())[self.stackedWidgetEmulation.currentIndex()]]

        if len(self.settings.value('emulation/emulator/' + emulator['name'].lower().replace(' ', '_') + '/path', emulator['path'], type=str)) == 0:
            self.toolBarEmulation.widgetForAction(emulator['action']).setStyleSheet('color: ' + QApplication.palette().color(QPalette.Disabled, QPalette.WindowText).name() + ';')
        else:
            self.toolBarEmulation.widgetForAction(emulator['action']).setStyleSheet('')



    def find_emulators(self):
        #Search for FCEUX
        self.emulator_found.emit(fceux.find(None if not self.settings.contains('emulation/emulator/fceux/path') else self.settings.value('emulation/emulator/fceux/path', type=str)))
        #Search for ZSNES
        self.emulator_found.emit(zsnes.find(None if not self.settings.contains('emulation/emulator/zsnes/path') else self.settings.value('emulation/emulator/zsnes/path', type=str)))
        #Search for Mupen64Plus
        self.emulator_found.emit(mupen64plus.find(None if not self.settings.contains('emulation/emulator/mupen64plus/path') else self.settings.value('emulation/emulator/mupen64plus/path', type=str)))
        #Search for Dolphin Emulator
        self.emulator_found.emit(dolphin.find(None if not self.settings.contains('emulation/emulator/dolphin/path') else self.settings.value('emulation/emulator/dolphin/path', type=str)))
        #Search for Citra
        self.emulator_found.emit(citra.find(None if not self.settings.contains('emulation/emulator/citra/path') else self.settings.value('emulation/emulator/citra/path', type=str)))

        self.emulators_loaded.emit()
Esempio n. 45
0
class AppWindow(QMainWindow):
	"The application's main window"
	move_listener = pyqtSignal()
	db_activity_checker = pyqtSignal()
	graphics_blur = QGraphicsBlurEffect()
	def __init__(self):
		super().__init__()
		app_constants.GENERAL_THREAD = QThread(self)
		app_constants.GENERAL_THREAD.finished.connect(app_constants.GENERAL_THREAD.deleteLater)
		app_constants.GENERAL_THREAD.start()
		self.setAcceptDrops(True)
		self.initUI()
		self.start_up()
		QTimer.singleShot(3000, self._check_update)
		self.setFocusPolicy(Qt.NoFocus)
		self.set_shortcuts()
		self.graphics_blur.setParent(self)
		#ex = settings.ExProperties()
		#d = pewnet.ExHenManager(ex.ipb_id, ex.ipb_pass)
		#item = d.from_gallery_url('http://exhentai.org/g/861957/02741dc584/')
		#def a(): print(item.file)
		#if not item.file:
		#	item.file_rdy.connect(a)
		#else:
		#	a()

	def set_shortcuts(self):
		quit = QShortcut(QKeySequence('Ctrl+Q'), self, self.close)

	def init_watchers(self):

		def remove_gallery(g):
			index = self.manga_list_view.find_index(g.id, True)
			if index:
				self.manga_list_view.remove_gallery([index])

		def create_gallery(path):
			g_dia = gallerydialog.GalleryDialog(self, path)
			g_dia.SERIES.connect(self.manga_list_view.gallery_model.addRows)
			g_dia.show()

		def update_gallery(g):
			index = self.manga_list_view.find_index(g.id)
			if index:
				self.manga_list_view.replace_edit_gallery([g], index.row())
			else:
				log_e('Could not find gallery to update from watcher')

		def created(path):
			c_popup = io_misc.CreatedPopup(path, self)
			c_popup.ADD_SIGNAL.connect(create_gallery)
		def modified(path, gallery):
			mod_popup = io_misc.ModifiedPopup(path, gallery, self)
		def deleted(path, gallery):
			d_popup = io_misc.DeletedPopup(path, gallery, self)
			d_popup.UPDATE_SIGNAL.connect(update_gallery)
			d_popup.REMOVE_SIGNAL.connect(remove_gallery)
		def moved(new_path, gallery):
			mov_popup = io_misc.MovedPopup(new_path, gallery, self)
			mov_popup.UPDATE_SIGNAL.connect(update_gallery)

		self.watchers = io_misc.Watchers()
		self.watchers.gallery_handler.CREATE_SIGNAL.connect(created)
		self.watchers.gallery_handler.MODIFIED_SIGNAL.connect(modified)
		self.watchers.gallery_handler.MOVED_SIGNAL.connect(moved)
		self.watchers.gallery_handler.DELETED_SIGNAL.connect(deleted)

	admin_db_method_invoker = pyqtSignal(str)
	def start_up(self):
		hello = ["Hello!", "Hi!", "Onii-chan!", "Senpai!", "Hisashiburi!", "Welcome!", "Okaerinasai!", "Welcome back!", "Hajimemashite!"]
		self.notification_bar.add_text("{} Please don't hesitate to report any bugs you find.".format(hello[random.randint(0, len(hello)-1)])+
								 " Go to Settings -> About -> Bug Reporting for more info!")
		level = 5
		def normalize_first_time():
			settings.set(level, 'Application', 'first time level')
			settings.save()

		def done(status=True):
			gallerydb.DatabaseEmitter.RUN = True
			if app_constants.FIRST_TIME_LEVEL != level:
				normalize_first_time()
			if app_constants.ENABLE_MONITOR and\
				app_constants.MONITOR_PATHS and all(app_constants.MONITOR_PATHS):
				self.init_watchers()
				if app_constants.LOOK_NEW_GALLERY_STARTUP:
					if self.manga_list_view.gallery_model.db_emitter.count == app_constants.GALLERY_DATA:
						self.scan_for_new_galleries()
					else:
						self.manga_list_view.gallery_model.db_emitter.DONE.connect(self.scan_for_new_galleries)
			self.download_manager = pewnet.Downloader()
			app_constants.DOWNLOAD_MANAGER = self.download_manager
			self.download_manager.start_manager(4)

		if app_constants.FIRST_TIME_LEVEL < 4:
			log_i('Invoking first time level {}'.format(4))
			settings.set([], 'Application', 'monitor paths')
			settings.set([], 'Application', 'ignore paths')
			app_constants.MONITOR_PATHS = []
			app_constants.IGNORE_PATHS = []
			settings.save()
			done()
		elif app_constants.FIRST_TIME_LEVEL < 5:
			log_i('Invoking first time level {}'.format(5))
			app_widget = misc.ApplicationPopup(self)
			app_widget.note_info.setText("<font color='red'>IMPORTANT:</font> Application restart is required when done")
			app_widget.restart_info.hide()
			self.admin_db = gallerydb.AdminDB()
			self.admin_db.moveToThread(app_constants.GENERAL_THREAD)
			self.admin_db.DONE.connect(done)
			self.admin_db.DONE.connect(lambda: app_constants.NOTIF_BAR.add_text("Application requires a restart"))
			self.admin_db.DONE.connect(self.admin_db.deleteLater)
			self.admin_db.DATA_COUNT.connect(app_widget.prog.setMaximum)
			self.admin_db.PROGRESS.connect(app_widget.prog.setValue)
			self.admin_db_method_invoker.connect(self.admin_db.rebuild_db)
			self.admin_db_method_invoker.connect(app_widget.show)
			app_widget.adjustSize()
			db_p = os.path.join(os.path.split(database.db_constants.DB_PATH)[0], 'sadpanda.db')
			self.admin_db_method_invoker.emit(db_p)
		else:
			done()

	def initUI(self):
		self.center = QWidget()
		self.display = QStackedLayout()
		self._main_layout = QVBoxLayout()
		self._main_layout.setSpacing(0)
		self._main_layout.setContentsMargins(0,0,0,0)
		self._main_layout.addLayout(self.display)
		self.center.setLayout(self._main_layout)
		# init the manga view variables
		self.manga_display()
		log_d('Create manga display: OK')
		# init the chapter view variables
		#self.chapter_display()
		self.m_l_view_index = self.display.addWidget(self.manga_list_view)
		self.m_t_view_index = self.display.addWidget(self.manga_table_view)
		self.download_window = io_misc.GalleryDownloader(self)
		self.download_window.hide()
		# init toolbar
		self.init_toolbar()
		log_d('Create toolbar: OK')
		# init status bar
		self.init_stat_bar()
		log_d('Create statusbar: OK')

		self.tags_treeview = None
		if app_constants.TAGS_TREEVIEW_ON_START:
			def tags_tree_none(): self.tags_treeview = None
			self.tags_treeview = misc_db.DBOverview(self, True)
			self.tags_treeview.about_to_close.connect(tags_tree_none)
			self.tags_treeview.show()

		self.system_tray = misc.SystemTray(QIcon(app_constants.APP_ICO_PATH), self)
		app_constants.SYSTEM_TRAY = self.system_tray
		tray_menu = QMenu(self)
		self.system_tray.setContextMenu(tray_menu)
		self.system_tray.setToolTip('Happypanda {}'.format(app_constants.vs))
		tray_quit = QAction('Quit', tray_menu)
		tray_update = tray_menu.addAction('Check for update')
		tray_update.triggered.connect(self._check_update)
		tray_menu.addAction(tray_quit)
		tray_quit.triggered.connect(self.close)
		self.system_tray.show()
		def tray_activate(r=None):
			if not r or r == QSystemTrayIcon.Trigger:
				self.showNormal()
				self.activateWindow()
		self.system_tray.messageClicked.connect(tray_activate)
		self.system_tray.activated.connect(tray_activate)
		log_d('Create system tray: OK')
		#self.display.addWidget(self.chapter_main)

		self.setCentralWidget(self.center)
		self.setWindowIcon(QIcon(app_constants.APP_ICO_PATH))


		props = settings.win_read(self, 'AppWindow')
		if props.resize:
			x, y = props.resize
			self.resize(x, y)
		else:
			self.resize(app_constants.MAIN_W, app_constants.MAIN_H)
		posx, posy = props.pos
		self.move(posx, posy)
		self.init_spinners()
		self.show()
		log_d('Show window: OK')

		self.notification_bar = misc.NotificationOverlay(self)
		p = self.toolbar.pos()
		self.notification_bar.move(p.x(), p.y()+self.toolbar.height())
		self.notification_bar.resize(self.width())
		app_constants.NOTIF_BAR = self.notification_bar
		log_d('Create notificationbar: OK')

		log_d('Window Create: OK')

	def _check_update(self):
		class upd_chk(QObject):
			UPDATE_CHECK = pyqtSignal(str)
			def __init__(self, **kwargs):
				super().__init__(**kwargs)
			def fetch_vs(self):
				import requests
				import time
				log_d('Checking Update')
				time.sleep(1.5)
				try:
					if os.path.exists('cacert.pem'):
						r = requests.get("https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt",
							  verify='cacert.pem')
					else:
						r = requests.get("https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt")
					a = r.text
					vs = a.strip()
					self.UPDATE_CHECK.emit(vs)
				except:
					log.exception('Checking Update: FAIL')
					self.UPDATE_CHECK.emit('this is a very long text which is sure to be over limit')

		def check_update(vs):
			log_i('Received version: {}\nCurrent version: {}'.format(vs, app_constants.vs))
			if vs != app_constants.vs:
				if len(vs) < 10:
					self.notification_bar.begin_show()
					self.notification_bar.add_text("Version {} of Happypanda is".format(vs)+
									   " available. Click here to update!", False)
					self.notification_bar.clicked.connect(lambda: utils.open_web_link(
						'https://github.com/Pewpews/happypanda/releases'))
					self.notification_bar.set_clickable(True)
				else:
					self.notification_bar.add_text("An error occurred while checking for new version")

		self.update_instance = upd_chk()
		thread = QThread(self)
		self.update_instance.moveToThread(thread)
		thread.started.connect(self.update_instance.fetch_vs)
		self.update_instance.UPDATE_CHECK.connect(check_update)
		self.update_instance.UPDATE_CHECK.connect(self.update_instance.deleteLater)
		thread.finished.connect(thread.deleteLater)
		thread.start()

	def _web_metadata_picker(self, gallery, title_url_list, queue, parent=None):
		if not parent:
			parent = self
		text = "Which gallery do you want to extract metadata from?"
		s_gallery_popup = misc.SingleGalleryChoices(gallery, title_url_list,
											  text, parent)
		s_gallery_popup.USER_CHOICE.connect(queue.put)

	def get_metadata(self, gal=None):
		metadata_spinner = misc.Spinner(self)
		def move_md_spinner():
			metadata_spinner.update_move(
			QPoint(
				self.pos().x()+self.width()-65,
				self.pos().y()+self.toolbar.height()+55))
		metadata_spinner.set_text("Metadata")
		metadata_spinner.set_size(55)
		metadata_spinner.move(QPoint(self.pos().x()+self.width()-65,
							   self.pos().y()+self.toolbar.height()+55))
		self.move_listener.connect(move_md_spinner)
		thread = QThread(self)
		thread.setObjectName('App.get_metadata')
		fetch_instance = fetch.Fetch()
		if gal:
			if not isinstance(gal, list):
				galleries = [gal]
			else:
				galleries = gal
		else:
			if app_constants.CONTINUE_AUTO_METADATA_FETCHER:
				galleries = [g for g in self.manga_list_view.gallery_model._data if not g.exed]
			else:
				galleries = self.manga_list_view.gallery_model._data
			if not galleries:
				self.notification_bar.add_text('Looks like we\'ve already gone through all galleries!')
				return None
		fetch_instance.galleries = galleries

		self.notification_bar.begin_show()
		fetch_instance.moveToThread(thread)

		def done(status):
			self.notification_bar.end_show()
			fetch_instance.deleteLater()
			if not isinstance(status, bool):
				galleries = []
				for tup in status:
					galleries.append(tup[0])

				class GalleryContextMenu(QMenu):
					app_instance = self
					def __init__(self, parent=None):
						super().__init__(parent)
						show_in_library_act = self.addAction('Show in library')
						show_in_library_act.triggered.connect(self.show_in_library)

					def show_in_library(self):
						viewer = self.app_instance.manga_list_view
						index = viewer.find_index(self.gallery_widget.gallery.id, True)
						if index:
							self.app_instance.manga_table_view.scroll_to_index(index)
							self.app_instance.manga_list_view.scroll_to_index(index)

				g_popup = io_misc.GalleryPopup(('Fecthing metadata for these galleries failed.'+
									  ' Check happypanda.log for details.', galleries), self, menu=GalleryContextMenu())
				#errors = {g[0].id: g[1] for g in status}
				#for g_item in g_popup.get_all_items():
				#	g_item.setToolTip(errors[g_item.gallery.id])
				g_popup.graphics_blur.setEnabled(False)
				close_button = g_popup.add_buttons('Close')[0]
				close_button.clicked.connect(g_popup.close)

		fetch_instance.GALLERY_PICKER.connect(self._web_metadata_picker)
		fetch_instance.GALLERY_EMITTER.connect(self.manga_list_view.replace_edit_gallery)
		fetch_instance.AUTO_METADATA_PROGRESS.connect(self.notification_bar.add_text)
		thread.started.connect(fetch_instance.auto_web_metadata)
		fetch_instance.FINISHED.connect(done)
		fetch_instance.FINISHED.connect(metadata_spinner.close)
		fetch_instance.FINISHED.connect(lambda: self.move_listener.disconnect(move_md_spinner))
		thread.finished.connect(thread.deleteLater)
		thread.start()
		metadata_spinner.show()

	def init_stat_bar(self):
		self.status_bar = self.statusBar()
		self.status_bar.setMaximumHeight(20)
		self.status_bar.setSizeGripEnabled(False)
		self.stat_info = QLabel()
		self.stat_info.setIndent(5)
		self.sort_main = QAction("Asc", self)
		sort_menu = QMenu()
		self.sort_main.setMenu(sort_menu)
		s_by_title = QAction("Title", sort_menu)
		s_by_artist = QAction("Artist", sort_menu)
		sort_menu.addAction(s_by_title)
		sort_menu.addAction(s_by_artist)
		self.status_bar.addPermanentWidget(self.stat_info)
		#self.status_bar.addAction(self.sort_main)
		self.temp_msg = QLabel()
		self.temp_timer = QTimer()

		self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.connect(self.stat_row_info)
		self.manga_list_view.gallery_model.db_emitter.COUNT_CHANGE.connect(self.stat_row_info)
		self.manga_list_view.gallery_model.STATUSBAR_MSG.connect(self.stat_temp_msg)
		self.manga_list_view.STATUS_BAR_MSG.connect(self.stat_temp_msg)
		self.manga_table_view.STATUS_BAR_MSG.connect(self.stat_temp_msg)
		self.stat_row_info()
		app_constants.STAT_MSG_METHOD = self.stat_temp_msg

	def stat_temp_msg(self, msg):
		self.temp_timer.stop()
		self.temp_msg.setText(msg)
		self.status_bar.addWidget(self.temp_msg)
		self.temp_timer.timeout.connect(self.temp_msg.clear)
		self.temp_timer.setSingleShot(True)
		self.temp_timer.start(5000)

	def stat_row_info(self):
		r = self.manga_list_view.model().rowCount()
		t = self.manga_list_view.gallery_model.db_emitter.count
		self.stat_info.setText("Loaded {} of {} ".format(r, t))

	def manga_display(self):
		"initiates the manga view and related things"
		#list view
		self.manga_list_view = gallery.MangaView(self)

		#table view

		self.manga_table_view = gallery.MangaTableView(self)
		self.manga_table_view.gallery_model = self.manga_list_view.gallery_model
		self.manga_table_view.sort_model = self.manga_list_view.sort_model
		self.manga_table_view.setModel(self.manga_table_view.sort_model)
		self.manga_table_view.sort_model.change_model(self.manga_table_view.gallery_model)
		self.manga_table_view.setColumnWidth(app_constants.FAV, 20)
		self.manga_table_view.setColumnWidth(app_constants.ARTIST, 200)
		self.manga_table_view.setColumnWidth(app_constants.TITLE, 400)
		self.manga_table_view.setColumnWidth(app_constants.TAGS, 300)
		self.manga_table_view.setColumnWidth(app_constants.TYPE, 60)
		self.manga_table_view.setColumnWidth(app_constants.CHAPTERS, 60)
		self.manga_table_view.setColumnWidth(app_constants.LANGUAGE, 100)
		self.manga_table_view.setColumnWidth(app_constants.LINK, 400)


	def init_spinners(self):
		# fetching spinner
		self.data_fetch_spinner = misc.Spinner(self)
		self.data_fetch_spinner.set_size(60)
		self.move_listener.connect(
			lambda: self.data_fetch_spinner.update_move(
				QPoint(self.pos().x()+self.width()//2, self.pos().y()+self.height()//2)))
		
		self.manga_list_view.gallery_model.ADD_MORE.connect(self.data_fetch_spinner.show)
		self.manga_list_view.gallery_model.db_emitter.START.connect(self.data_fetch_spinner.show)
		self.manga_list_view.gallery_model.ADDED_ROWS.connect(self.data_fetch_spinner.before_hide)
		self.manga_list_view.gallery_model.db_emitter.CANNOT_FETCH_MORE.connect(self.data_fetch_spinner.before_hide)

		## deleting spinner
		#self.gallery_delete_spinner = misc.Spinner(self)
		#self.gallery_delete_spinner.set_size(40,40)
		##self.gallery_delete_spinner.set_text('Removing...')
		#self.manga_list_view.gallery_model.rowsAboutToBeRemoved.connect(self.gallery_delete_spinner.show)
		#self.manga_list_view.gallery_model.rowsRemoved.connect(self.gallery_delete_spinner.before_hide)


	def search(self, srch_string):
		self.search_bar.setText(srch_string)
		self.search_backward.setVisible(True)
		self.manga_list_view.sort_model.init_search(srch_string)
		old_cursor_pos = self._search_cursor_pos[0]
		self.search_bar.end(False)
		if self.search_bar.cursorPosition() != old_cursor_pos+1:
			self.search_bar.setCursorPosition(old_cursor_pos)


	def favourite_display(self):
		"Switches to favourite display"
		self.manga_table_view.sort_model.fav_view()
		self.favourite_btn.selected = True
		self.library_btn.selected = False

	def catalog_display(self):
		"Switches to catalog display"
		self.manga_table_view.sort_model.catalog_view()
		self.library_btn.selected = True
		self.favourite_btn.selected = False

	def settings(self):
		sett = settingsdialog.SettingsDialog(self)
		sett.scroll_speed_changed.connect(self.manga_list_view.updateGeometries)
		#sett.show()

	def init_toolbar(self):
		self.toolbar = QToolBar()
		self.toolbar.setFixedHeight(25)
		self.toolbar.setWindowTitle("Show") # text for the contextmenu
		#self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined?
		self.toolbar.setMovable(False)
		self.toolbar.setFloatable(False)
		#self.toolbar.setIconSize(QSize(20,20))
		self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
		self.toolbar.setIconSize(QSize(20,20))

		spacer_start = QWidget() # aligns the first actions properly
		spacer_start.setFixedSize(QSize(10, 1))
		self.toolbar.addWidget(spacer_start)

		self.favourite_btn = misc.ToolbarButton(self.toolbar, 'Favorites')
		self.toolbar.addWidget(self.favourite_btn)
		self.favourite_btn.clicked.connect(self.favourite_display) #need lambda to pass extra args

		self.library_btn = misc.ToolbarButton(self.toolbar, 'Library')
		self.toolbar.addWidget(self.library_btn)
		self.library_btn.clicked.connect(self.catalog_display) #need lambda to pass extra args
		self.library_btn.selected = True

		self.toolbar.addSeparator()

		gallery_menu = QMenu()
		gallery_action = QToolButton()
		gallery_action.setText('Gallery ')
		gallery_action.setPopupMode(QToolButton.InstantPopup)
		gallery_action.setToolTip('Contains various gallery related features')
		gallery_action.setMenu(gallery_menu)
		add_gallery_icon = QIcon(app_constants.PLUS_PATH)
		gallery_action_add = QAction(add_gallery_icon, "Add single gallery...", self)
		gallery_action_add.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit)
		gallery_action_add.setToolTip('Add a single gallery thoroughly')
		gallery_menu.addAction(gallery_action_add)
		add_more_action = QAction(add_gallery_icon, "Add galleries...", self)
		add_more_action.setStatusTip('Add galleries from different folders')
		add_more_action.triggered.connect(lambda: self.populate(True))
		gallery_menu.addAction(add_more_action)
		populate_action = QAction(add_gallery_icon, "Populate from directory/archive...", self)
		populate_action.setStatusTip('Populates the DB with galleries from a single folder or archive')
		populate_action.triggered.connect(self.populate)
		gallery_menu.addAction(populate_action)
		gallery_menu.addSeparator()
		metadata_action = QAction('Get metadata for all galleries', self)
		metadata_action.triggered.connect(self.get_metadata)
		gallery_menu.addAction(metadata_action)
		scan_galleries_action = QAction('Scan for new galleries', self)
		scan_galleries_action.triggered.connect(self.scan_for_new_galleries)
		scan_galleries_action.setStatusTip('Scan monitored folders for new galleries')
		gallery_menu.addAction(scan_galleries_action)
		gallery_action_random = gallery_menu.addAction("Open random gallery")
		gallery_action_random.triggered.connect(self.manga_list_view.open_random_gallery)
		self.toolbar.addWidget(gallery_action)


		misc_action = QToolButton()
		misc_action.setText('Tools ')
		misc_action_menu = QMenu()
		misc_action.setMenu(misc_action_menu)
		misc_action.setPopupMode(QToolButton.InstantPopup)
		misc_action.setToolTip("Contains misc. features")
		gallery_downloader = QAction("Gallery Downloader", misc_action_menu)
		gallery_downloader.triggered.connect(self.download_window.show)
		misc_action_menu.addAction(gallery_downloader)
		duplicate_check_simple = QAction("Simple Duplicate Finder", misc_action_menu)
		duplicate_check_simple.triggered.connect(lambda: self.manga_list_view.duplicate_check())
		misc_action_menu.addAction(duplicate_check_simple)
		self.toolbar.addWidget(misc_action)

		spacer_middle = QWidget() # aligns buttons to the right
		spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
		self.toolbar.addWidget(spacer_middle)


		sort_action = QToolButton()
		sort_action.setIcon(QIcon(app_constants.SORT_PATH))
		sort_action.setMenu(misc.SortMenu(self.toolbar, self.manga_list_view))
		sort_action.setPopupMode(QToolButton.InstantPopup)
		self.toolbar.addWidget(sort_action)
		
		self.grid_toggle_g_icon = QIcon(app_constants.GRID_PATH)
		self.grid_toggle_l_icon = QIcon(app_constants.LIST_PATH)
		self.grid_toggle = QToolButton()
		if self.display.currentIndex() == self.m_l_view_index:
			self.grid_toggle.setIcon(self.grid_toggle_l_icon)
		else:
			self.grid_toggle.setIcon(self.grid_toggle_g_icon)
		self.grid_toggle.setObjectName('gridtoggle')
		self.grid_toggle.clicked.connect(self.toggle_view)
		self.toolbar.addWidget(self.grid_toggle)

		spacer_mid2 = QWidget()
		spacer_mid2.setFixedSize(QSize(5, 1))
		self.toolbar.addWidget(spacer_mid2)

		def set_search_case(b):
			app_constants.GALLERY_SEARCH_CASE = b
			settings.set(b, 'Application', 'gallery search case')
			settings.save()

		def set_search_strict(b):
			app_constants.GALLERY_SEARCH_STRICT = b
			settings.set(b, 'Application', 'gallery search strict')
			settings.save()

		self.search_bar = misc.LineEdit()
		search_options = self.search_bar.addAction(QIcon(app_constants.SEARCH_OPTIONS_PATH), QLineEdit.TrailingPosition)
		search_options_menu = QMenu(self)
		search_options.triggered.connect(lambda: search_options_menu.popup(QCursor.pos()))
		search_options.setMenu(search_options_menu)
		case_search_option = search_options_menu.addAction('Case Sensitive')
		case_search_option.setCheckable(True)
		case_search_option.setChecked(app_constants.GALLERY_SEARCH_CASE)
		case_search_option.toggled.connect(set_search_case)
		strict_search_option = search_options_menu.addAction('Match whole terms')
		strict_search_option.setCheckable(True)
		strict_search_option.setChecked(app_constants.GALLERY_SEARCH_STRICT)
		strict_search_option.toggled.connect(set_search_strict)
		self.search_bar.setObjectName('search_bar')
		self.search_timer = QTimer(self)
		self.search_timer.setSingleShot(True)
		self.search_timer.timeout.connect(lambda: self.search(self.search_bar.text()))
		self._search_cursor_pos = [0, 0]
		def set_cursor_pos(old, new):
			self._search_cursor_pos[0] = old
			self._search_cursor_pos[1] = new
		self.search_bar.cursorPositionChanged.connect(set_cursor_pos)

		if app_constants.SEARCH_AUTOCOMPLETE:
			completer = QCompleter(self)
			completer_view = misc.CompleterPopupView()
			completer.setPopup(completer_view)
			completer_view._setup()
			completer.setModel(self.manga_list_view.gallery_model)
			completer.setCaseSensitivity(Qt.CaseInsensitive)
			completer.setCompletionMode(QCompleter.PopupCompletion)
			completer.setCompletionRole(Qt.DisplayRole)
			completer.setCompletionColumn(app_constants.TITLE)
			completer.setFilterMode(Qt.MatchContains)
			self.search_bar.setCompleter(completer)
			self.search_bar.returnPressed.connect(lambda: self.search(self.search_bar.text()))
		if not app_constants.SEARCH_ON_ENTER:
			self.search_bar.textEdited.connect(lambda: self.search_timer.start(800))
		self.search_bar.setPlaceholderText("Search title, artist, namespace & tags")
		self.search_bar.setMinimumWidth(150)
		self.search_bar.setMaximumWidth(500)
		self.search_bar.setFixedHeight(19)
		self.manga_list_view.sort_model.HISTORY_SEARCH_TERM.connect(lambda a: self.search_bar.setText(a))
		self.toolbar.addWidget(self.search_bar)

		def search_history(_, back=True): # clicked signal passes a bool
			sort_model =  self.manga_list_view.sort_model
			nav = sort_model.PREV if back else sort_model.NEXT
			history_term = sort_model.navigate_history(nav)
			if back:
				self.search_forward.setVisible(True)

		back = QShortcut(QKeySequence(QKeySequence.Back), self, lambda: search_history(None))
		forward = QShortcut(QKeySequence(QKeySequence.Forward), self, lambda: search_history(None, False))

		search_backbutton = QToolButton(self.toolbar)
		search_backbutton.setText(u'\u25C0')
		search_backbutton.setFixedWidth(15)
		search_backbutton.clicked.connect(search_history)
		self.search_backward = self.toolbar.addWidget(search_backbutton)
		self.search_backward.setVisible(False)
		search_forwardbutton = QToolButton(self.toolbar)
		search_forwardbutton.setText(u'\u25B6')
		search_forwardbutton.setFixedWidth(15)
		search_forwardbutton.clicked.connect(lambda: search_history(None, False))
		self.search_forward = self.toolbar.addWidget(search_forwardbutton)
		self.search_forward.setVisible(False)

		spacer_end = QWidget() # aligns settings action properly
		spacer_end.setFixedSize(QSize(10, 1))
		self.toolbar.addWidget(spacer_end)

		settings_act = QToolButton(self.toolbar)
		settings_act.setIcon(QIcon(app_constants.SETTINGS_PATH))
		settings_act.clicked.connect(self.settings)
		self.toolbar.addWidget(settings_act)

		spacer_end2 = QWidget() # aligns About action properly
		spacer_end2.setFixedSize(QSize(5, 1))
		self.toolbar.addWidget(spacer_end2)
		self.addToolBar(self.toolbar)

	def toggle_view(self):
		"""
		Toggles the current display view
		"""
		if self.display.currentIndex() == self.m_l_view_index:
			self.display.setCurrentIndex(self.m_t_view_index)
			self.grid_toggle.setIcon(self.grid_toggle_g_icon)
		else:
			self.display.setCurrentIndex(self.m_l_view_index)
			self.grid_toggle.setIcon(self.grid_toggle_l_icon)

	# TODO: Improve this so that it adds to the gallery dialog,
	# so user can edit data before inserting (make it a choice)
	def populate(self, mixed=None):
		"Populates the database with gallery from local drive'"
		if mixed:
			gallery_view = misc.GalleryListView(self, True)
			gallery_view.SERIES.connect(self.gallery_populate)
			gallery_view.show()
		else:
			msg_box = misc.BasePopup(self)
			l = QVBoxLayout()
			msg_box.main_widget.setLayout(l)
			l.addWidget(QLabel('Directory or Archive?'))
			l.addLayout(msg_box.buttons_layout)

			def from_dir():
				path = QFileDialog.getExistingDirectory(self, "Choose a directory containing your galleries")
				if not path:
					return
				msg_box.close()
				app_constants.OVERRIDE_SUBFOLDER_AS_GALLERY = True
				self.gallery_populate(path, True)
			def from_arch():
				path = QFileDialog.getOpenFileName(self, 'Choose an archive containing your galleries',
									   filter=utils.FILE_FILTER)
				path = [path[0]]
				if not all(path) or not path:
					return
				msg_box.close()
				app_constants.OVERRIDE_SUBFOLDER_AS_GALLERY = True
				self.gallery_populate(path, True)

			buttons = msg_box.add_buttons('Directory', 'Archive', 'Close')
			buttons[2].clicked.connect(msg_box.close)
			buttons[0].clicked.connect(from_dir)
			buttons[1].clicked.connect(from_arch)
			msg_box.adjustSize()
			msg_box.show()

	def gallery_populate(self, path, validate=False):
		"Scans the given path for gallery to add into the DB"
		if len(path) is not 0:
			data_thread = QThread(self)
			data_thread.setObjectName('General gallery populate')
			loading = misc.Loading(self)
			self.g_populate_inst = fetch.Fetch()
			self.g_populate_inst.series_path = path
			loading.show()

			def finished(status):
				def hide_loading():
					loading.hide()
				if status:
					if len(status) != 0:
						def add_gallery(gallery_list):
							def append_to_model(x):
								self.manga_list_view.sort_model.insertRows(x, None, len(x))
								self.manga_list_view.sort_model.init_search(
									self.manga_list_view.sort_model.current_term)

							class A(QObject):
								done = pyqtSignal()
								prog = pyqtSignal(int)
								def __init__(self, obj, parent=None):
									super().__init__(parent)
									self.obj = obj
									self.galleries = []

								def add_to_db(self):
									for y, x in enumerate(self.obj):
										gallerydb.add_method_queue(
											gallerydb.GalleryDB.add_gallery_return, False, x)
										self.galleries.append(x)
										y += 1
										self.prog.emit(y)
									append_to_model(self.galleries)
									self.done.emit()
							loading.progress.setMaximum(len(gallery_list))
							self.a_instance = A(gallery_list)
							thread = QThread(self)
							thread.setObjectName('Database populate')
							def loading_show(numb):
								if loading.isHidden():
									loading.show()
								loading.setText('Populating database ({}/{})\nPlease wait...'.format(
									numb, loading.progress.maximum()))
								loading.progress.setValue(numb)
								loading.show()

							def loading_hide():
								loading.close()
								self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.emit()

							self.a_instance.moveToThread(thread)
							self.a_instance.prog.connect(loading_show)
							thread.started.connect(self.a_instance.add_to_db)
							self.a_instance.done.connect(loading_hide)
							self.a_instance.done.connect(self.a_instance.deleteLater)
							#a_instance.add_to_db()
							thread.finished.connect(thread.deleteLater)
							thread.start()
						#data_thread.quit
						hide_loading()
						log_i('Populating DB from gallery folder: OK')
						if validate:
							gallery_list = misc.GalleryListView(self)
							gallery_list.SERIES.connect(add_gallery)
							for ser in status:
								if ser.is_archive and app_constants.SUBFOLDER_AS_GALLERY:
									p = os.path.split(ser.path)[1]
									if ser.chapters[0].path:
										pt_in_arch = os.path.split(ser.path_in_archive)
										pt_in_arch = pt_in_arch[1] or pt_in_arch[0]
										text = '{}: {}'.format(p, pt_in_arch)
									else:
										text = p
									gallery_list.add_gallery(ser, text)
								else:
									gallery_list.add_gallery(ser, os.path.split(ser.path)[1])
							#self.manga_list_view.gallery_model.populate_data()
							gallery_list.update_count()
							gallery_list.show()
						else:
							add_gallery(status)
					else:
						log_d('No new gallery was found')
						loading.setText("No new gallery found")
						#data_thread.quit

				else:
					log_e('Populating DB from gallery folder: Nothing was added!')
					loading.setText("<font color=red>Nothing was added. Check happypanda_log for details..</font>")
					loading.progress.setStyleSheet("background-color:red;")
					data_thread.quit
					QTimer.singleShot(8000, loading.close)

			def skipped_gs(s_list):
				"Skipped galleries"
				msg_box = QMessageBox(self)
				msg_box.setIcon(QMessageBox.Question)
				msg_box.setText('Do you want to view skipped paths?')
				msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
				msg_box.setDefaultButton(QMessageBox.No)
				if msg_box.exec() == QMessageBox.Yes:
					list_wid = QTableWidget(self)
					list_wid.setAttribute(Qt.WA_DeleteOnClose)
					list_wid.setRowCount(len(s_list))
					list_wid.setColumnCount(2)
					list_wid.setAlternatingRowColors(True)
					list_wid.setEditTriggers(list_wid.NoEditTriggers)
					list_wid.setHorizontalHeaderLabels(['Reason', 'Path'])
					list_wid.setSelectionBehavior(list_wid.SelectRows)
					list_wid.setSelectionMode(list_wid.SingleSelection)
					list_wid.setSortingEnabled(True)
					list_wid.verticalHeader().hide()
					list_wid.setAutoScroll(False)
					for x, g in enumerate(s_list):
						list_wid.setItem(x, 0, QTableWidgetItem(g[1]))
						list_wid.setItem(x, 1, QTableWidgetItem(g[0]))
					list_wid.resizeColumnsToContents()
					list_wid.setWindowTitle('{} skipped paths'.format(len(s_list)))
					list_wid.setWindowFlags(Qt.Window)
					list_wid.resize(900,400)

					list_wid.doubleClicked.connect(lambda i: utils.open_path(
						list_wid.item(i.row(), 1).text(), list_wid.item(i.row(), 1).text()))

					list_wid.show()

			def a_progress(prog):
				loading.progress.setValue(prog)
				loading.setText("Preparing galleries...")

			self.g_populate_inst.moveToThread(data_thread)
			self.g_populate_inst.DATA_COUNT.connect(loading.progress.setMaximum)
			self.g_populate_inst.PROGRESS.connect(a_progress)
			self.g_populate_inst.FINISHED.connect(finished)
			self.g_populate_inst.FINISHED.connect(self.g_populate_inst.deleteLater)
			self.g_populate_inst.SKIPPED.connect(skipped_gs)
			data_thread.finished.connect(data_thread.deleteLater)
			data_thread.started.connect(self.g_populate_inst.local)
			data_thread.start()
			#.g_populate_inst.local()
			log_i('Populating DB from directory/archive')

	def scan_for_new_galleries(self):
		available_folders =  app_constants.ENABLE_MONITOR and\
									app_constants.MONITOR_PATHS and all(app_constants.MONITOR_PATHS)
		if available_folders and not app_constants.SCANNING_FOR_GALLERIES:
			app_constants.SCANNING_FOR_GALLERIES = True
			self.notification_bar.add_text("Scanning for new galleries...")
			log_i('Scanning for new galleries...')
			try:
				class ScanDir(QObject):
					final_paths_and_galleries = pyqtSignal(list, list)
					finished = pyqtSignal()
					def __init__(self, parent=None):
						super().__init__(parent)
						self.scanned_data = []
					def scan_dirs(self):
						paths = []
						for p in app_constants.MONITOR_PATHS:
							if os.path.exists(p):
								dir_content = scandir.scandir(p)
								for d in dir_content:
									paths.append(d.path)
							else:
								log_e("Monitored path does not exists: {}".format(p.encode(errors='ignore')))

						fetch_inst = fetch.Fetch(self)
						fetch_inst.series_path = paths
						def set_scanned_d(d):
							self.scanned_data = d
						fetch_inst.FINISHED.connect(set_scanned_d)
						fetch_inst.local()
						#contents = []
						#for g in self.scanned_data:
						#	contents.append(g)

						#paths = sorted(paths)
						#new_galleries = []
						#for x in contents:
						#	y = utils.b_search(paths, os.path.normcase(x.path))
						#	if not y:
						#		new_galleries.append(x)

						galleries = []
						final_paths = []
						if self.scanned_data:
							for g in self.scanned_data:
								try:
									if g.is_archive:
										g.profile = utils.get_gallery_img(g.chapters[0].path, g.path)
									else:
										g.profile = utils.get_gallery_img(g.chapters[0].path)
									if not g.profile:
										raise Exception
								except:
									g.profile = app_constants.NO_IMAGE_PATH
							
								galleries.append(g)
								final_paths.append(g.path)
						self.final_paths_and_galleries.emit(final_paths, galleries)
						self.finished.emit()
						self.deleteLater()
					#if app_constants.LOOK_NEW_GALLERY_AUTOADD:
					#	QTimer.singleShot(10000, self.gallery_populate(final_paths))
					#	return

				def show_new_galleries(final_paths, galleries):
					if final_paths and galleries:
						app_constants.OVERRIDE_MOVE_IMPORTED_IN_FETCH = True
						if app_constants.LOOK_NEW_GALLERY_AUTOADD:
							self.gallery_populate(final_paths)
						else:

							class NewGalleryMenu(QMenu):

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

									ignore_act = self.addAction('Add to ignore list')
									ignore_act.triggered.connect(self.add_to_ignore)

								def add_to_ignore(self):
									gallery = self.gallery_widget.gallery
									app_constants.IGNORE_PATHS.append(gallery.path)
									settings.set(app_constants.IGNORE_PATHS, 'Application', 'ignore paths')
									if self.gallery_widget.parent_widget.gallery_layout.count() == 1:
										self.gallery_widget.parent_widget.close()
									else:
										self.gallery_widget.close()

							if len(galleries) == 1:
								self.notification_bar.add_text("{} new gallery was discovered in one of your monitored directories".format(len(galleries)))
							else:
								self.notification_bar.add_text("{} new galleries were discovered in one of your monitored directories".format(len(galleries)))
							text = "These new galleries were discovered! Do you want to add them?"\
								if len(galleries) > 1 else "This new gallery was discovered! Do you want to add it?"
							g_popup = io_misc.GalleryPopup((text, galleries), self, NewGalleryMenu())
							buttons = g_popup.add_buttons('Add', 'Close')

							def populate_n_close():
								g_popup.close()
								self.gallery_populate(final_paths)
							buttons[0].clicked.connect(populate_n_close)
							buttons[1].clicked.connect(g_popup.close)

				def finished(): app_constants.SCANNING_FOR_GALLERIES = False

				thread = QThread(self)
				self.scan_inst = ScanDir()
				self.scan_inst.moveToThread(thread)
				self.scan_inst.final_paths_and_galleries.connect(show_new_galleries)
				self.scan_inst.finished.connect(finished)
				thread.started.connect(self.scan_inst.scan_dirs)
				#self.scan_inst.scan_dirs()
				thread.finished.connect(thread.deleteLater)
				thread.start()
			except:
				self.notification_bar.add_text('An error occured while attempting to scan for new galleries. Check happypanda.log.')
				log.exception('An error occured while attempting to scan for new galleries.')
				app_constants.SCANNING_FOR_GALLERIES = False

	def dragEnterEvent(self, event):
		if event.mimeData().hasUrls():
			event.acceptProposedAction()
		else:
			self.notification_bar.add_text('File is not supported')

	def dropEvent(self, event):
		acceptable = []
		unaccept = []
		for u in event.mimeData().urls():
			path = u.toLocalFile()
			if os.path.isdir(path) or path.endswith(utils.ARCHIVE_FILES):
				acceptable.append(path)
			else:
				unaccept.append(path)
		log_i('Acceptable dropped items: {}'.format(len(acceptable)))
		log_i('Unacceptable dropped items: {}'.format(len(unaccept)))
		log_d('Dropped items: {}\n{}'.format(acceptable, unaccept).encode(errors='ignore'))
		if acceptable:
			self.notification_bar.add_text('Adding dropped items...')
			log_i('Adding dropped items')
			l = len(acceptable) == 1
			f_item = acceptable[0]
			if f_item.endswith(utils.ARCHIVE_FILES):
				f_item = utils.check_archive(f_item)
			else:
				f_item = utils.recursive_gallery_check(f_item)
			f_item_l = len(f_item) < 2
			subfolder_as_c = not app_constants.SUBFOLDER_AS_GALLERY
			if l and subfolder_as_c or l and f_item_l:
				g_d = gallerydialog.GalleryDialog(self, acceptable[0])
				g_d.SERIES.connect(self.manga_list_view.gallery_model.addRows)
				g_d.show()
			else:
				self.gallery_populate(acceptable, True)
		else:
			text = 'File not supported' if len(unaccept) < 2 else 'Files not supported'
			self.notification_bar.add_text(text)

		if unaccept:
			self.notification_bar.add_text('Some unsupported files did not get added')

	def resizeEvent(self, event):
		try:
			self.notification_bar.resize(event.size().width())
		except AttributeError:
			pass
		self.move_listener.emit()
		return super().resizeEvent(event)

	def moveEvent(self, event):
		self.move_listener.emit()
		return super().moveEvent(event)

	def showEvent(self, event):
		return super().showEvent(event)

	def cleanup_exit(self):
		self.system_tray.hide()
		# watchers
		try:
			self.watchers.stop_all()
		except AttributeError:
			pass

		# settings
		settings.set(self.manga_list_view.current_sort, 'General', 'current sort')
		settings.set(app_constants.IGNORE_PATHS, 'Application', 'ignore paths')
		settings.win_save(self, 'AppWindow')

		# temp dir
		try:
			for root, dirs, files in scandir.walk('temp', topdown=False):
				for name in files:
					os.remove(os.path.join(root, name))
				for name in dirs:
					os.rmdir(os.path.join(root, name))
			log_d('Flush temp on exit: OK')
		except:
			log.exception('Flush temp on exit: FAIL')

		if self.tags_treeview:
			self.tags_treeview.close()
		self.download_window.close()

		# check if there is db activity
		if not gallerydb.method_queue.empty():
			class DBActivityChecker(QObject):
				FINISHED = pyqtSignal()
				def __init__(self, **kwargs):
					super().__init__(**kwargs)

				def check(self):
					gallerydb.method_queue.join()
					self.FINISHED.emit()
					self.deleteLater()

			db_activity = DBActivityChecker()
			db_spinner = misc.Spinner(self)
			self.db_activity_checker.connect(db_activity.check)
			db_activity.moveToThread(app_constants.GENERAL_THREAD)
			db_activity.FINISHED.connect(db_spinner.close)
			db_spinner.set_size(50)
			db_spinner.set_text('Activity')
			db_spinner.move(QPoint(self.pos().x()+self.width()-70, self.pos().y()+self.height()-70))
			self.move_listener.connect(lambda: db_spinner.update_move(QPoint(self.pos().x()+self.width()-70,
																	self.pos().y()+self.height()-70)))
			db_spinner.show()
			self.db_activity_checker.emit()
			msg_box = QMessageBox(self)
			msg_box.setText('Database activity detected!')
			msg_box.setInformativeText("Closing now might result in data loss." +
								 " Do you still want to close?\n(Wait for the activity spinner to hide before closing)")
			msg_box.setIcon(QMessageBox.Critical)
			msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
			msg_box.setDefaultButton(QMessageBox.No)
			if msg_box.exec() == QMessageBox.Yes:
				return 1
			else:
				return 2
		else:
			return 0

	def closeEvent(self, event):
		r_code = self.cleanup_exit()
		if r_code == 1:
			log_d('Force Exit App: OK')
			super().closeEvent(event)
		elif r_code == 2:
			log_d('Ignore Exit App')
			event.ignore()
		else:
			log_d('Normal Exit App: OK')
			super().closeEvent(event)
Esempio n. 46
0
class EditorWidget(QWidget):

    TOOLBAR_ITEMS = [
        'save_query',
        '',
        'undo_action',
        'redo_action',
        'cut_action',
        'paste_action',
    ]

    editorModified = pyqtSignal(bool)

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)
        self.setStyleSheet("outline: none")
        hbox = QHBoxLayout()
        # Position
        self._column_str = "Col: {}"
        self._column_lbl = QLabel(self._column_str.format(0))
        # Toolbar
        self._toolbar = QToolBar(self)
        self._toolbar.setIconSize(QSize(16, 16))
        pireal = Pireal.get_service("pireal")
        for action in self.TOOLBAR_ITEMS:
            qaction = pireal.get_action(action)
            if qaction is not None:
                self._toolbar.addAction(qaction)
            else:
                self._toolbar.addSeparator()
        # hbox.addWidget(self._toolbar, 1)
        # hbox.addWidget(self._column_lbl)
        vbox.addLayout(hbox)
        # Editor
        self._editor = editor.Editor()
        vbox.addWidget(self._editor)
        # Search widget
        self._search_widget = SearchWidget(self)
        self._search_widget.hide()
        vbox.addWidget(self._search_widget)

        # Editor connections
        self._editor.customContextMenuRequested.connect(
            self.__show_context_menu)
        self._editor.modificationChanged[bool].connect(
            lambda modified: self.editorModified.emit(modified))
        self._editor.undoAvailable[bool].connect(
            self.__on_undo_available)
        self._editor.redoAvailable[bool].connect(
            self.__on_redo_available)
        self._editor.copyAvailable[bool].connect(
            self.__on_copy_available)
        self._editor.cursorPositionChanged.connect(
            self._update_column_label)

    def show_search_widget(self):
        self._search_widget.show()
        self._search_widget._line_search.setFocus()
        self._search_widget._execute_search(self._search_widget.search_text)

    def hide_search_widget(self):
        self._search_widget.hide()
        self._search_widget._line_search.clear()

    def _update_column_label(self):
        col = str(self._editor.textCursor().columnNumber() + 1)
        self._column_lbl.setText(self._column_str.format(col))

    def get_editor(self):
        return self._editor

    def __show_context_menu(self, point):
        popup_menu = self._editor.createStandardContextMenu()

        undock_editor = QAction(self.tr("Undock"), self)
        popup_menu.insertAction(popup_menu.actions()[0],
                                undock_editor)
        popup_menu.insertSeparator(popup_menu.actions()[1])
        undock_editor.triggered.connect(self.__undock_editor)

        popup_menu.exec_(self.mapToGlobal(point))

    def __undock_editor(self):
        new_editor = editor.Editor()
        actual_doc = self._editor.document()
        new_editor.setDocument(actual_doc)
        new_editor.resize(900, 400)
        # Set text cursor
        tc = self._editor.textCursor()
        new_editor.setTextCursor(tc)
        # Set title
        db = Pireal.get_service("central").get_active_db()
        qc = db.query_container
        new_editor.setWindowTitle(qc.tab_text(qc.current_index()))
        new_editor.show()

    def __on_undo_available(self, value):
        """ Change state of undo action """

        pireal = Pireal.get_service("pireal")
        action = pireal.get_action("undo_action")
        action.setEnabled(value)

    def __on_redo_available(self, value):
        """ Change state of redo action """

        pireal = Pireal.get_service("pireal")
        action = pireal.get_action("redo_action")
        action.setEnabled(value)

    def __on_copy_available(self, value):
        """ Change states of cut and copy action """

        cut_action = Pireal.get_action("cut_action")
        cut_action.setEnabled(value)
        copy_action = Pireal.get_action("copy_action")
        copy_action.setEnabled(value)
Esempio n. 47
0
class ImportImage(QWidget):
    def icon():
        return QIcon(':/image_20')
    ACTION_TEXT = 'Import Image'
    def pick(on_ok, settings):
        open_file_sticky('Open FITS Image',FITS_IMG_EXTS, on_ok, settings, IMPORT_IMG )
    
    def __init__(self, fits_file, settings, project = None):
        super(ImportImage, self).__init__()
        self.settings = settings
        self.fits_file = fits_file
        self.project = project
        try:
            image_hdu_index = fits_file.index_of('IMAGE')
        except KeyError:
            image_hdu_index = 0
        
        original_image = fits.ImageHDU(data=fits_file[image_hdu_index].data, header=fits_file[image_hdu_index].header, name='IMAGE')
        for hdu in [h for h in self.fits_file if h.name == 'IMAGE']: self.fits_file.remove(hdu)
        self.fits_file.append(original_image)
        
        self.ui = Ui_ImportImage()
        self.ui.setupUi(self)
        
        self.rotate_dialog = RotateImageDialog(self.fits_file, image_hdu_index, project=project)
        self.rotate_dialog.rotated.connect(self.rotated)
        
        self.image_plot = QtCommons.nestWidget(self.ui.image_widget, QImPlotWidget(self.rotate_dialog.data_rotated, cmap='gray'))
        self.spatial_plot = QtCommons.nestWidget(self.ui.spatial_plot_widget, QMathPlotWidget())
        self.spectrum_plot = QtCommons.nestWidget(self.ui.spectrum_plot_widget, QMathPlotWidget())
        
        self.image_view = self.image_plot.axes_image
        
        self.toolbar = QToolBar('Image Toolbar')
        self.toolbar.addAction(QIcon(':/rotate_20'), "Rotate", lambda: self.rotate_dialog.show())
        self.toolbar.addAction(QIcon(':/save_20'), "Save", self.save_profile)
        self.toolbar.addAction(QIcon(':/select_all_20'), "Select spectrum data", lambda: self.spatial_plot.add_span_selector('select_spectrum', self.spectrum_span_selected,direction='horizontal'))
        self.toolbar.addAction(QIcon.fromTheme('edit-select-invert'), "Select background data", lambda: self.spatial_plot.add_span_selector('select_background', self.background_span_selected,direction='horizontal', rectprops = dict(facecolor='blue', alpha=0.5))).setEnabled(False)
        #self.toolbar.addAction('Stack', self.show_stack_images_dialog)
        self.toolbar.addSeparator()
        self.object_properties = ObjectProperties(self.fits_file, project=project)
        self.object_properties_dialog = ObjectPropertiesDialog(settings, self.object_properties)
        self.toolbar.addAction("Object properties", self.object_properties_dialog.show)
        self.rotated()
        
    def rotated(self):
        self.image_view.set_data(self.rotate_dialog.data_rotated)
        self.image_view.axes.relim() 
        self.image_view.axes.autoscale_view() 
        self.image_view.set_extent([self.rotate_dialog.data_rotated.shape[1],0, self.rotate_dialog.data_rotated.shape[0],0])
        self.image_view.figure.canvas.draw()
        self.draw_plot(self.spectrum_plot.axes, self.spectrum_profile())
        self.draw_plot(self.spatial_plot.axes, self.spatial_profile())
        
    def background_span_selected(self, min, max):
        self.background_span_selection = (min, max)
        self.spatial_plot.add_span('background_window', min, max, 'v', facecolor='gray', alpha=0.5)
        self.image_plot.add_span('background_window', min, max, 'h', facecolor='red', alpha=0.5, clip_on=True)
        self.draw_plot(self.spectrum_plot.axes, self.spectrum_profile())
        
        
    def spectrum_span_selected(self, min, max):
        self.spectrum_span_selection = (min, max)
        self.spatial_plot.add_span('spectrum_window', min, max, 'v', facecolor='g', alpha=0.5)
        self.image_plot.add_span('spectrum_window', min, max, 'h', facecolor='y', alpha=0.25, clip_on=True)
        self.draw_plot(self.spectrum_plot.axes, self.spectrum_profile())
        
    def draw_plot(self, axes, data):
        axes.clear()
        axes.plot(data)
        axes.figure.tight_layout()
        axes.figure.canvas.draw()
        
    def spatial_profile(self):
        return self.rotate_dialog.data_rotated.sum(1)
    
    def spectrum_profile(self):
        return self.rotate_dialog.data_rotated[self.spectrum_span_selection[0]:self.spectrum_span_selection[1]+1,:].sum(0) if hasattr(self, 'spectrum_span_selection') else self.rotate_dialog.data_rotated.sum(0)
                
    def save(self, save_file):
        data = self.spectrum_profile()
        data -= np.amin(data)
        data /= np.amax(data)
        hdu = self.fits_file[0]
        hdu.data = data
        hdu.header['ORIGIN'] = 'PySpectrum'
        self.fits_file.writeto(save_file, clobber=True)
        
    def save_profile(self):
        if not self.project:
            save_file_sticky('Save plot...', 'FITS file (.fit)', lambda f: self.save(f[0]), self.settings, RAW_PROFILE )
            return
        if not self.object_properties.name:
            QMessageBox.information(self, 'Save FITS', 'Please set file information (name, date, etc) using the Object Properties button before saving')
            return
        file_path = self.project.add_file(Project.RAW_PROFILE, object_properties = self.object_properties, on_added=self.save)
Esempio n. 48
0
class AppWindow(QMainWindow):
	"The application's main window"
	def __init__(self):
		super().__init__()
		self.center = QWidget()
		self.display = QStackedLayout()
		self.center.setLayout(self.display)
		# init the manga view variables
		self.manga_display()
		# init the chapter view variables
		self.chapter_display()
		# init toolbar
		self.init_toolbar()
		# init status bar
		self.init_stat_bar()

		self.display.addWidget(self.manga_main)
		self.display.addWidget(self.chapter_main)

		self.setCentralWidget(self.center)
		self.setWindowTitle("Happypanda")
		self.resize(1029, 650)
		self.show()
	
	def init_stat_bar(self):
		self.status_bar = self.statusBar()
		self.status_bar.setMaximumHeight(20)
		self.status_bar.setSizeGripEnabled(False)
		self.stat_info = QLabel()
		self.stat_info.setIndent(5)
		self.sort_main = QAction("Asc", self)
		sort_menu = QMenu()
		self.sort_main.setMenu(sort_menu)
		s_by_title = QAction("Title", sort_menu)
		s_by_artist = QAction("Artist", sort_menu)
		sort_menu.addAction(s_by_title)
		sort_menu.addAction(s_by_artist)
		self.status_bar.addPermanentWidget(self.stat_info)
		#self.status_bar.addAction(self.sort_main)
		self.temp_msg = QLabel()
		self.temp_timer = QTimer()
		self.manga_list_view.series_model.ROWCOUNT_CHANGE.connect(self.stat_row_info)
		self.manga_list_view.series_model.STATUSBAR_MSG.connect(self.stat_temp_msg)

	def stat_temp_msg(self, msg):
		self.temp_timer.stop()
		self.temp_msg.setText(msg)
		self.status_bar.addWidget(self.temp_msg)
		self.temp_timer.timeout.connect(self.temp_msg.clear)
		self.temp_timer.setSingleShot(True)
		self.temp_timer.start(5000)

	def stat_row_info(self):
		r = self.manga_list_view.series_model.rowCount()
		t = len(self.manga_list_view.series_model._data)
		self.stat_info.setText("<b>Showing {} of {} </b>".format(r, t))

	def manga_display(self):
		"initiates the manga view"
		self.manga_main = QWidget()
		self.manga_main.setContentsMargins(-10, -12, -10, -10)
		self.manga_view = QHBoxLayout()
		self.manga_main.setLayout(self.manga_view)

		manga_delegate = series.CustomDelegate()
		manga_delegate.BUTTON_CLICKED.connect(self.setCurrentIndex)
		self.manga_list_view = series.MangaView()
		self.manga_list_view.setItemDelegate(manga_delegate)
		self.manga_view.addWidget(self.manga_list_view)

	def favourite_display(self):
		"initiates favourite display"
		pass

	def chapter_display(self):
		"Initiates chapter view"
		self.chapter_main = QWidget()
		self.chapter_main.setObjectName("chapter_main") # to allow styling this object
		self.chapter_layout = QHBoxLayout()
		self.chapter_main.setLayout(self.chapter_layout)

		#self.chapter_info.setContentsMargins(-8,-7,-7,-7)
		#self.chapter_info.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
		self.chapter_info_view = series.ChapterInfo()
		self.chapter_layout.addWidget(self.chapter_info_view)

		chapter_list_view = series.ChapterView()
		self.chapter_layout.addWidget(chapter_list_view)
		#self.chapter.setCollapsible(0, True)
		#self.chapter.setCollapsible(1, False)

	def init_toolbar(self):
		self.toolbar = QToolBar()
		self.toolbar.setFixedHeight(30)
		self.toolbar.setWindowTitle("Show") # text for the contextmenu
		#self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined?
		self.toolbar.setMovable(False)
		self.toolbar.setFloatable(False)
		#self.toolbar.setIconSize(QSize(20,20))
		self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

		spacer_start = QWidget() # aligns the first actions properly
		spacer_start.setFixedSize(QSize(10, 1))
		self.toolbar.addWidget(spacer_start)

		favourite_view_icon = QIcon(gui_constants.STAR_BTN_PATH)
		favourite_view_action = QAction(favourite_view_icon, "Favourite", self)
		#favourite_view_action.setText("Manga View")
		favourite_view_action.triggered.connect(lambda: self.setCurrentIndex(1)) #need lambda to pass extra args
		self.toolbar.addAction(favourite_view_action)

		catalog_view_icon = QIcon(gui_constants.HOME_BTN_PATH)
		catalog_view_action = QAction(catalog_view_icon, "Library", self)
		#catalog_view_action.setText("Catalog")
		catalog_view_action.triggered.connect(lambda: self.setCurrentIndex(0)) #need lambda to pass extra args
		self.toolbar.addAction(catalog_view_action)
		self.toolbar.addSeparator()

		series_icon = QIcon(gui_constants.PLUS_PATH)
		series_action = QAction(series_icon, "Add series...", self)
		series_action.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit)
		series_menu = QMenu()
		series_menu.addSeparator()
		populate_action = QAction("Populate from folder...", self)
		populate_action.triggered.connect(self.populate)
		series_menu.addAction(populate_action)
		series_action.setMenu(series_menu)
		self.toolbar.addAction(series_action)

		spacer_middle = QWidget() # aligns buttons to the right
		spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
		self.toolbar.addWidget(spacer_middle)
		
		self.search_bar = QLineEdit()
		self.search_bar.setPlaceholderText("Search title, artist, genres")
		self.search_bar.setMaximumWidth(200)
		self.toolbar.addWidget(self.search_bar)
		self.toolbar.addSeparator()
		settings_icon = QIcon(gui_constants.SETTINGS_PATH)
		settings_action = QAction(settings_icon, "Set&tings", self)
		self.toolbar.addAction(settings_action)
		self.addToolBar(self.toolbar)
		
		spacer_end = QWidget() # aligns About action properly
		spacer_end.setFixedSize(QSize(10, 1))
		self.toolbar.addWidget(spacer_end)

	def setCurrentIndex(self, number, index=None):
		"""Changes the current display view.
		Params:
			number <- int (0 for manga view, 1 for chapter view
		Optional:
			index <- QModelIndex for chapter view
		Note: 0-based indexing
		"""
		if index is not None:
			self.chapter_info_view.display_manga(index)
			self.display.setCurrentIndex(number)
		else:
			self.display.setCurrentIndex(number)

	# TODO: Improve this so that it adds to the series dialog,
	# so user can edit data before inserting (make it a choice)
	def populate(self):
		"Populates the database with series from local drive'"
		msgbox = QMessageBox()
		msgbox.setText("<font color='red'><b>Use with care.</b></font> Choose a folder containing all your series'.")
		msgbox.setInformativeText("Oniichan, are you sure you want to do this?")
		msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
		msgbox.setDefaultButton(QMessageBox.No)
		if msgbox.exec() == QMessageBox.Yes:
			path = QFileDialog.getExistingDirectory(None, "Choose a folder containing your series'")
			if len(path) is not 0:
				data_thread = QThread()
				loading_thread = QThread()
				loading = misc.Loading()

				if not loading.ON:
					misc.Loading.ON = True
					fetch_instance = fetch.Fetch()
					fetch_instance.series_path = path
					loading.show()

					def finished(status):
						if status:
							self.manga_list_view.series_model.populate_data()
							# TODO: make it spawn a dialog instead (from utils.py or misc.py)
							if loading.progress.maximum() == loading.progress.value():
								misc.Loading.ON = False
								loading.hide()
							data_thread.quit
						else:
							loading.setText("<font color=red>An error occured. Try restarting..</font>")
							loading.progress.setStyleSheet("background-color:red")
							data_thread.quit

					def fetch_deleteLater():
						try:
							fetch_instance.deleteLater
						except NameError:
							pass

					def thread_deleteLater(): #NOTE: Isn't this bad?
						data_thread.deleteLater

					def a_progress(prog):
						loading.progress.setValue(prog)
						loading.setText("Searching on local disk...\n(Will take a while on first time)")

					fetch_instance.moveToThread(data_thread)
					fetch_instance.DATA_COUNT.connect(loading.progress.setMaximum)
					fetch_instance.PROGRESS.connect(a_progress)
					data_thread.started.connect(fetch_instance.local)
					fetch_instance.FINISHED.connect(finished)
					fetch_instance.FINISHED.connect(fetch_deleteLater)
					fetch_instance.FINISHED.connect(thread_deleteLater)
					data_thread.start()
Esempio n. 49
0
class ImageViewer(QMainWindow):
	def __init__(self, db, *args, **kwargs):
		super(ImageViewer, self).__init__(*args, **kwargs)

		self.db = db
		self.currentIndex = -1
		self.files = []

		self._init_widgets()

	def _init_widgets(self):
		#self.toolbar = AutoHideToolBar()
		self.toolbar = QToolBar()
		self.addToolBar(self.toolbar)
		self.toolbar.hide()

		act = self.toolbar.addAction(QIcon.fromTheme('go-previous'), 'Previous')
		act.setShortcut(QKeySequence(Qt.Key_Backspace))
		act.triggered.connect(self.showPreviousFile)
		act = self.toolbar.addAction(QIcon.fromTheme('go-next'), 'Next')
		act.setShortcut(QKeySequence(Qt.Key_Space))
		act.triggered.connect(self.showNextFile)
		self.toolbar.addSeparator()

		self.toolbar.addAction(QIcon.fromTheme('zoom-original'), 'Z 1:1').triggered.connect(self.doNormalZoom)
		self.toolbar.addAction(QIcon.fromTheme('zoom-fit-best'), 'Z Fit').triggered.connect(self.doFitAllZoom)
		self.toolbar.addAction(QIcon.fromTheme('zoom-fit-best'), 'Z FitExp').triggered.connect(self.doFitCutZoom)
		self.toolbar.addAction(QIcon.fromTheme('zoom-in'), 'Z x1.5').triggered.connect(self.zoom)
		self.toolbar.addAction(QIcon.fromTheme('zoom-out'), 'Z /1.5').triggered.connect(self.unzoom)

		self.fullscreenAction = self.toolbar.addAction(QIcon.fromTheme('view-fullscreen'), 'Fullscreen')
		self.fullscreenAction.setCheckable(True)
		self.fullscreenAction.toggled.connect(self.setFullscreen)
		self.toolbar.addSeparator()

		self.toolbar.addAction('Copy tags').triggered.connect(self.copyPreviousTags)

		self.tageditor = TagEditor(self.db)
		self.docktagger = AutoHideDock()
		self.docktagger.setWidget(self.tageditor)
		self.addDockWidget(Qt.LeftDockWidgetArea, self.docktagger)
		self.docktagger.hide()

		self.scrollview = ImageViewerCenter()
		self.scrollview.installEventFilter(self) ### !
		self.setCentralWidget(self.scrollview)

		self.scrollview.topZoneEntered.connect(self.toolbar.show)
		self.scrollview.topZoneLeft.connect(self.toolbar.hide)
		self.scrollview.leftZoneEntered.connect(self.docktagger.show)
		self.scrollview.leftZoneLeft.connect(self.docktagger.hide)

		#~ self.setWindowState(self.windowState() | Qt.WindowMaximized)

		'''
		self.qtagwl = QListWidget()
		self.qtagwl.setParent(self)
		self.qtagwl.hide()
		#self.qtagwl.setFixedSize(self.qtagwl.minimumSizeHint())
		self.qtagwl.setFrameShape(QFrame.NoFrame)
		self.qtagwl.setStyleSheet('QListWidget{background-color: rgba(255,255,255,200);}\n *{background-color:rgba(0,255,255,255);}')
		self.qtagwl.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)

		self.qthtimer = QTimer()
		self.connect(self.qthtimer, SIGNAL('timeout()'), self.qtaghide)
		'''

	def eventFilter(self, sview, ev):
		if ev.type() == QEvent.KeyPress:
			if ev.key() == Qt.Key_Escape:
				self.fullscreenAction.setChecked(False)
				return True
			elif ev.key() in [Qt.Key_PageUp, Qt.Key_Backspace]: # qactions
				self.showPreviousFile()
				return True
			elif ev.key() in [Qt.Key_PageDown, Qt.Key_Space]:
				self.showNextFile()
				return True
		return super(ImageViewer, self).eventFilter(sview, ev)

	@Slot()
	def doNormalZoom(self):
		self.scrollview.setZoomFactor(1.)

	@Slot()
	def doFitAllZoom(self):
		self.scrollview.setZoomMode(ZOOM_FITALL)

	@Slot()
	def doFitCutZoom(self):
		self.scrollview.setZoomMode(ZOOM_FITCUT)

	@Slot()
	def zoom(self):
		self.scrollview.multiplyZoomFactor(1.5)

	@Slot()
	def unzoom(self):
		self.scrollview.multiplyZoomFactor(1/1.5)

	def spawn(self, files, currentFile):
		self.files = files
		self.currentIndex = files.index(currentFile)

		self.setFile(currentFile)
		if self.isHidden():
			#~ self.setWindowState(self.windowState() | Qt.WindowMaximized)
			#~ self.show()
			self.fullscreenAction.setChecked(False)
			self.fullscreenAction.setChecked(True)
			#~ self.showMaximized()
		else:
			self.show()

	def setFile(self, file):
		self.tageditor.setFile(file)
		self.scrollview.setFile(file)

	@Slot()
	def copyPreviousTags(self):
		tags = self.db.find_tags_by_file(self.files[self.currentIndex - 1])
		with self.db:
			self.db.tag_file(self.files[self.currentIndex], tags)
		self.tageditor.setFile(self.files[self.currentIndex])

	def setFullscreen(self, full):
		if full:
			self.showFullScreen()
		else:
			self.showNormal()

	@Slot()
	def showPreviousFile(self):
		if self.currentIndex > 0:
			self.currentIndex -= 1
			self.setFile(self.files[self.currentIndex])

	@Slot()
	def showNextFile(self):
		if self.currentIndex < len(self.files) - 1:
			self.currentIndex += 1
			self.setFile(self.files[self.currentIndex])
Esempio n. 50
0
class SvnProjectHelper(VcsProjectHelper):
    """
    Class implementing the VCS project helper for Subversion.
    """
    def __init__(self, vcsObject, projectObject, parent=None, name=None):
        """
        Constructor
        
        @param vcsObject reference to the vcs object
        @param projectObject reference to the project object
        @param parent parent widget (QWidget)
        @param name name of this object (string)
        """
        VcsProjectHelper.__init__(self, vcsObject, projectObject, parent, name)
        
    def getActions(self):
        """
        Public method to get a list of all actions.
        
        @return list of all actions (list of E5Action)
        """
        return self.actions[:]
        
    def initActions(self):
        """
        Public method to generate the action objects.
        """
        self.vcsNewAct = E5Action(
            self.tr('New from repository'),
            UI.PixmapCache.getIcon("vcsCheckout.png"),
            self.tr('&New from repository...'), 0, 0, self,
            'subversion_new')
        self.vcsNewAct.setStatusTip(self.tr(
            'Create a new project from the VCS repository'
        ))
        self.vcsNewAct.setWhatsThis(self.tr(
            """<b>New from repository</b>"""
            """<p>This creates a new local project from the VCS"""
            """ repository.</p>"""
        ))
        self.vcsNewAct.triggered.connect(self._vcsCheckout)
        self.actions.append(self.vcsNewAct)
        
        self.vcsUpdateAct = E5Action(
            self.tr('Update from repository'),
            UI.PixmapCache.getIcon("vcsUpdate.png"),
            self.tr('&Update from repository'), 0, 0, self,
            'subversion_update')
        self.vcsUpdateAct.setStatusTip(self.tr(
            'Update the local project from the VCS repository'
        ))
        self.vcsUpdateAct.setWhatsThis(self.tr(
            """<b>Update from repository</b>"""
            """<p>This updates the local project from the VCS"""
            """ repository.</p>"""
        ))
        self.vcsUpdateAct.triggered.connect(self._vcsUpdate)
        self.actions.append(self.vcsUpdateAct)
        
        self.vcsCommitAct = E5Action(
            self.tr('Commit changes to repository'),
            UI.PixmapCache.getIcon("vcsCommit.png"),
            self.tr('&Commit changes to repository...'), 0, 0, self,
            'subversion_commit')
        self.vcsCommitAct.setStatusTip(self.tr(
            'Commit changes to the local project to the VCS repository'
        ))
        self.vcsCommitAct.setWhatsThis(self.tr(
            """<b>Commit changes to repository</b>"""
            """<p>This commits changes to the local project to the VCS"""
            """ repository.</p>"""
        ))
        self.vcsCommitAct.triggered.connect(self._vcsCommit)
        self.actions.append(self.vcsCommitAct)
        
        self.vcsLogAct = E5Action(
            self.tr('Show log'),
            UI.PixmapCache.getIcon("vcsLog.png"),
            self.tr('Show &log'),
            0, 0, self, 'subversion_log')
        self.vcsLogAct.setStatusTip(self.tr(
            'Show the log of the local project'
        ))
        self.vcsLogAct.setWhatsThis(self.tr(
            """<b>Show log</b>"""
            """<p>This shows the log of the local project.</p>"""
        ))
        self.vcsLogAct.triggered.connect(self._vcsLog)
        self.actions.append(self.vcsLogAct)
        
        self.svnLogBrowserAct = E5Action(
            self.tr('Show log browser'),
            UI.PixmapCache.getIcon("vcsLog.png"),
            self.tr('Show log browser'),
            0, 0, self, 'subversion_log_browser')
        self.svnLogBrowserAct.setStatusTip(self.tr(
            'Show a dialog to browse the log of the local project'
        ))
        self.svnLogBrowserAct.setWhatsThis(self.tr(
            """<b>Show log browser</b>"""
            """<p>This shows a dialog to browse the log of the local"""
            """ project. A limited number of entries is shown first. More"""
            """ can be retrieved later on.</p>"""
        ))
        self.svnLogBrowserAct.triggered.connect(self._vcsLogBrowser)
        self.actions.append(self.svnLogBrowserAct)
        
        self.vcsDiffAct = E5Action(
            self.tr('Show differences'),
            UI.PixmapCache.getIcon("vcsDiff.png"),
            self.tr('Show &difference'),
            0, 0, self, 'subversion_diff')
        self.vcsDiffAct.setStatusTip(self.tr(
            'Show the difference of the local project to the repository'
        ))
        self.vcsDiffAct.setWhatsThis(self.tr(
            """<b>Show differences</b>"""
            """<p>This shows differences of the local project to the"""
            """ repository.</p>"""
        ))
        self.vcsDiffAct.triggered.connect(self._vcsDiff)
        self.actions.append(self.vcsDiffAct)
        
        self.svnExtDiffAct = E5Action(
            self.tr('Show differences (extended)'),
            UI.PixmapCache.getIcon("vcsDiff.png"),
            self.tr('Show differences (extended)'),
            0, 0, self, 'subversion_extendeddiff')
        self.svnExtDiffAct.setStatusTip(self.tr(
            'Show the difference of revisions of the project to the repository'
        ))
        self.svnExtDiffAct.setWhatsThis(self.tr(
            """<b>Show differences (extended)</b>"""
            """<p>This shows differences of selectable revisions of"""
            """ the project.</p>"""
        ))
        self.svnExtDiffAct.triggered.connect(self.__svnExtendedDiff)
        self.actions.append(self.svnExtDiffAct)
        
        self.svnUrlDiffAct = E5Action(
            self.tr('Show differences (URLs)'),
            UI.PixmapCache.getIcon("vcsDiff.png"),
            self.tr('Show differences (URLs)'),
            0, 0, self, 'subversion_urldiff')
        self.svnUrlDiffAct.setStatusTip(self.tr(
            'Show the difference of the project between two repository URLs'
        ))
        self.svnUrlDiffAct.setWhatsThis(self.tr(
            """<b>Show differences (URLs)</b>"""
            """<p>This shows differences of the project between"""
            """ two repository URLs.</p>"""
        ))
        self.svnUrlDiffAct.triggered.connect(self.__svnUrlDiff)
        self.actions.append(self.svnUrlDiffAct)
        
        self.vcsStatusAct = E5Action(
            self.tr('Show status'),
            UI.PixmapCache.getIcon("vcsStatus.png"),
            self.tr('Show &status'),
            0, 0, self, 'subversion_status')
        self.vcsStatusAct.setStatusTip(self.tr(
            'Show the status of the local project'
        ))
        self.vcsStatusAct.setWhatsThis(self.tr(
            """<b>Show status</b>"""
            """<p>This shows the status of the local project.</p>"""
        ))
        self.vcsStatusAct.triggered.connect(self._vcsStatus)
        self.actions.append(self.vcsStatusAct)
        
        self.svnChangeListsAct = E5Action(
            self.tr('Show change lists'),
            UI.PixmapCache.getIcon("vcsChangeLists.png"),
            self.tr('Show change lists'),
            0, 0, self, 'subversion_changelists')
        self.svnChangeListsAct.setStatusTip(self.tr(
            'Show the change lists and associated files of the local project'
        ))
        self.svnChangeListsAct.setWhatsThis(self.tr(
            """<b>Show change lists</b>"""
            """<p>This shows the change lists and associated files of the"""
            """ local project.</p>"""
        ))
        self.svnChangeListsAct.triggered.connect(self.__svnChangeLists)
        self.actions.append(self.svnChangeListsAct)
        
        self.vcsTagAct = E5Action(
            self.tr('Tag in repository'),
            UI.PixmapCache.getIcon("vcsTag.png"),
            self.tr('&Tag in repository...'),
            0, 0, self, 'subversion_tag')
        self.vcsTagAct.setStatusTip(self.tr(
            'Tag the local project in the repository'
        ))
        self.vcsTagAct.setWhatsThis(self.tr(
            """<b>Tag in repository</b>"""
            """<p>This tags the local project in the repository.</p>"""
        ))
        self.vcsTagAct.triggered.connect(self._vcsTag)
        self.actions.append(self.vcsTagAct)
        
        self.vcsExportAct = E5Action(
            self.tr('Export from repository'),
            UI.PixmapCache.getIcon("vcsExport.png"),
            self.tr('&Export from repository...'),
            0, 0, self, 'subversion_export')
        self.vcsExportAct.setStatusTip(self.tr(
            'Export a project from the repository'
        ))
        self.vcsExportAct.setWhatsThis(self.tr(
            """<b>Export from repository</b>"""
            """<p>This exports a project from the repository.</p>"""
        ))
        self.vcsExportAct.triggered.connect(self._vcsExport)
        self.actions.append(self.vcsExportAct)
        
        self.vcsPropsAct = E5Action(
            self.tr('Command options'),
            self.tr('Command &options...'), 0, 0, self,
            'subversion_options')
        self.vcsPropsAct.setStatusTip(self.tr(
            'Show the VCS command options'))
        self.vcsPropsAct.setWhatsThis(self.tr(
            """<b>Command options...</b>"""
            """<p>This shows a dialog to edit the VCS command options.</p>"""
        ))
        self.vcsPropsAct.triggered.connect(self._vcsCommandOptions)
        self.actions.append(self.vcsPropsAct)
        
        self.vcsRevertAct = E5Action(
            self.tr('Revert changes'),
            UI.PixmapCache.getIcon("vcsRevert.png"),
            self.tr('Re&vert changes'),
            0, 0, self, 'subversion_revert')
        self.vcsRevertAct.setStatusTip(self.tr(
            'Revert all changes made to the local project'
        ))
        self.vcsRevertAct.setWhatsThis(self.tr(
            """<b>Revert changes</b>"""
            """<p>This reverts all changes made to the local project.</p>"""
        ))
        self.vcsRevertAct.triggered.connect(self._vcsRevert)
        self.actions.append(self.vcsRevertAct)
        
        self.vcsMergeAct = E5Action(
            self.tr('Merge'),
            UI.PixmapCache.getIcon("vcsMerge.png"),
            self.tr('Mer&ge changes...'),
            0, 0, self, 'subversion_merge')
        self.vcsMergeAct.setStatusTip(self.tr(
            'Merge changes of a tag/revision into the local project'
        ))
        self.vcsMergeAct.setWhatsThis(self.tr(
            """<b>Merge</b>"""
            """<p>This merges changes of a tag/revision into the local"""
            """ project.</p>"""
        ))
        self.vcsMergeAct.triggered.connect(self._vcsMerge)
        self.actions.append(self.vcsMergeAct)
        
        self.vcsSwitchAct = E5Action(
            self.tr('Switch'),
            UI.PixmapCache.getIcon("vcsSwitch.png"),
            self.tr('S&witch...'),
            0, 0, self, 'subversion_switch')
        self.vcsSwitchAct.setStatusTip(self.tr(
            'Switch the local copy to another tag/branch'
        ))
        self.vcsSwitchAct.setWhatsThis(self.tr(
            """<b>Switch</b>"""
            """<p>This switches the local copy to another tag/branch.</p>"""
        ))
        self.vcsSwitchAct.triggered.connect(self._vcsSwitch)
        self.actions.append(self.vcsSwitchAct)
        
        self.vcsResolveAct = E5Action(
            self.tr('Conflicts resolved'),
            self.tr('Con&flicts resolved'),
            0, 0, self, 'subversion_resolve')
        self.vcsResolveAct.setStatusTip(self.tr(
            'Mark all conflicts of the local project as resolved'
        ))
        self.vcsResolveAct.setWhatsThis(self.tr(
            """<b>Conflicts resolved</b>"""
            """<p>This marks all conflicts of the local project as"""
            """ resolved.</p>"""
        ))
        self.vcsResolveAct.triggered.connect(self.__svnResolve)
        self.actions.append(self.vcsResolveAct)
        
        self.vcsCleanupAct = E5Action(
            self.tr('Cleanup'),
            self.tr('Cleanu&p'),
            0, 0, self, 'subversion_cleanup')
        self.vcsCleanupAct.setStatusTip(self.tr(
            'Cleanup the local project'
        ))
        self.vcsCleanupAct.setWhatsThis(self.tr(
            """<b>Cleanup</b>"""
            """<p>This performs a cleanup of the local project.</p>"""
        ))
        self.vcsCleanupAct.triggered.connect(self._vcsCleanup)
        self.actions.append(self.vcsCleanupAct)
        
        self.vcsCommandAct = E5Action(
            self.tr('Execute command'),
            self.tr('E&xecute command...'),
            0, 0, self, 'subversion_command')
        self.vcsCommandAct.setStatusTip(self.tr(
            'Execute an arbitrary VCS command'
        ))
        self.vcsCommandAct.setWhatsThis(self.tr(
            """<b>Execute command</b>"""
            """<p>This opens a dialog to enter an arbitrary VCS command.</p>"""
        ))
        self.vcsCommandAct.triggered.connect(self._vcsCommand)
        self.actions.append(self.vcsCommandAct)
        
        self.svnTagListAct = E5Action(
            self.tr('List tags'),
            self.tr('List tags...'),
            0, 0, self, 'subversion_list_tags')
        self.svnTagListAct.setStatusTip(self.tr(
            'List tags of the project'
        ))
        self.svnTagListAct.setWhatsThis(self.tr(
            """<b>List tags</b>"""
            """<p>This lists the tags of the project.</p>"""
        ))
        self.svnTagListAct.triggered.connect(self.__svnTagList)
        self.actions.append(self.svnTagListAct)
        
        self.svnBranchListAct = E5Action(
            self.tr('List branches'),
            self.tr('List branches...'),
            0, 0, self, 'subversion_list_branches')
        self.svnBranchListAct.setStatusTip(self.tr(
            'List branches of the project'
        ))
        self.svnBranchListAct.setWhatsThis(self.tr(
            """<b>List branches</b>"""
            """<p>This lists the branches of the project.</p>"""
        ))
        self.svnBranchListAct.triggered.connect(self.__svnBranchList)
        self.actions.append(self.svnBranchListAct)
        
        self.svnListAct = E5Action(
            self.tr('List repository contents'),
            self.tr('List repository contents...'),
            0, 0, self, 'subversion_contents')
        self.svnListAct.setStatusTip(self.tr(
            'Lists the contents of the repository'
        ))
        self.svnListAct.setWhatsThis(self.tr(
            """<b>List repository contents</b>"""
            """<p>This lists the contents of the repository.</p>"""
        ))
        self.svnListAct.triggered.connect(self.__svnTagList)
        self.actions.append(self.svnListAct)
        
        self.svnPropSetAct = E5Action(
            self.tr('Set Property'),
            self.tr('Set Property...'),
            0, 0, self, 'subversion_property_set')
        self.svnPropSetAct.setStatusTip(self.tr(
            'Set a property for the project files'
        ))
        self.svnPropSetAct.setWhatsThis(self.tr(
            """<b>Set Property</b>"""
            """<p>This sets a property for the project files.</p>"""
        ))
        self.svnPropSetAct.triggered.connect(self.__svnPropSet)
        self.actions.append(self.svnPropSetAct)
        
        self.svnPropListAct = E5Action(
            self.tr('List Properties'),
            self.tr('List Properties...'),
            0, 0, self, 'subversion_property_list')
        self.svnPropListAct.setStatusTip(self.tr(
            'List properties of the project files'
        ))
        self.svnPropListAct.setWhatsThis(self.tr(
            """<b>List Properties</b>"""
            """<p>This lists the properties of the project files.</p>"""
        ))
        self.svnPropListAct.triggered.connect(self.__svnPropList)
        self.actions.append(self.svnPropListAct)
        
        self.svnPropDelAct = E5Action(
            self.tr('Delete Property'),
            self.tr('Delete Property...'),
            0, 0, self, 'subversion_property_delete')
        self.svnPropDelAct.setStatusTip(self.tr(
            'Delete a property for the project files'
        ))
        self.svnPropDelAct.setWhatsThis(self.tr(
            """<b>Delete Property</b>"""
            """<p>This deletes a property for the project files.</p>"""
        ))
        self.svnPropDelAct.triggered.connect(self.__svnPropDel)
        self.actions.append(self.svnPropDelAct)
        
        self.svnRelocateAct = E5Action(
            self.tr('Relocate'),
            UI.PixmapCache.getIcon("vcsSwitch.png"),
            self.tr('Relocate...'),
            0, 0, self, 'subversion_relocate')
        self.svnRelocateAct.setStatusTip(self.tr(
            'Relocate the working copy to a new repository URL'
        ))
        self.svnRelocateAct.setWhatsThis(self.tr(
            """<b>Relocate</b>"""
            """<p>This relocates the working copy to a new repository"""
            """ URL.</p>"""
        ))
        self.svnRelocateAct.triggered.connect(self.__svnRelocate)
        self.actions.append(self.svnRelocateAct)
        
        self.svnRepoBrowserAct = E5Action(
            self.tr('Repository Browser'),
            UI.PixmapCache.getIcon("vcsRepoBrowser.png"),
            self.tr('Repository Browser...'),
            0, 0, self, 'subversion_repo_browser')
        self.svnRepoBrowserAct.setStatusTip(self.tr(
            'Show the Repository Browser dialog'
        ))
        self.svnRepoBrowserAct.setWhatsThis(self.tr(
            """<b>Repository Browser</b>"""
            """<p>This shows the Repository Browser dialog.</p>"""
        ))
        self.svnRepoBrowserAct.triggered.connect(self.__svnRepoBrowser)
        self.actions.append(self.svnRepoBrowserAct)
        
        self.svnConfigAct = E5Action(
            self.tr('Configure'),
            self.tr('Configure...'),
            0, 0, self, 'subversion_configure')
        self.svnConfigAct.setStatusTip(self.tr(
            'Show the configuration dialog with the Subversion page selected'
        ))
        self.svnConfigAct.setWhatsThis(self.tr(
            """<b>Configure</b>"""
            """<p>Show the configuration dialog with the Subversion page"""
            """ selected.</p>"""
        ))
        self.svnConfigAct.triggered.connect(self.__svnConfigure)
        self.actions.append(self.svnConfigAct)
        
        self.svnUpgradeAct = E5Action(
            self.tr('Upgrade'),
            self.tr('Upgrade...'),
            0, 0, self, 'subversion_upgrade')
        self.svnUpgradeAct.setStatusTip(self.tr(
            'Upgrade the working copy to the current format'
        ))
        self.svnUpgradeAct.setWhatsThis(self.tr(
            """<b>Upgrade</b>"""
            """<p>Upgrades the working copy to the current format.</p>"""
        ))
        self.svnUpgradeAct.triggered.connect(self.__svnUpgrade)
        self.actions.append(self.svnUpgradeAct)
    
    def initMenu(self, menu):
        """
        Public method to generate the VCS menu.
        
        @param menu reference to the menu to be populated (QMenu)
        """
        menu.clear()
        
        act = menu.addAction(
            UI.PixmapCache.getIcon(
                os.path.join("VcsPlugins", "vcsSubversion", "icons",
                             "subversion.png")),
            self.vcs.vcsName(), self._vcsInfoDisplay)
        font = act.font()
        font.setBold(True)
        act.setFont(font)
        menu.addSeparator()
        
        menu.addAction(self.vcsUpdateAct)
        menu.addAction(self.vcsCommitAct)
        menu.addSeparator()
        menu.addAction(self.vcsTagAct)
        if self.vcs.otherData["standardLayout"]:
            menu.addAction(self.svnTagListAct)
            menu.addAction(self.svnBranchListAct)
        else:
            menu.addAction(self.svnListAct)
        menu.addSeparator()
        menu.addAction(self.vcsLogAct)
        menu.addAction(self.svnLogBrowserAct)
        menu.addSeparator()
        menu.addAction(self.vcsStatusAct)
        menu.addAction(self.svnChangeListsAct)
        menu.addSeparator()
        menu.addAction(self.vcsDiffAct)
        menu.addAction(self.svnExtDiffAct)
        menu.addAction(self.svnUrlDiffAct)
        menu.addSeparator()
        menu.addAction(self.vcsRevertAct)
        menu.addAction(self.vcsMergeAct)
        menu.addAction(self.vcsResolveAct)
        menu.addSeparator()
        menu.addAction(self.svnRelocateAct)
        menu.addAction(self.vcsSwitchAct)
        menu.addSeparator()
        menu.addAction(self.svnPropSetAct)
        menu.addAction(self.svnPropListAct)
        menu.addAction(self.svnPropDelAct)
        menu.addSeparator()
        menu.addAction(self.vcsCleanupAct)
        menu.addSeparator()
        menu.addAction(self.vcsCommandAct)
        menu.addAction(self.svnRepoBrowserAct)
        menu.addAction(self.svnUpgradeAct)
        menu.addSeparator()
        menu.addAction(self.vcsPropsAct)
        menu.addSeparator()
        menu.addAction(self.svnConfigAct)
        menu.addSeparator()
        menu.addAction(self.vcsNewAct)
        menu.addAction(self.vcsExportAct)
    
    def initToolbar(self, ui, toolbarManager):
        """
        Public slot to initialize the VCS toolbar.
        
        @param ui reference to the main window (UserInterface)
        @param toolbarManager reference to a toolbar manager object
            (E5ToolBarManager)
        """
        self.__toolbar = QToolBar(self.tr("Subversion (svn)"), ui)
        self.__toolbar.setIconSize(UI.Config.ToolBarIconSize)
        self.__toolbar.setObjectName("SubversionToolbar")
        self.__toolbar.setToolTip(self.tr('Subversion (svn)'))
        
        self.__toolbar.addAction(self.svnLogBrowserAct)
        self.__toolbar.addAction(self.vcsStatusAct)
        self.__toolbar.addSeparator()
        self.__toolbar.addAction(self.vcsDiffAct)
        self.__toolbar.addSeparator()
        self.__toolbar.addAction(self.svnRepoBrowserAct)
        self.__toolbar.addAction(self.vcsNewAct)
        self.__toolbar.addAction(self.vcsExportAct)
        self.__toolbar.addSeparator()
        
        title = self.__toolbar.windowTitle()
        toolbarManager.addToolBar(self.__toolbar, title)
        toolbarManager.addAction(self.vcsUpdateAct, title)
        toolbarManager.addAction(self.vcsCommitAct, title)
        toolbarManager.addAction(self.vcsLogAct, title)
        toolbarManager.addAction(self.svnExtDiffAct, title)
        toolbarManager.addAction(self.svnUrlDiffAct, title)
        toolbarManager.addAction(self.svnChangeListsAct, title)
        toolbarManager.addAction(self.vcsTagAct, title)
        toolbarManager.addAction(self.vcsRevertAct, title)
        toolbarManager.addAction(self.vcsMergeAct, title)
        toolbarManager.addAction(self.vcsSwitchAct, title)
        toolbarManager.addAction(self.svnRelocateAct, title)
        
        self.__toolbar.setEnabled(False)
        self.__toolbar.setVisible(False)
        
        ui.registerToolbar("subversion", self.__toolbar.windowTitle(),
                           self.__toolbar)
        ui.addToolBar(self.__toolbar)
    
    def removeToolbar(self, ui, toolbarManager):
        """
        Public method to remove a toolbar created by initToolbar().
        
        @param ui reference to the main window (UserInterface)
        @param toolbarManager reference to a toolbar manager object
            (E5ToolBarManager)
        """
        ui.removeToolBar(self.__toolbar)
        ui.unregisterToolbar("subversion")
        
        title = self.__toolbar.windowTitle()
        toolbarManager.removeCategoryActions(title)
        toolbarManager.removeToolBar(self.__toolbar)
        
        self.__toolbar.deleteLater()
        self.__toolbar = None
    
    def __svnResolve(self):
        """
        Private slot used to resolve conflicts of the local project.
        """
        self.vcs.svnResolve(self.project.ppath)
        
    def __svnPropList(self):
        """
        Private slot used to list the properties of the project files.
        """
        self.vcs.svnListProps(self.project.ppath, True)
        
    def __svnPropSet(self):
        """
        Private slot used to set a property for the project files.
        """
        self.vcs.svnSetProp(self.project.ppath, True)
        
    def __svnPropDel(self):
        """
        Private slot used to delete a property for the project files.
        """
        self.vcs.svnDelProp(self.project.ppath, True)
        
    def __svnTagList(self):
        """
        Private slot used to list the tags of the project.
        """
        self.vcs.svnListTagBranch(self.project.ppath, True)
        
    def __svnBranchList(self):
        """
        Private slot used to list the branches of the project.
        """
        self.vcs.svnListTagBranch(self.project.ppath, False)
        
    def __svnExtendedDiff(self):
        """
        Private slot used to perform a svn diff with the selection of
        revisions.
        """
        self.vcs.svnExtendedDiff(self.project.ppath)
        
    def __svnUrlDiff(self):
        """
        Private slot used to perform a svn diff with the selection of
        repository URLs.
        """
        self.vcs.svnUrlDiff(self.project.ppath)
        
    def __svnRelocate(self):
        """
        Private slot used to relocate the working copy to a new repository URL.
        """
        self.vcs.svnRelocate(self.project.ppath)
        
    def __svnRepoBrowser(self):
        """
        Private slot to open the repository browser.
        """
        self.vcs.svnRepoBrowser(projectPath=self.project.ppath)
        
    def __svnConfigure(self):
        """
        Private slot to open the configuration dialog.
        """
        e5App().getObject("UserInterface")\
            .showPreferences("zzz_subversionPage")
    
    def __svnChangeLists(self):
        """
        Private slot used to show a list of change lists.
        """
        self.vcs.svnShowChangelists(self.project.ppath)
    
    def __svnUpgrade(self):
        """
        Private slot used to upgrade the working copy format.
        """
        self.vcs.svnUpgrade(self.project.ppath)
Esempio n. 51
0
    def __init__(self, parent=None):
        super(GcodeEditor, self).__init__(parent)
        self.isCaseSensitive = 0

        self.setMinimumSize(QSize(300, 200))    
        self.setWindowTitle("PyQt5 editor test example") 

        lay = QVBoxLayout()
        lay.setContentsMargins(0,0,0,0)
        self.setLayout(lay)

        # make editor
        self.editor = GcodeDisplay(self)
        # class patch editor's function to ours
        # so we get the lines percent update
        self.editor.emit_percent = self.emit_percent
        self.editor.setReadOnly(True)

        ################################
        # add menubar actions
        ################################

        # Create new action
        newAction = QAction(QIcon.fromTheme('document-new'), 'New', self)        
        newAction.setShortcut('Ctrl+N')
        newAction.setStatusTip('New document')
        newAction.triggered.connect(self.newCall)

        # Create open action
        openAction = QAction(QIcon.fromTheme('document-open'), '&Open', self)        
        openAction.setShortcut('Ctrl+O')
        openAction.setStatusTip('Open document')
        openAction.triggered.connect(self.openCall)

        # Create save action
        saveAction = QAction(QIcon.fromTheme('document-save'), '&save', self)        
        saveAction.setShortcut('Ctrl+S')
        saveAction.setStatusTip('save document')
        saveAction.triggered.connect(self.saveCall)

        # Create exit action
        exitAction = QAction(QIcon.fromTheme('application-exit'), '&Exit', self)        
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(self.exitCall)

        # Create gcode lexer action
        gCodeLexerAction = QAction(QIcon.fromTheme('lexer.png'), '&Gcode\n lexer', self)
        gCodeLexerAction.setCheckable(1)
        gCodeLexerAction.setShortcut('Ctrl+G')
        gCodeLexerAction.setStatusTip('Set Gcode highlighting')
        gCodeLexerAction.triggered.connect(self.editor.set_gcode_lexer)

        # Create gcode lexer action
        pythonLexerAction = QAction(QIcon.fromTheme('lexer.png'), '&python\n lexer', self)        
        pythonLexerAction.setShortcut('Ctrl+P')
        pythonLexerAction.setStatusTip('Set Python highlighting')
        pythonLexerAction.triggered.connect(self.editor.set_python_lexer)

        # Create toolbar and add action
        toolBar = QToolBar('File')
        toolBar.addAction(newAction)
        toolBar.addAction(openAction)
        toolBar.addAction(saveAction)
        toolBar.addAction(exitAction)

        toolBar.addSeparator()

        # add lexer actions
        toolBar.addAction(gCodeLexerAction)
        toolBar.addAction(pythonLexerAction)

        toolBar.addSeparator()
        toolBar.addWidget(QLabel('<html><head/><body><p><span style=" font-size:20pt; font-weight:600;">Edit Mode</span></p></body></html>'))

        # create a frame for buttons
        box = QHBoxLayout()
        box.addWidget(toolBar)

        self.topMenu = QFrame()
        self.topMenu.setLayout(box)

        # add widgets
        lay.addWidget(self.topMenu)
        lay.addWidget(self.editor)
        lay.addWidget(self.createGroup())

        self.readOnlyMode()
Esempio n. 52
0
class CalibrateSpectrum(QWidget):
    def __init__(self, fits_file, settings, database, project=None):
        super(CalibrateSpectrum, self).__init__()
        self.project=project
        self.settings = settings
        self.fits_spectrum = FitsSpectrum(fits_file)
        self.fits_spectrum.spectrum.normalize_to_max()
        self.fits_file = fits_file
        self.ui = Ui_CalibrateSpectrum()
        self.ui.setupUi(self)
        self.toolbar = QToolBar('Calibration Toolbar')
        self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.ui.x_axis_pick.setMenu(QMenu())
        self.ui.x_axis_pick.menu().addAction("Maximum from range").triggered.connect(lambda: self.pick_from_range('maximum'))
        self.ui.x_axis_pick.menu().addAction("Minimum from range").triggered.connect(lambda: self.pick_from_range('minimum'))
        self.ui.x_axis_pick.menu().addAction("Central value from range").triggered.connect(lambda: self.pick_from_range('central'))
        self.ui.wavelength_pick.clicked.connect(lambda: self.lines_dialog.show())
        
        save_action = self.toolbar.addAction(QIcon(':/save_20'), 'Save', self.save_spectrum)
        self.spectrum_plot = QtCommons.nestWidget(self.ui.spectrum_plot_widget, QMathPlotWidget())
        
        self.reference_spectra_dialog = ReferenceSpectraDialog(database)
        self.reference_spectra_dialog.setup_menu(self.toolbar, self.spectrum_plot.axes, settings)

        self.object_properties = ObjectProperties(self.fits_file, project=project)
        self.object_properties_dialog = ObjectPropertiesDialog(settings, self.object_properties)
        self.toolbar.addAction("Object properties", self.object_properties_dialog.show)

        self.calibration_model = QStandardItemModel()
        self.calibration_model.setHorizontalHeaderLabels(["x-axis", "wavelength", "error"])
        self.ui.calibration_points.setModel(self.calibration_model)
        self.ui.calibration_points.selectionModel().selectionChanged.connect(lambda selected, deselected: self.ui.remove_calibration_point.setEnabled(len(selected.indexes()) > 0)  )
        self.ui.add_calibration_point.clicked.connect(self.add_calibration_point)
        self.ui.remove_calibration_point.setEnabled(False)
        self.ui.remove_calibration_point.clicked.connect(self.remove_calibration_point)
        if project and project.avg_dispersion():
            self.ui.set_dispersion.setMenu(QMenu())
            self.ui.set_dispersion.menu().addAction('From input value', self.calculate_calibration)
            self.ui.set_dispersion.menu().addAction('From Project', lambda: self.calculate_calibration(project.avg_dispersion()))
        else:
            self.ui.set_dispersion.clicked.connect(self.calculate_calibration)
        self.ui.point_is_star.toggled.connect(lambda checked: self.ui.wavelength_pick.setEnabled(not checked))
        self.ui.point_is_star.toggled.connect(lambda checked: self.ui.point_wavelength.setEnabled(not checked))
        self.fits_spectrum.plot_to(self.spectrum_plot.axes)
        
        self.toolbar.addSeparator()
        self.toolbar.addAction("Zoom", self.spectrum_plot.select_zoom)
        self.toolbar.addAction("Reset Zoom", lambda: self.spectrum_plot.reset_zoom(self.fits_spectrum.spectrum.wavelengths, self.fits_spectrum.spectrum.fluxes.min(), self.fits_spectrum.spectrum.fluxes.max()))
        self.toolbar.addSeparator()
        
        self.lines_dialog = LinesDialog(database, settings, self.spectrum_plot, enable_picker = False, selection_mode = 'single')
        self.lines_dialog.lines.connect(self.picked_line)

        hdu_calibration_points = [h for h in self.fits_file if h.name == FitsSpectrum.CALIBRATION_DATA]
        if len(hdu_calibration_points) > 0:                
            for point in hdu_calibration_points[-1].data:
                self.add_calibration_point_data(point[0], point[1])
        self.calculate_calibration()
    
  
    def picked_from_range(self, type, min, max):
        min=(self.fits_spectrum.spectrum.wavelength_index(min))
        max=(self.fits_spectrum.spectrum.wavelength_index(max))
        add_line = lambda x: self.spectrum_plot.add_line("x_axis_pick", self.fits_spectrum.spectrum.wavelengths[x], color='r')
        set_x_value = lambda x: self.ui.point_x_axis.setValue(x)

        if type != 'central':
            subplot = SelectPlottedPoints(self.fits_spectrum.spectrum.fluxes, min, max+1, self.settings, type)
            subplot.point.connect(add_line)
            subplot.point.connect(set_x_value)
            subplot.show()
            return
        point = min+(max-min)/2
        self.ui.point_x_axis.setValue(point)
        set_x_value(point)
        add_line(point)

    def pick_from_range(self, type):
        self.spectrum_plot.add_span_selector('pick_x_axis', lambda min,max: self.picked_from_range(type, min, max),direction='horizontal')

    def remove_calibration_point(self):
        self.calibration_model.removeRow(self.ui.calibration_points.selectionModel().selectedIndexes()[0].row())
        self.calculate_calibration()
    
    def add_calibration_point_data(self, x_value, wavelength):
        x_axis_item = QStandardItem("star" if x_value == 0 else "{}".format(x_value))
        x_axis_item.setData(x_value)
        wavelength_item = QStandardItem("{:.2f}".format(wavelength))
        wavelength_item.setData(wavelength)
        self.calibration_model.appendRow([x_axis_item, wavelength_item, QStandardItem("n/a")])
        self.spectrum_plot.rm_element('x_axis_pick')
        
    def picked_line(self, lines):
        self.ui.point_wavelength.setValue(lines[0]['lambda'])
    
    def add_calibration_point(self):
        self.add_calibration_point_data(self.ui.point_x_axis.value(), 0 if self.ui.point_is_star.isChecked() else self.ui.point_wavelength.value())
        self.calculate_calibration()
        
    def calibration_points(self):
        return [{'row': row, 'x': self.calibration_model.item(row, 0).data(), 'wavelength': self.calibration_model.item(row, 1).data()} for row in range(self.calibration_model.rowCount())]
    

    def calculate_calibration(self, dispersion = None):
        points_number = self.calibration_model.rowCount()
        self.ui.set_dispersion.setEnabled(points_number == 1)
        self.ui.dispersion.setEnabled(points_number == 1)
        
        if points_number == 0:
            self.fits_spectrum.reset()
            self.lines_dialog.set_picker_enabled(False)
        else:
            self.lines_dialog.set_picker_enabled(True)
            points = sorted(self.calibration_points(), key=lambda point: point['x'])
            self.fits_spectrum.calibrate(points, dispersion if dispersion else self.ui.dispersion.value() )
            for row, value in [(p['row'], "{:.2f}".format( p['wavelength']-self.fits_spectrum.spectrum.wavelengths[p['x']])) for p in points]:
                self.calibration_model.item(row, 2).setText(value)
            
        self.ui.dispersion.setValue(self.fits_spectrum.spectrum.dispersion())
        self.fits_spectrum.plot_to(self.spectrum_plot.axes)
        
    def save_spectrum(self):
        if not self.project:
            save_file_sticky('Save plot...', 'FITS file (.fit)', lambda f: self.save(f[0]), self.settings, CALIBRATED_PROFILE, [RAW_PROFILE])
            return
        self.project.add_file(Project.CALIBRATED_PROFILE, object_properties = self.object_properties, on_added=self.save)
        
    def save(self, filename):
        self.fits_spectrum.save(filename, self.calibration_points())
Esempio n. 53
0
class UMLDialog(E5MainWindow):
    """
    Class implementing a dialog showing UML like diagrams.
    """
    NoDiagram = 255
    ClassDiagram = 0
    PackageDiagram = 1
    ImportsDiagram = 2
    ApplicationDiagram = 3
    
    FileVersions = ["1.0"]
    
    def __init__(self, diagramType, project, path="", parent=None,
                 initBuilder=True, **kwargs):
        """
        Constructor
        
        @param diagramType type of the diagram (one of ApplicationDiagram,
            ClassDiagram, ImportsDiagram, NoDiagram, PackageDiagram)
        @param project reference to the project object (Project)
        @param path file or directory path to build the diagram from (string)
        @param parent parent widget of the dialog (QWidget)
        @keyparam initBuilder flag indicating to initialize the diagram
            builder (boolean)
        @keyparam kwargs diagram specific data
        """
        super(UMLDialog, self).__init__(parent)
        self.setObjectName("UMLDialog")
        
        self.__diagramType = diagramType
        self.__project = project
        
        from .UMLGraphicsView import UMLGraphicsView
        self.scene = QGraphicsScene(0.0, 0.0, 800.0, 600.0)
        self.umlView = UMLGraphicsView(self.scene, parent=self)
        self.builder = self.__diagramBuilder(
            self.__diagramType, path, **kwargs)
        if self.builder and initBuilder:
            self.builder.initialize()
        
        self.__fileName = ""
        
        self.__initActions()
        self.__initToolBars()
        
        self.setCentralWidget(self.umlView)
        
        self.umlView.relayout.connect(self.__relayout)
        
        self.setWindowTitle(self.__diagramTypeString())
    
    def __initActions(self):
        """
        Private slot to initialize the actions.
        """
        self.closeAct = \
            QAction(UI.PixmapCache.getIcon("close.png"),
                    self.tr("Close"), self)
        self.closeAct.triggered.connect(self.close)
        
        self.openAct = \
            QAction(UI.PixmapCache.getIcon("open.png"),
                    self.tr("Load"), self)
        self.openAct.triggered.connect(self.load)
        
        self.saveAct = \
            QAction(UI.PixmapCache.getIcon("fileSave.png"),
                    self.tr("Save"), self)
        self.saveAct.triggered.connect(self.__save)
        
        self.saveAsAct = \
            QAction(UI.PixmapCache.getIcon("fileSaveAs.png"),
                    self.tr("Save As..."), self)
        self.saveAsAct.triggered.connect(self.__saveAs)
        
        self.saveImageAct = \
            QAction(UI.PixmapCache.getIcon("fileSavePixmap.png"),
                    self.tr("Save as Image"), self)
        self.saveImageAct.triggered.connect(self.umlView.saveImage)
        
        self.printAct = \
            QAction(UI.PixmapCache.getIcon("print.png"),
                    self.tr("Print"), self)
        self.printAct.triggered.connect(self.umlView.printDiagram)
        
        self.printPreviewAct = \
            QAction(UI.PixmapCache.getIcon("printPreview.png"),
                    self.tr("Print Preview"), self)
        self.printPreviewAct.triggered.connect(
            self.umlView.printPreviewDiagram)
    
    def __initToolBars(self):
        """
        Private slot to initialize the toolbars.
        """
        self.windowToolBar = QToolBar(self.tr("Window"), self)
        self.windowToolBar.setIconSize(UI.Config.ToolBarIconSize)
        self.windowToolBar.addAction(self.closeAct)
        
        self.fileToolBar = QToolBar(self.tr("File"), self)
        self.fileToolBar.setIconSize(UI.Config.ToolBarIconSize)
        self.fileToolBar.addAction(self.openAct)
        self.fileToolBar.addSeparator()
        self.fileToolBar.addAction(self.saveAct)
        self.fileToolBar.addAction(self.saveAsAct)
        self.fileToolBar.addAction(self.saveImageAct)
        self.fileToolBar.addSeparator()
        self.fileToolBar.addAction(self.printPreviewAct)
        self.fileToolBar.addAction(self.printAct)
        
        self.umlToolBar = self.umlView.initToolBar()
        
        self.addToolBar(Qt.TopToolBarArea, self.fileToolBar)
        self.addToolBar(Qt.TopToolBarArea, self.windowToolBar)
        self.addToolBar(Qt.TopToolBarArea, self.umlToolBar)
    
    def show(self, fromFile=False):
        """
        Public method to show the dialog.
        
        @keyparam fromFile flag indicating, that the diagram was loaded
            from file (boolean)
        """
        if not fromFile and self.builder:
            self.builder.buildDiagram()
        super(UMLDialog, self).show()
    
    def __relayout(self):
        """
        Private method to relayout the diagram.
        """
        if self.builder:
            self.builder.buildDiagram()
    
    def __diagramBuilder(self, diagramType, path, **kwargs):
        """
        Private method to instantiate a diagram builder object.
        
        @param diagramType type of the diagram
            (one of ApplicationDiagram, ClassDiagram, ImportsDiagram,
            PackageDiagram)
        @param path file or directory path to build the diagram from (string)
        @keyparam kwargs diagram specific data
        @return reference to the instantiated diagram builder
        @exception ValueError raised to indicate an illegal diagram type
        """
        if diagramType == UMLDialog.ClassDiagram:
            from .UMLClassDiagramBuilder import UMLClassDiagramBuilder
            return UMLClassDiagramBuilder(
                self, self.umlView, self.__project, path, **kwargs)
        elif diagramType == UMLDialog.PackageDiagram:
            from .PackageDiagramBuilder import PackageDiagramBuilder
            return PackageDiagramBuilder(
                self, self.umlView, self.__project, path, **kwargs)
        elif diagramType == UMLDialog.ImportsDiagram:
            from .ImportsDiagramBuilder import ImportsDiagramBuilder
            return ImportsDiagramBuilder(
                self, self.umlView, self.__project, path, **kwargs)
        elif diagramType == UMLDialog.ApplicationDiagram:
            from .ApplicationDiagramBuilder import ApplicationDiagramBuilder
            return ApplicationDiagramBuilder(
                self, self.umlView, self.__project, **kwargs)
        elif diagramType == UMLDialog.NoDiagram:
            return None
        else:
            raise ValueError(self.tr(
                "Illegal diagram type '{0}' given.").format(diagramType))
    
    def __diagramTypeString(self):
        """
        Private method to generate a readable string for the diagram type.
        
        @return readable type string (string)
        """
        if self.__diagramType == UMLDialog.ClassDiagram:
            return "Class Diagram"
        elif self.__diagramType == UMLDialog.PackageDiagram:
            return "Package Diagram"
        elif self.__diagramType == UMLDialog.ImportsDiagram:
            return "Imports Diagram"
        elif self.__diagramType == UMLDialog.ApplicationDiagram:
            return "Application Diagram"
        else:
            return "Illegal Diagram Type"
    
    def __save(self):
        """
        Private slot to save the diagram with the current name.
        """
        self.__saveAs(self.__fileName)
    
    @pyqtSlot()
    def __saveAs(self, filename=""):
        """
        Private slot to save the diagram.
        
        @param filename name of the file to write to (string)
        """
        if not filename:
            fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
                self,
                self.tr("Save Diagram"),
                "",
                self.tr("Eric Graphics File (*.e5g);;All Files (*)"),
                "",
                E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
            if not fname:
                return
            ext = QFileInfo(fname).suffix()
            if not ext:
                ex = selectedFilter.split("(*")[1].split(")")[0]
                if ex:
                    fname += ex
            if QFileInfo(fname).exists():
                res = E5MessageBox.yesNo(
                    self,
                    self.tr("Save Diagram"),
                    self.tr("<p>The file <b>{0}</b> already exists."
                            " Overwrite it?</p>").format(fname),
                    icon=E5MessageBox.Warning)
                if not res:
                    return
            filename = fname
        
        lines = [
            "version: 1.0",
            "diagram_type: {0} ({1})".format(
                self.__diagramType, self.__diagramTypeString()),
            "scene_size: {0};{1}".format(self.scene.width(),
                                         self.scene.height()),
        ]
        persistenceData = self.builder.getPersistenceData()
        if persistenceData:
            lines.append("builder_data: {0}".format(persistenceData))
        lines.extend(self.umlView.getPersistenceData())
        
        try:
            f = open(filename, "w", encoding="utf-8")
            f.write("\n".join(lines))
            f.close()
        except (IOError, OSError) as err:
            E5MessageBox.critical(
                self,
                self.tr("Save Diagram"),
                self.tr(
                    """<p>The file <b>{0}</b> could not be saved.</p>"""
                    """<p>Reason: {1}</p>""").format(filename, str(err)))
            return
        
        self.__fileName = filename
    
    def load(self):
        """
        Public method to load a diagram from a file.
        
        @return flag indicating success (boolean)
        """
        filename = E5FileDialog.getOpenFileName(
            self,
            self.tr("Load Diagram"),
            "",
            self.tr("Eric Graphics File (*.e5g);;All Files (*)"))
        if not filename:
            # Cancelled by user
            return False
        
        try:
            f = open(filename, "r", encoding="utf-8")
            data = f.read()
            f.close()
        except (IOError, OSError) as err:
            E5MessageBox.critical(
                self,
                self.tr("Load Diagram"),
                self.tr(
                    """<p>The file <b>{0}</b> could not be read.</p>"""
                    """<p>Reason: {1}</p>""").format(filename, str(err)))
            return False
        
        lines = data.splitlines()
        if len(lines) < 3:
            self.__showInvalidDataMessage(filename)
            return False
        
        try:
            # step 1: check version
            linenum = 0
            key, value = lines[linenum].split(": ", 1)
            if key.strip() != "version" or \
                    value.strip() not in UMLDialog.FileVersions:
                self.__showInvalidDataMessage(filename, linenum)
                return False
            else:
                version = value
            
            # step 2: extract diagram type
            linenum += 1
            key, value = lines[linenum].split(": ", 1)
            if key.strip() != "diagram_type":
                self.__showInvalidDataMessage(filename, linenum)
                return False
            try:
                self.__diagramType = int(value.strip().split(None, 1)[0])
            except ValueError:
                self.__showInvalidDataMessage(filename, linenum)
                return False
            self.scene.clear()
            self.builder = self.__diagramBuilder(self.__diagramType, "")
            
            # step 3: extract scene size
            linenum += 1
            key, value = lines[linenum].split(": ", 1)
            if key.strip() != "scene_size":
                self.__showInvalidDataMessage(filename, linenum)
                return False
            try:
                width, height = [float(v.strip()) for v in value.split(";")]
            except ValueError:
                self.__showInvalidDataMessage(filename, linenum)
                return False
            self.umlView.setSceneSize(width, height)
            
            # step 4: extract builder data if available
            linenum += 1
            key, value = lines[linenum].split(": ", 1)
            if key.strip() == "builder_data":
                ok = self.builder.parsePersistenceData(version, value)
                if not ok:
                    self.__showInvalidDataMessage(filename, linenum)
                    return False
                linenum += 1
            
            # step 5: extract the graphics items
            ok, vlinenum = self.umlView.parsePersistenceData(
                version, lines[linenum:])
            if not ok:
                self.__showInvalidDataMessage(filename, linenum + vlinenum)
                return False
        
        except IndexError:
            self.__showInvalidDataMessage(filename)
            return False
        
        # everything worked fine, so remember the file name
        self.__fileName = filename
        return True
    
    def __showInvalidDataMessage(self, filename, linenum=-1):
        """
        Private slot to show a message dialog indicating an invalid data file.
        
        @param filename name of the file containing the invalid data (string)
        @param linenum number of the invalid line (integer)
        """
        if linenum < 0:
            msg = self.tr("""<p>The file <b>{0}</b> does not contain"""
                          """ valid data.</p>""").format(filename)
        else:
            msg = self.tr("""<p>The file <b>{0}</b> does not contain"""
                          """ valid data.</p><p>Invalid line: {1}</p>"""
                          ).format(filename, linenum + 1)
        E5MessageBox.critical(self, self.tr("Load Diagram"), msg)
Esempio n. 54
0
class HgServeDialog(E5MainWindow):
    """
    Class implementing a dialog for the Mercurial server.
    """
    def __init__(self, vcs, path, parent=None):
        """
        Constructor
        
        @param vcs reference to the vcs object
        @param path path of the repository to serve (string)
        @param parent reference to the parent widget (QWidget)
        """
        super(HgServeDialog, self).__init__(parent)
        
        self.vcs = vcs
        self.__repoPath = path
        
        self.__styles = ["paper", "coal", "gitweb", "monoblue", "spartan", ]
        
        self.setWindowTitle(self.tr("Mercurial Server"))
        
        self.__startAct = QAction(
            UI.PixmapCache.getIcon(
                os.path.join("VcsPlugins", "vcsMercurial", "icons",
                             "startServer.png")),
            self.tr("Start Server"), self)
        self.__startAct.triggered.connect(self.__startServer)
        self.__stopAct = QAction(
            UI.PixmapCache.getIcon(
                os.path.join("VcsPlugins", "vcsMercurial", "icons",
                             "stopServer.png")),
            self.tr("Stop Server"), self)
        self.__stopAct.triggered.connect(self.__stopServer)
        self.__browserAct = QAction(
            UI.PixmapCache.getIcon("home.png"),
            self.tr("Start Browser"), self)
        self.__browserAct.triggered.connect(self.__startBrowser)
        
        self.__portSpin = QSpinBox(self)
        self.__portSpin.setMinimum(2048)
        self.__portSpin.setMaximum(65535)
        self.__portSpin.setToolTip(self.tr("Enter the server port"))
        self.__portSpin.setValue(
            self.vcs.getPlugin().getPreferences("ServerPort"))
        
        self.__styleCombo = QComboBox(self)
        self.__styleCombo.addItems(self.__styles)
        self.__styleCombo.setToolTip(self.tr("Select the style to use"))
        self.__styleCombo.setCurrentIndex(self.__styleCombo.findText(
            self.vcs.getPlugin().getPreferences("ServerStyle")))
        
        self.__serverToolbar = QToolBar(self.tr("Server"), self)
        self.__serverToolbar.addAction(self.__startAct)
        self.__serverToolbar.addAction(self.__stopAct)
        self.__serverToolbar.addSeparator()
        self.__serverToolbar.addWidget(self.__portSpin)
        self.__serverToolbar.addWidget(self.__styleCombo)
        
        self.__browserToolbar = QToolBar(self.tr("Browser"), self)
        self.__browserToolbar.addAction(self.__browserAct)
        
        self.addToolBar(Qt.TopToolBarArea, self.__serverToolbar)
        self.addToolBar(Qt.TopToolBarArea, self.__browserToolbar)
        
        self.__log = QPlainTextEdit(self)
        self.setCentralWidget(self.__log)
        
        # polish up the dialog
        self.__startAct.setEnabled(True)
        self.__stopAct.setEnabled(False)
        self.__browserAct.setEnabled(False)
        self.__portSpin.setEnabled(True)
        self.__styleCombo.setEnabled(True)
        self.resize(QSize(800, 600).expandedTo(self.minimumSizeHint()))
        
        self.process = QProcess()
        self.process.finished.connect(self.__procFinished)
        self.process.readyReadStandardOutput.connect(self.__readStdout)
        self.process.readyReadStandardError.connect(self.__readStderr)
        
        self.cNormalFormat = self.__log.currentCharFormat()
        self.cErrorFormat = self.__log.currentCharFormat()
        self.cErrorFormat.setForeground(
            QBrush(Preferences.getUI("LogStdErrColour")))
    
    def __startServer(self):
        """
        Private slot to start the Mercurial server.
        """
        port = self.__portSpin.value()
        style = self.__styleCombo.currentText()
        
        args = self.vcs.initCommand("serve")
        args.append("-v")
        args.append("--port")
        args.append(str(port))
        args.append("--style")
        args.append(style)
        
        self.process.setWorkingDirectory(self.__repoPath)
        
        self.process.start('hg', args)
        procStarted = self.process.waitForStarted(5000)
        if procStarted:
            self.__startAct.setEnabled(False)
            self.__stopAct.setEnabled(True)
            self.__browserAct.setEnabled(True)
            self.__portSpin.setEnabled(False)
            self.__styleCombo.setEnabled(False)
            self.vcs.getPlugin().setPreferences("ServerPort", port)
            self.vcs.getPlugin().setPreferences("ServerStyle", style)
        else:
            E5MessageBox.critical(
                self,
                self.tr('Process Generation Error'),
                self.tr(
                    'The process {0} could not be started. '
                    'Ensure, that it is in the search path.'
                ).format('hg'))
    
    def __stopServer(self):
        """
        Private slot to stop the Mercurial server.
        """
        if self.process is not None and \
           self.process.state() != QProcess.NotRunning:
            self.process.terminate()
            self.process.waitForFinished(5000)
            if self.process.state() != QProcess.NotRunning:
                self.process.kill()
        
        self.__startAct.setEnabled(True)
        self.__stopAct.setEnabled(False)
        self.__browserAct.setEnabled(False)
        self.__portSpin.setEnabled(True)
        self.__styleCombo.setEnabled(True)
    
    def __startBrowser(self):
        """
        Private slot to start a browser for the served repository.
        """
        ui = e5App().getObject("UserInterface")
        ui.launchHelpViewer(
            "http://localhost:{0}".format(self.__portSpin.value()))
    
    def closeEvent(self, e):
        """
        Protected slot implementing a close event handler.
        
        @param e close event (QCloseEvent)
        """
        self.__stopServer()
    
    def __procFinished(self, exitCode, exitStatus):
        """
        Private slot connected to the finished signal.
        
        @param exitCode exit code of the process (integer)
        @param exitStatus exit status of the process (QProcess.ExitStatus)
        """
        self.__stopServer()
    
    def __readStdout(self):
        """
        Private slot to handle the readyReadStandardOutput signal.
        
        It reads the output of the process and inserts it into the log.
        """
        if self.process is not None:
            s = str(self.process.readAllStandardOutput(),
                    self.vcs.getEncoding(), 'replace')
            self.__appendText(s, False)
    
    def __readStderr(self):
        """
        Private slot to handle the readyReadStandardError signal.
        
        It reads the error output of the process and inserts it into the log.
        """
        if self.process is not None:
            s = str(self.process.readAllStandardError(),
                    self.vcs.getEncoding(), 'replace')
            self.__appendText(s, True)
    
    def __appendText(self, txt, error=False):
        """
        Private method to append text to the end.
        
        @param txt text to insert (string)
        @param error flag indicating to insert error text (boolean)
        """
        tc = self.__log.textCursor()
        tc.movePosition(QTextCursor.End)
        self.__log.setTextCursor(tc)
        if error:
            self.__log.setCurrentCharFormat(self.cErrorFormat)
        else:
            self.__log.setCurrentCharFormat(self.cNormalFormat)
        self.__log.insertPlainText(txt)
        self.__log.ensureCursorVisible()
Esempio n. 55
0
class ReTextWindow(QMainWindow):
	def __init__(self, parent=None):
		QMainWindow.__init__(self, parent)
		self.resize(950, 700)
		screenRect = QDesktopWidget().screenGeometry()
		if globalSettings.windowGeometry:
			self.restoreGeometry(globalSettings.windowGeometry)
		else:
			self.move((screenRect.width()-self.width())/2, (screenRect.height()-self.height())/2)
		if not screenRect.contains(self.geometry()):
			self.showMaximized()
		if globalSettings.iconTheme:
			QIcon.setThemeName(globalSettings.iconTheme)
		if QIcon.themeName() in ('hicolor', ''):
			if not QFile.exists(icon_path + 'document-new.png'):
				QIcon.setThemeName(get_icon_theme())
		if QFile.exists(icon_path+'retext.png'):
			self.setWindowIcon(QIcon(icon_path+'retext.png'))
		elif QFile.exists('/usr/share/pixmaps/retext.png'):
			self.setWindowIcon(QIcon('/usr/share/pixmaps/retext.png'))
		else:
			self.setWindowIcon(QIcon.fromTheme('retext',
				QIcon.fromTheme('accessories-text-editor')))
		self.editBoxes = []
		self.previewBoxes = []
		self.highlighters = []
		self.markups = []
		self.fileNames = []
		self.actionPreviewChecked = []
		self.actionLivePreviewChecked = []
		self.tabWidget = QTabWidget(self)
		self.initTabWidget()
		self.setCentralWidget(self.tabWidget)
		self.tabWidget.currentChanged.connect(self.changeIndex)
		self.tabWidget.tabCloseRequested.connect(self.closeTab)
		toolBar = QToolBar(self.tr('File toolbar'), self)
		self.addToolBar(Qt.TopToolBarArea, toolBar)
		self.editBar = QToolBar(self.tr('Edit toolbar'), self)
		self.addToolBar(Qt.TopToolBarArea, self.editBar)
		self.searchBar = QToolBar(self.tr('Search toolbar'), self)
		self.addToolBar(Qt.BottomToolBarArea, self.searchBar)
		toolBar.setVisible(not globalSettings.hideToolBar)
		self.editBar.setVisible(not globalSettings.hideToolBar)
		self.actionNew = self.act(self.tr('New'), 'document-new',
			self.createNew, shct=QKeySequence.New)
		self.actionNew.setPriority(QAction.LowPriority)
		self.actionOpen = self.act(self.tr('Open'), 'document-open',
			self.openFile, shct=QKeySequence.Open)
		self.actionOpen.setPriority(QAction.LowPriority)
		self.actionSetEncoding = self.act(self.tr('Set encoding'),
			trig=self.showEncodingDialog)
		self.actionSetEncoding.setEnabled(False)
		self.actionReload = self.act(self.tr('Reload'), 'view-refresh', trig=self.openFileMain)
		self.actionReload.setEnabled(False)
		self.actionSave = self.act(self.tr('Save'), 'document-save',
			self.saveFile, shct=QKeySequence.Save)
		self.actionSave.setEnabled(False)
		self.actionSave.setPriority(QAction.LowPriority)
		self.actionSaveAs = self.act(self.tr('Save as'), 'document-save-as',
			self.saveFileAs, shct=QKeySequence.SaveAs)
		self.actionNextTab = self.act(self.tr('Next tab'), 'go-next',
			lambda: self.switchTab(1), shct=Qt.CTRL+Qt.Key_PageDown)
		self.actionPrevTab = self.act(self.tr('Previous tab'), 'go-previous',
			lambda: self.switchTab(-1), shct=Qt.CTRL+Qt.Key_PageUp)
		self.actionPrint = self.act(self.tr('Print'), 'document-print',
			self.printFile, shct=QKeySequence.Print)
		self.actionPrint.setPriority(QAction.LowPriority)
		self.actionPrintPreview = self.act(self.tr('Print preview'), 'document-print-preview',
			self.printPreview)
		self.actionViewHtml = self.act(self.tr('View HTML code'), 'text-html', self.viewHtml)
		self.actionChangeEditorFont = self.act(self.tr('Change editor font'),
			trig=self.changeEditorFont)
		self.actionChangePreviewFont = self.act(self.tr('Change preview font'),
			trig=self.changePreviewFont)
		self.actionSearch = self.act(self.tr('Find text'), 'edit-find', shct=QKeySequence.Find)
		self.actionSearch.setCheckable(True)
		self.actionSearch.triggered[bool].connect(self.searchBar.setVisible)
		self.searchBar.visibilityChanged.connect(self.searchBarVisibilityChanged)
		self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E,
			trigbool=self.preview)
		if QIcon.hasThemeIcon('document-preview'):
			self.actionPreview.setIcon(QIcon.fromTheme('document-preview'))
		elif QIcon.hasThemeIcon('preview-file'):
			self.actionPreview.setIcon(QIcon.fromTheme('preview-file'))
		elif QIcon.hasThemeIcon('x-office-document'):
			self.actionPreview.setIcon(QIcon.fromTheme('x-office-document'))
		else:
			self.actionPreview.setIcon(QIcon(icon_path+'document-preview.png'))
		self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.Key_L,
		trigbool=self.enableLivePreview)
		self.actionTableMode = self.act(self.tr('Table mode'), shct=Qt.CTRL+Qt.Key_T,
			trigbool=lambda x: self.editBoxes[self.ind].enableTableMode(x))
		if ReTextFakeVimHandler:
			self.actionFakeVimMode = self.act(self.tr('FakeVim mode'),
				shct=Qt.CTRL+Qt.ALT+Qt.Key_V, trigbool=self.enableFakeVimMode)
			if globalSettings.useFakeVim:
				self.actionFakeVimMode.setChecked(True)
				self.enableFakeVimMode(True)
		self.actionFullScreen = self.act(self.tr('Fullscreen mode'), 'view-fullscreen',
			shct=Qt.Key_F11, trigbool=self.enableFullScreen)
		self.actionFullScreen.setPriority(QAction.LowPriority)
		self.actionConfig = self.act(self.tr('Preferences'), icon='preferences-system',
			trig=self.openConfigDialog)
		self.actionConfig.setMenuRole(QAction.PreferencesRole)
		self.actionSaveHtml = self.act('HTML', 'text-html', self.saveFileHtml)
		self.actionPdf = self.act('PDF', 'application-pdf', self.savePdf)
		self.actionOdf = self.act('ODT', 'x-office-document', self.saveOdf)
		self.getExportExtensionsList()
		self.actionQuit = self.act(self.tr('Quit'), 'application-exit', shct=QKeySequence.Quit)
		self.actionQuit.setMenuRole(QAction.QuitRole)
		self.actionQuit.triggered.connect(self.close)
		self.actionUndo = self.act(self.tr('Undo'), 'edit-undo',
			lambda: self.editBoxes[self.ind].undo(), shct=QKeySequence.Undo)
		self.actionRedo = self.act(self.tr('Redo'), 'edit-redo',
			lambda: self.editBoxes[self.ind].redo(), shct=QKeySequence.Redo)
		self.actionCopy = self.act(self.tr('Copy'), 'edit-copy',
			lambda: self.editBoxes[self.ind].copy(), shct=QKeySequence.Copy)
		self.actionCut = self.act(self.tr('Cut'), 'edit-cut',
			lambda: self.editBoxes[self.ind].cut(), shct=QKeySequence.Cut)
		self.actionPaste = self.act(self.tr('Paste'), 'edit-paste',
			lambda: self.editBoxes[self.ind].paste(), shct=QKeySequence.Paste)
		self.actionUndo.setEnabled(False)
		self.actionRedo.setEnabled(False)
		self.actionCopy.setEnabled(False)
		self.actionCut.setEnabled(False)
		qApp = QApplication.instance()
		qApp.clipboard().dataChanged.connect(self.clipboardDataChanged)
		self.clipboardDataChanged()
		if enchant_available:
			self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSpellCheck)
			self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale)
		self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit)
		self.actionWebKit.setChecked(globalSettings.useWebKit)
		self.actionShow = self.act(self.tr('Show directory'), 'system-file-manager', self.showInDir)
		self.actionFind = self.act(self.tr('Next'), 'go-next', self.find,
			shct=QKeySequence.FindNext)
		self.actionFindPrev = self.act(self.tr('Previous'), 'go-previous',
			lambda: self.find(back=True), shct=QKeySequence.FindPrevious)
		self.actionHelp = self.act(self.tr('Get help online'), 'help-contents', self.openHelp)
		self.aboutWindowTitle = self.tr('About ReText')
		self.actionAbout = self.act(self.aboutWindowTitle, 'help-about', self.aboutDialog)
		self.actionAbout.setMenuRole(QAction.AboutRole)
		self.actionAboutQt = self.act(self.tr('About Qt'))
		self.actionAboutQt.setMenuRole(QAction.AboutQtRole)
		self.actionAboutQt.triggered.connect(qApp.aboutQt)
		availableMarkups = markups.get_available_markups()
		if not availableMarkups:
			print('Warning: no markups are available!')
		self.defaultMarkup = availableMarkups[0] if availableMarkups else None
		if globalSettings.defaultMarkup:
			mc = markups.find_markup_class_by_name(globalSettings.defaultMarkup)
			if mc and mc.available():
				self.defaultMarkup = mc
		if len(availableMarkups) > 1:
			self.chooseGroup = QActionGroup(self)
			markupActions = []
			for markup in availableMarkups:
				markupAction = self.act(markup.name, trigbool=self.markupFunction(markup))
				if markup == self.defaultMarkup:
					markupAction.setChecked(True)
				self.chooseGroup.addAction(markupAction)
				markupActions.append(markupAction)
		self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold,
			trig=lambda: self.insertChars('**'))
		self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic,
			trig=lambda: self.insertChars('*'))
		self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline,
			trig=lambda: self.insertTag('u'))
		self.usefulTags = ('a', 'big', 'center', 'img', 's', 'small', 'span',
			'table', 'td', 'tr', 'u')
		self.usefulChars = ('deg', 'divide', 'dollar', 'hellip', 'laquo', 'larr',
			'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo',
			'rarr', 'rsquo', 'times')
		self.tagsBox = QComboBox(self.editBar)
		self.tagsBox.addItem(self.tr('Tags'))
		self.tagsBox.addItems(self.usefulTags)
		self.tagsBox.activated.connect(self.insertTag)
		self.symbolBox = QComboBox(self.editBar)
		self.symbolBox.addItem(self.tr('Symbols'))
		self.symbolBox.addItems(self.usefulChars)
		self.symbolBox.activated.connect(self.insertSymbol)
		self.updateStyleSheet()
		menubar = QMenuBar(self)
		menubar.setGeometry(QRect(0, 0, 800, 25))
		self.setMenuBar(menubar)
		menuFile = menubar.addMenu(self.tr('File'))
		menuEdit = menubar.addMenu(self.tr('Edit'))
		menuHelp = menubar.addMenu(self.tr('Help'))
		menuFile.addAction(self.actionNew)
		menuFile.addAction(self.actionOpen)
		self.menuRecentFiles = menuFile.addMenu(self.tr('Open recent'))
		self.menuRecentFiles.aboutToShow.connect(self.updateRecentFiles)
		menuFile.addMenu(self.menuRecentFiles)
		menuFile.addAction(self.actionShow)
		menuFile.addAction(self.actionSetEncoding)
		menuFile.addAction(self.actionReload)
		menuFile.addSeparator()
		menuFile.addAction(self.actionSave)
		menuFile.addAction(self.actionSaveAs)
		menuFile.addSeparator()
		menuFile.addAction(self.actionNextTab)
		menuFile.addAction(self.actionPrevTab)
		menuFile.addSeparator()
		menuExport = menuFile.addMenu(self.tr('Export'))
		menuExport.addAction(self.actionSaveHtml)
		menuExport.addAction(self.actionOdf)
		menuExport.addAction(self.actionPdf)
		if self.extensionActions:
			menuExport.addSeparator()
			for action, mimetype in self.extensionActions:
				menuExport.addAction(action)
			menuExport.aboutToShow.connect(self.updateExtensionsVisibility)
		menuFile.addAction(self.actionPrint)
		menuFile.addAction(self.actionPrintPreview)
		menuFile.addSeparator()
		menuFile.addAction(self.actionQuit)
		menuEdit.addAction(self.actionUndo)
		menuEdit.addAction(self.actionRedo)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionCut)
		menuEdit.addAction(self.actionCopy)
		menuEdit.addAction(self.actionPaste)
		menuEdit.addSeparator()
		if enchant_available:
			menuSC = menuEdit.addMenu(self.tr('Spell check'))
			menuSC.addAction(self.actionEnableSC)
			menuSC.addAction(self.actionSetLocale)
		menuEdit.addAction(self.actionSearch)
		menuEdit.addAction(self.actionChangeEditorFont)
		menuEdit.addAction(self.actionChangePreviewFont)
		menuEdit.addSeparator()
		if len(availableMarkups) > 1:
			self.menuMode = menuEdit.addMenu(self.tr('Default markup'))
			for markupAction in markupActions:
				self.menuMode.addAction(markupAction)
		menuFormat = menuEdit.addMenu(self.tr('Formatting'))
		menuFormat.addAction(self.actionBold)
		menuFormat.addAction(self.actionItalic)
		menuFormat.addAction(self.actionUnderline)
		menuEdit.addAction(self.actionWebKit)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionViewHtml)
		menuEdit.addAction(self.actionLivePreview)
		menuEdit.addAction(self.actionPreview)
		menuEdit.addAction(self.actionTableMode)
		if ReTextFakeVimHandler:
			menuEdit.addAction(self.actionFakeVimMode)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionFullScreen)
		menuEdit.addAction(self.actionConfig)
		menuHelp.addAction(self.actionHelp)
		menuHelp.addSeparator()
		menuHelp.addAction(self.actionAbout)
		menuHelp.addAction(self.actionAboutQt)
		menubar.addMenu(menuFile)
		menubar.addMenu(menuEdit)
		menubar.addMenu(menuHelp)
		toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
		toolBar.addAction(self.actionNew)
		toolBar.addSeparator()
		toolBar.addAction(self.actionOpen)
		toolBar.addAction(self.actionSave)
		toolBar.addAction(self.actionPrint)
		toolBar.addSeparator()
		toolBar.addAction(self.actionPreview)
		toolBar.addAction(self.actionFullScreen)
		self.editBar.addAction(self.actionUndo)
		self.editBar.addAction(self.actionRedo)
		self.editBar.addSeparator()
		self.editBar.addAction(self.actionCut)
		self.editBar.addAction(self.actionCopy)
		self.editBar.addAction(self.actionPaste)
		self.editBar.addSeparator()
		self.editBar.addWidget(self.tagsBox)
		self.editBar.addWidget(self.symbolBox)
		self.searchEdit = QLineEdit(self.searchBar)
		self.searchEdit.setPlaceholderText(self.tr('Search'))
		self.searchEdit.returnPressed.connect(self.find)
		self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar)
		self.searchBar.addWidget(self.searchEdit)
		self.searchBar.addSeparator()
		self.searchBar.addWidget(self.csBox)
		self.searchBar.addAction(self.actionFindPrev)
		self.searchBar.addAction(self.actionFind)
		self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
		self.searchBar.setVisible(False)
		self.autoSaveEnabled = globalSettings.autoSave
		if self.autoSaveEnabled:
			timer = QTimer(self)
			timer.start(60000)
			timer.timeout.connect(self.saveAll)
		self.ind = None
		if enchant_available:
			self.sl = globalSettings.spellCheckLocale
			if self.sl:
				try:
					enchant.Dict(self.sl)
				except Exception as e:
					print(e, file=sys.stderr)
					self.sl = None
			if globalSettings.spellCheck:
				self.actionEnableSC.setChecked(True)
				self.enableSpellCheck(True)
		self.fileSystemWatcher = QFileSystemWatcher()
		self.fileSystemWatcher.fileChanged.connect(self.fileChanged)

	def updateStyleSheet(self):
		if globalSettings.styleSheet:
			sheetfile = QFile(globalSettings.styleSheet)
			sheetfile.open(QIODevice.ReadOnly)
			self.ss = QTextStream(sheetfile).readAll()
			sheetfile.close()
		else:
			self.ss = ''

	def initTabWidget(self):
		def dragEnterEvent(e):
			e.acceptProposedAction()
		def dropEvent(e):
			fn = bytes(e.mimeData().data('text/plain')).decode().rstrip()
			if fn.startswith('file:'):
				fn = QUrl(fn).toLocalFile()
			self.openFileWrapper(fn)
		self.tabWidget.setTabsClosable(True)
		self.tabWidget.setAcceptDrops(True)
		self.tabWidget.dragEnterEvent = dragEnterEvent
		self.tabWidget.dropEvent = dropEvent

	def act(self, name, icon=None, trig=None, trigbool=None, shct=None):
		if not isinstance(shct, QKeySequence):
			shct = QKeySequence(shct)
		if icon:
			action = QAction(self.actIcon(icon), name, self)
		else:
			action = QAction(name, self)
		if trig:
			action.triggered.connect(trig)
		elif trigbool:
			action.setCheckable(True)
			action.triggered[bool].connect(trigbool)
		if shct:
			action.setShortcut(shct)
		return action

	def actIcon(self, name):
		return QIcon.fromTheme(name, QIcon(icon_path+name+'.png'))

	def printError(self):
		import traceback
		print('Exception occured while parsing document:', file=sys.stderr)
		traceback.print_exc()

	def getSplitter(self, index):
		splitter = QSplitter(Qt.Horizontal)
		# Give both boxes a minimum size so the minimumSizeHint will be
		# ignored when splitter.setSizes is called below
		for widget in self.editBoxes[index], self.previewBoxes[index]:
			widget.setMinimumWidth(125)
			splitter.addWidget(widget)
		splitter.setSizes((50, 50))
		splitter.setChildrenCollapsible(False)
		return splitter

	def getWebView(self):
		webView = QWebView()
		if not globalSettings.handleWebLinks:
			webView.page().setLinkDelegationPolicy(QWebPage.DelegateExternalLinks)
			webView.page().linkClicked.connect(QDesktopServices.openUrl)
		settings = webView.settings()
		settings.setAttribute(QWebSettings.LocalContentCanAccessFileUrls, False)
		settings.setDefaultTextEncoding('utf-8')
		return webView

	def createTab(self, fileName):
		self.previewBlocked = False
		self.editBoxes.append(ReTextEdit(self))
		self.highlighters.append(ReTextHighlighter(self.editBoxes[-1].document()))
		if enchant_available and self.actionEnableSC.isChecked():
			self.highlighters[-1].dictionary = \
			enchant.Dict(self.sl) if self.sl else enchant.Dict()
			self.highlighters[-1].rehighlight()
		if globalSettings.useWebKit:
			self.previewBoxes.append(self.getWebView())
		else:
			self.previewBoxes.append(QTextBrowser())
			self.previewBoxes[-1].setOpenExternalLinks(True)
		self.previewBoxes[-1].setVisible(False)
		self.fileNames.append(fileName)
		markupClass = self.getMarkupClass(fileName)
		self.markups.append(self.getMarkup(fileName))
		self.highlighters[-1].docType = (markupClass.name if markupClass else '')
		liveMode = globalSettings.restorePreviewState and globalSettings.previewState
		self.actionPreviewChecked.append(liveMode)
		self.actionLivePreviewChecked.append(liveMode)
		metrics = QFontMetrics(self.editBoxes[-1].font())
		self.editBoxes[-1].setTabStopWidth(globalSettings.tabWidth * metrics.width(' '))
		self.editBoxes[-1].textChanged.connect(self.updateLivePreviewBox)
		self.editBoxes[-1].undoAvailable.connect(self.actionUndo.setEnabled)
		self.editBoxes[-1].redoAvailable.connect(self.actionRedo.setEnabled)
		self.editBoxes[-1].copyAvailable.connect(self.enableCopy)
		self.editBoxes[-1].document().modificationChanged.connect(self.modificationChanged)
		if globalSettings.useFakeVim:
			self.installFakeVimHandler(self.editBoxes[-1])
		return self.getSplitter(-1)

	def closeTab(self, ind):
		if self.maybeSave(ind):
			if self.tabWidget.count() == 1:
				self.tabWidget.addTab(self.createTab(""), self.tr("New document"))
			if self.fileNames[ind]:
				self.fileSystemWatcher.removePath(self.fileNames[ind])
			del self.editBoxes[ind]
			del self.previewBoxes[ind]
			del self.highlighters[ind]
			del self.markups[ind]
			del self.fileNames[ind]
			del self.actionPreviewChecked[ind]
			del self.actionLivePreviewChecked[ind]
			self.tabWidget.removeTab(ind)

	def getMarkupClass(self, fileName=None):
		if fileName is None:
			fileName = self.fileNames[self.ind]
		if fileName:
			markupClass = markups.get_markup_for_file_name(
				fileName, return_class=True)
			if markupClass:
				return markupClass
		return self.defaultMarkup

	def getMarkup(self, fileName=None):
		if fileName is None:
			fileName = self.fileNames[self.ind]
		markupClass = self.getMarkupClass(fileName=fileName)
		if markupClass and markupClass.available():
			return markupClass(filename=fileName)

	def docTypeChanged(self):
		oldType = self.highlighters[self.ind].docType
		markupClass = self.getMarkupClass()
		newType = markupClass.name if markupClass else ''
		if oldType != newType:
			self.markups[self.ind] = self.getMarkup()
			self.updatePreviewBox()
			self.highlighters[self.ind].docType = newType
			self.highlighters[self.ind].rehighlight()
		dtMarkdown = (newType == DOCTYPE_MARKDOWN)
		dtMkdOrReST = (newType in (DOCTYPE_MARKDOWN, DOCTYPE_REST))
		self.tagsBox.setEnabled(dtMarkdown)
		self.symbolBox.setEnabled(dtMarkdown)
		self.actionUnderline.setEnabled(dtMarkdown)
		self.actionBold.setEnabled(dtMkdOrReST)
		self.actionItalic.setEnabled(dtMkdOrReST)
		canReload = bool(self.fileNames[self.ind]) and not self.autoSaveActive()
		self.actionSetEncoding.setEnabled(canReload)
		self.actionReload.setEnabled(canReload)

	def changeIndex(self, ind):
		if ind > -1:
			self.actionUndo.setEnabled(self.editBoxes[ind].document().isUndoAvailable())
			self.actionRedo.setEnabled(self.editBoxes[ind].document().isRedoAvailable())
			self.actionCopy.setEnabled(self.editBoxes[ind].textCursor().hasSelection())
			self.actionCut.setEnabled(self.editBoxes[ind].textCursor().hasSelection())
			self.actionPreview.setChecked(self.actionPreviewChecked[ind])
			self.actionLivePreview.setChecked(self.actionLivePreviewChecked[ind])
			self.actionTableMode.setChecked(self.editBoxes[ind].tableModeEnabled)
			self.editBar.setDisabled(self.actionPreviewChecked[ind])
		self.ind = ind
		if self.fileNames[ind]:
			self.setCurrentFile()
		else:
			self.setWindowTitle(self.tr('New document') + '[*]')
			self.docTypeChanged()
		self.modificationChanged(self.editBoxes[ind].document().isModified())
		if globalSettings.restorePreviewState:
			globalSettings.previewState = self.actionLivePreviewChecked[ind]
		if self.actionLivePreviewChecked[ind]:
			self.enableLivePreview(True)
		self.editBoxes[self.ind].setFocus(Qt.OtherFocusReason)

	def changeEditorFont(self):
		font, ok = QFontDialog.getFont(globalSettings.editorFont, self)
		if ok:
			globalSettings.editorFont = font
			for editor in self.editBoxes:
				editor.updateFont()

	def changePreviewFont(self):
		font, ok = QFontDialog.getFont(globalSettings.font, self)
		if ok:
			globalSettings.font = font
			self.updatePreviewBox()

	def preview(self, viewmode):
		self.actionPreviewChecked[self.ind] = viewmode
		if self.actionLivePreview.isChecked():
			self.actionLivePreview.setChecked(False)
			return self.enableLivePreview(False)
		self.editBar.setDisabled(viewmode)
		self.editBoxes[self.ind].setVisible(not viewmode)
		self.previewBoxes[self.ind].setVisible(viewmode)
		if viewmode:
			self.updatePreviewBox()

	def enableLivePreview(self, livemode):
		if globalSettings.restorePreviewState:
			globalSettings.previewState = livemode
		self.actionLivePreviewChecked[self.ind] = livemode
		self.actionPreviewChecked[self.ind] = livemode
		self.actionPreview.setChecked(livemode)
		self.editBar.setEnabled(True)
		self.previewBoxes[self.ind].setVisible(livemode)
		self.editBoxes[self.ind].setVisible(True)
		if livemode:
			self.updatePreviewBox()

	def enableWebKit(self, enable):
		globalSettings.useWebKit = enable
		oldind = self.ind
		self.tabWidget.clear()
		for self.ind in range(len(self.editBoxes)):
			if enable:
				self.previewBoxes[self.ind] = self.getWebView()
			else:
				self.previewBoxes[self.ind] = QTextBrowser()
				self.previewBoxes[self.ind].setOpenExternalLinks(True)
			splitter = self.getSplitter(self.ind)
			self.tabWidget.addTab(splitter, self.getDocumentTitle(baseName=True))
			self.updatePreviewBox()
			self.previewBoxes[self.ind].setVisible(self.actionPreviewChecked[self.ind])
		self.ind = oldind
		self.tabWidget.setCurrentIndex(self.ind)

	def enableCopy(self, copymode):
		self.actionCopy.setEnabled(copymode)
		self.actionCut.setEnabled(copymode)

	def enableFullScreen(self, yes):
		if yes:
			self.showFullScreen()
		else:
			self.showNormal()

	def openConfigDialog(self):
		dlg = ConfigDialog(self)
		dlg.setWindowTitle(self.tr('Preferences'))
		dlg.show()

	def installFakeVimHandler(self, editor):
		if ReTextFakeVimHandler:
			fakeVimEditor = ReTextFakeVimHandler(editor, self)
			fakeVimEditor.setSaveAction(self.actionSave)
			fakeVimEditor.setQuitAction(self.actionQuit)
			self.actionFakeVimMode.triggered.connect(fakeVimEditor.remove)

	def enableFakeVimMode(self, yes):
		globalSettings.useFakeVim = yes
		if yes:
			FakeVimMode.init(self)
			for editor in self.editBoxes:
				self.installFakeVimHandler(editor)
		else:
			FakeVimMode.exit(self)

	def enableSpellCheck(self, yes):
		if yes:
			if self.sl:
				self.setAllDictionaries(enchant.Dict(self.sl))
			else:
				self.setAllDictionaries(enchant.Dict())
		else:
			self.setAllDictionaries(None)
		globalSettings.spellCheck = yes

	def setAllDictionaries(self, dictionary):
		for hl in self.highlighters:
			hl.dictionary = dictionary
			hl.rehighlight()

	def changeLocale(self):
		if self.sl:
			localedlg = LocaleDialog(self, defaultText=self.sl)
		else:
			localedlg = LocaleDialog(self)
		if localedlg.exec() != QDialog.Accepted:
			return
		sl = localedlg.localeEdit.text()
		setdefault = localedlg.checkBox.isChecked()
		if sl:
			try:
				sl = str(sl)
				enchant.Dict(sl)
			except Exception as e:
				QMessageBox.warning(self, '', str(e))
			else:
				self.sl = sl
				self.enableSpellCheck(self.actionEnableSC.isChecked())
		else:
			self.sl = None
			self.enableSpellCheck(self.actionEnableSC.isChecked())
		if setdefault:
			globalSettings.spellCheckLocale = sl

	def searchBarVisibilityChanged(self, visible):
		self.actionSearch.setChecked(visible)
		if visible:
			self.searchEdit.setFocus(Qt.ShortcutFocusReason)

	def find(self, back=False):
		flags = QTextDocument.FindFlags()
		if back:
			flags |= QTextDocument.FindBackward
		if self.csBox.isChecked():
			flags |= QTextDocument.FindCaseSensitively
		text = self.searchEdit.text()
		editBox = self.editBoxes[self.ind]
		cursor = editBox.textCursor()
		newCursor = editBox.document().find(text, cursor, flags)
		if not newCursor.isNull():
			editBox.setTextCursor(newCursor)
			return self.setSearchEditColor(True)
		cursor.movePosition(QTextCursor.End if back else QTextCursor.Start)
		newCursor = editBox.document().find(text, cursor, flags)
		if not newCursor.isNull():
			editBox.setTextCursor(newCursor)
			return self.setSearchEditColor(True)
		self.setSearchEditColor(False)

	def setSearchEditColor(self, found):
		palette = self.searchEdit.palette()
		palette.setColor(QPalette.Active, QPalette.Base,
		                 Qt.white if found else QColor(255, 102, 102))
		self.searchEdit.setPalette(palette)

	def getHtml(self, includeStyleSheet=True, includeTitle=True,
	            includeMeta=False, webenv=False):
		if self.markups[self.ind] is None:
			markupClass = self.getMarkupClass()
			errMsg = self.tr('Could not parse file contents, check if '
			'you have the <a href="%s">necessary module</a> installed!')
			try:
				errMsg %= markupClass.attributes[MODULE_HOME_PAGE]
			except (AttributeError, KeyError):
				# Remove the link if markupClass doesn't have the needed attribute
				errMsg = errMsg.replace('<a href="%s">', '')
				errMsg = errMsg.replace('</a>', '')
			return '<p style="color: red">%s</p>' % errMsg
		text = self.editBoxes[self.ind].toPlainText()
		headers = ''
		if includeStyleSheet:
			headers += '<style type="text/css">\n' + self.ss + '</style>\n'
		cssFileName = self.getDocumentTitle(baseName=True)+'.css'
		if QFile(cssFileName).exists():
			headers += '<link rel="stylesheet" type="text/css" href="%s">\n' \
			% cssFileName
		if includeMeta:
			headers += ('<meta name="generator" content="ReText %s">\n' %
			            app_version)
		fallbackTitle = self.getDocumentTitle() if includeTitle else ''
		return self.markups[self.ind].get_whole_html(text,
			custom_headers=headers, include_stylesheet=includeStyleSheet,
			fallback_title=fallbackTitle, webenv=webenv)

	def updatePreviewBox(self):
		self.previewBlocked = False
		pb = self.previewBoxes[self.ind]
		textedit = isinstance(pb, QTextEdit)
		if textedit:
			scrollbar = pb.verticalScrollBar()
			disttobottom = scrollbar.maximum() - scrollbar.value()
		else:
			frame = pb.page().mainFrame()
			scrollpos = frame.scrollPosition()
		try:
			html = self.getHtml()
		except Exception:
			return self.printError()
		if textedit:
			pb.setHtml(html)
			pb.document().setDefaultFont(globalSettings.font)
			scrollbar.setValue(scrollbar.maximum() - disttobottom)
		else:
			pb.settings().setFontFamily(QWebSettings.StandardFont,
			                            globalSettings.font.family())
			pb.settings().setFontSize(QWebSettings.DefaultFontSize,
			                          globalSettings.font.pointSize())
			pb.setHtml(html, QUrl.fromLocalFile(self.fileNames[self.ind]))
			frame.setScrollPosition(scrollpos)

	def updateLivePreviewBox(self):
		if self.actionLivePreview.isChecked() and self.previewBlocked == False:
			self.previewBlocked = True
			QTimer.singleShot(1000, self.updatePreviewBox)

	def showInDir(self):
		if self.fileNames[self.ind]:
			path = QFileInfo(self.fileNames[self.ind]).path()
			QDesktopServices.openUrl(QUrl.fromLocalFile(path))
		else:
			QMessageBox.warning(self, '', self.tr("Please, save the file somewhere."))

	def setCurrentFile(self):
		self.setWindowTitle("")
		self.tabWidget.setTabText(self.ind, self.getDocumentTitle(baseName=True))
		self.setWindowFilePath(self.fileNames[self.ind])
		files = readListFromSettings("recentFileList")
		while self.fileNames[self.ind] in files:
			files.remove(self.fileNames[self.ind])
		files.insert(0, self.fileNames[self.ind])
		if len(files) > 10:
			del files[10:]
		writeListToSettings("recentFileList", files)
		QDir.setCurrent(QFileInfo(self.fileNames[self.ind]).dir().path())
		self.docTypeChanged()

	def createNew(self, text=None):
		self.tabWidget.addTab(self.createTab(""), self.tr("New document"))
		self.ind = self.tabWidget.count()-1
		self.tabWidget.setCurrentIndex(self.ind)
		if text:
			self.editBoxes[self.ind].textCursor().insertText(text)

	def switchTab(self, shift=1):
		self.tabWidget.setCurrentIndex((self.ind + shift) % self.tabWidget.count())

	def updateRecentFiles(self):
		self.menuRecentFiles.clear()
		self.recentFilesActions = []
		filesOld = readListFromSettings("recentFileList")
		files = []
		for f in filesOld:
			if QFile.exists(f):
				files.append(f)
				self.recentFilesActions.append(self.act(f, trig=self.openFunction(f)))
		writeListToSettings("recentFileList", files)
		for action in self.recentFilesActions:
			self.menuRecentFiles.addAction(action)

	def markupFunction(self, markup):
		return lambda: self.setDefaultMarkup(markup)

	def openFunction(self, fileName):
		return lambda: self.openFileWrapper(fileName)

	def extensionFuntion(self, data):
		return lambda: \
		self.runExtensionCommand(data['Exec'], data['FileFilter'], data['DefaultExtension'])

	def getExportExtensionsList(self):
		extensions = []
		for extsprefix in datadirs:
			extsdir = QDir(extsprefix+'/export-extensions/')
			if extsdir.exists():
				for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'],
				QDir.Files | QDir.Readable):
					extensions.append(self.readExtension(fileInfo.filePath()))
		locale = QLocale.system().name()
		self.extensionActions = []
		for extension in extensions:
			try:
				if ('Name[%s]' % locale) in extension:
					name = extension['Name[%s]' % locale]
				elif ('Name[%s]' % locale.split('_')[0]) in extension:
					name = extension['Name[%s]' % locale.split('_')[0]]
				else:
					name = extension['Name']
				data = {}
				for prop in ('FileFilter', 'DefaultExtension', 'Exec'):
					if 'X-ReText-'+prop in extension:
						data[prop] = extension['X-ReText-'+prop]
					elif prop in extension:
						data[prop] = extension[prop]
					else:
						data[prop] = ''
				action = self.act(name, trig=self.extensionFuntion(data))
				if 'Icon' in extension:
					action.setIcon(self.actIcon(extension['Icon']))
				mimetype = extension['MimeType'] if 'MimeType' in extension else None
			except KeyError:
				print('Failed to parse extension: Name is required', file=sys.stderr)
			else:
				self.extensionActions.append((action, mimetype))

	def updateExtensionsVisibility(self):
		markupClass = self.getMarkupClass()
		for action in self.extensionActions:
			if markupClass is None:
				action[0].setEnabled(False)
				continue
			mimetype = action[1]
			if mimetype == None:
				enabled = True
			elif markupClass == markups.MarkdownMarkup:
				enabled = (mimetype in ("text/x-retext-markdown", "text/x-markdown"))
			elif markupClass == markups.ReStructuredTextMarkup:
				enabled = (mimetype in ("text/x-retext-rst", "text/x-rst"))
			else:
				enabled = False
			action[0].setEnabled(enabled)

	def readExtension(self, fileName):
		extFile = QFile(fileName)
		extFile.open(QIODevice.ReadOnly)
		extension = {}
		stream = QTextStream(extFile)
		while not stream.atEnd():
			line = stream.readLine()
			if '=' in line:
				index = line.index('=')
				extension[line[:index].rstrip()] = line[index+1:].lstrip()
		extFile.close()
		return extension

	def openFile(self):
		supportedExtensions = ['.txt']
		for markup in markups.get_all_markups():
			supportedExtensions += markup.file_extensions
		fileFilter = ' (' + str.join(' ', ['*'+ext for ext in supportedExtensions]) + ');;'
		fileNames = QFileDialog.getOpenFileNames(self,
			self.tr("Select one or several files to open"), "",
			self.tr("Supported files") + fileFilter + self.tr("All files (*)"))
		for fileName in fileNames[0]:
			self.openFileWrapper(fileName)

	def openFileWrapper(self, fileName):
		if not fileName:
			return
		fileName = QFileInfo(fileName).canonicalFilePath()
		exists = False
		for i in range(self.tabWidget.count()):
			if self.fileNames[i] == fileName:
				exists = True
				ex = i
		if exists:
			self.tabWidget.setCurrentIndex(ex)
		elif QFile.exists(fileName):
			noEmptyTab = (
				(self.ind is None) or
				self.fileNames[self.ind] or
				self.editBoxes[self.ind].toPlainText() or
				self.editBoxes[self.ind].document().isModified()
			)
			if noEmptyTab:
				self.tabWidget.addTab(self.createTab(fileName), "")
				self.ind = self.tabWidget.count()-1
				self.tabWidget.setCurrentIndex(self.ind)
			if fileName:
				self.fileSystemWatcher.addPath(fileName)
			self.fileNames[self.ind] = fileName
			self.openFileMain()

	def openFileMain(self, encoding=None):
		openfile = QFile(self.fileNames[self.ind])
		openfile.open(QIODevice.ReadOnly)
		stream = QTextStream(openfile)
		if encoding:
			stream.setCodec(encoding)
		elif globalSettings.defaultCodec:
			stream.setCodec(globalSettings.defaultCodec)
		text = stream.readAll()
		openfile.close()
		markupClass = markups.get_markup_for_file_name(
			self.fileNames[self.ind], return_class=True)
		self.highlighters[self.ind].docType = (markupClass.name if markupClass else '')
		self.markups[self.ind] = self.getMarkup()
		if self.defaultMarkup:
			self.highlighters[self.ind].docType = self.defaultMarkup.name
		editBox = self.editBoxes[self.ind]
		modified = bool(encoding) and (editBox.toPlainText() != text)
		editBox.setPlainText(text)
		self.setCurrentFile()
		editBox.document().setModified(modified)
		self.setWindowModified(modified)

	def showEncodingDialog(self):
		if not self.maybeSave(self.ind):
			return
		encoding, ok = QInputDialog.getItem(self, '',
			self.tr('Select file encoding from the list:'),
			[bytes(b).decode() for b in QTextCodec.availableCodecs()],
			0, False)
		if ok:
			self.openFileMain(encoding)

	def saveFile(self):
		self.saveFileMain(dlg=False)

	def saveFileAs(self):
		self.saveFileMain(dlg=True)

	def saveAll(self):
		oldind = self.ind
		for self.ind in range(self.tabWidget.count()):
			if self.fileNames[self.ind] and QFileInfo(self.fileNames[self.ind]).isWritable():
				self.saveFileCore(self.fileNames[self.ind])
				self.editBoxes[self.ind].document().setModified(False)
		self.ind = oldind

	def saveFileMain(self, dlg):
		if (not self.fileNames[self.ind]) or dlg:
			markupClass = self.getMarkupClass()
			if (markupClass is None) or not hasattr(markupClass, 'default_extension'):
				defaultExt = self.tr("Plain text (*.txt)")
				ext = ".txt"
			else:
				defaultExt = self.tr('%s files',
					'Example of final string: Markdown files') \
					% markupClass.name + ' (' + str.join(' ',
					('*'+extension for extension in markupClass.file_extensions)) + ')'
				if markupClass == markups.MarkdownMarkup:
					ext = globalSettings.markdownDefaultFileExtension
				elif markupClass == markups.ReStructuredTextMarkup:
					ext = globalSettings.restDefaultFileExtension
				else:
					ext = markupClass.default_extension
			newFileName = QFileDialog.getSaveFileName(self,
				self.tr("Save file"), "", defaultExt)[0]
			if newFileName:
				if not QFileInfo(newFileName).suffix():
					newFileName += ext
				if self.fileNames[self.ind]:
					self.fileSystemWatcher.removePath(self.fileNames[self.ind])
				self.fileNames[self.ind] = newFileName
				self.actionSetEncoding.setDisabled(self.autoSaveActive())
		if self.fileNames[self.ind]:
			result = self.saveFileCore(self.fileNames[self.ind])
			if result:
				self.setCurrentFile()
				self.editBoxes[self.ind].document().setModified(False)
				self.setWindowModified(False)
				return True
			else:
				QMessageBox.warning(self, '',
				self.tr("Cannot save to file because it is read-only!"))
		return False

	def saveFileCore(self, fn, addToWatcher=True):
		self.fileSystemWatcher.removePath(fn)
		savefile = QFile(fn)
		result = savefile.open(QIODevice.WriteOnly)
		if result:
			savestream = QTextStream(savefile)
			if globalSettings.defaultCodec:
				savestream.setCodec(globalSettings.defaultCodec)
			savestream << self.editBoxes[self.ind].toPlainText()
			savefile.close()
		if result and addToWatcher:
			self.fileSystemWatcher.addPath(fn)
		return result

	def saveHtml(self, fileName):
		if not QFileInfo(fileName).suffix():
			fileName += ".html"
		try:
			htmltext = self.getHtml(includeStyleSheet=False, includeMeta=True,
			webenv=True)
		except Exception:
			return self.printError()
		htmlFile = QFile(fileName)
		htmlFile.open(QIODevice.WriteOnly)
		html = QTextStream(htmlFile)
		if globalSettings.defaultCodec:
			html.setCodec(globalSettings.defaultCodec)
		html << htmltext
		htmlFile.close()

	def textDocument(self):
		td = QTextDocument()
		td.setMetaInformation(QTextDocument.DocumentTitle, self.getDocumentTitle())
		if self.ss:
			td.setDefaultStyleSheet(self.ss)
		td.setHtml(self.getHtml())
		td.setDefaultFont(globalSettings.font)
		return td

	def saveOdf(self):
		try:
			document = self.textDocument()
		except Exception:
			return self.printError()
		fileName = QFileDialog.getSaveFileName(self,
			self.tr("Export document to ODT"), "",
			self.tr("OpenDocument text files (*.odt)"))[0]
		if not QFileInfo(fileName).suffix():
			fileName += ".odt"
		writer = QTextDocumentWriter(fileName)
		writer.setFormat("odf")
		writer.write(document)

	def saveFileHtml(self):
		fileName = QFileDialog.getSaveFileName(self,
			self.tr("Save file"), "",
			self.tr("HTML files (*.html *.htm)"))[0]
		if fileName:
			self.saveHtml(fileName)

	def getDocumentForPrint(self):
		if globalSettings.useWebKit:
			return self.previewBoxes[self.ind]
		try:
			return self.textDocument()
		except Exception:
			self.printError()

	def standardPrinter(self):
		printer = QPrinter(QPrinter.HighResolution)
		printer.setDocName(self.getDocumentTitle())
		printer.setCreator('ReText %s' % app_version)
		return printer

	def savePdf(self):
		self.updatePreviewBox()
		fileName = QFileDialog.getSaveFileName(self,
			self.tr("Export document to PDF"),
			"", self.tr("PDF files (*.pdf)"))[0]
		if fileName:
			if not QFileInfo(fileName).suffix():
				fileName += ".pdf"
			printer = self.standardPrinter()
			printer.setOutputFormat(QPrinter.PdfFormat)
			printer.setOutputFileName(fileName)
			document = self.getDocumentForPrint()
			if document != None:
				document.print(printer)

	def printFile(self):
		self.updatePreviewBox()
		printer = self.standardPrinter()
		dlg = QPrintDialog(printer, self)
		dlg.setWindowTitle(self.tr("Print document"))
		if (dlg.exec() == QDialog.Accepted):
			document = self.getDocumentForPrint()
			if document != None:
				document.print(printer)

	def printPreview(self):
		document = self.getDocumentForPrint()
		if document == None:
			return
		printer = self.standardPrinter()
		preview = QPrintPreviewDialog(printer, self)
		preview.paintRequested.connect(document.print)
		preview.exec()

	def runExtensionCommand(self, command, filefilter, defaultext):
		of = ('%of' in command)
		html = ('%html' in command)
		if of:
			if defaultext and not filefilter:
				filefilter = '*'+defaultext
			fileName = QFileDialog.getSaveFileName(self,
				self.tr('Export document'), '', filefilter)[0]
			if not fileName:
				return
			if defaultext and not QFileInfo(fileName).suffix():
				fileName += defaultext
		basename = '.%s.retext-temp' % self.getDocumentTitle(baseName=True)
		if html:
			tmpname = basename+'.html'
			self.saveHtml(tmpname)
		else:
			tmpname = basename+self.getMarkupClass().default_extension
			self.saveFileCore(tmpname, addToWatcher=False)
		command = command.replace('%of', '"out'+defaultext+'"')
		command = command.replace('%html' if html else '%if', '"'+tmpname+'"')
		try:
			Popen(str(command), shell=True).wait()
		except Exception as error:
			errorstr = str(error)
			QMessageBox.warning(self, '', self.tr('Failed to execute the command:')
			+ '\n' + errorstr)
		QFile(tmpname).remove()
		if of:
			QFile('out'+defaultext).rename(fileName)

	def getDocumentTitle(self, baseName=False):
		markup = self.markups[self.ind]
		realTitle = ''
		if markup and not baseName:
			text = self.editBoxes[self.ind].toPlainText()
			try:
				realTitle = markup.get_document_title(text)
			except Exception:
				self.printError()
		if realTitle:
			return realTitle
		elif self.fileNames[self.ind]:
			fileinfo = QFileInfo(self.fileNames[self.ind])
			basename = fileinfo.completeBaseName()
			return (basename if basename else fileinfo.fileName())
		return self.tr("New document")

	def autoSaveActive(self):
		return self.autoSaveEnabled and self.fileNames[self.ind] and \
		QFileInfo(self.fileNames[self.ind]).isWritable()

	def modificationChanged(self, changed):
		if self.autoSaveActive():
			changed = False
		self.actionSave.setEnabled(changed)
		self.setWindowModified(changed)

	def clipboardDataChanged(self):
		mimeData = QApplication.instance().clipboard().mimeData()
		if mimeData is not None:
			self.actionPaste.setEnabled(mimeData.hasText())

	def insertChars(self, chars):
		tc = self.editBoxes[self.ind].textCursor()
		if tc.hasSelection():
			selection = tc.selectedText()
			if selection.startswith(chars) and selection.endswith(chars):
				if len(selection) > 2*len(chars):
					selection = selection[len(chars):-len(chars)]
					tc.insertText(selection)
			else:
				tc.insertText(chars+tc.selectedText()+chars)
		else:
			tc.insertText(chars)

	def insertTag(self, ut):
		if not ut:
			return
		if isinstance(ut, int):
			ut = self.usefulTags[ut - 1]
		arg = ' style=""' if ut == 'span' else ''
		tc = self.editBoxes[self.ind].textCursor()
		if ut == 'img':
			toinsert = ('<a href="' + tc.selectedText() +
			'" target="_blank"><img src="' + tc.selectedText() + '"/></a>')
		elif ut == 'a':
			toinsert = ('<a href="' + tc.selectedText() +
			'" target="_blank">' + tc.selectedText() + '</a>')
		else:
			toinsert = '<'+ut+arg+'>'+tc.selectedText()+'</'+ut+'>'
		tc.insertText(toinsert)
		self.tagsBox.setCurrentIndex(0)

	def insertSymbol(self, num):
		if num:
			self.editBoxes[self.ind].insertPlainText('&'+self.usefulChars[num-1]+';')
		self.symbolBox.setCurrentIndex(0)

	def fileChanged(self, fileName):
		ind = self.fileNames.index(fileName)
		self.tabWidget.setCurrentIndex(ind)
		if not QFile.exists(fileName):
			self.editBoxes[ind].document().setModified(True)
			QMessageBox.warning(self, '', self.tr(
				'This file has been deleted by other application.\n'
				'Please make sure you save the file before exit.'))
		elif not self.editBoxes[ind].document().isModified():
			# File was not modified in ReText, reload silently
			self.openFileMain()
			self.updatePreviewBox()
		else:
			text = self.tr(
				'This document has been modified by other application.\n'
				'Do you want to reload the file (this will discard all '
				'your changes)?\n')
			if self.autoSaveEnabled:
				text += self.tr(
					'If you choose to not reload the file, auto save mode will '
					'be disabled for this session to prevent data loss.')
			messageBox = QMessageBox(QMessageBox.Warning, '', text)
			reloadButton = messageBox.addButton(self.tr('Reload'), QMessageBox.YesRole)
			messageBox.addButton(QMessageBox.Cancel)
			messageBox.exec()
			if messageBox.clickedButton() is reloadButton:
				self.openFileMain()
				self.updatePreviewBox()
			else:
				self.autoSaveEnabled = False
				self.editBoxes[ind].document().setModified(True)
		if fileName not in self.fileSystemWatcher.files():
			# https://github.com/retext-project/retext/issues/137
			self.fileSystemWatcher.addPath(fileName)

	def maybeSave(self, ind):
		if self.autoSaveActive():
			self.saveFileCore(self.fileNames[self.ind])
			return True
		if not self.editBoxes[ind].document().isModified():
			return True
		self.tabWidget.setCurrentIndex(ind)
		ret = QMessageBox.warning(self, '',
			self.tr("The document has been modified.\nDo you want to save your changes?"),
			QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
		if ret == QMessageBox.Save:
			return self.saveFileMain(False)
		elif ret == QMessageBox.Cancel:
			return False
		return True

	def closeEvent(self, closeevent):
		for self.ind in range(self.tabWidget.count()):
			if not self.maybeSave(self.ind):
				return closeevent.ignore()
		if globalSettings.saveWindowGeometry and not self.isMaximized():
			globalSettings.windowGeometry = self.saveGeometry()
		closeevent.accept()

	def viewHtml(self):
		htmlDlg = HtmlDialog(self)
		try:
			htmltext = self.getHtml(includeStyleSheet=False, includeTitle=False)
		except Exception:
			return self.printError()
		winTitle = self.getDocumentTitle(baseName=True)
		htmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+")")
		htmlDlg.textEdit.setPlainText(htmltext.rstrip())
		htmlDlg.hl.rehighlight()
		htmlDlg.show()
		htmlDlg.raise_()
		htmlDlg.activateWindow()

	def openHelp(self):
		QDesktopServices.openUrl(QUrl('https://github.com/retext-project/retext/wiki'))

	def aboutDialog(self):
		QMessageBox.about(self, self.aboutWindowTitle,
		'<p><b>' + (self.tr('ReText %s (using PyMarkups %s)') % (app_version, markups.__version__))
		+'</b></p>' + self.tr('Simple but powerful editor'
		' for Markdown and reStructuredText')
		+'</p><p>'+self.tr('Author: Dmitry Shachnev, 2011').replace('2011', '2011\u2013' '2015')
		+'<br><a href="https://github.com/retext-project/retext">'+self.tr('Website')
		+'</a> | <a href="http://daringfireball.net/projects/markdown/syntax">'
		+self.tr('Markdown syntax')
		+'</a> | <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">'
		+self.tr('reStructuredText syntax')+'</a></p>')

	def setDefaultMarkup(self, markup):
		self.defaultMarkup = markup
		defaultName = markups.get_available_markups()[0].name
		writeToSettings('defaultMarkup', markup.name, defaultName)
		oldind = self.ind
		for self.ind in range(len(self.previewBoxes)):
			self.docTypeChanged()
		self.ind = oldind
Esempio n. 56
0
class AppWindow(QMainWindow):
    "The application's main window"

    def __init__(self):
        super().__init__()
        self.initUI()
        self.start_up()
        QTimer.singleShot(3000, self._check_update)

    def init_watchers(self):
        def remove_gallery(g):
            index = self.manga_list_view.find_index(g.id)
            if index:
                self.manga_list_view.remove_gallery([index])

        def create_gallery(path):
            g_dia = gallerydialog.GalleryDialog(self, path)
            g_dia.SERIES.connect(self.manga_list_view.gallery_model.addRows)
            g_dia.show()

        def update_gallery(g):
            index = self.manga_list_view.find_index(g.id)
            if index:
                self.manga_list_view.replace_edit_gallery([g], index.row())
            else:
                log_e("Could not find gallery to update from Watcher")

        def created(path):
            c_popup = file_misc.CreatedPopup(path, self)
            c_popup.ADD_SIGNAL.connect(create_gallery)

        def modified(path, gallery):
            mod_popup = file_misc.ModifiedPopup(path, gallery, self)

        def deleted(path, gallery):
            d_popup = file_misc.DeletedPopup(path, gallery, self)
            d_popup.UPDATE_SIGNAL.connect(update_gallery)
            d_popup.REMOVE_SIGNAL.connect(remove_gallery)

        def moved(new_path, gallery):
            mov_popup = file_misc.MovedPopup(new_path, gallery, self)
            mov_popup.UPDATE_SIGNAL.connect(update_gallery)

        self.watchers = file_misc.Watchers()
        self.watchers.gallery_handler.CREATE_SIGNAL.connect(created)
        self.watchers.gallery_handler.MODIFIED_SIGNAL.connect(modified)
        self.watchers.gallery_handler.MOVED_SIGNAL.connect(moved)
        self.watchers.gallery_handler.DELETED_SIGNAL.connect(deleted)

        if gui_constants.LOOK_NEW_GALLERY_STARTUP:
            self.notification_bar.add_text("Looking for new galleries...")
            try:

                class ScanDir(QObject):
                    final_paths_and_galleries = pyqtSignal(list, list)

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

                    def scan_dirs(self):
                        db_data = self.model_data
                        paths = []
                        for g in range(len(db_data)):
                            paths.append(os.path.normcase(db_data[g].path))

                        contents = []
                        case_path = []  # needed for tile and artist parsing... e.g to avoid lowercase
                        for m_path in gui_constants.MONITOR_PATHS:
                            for p in os.listdir(m_path):
                                abs_p = os.path.join(m_path, p)
                                if os.path.isdir(abs_p) or p.endswith(utils.ARCHIVE_FILES):
                                    case_path.append(abs_p)
                                    contents.append(os.path.normcase(abs_p))

                        paths = sorted(paths)
                        new_galleries = []
                        for c, x in enumerate(contents):
                            y = utils.b_search(paths, x)
                            if not y:
                                # (path, number for case_path)
                                new_galleries.append((x, c))

                        galleries = []
                        final_paths = []
                        if new_galleries:
                            for g in new_galleries:
                                gallery = gallerydb.Gallery()
                                try:
                                    gallery.profile = utils.get_gallery_img(g[0])
                                except:
                                    gallery.profile = gui_constants.NO_IMAGE_PATH
                                parser_dict = utils.title_parser(os.path.split(case_path[g[1]])[1])
                                gallery.title = parser_dict["title"]
                                gallery.artist = parser_dict["artist"]
                                galleries.append(gallery)
                                final_paths.append(case_path[g[1]])
                        self.final_paths_and_galleries.emit(final_paths, galleries)
                        # if gui_constants.LOOK_NEW_GALLERY_AUTOADD:
                        # 	QTimer.singleShot(10000, self.gallery_populate(final_paths))
                        # 	return

                def show_new_galleries(final_paths, galleries):
                    if final_paths and galleries:
                        if gui_constants.LOOK_NEW_GALLERY_AUTOADD:
                            self.gallery_populate(final_paths)
                        else:
                            if len(galleries) == 1:
                                self.notification_bar.add_text(
                                    "{} new gallery was discovered in one of your monitored directories".format(
                                        len(galleries)
                                    )
                                )
                            else:
                                self.notification_bar.add_text(
                                    "{} new galleries were discovered in one of your monitored directories".format(
                                        len(galleries)
                                    )
                                )
                            text = (
                                "These new galleries were discovered! Do you want to add them?"
                                if len(galleries) > 1
                                else "This new gallery was discovered! Do you want to add it?"
                            )
                            g_popup = file_misc.GalleryPopup((text, galleries), self)
                            buttons = g_popup.add_buttons("Add", "Close")

                            def populate_n_close():
                                self.gallery_populate(final_paths)
                                g_popup.close()

                            buttons[0].clicked.connect(populate_n_close)
                            buttons[1].clicked.connect(g_popup.close)

                thread = QThread(self)
                self.scan_inst = ScanDir(self.manga_list_view.gallery_model._data)
                self.scan_inst.moveToThread(thread)
                self.scan_inst.final_paths_and_galleries.connect(show_new_galleries)
                self.scan_inst.final_paths_and_galleries.connect(lambda a: self.scan_inst.deleteLater())
                thread.started.connect(self.scan_inst.scan_dirs)
                thread.finished.connect(thread.deleteLater)
                thread.start()
            except:
                self.notification_bar.add_text(
                    "An error occured while attempting to scan for new galleries. Check happypanda.log."
                )
                log.exception("An error occured while attempting to scan for new galleries.")

    def start_up(self):
        def normalize_first_time():
            settings.set(2, "Application", "first time level")

        def done():
            self.manga_list_view.gallery_model.init_data()
            if gui_constants.ENABLE_MONITOR and gui_constants.MONITOR_PATHS and all(gui_constants.MONITOR_PATHS):
                self.init_watchers()
            if gui_constants.FIRST_TIME_LEVEL != 2:
                normalize_first_time()

        if gui_constants.FIRST_TIME_LEVEL < 2:

            class FirstTime(file_misc.BasePopup):
                def __init__(self, parent=None):
                    super().__init__(parent)
                    main_layout = QVBoxLayout()
                    info_lbl = QLabel(
                        "Hi there! Some big changes are about to occur!\n"
                        + "Please wait.. This will take at most a few minutes.\n"
                        + "If not then try restarting the application."
                    )
                    info_lbl.setAlignment(Qt.AlignCenter)
                    main_layout.addWidget(info_lbl)
                    prog = QProgressBar(self)
                    prog.setMinimum(0)
                    prog.setMaximum(0)
                    prog.setTextVisible(False)
                    main_layout.addWidget(prog)
                    main_layout.addWidget(QLabel("Note: This popup will close itself when everything is ready"))
                    self.main_widget.setLayout(main_layout)

            ft_widget = FirstTime(self)
            log_i("Invoking first time level 2")
            bridge = gallerydb.Bridge()
            thread = QThread(self)
            thread.setObjectName("Startup")
            bridge.moveToThread(thread)
            thread.started.connect(bridge.rebuild_galleries)
            bridge.DONE.connect(ft_widget.close)
            bridge.DONE.connect(self.setEnabled)
            bridge.DONE.connect(done)
            bridge.DONE.connect(bridge.deleteLater)
            thread.finished.connect(thread.deleteLater)
            thread.start()
            ft_widget.adjustSize()
            ft_widget.show()
            self.setEnabled(False)
        else:
            done()

    def initUI(self):
        self.center = QWidget()
        self.display = QStackedLayout()
        self.center.setLayout(self.display)
        # init the manga view variables
        self.manga_display()
        log_d("Create manga display: OK")
        # init the chapter view variables
        # self.chapter_display()
        self.m_l_view_index = self.display.addWidget(self.manga_list_main)
        self.m_t_view_index = self.display.addWidget(self.manga_table_view)
        # init toolbar
        self.init_toolbar()
        log_d("Create toolbar: OK")
        # init status bar
        self.init_stat_bar()
        log_d("Create statusbar: OK")

        self.system_tray = misc.SystemTray(QIcon(gui_constants.APP_ICO_PATH), self)
        gui_constants.SYSTEM_TRAY = self.system_tray
        tray_menu = QMenu(self)
        self.system_tray.setContextMenu(tray_menu)
        self.system_tray.setToolTip("Happypanda {}".format(gui_constants.vs))
        tray_quit = QAction("Quit", tray_menu)
        tray_menu.addAction(tray_quit)
        tray_quit.triggered.connect(self.close)
        self.system_tray.show()
        log_d("Create system tray: OK")

        # self.display.addWidget(self.chapter_main)

        self.setCentralWidget(self.center)
        self.setWindowTitle("Happypanda")
        self.setWindowIcon(QIcon(gui_constants.APP_ICO_PATH))

        props = settings.win_read(self, "AppWindow")
        if props.resize:
            x, y = props.resize
            self.resize(x, y)
        else:
            self.resize(gui_constants.MAIN_W, gui_constants.MAIN_H)
        posx, posy = props.pos
        self.move(posx, posy)
        self.show()
        log_d("Show window: OK")

        self.notification_bar = misc.NotificationOverlay(self)
        p = self.toolbar.pos()
        self.notification_bar.move(p.x(), p.y() + self.toolbar.height())
        self.notification_bar.resize(self.width())
        gui_constants.NOTIF_BAR = self.notification_bar
        log_d("Create notificationbar: OK")

        log_d("Window Create: OK")

    def _check_update(self):
        class upd_chk(QObject):
            UPDATE_CHECK = pyqtSignal(str)

            def __init__(self, **kwargs):
                super().__init__(**kwargs)

            def fetch_vs(self):
                import requests
                import time

                try:
                    log_d("Checking Update")
                    time.sleep(1.5)
                    r = requests.get(
                        "https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt", verify="cacert.pem"
                    )
                    a = r.text
                    vs = a.strip()
                    self.UPDATE_CHECK.emit(vs)
                except:
                    log.exception("Checking Update: FAIL")
                    self.UPDATE_CHECK.emit("this is a very long text which is is sure to be over limit")

        def check_update(vs):
            log_i("Received version: {}\nCurrent version: {}".format(vs, gui_constants.vs))
            if vs != gui_constants.vs:
                if len(vs) < 10:
                    self.notification_bar.add_text(
                        "Version {} of Happypanda is".format(vs) + " available. Click here to update!", False
                    )
                    self.notification_bar.clicked.connect(
                        lambda: utils.open_web_link("https://github.com/Pewpews/happypanda/releases")
                    )
                    self.notification_bar.set_clickable(True)
                else:
                    self.notification_bar.add_text("An error occurred while checking for new version")

        self.update_instance = upd_chk()
        thread = QThread(self)
        self.update_instance.moveToThread(thread)
        thread.started.connect(self.update_instance.fetch_vs)
        self.update_instance.UPDATE_CHECK.connect(check_update)
        self.update_instance.UPDATE_CHECK.connect(self.update_instance.deleteLater)
        thread.finished.connect(thread.deleteLater)
        thread.start()

    def _web_metadata_picker(self, gallery, title_url_list, queue, parent=None):
        if not parent:
            parent = self
        text = "Which gallery do you want to extract metadata from?"
        s_gallery_popup = misc.SingleGalleryChoices(gallery, title_url_list, text, parent)
        s_gallery_popup.USER_CHOICE.connect(queue.put)

    def get_metadata(self, gal=None):
        thread = QThread(self)
        thread.setObjectName("App.get_metadata")
        fetch_instance = fetch.Fetch()
        if gal:
            galleries = [gal]
        else:
            if gui_constants.CONTINUE_AUTO_METADATA_FETCHER:
                galleries = [g for g in self.manga_list_view.gallery_model._data if not g.exed]
            else:
                galleries = self.manga_list_view.gallery_model._data
            if not galleries:
                self.notification_bar.add_text("Looks like we've already gone through all galleries!")
                return None
        fetch_instance.galleries = galleries

        self.notification_bar.begin_show()
        fetch_instance.moveToThread(thread)

        def done(status):
            self.notification_bar.end_show()
            fetch_instance.deleteLater()

        fetch_instance.GALLERY_PICKER.connect(self._web_metadata_picker)
        fetch_instance.GALLERY_EMITTER.connect(self.manga_list_view.replace_edit_gallery)
        fetch_instance.AUTO_METADATA_PROGRESS.connect(self.notification_bar.add_text)
        thread.started.connect(fetch_instance.auto_web_metadata)
        fetch_instance.FINISHED.connect(done)
        thread.finished.connect(thread.deleteLater)
        thread.start()

        # def style_tooltip(self):
        # 	palette = QToolTip.palette()
        # 	palette.setColor()

    def init_stat_bar(self):
        self.status_bar = self.statusBar()
        self.status_bar.setMaximumHeight(20)
        self.status_bar.setSizeGripEnabled(False)
        self.stat_info = QLabel()
        self.stat_info.setIndent(5)
        self.sort_main = QAction("Asc", self)
        sort_menu = QMenu()
        self.sort_main.setMenu(sort_menu)
        s_by_title = QAction("Title", sort_menu)
        s_by_artist = QAction("Artist", sort_menu)
        sort_menu.addAction(s_by_title)
        sort_menu.addAction(s_by_artist)
        self.status_bar.addPermanentWidget(self.stat_info)
        # self.status_bar.addAction(self.sort_main)
        self.temp_msg = QLabel()
        self.temp_timer = QTimer()

        self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.connect(self.stat_row_info)
        self.manga_list_view.gallery_model.STATUSBAR_MSG.connect(self.stat_temp_msg)
        self.manga_list_view.STATUS_BAR_MSG.connect(self.stat_temp_msg)
        self.manga_table_view.STATUS_BAR_MSG.connect(self.stat_temp_msg)
        self.stat_row_info()

    def stat_temp_msg(self, msg):
        self.temp_timer.stop()
        self.temp_msg.setText(msg)
        self.status_bar.addWidget(self.temp_msg)
        self.temp_timer.timeout.connect(self.temp_msg.clear)
        self.temp_timer.setSingleShot(True)
        self.temp_timer.start(5000)

    def stat_row_info(self):
        r = self.manga_list_view.model().rowCount()
        t = self.manga_list_view.gallery_model._data_count
        self.stat_info.setText("Loaded {} of {} ".format(r, t))

    def manga_display(self):
        "initiates the manga view"
        # list view
        self.manga_list_main = QWidget()
        # self.manga_list_main.setContentsMargins(-10, -12, -10, -10)
        self.manga_list_main.setContentsMargins(10, -9, -10, -10)  # x, y, inverted_width, inverted_height
        self.manga_list_layout = QHBoxLayout()
        self.manga_list_main.setLayout(self.manga_list_layout)

        self.manga_list_view = gallery.MangaView(self)
        self.manga_list_view.clicked.connect(self.popup)
        self.manga_list_view.manga_delegate.POPUP.connect(self.popup)
        self.popup_window = self.manga_list_view.manga_delegate.popup_window
        self.manga_list_layout.addWidget(self.manga_list_view)

        # table view
        self.manga_table_main = QWidget()
        self.manga_table_layout = QVBoxLayout()
        self.manga_table_main.setLayout(self.manga_table_layout)

        self.manga_table_view = gallery.MangaTableView(self)
        self.manga_table_view.gallery_model = self.manga_list_view.gallery_model
        self.manga_table_view.sort_model = self.manga_list_view.sort_model
        self.manga_table_view.setModel(self.manga_table_view.sort_model)
        self.manga_table_view.sort_model.change_model(self.manga_table_view.gallery_model)
        self.manga_table_view.setColumnWidth(gui_constants.FAV, 20)
        self.manga_table_view.setColumnWidth(gui_constants.ARTIST, 200)
        self.manga_table_view.setColumnWidth(gui_constants.TITLE, 400)
        self.manga_table_view.setColumnWidth(gui_constants.TAGS, 300)
        self.manga_table_view.setColumnWidth(gui_constants.TYPE, 60)
        self.manga_table_view.setColumnWidth(gui_constants.CHAPTERS, 60)
        self.manga_table_view.setColumnWidth(gui_constants.LANGUAGE, 100)
        self.manga_table_view.setColumnWidth(gui_constants.LINK, 400)
        self.manga_table_layout.addWidget(self.manga_table_view)

    def search(self, srch_string):
        case_ins = srch_string.lower()
        if not gui_constants.ALLOW_SEARCH_REGEX:
            remove = "^$*+?{}\\|()[]"
            for x in remove:
                if x == "[" or x == "]":
                    continue
                else:
                    case_ins = case_ins.replace(x, ".")
        else:
            try:
                re.compile(case_ins)
            except re.error:
                return
        self.manga_list_view.sort_model.search(case_ins)

    def popup(self, index):
        if not self.popup_window.isVisible():
            m_x = QCursor.pos().x()
            m_y = QCursor.pos().y()
            d_w = QDesktopWidget().width()
            d_h = QDesktopWidget().height()
            p_w = gui_constants.POPUP_WIDTH
            p_h = gui_constants.POPUP_HEIGHT

            index_rect = self.manga_list_view.visualRect(index)
            index_point = self.manga_list_view.mapToGlobal(index_rect.topRight())
            # adjust so it doesn't go offscreen
            if d_w - m_x < p_w and d_h - m_y < p_h:  # bottom
                self.popup_window.move(m_x - p_w + 5, m_y - p_h)
            elif d_w - m_x > p_w and d_h - m_y < p_h:
                self.popup_window.move(m_x + 5, m_y - p_h)
            elif d_w - m_x < p_w:
                self.popup_window.move(m_x - p_w + 5, m_y + 5)
            else:
                self.popup_window.move(index_point)

            self.popup_window.set_gallery(index.data(Qt.UserRole + 1))
            self.popup_window.show()

    def favourite_display(self):
        "Switches to favourite display"
        if self.display.currentIndex() == self.m_l_view_index:
            self.manga_list_view.sort_model.fav_view()
        else:
            self.manga_table_view.sort_model.fav_view()

    def catalog_display(self):
        "Switches to catalog display"
        if self.display.currentIndex() == self.m_l_view_index:
            self.manga_list_view.sort_model.catalog_view()
        else:
            self.manga_table_view.sort_model.catalog_view()

    def settings(self):
        sett = settingsdialog.SettingsDialog(self)
        sett.scroll_speed_changed.connect(self.manga_list_view.updateGeometries)
        # sett.show()

    def init_toolbar(self):
        self.toolbar = QToolBar()
        self.toolbar.setFixedHeight(25)
        self.toolbar.setWindowTitle("Show")  # text for the contextmenu
        # self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined?
        self.toolbar.setMovable(False)
        self.toolbar.setFloatable(False)
        # self.toolbar.setIconSize(QSize(20,20))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        spacer_start = QWidget()  # aligns the first actions properly
        spacer_start.setFixedSize(QSize(10, 1))
        self.toolbar.addWidget(spacer_start)

        favourite_view_icon = QIcon(gui_constants.STAR_BTN_PATH)
        favourite_view_action = QAction(favourite_view_icon, "Favorites", self)
        favourite_view_action.setToolTip("Show only favourite galleries")
        favourite_view_action.triggered.connect(self.favourite_display)  # need lambda to pass extra args
        self.toolbar.addAction(favourite_view_action)

        catalog_view_icon = QIcon(gui_constants.HOME_BTN_PATH)
        catalog_view_action = QAction(catalog_view_icon, "Library", self)
        catalog_view_action.setToolTip("Show all your galleries")
        # catalog_view_action.setText("Catalog")
        catalog_view_action.triggered.connect(self.catalog_display)  # need lambda to pass extra args
        self.toolbar.addAction(catalog_view_action)
        self.toolbar.addSeparator()

        gallery_menu = QMenu()
        gallery_action = QToolButton()
        gallery_action.setText("Gallery ")
        gallery_action.setPopupMode(QToolButton.InstantPopup)
        gallery_action.setToolTip("Contains various gallery related features")
        gallery_action.setMenu(gallery_menu)
        add_gallery_icon = QIcon(gui_constants.PLUS_PATH)
        gallery_action_add = QAction(add_gallery_icon, "Add gallery", self)
        gallery_action_add.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit)
        gallery_action_add.setToolTip("Add a single gallery thoroughly")
        gallery_menu.addAction(gallery_action_add)
        add_more_action = QAction(add_gallery_icon, "Add galleries...", self)
        add_more_action.setStatusTip("Add galleries from different folders")
        add_more_action.triggered.connect(lambda: self.populate(True))
        gallery_menu.addAction(add_more_action)
        populate_action = QAction(add_gallery_icon, "Populate from folder...", self)
        populate_action.setStatusTip("Populates the DB with galleries from a single folder")
        populate_action.triggered.connect(self.populate)
        gallery_menu.addAction(populate_action)
        gallery_menu.addSeparator()
        metadata_action = QAction("Get metadata for all galleries", self)
        metadata_action.triggered.connect(self.get_metadata)
        gallery_menu.addAction(metadata_action)
        self.toolbar.addWidget(gallery_action)
        self.toolbar.addSeparator()

        misc_action = QToolButton()
        misc_action.setText("Misc ")
        misc_action_menu = QMenu()
        misc_action.setMenu(misc_action_menu)
        misc_action.setPopupMode(QToolButton.InstantPopup)
        misc_action.setToolTip("Contains misc. features")
        misc_action_random = QAction("Open random gallery", misc_action_menu)
        misc_action_random.triggered.connect(self.manga_list_view.open_random_gallery)
        misc_action_menu.addAction(misc_action_random)
        duplicate_check_simple = QAction("Simple duplicate finder", misc_action_menu)
        duplicate_check_simple.triggered.connect(lambda: self.manga_list_view.duplicate_check())
        misc_action_menu.addAction(duplicate_check_simple)
        self.toolbar.addWidget(misc_action)

        spacer_middle = QWidget()  # aligns buttons to the right
        spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.toolbar.addWidget(spacer_middle)

        self.grid_toggle_g_icon = QIcon(gui_constants.GRID_PATH)
        self.grid_toggle_l_icon = QIcon(gui_constants.LIST_PATH)
        self.grid_toggle = QToolButton()
        if self.display.currentIndex() == self.m_l_view_index:
            self.grid_toggle.setIcon(self.grid_toggle_l_icon)
        else:
            self.grid_toggle.setIcon(self.grid_toggle_g_icon)
        self.grid_toggle.setObjectName("gridtoggle")
        self.grid_toggle.clicked.connect(self.toggle_view)
        self.toolbar.addWidget(self.grid_toggle)

        self.search_bar = misc.LineEdit()
        if gui_constants.SEARCH_AUTOCOMPLETE:
            completer = QCompleter(self)
            completer.setModel(self.manga_list_view.gallery_model)
            completer.setCaseSensitivity(Qt.CaseInsensitive)
            completer.setCompletionMode(QCompleter.PopupCompletion)
            completer.setCompletionRole(Qt.DisplayRole)
            completer.setCompletionColumn(gui_constants.TITLE)
            completer.setFilterMode(Qt.MatchContains)
            self.search_bar.setCompleter(completer)
        if gui_constants.SEARCH_ON_ENTER:
            self.search_bar.returnPressed.connect(lambda: self.search(self.search_bar.text()))
        else:
            self.search_bar.textChanged[str].connect(self.search)
        self.search_bar.setPlaceholderText("Search title, artist, namespace & tags")
        self.search_bar.setMinimumWidth(150)
        self.search_bar.setMaximumWidth(500)
        self.toolbar.addWidget(self.search_bar)
        self.toolbar.addSeparator()
        settings_icon = QIcon(gui_constants.SETTINGS_PATH)
        settings_action = QAction("Set&tings", self)
        settings_action.triggered.connect(self.settings)
        self.toolbar.addAction(settings_action)

        spacer_end = QWidget()  # aligns About action properly
        spacer_end.setFixedSize(QSize(10, 1))
        self.toolbar.addWidget(spacer_end)
        self.addToolBar(self.toolbar)

    def toggle_view(self):
        """
		Toggles the current display view
		"""
        if self.display.currentIndex() == self.m_l_view_index:
            self.display.setCurrentIndex(self.m_t_view_index)
            self.grid_toggle.setIcon(self.grid_toggle_g_icon)
        else:
            self.display.setCurrentIndex(self.m_l_view_index)
            self.grid_toggle.setIcon(self.grid_toggle_l_icon)

            # TODO: Improve this so that it adds to the gallery dialog,
            # so user can edit data before inserting (make it a choice)

    def populate(self, mixed=None):
        "Populates the database with gallery from local drive'"
        if mixed:
            gallery_view = misc.GalleryListView(self, True)
            gallery_view.SERIES.connect(self.gallery_populate)
            gallery_view.show()
        else:
            path = QFileDialog.getExistingDirectory(None, "Choose a folder containing your galleries")
            self.gallery_populate(path, True)

    def gallery_populate(self, path, validate=False):
        "Scans the given path for gallery to add into the DB"
        if len(path) is not 0:
            data_thread = QThread(self)
            data_thread.setObjectName("General gallery populate")
            loading = misc.Loading(self)
            if not loading.ON:
                misc.Loading.ON = True
                fetch_instance = fetch.Fetch()
                fetch_instance.series_path = path
                loading.show()

                def finished(status):
                    def hide_loading():
                        loading.hide()

                    if status:
                        if len(status) != 0:

                            def add_gallery(gallery_list):
                                def append_to_model(x):
                                    self.manga_list_view.gallery_model.insertRows(x, None, len(x))

                                class A(QObject):
                                    done = pyqtSignal()
                                    prog = pyqtSignal(int)

                                    def __init__(self, obj, parent=None):
                                        super().__init__(parent)
                                        self.obj = obj
                                        self.galleries = []

                                    def add_to_db(self):
                                        gui_constants.NOTIF_BAR.begin_show()
                                        gui_constants.NOTIF_BAR.add_text("Populating database...")
                                        for y, x in enumerate(self.obj):
                                            gui_constants.NOTIF_BAR.add_text(
                                                "Populating database {}/{}".format(y + 1, len(self.obj))
                                            )
                                            gallerydb.add_method_queue(gallerydb.GalleryDB.add_gallery_return, False, x)
                                            self.galleries.append(x)
                                            y += 1
                                            self.prog.emit(y)
                                        append_to_model(self.galleries)
                                        gui_constants.NOTIF_BAR.end_show()
                                        self.done.emit()

                                loading.progress.setMaximum(len(gallery_list))
                                a_instance = A(gallery_list)
                                thread = QThread(self)
                                thread.setObjectName("Database populate")

                                def loading_show():
                                    loading.setText("Populating database.\nPlease wait...")
                                    loading.show()

                                def loading_hide():
                                    loading.close()
                                    self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.emit()

                                def del_later():
                                    try:
                                        a_instance.deleteLater()
                                    except NameError:
                                        pass

                                a_instance.moveToThread(thread)
                                a_instance.prog.connect(loading.progress.setValue)
                                thread.started.connect(loading_show)
                                thread.started.connect(a_instance.add_to_db)
                                a_instance.done.connect(loading_hide)
                                a_instance.done.connect(del_later)
                                thread.finished.connect(thread.deleteLater)
                                thread.start()

                            data_thread.quit
                            hide_loading()
                            log_i("Populating DB from gallery folder: OK")
                            if validate:
                                gallery_list = misc.GalleryListView(self)
                                gallery_list.SERIES.connect(add_gallery)
                                for ser in status:
                                    gallery_list.add_gallery(ser, os.path.split(ser.path)[1])
                                    # self.manga_list_view.gallery_model.populate_data()
                                gallery_list.show()
                            else:
                                add_gallery(status)
                            misc.Loading.ON = False
                        else:
                            log_d("No new gallery was found")
                            loading.setText("No new gallery found")
                            data_thread.quit
                            misc.Loading.ON = False

                    else:
                        log_e("Populating DB from gallery folder: Nothing was added!")
                        loading.setText("<font color=red>Nothing was added. Check happypanda_log for details..</font>")
                        loading.progress.setStyleSheet("background-color:red;")
                        data_thread.quit
                        QTimer.singleShot(10000, loading.close)

                def fetch_deleteLater():
                    try:
                        fetch_instance.deleteLater
                    except NameError:
                        pass

                def a_progress(prog):
                    loading.progress.setValue(prog)
                    loading.setText("Searching for galleries...")

                fetch_instance.moveToThread(data_thread)
                fetch_instance.DATA_COUNT.connect(loading.progress.setMaximum)
                fetch_instance.PROGRESS.connect(a_progress)
                data_thread.started.connect(fetch_instance.local)
                fetch_instance.FINISHED.connect(finished)
                fetch_instance.FINISHED.connect(fetch_deleteLater)
                data_thread.finished.connect(data_thread.deleteLater)
                data_thread.start()
                log_i("Populating DB from gallery folder")

    def resizeEvent(self, event):
        try:
            self.notification_bar.resize(event.size().width())
        except AttributeError:
            pass
        return super().resizeEvent(event)

    def closeEvent(self, event):
        # watchers
        try:
            self.watchers.stop_all()
        except AttributeError:
            pass

            # settings
        settings.set(self.manga_list_view.current_sort, "General", "current sort")
        settings.win_save(self, "AppWindow")

        # temp dir
        try:
            for root, dirs, files in os.walk("temp", topdown=False):
                for name in files:
                    os.remove(os.path.join(root, name))
                for name in dirs:
                    os.rmdir(os.path.join(root, name))
            log_d("Empty temp on exit: OK")
        except:
            log_d("Empty temp on exit: FAIL")

            # error
        err = sys.exc_info()
        if all(err):
            log_c("Last error before exit:\n{}\n{}\n{}".format(err[0], err[1], err[2]))
        else:
            log_d("Normal Exit App: OK")
        super().closeEvent(event)
        app = QApplication.instance()
        app.quit()
        sys.exit()
Esempio n. 57
0
class MainWindow(QMainWindow):

    """This is the main application window class

    it defines the GUI window for the browser
    """

    def parse_config(self, file_config, options):
        self.config = {}
        options = vars(options)
        for key, metadata in CONFIG_OPTIONS.items():
            options_val = options.get(key)
            file_val = file_config.get(key)
            env_val = os.environ.get(metadata.get("env", ''))
            default_val = metadata.get("default")
            vals = metadata.get("values")
            debug("key: {}, default: {}, file: {}, options: {}".format(
                key, default_val, file_val, options_val
            ))
            if vals:
                options_val = (options_val in vals and options_val) or None
                file_val = (file_val in vals and file_val) or None
                env_val = (env_val in vals and env_val) or None
            if metadata.get("is_file"):
                filename = options_val or env_val
                if not filename:
                    self.config[key] = default_val
                else:
                    try:
                        with open(filename, 'r') as fh:
                            self.config[key] = fh.read()
                    except IOError:
                        debug("Could not open file {} for reading.".format(
                            filename)
                        )
                        self.config[key] = default_val
            else:
                set_values = [
                    val for val in (options_val, env_val, file_val)
                    if val is not None
                ]
                if len(set_values) > 0:
                    self.config[key] = set_values[0]
                else:
                    self.config[key] = default_val
            if metadata.get("type") and self.config[key]:
                debug("{} cast to {}".format(key, metadata.get("type")))
                self.config[key] = metadata.get("type")(self.config[key])
        debug(repr(self.config))

    def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None,
                     checkable=False, signal="triggered"):
        """Return a QAction given a number of common QAction attributes

        Just a shortcut function Originally borrowed from
        'Rapid GUI Development with PyQT' by Mark Summerset
        """
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon.fromTheme(
                icon, QIcon(":/{}.png".format(icon))
            ))
        if shortcut is not None and not shortcut.isEmpty():
            action.setShortcut(shortcut)
            tip += " ({})".format(shortcut.toString())
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            action.__getattr__(signal).connect(slot)
        if checkable:
            action.setCheckable()
        return action

    def __init__(self, options, parent=None):
        """Construct a MainWindow Object."""
        super(MainWindow, self).__init__(parent)
        # Load config file
        self.setWindowTitle("Browser")
        debug("loading configuration from '{}'".format(options.config_file))
        configfile = {}
        if options.config_file:
            configfile = yaml.safe_load(open(options.config_file, 'r'))
        self.parse_config(configfile, options)
        # self.popup will hold a reference to the popup window
        # if it gets opened
        self.popup = None

        # Stylesheet support
        if self.config.get("stylesheet"):
            try:
                with open(self.config.get("stylesheet")) as ss:
                    self.setStyleSheet(ss.read())
            except:
                debug(
                    """Problem loading stylesheet file "{}", """
                    """using default style."""
                    .format(self.config.get("stylesheet"))
                )
        self.setObjectName("global")

        # If the whitelist is activated, add the bookmarks and start_url
        if self.config.get("whitelist"):
            # we can just specify whitelist = True,
            # which should whitelist just the start_url and bookmark urls.
            whitelist = self.config.get("whitelist")
            if type(whitelist) is not list:
                whitelist = []
            whitelist.append(str(QUrl(
                self.config.get("start_url")
            ).host()))
            bookmarks = self.config.get("bookmarks")
            if bookmarks:
                whitelist += [
                    str(QUrl(b.get("url")).host())
                    for k, b in bookmarks.items()
                ]
                self.config["whitelist"] = set(whitelist)  # uniquify and optimize
            debug("Generated whitelist: " + str(whitelist))

        # If diagnostic is enabled, connect CTRL+ALT+? to show some diagnistic info
        if (self.config.get("enable_diagnostic")):
            self.diagnostic_action = self.createAction(
                "Show Diagnostic",
                self.show_diagnostic,
                QKeySequence("Ctrl+Alt+/"),
                tip=''
            )
            self.addAction(self.diagnostic_action)

        self.build_ui()

    # ## END OF CONSTRUCTOR ## #

    def build_ui(self):
        """Set up the user interface for the main window.

        Unlike the constructor, this method is re-run
        whenever the browser is "reset" by the user.
        """

        debug("build_ui")
        inactivity_timeout = self.config.get("timeout")
        quit_button_tooltip = (
            self.config.get("quit_button_mode") == 'close'
            and "Click here to quit the browser."
            or """Click here when you are done.
            It will clear your browsing history"""
            """ and return you to the start page.""")
        qb_mode_callbacks = {'close': self.close, 'reset': self.reset_browser}
        to_mode_callbacks = {'close': self.close,
                             'reset': self.reset_browser,
                             'screensaver': self.screensaver}
        self.screensaver_active = False

        # ##Start GUI configuration## #
        self.browser_window = WcgWebView(self.config)
        self.browser_window.setObjectName("web_content")

        if (
            self.config.get("icon_theme") is not None
            and QT_VERSION_STR > '4.6'
        ):
            QIcon.setThemeName(self.config.get("icon_theme"))
        self.setCentralWidget(self.browser_window)
        debug("loading {}".format(self.config.get("start_url")))
        self.browser_window.setUrl(QUrl(self.config.get("start_url")))
        if self.config.get("fullscreen"):
            self.showFullScreen()
        elif (
            self.config.get("window_size") and
            self.config.get("window_size").lower() == 'max'
        ):
            self.showMaximized()
        elif self.config.get("window_size"):
            size = re.match(r"(\d+)x(\d+)", self.config.get("window_size"))
            if size:
                width, height = size.groups()
                self.setFixedSize(int(width), int(height))
            else:
                debug('Ignoring invalid window size "{}"'.format(
                    self.config.get("window_size")
                ))

        # Set up the top navigation bar if it's configured to exist
        if self.config.get("navigation"):
            self.navigation_bar = QToolBar("Navigation")
            self.navigation_bar.setObjectName("navigation")
            self.addToolBar(Qt.TopToolBarArea, self.navigation_bar)
            self.navigation_bar.setMovable(False)
            self.navigation_bar.setFloatable(False)

            #  Standard navigation tools
            self.nav_items = {}
            self.nav_items["back"] = self.browser_window.pageAction(QWebPage.Back)
            self.nav_items["forward"] = self.browser_window.pageAction(QWebPage.Forward)
            self.nav_items["refresh"] = self.browser_window.pageAction(QWebPage.Reload)
            self.nav_items["stop"] = self.browser_window.pageAction(QWebPage.Stop)
            # The "I'm finished" button.
            self.nav_items["quit"] = self.createAction(
                self.config.get("quit_button_text"),
                qb_mode_callbacks.get(self.config.get("quit_button_mode"), self.reset_browser),
                QKeySequence("Alt+F"),
                None,
                quit_button_tooltip)
            # Zoom buttons
            self.nav_items["zoom_in"] = self.createAction(
                "Zoom In",
                self.zoom_in,
                QKeySequence("Alt++"),
                "zoom-in",
                "Increase the size of the text and images on the page")
            self.nav_items["zoom_out"] = self.createAction(
                "Zoom Out",
                self.zoom_out,
                QKeySequence("Alt+-"),
                "zoom-out",
                "Decrease the size of text and images on the page")
            if self.config.get("allow_printing"):
                self.nav_items["print"] = self.createAction(
                    "Print",
                    self.browser_window.print_webpage,
                    QKeySequence("Ctrl+p"),
                    "document-print",
                    "Print this page")

            # Add all the actions to the navigation bar.
            for item in self.config.get("navigation_layout"):
                if item == "separator":
                    self.navigation_bar.addSeparator()
                elif item == "spacer":
                    # an expanding spacer.
                    spacer = QWidget()
                    spacer.setSizePolicy(
                        QSizePolicy.Expanding, QSizePolicy.Preferred)
                    self.navigation_bar.addWidget(spacer)
                elif item == "bookmarks":
                    # Insert bookmarks buttons here.
                    self.bookmark_buttons = []
                    for bookmark in self.config.get("bookmarks", {}).items():
                        debug("Bookmark:\n" + bookmark.__str__())
                        # bookmark name will use the "name" attribute, if present
                        # or else just the key:
                        bookmark_name = bookmark[1].get("name") or bookmark[0]
                        # Create a button for the bookmark as a QAction,
                        # which we'll add to the toolbar
                        button = self.createAction(
                            bookmark_name,
                            lambda url=bookmark[1].get("url"): self.browser_window.load(QUrl(url)),
                            QKeySequence.mnemonic(bookmark_name),
                            None,
                            bookmark[1].get("description")
                            )
                        self.navigation_bar.addAction(button)
                        self.navigation_bar.widgetForAction(button).setObjectName("navigation_button")
                else:
                    action = self.nav_items.get(item, None)
                    if action:
                        self.navigation_bar.addAction(action)
                        self.navigation_bar.widgetForAction(action).setObjectName("navigation_button")

            # This removes the ability to toggle off the navigation bar:
            self.nav_toggle = self.navigation_bar.toggleViewAction()
            self.nav_toggle.setVisible(False)
            # End "if show_navigation is True" block

        # set hidden quit action
        # For reasons I haven't adequately ascertained,
        # this shortcut fails now and then claiming
        # "Ambiguous shortcut overload".
        # No idea why, as it isn't consistent.
        self.really_quit = self.createAction(
            "", self.close, QKeySequence("Ctrl+Alt+Q"), None, ""
        )
        self.addAction(self.really_quit)

        # Call a reset function after timeout
        if inactivity_timeout != 0:
            self.event_filter = InactivityFilter(inactivity_timeout)
            QCoreApplication.instance().installEventFilter(self.event_filter)
            self.browser_window.page().installEventFilter(self.event_filter)
            self.event_filter.timeout.connect(
                to_mode_callbacks.get(self.config.get("timeout_mode"),
                                      self.reset_browser))
        else:
            self.event_filter = None

        # ##END OF UI SETUP## #

    def screensaver(self):
        """Enter "screensaver" mode

        This method puts the browser in screensaver mode, where a URL
        is displayed while the browser is idle.  Activity causes the browser to
        return to the home screen.
        """
        debug("screensaver started")
        self.screensaver_active = True
        if self.popup:
            self.popup.close()
        if self.config.get("navigation"):
            self.navigation_bar.hide()
        self.browser_window.setZoomFactor(self.config.get("zoom_factor"))
        self.browser_window.load(QUrl(self.config.get("screensaver_url")))
        self.event_filter.timeout.disconnect()
        self.event_filter.activity.connect(self.reset_browser)

    def reset_browser(self):
        """Clear the history and reset the UI.

        Called whenever the inactivity filter times out,
        or when the user clicks the "finished" button in
        'reset' mode.
        """
        # Clear out the memory cache
        QWebSettings.clearMemoryCaches()
        self.browser_window.history().clear()
        # self.navigation_bar.clear() doesn't do its job,
        # so remove the toolbar first, then rebuild the UI.
        debug("RESET BROWSER")
        if self.event_filter:
            self.event_filter.blockSignals(True)
        if self.screensaver_active is True:
            self.screensaver_active = False
            self.event_filter.activity.disconnect()
        if self.event_filter:
            self.event_filter.blockSignals(False)
        if hasattr(self, "navigation_bar"):
            self.removeToolBar(self.navigation_bar)
        self.build_ui()

    def zoom_in(self):
        """Zoom in action callback.

        Note that we cap zooming in at a factor of 3x.
        """
        if self.browser_window.zoomFactor() < 3.0:
            self.browser_window.setZoomFactor(
                self.browser_window.zoomFactor() + 0.1
            )
            self.nav_items["zoom_out"].setEnabled(True)
        else:
            self.nav_items["zoom_in"].setEnabled(False)

    def zoom_out(self):
        """Zoom out action callback.

        Note that we cap zooming out at 0.1x.
        """
        if self.browser_window.zoomFactor() > 0.1:
            self.browser_window.setZoomFactor(
                self.browser_window.zoomFactor() - 0.1
            )
            self.nav_items["zoom_in"].setEnabled(True)
        else:
            self.nav_items["zoom_out"].setEnabled(False)

    def show_diagnostic(self):
        "Display a dialog box with some diagnostic info"
        data = {
            "OS": os.uname(),
            "USER": (os.environ.get("USER")
                     or os.environ.get("USERNAME")),
            "Python": sys.version,
            "Qt": QT_VERSION_STR,
            "Script Date": (
                datetime.datetime.fromtimestamp(
                    os.stat(__file__).st_mtime).isoformat()
            )
        }
        html = "\n".join([
            "<h1>System Information</h1>",
            "<h2>Please click &quot;",
            self.config.get("quit_button_text").replace("&", ''),
            "&quot; when you are finished.</h2>",
            "<ul>",
            "\n".join([
                "<li><b>{}</b>: {}</li>".format(k, v)
                for k, v in data.items()
            ]),
            "</ul>"
        ])
        self.browser_window.setHtml(html)
Esempio n. 58
0
class FinishSpectrum(QWidget):
    def __init__(self, fits_file, settings, database, project=None):
        super(FinishSpectrum, self).__init__()
        self.settings = settings
        self.ui = Ui_FinishSpectrum()
        self.ui.setupUi(self)
        self.profile_line = None
        self.project = project
        self.fits_spectrum = FitsSpectrum(fits_file)
        self.undo = Undo(self.fits_spectrum.spectrum, self.draw)
        try:
            fits_file.index_of('ORIGINAL_DATA')
        except KeyError:
            hdu = fits.ImageHDU(data = fits_file[0].data, header = fits_file[0].header, name='ORIGINAL_DATA')
            fits_file.append(hdu)

        self.fits_spectrum.spectrum.normalize_to_max()
        self.spectrum = self.fits_spectrum.spectrum
        self.spectrum_plot = QtCommons.nestWidget(self.ui.plot, QMathPlotWidget())
        self.spectrum_plot.mouse_moved.connect(Instances.MainWindow.print_coordinates)

        self.split_view()
        self.toolbar = QToolBar('Finish Spectrum Toolbar')
        if project:
            instrument_response_action = QtCommons.addToolbarPopup(self.toolbar, "Instrument Response")
            instrument_response_action.menu().addAction('From FITS file...', lambda: open_file_sticky('Open Instrument Response Profile', FITS_EXTS, lambda f: self.instrument_response(f[0]), settings, MATH_OPERATION, [RAW_PROFILE]))
            for instrument_response in project.get_instrument_responses():
                print("Adding instrument response {}".format(instrument_response))
                instrument_response_action.menu().addAction(os.path.basename(instrument_response[1]), lambda: self.instrument_response(instrument_response[1]))
        else:
            self.toolbar.addAction('Instrument Response', lambda: open_file_sticky('Open Instrument Response Profile', FITS_EXTS, lambda f: self.instrument_response(f[0]), settings, MATH_OPERATION, [RAW_PROFILE]))
        self.toolbar.addAction("Zoom", lambda: self.spectrum_plot.select_zoom(self.profile_plot.axes))
        self.toolbar.addAction("Reset Zoom", lambda: self.spectrum_plot.reset_zoom(self.spectrum.wavelengths, self.spectrum.fluxes.min(), self.spectrum.fluxes.max(), self.profile_plot.axes))
        remove_action = QtCommons.addToolbarPopup(self.toolbar, "Remove")
        remove_action.menu().addAction("Before point", lambda: spectrum_trim_dialog(self.spectrum, 'before', self.profile_plot.axes, lambda: self.draw(), self, before_removal=self.undo.save_undo))
        remove_action.menu().addAction("After point", lambda: spectrum_trim_dialog(self.spectrum, 'after', self.profile_plot.axes, lambda: self.draw(), self, before_removal=self.undo.save_undo))
        self.undo.add_actions(self.toolbar)
        self.toolbar.addSeparator()
        
        self.reference_spectra_dialog = ReferenceSpectraDialog(database, self.fits_spectrum.spectrum)
        self.reference_spectra_dialog.setup_menu(self.toolbar, self.profile_plot.axes, settings)

        lines_menu = QtCommons.addToolbarPopup(self.toolbar, "Spectral Lines..")
        lines_menu.menu().addAction('Lines Database', lambda: self.lines_dialog.show())
        lines_menu.menu().addAction('Custom line', self.add_custom_line)
        labels_action = QtCommons.addToolbarPopup(self.toolbar, "Labels..")
        self.object_properties = ObjectProperties(fits_file, project=project)
        labels_action.menu().addAction('Title', self.add_title)
        if self.object_properties:
            labels_action.menu().addAction('Information from FITS file', self.add_fits_information_label)
        labels_action.menu().addAction('Custom', self.add_label)
        

        self.object_properties_dialog = ObjectPropertiesDialog(settings, self.object_properties)
        self.toolbar.addAction("Object properties", self.object_properties_dialog.show)

        self.labels, self.lines = [], []
        for label in self.fits_spectrum.labels():
            self.add_label(text=label['text'], coords=label['coords'], type=label['type'], fontsize=label['fontsize'])
        
        self.toolbar.addSeparator()
        if project:
            self.toolbar.addAction(QIcon(':/image_20'), "Export Image...", lambda: QtCommons.save_file('Export plot to image', 'PNG (*.png);;PDF (*.pdf);;PostScript (*.ps);;SVG (*.svg)', lambda f: self.save_image(f[0]), project.directory_path(Project.EXPORTED_IMAGES)))
            self.toolbar.addAction(QIcon(':/save_20'), 'Save', self.save_finished_in_project)
        else:
            self.toolbar.addAction(QIcon(':/image_20'), "Export Image...", lambda: save_file_sticky('Export plot to image', 'PNG (*.png);;PDF (*.pdf);;PostScript (*.ps);;SVG (*.svg)', lambda f: self.save_image(f[0]), self.settings, EXPORT_IMAGES, [CALIBRATED_PROFILE]))
            self.toolbar.addAction(QIcon(':/save_20'), 'Save', lambda: save_file_sticky('Save plot...', 'FITS file (.fit)', lambda f: self.__save(f[0]), self.settings, CALIBRATED_PROFILE))
            
        self.lines_dialog = LinesDialog(database, settings, self.spectrum_plot, self.profile_plot.axes)
        self.lines_dialog.lines.connect(self.add_lines)
        for line in self.fits_spectrum.lines_labels():
            self.lines.append(ReferenceLine(line['text'], line['wavelength'], self.profile_plot.axes, lambda line: self.lines.remove(line), show_wavelength=line['display_wavelength'], fontsize=line['fontsize'], position=line['position']))
                
    
        
    def add_custom_line(self):
        wl = QInputDialog.getDouble(self, "Custom Line", "Enter line wavelength in Å", self.fits_spectrum.spectrum.wavelengths[0],self.fits_spectrum.spectrum.wavelengths[0],self.fits_spectrum.spectrum.wavelengths[-1],3)
        if not wl[1]: return
        self.add_lines([{'name': 'Custom Line', 'lambda': wl[0]}])
        
    def add_lines(self, lines):
        for line in lines:
            self.lines.append(ReferenceLine(line['name'], line['lambda'], self.profile_plot.axes, lambda line: self.lines.remove(line)))

    
    def synthetize_img(wavelengths, fluxes):
        f_fluxes = lambda f: math.pow(f, 3/5)
        colors = [wavelength_to_rgb(w/10., f_fluxes(fluxes[i])) for i,w in enumerate(wavelengths)]
        im_height = 150
        colors = np.array(colors*im_height).reshape(im_height,len(colors),4)
        return colors, im_height
        

    def split_view(self):
        figure = self.spectrum_plot.figure
        figure.clear()
        self.gs = gridspec.GridSpec(40,1)
        self.profile_plot = figure.add_subplot(self.gs[0:-6])
        self.synthetize = figure.add_subplot(self.gs[-3:-1], sharex = self.profile_plot)
        self.synthetize.yaxis.set_visible(False)
        self.synthetize.xaxis.set_visible(False)
        self.draw()
        
    
    def draw(self):
#        self.profile_plot.clear()
        if self.profile_line:
            self.profile_line.remove()
        self.profile_line = self.profile_plot.plot(self.spectrum.wavelengths, self.spectrum.fluxes, color='blue')[0]

        self.synthetize.axes.set_axis_bgcolor('black')
        
        with ThreadPoolExecutor(max_workers=1) as executor:
            future = executor.submit(FinishSpectrum.synthetize_img, self.spectrum.wavelengths, self.spectrum.fluxes)
            future.add_done_callback(lambda f: self.synthetize.imshow(f.result()[0], extent=[self.spectrum.wavelengths[0], self.spectrum.wavelengths[-1], 0, f.result()[1]]) )

        self.profile_plot.axes.set_xlabel('wavelength (Å)')
        self.profile_plot.axes.set_ylabel('relative flux')
        self.profile_plot.axes.xaxis.set_major_locator(MaxNLocator(16)) # TODO: settings for customization?
        self.profile_plot.axes.xaxis.set_minor_locator(MaxNLocator(200))
        self.spectrum_plot.figure.canvas.draw()
        self.gs.tight_layout(self.spectrum_plot.figure)
        
    def instrument_response(self, filename):
        print("Applying instrument response {}".format(filename))
        instrument_response_file = fits.open(filename)
        instrument_response = FitsSpectrum(instrument_response_file)
        response = instrument_response.spectrum
        response.normalize_to_max()
        
        range = (max(response.wavelengths[0], self.spectrum.wavelengths[0] ), min(response.wavelengths[-1], self.spectrum.wavelengths[-1]))
        self.spectrum.cut(self.spectrum.wavelength_index(range[0]), self.spectrum.wavelength_index(range[1]))
        spline = InterpolatedUnivariateSpline(response.wavelengths, response.fluxes)
        
        response_data = [spline(x) for x in self.spectrum.wavelengths]
        self.spectrum.fluxes /= response_data
        self.spectrum.normalize_to_max()
        self.draw()
        
        
    def save_image(self, filename):
        Notification('Image {} saved in {}'.format(os.path.basename(filename), os.path.dirname(filename)), title='File Saved', type='success', timeout=5)
        self.spectrum_plot.figure.savefig(filename, bbox_inches='tight', dpi=300)

    def save_finished_in_project(self):
        self.project.add_file(Project.FINISHED_PROFILES, self.__save, self.object_properties)
        
    def __save(self, filename):
        self.fits_spectrum.save(filename, spectral_lines = self.lines, labels = self.labels)
        
    def add_title(self):
        title = self.object_properties.name if self.object_properties else 'Title - double click to edit'
        self.add_label(text=title, coords=(self.spectrum.wavelengths[len(self.spectrum.wavelengths)/2-100], 0.95), fontsize=25, type='lineedit')
    
    def add_fits_information_label(self):
        info_text = "Object Name: {}, type: {}, spectral class: {}\nCoordinates: {}\nDate: {}\nObserver: {}\nEquipment: {}\nPosition: {}".format(
                self.object_properties.name,
                self.object_properties.type,
                self.object_properties.sptype,
                self.object_properties.printable_coordinates(),
                self.object_properties.date.toString(),
                self.object_properties.observer,
                self.object_properties.equipment,
                self.object_properties.position
            )
        self.add_label(info_text, type='textbox', coords=(self.spectrum.wavelengths[len(self.spectrum.wavelengths)/4*3], 0.80), fontsize=14)
        self.profile_plot.figure.canvas.draw()
    
    def add_label(self, text=None, type='textbox', coords = None, fontsize = 12, color='black'):
        if not coords: coords = (self.spectrum.wavelengths[len(self.spectrum.wavelengths)/2], 0.5)
        self.labels.append((type, MoveableLabel(text=text if text else 'Label - double click to edit', on_dblclick=lambda l: self.edit_label(l, type=type), x=coords[0], y=coords[1], fontsize=fontsize, color=color, axes=self.profile_plot.axes)))
        self.profile_plot.figure.canvas.draw()
        
    def edit_label(self, label, type='lineedit'):
        def remove_label(self, label, dialog):
            label.remove()
            self.labels.remove([l for l in self.labels if l[1] == label][0])
            self.profile_plot.figure.canvas.draw()
            dialog.reject()
        dialog = QDialog()
        dialog.setWindowTitle("Edit Label")
        dialog.setLayout(QVBoxLayout())
        font_size = QSpinBox()
        font_size.setValue(label.get_fontsize())
        dialog.layout().addWidget(QLabel("Font Size"))
        dialog.layout().addWidget(font_size)
        text_edit = None
        if type == 'lineedit':
            text_edit = QLineEdit(label.get_text())
        else:
            text_edit = QTextEdit()
            text_edit.setPlainText(label.get_text())
            
        dialog.layout().addWidget(QLabel("Text"))
        dialog.layout().addWidget(text_edit)
        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        button_box.accepted.connect(dialog.accept)
        button_box.rejected.connect(dialog.reject)
        remove_button = QPushButton('Remove')
        remove_button.clicked.connect(lambda: remove_label(self, label, dialog))
        dialog.layout().addWidget(remove_button)
        dialog.layout().addWidget(button_box)
        if QDialog.Accepted != dialog.exec():
            return
        label.set_text(text_edit.text() if type=='lineedit' else text_edit.toPlainText())
        label.set_fontsize(font_size.value())
        label.axes.figure.canvas.draw()
Esempio n. 59
0
class _s_MiscContainer(QWidget):
    """From Miscellaneous, contains all the widgets in the bottom area."""
    #Miscellaneous was to long and dificult to write :P

    def __init__(self, parent=None):
        super(_s_MiscContainer, self).__init__(parent)
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)

        self.__toolbar = QToolBar()
        self.__toolbar.setObjectName('custom')
        hbox = QHBoxLayout()
        vbox.addLayout(hbox)

        self.stack = StackedWidget()
        vbox.addWidget(self.stack)

        self._console = console_widget.ConsoleWidget()
        self.stack.addWidget(self._console)

        self._runWidget = run_widget.RunWidget()
        self.stack.addWidget(self._runWidget)

        self._web = web_render.WebRender()
        self.stack.addWidget(self._web)

        self._findInFilesWidget = find_in_files.FindInFilesWidget(
            self.parent())
        self.stack.addWidget(self._findInFilesWidget)

        #Last Element in the Stacked widget
        self._results = results.Results(self)
        self.stack.addWidget(self._results)

        self._btnConsole = QPushButton(QIcon(resources.IMAGES['console']), '')
        self._btnConsole.setToolTip(_translate("_s_MiscContainer", "Console"))
        self._btnRun = QPushButton(QIcon(resources.IMAGES['play']), '')
        self._btnRun.setToolTip(_translate("_s_MiscContainer", "Output"))
        self._btnWeb = QPushButton(QIcon(resources.IMAGES['web']), '')
        self._btnWeb.setToolTip(_translate("_s_MiscContainer", "Web Preview"))
        self._btnFind = QPushButton(QIcon(resources.IMAGES['find']), '')
        self._btnFind.setToolTip(_translate("_s_MiscContainer", "Find in Files"))
        #Toolbar
        hbox.addWidget(self.__toolbar)
        self.__toolbar.addWidget(self._btnConsole)
        self.__toolbar.addWidget(self._btnRun)
        self.__toolbar.addWidget(self._btnWeb)
        self.__toolbar.addWidget(self._btnFind)
        self.__toolbar.addSeparator()
        hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding))
        btn_close = QPushButton(
            self.style().standardIcon(QStyle.SP_DialogCloseButton), '')
        btn_close.setObjectName('navigation_button')
        btn_close.setToolTip(_translate("_s_MiscContainer", 'F4: Show/Hide'))
        hbox.addWidget(btn_close)

        self._btnConsole.clicked['bool'].connect(lambda: self._item_changed(0))
        self._btnRun.clicked['bool'].connect(lambda: self._item_changed(1))
        self._btnWeb.clicked['bool'].connect(lambda: self._item_changed(2))
        self._btnFind.clicked['bool'].connect(lambda: self._item_changed(3))
        btn_close.clicked['bool'].connect(self.hide)

    def gain_focus(self):
        self._console.setFocus()

    def _item_changed(self, val):
        if not self.isVisible():
            self.show()
        self.stack.show_display(val)

    def show_find_in_files_widget(self):
        index_of = self.stack.indexOf(self._findInFilesWidget)
        self._item_changed(index_of)
        self._findInFilesWidget.open()

    def show_find_occurrences(self, word):
        index_of = self.stack.indexOf(self._findInFilesWidget)
        self._item_changed(index_of)
        self._findInFilesWidget.find_occurrences(word)

    def load_toolbar(self, toolbar):
        toolbar.addWidget(self._combo)
        toolbar.addSeparator()

    def run_application(self, fileName, pythonPath=False, PYTHONPATH=None,
            programParams='', preExec='', postExec=''):
        self._item_changed(1)
        self.show()
        self._runWidget.start_process(fileName, pythonPath, PYTHONPATH,
            programParams, preExec, postExec)
        self._runWidget.input.setFocus()

    def show_results(self, items):
        self._item_changed(4)
        self.show()
        self._results.update_result(items)
        self._results._tree.setFocus()

    def kill_application(self):
        self._runWidget.kill_process()

    def render_web_page(self, url):
        self._item_changed(2)
        self.show()
        self._web.render_page(url)
        if settings.SHOW_WEB_INSPECTOR:
            explorer_container.ExplorerContainer().set_inspection_page(
            self._web.webFrame.page())
            self._web.webFrame.triggerPageAction(
                QWebPage.InspectElement, True)
            explorer_container.ExplorerContainer().refresh_inspector()

    def add_to_stack(self, widget, icon_path, description):
        """
        Add a widget to the container and an button(with icon))to the toolbar
        to show the widget
        """
        #add the widget
        self.stack.addWidget(widget)
        #create a button in the toolbar to show the widget
        button = QPushButton(QIcon(icon_path), '')
        button.setToolTip(description)
        #index = self.stack.count() - 1
        #func = lambda: self._item_changed(index)
        button.clicked['bool'].connect(lambda: self._item_changed(self.stack.count() - 1))#func
        self.__toolbar.addWidget(button)