Beispiel #1
0
class QChatTab(QWidget):

    def __init__(self, chat_window, nick=None, just_accepted=False):
        QWidget.__init__(self)

        self.chat_window = chat_window
        self.nick = nick
        self.just_accepted = just_accepted
        self.unread_count = 0

        self.widget_stack = QStackedWidget(self)
        self.widget_stack.addWidget(QNickInputWidget('new_chat.png', 150, self.connectClicked, parent=self))
        self.widget_stack.addWidget(QConnectingWidget(parent=self))
        self.widget_stack.addWidget(QChatWidget(self.chat_window, nick=nick, parent=self))

        if self.nick is not None:
            self.widget_stack.setCurrentIndex(2)
        else:
            self.widget_stack.setCurrentIndex(0)

        layout = QHBoxLayout()
        layout.addWidget(self.widget_stack)
        self.setLayout(layout)

    def connectClicked(self, nick):
        if self.chat_window.isNickInTabs(nick):
            QMessageBox.warning(self, TITLE_ALREADY_CONNECTED, ALREADY_CONNECTED % (nick))
            return

        self.nick = nick
        self.widget_stack.widget(1).setConnectingToNick(self.nick)
        self.widget_stack.setCurrentIndex(1)
        self.chat_window.client.openSession(self.nick)
        self.widget_stack.widget(2).setRemoteNick(self.nick)

    def appendMessage(self, message, source):
        self.widget_stack.widget(2).appendMessage(message, source)

    def showNowChattingMessage(self):
        self.widget_stack.setCurrentIndex(2)
        self.widget_stack.widget(2).showNowChattingMessage(self.nick)

    def enable(self):
        self.widget_stack.setCurrentIndex(2)
        self.widget_stack.widget(2).enable()

    def resetOrDisable(self):
        cur_widget_index = self.widget_stack.currentIndex()
        if cur_widget_index == 1:
            self.widget_stack.setCurrentIndex(0)
        elif cur_widget_index == 2:
            self.widget_stack.widget(2).disable()
Beispiel #2
0
class LoadFilesDialog(QDialog):
    def __init__(self):
        super().__init__()
        self.ui()

    def ui(self):
        vbox = QVBoxLayout()
        hbox1 = QHBoxLayout()
        hbox1Widget = QWidget(parent=self)
        self.arch_label = QLabel(parent=hbox1Widget)
        self.arch_label.setText("架构")
        self.combobox = QComboBox(parent=hbox1Widget)
        self.combobox.addItem("riscv64")
        self.combobox.setCurrentIndex(0)
        hbox1.addWidget(self.arch_label)
        hbox1.addStretch()
        hbox1.addWidget(self.combobox)
        hbox1Widget.setLayout(hbox1)
        vbox.addWidget(hbox1Widget)

        hbox2 = QHBoxLayout()
        hbox2Widget = QWidget(parent=self)
        self.guest_label = QLabel(parent=hbox2Widget)
        self.guest_label.setText("宿主程序")
        self.guest_combobox = QComboBox(parent=self)
        self.guest_combobox.addItems(["Linux", "Binary"])
        self.guest_combobox.setCurrentIndex(0)
        self.guest_combobox.currentIndexChanged.connect(self.switchStack)
        hbox2.addWidget(self.guest_label)
        hbox2.addStretch()
        hbox2.addWidget(self.guest_combobox)
        hbox2Widget.setLayout(hbox2)
        vbox.addWidget(hbox2Widget)

        self.stack = QStackedWidget(parent=self)
        self.buildStackWidgets(self.stack)
        self.stack.setCurrentIndex(0)
        vbox.addWidget(self.stack)

        self.input_button = QPushButton(parent=self)
        self.input_button.setText("确认")
        self.input_button.clicked.connect(self.judge)
        vbox.addWidget(self.input_button)

        self.setLayout(vbox)
        self.setStyleSheet(dialog_stylesheet)
        self.resize(500, 300)
        self.setWindowTitle("选择加载文件")

    def buildStackWidgets(self, stack):
        linux_layout1 = QFormLayout()
        self.bbl_btn = QPushButton(parent=stack)
        self.bbl_btn.setText("加载")
        self.bbl_btn.clicked.connect(lambda: self.getFileName(self.bbl_btn))
        self.kernel_btn = QPushButton(parent=stack)
        self.kernel_btn.setText("加载")
        self.kernel_btn.clicked.connect(
            lambda: self.getFileName(self.kernel_btn))
        self.rootfs_btn = QPushButton(parent=stack)
        self.rootfs_btn.setText("加载")
        self.rootfs_btn.clicked.connect(
            lambda: self.getFileName(self.rootfs_btn))
        self.cmdline_edit = QLineEdit(parent=stack)
        self.cmdline_edit.setText("console=hvc0 root=/dev/vda rw")
        linux_layout1.addRow("BBL", self.bbl_btn)
        linux_layout1.addRow("Kernel", self.kernel_btn)
        linux_layout1.addRow("RootFS", self.rootfs_btn)
        linux_layout1.addRow("cmdline", self.cmdline_edit)
        linux_widget = QWidget(parent=stack)
        linux_widget.setLayout(linux_layout1)
        stack.addWidget(linux_widget)

        bin_layout2 = QFormLayout()
        self.bin_btn = QPushButton(parent=stack)
        self.bin_btn.setText("加载")
        self.bin_btn.clicked.connect(lambda: self.getFileName(self.bin_btn))
        bin_layout2.addRow("binary", self.bin_btn)
        bin_widget = QWidget(parent=stack)
        bin_widget.setLayout(bin_layout2)
        stack.addWidget(bin_widget)

    def switchStack(self, i):
        self.stack.setCurrentIndex(i)

    def getFileName(self, btn):
        filename = QFileDialog.getOpenFileName(
            self, caption="选取文件", filter="Binarys (*.bin);;AllFiles (*.*)")
        if (filename and filename[0] != ""):
            btn.setText(filename[0])
        else:
            btn.setText("加载")

    def judge(self):
        global load_files
        if (self.stack.currentIndex() == 0):
            if ((self.bbl_btn.text() == "加载")
                    or (self.kernel_btn.text() == "加载")
                    or (self.rootfs_btn.text() == "加载")):
                QMessageBox.information(self, "提示", "请加载全部文件", QMessageBox.Ok,
                                        QMessageBox.Ok)
                return
            else:
                stopEmulator()
                load_files['type'] = self.guest_combobox.currentText()
                load_files["bbl"] = self.bbl_btn.text()
                load_files["kernel"] = self.kernel_btn.text()
                load_files["rootfs"] = self.rootfs_btn.text()
                load_files["cmdline"] = self.cmdline_edit.text()
                self.accept()
        else:
            if (self.bin_btn.text() == "加载"):
                QMessageBox.information(self, "提示", "请加载全部文件", QMessageBox.Ok,
                                        QMessageBox.Ok)
                return
            else:
                stopEmulator()
                load_files["type"] = self.guest_combobox.currentText()
                load_files["bin"] = self.bin_btn.text()
                self.accept()
Beispiel #3
0
class AboutDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(
            parent, Qt.MSWindowsFixedSizeDialogHint | Qt.WindowTitleHint
            | Qt.WindowSystemMenuHint | Qt.WindowCloseButtonHint)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setWindowModality(Qt.ApplicationModal)
        self.setWindowTitle(self.tr("About"))

        app = QApplication.instance()
        name = app.applicationName()
        domain = app.organizationDomain()

        iconLabel = QLabel(self)
        iconLabel.setAlignment(Qt.AlignCenter)
        iconLabel.setMaximumWidth(250)
        iconLabel.setMinimumSize(250, 210)
        icon = self.windowIcon()
        size = icon.actualSize(QSize(186, 186))
        iconLabel.setPixmap(icon.pixmap(size))
        titleLabel = QLabel(self)
        titleLabel.setText(
            self.
            tr("<p style='color: #353535; font-size: 24pt; font-weight: 250'>"
               "TruFont Font Editor</p>"
               "<p style='font-size: 13pt; font-weight: 400'>{} Pristine Wax</p>"
               ).format(__version__))
        textLabel = QLabel(self)
        text = self.tr(
            "<p>{n} is a free and open source font editor and scripting "
            "environment made by the developers of the {n} community.</p>"
            "<p>{n} is built upon the "
            "<a href='http://ts-defcon.readthedocs.org/en/ufo3/' "
            "style='color: #356FDE'>defcon</a> UFO library and exposes a "
            "<a href='http://robofab.com/' style='color: #356FDE'>robofab</a>"
            "-like API for scripting purposes.</p>"
            "<p>Running on Qt {} (PyQt {}).</p>"
            "<p>Version {} {} – Python {}.").format(QT_VERSION_STR,
                                                    PYQT_VERSION_STR,
                                                    __version__,
                                                    gitShortHash,
                                                    platform.python_version(),
                                                    n=name)
        if domain:
            text += self.tr(
                "<br>See <a href='http://{d}' style='color: #356FDE'>{d}</a> "
                "for more information.</p>").format(d=domain)
        else:
            text += "</p>"
        textLabel.setText(text)
        textLabel.setOpenExternalLinks(True)
        textLabel.setWordWrap(True)

        authorsLabel = QTextBrowser(self)
        authorsLabel.setText("\n".join(self.authors()))

        licenseLabel = QTextBrowser(self)
        licenseLabel.setText(licenseText)

        thanksLabel = QTextBrowser(self)
        thanksLabel.setText(thanksText)

        self.stackWidget = QStackedWidget(self)
        self.stackWidget.addWidget(textLabel)
        self.stackWidget.addWidget(authorsLabel)
        self.stackWidget.addWidget(licenseLabel)
        self.stackWidget.addWidget(thanksLabel)

        textLayout = QVBoxLayout()
        textLayout.setContentsMargins(0, 14, 16, 16)
        textLayout.addWidget(titleLabel)
        textLayout.addWidget(self.stackWidget)
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        textLayout.addWidget(spacer)

        mainLayout = QHBoxLayout()
        mainLayout.addWidget(iconLabel)
        mainLayout.addLayout(textLayout)

        frame = QFrame()
        frame.setMinimumHeight(54)
        frame.setMaximumHeight(54)
        frame.setStyleSheet("background: rgb(230, 230, 230)")

        buttonsLayout = QHBoxLayout(frame)
        for index, text in enumerate(("Authors", "License", "Credits")):
            label = QLabel(text, self)
            label.setAlignment(Qt.AlignCenter)
            label.setCursor(Qt.PointingHandCursor)
            label.setProperty("index", index + 1)
            label.setStyleSheet("color: #356FDE; text-decoration: underline")
            label.installEventFilter(self)
            buttonsLayout.addWidget(label)

        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addLayout(mainLayout)
        layout.addWidget(frame)

    def authors(self):
        for line in gitShortLog.splitlines():
            elem = line.split("\t")[1]
            if not elem or elem.startswith("=?") or elem.endswith("bot"):
                continue
            yield elem

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

    def eventFilter(self, obj, event):
        if event.type() == QEvent.MouseButtonPress:
            index = obj.property("index")
            if index is not None:
                if self.stackWidget.currentIndex() == index:
                    index = 0
                self.stackWidget.setCurrentIndex(index)
                return True
            return False
        return super().eventFilter(obj, event)

    def sizeHint(self):
        return QSize(600, 314)
Beispiel #4
0
class FlowDialog(QWidget):
    """
    Class for controlling the order of screens in the experiment.
    """

    def __init__(self, parent=None):
        super(FlowDialog, self).__init__(parent)
        self._n_steps = 1  # number of performed steps

        self.pages_widget = QStackedWidget()

        # create widgets
        self.experiment_setup = ExperimentSetup()
        self.instructions = InstructionsScreen("./instructions/he-informed-consent.txt")
        self.new_user_form = NewUserForm()
        self.data_widget = DataWidget()

        # add widgets to pages_widget
        self.pages_widget.addWidget(self.experiment_setup)
        self.pages_widget.addWidget(self.instructions)
        self.pages_widget.addWidget(self.new_user_form)
        self.pages_widget.addWidget(self.data_widget)

        # next button
        self.next_button = QPushButton("&Next")
        self.next_button_layout = QHBoxLayout()
        self.next_button_layout.addStretch(1)
        self.next_button_layout.addWidget(self.next_button)
        self.next_button.pressed.connect(self.nextPressed)

        main_layout = QGridLayout()

        # set screen shoulders
        main_layout.setRowMinimumHeight(0, 80)
        main_layout.setRowMinimumHeight(3, 80)
        main_layout.setColumnMinimumWidth(0, 80)
        main_layout.setColumnMinimumWidth(2, 80)
        main_layout.addWidget(self.pages_widget, 1, 1)
        main_layout.addLayout(self.next_button_layout, 2, 1)
        self.setLayout(main_layout)
        self.pages_widget.setCurrentIndex(0)

    def nextPressed(self):
        """
        control the order and number of repetitions of the experiment
        """
        self.data_widget.count_down_timer.restart_timer()  # restart timer

        # if on setup screen
        if self.pages_widget.currentIndex() == 0:
            # get condition index number
            condition_index = self.experiment_setup.conditions_combo.currentIndex()
            # DEBUGGING:
            debug_condition = config.CONDITIONS['a'][condition_index]
            print(debug_condition)

            self.raise_index()

        # welcome screen
        elif self.pages_widget.currentIndex() == 1:
            self.raise_index()

        # if on new_user screen
        elif self.pages_widget.currentIndex() == 2:
            self.new_user_form.save_results()
            self.raise_index()

        # if on data screen
        elif self.pages_widget.currentIndex() == 3:
            if self._n_steps < config.NUM_STEPS:
                self.data_widget.add_log_row()
                self._n_steps += 1
                print("Step number {}".format(self._n_steps))  # DEBUGGING
            else:
                self.raise_index()

        elif self.pages_widget.currentIndex() == 4:
            print("That's it")  # DEBUGGING

        else:
            # If reached wrong index
            raise RuntimeError("No such index")

    def raise_index(self):
        current = self.pages_widget.currentIndex()
        self.pages_widget.setCurrentIndex(current + 1)
Beispiel #5
0
class OptionSettings(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self, parent)
 
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
 
        self.btnWin1 = OptionButtons('Win One', self.ShowWindow1)
        self.btnWin2 = OptionButtons('Win Two', self.ShowWindow2)
      # Vertical Box for Buttons *************************************
        self.UpLeft  = QVBoxLayout()
        self.UpLeft.addWidget(self.btnWin1)
        self.UpLeft.addWidget(self.btnWin2)
        self.UpLeft.addStretch(1)
  # Display Area on Right
      # Widget Flip Display ******************************************
        self.UpRite   = QHBoxLayout()
        self.Contents = QStackedWidget()
        self.Contents.addWidget(QTextEdit('Nothing Selected'))
        self.Contents.addWidget(Win1Disply(self))
        self.Contents.addWidget(Win2Disply(self))
        self.Contents.addWidget(QTextEdit('Settings Saved'))
        self.Contents.setCurrentIndex(0)
        self.UpRite.addWidget(self.Contents)
 
  # Button and Display Area on Top
        self.Upper = QHBoxLayout()
        self.Upper.addLayout(self.UpLeft)
        self.Upper.addLayout(self.UpRite)
  # Save and Cancel Area on Bottom
        self.btnSave = QPushButton("Save")
        self.btnSave.clicked.connect(self.SaveSettings)
        self.btnCncl = QPushButton("Cancel")
        self.btnCncl.clicked.connect(self.close)
        self.Lower   = QHBoxLayout()
        self.Lower.addStretch(1)
        self.Lower.addWidget(self.btnSave)
        self.Lower.addWidget(self.btnCncl)
  # Entire Options Window Layout
        self.OuterBox = QVBoxLayout()
        self.OuterBox.addLayout(self.Upper)
        self.OuterBox.addLayout(self.Lower)
        self.setLayout(self.OuterBox)
        self.setWindowTitle('Settings')
        #Geometry(Left, Top, Width, Hight)
        self.setGeometry(250, 250, 550, 450)
        self.setModal(True)
        self.exec()
 
    def ShowWindow1(self):
        self.Contents.setCurrentIndex(1)
 
    def ShowWindow2(self):
        self.Contents.setCurrentIndex(2)
 
    def SaveSettings(self):
        self.Contents.setCurrentIndex(3)
     
    def RightArrow(self):
        if self.Contents.currentIndex() == 1:
           self.Contents.setCurrentIndex(2)
        else:
           self.Contents.setCurrentIndex(1)
Beispiel #6
0
class Slideshow(QMainWindow):
    
    def __init__(self, parent, gallery=list(), index=0):
        
        super(Slideshow, self).__init__()
        self.parent = parent
        self.configure_gui()
        self.create_widgets(gallery)
        self.create_menu()
        self.index = index
        self.move()
        self.showMaximized()
        self.activateWindow()
    
    def configure_gui(self):
        
        self.stack = QStackedWidget(self)
        self.setCentralWidget(self.stack)

    def create_widgets(self, gallery):
        
        self.model = Model(self, gallery)

        self.image = imageViewer(self)
        self.video = videoPlayer(self)
        self.stack.addWidget(self.image)
        self.stack.addWidget(self.video)
        
        self.setMouseTracking(True)
        self.timer = QTimer()
        self.timer.timeout.connect(
            lambda: self.setCursor(Qt.BlankCursor)
            )
        
    def create_menu(self):

        self.menubar = self.menuBar()
        self.menubar.triggered.connect(self.menuPressEvent)
        
        # File
        file = self.menubar.addMenu('File')
        file.addAction('Copy Image to',  lambda: copy_to(self, [self.model.index(self.index)]), shortcut='CTRL+SHIFT+C')
        file.addSeparator()
        file.addAction('Exit', self.close, shortcut='Ctrl+W')
        
        # View
        view = self.menubar.addMenu('View')
        view.addAction('Fullscreen', self.fullscreen, shortcut='F11')
        
        # Help
        help = self.menubar.addMenu('Help')
        
        menu = QMenu(self)
        # menu = create_menu(
            # self,
            
            # )

        self.full = QAction(
            'Fullscreen', menu, triggered=self.fullscreen
            )
        menu.addAction(self.full)
        
        self.play_menu = QMenu('Slideshow')
        group1 = QActionGroup(self.play_menu)
        group1.setExclusive(True)
        self.play_menu.addActions([
                QAction(
                    'Pause', self.play_menu, triggered=self.playEvent, checked=True
                    ),
                QAction(
                    'Play', self.play_menu, triggered=self.playEvent
                    )
            ])
        self.play_menu.addSeparator()
        group2 = QActionGroup(self.play_menu)
        group2.setExclusive(True)
        self.play_menu.addActions([
                QAction(
                    'Speed - Slow', self.play_menu, triggered=self.playEvent
                ),
                QAction(
                    'Speed - Medium', self.play_menu, triggered=self.playEvent, checked=True
                ),
                QAction(
                    'Speed - Fast', self.play_menu, triggered=self.playEvent
                )
            ])
        # menu.addMenu(self.play_menu)
        
        menu.addSeparator()
        
        menu.addAction(QAction(
            'Rotate right', menu, triggered=lambda: self.rotate(+1)
            ))
        menu.addAction(QAction(
            'Rotate left', menu, triggered=lambda: self.rotate(-1)
            ))
        
        menu.addSeparator()
        
        menu.addAction(QAction(
            'Copy', menu, triggered=self.copy
            ))
        menu.addAction(QAction(
            'Delete', menu, triggered=self.delete
            ))
        
        menu.addSeparator()
        
        menu.addAction(QAction(
            'Properties', menu, triggered=self.openEditor
            ))

        self.menu = menu

    def move(self, delta=0):
        
        if not self.model.gallery:
            self.image.no_image()
            return 0
        
        self.index = (self.index + delta) % len(self.model.gallery)
        path = self.model.index(self.index).data(Qt.UserRole)[0]
        self.setWindowTitle(f'{Path(path).name} - Slideshow')

        if path is None: pixmap = QPixmap()
        else:
            if path.endswith(('.jpg', '.png')):
                image = QImage(path)
                path = None
            elif path.endswith(('.gif', '.mp4', '.webm')):
                image = get_frame(path)
            else: print(path)
            
            pixmap = QPixmap(image).scaled(
                self.size(), Qt.KeepAspectRatio, 
                transformMode=Qt.SmoothTransformation
                )

        self.image.update(pixmap)
        self.stack.setCurrentIndex(0)
        self.video.update(path)
    
    def fullscreen(self):

        if self.isFullScreen():

            self.timer.stop()
            self.image.setStyleSheet('background: ')
            self.full.setText('Fullscreen')
            self.setCursor(Qt.ArrowCursor)
            self.showMaximized()
            self.menubar.show()

        else:

            self.image.setStyleSheet('background: black')
            self.full.setText('Exit fullscreen')
            self.setCursor(Qt.BlankCursor)
            self.menubar.hide()
            self.showFullScreen()

    def rotate(self, sign):

        path = self.model.index(self.index).data(Qt.UserRole)[0]

        if path.endswith(('jpg', 'png')):
            self.image.rotate(path, sign)
        
        else:
            self.video.rotate(path, sign)
            
        self.move()

    def copy(self):
        
        path = self.model.index(self.index).data(Qt.UserRole)[0]

        if path.endswith(('gif', '.mp4', '.webm')):
            return

            path = mktemp(suffix='.png')
            image = ImageGrab.grab(
                (0, 0, self.width(),self.height())
                )
            image.save(path)

        cb = QApplication.clipboard()
        cb.clear(mode=cb.Clipboard)
        cb.setText(path, mode=cb.Clipboard)

    def delete(self):
        
        if self.stack.currentIndex():
            self.video.update(None)
        else: self.image.update(None)
        
        index = [self.model.index(self.index)]

        if self.parent.delete_records(index):
            del self.model.gallery[self.index]
            self.model.layoutChanged.emit()

    def openEditor(self):

        index = self.model.index(self.index)
        
        Properties(self.parent, [index.data(Qt.EditRole)])
    
    def playEvent(self, event):
        
        match self.play_menu.activeAction().text:
            
            case 'Speed - Slow': pass
            case 'Speed - Medium': pass
            case 'Speed - Fast': pass
          
    def contextMenuEvent(self, event):
        
        self.menu.popup(event.globalPos())

    def menuPressEvent(self, event=None):

        action = event.text()
        
        # if action == 'Copy Image to':
            
        #     copy_to(self, [self.model.index(self.index)])

        if action == 'Exit': self.close()
        
    def keyPressEvent(self, event):

        key_press = event.key()
        video = self.stack.currentIndex()
        alt = event.modifiers() == Qt.AltModifier
        ctrl = event.modifiers() == Qt.ControlModifier
        shift = event.modifiers() == Qt.ShiftModifier

        if key_press in (Qt.Key_Right, Qt.Key_Left):
            
            self.move(1 if key_press == Qt.Key_Right else -1)
            
        elif video and key_press in (Qt.Key_Home, Qt.Key_End):
            
            if key_press == Qt.Key_Home: self.video.position(0)
            else: self.video.position(0)
            
        elif video and key_press in (Qt.Key_Period, Qt.Key_Comma):

            sign = 1 if key_press == Qt.Key_Period else -1
            if ctrl: self.video.position(sign * 50)
            else: self.video.position(sign * 5000)
        
        elif video and key_press in (Qt.Key_Up, Qt.Key_Down):

            sign = 1 if key_press == Qt.Key_Up else -1
            if ctrl: self.video.volume(sign * 1)
            else: self.video.volume(sign * 10)
        
        elif video and key_press == Qt.Key_Space: self.video.pause()
        
        elif video and key_press == Qt.Key_M: self.video.mute()
        
        elif key_press == Qt.Key_Delete: self.delete()
        
        elif key_press == Qt.Key_F11: self.fullscreen()

        elif key_press == Qt.Key_Escape:
            
            if self.isFullScreen(): self.fullscreen()
            else: self.close()
        
        elif alt:
            
            if key_press in (Qt.Key_Return, Qt.Key_Enter): self.openEditor()
            
        elif ctrl:
            
            if shift and key_press == Qt.Key_C: opy_to(self, [self.model.index(self.index)])
            
            elif key_press == Qt.Key_C: self.copy()
            
            elif key_press == Qt.Key_W: self.close()
    
    def mousePressEvent(self, event):
        
        if event.button() == Qt.MouseButton.LeftButton:
            
            if event.x() > (self.width() * .5): self.move(+1)
            elif event.x() < (self.width() * .5): self.move(-1)
    
    def mouseMoveEvent(self, event):
        
        if self.isFullScreen():

            self.setCursor(Qt.ArrowCursor)
            self.timer.start(1500)
        
    def closeEvent(self, event): self.video.update(None)
Beispiel #7
0
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        # window = QMainWindow()
        # window.resize(800, 600)

        # self.centralwidget = QtWidgets.QWidget(MainWindow)
        # self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.stack = QStackedWidget(parent=MainWindow)
        self.centralwidget = QStackedWidget(parent=MainWindow)

        self.centralwidget.setObjectName("centralwidget")

        # self.matplotlibwidget_static = MatplotlibWidget(self.centralwidget)
        matplotlibwidget_static = MatplotlibWidget()
        matplotlibwidget_static.setGeometry(QtCore.QRect(10, 0, 611, 271))
        matplotlibwidget_static.setObjectName("matplotlibwidget_static")

        self.stack.addWidget(matplotlibwidget_static)

        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(670, 80, 75, 23))
        self.pushButton.setObjectName("pushButton")

        # self.matplotlibwidget_dynamic = MatplotlibWidget()
        # self.matplotlibwidget_dynamic.setEnabled(True)
        # self.matplotlibwidget_dynamic.setGeometry(QtCore.QRect(10, 270, 611, 291))
        # self.matplotlibwidget_dynamic.setObjectName("matplotlibwidget_dynamic")
        matplotlibwidget_dynamic = MatplotlibWidget()
        matplotlibwidget_dynamic.setEnabled(True)
        matplotlibwidget_dynamic.setGeometry(QtCore.QRect(10, 0, 611, 271))
        matplotlibwidget_dynamic.setObjectName("matplotlibwidget_dynamic")

        self.stack.addWidget(matplotlibwidget_dynamic)

        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(670, 370, 75, 23))
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)

        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        self.stack.show()
        # window.show()

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "显示静态图"))
        self.pushButton_2.setText(_translate("MainWindow", "显示动态图"))

    def next(self):
        self.stack.setCurrentIndex(self.stack.currentIndex() + 1)
        print('current', self.stack.currentIndex())
Beispiel #8
0
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.setWindowTitle("")
        self.screens_config = []
        self.current_screen = 0
        self.sub_screens = {}
        self.button = {}
        self.central_widget = QStackedWidget()
        self.number_of_subs = 0
        self.main_layout = QGridLayout()
        self.gui_element_builder = GuiElementsBuilder()

    def say_hello(self):
        print("Button clicked, Hello!")

    def set_central_widget2(self, widget: QWidget):
        source_button = self.sender()
        print("set_central_widget2 WidgetName:  " + widget.getName())
        index = self.central_widget.widget(widget)
        self.central_widget.setCurrentIndex(index)

    def set_central_widget(self):
        source_button = self.sender()
        for i in range(0, self.number_of_subs):
            if self.central_widget.widget(i).getName() == source_button.text():
                self.central_widget.setCurrentIndex(i)

    def change_widget(self, widget: QWidget, direction: int):
        #print("len: " + str(len(self.screens_config["sub"])))
        max_screen = len(self.screens_config["sub"])
        if direction == 0:
            self.central_widget.setCurrentIndex(
                (self.central_widget.currentIndex() - 1) % max_screen)
            #self.current_screen = (self.current_screen + 1) % max_screen
        elif direction == 1:
            self.central_widget.setCurrentIndex(
                (self.central_widget.currentIndex() + 1) % max_screen)
            #self.current_screen = (self.current_screen - 1) % max_screen
        else:
            print("that not a valid direction")

        #Farbe sollte im Widget selbst geetzt werden bzw. schon bei Erstellung
        #for items in self.screens_config['sub']:
        #b = QColor(self.screens_config['sub'][self.current_screen]["Background"])
        #b = QColor(self.screens_config['sub'][self.central_widget.currentIndex()]["Background"])
        #p = self.central_widget.palette()
        #p.setColor(self.central_widget.backgroundRole(), b)
        #self.central_widget.setPalette(p)
        #self.central_widget.setAutoFillBackground(True)

    def init_with_config(self, config: dict):
        self.screens_config = config

        # TODO: put in delegation or inheritance
        #Set Title
        if 'name' not in config:
            title = str(config['main']['name'])
            self.setWindowTitle(title)
            # Set Resolution
            window_width = config['main']["resolution"][0]
            window_height = config['main']["resolution"][1]
            button_width = config['main']["button-size"][0]
            button_height = config['main']["button-size"][1]
            self.number_of_subs = len(config['sub'])
        else:
            title = str(config['name'])
            self.setWindowTitle(title)
            # Set Resolution
            window_width = config["resolution"][0]
            window_height = config["resolution"][1]
            button_width = config["button-size"][0]
            button_height = config["button-size"][1]
            self.number_of_subs = len(self.screens_config["sub"])
        self.setFixedSize(window_width, window_height)

        main_widget = QWidget()
        main_widget.setLayout(self.main_layout)
        vbox_menu = QVBoxLayout()
        vbox_menu.setSizeConstraint(QLayout.SetFixedSize)

        system_color = "#ffd966"

        # Header
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.TOP_LEFT, 0,
                                                    0), 0, 0)
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.BUTTON, 30,
                                                    650), 0, 1, Qt.AlignTop)
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.END_RIGHT, 0,
                                                    0), 0, 3, Qt.AlignTop)

        # Menu
        self.main_layout.addLayout(vbox_menu, 1, 0)

        # Footer
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.BOTTOM_LEFT, 0,
                                                    0), 2, 0)
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.BUTTON, 30,
                                                    650), 2, 1, Qt.AlignBottom)
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.END_RIGHT, 0,
                                                    0), 2, 3, Qt.AlignBottom)

        # button_stylesheet = "background-color:" + system_color +"; border-width: 2px; border-radius: 36px; bordericolor: black; font: bold 14px; min-width: 10em; padding: 6px;"
        # button_up = QPushButton("\u1403")
        # button_up.setFixedSize(button_width * window_width/100, button_height * window_height/100)
        # button_up.setStyleSheet(button_stylesheet)
        #
        # button_down = QPushButton("\u1401")
        # button_down.setFixedSize(button_width * window_width / 100, button_height * window_height / 100)
        # button_down.setStyleSheet(button_stylesheet)

        button_ListWidget = QListWidget()
        button_ListWidget.setStyleSheet(
            """QListWidget{background: #f2eeed;  border: 0px solid #f2eeed;}"""
        )
        # Erstellen der rechten Button-Leiste ##############
        #vbox_menu.addWidget(button_up)
        button_width = button_width * window_width / 100
        button_height = button_height * window_height / 100
        background_color = "#f2eeed"
        button_size = QSize(button_width, button_height)
        for i in range(0, self.number_of_subs):
            subbutton_list_item = QListWidgetItem(button_ListWidget)
            placeholder_listItem = QListWidgetItem(button_ListWidget)
            placeholder_listItem.setSizeHint(QSize(button_width, 4))

            flag = placeholder_listItem.flags() & Qt.ItemIsUserCheckable
            placeholder_listItem.setFlags(flag)
            #subbutton_listItem.setBackground(QColor("#f2eeed"))
            # Widgets ##################################################################################################
            if i == 0:
                self.central_widget.insertWidget(i, Clock())
            else:
                self.central_widget.insertWidget(
                    i, Placeholder(self.screens_config["sub"][i]["name"]))
            # Buttons ##################################################################################################
            button_color = self.screens_config['sub'][i]["Background"]
            self.button[i] = QPushButton(self.screens_config["sub"][i]["name"],
                                         self)
            self.button[i].setFixedSize(button_size)

            #Button with stylsheet
            #self.button[i].setStyleSheet("background-color:" +button_color +"; border-width: 2px; border-radius: 10px; bordericolor: black; font: bold 14px; min-width: 10em; padding: 6px;")

            path_button = Button_full.build_svg(
                Button_full(
                    button_width, button_height, background_color,
                    self.screens_config["sub"][i]["Background"] + "_button"))
            self.button[i].setStyleSheet("background-image: url(" +
                                         path_button + ");"
                                         "border:1px; background-color:" +
                                         button_color + ";")
            #print("Button: " + self.button[i].text() + "Screen: " + self.central_widget.widget(i).getName())
            # signals ##################################################################################################
            #self.button[i].clicked.connect(lambda widget=self.central_widget.widget(i): self.set_central_widget2(self, widget))
            self.button[i].clicked.connect(
                lambda widget=self.central_widget.widget(
                    i): self.set_central_widget())
            subbutton_list_item.setSizeHint(button_size)
            #print(self.button[i].size())
            #
            button_ListWidget.addItem(placeholder_listItem)
            button_ListWidget.addItem(subbutton_list_item)
            button_ListWidget.setItemWidget(subbutton_list_item,
                                            self.button[i])
            button_ListWidget.setMaximumWidth(1000)
            #vbox_menu.addWidget(self.button[i])

        vbox_menu.addWidget(button_ListWidget)
        #vbox_menu.addWidget(button_down)
        #downbutton_listItem = QListWidgetItem(button_ListWidget)
        #downbutton_listItem.setSizeHint(button_down.size())
        #button_ListWidget.addItem(downbutton_listItem)
        #button_ListWidget.setItemWidget(downbutton_listItem, button_down)
        button_ListWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        button_ListWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        button_ListWidget.setMaximumWidth(
            button_ListWidget.sizeHintForColumn(0))

        #############################################
        self.central_widget.setCurrentIndex(1)
        self.main_layout.addWidget(self.central_widget, 1, 1)

        self.setCentralWidget(main_widget)
Beispiel #9
0
class WindowOption(QWidget):
    def __init__(self):
        super(WindowOption, self).__init__()
        self.initUI()

    def initUI(self):
        """# Global """
        self.setWindowTitle('Option')
        self.setFixedSize(800, 450)

        self.list_option = QListWidget(self)
        self.stack_window = QStackedWidget(self)

        self.style_list_option = "QListWidget{\
                                min-width: 120px;\
                                max-width: 120px;\
                                color: white;\
                                background: grey;}"

        self.style_groupbox = "QGroupBox{\
                               border: None;}"

        self.style_groupbox_font = "QGroupBox{\
                                    font-family: MonoxRegular;\
                                    font-size: 20px;}"

        layout_main = QHBoxLayout(spacing=0)
        layout_main.setContentsMargins(0, 0, 0, 0)

        layout_main.addWidget(self.list_option)
        layout_main.addWidget(self.stack_window)
        self.setLayout(layout_main)

        self.list_option.setStyleSheet(self.style_list_option)
        """# List Option"""
        self.list_option.setFrameShape(QListWidget.NoFrame)
        self.list_option.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.list_option.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.list_option.currentRowChanged.connect(
            self.stack_window.setCurrentIndex)

        font = QFont()
        font.setFamily('MonoxLight')
        font.setPointSize(20)

        item = QListWidgetItem()
        item.setFont(font)
        item.setText('DIY')
        item.setSizeHint(QSize(0, 60))
        item.setTextAlignment(Qt.AlignCenter)
        self.list_option.addItem(item)

        item = QListWidgetItem()
        item.setFont(font)
        item.setText('PRO')
        item.setSizeHint(QSize(0, 60))
        item.setTextAlignment(Qt.AlignCenter)
        self.list_option.addItem(item)
        """# Stack Window"""
        """# Page 0"""
        self.page0 = QWidget()

        self.tabwidget_page0 = QTabWidget(self.page0)

        self.tab0_page0 = QWidget(self.page0)
        self.tab1_page0 = QWidget(self.page0)

        self.tabwidget_page0.addTab(self.tab0_page0, 'Info')
        self.tabwidget_page0.addTab(self.tab1_page0, 'Option')
        self.page0tab0()
        self.page0tab1()
        self.page0global()
        self.page1tab0()
        self.page1tab1()
        self.page1global()

    def keyReleaseEvent(self, event):
        """DocString for pressKeyEwent"""
        #@todo: to be defined.
        if event.key() == Qt.Key_D:
            self.list_option.setCurrentRow(0)
            self.stack_window.setCurrentIndex(0)

        if event.key() == Qt.Key_P:
            self.list_option.setCurrentRow(1)
            self.stack_window.setCurrentIndex(1)

        if event.key() == Qt.Key_O:
            if self.stack_window.currentIndex() == 1:
                self.tabwidget_page1.setCurrentIndex(1)
            else:
                self.tabwidget_page0.setCurrentIndex(1)

        if event.key() == Qt.Key_I:
            if self.stack_window.currentIndex() == 1:
                self.tabwidget_page1.setCurrentIndex(0)
            else:
                self.tabwidget_page0.setCurrentIndex(0)

        if event.key() == Qt.Key_Return:
            if self.stack_window.currentIndex() == 1:
                self.pushbutton_ok_page1.click()
            else:
                self.pushbutton_ok_page0.click()

        if event.key() == Qt.Key_R:
            if self.stack_window.currentIndex() == 1:
                self.pushbutton_re_page1.click()
            else:
                self.pushbutton_re_page0.click()

        if event.key() == Qt.Key_Q:
            self.close()

    def page0tab0(self):
        """DocString for page0tab0"""
        #@todo: to be defined.

        font = QFont()
        font.setFamily('MonoxLight')
        font.setPointSize(12)

        label_date_page0 = QLabel('Date      ')
        label_date_page0.setFont(font)

        label_time_page0 = QLabel('Time      ')
        label_time_page0.setFont(font)

        label_loc_page0 = QLabel('Location  ')
        label_loc_page0.setFont(font)

        label_users_name_page0 = QLabel('Name     ')
        label_users_name_page0.setFont(font)

        font_spe = QFont()
        font_spe.setFamily('MonoxRegular Bold')
        font_spe.setPointSize(12)
        label_users_gender_page0 = QLabel('Gender')
        label_users_gender_page0.setFont(font_spe)

        label_note_page0 = QLabel('Note')
        label_note_page0.setFont(font)

        self.dateedit_date_page0 = QDateEdit(QDate.currentDate())
        self.dateedit_date_page0.setDisplayFormat('yyyy/MM/dd')
        self.dateedit_date_page0.setCalendarPopup(True)
        self.dateedit_date_page0.setFont(font)
        self.timeedit_time_page0 = QTimeEdit(QTime.currentTime())
        self.timeedit_time_page0.setDisplayFormat('HH : mm')
        self.timeedit_time_page0.setFont(font)
        self.lineedit_loc_page0 = QLineEdit('Mars')
        self.lineedit_loc_page0.setFont(font)
        self.lineedit_users_name_page0 = QLineEdit('Object')
        self.lineedit_users_name_page0.setFont(font)

        self.radiobutton_users_gender_male_page0 = QRadioButton('Male')
        self.radiobutton_users_gender_female_page0 = QRadioButton('Female')
        self.radiobutton_users_gender_secret_page0 = QRadioButton('Secret')

        self.textedit_note_page0 = QTextEdit('None')
        self.textedit_note_page0.setFont(font)

        line_split_page0 = QFrame()
        line_split_page0.setFrameShape(QFrame.VLine)
        line_split_page0.setFrameShadow(QFrame.Sunken)

        groupbox_radio_button = QGroupBox()
        groupbox_radio_button.setStyleSheet(self.style_groupbox)

        layout_groupbox_radio_button = QHBoxLayout()
        layout_groupbox_radio_button.addWidget(
            self.radiobutton_users_gender_male_page0)
        layout_groupbox_radio_button.addWidget(
            self.radiobutton_users_gender_female_page0)
        layout_groupbox_radio_button.addWidget(
            self.radiobutton_users_gender_secret_page0)
        groupbox_radio_button.setLayout(layout_groupbox_radio_button)

        layout_tab0_page0_global = QVBoxLayout(self.tab0_page0)

        layout_info_page0 = QHBoxLayout()
        layout_note_page0 = QVBoxLayout()

        layout_time_page0 = QVBoxLayout()
        layout_user_page0 = QVBoxLayout()

        layout_date_page0 = QHBoxLayout()
        layout_date_page0.setAlignment(Qt.AlignLeft)
        layout_clock_page0 = QHBoxLayout()
        layout_clock_page0.setAlignment(Qt.AlignLeft)
        layout_loc_page0 = QHBoxLayout()
        layout_loc_page0.setAlignment(Qt.AlignLeft)
        layout_name_page0 = QHBoxLayout()
        layout_gender_page0 = QVBoxLayout()

        layout_date_page0.addWidget(label_date_page0)
        layout_date_page0.addWidget(self.dateedit_date_page0)
        layout_clock_page0.addWidget(label_time_page0)
        layout_clock_page0.addWidget(self.timeedit_time_page0)
        layout_loc_page0.addWidget(label_loc_page0)
        layout_loc_page0.addWidget(self.lineedit_loc_page0)
        layout_name_page0.addWidget(label_users_name_page0)
        layout_name_page0.addWidget(self.lineedit_users_name_page0)
        layout_gender_page0.addWidget(label_users_gender_page0)
        layout_gender_page0.addWidget(groupbox_radio_button)

        layout_time_page0.addLayout(layout_date_page0)
        layout_time_page0.addLayout(layout_clock_page0)
        layout_time_page0.addLayout(layout_loc_page0)
        layout_user_page0.addLayout(layout_name_page0)
        layout_user_page0.addLayout(layout_gender_page0)

        layout_info_page0.addLayout(layout_time_page0)
        layout_info_page0.addWidget(line_split_page0)
        layout_info_page0.addLayout(layout_user_page0)

        layout_note_page0.addWidget(label_note_page0)
        layout_note_page0.addWidget(self.textedit_note_page0)

        layout_tab0_page0_global.addLayout(layout_info_page0)
        layout_tab0_page0_global.addLayout(layout_note_page0)

        self.tab0_page0.setLayout(layout_tab0_page0_global)

    def page0tab1(self):
        """DocString for page0tab1"""
        #@todo: to be defined.
        font = QFont()
        font.setFamily('MonoxLight')
        font.setPointSize(12)

    def page0global(self):
        """DocString for page0global"""
        #@todo: to be defined.
        self.pushbutton_ok_page0 = QPushButton('&Ok')
        self.pushbutton_re_page0 = QPushButton('&Reset')
        self.pushbutton_de_page0 = QPushButton('&Cancel')

        layout_page0_option = QHBoxLayout()
        layout_page0_option.addStretch(1)
        layout_page0_option.addWidget(self.pushbutton_ok_page0)
        layout_page0_option.addWidget(self.pushbutton_re_page0)
        layout_page0_option.addWidget(self.pushbutton_de_page0)

        layout_page0_global = QVBoxLayout(self.page0)
        layout_page0_global.addWidget(self.tabwidget_page0)
        layout_page0_global.addLayout(layout_page0_option)

        self.stack_window.addWidget(self.page0)

        self.page1 = QWidget()

        self.tabwidget_page1 = QTabWidget(self.page1)

        self.tab0_page1 = QWidget(self.page1)
        self.tab1_page1 = QWidget(self.page1)

        self.tabwidget_page1.addTab(self.tab0_page1, 'Info')
        self.tabwidget_page1.addTab(self.tab1_page1, 'Option')

    def page1tab0(self):
        """DocString for page1tab0"""
        #@todo: to be defined.

        font = QFont()
        font.setFamily('MonoxLight')
        font.setPointSize(12)

        label_date_page1 = QLabel('Date      ')
        label_date_page1.setFont(font)

        label_time_page1 = QLabel('Time      ')
        label_time_page1.setFont(font)

        label_loc_page1 = QLabel('Location  ')
        label_loc_page1.setFont(font)

        label_users_name_page1 = QLabel('Name     ')
        label_users_name_page1.setFont(font)

        label_users_gender_page1 = QLabel('Gender')
        label_users_gender_page1.setFont(font)

        label_note_page1 = QLabel('Note')
        label_note_page1.setFont(font)

        line_split_page1 = QFrame()
        line_split_page1.setFrameShape(QFrame.VLine)
        line_split_page1.setFrameShadow(QFrame.Sunken)

        self.dateedit_date_page1 = QDateEdit(QDate.currentDate())
        self.dateedit_date_page1.setDisplayFormat('yyyy/MM/dd')
        self.dateedit_date_page1.setCalendarPopup(True)
        self.dateedit_date_page1.setFont(font)
        self.timeedit_time_page1 = QTimeEdit(QTime.currentTime())
        self.timeedit_time_page1.setDisplayFormat('HH : mm')
        self.timeedit_time_page1.setFont(font)
        self.lineedit_loc_page1 = QLineEdit('Mars')
        self.lineedit_loc_page1.setFont(font)
        self.lineedit_users_name_page1 = QLineEdit('Object')
        self.lineedit_users_name_page1.setFont(font)

        self.radiobutton_users_gender_male_page1 = QRadioButton('Male')
        self.radiobutton_users_gender_female_page1 = QRadioButton('Female')
        self.radiobutton_users_gender_secret_page1 = QRadioButton('Secret')

        self.textedit_note_page1 = QTextEdit('None')
        self.textedit_note_page1.setFont(font)

        groupbox_radio_button = QGroupBox()
        groupbox_radio_button.setStyleSheet(self.style_groupbox)

        layout_groupbox_radio_button = QHBoxLayout()
        layout_groupbox_radio_button.addWidget(
            self.radiobutton_users_gender_male_page1)
        layout_groupbox_radio_button.addWidget(
            self.radiobutton_users_gender_female_page1)
        layout_groupbox_radio_button.addWidget(
            self.radiobutton_users_gender_secret_page1)
        groupbox_radio_button.setLayout(layout_groupbox_radio_button)

        layout_tab0_page1_global = QVBoxLayout(self.tab0_page1)

        layout_info_page1 = QHBoxLayout()
        layout_note_page1 = QVBoxLayout()

        layout_time_page1 = QVBoxLayout()
        layout_user_page1 = QVBoxLayout()

        layout_date_page1 = QHBoxLayout()
        layout_date_page1.setAlignment(Qt.AlignLeft)
        layout_clock_page1 = QHBoxLayout()
        layout_clock_page1.setAlignment(Qt.AlignLeft)
        layout_loc_page1 = QHBoxLayout()
        layout_loc_page1.setAlignment(Qt.AlignLeft)
        layout_name_page1 = QHBoxLayout()
        layout_gender_page1 = QVBoxLayout()

        layout_date_page1.addWidget(label_date_page1)
        layout_date_page1.addWidget(self.dateedit_date_page1)
        layout_clock_page1.addWidget(label_time_page1)
        layout_clock_page1.addWidget(self.timeedit_time_page1)
        layout_loc_page1.addWidget(label_loc_page1)
        layout_loc_page1.addWidget(self.lineedit_loc_page1)
        layout_name_page1.addWidget(label_users_name_page1)
        layout_name_page1.addWidget(self.lineedit_users_name_page1)
        layout_gender_page1.addWidget(label_users_gender_page1)
        layout_gender_page1.addWidget(groupbox_radio_button)

        layout_time_page1.addLayout(layout_date_page1)
        layout_time_page1.addLayout(layout_clock_page1)
        layout_time_page1.addLayout(layout_loc_page1)
        layout_user_page1.addLayout(layout_name_page1)
        layout_user_page1.addLayout(layout_gender_page1)

        layout_info_page1.addLayout(layout_time_page1)
        layout_info_page1.addWidget(line_split_page1)
        layout_info_page1.addLayout(layout_user_page1)

        layout_note_page1.addWidget(label_note_page1)
        layout_note_page1.addWidget(self.textedit_note_page1)

        layout_tab0_page1_global.addLayout(layout_info_page1)
        layout_tab0_page1_global.addLayout(layout_note_page1)

        self.tab0_page1.setLayout(layout_tab0_page1_global)

    def page1tab1(self):
        """DocString for page1tab1"""
        #@todo: to be defined.

        font = QFont()
        font.setFamily('MonoxLight')
        font.setPointSize(12)

        label_filter_or_not = QLabel('Filter')
        label_filter_or_not.setFont(font)

        label_filter_hz1 = QLabel('Hz')
        label_filter_hz1.setFont(font)
        label_filter_hz2 = QLabel('Hz')
        label_filter_hz2.setFont(font)
        label_filter_hz3 = QLabel('Hz')
        label_filter_hz3.setFont(font)
        label_filter_hz4 = QLabel('Hz')
        label_filter_hz4.setFont(font)

        label_sampling_freq = QLabel('Sampling Frequency')
        label_sampling_freq.setFont(font)

        label_notch_filter = QLabel('Notch Filter')
        label_notch_filter.setFont(font)

        label_bandpass_filter = QLabel('Bandpass Filter')
        label_bandpass_filter.setFont(font)

        label_bandpass_filter_to = QLabel('to')
        label_bandpass_filter_to.setFont(font)

        label_set_num = QLabel('Set Number')
        label_set_num.setFont(font)

        label_set_time = QLabel('Set Time')
        label_set_time.setFont(font)

        label_set_interval = QLabel('Auto Restart')
        label_set_interval.setFont(font)

        label_set_interval_s = QLabel('s')
        label_set_interval_s.setFont(font)

        label_tcp_address = QLabel('TCP Address')
        label_tcp_address.setFont(font)

        label_tcp_port = QLabel('TCP Port')
        label_tcp_port.setFont(font)

        label_filetype_save = QLabel('Filetype')
        label_filetype_save.setFont(font)

        label_channel_num = QLabel('Channel Number')
        label_channel_num.setFont(font)

        self.spinbox_set_num = QSpinBox()
        self.spinbox_set_time = QSpinBox()

        self.lineedit_tcp_address = QLineEdit()
        self.lineedit_tcp_address.setFont(font)

        self.lineedit_tcp_port = QLineEdit()
        self.lineedit_tcp_port.setFont(font)

        self.combobox_bandpass_high = QComboBox()
        self.combobox_bandpass_high.addItem('1')
        self.combobox_bandpass_high.addItem('5')
        self.combobox_bandpass_high.addItem('10')
        self.combobox_bandpass_high.addItem('20')

        self.combobox_bandpass_low = QComboBox()
        self.combobox_bandpass_low.addItem('50')
        self.combobox_bandpass_low.addItem('100')
        self.combobox_bandpass_low.addItem('200')
        self.combobox_bandpass_low.addItem('450')

        self.combobox_sampling_freq = QComboBox()
        self.combobox_sampling_freq.addItem('250')
        self.combobox_sampling_freq.addItem('500')
        self.combobox_sampling_freq.addItem('1000')
        self.combobox_sampling_freq.addItem('2000')

        self.combobox_notch_filter = QComboBox()
        self.combobox_notch_filter.addItem('50')
        self.combobox_notch_filter.addItem('60')

        self.combobox_filetype_save = QComboBox()
        self.combobox_filetype_save.addItem('csv')
        self.combobox_filetype_save.addItem('npy')

        self.combobox_channel_num = QComboBox()
        self.combobox_channel_num.addItem('64')
        self.combobox_channel_num.addItem('128')
        self.combobox_channel_num.addItem('192')

        self.checkbox_notch_filter = QCheckBox('Notch Filter')
        self.checkbox_bandpass_filter = QCheckBox('Bandpass Filter')

        self.radiobutton_restart_auto = QRadioButton('Auto Restart')
        self.radiobutton_restart_press = QRadioButton('Manual Restart')

        self.spinbox_restart_auto = QSpinBox()

        groupbox_filter_page1 = QGroupBox('Filter')
        groupbox_filter_page1.setStyleSheet(self.style_groupbox_font)
        groupbox_data_page1 = QGroupBox('Data')
        groupbox_data_page1.setStyleSheet(self.style_groupbox_font)
        groupbox_tcpip_page1 = QGroupBox('TCP/IP')
        groupbox_tcpip_page1.setStyleSheet(self.style_groupbox_font)

        layout_filter_or_not = QHBoxLayout()
        layout_filter_notch = QHBoxLayout()
        layout_filter_bandpass = QHBoxLayout()
        layout_sampling_freq = QHBoxLayout()
        layout_button_filter_reset = QHBoxLayout()

        layout_filter_or_not.addWidget(label_filter_or_not)
        layout_filter_or_not.addWidget(self.checkbox_notch_filter)
        layout_filter_or_not.addWidget(self.checkbox_bandpass_filter)

        layout_filter_notch.addWidget(label_notch_filter)
        layout_filter_notch.addWidget(self.combobox_notch_filter)
        layout_filter_notch.addWidget(label_filter_hz1)

        layout_filter_bandpass.addWidget(label_bandpass_filter)
        layout_filter_bandpass.addWidget(self.combobox_bandpass_high)
        layout_filter_bandpass.addWidget(label_filter_hz2)
        layout_filter_bandpass.addWidget(label_bandpass_filter_to)
        layout_filter_bandpass.addWidget(self.combobox_bandpass_low)
        layout_filter_bandpass.addWidget(label_filter_hz3)

        layout_sampling_freq.addWidget(label_sampling_freq)
        layout_sampling_freq.addWidget(self.combobox_sampling_freq)
        layout_sampling_freq.addWidget(label_filter_hz4)

        layout_data_channel_num = QHBoxLayout()
        layout_data_set = QHBoxLayout()
        layout_data_interval = QVBoxLayout()
        layout_data_filetype = QHBoxLayout()
        layout_button_data_reset = QHBoxLayout()

        layout_data_channel_num.addWidget(label_channel_num)
        layout_data_channel_num.addWidget(self.combobox_channel_num)
        layout_data_interval_auto = QHBoxLayout()
        layout_data_interval_press = QHBoxLayout()
        layout_data_interval_auto.addWidget(self.radiobutton_restart_auto)
        layout_data_interval_auto.addWidget(self.spinbox_restart_auto)
        layout_data_interval_auto.addWidget(label_set_interval_s)
        layout_data_interval_press.addWidget(self.radiobutton_restart_press)

        layout_data_interval.addLayout(layout_data_interval_auto)
        layout_data_interval.addLayout(layout_data_interval_press)

        layout_data_set.addWidget(label_set_num)
        layout_data_set.addWidget(self.spinbox_set_num)
        layout_data_set.addWidget(label_set_time)
        layout_data_set.addWidget(self.spinbox_set_time)

        layout_data_filetype.addWidget(label_filetype_save)
        layout_data_filetype.addWidget(self.combobox_filetype_save)

        layout_filter_page1 = QVBoxLayout()
        layout_filter_page1.addLayout(layout_filter_or_not)
        layout_filter_page1.addLayout(layout_filter_notch)
        layout_filter_page1.addLayout(layout_filter_bandpass)
        layout_filter_page1.addLayout(layout_sampling_freq)

        layout_data_page1 = QVBoxLayout()
        layout_data_page1.addLayout(layout_data_channel_num)
        layout_data_page1.addLayout(layout_data_set)
        layout_data_page1.addLayout(layout_data_interval)
        layout_data_page1.addLayout(layout_data_filetype)

        layout_tcpip_page1_global = QVBoxLayout()
        layout_tcpip_data = QHBoxLayout()
        layout_tcpip_button_reset = QHBoxLayout()

        layout_tcpip_data.addWidget(label_tcp_address)
        layout_tcpip_data.addWidget(self.lineedit_tcp_address)
        layout_tcpip_data.addWidget(label_tcp_port)
        layout_tcpip_data.addWidget(self.lineedit_tcp_port)

        layout_tcpip_page1_global.addLayout(layout_tcpip_data)

        groupbox_filter_page1.setLayout(layout_filter_page1)
        groupbox_data_page1.setLayout(layout_data_page1)
        groupbox_tcpip_page1.setLayout(layout_tcpip_page1_global)

        layout_tab1_page1_up = QHBoxLayout()
        layout_tab1_page1_down = QHBoxLayout()

        layout_tab1_page1_up.addWidget(groupbox_filter_page1)
        layout_tab1_page1_up.addWidget(groupbox_data_page1)
        layout_tab1_page1_down.addWidget(groupbox_tcpip_page1)

        layout_tab1_page1_global = QVBoxLayout()
        layout_tab1_page1_global.addLayout(layout_tab1_page1_up)
        layout_tab1_page1_global.addLayout(layout_tab1_page1_down)

        self.tab1_page1.setLayout(layout_tab1_page1_global)

    def page1global(self):
        """DocString for page1global"""
        #@todo: to be defined.

        self.pushbutton_ok_page1 = QPushButton('&Ok')
        self.pushbutton_de_page1 = QPushButton('&Cancel')
        self.pushbutton_re_page1 = QPushButton('&Reset')

        layout_page1_option = QHBoxLayout()
        layout_page1_option.addStretch(1)
        layout_page1_option.addWidget(self.pushbutton_ok_page1)
        layout_page1_option.addWidget(self.pushbutton_re_page1)
        layout_page1_option.addWidget(self.pushbutton_de_page1)

        layout_page1_global = QVBoxLayout()
        layout_page1_global.addWidget(self.tabwidget_page1)
        layout_page1_global.addLayout(layout_page1_option)

        self.page1.setLayout(layout_page1_global)

        self.stack_window.addWidget(self.page1)
Beispiel #10
0
class _ToolsDock(QWidget):

    __WIDGETS = {}
    __created = False
    __index = 0

    # Signals
    executeFile = pyqtSignal()
    executeProject = pyqtSignal()
    executeSelection = pyqtSignal()
    stopApplication = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        # Register signals connections
        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        self.__buttons = []
        self.__action_number = 1
        self.__buttons_visibility = {}
        self.__current_widget = None
        self.__last_index = -1

        self._stack_widgets = QStackedWidget()
        layout.addWidget(self._stack_widgets)

        # Buttons Widget
        self.buttons_widget = QWidget()
        self.buttons_widget.setObjectName("tools_dock")
        self.buttons_widget.setFixedHeight(26)
        self.buttons_widget.setLayout(QHBoxLayout())
        self.buttons_widget.layout().setContentsMargins(2, 2, 5, 2)
        self.buttons_widget.layout().setSpacing(10)

        IDE.register_service("tools_dock", self)
        _ToolsDock.__created = True

    @classmethod
    def register_widget(cls, display_name, obj):
        """Register a widget providing the service name and the instance"""

        cls.__WIDGETS[cls.__index] = (obj, display_name)
        cls.__index += 1

    def install(self):
        self._load_ui()
        ninjaide = IDE.get_service("ide")
        ninjaide.place_me_on("tools_dock", self, "central")
        ui_tools.install_shortcuts(self, actions.ACTIONS, ninjaide)
        ninjaide.goingDown.connect(self._save_settings)
        ninja_settings = IDE.ninja_settings()
        index = int(ninja_settings.value("tools_dock/widgetVisible", -1))
        if index == -1:
            self.hide()
        else:
            self._show(index)

    def _load_ui(self):
        ninjaide = IDE.get_service("ide")

        shortcut_number = 1

        for index, (obj, name) in _ToolsDock.__WIDGETS.items():
            button = ToolButton(name, index + 1)
            button.setCheckable(True)
            button.clicked.connect(self.on_button_triggered)
            self.__buttons.append(button)
            self.buttons_widget.layout().addWidget(button)
            self.add_widget(name, obj)
            self.__buttons_visibility[button] = True
            # Shortcut action
            ksequence = self._get_shortcut(shortcut_number)
            short = QShortcut(ksequence, ninjaide)
            button.setToolTip(
                ui_tools.tooltip_with_shortcut(button._text, ksequence))
            short.activated.connect(self._shortcut_triggered)
            shortcut_number += 1

        self.buttons_widget.layout().addItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding))

        # Python Selector
        btn_selector = ui_tools.FancyButton("Loading...")
        btn_selector.setIcon(ui_tools.get_icon("python"))
        btn_selector.setCheckable(True)
        btn_selector.setEnabled(False)
        self.buttons_widget.layout().addWidget(btn_selector)

        # QML Interface
        self._python_selector = python_selector.PythonSelector(btn_selector)

        interpreter_srv = IDE.get_service("interpreter")
        interpreter_srv.foundInterpreters.connect(
            self._python_selector.add_model)

        btn_selector.toggled[bool].connect(
            lambda v: self._python_selector.setVisible(v))

        # Popup for show/hide tools widget
        button_toggle_widgets = ToggleButton()
        self.buttons_widget.layout().addWidget(button_toggle_widgets)
        button_toggle_widgets.clicked.connect(self._show_menu)

    def _get_shortcut(self, short_number: int):
        """Return shortcut as ALT + number"""

        if short_number < 1 or short_number > 9:
            return QKeySequence()
        modifier = Qt.ALT if not settings.IS_MAC_OS else Qt.CTRL
        return QKeySequence(modifier + (Qt.Key_0 + short_number))

    def _shortcut_triggered(self):
        short = self.sender()
        widget_index = int(short.key().toString()[-1]) - 1
        widget = self.widget(widget_index)
        if widget.isVisible():
            self._hide()
        else:
            self._show(widget_index)

    def _show_menu(self):
        menu = QMenu()
        for n, (obj, display_name) in _ToolsDock.__WIDGETS.items():
            action = menu.addAction(display_name)
            action.setCheckable(True)
            action.setData(n)

            button = self.__buttons[n]
            visible = self.__buttons_visibility.get(button)
            action.setChecked(visible)

        result = menu.exec_(QCursor.pos())

        if not result:
            return
        index = result.data()
        btn = self.__buttons[index]
        visible = self.__buttons_visibility.get(btn, False)
        self.__buttons_visibility[btn] = not visible
        if visible:
            btn.hide()
        else:
            btn.show()

    def get_widget_index_by_instance(self, instance):
        index = -1
        for i, (obj, _) in self.__WIDGETS.items():
            if instance == obj:
                index = i
                break
        return index

    def execute_file(self):
        run_widget = IDE.get_service("run_widget")
        index = self.get_widget_index_by_instance(run_widget)
        self._show(index)
        self.executeFile.emit()

    def execute_project(self):
        run_widget = IDE.get_service("run_widget")
        index = self.get_widget_index_by_instance(run_widget)
        self._show(index)
        self.executeProject.emit()

    def execute_selection(self):
        run_widget = IDE.get_service("run_widget")
        index = self.get_widget_index_by_instance(run_widget)
        self._show(index)
        self.executeSelection.emit()

    def kill_application(self):
        self.stopApplication.emit()

    def add_widget(self, display_name, obj):
        self._stack_widgets.addWidget(obj)
        func = getattr(obj, "install_widget", None)
        if isinstance(func, collections.Callable):
            func()

    def on_button_triggered(self):
        # Get button index
        button = self.sender()
        index = self.__buttons.index(button)
        if index == self.current_index() and self._is_current_visible():
            self._hide()
        else:
            self._show(index)

    def widget(self, index):
        return self.__WIDGETS[index][0]

    def _hide(self):
        self.__current_widget.setVisible(False)
        index = self.current_index()
        self.__buttons[index].setChecked(False)
        self.widget(index).setVisible(False)
        self.hide()

    def hide_widget(self, obj):
        index = self.get_widget_index_by_instance(obj)
        self.set_current_index(index)
        self._hide()

    def _show(self, index):
        widget = self.widget(index)
        self.__current_widget = widget
        widget.setVisible(True)
        widget.setFocus()
        self.set_current_index(index)
        self.show()

    def set_current_index(self, index):
        if self.__last_index != -1:
            self.__buttons[self.__last_index].setChecked(False)
        self.__buttons[index].setChecked(True)
        if index != -1:
            self._stack_widgets.setCurrentIndex(index)
            widget = self.widget(index)
            widget.setVisible(True)
        self.__last_index = index

    def current_index(self):
        return self._stack_widgets.currentIndex()

    def _is_current_visible(self):
        return self.__current_widget and self.__current_widget.isVisible()

    def _save_settings(self):
        ninja_settings = IDE.ninja_settings()
        visible_widget = self.current_index()
        if not self.isVisible():
            visible_widget = -1
        ninja_settings.setValue("tools_dock/widgetVisible", visible_widget)
Beispiel #11
0
class _ToolsDock(QWidget):

    __WIDGETS = {}
    __created = False
    __index = 0

    # Signals
    executeFile = pyqtSignal()
    executeProject = pyqtSignal()
    executeSelection = pyqtSignal()
    stopApplication = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        # Register signals connections
        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        self.__buttons = []
        self.__action_number = 1
        self.__buttons_visibility = {}
        self.__current_widget = None
        self.__last_index = -1

        self._stack_widgets = QStackedWidget()
        layout.addWidget(self._stack_widgets)

        # Buttons Widget
        self.buttons_widget = QWidget()
        self.buttons_widget.setObjectName("tools_dock")
        self.buttons_widget.setFixedHeight(26)
        self.buttons_widget.setLayout(QHBoxLayout())
        self.buttons_widget.layout().setContentsMargins(2, 2, 5, 2)
        self.buttons_widget.layout().setSpacing(10)

        IDE.register_service("tools_dock", self)
        _ToolsDock.__created = True

    @classmethod
    def register_widget(cls, display_name, obj):
        """Register a widget providing the service name and the instance"""

        cls.__WIDGETS[cls.__index] = (obj, display_name)
        cls.__index += 1

    def install(self):
        self._load_ui()
        ninjaide = IDE.get_service("ide")
        ninjaide.place_me_on("tools_dock", self, "central")
        ui_tools.install_shortcuts(self, actions.ACTIONS, ninjaide)
        ninjaide.goingDown.connect(self._save_settings)
        ninja_settings = IDE.ninja_settings()
        index = int(ninja_settings.value("tools_dock/widgetVisible", -1))
        if index == -1:
            self.hide()
        else:
            self._show(index)

    def _load_ui(self):
        ninjaide = IDE.get_service("ide")

        shortcut_number = 1

        for index, (obj, name) in _ToolsDock.__WIDGETS.items():
            button = ToolButton(name, index + 1)
            button.setCheckable(True)
            button.clicked.connect(self.on_button_triggered)
            self.__buttons.append(button)
            self.buttons_widget.layout().addWidget(button)
            self.add_widget(name, obj)
            self.__buttons_visibility[button] = True
            # Shortcut action
            ksequence = self._get_shortcut(shortcut_number)
            short = QShortcut(ksequence, ninjaide)
            button.setToolTip(
                ui_tools.tooltip_with_shortcut(button._text, ksequence))
            short.activated.connect(self._shortcut_triggered)
            shortcut_number += 1

        self.buttons_widget.layout().addItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding))

        # Python Selector
        btn_selector = ui_tools.FancyButton("Loading...")
        btn_selector.setIcon(ui_tools.get_icon("python"))
        btn_selector.setCheckable(True)
        btn_selector.setEnabled(False)
        self.buttons_widget.layout().addWidget(btn_selector)

        # QML Interface
        self._python_selector = python_selector.PythonSelector(btn_selector)

        interpreter_srv = IDE.get_service("interpreter")
        interpreter_srv.foundInterpreters.connect(
            self._python_selector.add_model)

        btn_selector.toggled[bool].connect(
            lambda v: self._python_selector.setVisible(v))

        # Popup for show/hide tools widget
        button_toggle_widgets = ToggleButton()
        self.buttons_widget.layout().addWidget(button_toggle_widgets)
        button_toggle_widgets.clicked.connect(self._show_menu)

    def _get_shortcut(self, short_number: int):
        """Return shortcut as ALT + number"""

        if short_number < 1 or short_number > 9:
            return QKeySequence()
        modifier = Qt.ALT if not settings.IS_MAC_OS else Qt.CTRL
        return QKeySequence(modifier + (Qt.Key_0 + short_number))

    def _shortcut_triggered(self):
        short = self.sender()
        widget_index = int(short.key().toString()[-1]) - 1
        widget = self.widget(widget_index)
        if widget.isVisible():
            self._hide()
        else:
            self._show(widget_index)

    def _show_menu(self):
        menu = QMenu()
        for n, (obj, display_name) in _ToolsDock.__WIDGETS.items():
            action = menu.addAction(display_name)
            action.setCheckable(True)
            action.setData(n)

            button = self.__buttons[n]
            visible = self.__buttons_visibility.get(button)
            action.setChecked(visible)

        result = menu.exec_(QCursor.pos())

        if not result:
            return
        index = result.data()
        btn = self.__buttons[index]
        visible = self.__buttons_visibility.get(btn, False)
        self.__buttons_visibility[btn] = not visible
        if visible:
            btn.hide()
        else:
            btn.show()

    def get_widget_index_by_instance(self, instance):
        index = -1
        for i, (obj, _) in self.__WIDGETS.items():
            if instance == obj:
                index = i
                break
        return index

    def execute_file(self):
        run_widget = IDE.get_service("run_widget")
        index = self.get_widget_index_by_instance(run_widget)
        self._show(index)
        self.executeFile.emit()

    def execute_project(self):
        run_widget = IDE.get_service("run_widget")
        index = self.get_widget_index_by_instance(run_widget)
        self._show(index)
        self.executeProject.emit()

    def execute_selection(self):
        run_widget = IDE.get_service("run_widget")
        index = self.get_widget_index_by_instance(run_widget)
        self._show(index)
        self.executeSelection.emit()

    def kill_application(self):
        self.stopApplication.emit()

    def add_widget(self, display_name, obj):
        self._stack_widgets.addWidget(obj)
        func = getattr(obj, "install_widget", None)
        if isinstance(func, collections.Callable):
            func()

    def on_button_triggered(self):
        # Get button index
        button = self.sender()
        index = self.__buttons.index(button)
        if index == self.current_index() and self._is_current_visible():
            self._hide()
        else:
            self._show(index)

    def widget(self, index):
        return self.__WIDGETS[index][0]

    def _hide(self):
        self.__current_widget.setVisible(False)
        index = self.current_index()
        self.__buttons[index].setChecked(False)
        self.widget(index).setVisible(False)
        self.hide()

    def hide_widget(self, obj):
        index = self.get_widget_index_by_instance(obj)
        self.set_current_index(index)
        self._hide()

    def _show(self, index):
        widget = self.widget(index)
        self.__current_widget = widget
        widget.setVisible(True)
        widget.setFocus()
        self.set_current_index(index)
        self.show()

    def set_current_index(self, index):
        if self.__last_index != -1:
            self.__buttons[self.__last_index].setChecked(False)
        self.__buttons[index].setChecked(True)
        if index != -1:
            self._stack_widgets.setCurrentIndex(index)
            widget = self.widget(index)
            widget.setVisible(True)
        self.__last_index = index

    def current_index(self):
        return self._stack_widgets.currentIndex()

    def _is_current_visible(self):
        return self.__current_widget and self.__current_widget.isVisible()

    def _save_settings(self):
        ninja_settings = IDE.ninja_settings()
        visible_widget = self.current_index()
        if not self.isVisible():
            visible_widget = -1
        ninja_settings.setValue("tools_dock/widgetVisible", visible_widget)
class UiMainWindow(QWidget):
    """Main UI window of the application.

	Attributes:
	----------
	window_width: int
		Width of the window
	window_height: int
		Height of the window
	button_width: int
		Width of buttons
	button_height: int
		Height of buttons
	dist: int
		Distance to the edge of Widgets(Window/Button/Label...)
	model_selected: bool
		Shows whether a model is selected or not
	"""
    window_height = 650
    window_width = 800
    button_width = 180
    button_height = 50
    dist = 30
    model_selected = False
    textbox_height = 25
    small_button_width = 100
    small_button_height = 30
    debug_height = 200
    debug_mode = False
    accepted_download = False
    current_city = ""

    def __init__(self, parent) -> None:
        super().__init__(parent)

        main_window.setObjectName("main_window")
        main_window.resize(self.window_width, self.window_height)
        self.centralwidget = QWidget(main_window)
        self.centralwidget.setObjectName("centralwidget")
        self.detector = Detection()

        self.Box_Stadt = QComboBox(self.centralwidget)
        self.Box_Stadt.setGeometry(
            QRect(self.dist, self.dist, self.button_width, self.button_height))
        self.Box_Stadt.setObjectName("Box_Stadt")
        self.Box_Stadt.activated.connect(self.on_dropdown_selected)
        # dynamic city updates
        supported_cities_updater = Thread(target=update_dropdown,
                                          daemon=True,
                                          args=(self.Box_Stadt, ))
        supported_cities_updater.start()

        self.Text_City = QLineEdit(self.centralwidget)
        self.Text_City.setGeometry(
            QRect(self.dist + self.dist + self.button_width, self.dist + 10,
                  self.button_width, self.textbox_height))
        self.Text_City.setObjectName("Text_City")
        self.Text_City.setToolTip(
            'Enter a city you wish to detect sights in that you cannot find in the dropdown on the left after updating.'
        )

        self.Button_City = QPushButton(self.centralwidget)
        self.Button_City.setGeometry(
            QRect(
                int(2.3 * self.dist) + self.button_width + self.button_width,
                self.dist + 8, self.small_button_width,
                self.small_button_height))
        self.Button_City.setObjectName("Button_City")
        self.Button_City.clicked.connect(self.request_city)

        self.Button_Detection = QPushButton(self.centralwidget)
        self.Button_Detection.setGeometry(
            QRect(
                self.window_width - (self.dist + self.button_width),
                self.window_height - (self.dist + self.button_height + 20),
                self.button_width,
                self.button_height,
            ))
        self.Button_Detection.setObjectName("Button_Detection")
        self.Button_Detection.clicked.connect(self.detect_sights)

        self.Button_Bild = QPushButton(self.centralwidget)
        self.Button_Bild.setGeometry(
            QRect(
                self.dist,
                self.window_height - (self.dist + self.button_height + 20),
                self.button_width,
                self.button_height,
            ))
        self.Button_Bild.setObjectName("Button_Bild")
        self.Button_Bild.clicked.connect(lambda: self.camera_viewfinder.hide())
        self.Button_Bild.clicked.connect(
            lambda: self.Box_Camera_selector.setCurrentIndex(0))
        self.Button_Bild.clicked.connect(
            lambda: self.stacked_widget.setCurrentIndex(0))
        self.Button_Bild.clicked.connect(lambda: self.Label_Bild.show())
        self.Button_Bild.clicked.connect(self.dragdrop)

        self.available_cameras = QCameraInfo.availableCameras()

        self.Box_Camera_selector = QComboBox(self.centralwidget)
        self.Box_Camera_selector.setGeometry(
            QRect(
                self.window_width - (self.dist + self.button_width),
                self.dist,
                self.button_width,
                self.button_height,
            ))
        self.Box_Camera_selector.setObjectName("Box_Camera_selector")
        self.Box_Camera_selector.addItem("")
        # self.Box_Camera_selector.addItems([camera.description() for camera in self.available_cameras])
        self.Box_Camera_selector.addItems([
            "Camera " + str(i) + ": " +
            str(self.available_cameras[i].description())
            for i in range(len(self.available_cameras))
        ])
        self.Box_Camera_selector.currentIndexChanged.connect(
            self.select_camera)

        self.stacked_widget = QStackedWidget(self.centralwidget)
        label_height = (self.window_height - self.dist - self.button_height -
                        self.dist) - (self.dist + self.button_height +
                                      self.dist)
        label_start_y = self.dist + self.button_height + self.dist
        self.stacked_widget.setGeometry(
            QRect(
                self.dist,
                label_start_y,
                self.window_width - (self.dist * 2),
                label_height,
            ))

        self.camera_viewfinder = QCameraViewfinder()

        self.Label_Bild = ImageLabel(self)
        self.Label_Bild.setGeometry(
            QRect(0, 0, self.window_width - (self.dist * 2), label_height))

        self.checkBoxImprove = QCheckBox(
            "Help improving SightScan's detection quality", self.centralwidget)
        self.checkBoxImprove.setObjectName(u"improvement")
        self.checkBoxImprove.setGeometry(QRect(self.dist, 5, 350, 20))
        self.checkBoxImprove.setChecked(False)
        self.checkBoxImprove.stateChanged.connect(self.set_improve_quality_var)

        self.checkBox = QCheckBox("Debug", self.centralwidget)
        self.checkBox.setObjectName(u"checkBox")
        self.checkBox.setGeometry(
            QRect(self.window_width - (self.dist + 50),
                  self.window_height - (self.dist + 20), 70, 20))
        self.checkBox.setChecked(False)
        self.checkBox.stateChanged.connect(self.debug_click)

        # Setup logging
        fn = "logs/" + datetime.now().strftime(
            '%d_%m_%Y__%H_%M_%S') + 'log.log'
        if not os.path.exists("logs"):
            os.mkdir("logs")
        f = '%(asctime)s :: %(levelname)s :: %(filename)s :: %(funcName)s :: %(lineno)d :: %(message)s'
        self.textDebug = QTextEditLogger(self.centralwidget)
        self.textDebug.setFormatter(logging.Formatter(f))
        logging.basicConfig(filename=fn, format=f, level=logging.DEBUG)
        logging.getLogger().addHandler(self.textDebug)

        # Log Text Box in GUI
        self.textDebug.widget.setObjectName(u"textEdit")
        self.textDebug.widget.setEnabled(False)
        self.textDebug.widget.setGeometry(
            QRect(self.dist, self.window_height,
                  self.window_width - 2 * self.dist,
                  self.debug_height - self.dist))
        size_policy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
        size_policy.setHorizontalStretch(0)
        size_policy.setVerticalStretch(0)
        size_policy.setHeightForWidth(
            self.textDebug.widget.sizePolicy().hasHeightForWidth())
        self.textDebug.widget.setSizePolicy(size_policy)
        self.textDebug.widget.setReadOnly(True)

        self.stacked_widget.addWidget(self.Label_Bild)
        self.stacked_widget.addWidget(self.camera_viewfinder)

        main_window.setCentralWidget(self.centralwidget)
        self.menubar = QMenuBar(main_window)
        self.menubar.setGeometry(QRect(0, 0, 678, 21))
        self.menubar.setObjectName("menubar")
        main_window.setMenuBar(self.menubar)

        self.statusbar = QStatusBar(main_window)
        self.statusbar.setObjectName("statusbar")
        main_window.setStatusBar(self.statusbar)

        main_window.setWindowIcon(QIcon(logo_without_text))

        self.retranslateUi(main_window)
        QMetaObject.connectSlotsByName(main_window)

    def set_improve_quality_var(self):
        self.improve_checkbox_enabled = self.checkBoxImprove.isChecked()

    def retranslateUi(self, main_window: QMainWindow) -> None:
        """Set the text initially for all items.

		Parameters
		----------
		main_window: QMainWindow
		    The instance of the prepared application window
		"""
        _translate = QCoreApplication.translate
        main_window.setWindowTitle(_translate(WINDOW, "SightScan"))
        self.Box_Stadt.addItems(['Choose City'] + initialize_cities())
        self.Box_Camera_selector.setItemText(
            0, _translate(WINDOW, "Choose Webcam"))
        self.Button_Detection.setText(_translate(WINDOW, START))
        self.Button_Bild.setText(_translate(WINDOW, ENABLE))
        self.Button_City.setText(_translate(WINDOW, "Add City"))

    def on_dropdown_selected(self) -> None:
        """Shows a pop-up for confirming the download of the selected city."""
        city_pretty_print = self.Box_Stadt.currentText()
        city = self.Box_Stadt.currentText().replace(' ', '_').upper()

        if city != "CHOOSE_CITY":
            self.current_city = self.Box_Stadt.currentText()
            # if no connection to dos
            if get_supported_cities() == []:
                latest_version = "couldn't get the latest version"
                downloaded_version = "couldn't get the downloaded version"
                print('no connection to dos')

            # if connection to dos
            else:
                downloaded_version = -1  # initialization

                Path("weights").mkdir(mode=0o700, exist_ok=True)

                if not os.path.exists("weights/versions.txt"):
                    with open('weights/versions.txt',
                              'w'):  # creating a version file
                        pass

                with open("weights/versions.txt", "r") as file:
                    for line in file:
                        elements = line.split("=")
                        if elements[0].upper() == city:
                            downloaded_version = int(elements[1])
                            break

                latest_version = get_dwh_model_version(city)

                if downloaded_version == -1:
                    msg = QMessageBox()
                    msg.setWindowTitle("Download City")
                    msg.setWindowIcon(QIcon(logo_without_text))
                    msg.setText("Do you want to download " +
                                city_pretty_print + "?")
                    msg.setIcon(QMessageBox.Question)
                    msg.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
                    msg.setDefaultButton(QMessageBox.Ok)
                    msg.setInformativeText("When downloaded, sights of " +
                                           city_pretty_print +
                                           " can be detected.")
                    msg.buttonClicked.connect(self.handover_city)

                    msg.exec_()

                elif latest_version > downloaded_version:
                    update_msg = QMessageBox()
                    update_msg.setWindowTitle("Update available")
                    update_msg.setWindowIcon(QIcon(logo_without_text))
                    update_msg.setText(
                        "Do you want to download an update for " + city + "?")
                    update_msg.setIcon(QMessageBox.Question)
                    update_msg.setStandardButtons(QMessageBox.Cancel
                                                  | QMessageBox.Ok)
                    update_msg.setDefaultButton(QMessageBox.Ok)
                    update_msg.setInformativeText(
                        "Updated cities can detect sights faster and more accurately. If you choose not to download, the "
                        + "detection will still work.")
                    update_msg.buttonClicked.connect(self.handover_city)

                    update_msg.exec_()
            if self.accepted_download is True or latest_version == downloaded_version:
                self.accepted_download = False
                self.show_download_result()
            self.model_selected = True
        else:
            self.model_selected = False

    def handover_city(self, button) -> None:
        """Starts the download of the pre-trained model of the selected city.

		Parameters
		----------
		button:
			Pushed button inside the popup.
		"""

        if "OK" in button.text().upper():
            city = self.Box_Stadt.currentText().replace(' ', '_').upper()
            self.model_selected = True
            model = get_downloaded_model(city)
            if model is not None:
                with open("weights/" + city + ".pt", "wb+") as file:
                    file.write(model)
            self.accepted_download = True
        elif "CANCEL" in button.text().upper():
            self.Box_Stadt.setCurrentIndex(0)

    def detect_sights(self) -> None:
        """Starts detection for the dropped image or shown webcam video
		with the downloaded model and displays the results in the label."""
        city = self.Box_Stadt.currentText().replace(' ', '_').upper()
        print("Detection Status: " + str(self.detector.detection))

        if self.model_selected is False:
            self.show_missing_model_popup()
        else:
            # start drag&drop image detection
            if self.stacked_widget.currentIndex() == 0 and self.Button_Bild.text() == DISABLE and \
              self.Label_Bild.image != logo_with_text:
                print(f"Starting detection of {self.Label_Bild.image}")
                wipe_prediction_input_images(INPUT_PREDICTION_DIR)
                shutil.copy2(self.Label_Bild.image, INPUT_PREDICTION_DIR)
                self.detector.enable_detection()
                self.detector.detect(self,
                                     weights='weights/' + city + '.pt',
                                     debug=self.debug_mode)
            # stop video detection
            elif self.stacked_widget.currentIndex(
            ) == 0 and self.Button_Detection.text() == STOP:
                self.stop_video_detection()
                time.sleep(2)
                self.reactivate_cam()
            # if webcam activated
            elif self.stacked_widget.currentIndex() == 1:
                if self.Button_Detection.text() == START:
                    self.Button_Detection.setText(
                        QCoreApplication.translate(WINDOW, STOP))
                    self.Label_Bild.setStyleSheet("""
					""")
                    print("Video Detection Started")
                    self.prep_video_detection()
                    source = self.Box_Camera_selector.currentIndex()
                    self.detector.enable_detection()
                    self.detection_thread = Thread(target=self.detector.detect,
                                                   args=(self, ),
                                                   kwargs={
                                                       'weights':
                                                       'weights/' + city +
                                                       '.pt',
                                                       'source':
                                                       str(source - 1),
                                                       'image_size':
                                                       704,
                                                       'debug':
                                                       self.debug_mode
                                                   })
                    self.detection_thread.start()
            else:
                print("Drop a File or select a Webcam!")

    def show_missing_model_popup(self) -> None:
        # Show Pop Up to choose a city
        emsg = QMessageBox()
        emsg.setWindowTitle("No city chosen")
        emsg.setWindowIcon(QIcon(logo_without_text))
        emsg.setText(
            "You need to choose a city before the detection can start.")
        emsg.setIcon(QMessageBox.Warning)
        emsg.setStandardButtons(QMessageBox.Ok)
        emsg.setDefaultButton(QMessageBox.Ok)

        emsg.exec_()

    def show_download_result(self) -> None:
        # city_pretty_print = self.Box_Stadt.currentText()

        self.model_selected = True
        newest_vers_msg = QMessageBox()
        newest_vers_msg.setWindowTitle("Ready for Detection!")
        newest_vers_msg.setWindowIcon(QIcon(logo_without_text))
        newest_vers_msg.setText("You can start detecting sights in " +
                                self.current_city + "!")
        newest_vers_msg.setStandardButtons(QMessageBox.Ok)
        newest_vers_msg.setDefaultButton(QMessageBox.Ok)

        newest_vers_msg.exec_()

    def request_city(self) -> None:
        # Send entered city to dwh and show confirmation popup if the city name is known
        city_input = self.Text_City.text()
        city_request = city_input.upper()
        if len(filter_city(city_input)) == 1:
            send_city_request(city_request)

            cmsg = QMessageBox()
            cmsg.setWindowTitle("Request confirmed")
            cmsg.setWindowIcon(QIcon(logo_without_text))
            cmsg.setText("Your request to add support for " + city_input +
                         " has been sent to our backend.")
            cmsg.setStandardButtons(QMessageBox.Ok)
            cmsg.setDefaultButton(QMessageBox.Ok)
            cmsg.exec_()
        else:
            cmsg = QMessageBox()
            cmsg.setWindowTitle("Unknown city name")
            cmsg.setWindowIcon(QIcon(logo_without_text))
            cmsg.setText(
                "The typed city name is not known. Please check the spelling.")
            cmsg.setIcon(QMessageBox.Warning)
            cmsg.setStandardButtons(QMessageBox.Ok)
            cmsg.setDefaultButton(QMessageBox.Ok)
            cmsg.exec_()

    def dragdrop(self) -> None:
        """Enables / disables Drag&Drop of images."""
        if self.Button_Bild.text() == ENABLE:
            # stop video detection if active
            if self.Button_Detection.text() == STOP:
                self.Button_Detection.setText(
                    QCoreApplication.translate(WINDOW, START))
                self.detector.disable_detection()
            self.Label_Bild.setAcceptDrops(True)
            self.Label_Bild.setText("\n\n Drop Image here \n\n")
            self.Label_Bild.setStyleSheet("""
				QLabel{
					border: 4px dashed #aaa
				}
			""")
            self.Button_Bild.setText(
                QCoreApplication.translate(WINDOW, DISABLE))
        elif self.Button_Bild.text() == DISABLE:
            self.Label_Bild.setAcceptDrops(False)
            self.Label_Bild.setText("")
            self.Label_Bild.setStyleSheet("")
            self.Label_Bild.image = logo_with_text
            self.Label_Bild.setPixmap(QPixmap(self.Label_Bild.image))
            self.Button_Bild.setText(QCoreApplication.translate(
                WINDOW, ENABLE))

    def select_camera(self, i):
        """Starts the selected camera. If "Choose webcam" is selected, it stops the camera.

		Parameters
		----------
		i:
			Index of the chosen camera.
		"""
        self.Label_Bild.image = logo_with_text
        self.Label_Bild.setPixmap(QPixmap(self.Label_Bild.image))
        if i == 0:
            self.camera.stop()
            self.detector.disable_detection()
            self.Button_Detection.setText(
                QCoreApplication.translate(WINDOW, START))
            self.stacked_widget.setCurrentIndex(0)
            self.camera_viewfinder.hide()
            self.Label_Bild.show()
            time.sleep(2)
            self.Label_Bild.image = logo_with_text
            self.Label_Bild.setPixmap(QPixmap(self.Label_Bild.image))
            self.Label_Bild.setStyleSheet("""
			""")
        else:
            self.camera_viewfinder.show()
            self.stacked_widget.setCurrentIndex(1)
            self.Label_Bild.hide()
            self.camera = QCamera(self.available_cameras[i - 1])
            self.camera.setViewfinder(self.camera_viewfinder)
            self.camera.error.connect(
                lambda: self.alert(self.camera.errorString()))
            self.camera.start()
            self.Button_Bild.setText(QCoreApplication.translate(
                WINDOW, ENABLE))

    def prep_video_detection(self) -> None:
        self.camera.stop()
        self.camera_viewfinder.hide()
        self.stacked_widget.setCurrentIndex(0)
        self.Label_Bild.image = loading_image
        self.Label_Bild.setPixmap(QPixmap(self.Label_Bild.image))
        self.Label_Bild.show()

    def stop_video_detection(self) -> None:
        self.Button_Detection.setText(QCoreApplication.translate(
            WINDOW, START))
        self.detector.disable_detection()
        self.stacked_widget.setCurrentIndex(1)
        self.Label_Bild.hide()
        self.camera_viewfinder.show()

    def debug_click(self, state):
        self.debug_mode = bool(state)

        if state:
            main_window.resize(self.window_width,
                               self.window_height + self.debug_height)
            self.textDebug.widget.setEnabled(True)
        else:
            main_window.resize(self.window_width, self.window_height)
            self.textDebug.widget.setEnabled(False)

    def reactivate_cam(self) -> None:
        self.Label_Bild.image = logo_with_text
        self.Label_Bild.setPixmap(QPixmap(self.Label_Bild.image))
        self.camera.start()

    def close_all(self) -> None:
        if self.Button_Detection.text() == STOP:
            self.detector.disable_detection()
            self.stop_video_detection()
Beispiel #13
0
class ComprehensionTestApp(QMainWindow):
    """
	@modifies self.symbol_test, self.question1, self.question2
	@effects makes a new ComprehensionTestApp
	"""
    def __init__(self):
        super().__init__()
        self.symbol_tests = QStackedWidget()
        self.question1 = "Exactly what do you think this symbol means?"
        self.question2 = "What action would you take in response to this symbol?"
        self.init_gui()

    """
	@modifies self
	@effects sets up the main window
	"""

    def init_gui(self):
        exitAct = QAction('&Exit', self)
        exitAct.setShortcut('Ctrl+Q')
        exitAct.setStatusTip('Exit application')
        exitAct.triggered.connect(self.quit)

        saveAct = QAction('&Save as PDF', self)
        saveAct.setShortcut('Ctrl+S')
        saveAct.setStatusTip('Saves as PDF')
        saveAct.triggered.connect(self.save_as_pdf)

        importAct = QAction('&Import symbols', self)
        importAct.setShortcut('Ctrl+I')
        importAct.setStatusTip(
            'Imports all the specified images on to their own test page')
        importAct.triggered.connect(self.import_symbols)

        menu_bar = self.menuBar()
        filemenu = menu_bar.addMenu('&File')
        filemenu.addAction(importAct)
        filemenu.addAction(saveAct)
        filemenu.addAction(exitAct)

        goToAct = QAction('&Go to page', self)
        goToAct.setStatusTip('Go to the specified page')
        goToAct.setShortcut('Ctrl+G')
        goToAct.triggered.connect(self.go_to_page)

        newQuestAct = QAction('&Change default questions', self)
        newQuestAct.setStatusTip(
            'Changes the default questions asked about the symbol')
        newQuestAct.setShortcut('Ctrl+H')
        newQuestAct.triggered.connect(self.change_questions)

        moveLeftAct = QAction('&Move current page left', self)
        moveLeftAct.setShortcut('Ctrl+,')
        moveLeftAct.setStatusTip('Moves the current page one page to the left')
        moveLeftAct.triggered.connect(self.move_test_left)

        moveRightAct = QAction('&Move current page right', self)
        moveRightAct.setShortcut('Ctrl+.')
        moveRightAct.setStatusTip(
            'Moves the current page one page to the right')
        moveRightAct.triggered.connect(self.move_test_right)

        moveToAct = QAction('Move current page to', self)
        moveToAct.setShortcut('Ctrl+M')
        moveToAct.setStatusTip('Moves the current page to the specified page')
        moveToAct.triggered.connect(self.move_to_page)

        delPagesAct = QAction('Delete pages', self)
        delPagesAct.setShortcut('Ctrl+D')
        delPagesAct.setStatusTip('Deletes the specified pages')
        delPagesAct.triggered.connect(self.delete_pages)

        editmenu = menu_bar.addMenu('&Edit')
        editmenu.addAction(delPagesAct)
        editmenu.addAction(newQuestAct)
        editmenu.addAction(goToAct)
        editmenu.addAction(moveLeftAct)
        editmenu.addAction(moveRightAct)
        editmenu.addAction(moveToAct)

        nextAct = QAction(QIcon("next.png"), 'Next page (Ctrl+P)', self)
        nextAct.setShortcut('Ctrl+P')
        nextAct.setStatusTip('Goes to the next page')
        nextAct.triggered.connect(self.next_page)

        prevAct = QAction(QIcon("prev.png"), "Previous page (Ctrl+O)", self)
        prevAct.setShortcut('Ctrl+O')
        prevAct.setStatusTip('Goes to the previous page')
        prevAct.triggered.connect(self.prev_page)

        newAct = QAction(QIcon("add.png"), "Add a new page (Ctrl+N)", self)
        newAct.setShortcut('Ctrl+N')
        newAct.setStatusTip('Creates a new page')
        newAct.triggered.connect(self.add_blank_test)

        remAct = QAction(QIcon("remove.png"), "Delete page (Ctrl+R)", self)
        remAct.setShortcut('Ctrl+R')
        remAct.setStatusTip('Deletes the current page')
        remAct.triggered.connect(self.delete_test)

        tool_bar = self.addToolBar("Next Page")
        tool_bar.addAction(prevAct)
        tool_bar.addAction(nextAct)
        tool_bar.addAction(newAct)
        tool_bar.addAction(remAct)

        self.setWindowTitle("Open Comprehension Test Generator")
        self.setWindowIcon(QIcon("rubber ducky.png"))
        self.setGeometry(500, 500, 500, 450)
        self.add_blank_test()
        self.setCentralWidget(self.symbol_tests)

        self.show()

    """
	@modifies none
	@effects  confirms if the user wishes to exit
	"""

    def closeEvent(self, event):
        msg = QMessageBox.question(self, 'Message', "Really quit?",
                                   QMessageBox.Yes | QMessageBox.No,
                                   QMessageBox.No)
        if msg == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

    """
	@modifies none
	@effects  confirms if the user wishes to exit
	"""

    def quit(self):
        msg = QMessageBox.question(self, 'Message', "Really quit?",
                                   QMessageBox.Yes | QMessageBox.No,
                                   QMessageBox.No)
        if msg == QMessageBox.Yes:
            qApp.quit()

    """
	@modifies self.symbol_tests
	@effects  adds a blank symbol_test to the end of symbol_tests
			  sets the current symbol_test to the new one
	"""

    def add_blank_test(self):
        page = self.symbol_tests.count() + 1
        test = SymbolTestWidget(
            self,
            comp_questions=[self.question1, self.question2],
            pageNumber=page,
            pageTotal=page)
        test.set_visual_context("blank image.png")
        self.symbol_tests.addWidget(test)
        self.symbol_tests.setCurrentIndex(page - 1)
        for i in range(0, self.symbol_tests.count() - 1):
            self.symbol_tests.widget(i).set_numPages(self.symbol_tests.count())

    """
	@modifies self.symbol_tests
	@effects  changes the current symbol_test to the next one
	"""

    def next_page(self):
        if self.symbol_tests.currentIndex() < self.symbol_tests.count() - 1:
            self.symbol_tests.setCurrentIndex(
                self.symbol_tests.currentIndex() + 1)

    """
	@modifies self.symbol_test
	@effects  changes the current symbol_test to the previous one
	"""

    def prev_page(self):
        if self.symbol_tests.currentIndex() > 0:
            self.symbol_tests.setCurrentIndex(
                self.symbol_tests.currentIndex() - 1)

    """
	@modifies none
	@effects  saves all the symbol_tests as seperate pages on a PDF document
	"""

    def save_as_pdf(self):
        filename = QFileDialog.getSaveFileName(self, 'Save to PDF', 'c:\\',
                                               "*.pdf")

        if filename != ('', ''):
            if os.path.exists(filename[0]):
                try:
                    infile = PdfFileReader(filename[0], 'rb')
                except:
                    error = QMessageBox()
                    error.setIcon(QMessageBox.Warning)
                    error.setStandardButtons(QMessageBox.Ok)
                    error.setText(
                        "File could not be written to. If the file is currently open, try closing it"
                    )
                    error.exec_()
                    return
                if infile.getNumPages() == 0:
                    print("HERE!")
                    doc = QTextDocument()
                    doc.print(printer)

            printer = QPrinter()
            printer.setPageSize(QPrinter.A4)
            printer.setOutputFormat(QPrinter.PdfFormat)
            printer.setOutputFileName(filename[0])

            painter = QPainter()

            font = QFont("times")
            font.setPointSize(12)

            x = painter.begin(printer)
            if x == False:
                error = QMessageBox()
                error.setIcon(QMessageBox.Warning)
                error.setStandardButtons(QMessageBox.Ok)
                error.setText(
                    "File could not be saved. If the file is currently open, try closing it"
                )
                error.exec_()
                return
            painter.setFont(font)

            for i in range(0, self.symbol_tests.count()):
                cur_symbol_test = self.symbol_tests.widget(i)

                if cur_symbol_test.print_visual_context() == False:
                    pixmap = cur_symbol_test.get_symbol()
                    pixmap = pixmap.scaled(350, 350)

                    painter.drawPixmap(30, 100, pixmap)
                    painter.drawText(750, 20, cur_symbol_test.get_page())
                    painter.drawText(
                        420, 200, 350, 400, QtCore.Qt.TextWordWrap,
                        "Context: " + cur_symbol_test.get_context())
                    painter.drawText(30, 600, cur_symbol_test.get_question1())
                    painter.drawText(30, 830, cur_symbol_test.get_question2())

                    cur_pen = painter.pen()
                    line_pen = QPen()
                    line_pen.setWidth(2)
                    painter.setPen(line_pen)
                    painter.drawLine(70, 656, 600, 656)
                    painter.drawLine(70, 712, 600, 712)
                    painter.drawLine(70, 768, 600, 768)

                    painter.drawLine(70, 886, 600, 886)
                    painter.drawLine(70, 942, 600, 942)
                    painter.drawLine(70, 998, 600, 998)
                    painter.setPen(cur_pen)

                else:
                    pixmap = cur_symbol_test.get_visual_context()
                    pixmap = pixmap.scaled(300, 300)

                    painter.drawPixmap(200, 10, pixmap)

                    pixmap = cur_symbol_test.get_symbol()
                    pixmap = pixmap.scaled(250, 250)

                    painter.drawPixmap(225, 320, pixmap)

                    painter.drawText(750, 20, cur_symbol_test.get_page())
                    #painter.drawText(420, 200, 350, 400, QtCore.Qt.TextWordWrap, "Context: " + cur_symbol_test.get_context())
                    painter.drawText(30, 600, cur_symbol_test.get_question1())
                    painter.drawText(30, 830, cur_symbol_test.get_question2())

                    cur_pen = painter.pen()
                    line_pen = QPen()
                    line_pen.setWidth(2)
                    painter.setPen(line_pen)
                    painter.drawLine(70, 656, 600, 656)
                    painter.drawLine(70, 712, 600, 712)
                    painter.drawLine(70, 768, 600, 768)

                    painter.drawLine(70, 886, 600, 886)
                    painter.drawLine(70, 942, 600, 942)
                    painter.drawLine(70, 998, 600, 998)
                    painter.setPen(cur_pen)
                if (i < self.symbol_tests.count() - 1):
                    printer.newPage()

            painter.end()

    """
	@modifies self.symbol_tests
	@effects  adds all the specified symbols to their own symbol_test and adds those
			  tests to self.symbol_tests
	"""

    def import_symbols(self):
        fnames = QFileDialog.getOpenFileNames(self, 'Open file', 'c:\\',
                                              "Image files (*.jpg *.png)")
        if fnames != ([], ''):
            for i in range(0, len(fnames[0])):
                page = self.symbol_tests.count() + 1
                test = SymbolTestWidget(
                    self,
                    comp_questions=[self.question1, self.question2],
                    pageNumber=page,
                    pageTotal=page)
                test.set_symbol(fnames[0][i])
                test.set_visual_context("blank image.png")
                self.symbol_tests.addWidget(test)
            self.symbol_tests.setCurrentIndex(page - 1)

            for i in range(0, self.symbol_tests.count()):
                self.symbol_tests.widget(i).set_numPages(
                    self.symbol_tests.count())

    """
	@modifies self.symbol_tests
	@effects  removes the current test from self.symbol_tests
	"""

    def delete_test(self):
        msg = QMessageBox.question(self, 'Message', "Delete test?",
                                   QMessageBox.Yes | QMessageBox.No,
                                   QMessageBox.No)
        if msg == QMessageBox.No:
            return

        if self.symbol_tests.currentIndex() != 0:
            cur_idx = self.symbol_tests.currentIndex()
            self.symbol_tests.setCurrentIndex(cur_idx - 1)
            self.symbol_tests.removeWidget(self.symbol_tests.widget(cur_idx))
            for i in range(0, self.symbol_tests.count()):
                cur_symbol_test = self.symbol_tests.widget(i)
                cur_symbol_test.set_numPages(self.symbol_tests.count())

                if i >= cur_idx:
                    cur_symbol_test.set_page(i + 1)

        elif self.symbol_tests.currentIndex(
        ) == 0 and self.symbol_tests.count() > 1:
            self.symbol_tests.setCurrentIndex(1)
            self.symbol_tests.removeWidget(self.symbol_tests.widget(0))
            for i in range(0, self.symbol_tests.count()):
                cur_symbol_test = self.symbol_tests.widget(i)
                cur_symbol_test.set_numPages(self.symbol_tests.count())
                cur_symbol_test.set_page(i + 1)

        elif self.symbol_tests.currentIndex() == 0 and self.symbol_tests.count(
        ) == 1:
            page = 1
            test = SymbolTestWidget(
                self,
                comp_questions=[self.question1, self.question2],
                pageNumber=page,
                pageTotal=page)
            self.symbol_tests.addWidget(test)
            self.symbol_tests.setCurrentIndex(1)
            self.symbol_tests.removeWidget(self.symbol_tests.widget(0))

    """
	@modifies self.symbol_tests
	@effects  decreases the current symbol_test's index in self.symbol_tests by 1,
			  moves it down 1 postion
	"""

    def move_test_left(self):
        cur_idx = self.symbol_tests.currentIndex()
        if cur_idx > 0:
            cur_widget = self.symbol_tests.widget(cur_idx)
            prev_widget = self.symbol_tests.widget(cur_idx - 1)
            cur_widget.set_page(int(cur_widget.get_page()) - 1)
            prev_widget.set_page(int(prev_widget.get_page()) + 1)

            self.symbol_tests.removeWidget(prev_widget)
            self.symbol_tests.insertWidget(
                self.symbol_tests.indexOf(cur_widget) + 1, prev_widget)

    """
	@modifies self.symbol_tests
	@effects  increases the current symbol_test's index in self.symbol_tests by 1,
			  moves it up 1 postion
	"""

    def move_test_right(self):
        cur_idx = self.symbol_tests.currentIndex()
        if cur_idx < self.symbol_tests.count() - 1:
            cur_widget = self.symbol_tests.widget(cur_idx)
            next_widget = self.symbol_tests.widget(cur_idx + 1)
            cur_widget.set_page(int(cur_widget.get_page()) + 1)
            next_widget.set_page(int(next_widget.get_page()) - 1)

            self.symbol_tests.removeWidget(next_widget)
            self.symbol_tests.insertWidget(
                self.symbol_tests.indexOf(cur_widget), next_widget)

    """
	@modifies self.symbol_tests
	@effects  moves the current symbol_test to a specified position in self.symbol_tests
	"""

    def move_to_page(self):
        text = QInputDialog.getText(self, "Move Page",
                                    "Move current page to: ", QLineEdit.Normal)
        if text[1] != False:
            try:
                x = int(text[0])
                if x <= 0 or x > self.symbol_tests.count():
                    error = QMessageBox()
                    error.setIcon(QMessageBox.Warning)
                    error.setStandardButtons(QMessageBox.Ok)
                    error.setText("Page must be between 1 and " +
                                  str(self.symbol_tests.count()))
                    error.exec_()
                    return

                cur_idx = self.symbol_tests.currentIndex()
                cur_widget = self.symbol_tests.widget(cur_idx)
                cur_widget.set_page(x - 1)

                self.symbol_tests.removeWidget(cur_widget)
                self.symbol_tests.insertWidget(x - 1, cur_widget)
                self.symbol_tests.setCurrentIndex(x - 1)

                for i in range(0, self.symbol_tests.count()):
                    loop_widget = self.symbol_tests.widget(i)
                    loop_widget.set_page(i + 1)

            except ValueError:
                error = QMessageBox()
                error.setIcon(QMessageBox.Warning)
                error.setStandardButtons(QMessageBox.Ok)
                error.setText("Can only enter an integer between 1 and " +
                              str(self.symbol_tests.count()))
                error.exec_()

    """
	@modifies self.symbol_tests
	@effects  sets the surrent symbol_test equal to the one at the specified page
	"""

    def go_to_page(self):
        text = QInputDialog.getText(self, "Go to", "Go to page: ",
                                    QLineEdit.Normal)
        if text[1] != False:
            try:
                x = int(text[0])
                if x <= 0 or x > self.symbol_tests.count():
                    error = QMessageBox()
                    error.setIcon(QMessageBox.Warning)
                    error.setStandardButtons(QMessageBox.Ok)
                    error.setText("Page must be between 1 and " +
                                  str(self.symbol_tests.count()))
                    error.exec_()
                    return

                self.symbol_tests.setCurrentIndex(x - 1)
            except ValueError:
                error = QMessageBox()
                error.setIcon(QMessageBox.Warning)
                error.setStandardButtons(QMessageBox.Ok)
                error.setText("Can only enter an integer between 1 and " +
                              str(self.symbol_tests.count()))
                error.exec_()

    """
	@modifies self.question1, self.question2
	@effects  changes the default questions
	"""

    def change_questions(self):
        text = QInputDialog.getText(self, "Question 1 Input", "Question 1: ",
                                    QLineEdit.Normal, self.question1)
        if str(text[0]) != '':
            self.question1 = str(text[0])
        text = QInputDialog.getText(self, "Question 2 Input", "Question 2: ",
                                    QLineEdit.Normal, self.question2)
        if str(text[0]) != '':
            self.question2 = str(text[0])

    def delete_pages(self):
        text = QInputDialog.getText(
            self, "Delete pages", "Enter page numbers seperated by a comma:",
            QLineEdit.Normal)
        error = QMessageBox()
        error.setIcon(QMessageBox.Warning)
        error.setStandardButtons(QMessageBox.Ok)
        if text[1] != False:
            pages = text[0].split(",")
            del_widgets = []
            for page in pages:
                try:
                    x = int(page)
                except:
                    error.setText("All page numbers must be integers")
                    error.exec_()
                    return
                if int(page) > self.symbol_tests.count() or int(page) < 1:
                    error.setText("Page " + page + " does not exist")
                    error.exec_()
                    return
                del_widgets.append(self.symbol_tests.widget(int(page) - 1))

            confirm = QMessageBox.question(
                self, 'Message', "Really delete pages: " + text[0] + "?",
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

            for del_widget in del_widgets:
                del_idx = self.symbol_tests.indexOf(del_widget)
                if del_idx != self.symbol_tests.currentIndex():
                    self.symbol_tests.removeWidget(del_widget)
                else:
                    #Removing the only test
                    if self.symbol_tests.count() == 1:
                        self.add_blank_test()
                        self.symbol_tests.removeWidget(del_widget)
                        self.symbol_tests.currentWidget().set_page(1)
                        self.symbol_tests.currentWidget().set_numPages(1)
                        return
                    else:
                        if del_idx == self.symbol_tests.count() - 1:
                            self.symbol_tests.setCurrentIndex(del_idx - 1)
                            self.symbol_tests.removeWidget(del_widget)
                        else:
                            self.symbol_tests.setCurrentIndex(del_idx + 1)
                            self.symbol_tests.removeWidget(del_widget)

            count = 1
            for i in range(0, self.symbol_tests.count()):
                cur_widget = self.symbol_tests.widget(i)
                cur_widget.set_page(count)
                cur_widget.set_numPages(self.symbol_tests.count())
                count += 1
Beispiel #14
0
class DAT_GUI(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.initUI()

    def initUI(self):
        screen = QApplication.desktop().screenNumber(
            QApplication.desktop().cursor().pos())
        centerPoint = QApplication.desktop().screenGeometry(screen).center()

        # should fit in 1024x768 (old computer screens)
        window_width = 900
        window_height = 700
        self.setGeometry(
            QtCore.QRect(
                centerPoint.x() - int(window_width / 2),
                centerPoint.y() - int(window_height / 2), window_width,
                window_height))  # should I rather center on the screen

        # zoom parameters
        self.scale = 1.0
        self.min_scaling_factor = 0.1
        self.max_scaling_factor = 20
        self.zoom_increment = 0.05

        self.setWindowTitle(__NAME__ + ' v' + str(__VERSION__))

        self.paint = Createpaintwidget()

        # initiate 2D image for 2D display
        self.img = None

        self.list = QListWidget(
            self)  # a list that contains files to read or play with
        self.list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.list.selectionModel().selectionChanged.connect(
            self.selectionChanged)  # connect it to sel change

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

        self.table_widget = QWidget()
        table_widget_layout = QVBoxLayout()

        # Initialize tab screen
        self.tabs = QTabWidget(self)
        self.tab1 = QWidget()
        self.tab2 = QWidget()
        self.tab3 = QWidget()

        # Add tabs
        self.tabs.addTab(self.tab1, "Mask neuron")
        self.tabs.addTab(self.tab2, "Mask cell body")
        self.tabs.addTab(self.tab3, "Segment dendrites")

        # listen to tab changes
        self.tabs.currentChanged.connect(self._onTabChange)

        # Create first tab
        self.tab1.layout = QGridLayout()
        self.tab1.layout.setAlignment(Qt.AlignTop)
        self.tab1.layout.setHorizontalSpacing(3)
        self.tab1.layout.setVerticalSpacing(3)
        self.tab1.layout.setContentsMargins(0, 0, 0, 0)

        label1_tab1 = QLabel('Step 1:')
        self.tab1.layout.addWidget(label1_tab1, 0, 0)

        self.local_threshold = QPushButton("Local threshold")
        self.local_threshold.clicked.connect(self.run_threshold_neuron)
        self.tab1.layout.addWidget(self.local_threshold, 0, 1)
        self.global_threshold = QPushButton("Global threshold")
        self.global_threshold.clicked.connect(self.run_threshold_neuron)
        self.tab1.layout.addWidget(self.global_threshold, 0, 2)
        self.local_n_global_threshold = QPushButton(
            "Local AND Global threshold")
        self.local_n_global_threshold.clicked.connect(
            self.run_threshold_neuron)
        self.tab1.layout.addWidget(self.local_n_global_threshold, 0, 3)

        self.extra_value_for_threshold = QSpinBox()
        self.extra_value_for_threshold.setSingleStep(1)
        self.extra_value_for_threshold.setRange(0, 1_000_000)
        self.extra_value_for_threshold.setValue(6)
        self.tab1.layout.addWidget(self.extra_value_for_threshold, 0, 4)

        self.threshold_method = QComboBox()
        self.threshold_method.addItem('Mean')
        self.threshold_method.addItem('Median')
        self.tab1.layout.addWidget(self.threshold_method, 0, 5)

        label2_tab1 = QLabel('Step 2 (optional):')
        self.tab1.layout.addWidget(label2_tab1, 1, 0)

        self.remove_pixel_blobs_smaller_or_equal = QPushButton(
            "Remove pixel blobs smaller or equal to")
        self.remove_pixel_blobs_smaller_or_equal.clicked.connect(
            self.remove_blobs)
        self.tab1.layout.addWidget(self.remove_pixel_blobs_smaller_or_equal, 1,
                                   1)

        self.remove_blobs_size = QSpinBox()
        self.remove_blobs_size.setSingleStep(1)
        self.remove_blobs_size.setRange(0, 1_000_000)
        self.remove_blobs_size.setValue(1)
        self.tab1.layout.addWidget(self.remove_blobs_size, 1, 2)

        label3_tab1 = QLabel('Step 3: Save')
        self.tab1.layout.addWidget(label3_tab1, 2, 0)

        self.tab1.setLayout(self.tab1.layout)

        self.tab2.layout = QGridLayout()
        self.tab2.layout.setAlignment(Qt.AlignTop)
        self.tab2.layout.setHorizontalSpacing(3)
        self.tab2.layout.setVerticalSpacing(3)
        self.tab2.layout.setContentsMargins(0, 0, 0, 0)

        label1_tab2 = QLabel('Step 1:')
        self.tab2.layout.addWidget(label1_tab2, 0, 0)

        self.detect_cell_body = QPushButton("Detect cell body")
        self.detect_cell_body.clicked.connect(self.detect_neuronal_cell_body)
        self.tab2.layout.addWidget(self.detect_cell_body, 0, 1)

        self.extraCutOff_cell_body = QSpinBox()
        self.extraCutOff_cell_body.setSingleStep(1)
        self.extraCutOff_cell_body.setRange(0, 1_000_000)
        self.extraCutOff_cell_body.setValue(5)
        self.tab2.layout.addWidget(self.extraCutOff_cell_body, 0, 2)

        erosion_label = QLabel('erosion rounds')
        self.tab2.layout.addWidget(erosion_label, 0, 3)

        self.nb_erosion_cellbody = QSpinBox()
        self.nb_erosion_cellbody.setSingleStep(1)
        self.nb_erosion_cellbody.setRange(0, 1_000_000)
        self.nb_erosion_cellbody.setValue(2)
        self.tab2.layout.addWidget(self.nb_erosion_cellbody, 0, 4)

        min_object_size_label = QLabel('minimum object size')
        self.tab2.layout.addWidget(min_object_size_label, 0, 5)

        self.min_obj_size_px = QSpinBox()
        self.min_obj_size_px.setSingleStep(1)
        self.min_obj_size_px.setRange(0, 1_000_000)
        self.min_obj_size_px.setValue(600)
        self.tab2.layout.addWidget(self.min_obj_size_px, 0, 6)

        fill_label = QLabel('fill up to')
        self.tab2.layout.addWidget(fill_label, 0, 7)

        self.fill_holes_up_to = QSpinBox()
        self.fill_holes_up_to.setSingleStep(1)
        self.fill_holes_up_to.setRange(0, 1_000_000)
        self.fill_holes_up_to.setValue(600)
        self.tab2.layout.addWidget(self.fill_holes_up_to, 0, 8)

        nb_dilation_cell_body_label = QLabel('nb dilation cell body')
        self.tab2.layout.addWidget(nb_dilation_cell_body_label, 0, 9)

        self.nb_dilation_cellbody = QSpinBox()
        self.nb_dilation_cellbody.setSingleStep(1)
        self.nb_dilation_cellbody.setRange(0, 1_000_000)
        self.nb_dilation_cellbody.setValue(2)
        self.tab2.layout.addWidget(self.nb_dilation_cellbody, 0, 10)

        label2_tab2 = QLabel('Step 2: Save')
        self.tab2.layout.addWidget(label2_tab2, 6, 0)

        self.tab2.setLayout(self.tab2.layout)

        self.tab3.layout = QGridLayout()
        self.tab3.layout.setAlignment(Qt.AlignTop)
        self.tab3.layout.setHorizontalSpacing(3)
        self.tab3.layout.setVerticalSpacing(3)
        self.tab3.layout.setContentsMargins(0, 0, 0, 0)

        label1_tab3 = QLabel('Step 1:')
        self.tab3.layout.addWidget(label1_tab3, 0, 0)

        self.wshed = QPushButton("Watershed")
        self.wshed.clicked.connect(self.watershed_segment_the_neuron)
        self.tab3.layout.addWidget(self.wshed, 0, 1)

        self.whsed_big_blur = QDoubleSpinBox()
        self.whsed_big_blur.setSingleStep(0.1)
        self.whsed_big_blur.setRange(0, 100)
        self.whsed_big_blur.setValue(2.1)
        self.tab3.layout.addWidget(self.whsed_big_blur, 0, 2)

        self.whsed_small_blur = QDoubleSpinBox()
        self.whsed_small_blur.setSingleStep(0.1)
        self.whsed_small_blur.setRange(0, 100)
        self.whsed_small_blur.setValue(1.4)
        self.tab3.layout.addWidget(self.whsed_small_blur, 0, 3)

        self.wshed_rm_small_cells = QSpinBox()
        self.wshed_rm_small_cells.setSingleStep(1)
        self.wshed_rm_small_cells.setRange(0, 1_000_000)
        self.wshed_rm_small_cells.setValue(10)
        self.tab3.layout.addWidget(self.wshed_rm_small_cells, 0, 4)

        self.jSpinner11 = QSpinBox()
        self.jSpinner11.setSingleStep(1)
        self.jSpinner11.setRange(0, 1_000_000)
        self.jSpinner11.setValue(10)
        self.tab3.layout.addWidget(self.jSpinner11, 0, 5)

        label1_bis_tab3 = QLabel('Alternative Step 1:')
        self.tab3.layout.addWidget(label1_bis_tab3, 1, 0)

        self.skel = QPushButton("Skeletonize")
        self.skel.clicked.connect(self.skeletonize_mask)
        self.tab3.layout.addWidget(self.skel, 1, 1)

        label2_tab3 = QLabel('Step 2:')
        self.tab3.layout.addWidget(label2_tab3, 2, 0)

        self.apply_cell_body = QPushButton("Apply cell body")
        self.apply_cell_body.clicked.connect(
            self.apply_cell_body_to_skeletonized_mask)
        self.tab3.layout.addWidget(self.apply_cell_body, 2, 1)

        label3_tab3 = QLabel('Step 3 (Optional):')
        self.tab3.layout.addWidget(label3_tab3, 3, 0)

        self.prune = QPushButton("Prune")
        self.prune.clicked.connect(self.prune_dendrites)
        self.tab3.layout.addWidget(self.prune, 3, 1)

        self.prune_length = QSpinBox()
        self.prune_length.setSingleStep(1)
        self.prune_length.setRange(0, 1_000_000)
        self.prune_length.setValue(3)
        self.tab3.layout.addWidget(self.prune_length, 3, 2)

        label4_tab3 = QLabel('Step 4 (Optional):')
        self.tab3.layout.addWidget(label4_tab3, 4, 0)

        self.find_neurons = QPushButton("Find neurons")
        self.find_neurons.clicked.connect(self.find_neurons_in_mask)
        self.tab3.layout.addWidget(self.find_neurons, 4, 1)

        self.find_neurons_min_size = QSpinBox()
        self.find_neurons_min_size.setSingleStep(1)
        self.find_neurons_min_size.setRange(0, 1_000_000)
        self.find_neurons_min_size.setValue(45)
        self.tab3.layout.addWidget(self.find_neurons_min_size, 4, 2)

        self.prune_unconnected_segments = QPushButton(
            "Prune unconnected segments (run 'Find neurons' first)")
        self.prune_unconnected_segments.clicked.connect(
            self.prune_neuron_unconnected_segments)
        self.tab3.layout.addWidget(self.prune_unconnected_segments, 4, 3)

        label6_tab3 = QLabel('Step 5: Save')
        self.tab3.layout.addWidget(label6_tab3, 5, 0)

        label5_tab3 = QLabel('Step 6:')
        self.tab3.layout.addWidget(label5_tab3, 6, 0)

        self.create_n_save_bonds = QPushButton("Segment dendrites")
        self.create_n_save_bonds.clicked.connect(self.save_segmented_bonds)
        self.tab3.layout.addWidget(self.create_n_save_bonds, 6, 1)

        self.tab3.setLayout(self.tab3.layout)

        # Add tabs to widget
        table_widget_layout.addWidget(self.tabs)
        self.table_widget.setLayout(table_widget_layout)

        self.Stack = QStackedWidget(self)
        self.Stack.addWidget(self.scrollArea)

        # create a grid that will contain all the GUI interface
        self.grid = QGridLayout()
        self.grid.addWidget(self.Stack, 0, 0)
        self.grid.addWidget(self.list, 0, 1)
        # The first parameter of the rowStretch method is the row number, the second is the stretch factor. So you need two calls to rowStretch, like this: --> below the first row is occupying 80% and the second 20%
        self.grid.setRowStretch(0, 75)
        self.grid.setRowStretch(2, 25)

        # first col 75% second col 25% of total width
        self.grid.setColumnStretch(0, 75)
        self.grid.setColumnStretch(1, 25)

        # void QGridLayout::addLayout(QLayout * layout, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment = 0)
        self.grid.addWidget(self.table_widget, 2, 0, 1,
                            2)  # spans over one row and 2 columns

        # BEGIN TOOLBAR
        # pen spin box and connect
        self.penSize = QSpinBox()
        self.penSize.setSingleStep(1)
        self.penSize.setRange(1, 256)
        self.penSize.setValue(3)
        self.penSize.valueChanged.connect(self.penSizechange)

        self.channels = QComboBox()
        self.channels.addItem("merge")
        self.channels.currentIndexChanged.connect(self.channelChange)

        tb_drawing_pane = QToolBar()

        save_button = QToolButton()
        save_button.setText("Save")
        save_button.clicked.connect(self.save_current_mask)
        tb_drawing_pane.addWidget(save_button)

        tb_drawing_pane.addWidget(QLabel("Channels"))
        tb_drawing_pane.addWidget(self.channels)

        # tb.addAction("Save")
        #
        tb_drawing_pane.addWidget(QLabel("Pen size"))
        tb_drawing_pane.addWidget(self.penSize)

        self.grid.addWidget(tb_drawing_pane, 1, 0)
        # END toolbar

        # toolbar for the list
        tb_list = QToolBar()

        del_button = QToolButton()
        del_button.setText("Delete selection from list")
        del_button.clicked.connect(self.delete_from_list)
        tb_list.addWidget(del_button)

        self.grid.addWidget(tb_list, 1, 1)

        # self.setCentralWidget(self.scrollArea)
        self.setCentralWidget(QFrame())
        self.centralWidget().setLayout(self.grid)

        # self.statusBar().showMessage('Ready')
        statusBar = self.statusBar(
        )  # sets an empty status bar --> then can add messages in it
        self.paint.statusBar = statusBar

        # add progress bar to status bar
        self.progress = QProgressBar(self)
        self.progress.setGeometry(200, 80, 250, 20)
        statusBar.addWidget(self.progress)

        # Set up menu bar
        self.mainMenu = self.menuBar()

        self.zoomInAct = QAction(
            "Zoom &In (25%)",
            self,  # shortcut="Ctrl++",
            enabled=True,
            triggered=self.zoomIn)
        self.zoomOutAct = QAction(
            "Zoom &Out (25%)",
            self,  # shortcut="Ctrl+-",
            enabled=True,
            triggered=self.zoomOut)
        self.normalSizeAct = QAction(
            "&Normal Size",
            self,  # shortcut="Ctrl+S",
            enabled=True,
            triggered=self.defaultSize)

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

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

        self.setMenuBar(self.mainMenu)

        # set drawing window fullscreen
        fullScreenShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_F), self)
        fullScreenShortcut.activated.connect(self.fullScreen)
        fullScreenShortcut.setContext(QtCore.Qt.ApplicationShortcut)

        escapeShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Escape), self)
        escapeShortcut.activated.connect(self.escape)
        escapeShortcut.setContext(QtCore.Qt.ApplicationShortcut)

        # Show/Hide the mask
        escapeShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_H), self)
        escapeShortcut.activated.connect(self.showHideMask)
        escapeShortcut.setContext(QtCore.Qt.ApplicationShortcut)

        zoomPlus = QtWidgets.QShortcut("Ctrl+Shift+=", self)
        zoomPlus.activated.connect(self.zoomIn)
        zoomPlus.setContext(QtCore.Qt.ApplicationShortcut)

        zoomPlus2 = QtWidgets.QShortcut("Ctrl++", self)
        zoomPlus2.activated.connect(self.zoomIn)
        zoomPlus2.setContext(QtCore.Qt.ApplicationShortcut)

        zoomMinus = QtWidgets.QShortcut("Ctrl+Shift+-", self)
        zoomMinus.activated.connect(self.zoomOut)
        zoomMinus.setContext(QtCore.Qt.ApplicationShortcut)

        zoomMinus2 = QtWidgets.QShortcut("Ctrl+-", self)
        zoomMinus2.activated.connect(self.zoomOut)
        zoomMinus2.setContext(QtCore.Qt.ApplicationShortcut)

        spaceShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Space), self)
        spaceShortcut.activated.connect(self.nextFrame)
        spaceShortcut.setContext(QtCore.Qt.ApplicationShortcut)

        backspaceShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Backspace), self)
        backspaceShortcut.activated.connect(self.prevFrame)
        backspaceShortcut.setContext(QtCore.Qt.ApplicationShortcut)

        # connect enter keys to edit dendrites
        enterShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Return), self)
        enterShortcut.activated.connect(self.runSkel)
        enterShortcut.setContext(QtCore.Qt.ApplicationShortcut)
        enter2Shortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Enter), self)
        enter2Shortcut.activated.connect(self.runSkel)
        enter2Shortcut.setContext(QtCore.Qt.ApplicationShortcut)

        #Qt.Key_Enter is the Enter located on the keypad:
        #Qt::Key_Return  0x01000004
        #Qt::Key_Enter   0x01000005  Typically located on the keypad.

        self.setAcceptDrops(True)  # KEEP IMPORTANT

    def __get_mask_img_from_overlay(self):
        if self.paint.imageDraw:
            channels_count = 4
            s = self.paint.imageDraw.bits().asstring(
                self.img.shape[0] * self.img.shape[1] * channels_count)
            arr = np.frombuffer(s, dtype=np.uint8).reshape(
                (self.img.shape[0], self.img.shape[1], channels_count))
            return Img(arr[..., 2].copy(), dimensions='hw')
        else:
            return None

    def __get_output_folder(self):
        selected_items = self.list.selectedItems()
        if selected_items:
            filename = selected_items[0].toolTip()
            filename0_without_ext = os.path.splitext(filename)[0]
            return filename0_without_ext
        else:
            return None

    def delete_from_list(self):
        list_items = self.list.selectedItems()
        # empty list --> nothing to do
        if not list_items:
            return
        for item in list_items:
            self.list.takeItem(self.list.row(item))

    def save_current_mask(self):
        output_folder = self.__get_output_folder()
        if output_folder is None:
            logger.error('No image is selected --> nothing to save')
            return
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to save')
            return
        if self.tabs.currentIndex() == 0:
            print('saving', os.path.join(output_folder, 'mask.tif'))
            mask.save(os.path.join(output_folder, 'mask.tif'))
        elif self.tabs.currentIndex() == 1:
            print('saving', os.path.join(output_folder, 'cellBodyMask.tif'))
            mask.save(os.path.join(output_folder, 'cellBodyMask.tif'))
        else:
            print('saving', os.path.join(output_folder, 'handCorrection.tif'))
            mask.save(os.path.join(output_folder, 'handCorrection.tif'))

    def detect_neuronal_cell_body(self):
        try:
            # get image and detect cell body
            mask = detect_cell_body(
                self.img,
                fillHoles=self.fill_holes_up_to.value(),
                denoise=self.min_obj_size_px.value(),
                nbOfErosions=self.nb_erosion_cellbody.value(),
                nbOfDilatations=self.nb_dilation_cellbody.value(),
                extraCutOff=self.extraCutOff_cell_body.value(),
                channel=self.channels.currentText())
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
            else:
                logger.error('Cell body could not be detected')
        except:
            traceback.print_exc()

    def __get_neuronal_mask(self, warn=True):
        output_folder = self.__get_output_folder()
        if output_folder is None:
            if warn:
                logger.error('No image selected --> nothing to do')
            return None
        if os.path.exists(os.path.join(output_folder, 'mask.tif')):
            # NB should I check the nb of channels --> no I don't want to handle externally created files and want people to rely fully on my stuff that has
            return Img(os.path.join(output_folder, 'mask.tif'))
        else:
            if warn:
                logger.error(
                    'Neuronal mask not found --> Please create one in the "Mask neuron" tab first'
                )
        return None

    def __get_corrected_mask(self, warn=True):
        output_folder = self.__get_output_folder()
        if output_folder is None:
            if warn:
                logger.error('No image selected --> nothing to do')
            return None
        if os.path.exists(os.path.join(output_folder, 'handCorrection.tif')):
            return Img(os.path.join(output_folder, 'handCorrection.tif'))
        elif os.path.exists(os.path.join(output_folder,
                                         'mask.tif')) and not warn:
            return Img(os.path.join(output_folder, 'mask.tif'))
        return None

    def __get_cellbody_mask(self, warn=True):
        output_folder = self.__get_output_folder()
        if output_folder is None:
            if warn:
                logger.error('No image selected --> nothing to do')
            return None
        if os.path.exists(os.path.join(output_folder, 'cellBodyMask.tif')):
            # NB should I check the nb of channels --> no I don't want to handle externally created files and want people to rely fully on my stuff that has
            return Img(os.path.join(output_folder, 'cellBodyMask.tif'))
        else:
            if warn:
                logger.error(
                    'Cell body mask not found --> Please create one in the "Mask cell body" tab first'
                )
        return None

    # seems ok for now
    def watershed_segment_the_neuron(self):
        try:
            # get raw image and segment it using the watershed algorithm
            # make it load the neuronal mask
            neuronal_mask = self.__get_neuronal_mask()
            if neuronal_mask is None:
                return
            # TODO should I add autoskel or not
            mask = watershed_segment_neuron(
                self.img,
                neuronal_mask,
                fillSize=self.jSpinner11.value(),
                autoSkel=True,
                first_blur=self.whsed_big_blur.value(),
                second_blur=self.whsed_small_blur.value(),
                min_size=self.wshed_rm_small_cells.value(),
                channel=self.channels.currentText())
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
            else:
                logger.error(
                    'Something went wrong, the neuron could not be segmented, sorry...'
                )
        except:
            traceback.print_exc()

    def save_segmented_bonds(self):
        output_folder = self.__get_output_folder()
        if output_folder is None:
            logger.error('No image is selected --> nothing to save')
            return
        # get mask the find neurons
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to do')
            return
        mask = detect_cell_bonds(mask)

        if mask is None:
            logger.error(
                'Could not find dendrites, are you sure a mask is overlayed over the neuron'
            )
            return

        # code for conversion of 24 bits numpy array to an RGB one --> keep and store in Img at some point cause useful
        # convert 24 bits array to RGB
        RGB_mask = np.zeros(shape=(*mask.shape, 3), dtype=np.uint8)
        RGB_mask[..., 2] = mask & 255
        RGB_mask[..., 1] = (mask >> 8) & 255
        RGB_mask[..., 0] = (mask >> 16) & 255

        Img(RGB_mask,
            dimensions='hwc').save(os.path.join(output_folder, 'bonds.tif'))

    def prune_neuron_unconnected_segments(self):
        # get mask the find neurons
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to do')
            return
        mask = find_neurons(
            mask,
            neuron_minimum_size_threshold=self.find_neurons_min_size.value(),
            return_unconnected=True)
        if mask is None:
            logger.error(
                'Could not find neurons, are you sure a mask is overlayed over the neuron'
            )
            return

        self.paint.imageDraw = Img(self.createRGBA(mask),
                                   dimensions='hwc').getQimage()
        self.paint.update()

    def apply_cell_body_to_skeletonized_mask(self):
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to do')
            return
        cell_body_mask = self.__get_cellbody_mask()
        if cell_body_mask is None:
            return
        cell_body_outline = get_cell_body_outline(cell_body_mask, mask)
        if cell_body_outline is None:
            logger.error(
                'Error could not add cell body outline to the neuronal mask...'
            )
            return
        self.paint.imageDraw = Img(self.createRGBA(cell_body_outline),
                                   dimensions='hwc').getQimage()
        self.paint.update()

    def find_neurons_in_mask(self):
        # get mask the find neurons
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to do')
            return
        mask_copy = mask.copy()
        mask = find_neurons(
            mask,
            neuron_minimum_size_threshold=self.find_neurons_min_size.value())
        if mask is None:
            logger.error(
                'Could not find neurons, are you sure a mask is overlayed over the neuron'
            )
            return

        # we set the red channel, the blue one, the alpha transparency (channel 4) and finally we only allow alpha channel in the two masks regions to keep the rest of the stuff
        final_overlay = np.zeros(shape=(*mask_copy.shape, 4), dtype=np.uint8)
        final_overlay[..., 0] = np.logical_xor(mask, mask_copy).astype(
            np.uint8) * 255  # blue channel
        final_overlay[mask == 0, 0] = 0
        final_overlay[..., 1] = final_overlay[
            ...,
            0]  # green channel # copy the channel to make the stuff appear cyan
        final_overlay[..., 2] = mask_copy  # red channel
        final_overlay[np.logical_or(mask, mask_copy) != 0,
                      3] = 255  # --> need set alpha transparency of the image

        self.paint.imageDraw = Img(final_overlay, dimensions='hwc').getQimage()
        self.paint.update()

    def prune_dendrites(self):

        prune_lgth = self.prune_length.value()
        if prune_lgth <= 0:
            logger.info('prune length is 0 --> nothing to do')
            return

        # get the mask from displayed image
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to do')
            return

        # see how to get the stuff ????
        mask = prune_dendrites(mask, prune_below=prune_lgth)

        if mask is None:
            logger.error(
                'Could not prune dendrites, are you sure there is a mask ovrlayed on the neuron'
            )
            return

        self.paint.imageDraw = Img(self.createRGBA(mask),
                                   dimensions='hwc').getQimage()
        self.paint.update()

    def skeletonize_mask(self):
        # get mask then skeletonize it then return it --> see exactly
        try:
            # get raw image and segment it using the skeletonize algorithm
            # make it load the neuronal mask
            neuronal_mask = self.__get_neuronal_mask()
            if neuronal_mask is None:
                return
            mask = skel_segment_neuronal_mask(neuronal_mask)
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
            else:
                logger.error(
                    'Something went wrong, the neuron could not be sekeletonized, sorry...'
                )
        except:
            traceback.print_exc()

    def _onTabChange(self):
        # if tab is changed --> do stuff
        # load files or warn...
        if self.tabs.currentIndex() == 0:
            mask = self.__get_neuronal_mask(warn=False)
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
        elif self.tabs.currentIndex() == 1:
            mask = self.__get_cellbody_mask(warn=False)
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
        elif self.tabs.currentIndex() == 2:
            mask = self.__get_corrected_mask(warn=False)
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()

    def run_threshold_neuron(self):
        try:
            local_or_global = 'global'
            if self.sender() == self.local_threshold:
                local_or_global = 'local'
            elif self.sender() == self.local_n_global_threshold:
                local_or_global = 'local+global'

            mask = threshold_neuron(
                self.img,
                mode=local_or_global,
                blur_method=self.threshold_method.currentText(),
                spin_value=self.extra_value_for_threshold.value(),
                channel=self.channels.currentText())
            if mask is not None:
                self.paint.imageDraw = Img(self.createRGBA(mask),
                                           dimensions='hwc').getQimage()
                self.paint.update()
        except:
            traceback.print_exc()

    def channelChange(self, i):
        if self.Stack.currentIndex() == 0:
            if i == 0:
                self.paint.setImage(self.img)
            else:
                channel_img = self.img.imCopy(c=i - 1)
                self.paint.setImage(channel_img)
            self.paint.update()

    def penSizechange(self):
        self.paint.brushSize = self.penSize.value()

    def selectionChanged(self):
        self.paint.maskVisible = True
        selected_items = self.list.selectedItems()
        if selected_items:
            start = timer()
            if self.img is not None:
                # make sure we don't load the image twice
                if selected_items[0].toolTip() != self.img.metadata['path']:
                    self.img = Img(selected_items[0].toolTip())
                    logger.debug("took " + str(timer() - start) +
                                 " secs to load image")
                else:
                    logger.debug("image already loaded --> ignoring")
            else:
                self.img = Img(selected_items[0].toolTip())
                logger.debug("took " + str(timer() - start) +
                             " secs to load image")

        if self.img is not None:
            selection = self.channels.currentIndex()
            self.channels.disconnect()
            self.channels.clear()
            comboData = ['merge']
            if self.img.has_c():
                for i in range(self.img.get_dimension('c')):
                    comboData.append(str(i))
            logger.debug('channels found ' + str(comboData))
            self.channels.addItems(comboData)
            if selection != -1 and selection < self.channels.count():
                self.channels.setCurrentIndex(selection)
            else:
                self.channels.setCurrentIndex(0)
            self.channels.currentIndexChanged.connect(self.channelChange)

        if selected_items:
            self.statusBar().showMessage('Loading ' +
                                         selected_items[0].toolTip())
            selection = self.channels.currentIndex()
            if selection == 0:
                self.paint.setImage(self.img)
            else:
                self.paint.setImage(self.img.imCopy(c=selection - 1))
            self.scaleImage(0)
            self.update()
            self.paint.update()

            if self.list.currentItem() and self.list.currentItem().icon(
            ).isNull():
                logger.debug('Updating icon')
                icon = QIcon(QPixmap.fromImage(self.paint.image))
                pixmap = icon.pixmap(24, 24)
                icon = QIcon(pixmap)
                self.list.currentItem().setIcon(icon)
        else:
            logger.debug("Empty selection")
            self.paint.image = None
            self.scaleImage(0)
            self.update()
            self.paint.update()
            self.img = None
        # try update also the masks if they are available
        try:
            self._onTabChange()
        except:
            pass

    def clearlayout(self, layout):
        for i in reversed(range(layout.count())):
            layout.itemAt(i).widget().setParent(None)

    def showHideMask(self):
        self.paint.maskVisible = not self.paint.maskVisible
        self.paint.update()

    def escape(self):
        if self.Stack.isFullScreen():
            self.fullScreen()

    def fullScreen(self):
        if not self.Stack.isFullScreen():
            self.Stack.setWindowFlags(
                QtCore.Qt.Window | QtCore.Qt.CustomizeWindowHint |
                # QtCore.Qt.WindowTitleHint |
                # QtCore.Qt.WindowCloseButtonHint |
                QtCore.Qt.WindowStaysOnTopHint)
            self.Stack.showFullScreen()
        else:
            self.Stack.setWindowFlags(QtCore.Qt.Widget)
            self.grid.addWidget(self.Stack, 0, 0)
            # dirty hack to make it repaint properly --> obviously not all lines below are required but some are --> need test, the last line is key though
            self.grid.update()
            self.Stack.update()
            self.Stack.show()
            self.centralWidget().setLayout(self.grid)
            self.centralWidget().update()
            self.update()
            self.show()
            self.repaint()
            self.Stack.update()
            self.Stack.repaint()
            self.centralWidget().repaint()

    def nextFrame(self):
        idx = self.list.model().index(self.list.currentRow() + 1, 0)
        if idx.isValid():
            self.list.selectionModel().setCurrentIndex(
                idx, QItemSelectionModel.ClearAndSelect)  # SelectCurrent

    def remove_blobs(self):
        blob_size = self.remove_blobs_size.value()
        if blob_size <= 0:
            logger.info('blob size is 0 --> nothing to do')
            return

        # get the mask from displayed image
        mask = self.__get_mask_img_from_overlay()
        if mask is None:
            logger.error('No mask/overlay detected --> nothing to save')
            return

        mask = remove_small_objects(mask.astype(np.bool),
                                    min_size=blob_size,
                                    connectivity=2,
                                    in_place=False)
        # then place back pixels in the mask
        # now set the mask back
        # plt.imshow(mask)
        # plt.show()

        self.paint.imageDraw = Img(self.createRGBA(mask),
                                   dimensions='hwc').getQimage()
        self.paint.update()

    def runSkel(self):
        # only allow that for tab 3
        if self.tabs.currentIndex() == 2:
            mask = self.__get_mask_img_from_overlay()
            if mask is None:
                logger.error('No mask/overlay detected --> nothing to do')
                return
            # just skeletonize the image
            mask = skel_segment_neuronal_mask(
                mask, fill_holes=0)  # should I put it to 0 or other things ???
            if mask is None:
                logger.error('Could not skeletonize user edited mask...')
                return

            self.paint.imageDraw = Img(self.createRGBA(mask),
                                       dimensions='hwc').getQimage()
            self.paint.update()

    def createRGBA(self, handCorrection):
        # use pen color to display the mask
        # in fact I need to put the real color
        RGBA = np.zeros((handCorrection.shape[0], handCorrection.shape[1], 4),
                        dtype=np.uint8)
        red = self.paint.drawColor.red()
        green = self.paint.drawColor.green()
        blue = self.paint.drawColor.blue()

        # bug somewhere --> fix it some day --> due to bgra instead of RGBA
        RGBA[handCorrection != 0, 0] = blue  # b
        RGBA[handCorrection != 0, 1] = green  # g
        RGBA[handCorrection != 0, 2] = red  # r
        RGBA[..., 3] = 255  # alpha --> indeed alpha
        RGBA[handCorrection == 0, 3] = 0  # very complex fix some day

        return RGBA

    def prevFrame(self):
        idx = self.list.model().index(self.list.currentRow() - 1, 0)
        if idx.isValid():
            self.list.selectionModel().setCurrentIndex(
                idx, QItemSelectionModel.ClearAndSelect)

    def zoomIn(self):
        self.statusBar().showMessage('Zooming in', msecs=200)
        if self.Stack.currentIndex() == 0:
            self.scaleImage(self.zoom_increment)

    def zoomOut(self):
        self.statusBar().showMessage('Zooming out', msecs=200)
        if self.Stack.currentIndex() == 0:
            self.scaleImage(-self.zoom_increment)

    def defaultSize(self):
        self.paint.adjustSize()
        self.scale = 1.0
        self.scaleImage(0)

    def scaleImage(self, factor):
        self.scale += factor
        if self.paint.image is not None:
            self.paint.resize(self.scale * self.paint.image.size())
        else:
            # no image set size to 0, 0 --> scroll pane will auto adjust
            self.paint.resize(QSize(0, 0))
            self.scale -= factor  # reset zoom

        self.paint.scale = self.scale
        # self.paint.vdp.scale = self.scale

        self.zoomInAct.setEnabled(self.scale < self.max_scaling_factor)
        self.zoomOutAct.setEnabled(self.scale > self.min_scaling_factor)

        # allow DND

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

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

        # handle DND on drop

    def dropEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            urls = []
            for url in event.mimeData().urls():
                urls.append(url.toLocalFile())

            for url in urls:
                import os
                item = QListWidgetItem(os.path.basename(url), self.list)
                item.setToolTip(url)
                self.list.addItem(item)
        else:
            event.ignore()
Beispiel #15
0
class E5SideBar(QWidget):
    """
    Class implementing a sidebar with a widget area, that is hidden or shown,
    if the current tab is clicked again.
    """
    Version = 2

    North = 0
    East = 1
    South = 2
    West = 3

    def __init__(self, orientation=None, delay=200, parent=None):
        """
        Constructor
        
        @param orientation orientation of the sidebar widget (North, East,
            South, West)
        @param delay value for the expand/shrink delay in milliseconds
            (integer)
        @param parent parent widget (QWidget)
        """
        super(E5SideBar, self).__init__(parent)

        self.__tabBar = QTabBar()
        self.__tabBar.setDrawBase(True)
        self.__tabBar.setShape(QTabBar.RoundedNorth)
        self.__tabBar.setUsesScrollButtons(True)
        self.__tabBar.setDrawBase(False)
        self.__stackedWidget = QStackedWidget(self)
        self.__stackedWidget.setContentsMargins(0, 0, 0, 0)
        self.__autoHideButton = QToolButton()
        self.__autoHideButton.setCheckable(True)
        self.__autoHideButton.setIcon(
            UI.PixmapCache.getIcon("autoHideOff.png"))
        self.__autoHideButton.setChecked(True)
        self.__autoHideButton.setToolTip(
            self.tr("Deselect to activate automatic collapsing"))
        self.barLayout = QBoxLayout(QBoxLayout.LeftToRight)
        self.barLayout.setContentsMargins(0, 0, 0, 0)
        self.layout = QBoxLayout(QBoxLayout.TopToBottom)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)
        self.barLayout.addWidget(self.__autoHideButton)
        self.barLayout.addWidget(self.__tabBar)
        self.layout.addLayout(self.barLayout)
        self.layout.addWidget(self.__stackedWidget)
        self.setLayout(self.layout)

        # initialize the delay timer
        self.__actionMethod = None
        self.__delayTimer = QTimer(self)
        self.__delayTimer.setSingleShot(True)
        self.__delayTimer.setInterval(delay)
        self.__delayTimer.timeout.connect(self.__delayedAction)

        self.__minimized = False
        self.__minSize = 0
        self.__maxSize = 0
        self.__bigSize = QSize()

        self.splitter = None
        self.splitterSizes = []

        self.__hasFocus = False
        # flag storing if this widget or any child has the focus
        self.__autoHide = False

        self.__tabBar.installEventFilter(self)

        self.__orientation = E5SideBar.North
        if orientation is None:
            orientation = E5SideBar.North
        self.setOrientation(orientation)

        self.__tabBar.currentChanged[int].connect(
            self.__stackedWidget.setCurrentIndex)
        e5App().focusChanged.connect(self.__appFocusChanged)
        self.__autoHideButton.toggled[bool].connect(self.__autoHideToggled)

    def setSplitter(self, splitter):
        """
        Public method to set the splitter managing the sidebar.
        
        @param splitter reference to the splitter (QSplitter)
        """
        self.splitter = splitter
        self.splitter.splitterMoved.connect(self.__splitterMoved)
        self.splitter.setChildrenCollapsible(False)
        index = self.splitter.indexOf(self)
        self.splitter.setCollapsible(index, False)

    def __splitterMoved(self, pos, index):
        """
        Private slot to react on splitter moves.
        
        @param pos new position of the splitter handle (integer)
        @param index index of the splitter handle (integer)
        """
        if self.splitter:
            self.splitterSizes = self.splitter.sizes()

    def __delayedAction(self):
        """
        Private slot to handle the firing of the delay timer.
        """
        if self.__actionMethod is not None:
            self.__actionMethod()

    def setDelay(self, delay):
        """
        Public method to set the delay value for the expand/shrink delay in
        milliseconds.
        
        @param delay value for the expand/shrink delay in milliseconds
            (integer)
        """
        self.__delayTimer.setInterval(delay)

    def delay(self):
        """
        Public method to get the delay value for the expand/shrink delay in
        milliseconds.
        
        @return value for the expand/shrink delay in milliseconds (integer)
        """
        return self.__delayTimer.interval()

    def __cancelDelayTimer(self):
        """
        Private method to cancel the current delay timer.
        """
        self.__delayTimer.stop()
        self.__actionMethod = None

    def shrink(self):
        """
        Public method to record a shrink request.
        """
        self.__delayTimer.stop()
        self.__actionMethod = self.__shrinkIt
        self.__delayTimer.start()

    def __shrinkIt(self):
        """
        Private method to shrink the sidebar.
        """
        self.__minimized = True
        self.__bigSize = self.size()
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            self.__minSize = self.minimumSizeHint().height()
            self.__maxSize = self.maximumHeight()
        else:
            self.__minSize = self.minimumSizeHint().width()
            self.__maxSize = self.maximumWidth()
        if self.splitter:
            self.splitterSizes = self.splitter.sizes()

        self.__stackedWidget.hide()

        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            self.setFixedHeight(self.__tabBar.minimumSizeHint().height())
        else:
            self.setFixedWidth(self.__tabBar.minimumSizeHint().width())

        self.__actionMethod = None

    def expand(self):
        """
        Public method to record a expand request.
        """
        self.__delayTimer.stop()
        self.__actionMethod = self.__expandIt
        self.__delayTimer.start()

    def __expandIt(self):
        """
        Private method to expand the sidebar.
        """
        self.__minimized = False
        self.__stackedWidget.show()
        self.resize(self.__bigSize)
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            minSize = max(self.__minSize, self.minimumSizeHint().height())
            self.setMinimumHeight(minSize)
            self.setMaximumHeight(self.__maxSize)
        else:
            minSize = max(self.__minSize, self.minimumSizeHint().width())
            self.setMinimumWidth(minSize)
            self.setMaximumWidth(self.__maxSize)
        if self.splitter:
            self.splitter.setSizes(self.splitterSizes)

        self.__actionMethod = None

    def isMinimized(self):
        """
        Public method to check the minimized state.
        
        @return flag indicating the minimized state (boolean)
        """
        return self.__minimized

    def isAutoHiding(self):
        """
        Public method to check, if the auto hide function is active.
        
        @return flag indicating the state of auto hiding (boolean)
        """
        return self.__autoHide

    def eventFilter(self, obj, evt):
        """
        Public method to handle some events for the tabbar.
        
        @param obj reference to the object (QObject)
        @param evt reference to the event object (QEvent)
        @return flag indicating, if the event was handled (boolean)
        """
        if obj == self.__tabBar:
            if evt.type() == QEvent.MouseButtonPress:
                pos = evt.pos()
                for i in range(self.__tabBar.count()):
                    if self.__tabBar.tabRect(i).contains(pos):
                        break

                if i == self.__tabBar.currentIndex():
                    if self.isMinimized():
                        self.expand()
                    else:
                        self.shrink()
                    return True
                elif self.isMinimized():
                    self.expand()
            elif evt.type() == QEvent.Wheel:
                delta = evt.angleDelta().y()
                if delta > 0:
                    self.prevTab()
                else:
                    self.nextTab()
                return True

        return QWidget.eventFilter(self, obj, evt)

    def addTab(self, widget, iconOrLabel, label=None):
        """
        Public method to add a tab to the sidebar.
        
        @param widget reference to the widget to add (QWidget)
        @param iconOrLabel reference to the icon or the label text of the tab
            (QIcon, string)
        @param label the labeltext of the tab (string) (only to be
            used, if the second parameter is a QIcon)
        """
        if label:
            index = self.__tabBar.addTab(iconOrLabel, label)
            self.__tabBar.setTabToolTip(index, label)
        else:
            index = self.__tabBar.addTab(iconOrLabel)
            self.__tabBar.setTabToolTip(index, iconOrLabel)
        self.__stackedWidget.addWidget(widget)
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            self.__minSize = self.minimumSizeHint().height()
        else:
            self.__minSize = self.minimumSizeHint().width()

    def insertTab(self, index, widget, iconOrLabel, label=None):
        """
        Public method to insert a tab into the sidebar.
        
        @param index the index to insert the tab at (integer)
        @param widget reference to the widget to insert (QWidget)
        @param iconOrLabel reference to the icon or the labeltext of the tab
            (QIcon, string)
        @param label the labeltext of the tab (string) (only to be
            used, if the second parameter is a QIcon)
        """
        if label:
            index = self.__tabBar.insertTab(index, iconOrLabel, label)
            self.__tabBar.setTabToolTip(index, label)
        else:
            index = self.__tabBar.insertTab(index, iconOrLabel)
            self.__tabBar.setTabToolTip(index, iconOrLabel)
        self.__stackedWidget.insertWidget(index, widget)
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            self.__minSize = self.minimumSizeHint().height()
        else:
            self.__minSize = self.minimumSizeHint().width()

    def removeTab(self, index):
        """
        Public method to remove a tab.
        
        @param index the index of the tab to remove (integer)
        """
        self.__stackedWidget.removeWidget(self.__stackedWidget.widget(index))
        self.__tabBar.removeTab(index)
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            self.__minSize = self.minimumSizeHint().height()
        else:
            self.__minSize = self.minimumSizeHint().width()

    def clear(self):
        """
        Public method to remove all tabs.
        """
        while self.count() > 0:
            self.removeTab(0)

    def prevTab(self):
        """
        Public slot used to show the previous tab.
        """
        ind = self.currentIndex() - 1
        if ind == -1:
            ind = self.count() - 1

        self.setCurrentIndex(ind)
        self.currentWidget().setFocus()

    def nextTab(self):
        """
        Public slot used to show the next tab.
        """
        ind = self.currentIndex() + 1
        if ind == self.count():
            ind = 0

        self.setCurrentIndex(ind)
        self.currentWidget().setFocus()

    def count(self):
        """
        Public method to get the number of tabs.
        
        @return number of tabs in the sidebar (integer)
        """
        return self.__tabBar.count()

    def currentIndex(self):
        """
        Public method to get the index of the current tab.
        
        @return index of the current tab (integer)
        """
        return self.__stackedWidget.currentIndex()

    def setCurrentIndex(self, index):
        """
        Public slot to set the current index.
        
        @param index the index to set as the current index (integer)
        """
        self.__tabBar.setCurrentIndex(index)
        self.__stackedWidget.setCurrentIndex(index)
        if self.isMinimized():
            self.expand()

    def currentWidget(self):
        """
        Public method to get a reference to the current widget.
        
        @return reference to the current widget (QWidget)
        """
        return self.__stackedWidget.currentWidget()

    def setCurrentWidget(self, widget):
        """
        Public slot to set the current widget.
        
        @param widget reference to the widget to become the current widget
            (QWidget)
        """
        self.__stackedWidget.setCurrentWidget(widget)
        self.__tabBar.setCurrentIndex(self.__stackedWidget.currentIndex())
        if self.isMinimized():
            self.expand()

    def indexOf(self, widget):
        """
        Public method to get the index of the given widget.
        
        @param widget reference to the widget to get the index of (QWidget)
        @return index of the given widget (integer)
        """
        return self.__stackedWidget.indexOf(widget)

    def isTabEnabled(self, index):
        """
        Public method to check, if a tab is enabled.
        
        @param index index of the tab to check (integer)
        @return flag indicating the enabled state (boolean)
        """
        return self.__tabBar.isTabEnabled(index)

    def setTabEnabled(self, index, enabled):
        """
        Public method to set the enabled state of a tab.
        
        @param index index of the tab to set (integer)
        @param enabled enabled state to set (boolean)
        """
        self.__tabBar.setTabEnabled(index, enabled)

    def orientation(self):
        """
        Public method to get the orientation of the sidebar.
        
        @return orientation of the sidebar (North, East, South, West)
        """
        return self.__orientation

    def setOrientation(self, orient):
        """
        Public method to set the orientation of the sidebar.

        @param orient orientation of the sidebar (North, East, South, West)
        """
        if orient == E5SideBar.North:
            self.__tabBar.setShape(QTabBar.RoundedNorth)
            self.__tabBar.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Preferred)
            self.barLayout.setDirection(QBoxLayout.LeftToRight)
            self.layout.setDirection(QBoxLayout.TopToBottom)
            self.layout.setAlignment(self.barLayout, Qt.AlignLeft)
        elif orient == E5SideBar.East:
            self.__tabBar.setShape(QTabBar.RoundedEast)
            self.__tabBar.setSizePolicy(QSizePolicy.Preferred,
                                        QSizePolicy.Expanding)
            self.barLayout.setDirection(QBoxLayout.TopToBottom)
            self.layout.setDirection(QBoxLayout.RightToLeft)
            self.layout.setAlignment(self.barLayout, Qt.AlignTop)
        elif orient == E5SideBar.South:
            self.__tabBar.setShape(QTabBar.RoundedSouth)
            self.__tabBar.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Preferred)
            self.barLayout.setDirection(QBoxLayout.LeftToRight)
            self.layout.setDirection(QBoxLayout.BottomToTop)
            self.layout.setAlignment(self.barLayout, Qt.AlignLeft)
        elif orient == E5SideBar.West:
            self.__tabBar.setShape(QTabBar.RoundedWest)
            self.__tabBar.setSizePolicy(QSizePolicy.Preferred,
                                        QSizePolicy.Expanding)
            self.barLayout.setDirection(QBoxLayout.TopToBottom)
            self.layout.setDirection(QBoxLayout.LeftToRight)
            self.layout.setAlignment(self.barLayout, Qt.AlignTop)
        self.__orientation = orient

    def tabIcon(self, index):
        """
        Public method to get the icon of a tab.
        
        @param index index of the tab (integer)
        @return icon of the tab (QIcon)
        """
        return self.__tabBar.tabIcon(index)

    def setTabIcon(self, index, icon):
        """
        Public method to set the icon of a tab.
        
        @param index index of the tab (integer)
        @param icon icon to be set (QIcon)
        """
        self.__tabBar.setTabIcon(index, icon)

    def tabText(self, index):
        """
        Public method to get the text of a tab.
        
        @param index index of the tab (integer)
        @return text of the tab (string)
        """
        return self.__tabBar.tabText(index)

    def setTabText(self, index, text):
        """
        Public method to set the text of a tab.
        
        @param index index of the tab (integer)
        @param text text to set (string)
        """
        self.__tabBar.setTabText(index, text)

    def tabToolTip(self, index):
        """
        Public method to get the tooltip text of a tab.
        
        @param index index of the tab (integer)
        @return tooltip text of the tab (string)
        """
        return self.__tabBar.tabToolTip(index)

    def setTabToolTip(self, index, tip):
        """
        Public method to set the tooltip text of a tab.
        
        @param index index of the tab (integer)
        @param tip tooltip text to set (string)
        """
        self.__tabBar.setTabToolTip(index, tip)

    def tabWhatsThis(self, index):
        """
        Public method to get the WhatsThis text of a tab.
        
        @param index index of the tab (integer)
        @return WhatsThis text of the tab (string)
        """
        return self.__tabBar.tabWhatsThis(index)

    def setTabWhatsThis(self, index, text):
        """
        Public method to set the WhatsThis text of a tab.
        
        @param index index of the tab (integer)
        @param text WhatsThis text to set (string)
        """
        self.__tabBar.setTabWhatsThis(index, text)

    def widget(self, index):
        """
        Public method to get a reference to the widget associated with a tab.
        
        @param index index of the tab (integer)
        @return reference to the widget (QWidget)
        """
        return self.__stackedWidget.widget(index)

    def saveState(self):
        """
        Public method to save the state of the sidebar.
        
        @return saved state as a byte array (QByteArray)
        """
        if len(self.splitterSizes) == 0:
            if self.splitter:
                self.splitterSizes = self.splitter.sizes()
            self.__bigSize = self.size()
            if self.__orientation in [E5SideBar.North, E5SideBar.South]:
                self.__minSize = self.minimumSizeHint().height()
                self.__maxSize = self.maximumHeight()
            else:
                self.__minSize = self.minimumSizeHint().width()
                self.__maxSize = self.maximumWidth()

        data = QByteArray()
        stream = QDataStream(data, QIODevice.WriteOnly)
        stream.setVersion(QDataStream.Qt_4_6)

        stream.writeUInt16(self.Version)
        stream.writeBool(self.__minimized)
        stream << self.__bigSize
        stream.writeUInt32(self.__minSize)
        stream.writeUInt32(self.__maxSize)
        stream.writeUInt32(len(self.splitterSizes))
        for size in self.splitterSizes:
            stream.writeUInt32(size)
        stream.writeBool(self.__autoHide)

        return data

    def restoreState(self, state):
        """
        Public method to restore the state of the sidebar.
        
        @param state byte array containing the saved state (QByteArray)
        @return flag indicating success (boolean)
        """
        if state.isEmpty():
            return False

        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            minSize = self.layout.minimumSize().height()
            maxSize = self.maximumHeight()
        else:
            minSize = self.layout.minimumSize().width()
            maxSize = self.maximumWidth()

        data = QByteArray(state)
        stream = QDataStream(data, QIODevice.ReadOnly)
        stream.setVersion(QDataStream.Qt_4_6)
        version = stream.readUInt16()  # version
        minimized = stream.readBool()

        if minimized and not self.__minimized:
            self.shrink()

        stream >> self.__bigSize
        if version == 1:
            self.__minSize = max(stream.readUInt16(), minSize)
            self.__maxSize = max(stream.readUInt16(), maxSize)
            count = stream.readUInt16()
            self.splitterSizes = []
            for _ in range(count):
                self.splitterSizes.append(stream.readUInt16())
        elif version == 2:
            self.__minSize = max(stream.readUInt32(), minSize)
            self.__maxSize = max(stream.readUInt32(), maxSize)
            count = stream.readUInt32()
            self.splitterSizes = []
            for _ in range(count):
                self.splitterSizes.append(stream.readUInt32())

        self.__autoHide = stream.readBool()
        self.__autoHideButton.setChecked(not self.__autoHide)

        if not minimized:
            self.expand()

        return True

    #######################################################################
    ## methods below implement the autohide functionality
    #######################################################################

    def __autoHideToggled(self, checked):
        """
        Private slot to handle the toggling of the autohide button.
        
        @param checked flag indicating the checked state of the button
            (boolean)
        """
        self.__autoHide = not checked
        if self.__autoHide:
            self.__autoHideButton.setIcon(
                UI.PixmapCache.getIcon("autoHideOn.png"))
        else:
            self.__autoHideButton.setIcon(
                UI.PixmapCache.getIcon("autoHideOff.png"))

    def __appFocusChanged(self, old, now):
        """
        Private slot to handle a change of the focus.
        
        @param old reference to the widget, that lost focus (QWidget or None)
        @param now reference to the widget having the focus (QWidget or None)
        """
        if isinstance(now, QWidget):
            self.__hasFocus = self.isAncestorOf(now)
            if (self.__autoHide and not self.__hasFocus
                    and not self.isMinimized()):
                self.shrink()
            elif self.__autoHide and self.__hasFocus and self.isMinimized():
                self.expand()

    def enterEvent(self, event):
        """
        Protected method to handle the mouse entering this widget.
        
        @param event reference to the event (QEvent)
        """
        if self.__autoHide and self.isMinimized():
            self.expand()
        else:
            self.__cancelDelayTimer()

    def leaveEvent(self, event):
        """
        Protected method to handle the mouse leaving this widget.
        
        @param event reference to the event (QEvent)
        """
        if self.__autoHide and not self.__hasFocus and not self.isMinimized():
            self.shrink()
        else:
            self.__cancelDelayTimer()

    def shutdown(self):
        """
        Public method to shut down the object.
        
        This method does some preparations so the object can be deleted
        properly. It disconnects from the focusChanged signal in order to
        avoid trouble later on.
        """
        e5App().focusChanged.disconnect(self.__appFocusChanged)
Beispiel #16
0
class FontWindow(BaseWindow):

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

        self._infoWindow = None
        self._featuresWindow = None
        self._groupsWindow = None
        self._kerningWindow = None
        self._metricsWindow = None

        self.toolBar = ToolBar(self)
        self.toolBar.setTools(
            t() for t in QApplication.instance().drawingTools())

        self.glyphCellView = GlyphCellView(self)
        self.glyphCellView.glyphActivated.connect(self.openGlyphTab)
        self.glyphCellView.glyphsDropped.connect(self._orderChanged)
        self.glyphCellView.selectionChanged.connect(self._selectionChanged)
        self.glyphCellView.setAcceptDrops(True)
        self.glyphCellView.setCellRepresentationName("TruFont.GlyphCell")
        self.glyphCellView.setFrameShape(self.glyphCellView.NoFrame)
        self.glyphCellView.setFocus()

        self.tabWidget = TabWidget(self)
        self.tabWidget.setAutoHide(True)
        self.tabWidget.setHeroFirstTab(True)
        self.tabWidget.addTab(self.tr("Font"))

        self.stackWidget = QStackedWidget(self)
        self.stackWidget.addWidget(self.glyphCellView)
        self.tabWidget.currentTabChanged.connect(self._tabChanged)
        self.tabWidget.tabRemoved.connect(
            lambda index: self.stackWidget.removeWidget(
                self.stackWidget.widget(index)))
        self.stackWidget.currentChanged.connect(self._widgetChanged)

        self.propertiesView = PropertiesView(font, self)
        self.propertiesView.hide()

        self.statusBar = StatusBar(self)
        self.statusBar.setMinimumSize(32)
        self.statusBar.setMaximumSize(128)
        self.statusBar.sizeChanged.connect(self._sizeChanged)

        self.setFont_(font)

        app = QApplication.instance()
        app.dispatcher.addObserver(
            self, "_drawingToolRegistered", "drawingToolRegistered")
        app.dispatcher.addObserver(
            self, "_drawingToolUnregistered", "drawingToolUnregistered")
        app.dispatcher.addObserver(
            self, "_glyphViewGlyphsChanged", "glyphViewGlyphsChanged")

        layout = QHBoxLayout(self)
        layout.addWidget(self.toolBar)
        vLayout = QVBoxLayout()
        vLayout.addWidget(self.tabWidget)
        pageWidget = PageWidget()
        pageWidget.addWidget(self.stackWidget)
        pageWidget.addWidget(self.statusBar)
        vLayout.addWidget(pageWidget)
        layout.addLayout(vLayout)
        layout.addWidget(self.propertiesView)
        layout.setContentsMargins(0, 2, 0, 0)
        layout.setSpacing(2)

        elements = [
            ("Ctrl+D", self.deselect),
            (platformSpecific.closeKeySequence(), self.closeGlyphTab),
            # XXX: does this really not warrant widget focus?
            (QKeySequence.Delete, self.delete),
            ("Shift+" + QKeySequence(
                QKeySequence.Delete).toString(), self.delete),
            ("Z", lambda: self.zoom(1)),
            ("X", lambda: self.zoom(-1)),
        ]
        e = platformSpecific.altDeleteSequence()
        if e is not None:
            elements.append((e, self.delete))
        e = platformSpecific.altRedoSequence()
        if e is not None:
            elements.append((e, self.redo))
        for keys, callback in elements:
            shortcut = QShortcut(QKeySequence(keys), self)
            shortcut.activated.connect(callback)

        self.installEventFilter(PreviewEventFilter(self))

        self.readSettings()
        self.propertiesView.activeLayerModified.connect(
            self._activeLayerModified)
        self.statusBar.sizeChanged.connect(self.writeSettings)

    def readSettings(self):
        geometry = settings.fontWindowGeometry()
        if geometry:
            self.restoreGeometry(geometry)
        cellSize = settings.glyphCellSize()
        self.statusBar.setSize(cellSize)
        hidden = settings.propertiesHidden()
        if not hidden:
            self.properties()

    def writeSettings(self):
        settings.setFontWindowGeometry(self.saveGeometry())
        settings.setGlyphCellSize(self.glyphCellView.cellSize()[0])
        settings.setPropertiesHidden(self.propertiesView.isHidden())

    def menuBar(self):
        return self.layout().menuBar()

    def setMenuBar(self, menuBar):
        self.layout().setMenuBar(menuBar)

    def setupMenu(self, menuBar):
        app = QApplication.instance()

        fileMenu = menuBar.fetchMenu(Entries.File)
        fileMenu.fetchAction(Entries.File_New)
        fileMenu.fetchAction(Entries.File_Open)
        fileMenu.fetchMenu(Entries.File_Open_Recent)
        if not platformSpecific.mergeOpenAndImport():
            fileMenu.fetchAction(Entries.File_Import)
        fileMenu.addSeparator()
        fileMenu.fetchAction(Entries.File_Save, self.saveFile)
        fileMenu.fetchAction(Entries.File_Save_As, self.saveFileAs)
        fileMenu.fetchAction(Entries.File_Save_All)
        fileMenu.fetchAction(Entries.File_Reload, self.reloadFile)
        fileMenu.addSeparator()
        fileMenu.fetchAction(Entries.File_Export, self.exportFile)
        fileMenu.fetchAction(Entries.File_Exit)

        editMenu = menuBar.fetchMenu(Entries.Edit)
        self._undoAction = editMenu.fetchAction(Entries.Edit_Undo, self.undo)
        self._redoAction = editMenu.fetchAction(Entries.Edit_Redo, self.redo)
        editMenu.addSeparator()
        cut = editMenu.fetchAction(Entries.Edit_Cut, self.cut)
        copy = editMenu.fetchAction(Entries.Edit_Copy, self.copy)
        copyComponent = editMenu.fetchAction(
            Entries.Edit_Copy_As_Component, self.copyAsComponent)
        paste = editMenu.fetchAction(Entries.Edit_Paste, self.paste)
        self._clipboardActions = (cut, copy, copyComponent, paste)
        editMenu.fetchAction(Entries.Edit_Select_All, self.selectAll)
        # editMenu.fetchAction(Entries.Edit_Deselect, self.deselect)
        editMenu.fetchAction(Entries.Edit_Find, self.findGlyph)
        editMenu.addSeparator()
        editMenu.fetchAction(Entries.Edit_Settings)

        viewMenu = menuBar.fetchMenu(Entries.View)
        viewMenu.fetchAction(Entries.View_Zoom_In, lambda: self.zoom(1))
        viewMenu.fetchAction(Entries.View_Zoom_Out, lambda: self.zoom(-1))
        viewMenu.fetchAction(Entries.View_Reset_Zoom, self.resetZoom)
        viewMenu.addSeparator()
        viewMenu.fetchAction(
            Entries.View_Next_Tab, lambda: self.tabOffset(1))
        viewMenu.fetchAction(
            Entries.View_Previous_Tab, lambda: self.tabOffset(-1))
        viewMenu.fetchAction(
            Entries.View_Next_Glyph, lambda: self.glyphOffset(1))
        viewMenu.fetchAction(
            Entries.View_Previous_Glyph, lambda: self.glyphOffset(-1))
        viewMenu.fetchAction(
            Entries.View_Layer_Up, lambda: self.layerOffset(-1))
        viewMenu.fetchAction(
            Entries.View_Layer_Down, lambda: self.layerOffset(1))
        viewMenu.addSeparator()
        viewMenu.fetchAction(Entries.View_Show_Points)
        viewMenu.fetchAction(Entries.View_Show_Metrics)
        viewMenu.fetchAction(Entries.View_Show_Images)
        viewMenu.fetchAction(Entries.View_Show_Guidelines)

        fontMenu = menuBar.fetchMenu(Entries.Font)
        fontMenu.fetchAction(Entries.Font_Font_Info, self.fontInfo)
        fontMenu.fetchAction(Entries.Font_Font_Features, self.fontFeatures)
        fontMenu.addSeparator()
        fontMenu.fetchAction(Entries.Font_Add_Glyphs, self.addGlyphs)
        fontMenu.fetchAction(Entries.Font_Sort, self.sortGlyphs)

        # glyphMenu = menuBar.fetchMenu(self.tr("&Glyph"))
        # self._layerAction = glyphMenu.fetchAction(
        #     self.tr("&Layer Actions…"), self.layerActions, "L")

        menuBar.fetchMenu(Entries.Scripts)

        windowMenu = menuBar.fetchMenu(Entries.Window)
        windowMenu.fetchAction(Entries.Window_Groups, self.groups)
        windowMenu.fetchAction(Entries.Window_Kerning, self.kerning)
        windowMenu.fetchAction(Entries.Window_Metrics, self.metrics)
        windowMenu.fetchAction(Entries.Window_Scripting)
        windowMenu.fetchAction(Entries.Window_Properties, self.properties)
        windowMenu.addSeparator()
        action = windowMenu.fetchAction(Entries.Window_Output)
        action.setEnabled(app.outputWindow is not None)

        helpMenu = menuBar.fetchMenu(Entries.Help)
        helpMenu.fetchAction(Entries.Help_Documentation)
        helpMenu.fetchAction(Entries.Help_Report_An_Issue)
        helpMenu.addSeparator()
        helpMenu.fetchAction(Entries.Help_About)

        self._updateGlyphActions()

    # --------------
    # Custom methods
    # --------------

    def font_(self):
        return self._font

    def setFont_(self, font):
        if self._font is not None:
            self._font.removeObserver(self, "Font.Changed")
            self._font.removeObserver(self, "Font.GlyphOrderChanged")
            self._font.removeObserver(self, "Font.SortDescriptorChanged")
        self._font = font
        self.setWindowTitle(self.fontTitle())
        if font is None:
            return
        self._updateGlyphsFromGlyphOrder()
        font.addObserver(self, "_fontChanged", "Font.Changed")
        font.addObserver(
            self, "_glyphOrderChanged", "Font.GlyphOrderChanged")
        font.addObserver(
            self, "_sortDescriptorChanged", "Font.SortDescriptorChanged")

    def fontTitle(self):
        if self._font is None:
            return None
        path = self._font.path or self._font.binaryPath
        if path is not None:
            return os.path.basename(path.rstrip(os.sep))
        return self.tr("Untitled")

    def isGlyphTab(self):
        return bool(self.stackWidget.currentIndex())

    def openGlyphTab(self, glyph):
        # if a tab with this glyph exists already, switch to it
        for index in range(self.stackWidget.count()):
            if not index:
                continue
            view = self.stackWidget.widget(index)
            if list(view.glyphs()) == [glyph]:
                self.tabWidget.setCurrentTab(index)
                return
        # spawn
        widget = GlyphCanvasView(self)
        widget.setInputNames([glyph.name])
        widget.activeGlyphChanged.connect(self._selectionChanged)
        widget.glyphNamesChanged.connect(self._namesChanged)
        widget.pointSizeModified.connect(self.statusBar.setSize)
        widget.toolModified.connect(self.toolBar.setCurrentTool)
        # add
        self.tabWidget.addTab(_textForGlyphs([glyph]))
        self.stackWidget.addWidget(widget)
        # activate
        self.tabWidget.setCurrentTab(-1)

    def closeGlyphTab(self):
        index = self.stackWidget.currentIndex()
        if index:
            self.tabWidget.removeTab(index)

    def maybeSaveBeforeExit(self):
        if self._font.dirty:
            ret = CloseMessageBox.getCloseDocument(self, self.fontTitle())
            if ret == QMessageBox.Save:
                self.saveFile()
                return True
            elif ret == QMessageBox.Discard:
                return True
            return False
        return True

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

    # app

    def _drawingToolRegistered(self, notification):
        toolClass = notification.data["tool"]
        index = self.stackWidget.currentIndex()
        parent = self.stackWidget.currentWidget() if index else None
        self.toolBar.addTool(toolClass(parent=parent))

    def _drawingToolUnregistered(self, notification):
        toolClass = notification.data["tool"]
        for tool in self.toolBar.tools():
            if isinstance(tool, toolClass):
                self.toolBar.removeTool(tool)
                return
        raise ValueError(
            "couldn't find tool to unregister: {}".format(toolClass))

    def _glyphViewGlyphsChanged(self, notification):
        self._updateGlyphActions()

    # widgets

    def _activeLayerModified(self):
        if self.isGlyphTab():
            widget = self.stackWidget.currentWidget()
            index = self.sender().currentIndex().row()
            layers = self._font.layers
            layer = layers[layers.layerOrder[index]]
            currentGlyph = widget.activeGlyph()
            # XXX: adjust TLayer.get and use it
            if currentGlyph.name in layer:
                glyph = layer[currentGlyph.name]
            else:
                glyph = layer.newGlyph(currentGlyph.name)
            widget.setActiveGlyph(glyph)

    def _namesChanged(self):
        sender = self.sender()
        index = self.stackWidget.indexOf(sender)
        self.tabWidget.setTabName(index, _textForGlyphs(sender.glyphs()))

    def _sizeChanged(self):
        size = self.statusBar.size()
        if self.isGlyphTab():
            widget = self.stackWidget.currentWidget()
            widget.setPointSize(size)
        else:
            self.glyphCellView.setCellSize(size)

    def _tabChanged(self, index):
        self.statusBar.setShouldPropagateSize(not index)
        # we need to hide, then setParent, then show
        self.stackWidget.currentWidget().hide()
        newWidget = self.stackWidget.widget(index)
        if index:
            for tool in self.toolBar.tools():
                tool.setParent(newWidget)
        self.stackWidget.setCurrentIndex(index)
        newWidget.setFocus(Qt.OtherFocusReason)

    def _toolChanged(self, tool):
        widget = self.stackWidget.currentWidget()
        ok = widget.setCurrentTool(tool)
        # the glyph view NAKed the change (in mouseDown)
        # set back the current tool in the toolbar
        if not ok:
            self.toolBar.setCurrentTool(widget.currentTool())

    def _widgetChanged(self, index):
        # update current glyph
        self._updateCurrentGlyph()
        # update undo/redo
        self._updateGlyphActions()
        # update slider
        if self.isGlyphTab():
            lo, hi, unit = 0, 900000, " pt"
            widget = self.stackWidget.currentWidget()
            size = widget.pointSize()
        else:
            lo, hi, unit = 32, 128, None
            size = self.glyphCellView.cellSize()[0]
        self.statusBar.setMinimumSize(lo)
        self.statusBar.setMaximumSize(hi)
        self.statusBar.setSize(size)
        self.statusBar.setUnit(unit)
        self.statusBar.setTextVisible(not self.isGlyphTab())
        # update and connect setCurrentTool
        try:
            self.toolBar.currentToolChanged.disconnect()
        except TypeError:
            pass
        if not index:
            return
        widget = self.stackWidget.currentWidget()
        widget.setCurrentTool(self.toolBar.currentTool())
        self.toolBar.currentToolChanged.connect(self._toolChanged)

    def _orderChanged(self):
        # TODO: reimplement when we start showing glyph subsets
        glyphs = self.glyphCellView.glyphs()
        self._font.glyphOrder = [glyph.name for glyph in glyphs]

    def _selectionChanged(self):
        if self.isGlyphTab():
            activeGlyph = self.stackWidget.currentWidget().activeGlyph()
        else:
            activeGlyph = self.glyphCellView.lastSelectedGlyph()
            # selection text
            # TODO: this should probably be internal to the label
            selection = self.glyphCellView.selection()
            if selection is not None:
                count = len(selection)
                if count == 1:
                    glyph = self.glyphCellView.glyphsForIndexes(selection)[0]
                    text = "%s " % glyph.name
                else:
                    text = ""
                if count:
                    text = self.tr("{0}(%n selected)".format(text), n=count)
            else:
                text = ""
            self.statusBar.setText(text)
        # currentGlyph
        app = QApplication.instance()
        app.setCurrentGlyph(activeGlyph)
        # actions
        self._updateGlyphActions()

    # defcon

    def _fontChanged(self, notification):
        font = notification.object
        self.setWindowModified(font.dirty)

    def _glyphOrderChanged(self, notification):
        self._updateGlyphsFromGlyphOrder()

    def _updateGlyphsFromGlyphOrder(self):
        font = self._font
        glyphOrder = font.glyphOrder
        if glyphOrder:
            glyphCount = 0
            glyphs = []
            for glyphName in glyphOrder:
                if glyphName in font:
                    glyph = font[glyphName]
                    glyphCount += 1
                else:
                    glyph = font.get(glyphName, asTemplate=True)
                glyphs.append(glyph)
            if glyphCount < len(font):
                # if some glyphs in the font are not present in the glyph
                # order, loop again to add them at the end
                for glyph in font:
                    if glyph not in glyphs:
                        glyphs.append(glyph)
                font.disableNotifications(observer=self)
                font.glyphOrder = [glyph.name for glyph in glyphs]
                font.enableNotifications(observer=self)
        else:
            glyphs = list(font)
            font.disableNotifications(observer=self)
            font.glyphOrder = [glyph.name for glyph in glyphs]
            font.enableNotifications(observer=self)
        self.glyphCellView.setGlyphs(glyphs)

    def _sortDescriptorChanged(self, notification):
        font = notification.object
        descriptors = notification.data["newValue"]
        if descriptors is None:
            return
        if descriptors[0]["type"] == "glyphSet":
            glyphNames = descriptors[0]["glyphs"]
        else:
            glyphNames = font.unicodeData.sortGlyphNames(
                font.keys(), descriptors)
        font.glyphOrder = glyphNames

    # ------------
    # Menu methods
    # ------------

    # File

    def saveFile(self, path=None, ufoFormatVersion=3):
        if path is None and self._font.path is None:
            self.saveFileAs()
        else:
            if path is None:
                path = self._font.path
            self._font.save(path, ufoFormatVersion)

    def saveFileAs(self):
        fileFormats = OrderedDict([
            (self.tr("UFO Font version 3 {}").format("(*.ufo)"), 3),
            (self.tr("UFO Font version 2 {}").format("(*.ufo)"), 2),
        ])
        state = settings.saveFileDialogState()
        path = self._font.path or self._font.binaryPath
        if path:
            directory = os.path.dirname(path)
        else:
            directory = None if state else QStandardPaths.standardLocations(
                QStandardPaths.DocumentsLocation)[0]
        # TODO: switch to directory dlg on platforms that need it
        dialog = QFileDialog(
            self, self.tr("Save File"), directory,
            ";;".join(fileFormats.keys()))
        if state:
            dialog.restoreState(state)
        dialog.setAcceptMode(QFileDialog.AcceptSave)
        if directory:
            dialog.setDirectory(directory)
        ok = dialog.exec_()
        settings.setSaveFileDialogState(dialog.saveState())
        if ok:
            nameFilter = dialog.selectedNameFilter()
            path = dialog.selectedFiles()[0]
            if not os.path.basename(path).endswith(".ufo"):
                path += ".ufo"
            self.saveFile(path, fileFormats[nameFilter])
            app = QApplication.instance()
            app.setCurrentFile(self._font.path)
            self.setWindowTitle(self.fontTitle())
        # return ok

    def reloadFile(self):
        font = self._font
        path = font.path or font.binaryPath
        if not font.dirty or path is None:
            return
        if not ReloadMessageBox.getReloadDocument(self, self.fontTitle()):
            return
        if font.path is not None:
            font.reloadInfo()
            font.reloadKerning()
            font.reloadGroups()
            font.reloadFeatures()
            font.reloadLib()
            font.reloadGlyphs(font.keys())
            font.dirty = False
        else:
            # TODO: we should do this in-place
            font_ = font.__class__().new()
            font_.extract(font.binaryPath)
            self.setFont_(font_)

    def exportFile(self):
        params, ok = ExportDialog.getExportParameters(self, self._font)
        if not ok:
            return
        baseName = params['baseName']
        directory = params['exportDirectory']
        compression = set(map(str.lower, params['compression']))
        for format in map(str.lower, params['formats']):
            fileName = "{}.{}".format(baseName, format)
            path = os.path.join(directory, fileName)
            try:
                self._font.export(path, format, compression=compression)
            except Exception as e:
                msg = self.tr(
                    "This font’s feature file contains an error."
                    ) if isinstance(e, FeatureLibError) else None
                errorReports.showCriticalException(e, message=msg)

    # Edit

    def undo(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
        else:
            glyph = widget.lastSelectedGlyph()
        glyph.undo()

    def redo(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
        else:
            glyph = widget.lastSelectedGlyph()
        glyph.redo()

    def cut(self):
        self.copy()
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            deleteUISelection(glyph)
        else:
            glyphs = widget.glyphs()
            for index in widget.selection():
                glyph = glyphs[index]
                glyph.clear()

    def copy(self):
        font = self._font
        widget = self.stackWidget.currentWidget()
        clipboard = QApplication.clipboard()
        mimeData = QMimeData()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            copyGlyph = glyph.getRepresentation("TruFont.FilterSelection")
            packGlyphs = (copyGlyph,)
        else:
            glyphs = self.glyphCellView.glyphs()
            packGlyphs = (
                glyphs[index] for index in sorted(
                    self.glyphCellView.selection()))

        svgGlyphs = []
        pickled = []
        for i, glyph in enumerate(packGlyphs):
            pickled.append(glyph.serialize(
                blacklist=("name", "unicodes")
            ))

            pen = SVGPathPen(font)
            glyph.draw(pen)
            col = i % 5
            row = i // 5
            g = '<g transform="matrix(1,0,0,-1,%f,%f)"><path d="%s"/></g>' % (
                    font.info.unitsPerEm * col, font.info.unitsPerEm * row,
                    pen.getCommands())
            svgGlyphs.append(g)

        mimeData.setData("application/x-trufont-glyph-data",
                         pickle.dumps(pickled))

        svg = """\
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
 "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg">
%s
</svg>
""" % "\n".join(svgGlyphs)
        mimeData.setData("image/svg+xml", svg.encode("utf-8"))

        clipboard.setMimeData(mimeData)

    def copyAsComponent(self):
        if self.isGlyphTab():
            pass
        else:
            glyphs = self.glyphCellView.glyphs()
            pickled = []
            for index in self.glyphCellView.selection():
                glyph = glyphs[index]
                componentGlyph = glyph.__class__()
                componentGlyph.width = glyph.width
                component = componentGlyph.instantiateComponent()
                component.baseGlyph = glyph.name
                pickled.append(componentGlyph.serialize())
            clipboard = QApplication.clipboard()
            mimeData = QMimeData()
            mimeData.setData("application/x-trufont-glyph-data",
                             pickle.dumps(pickled))
            clipboard.setMimeData(mimeData)

    def paste(self):
        isGlyphTab = self.isGlyphTab()
        widget = self.stackWidget.currentWidget()
        if isGlyphTab:
            glyphs = (widget.activeGlyph(),)
        else:
            selection = self.glyphCellView.selection()
            glyphs = widget.glyphsForIndexes(selection)
        clipboard = QApplication.clipboard()
        mimeData = clipboard.mimeData()
        if mimeData.hasFormat("application/x-trufont-glyph-data"):
            data = pickle.loads(mimeData.data(
                "application/x-trufont-glyph-data"))
            if len(data) == len(glyphs):
                for pickled, glyph in zip(data, glyphs):
                    if isGlyphTab:
                        pasteGlyph = glyph.__class__()
                        pasteGlyph.deserialize(pickled)
                        # TODO: if we serialize selected state, we don't need
                        # to do this
                        pasteGlyph.selected = True
                        if len(pasteGlyph) or len(pasteGlyph.components) or \
                                len(pasteGlyph.anchors):
                            glyph.beginUndoGroup()
                            glyph.holdNotifications()
                            count = len(glyph)
                            pen = glyph.getPointPen()
                            # contours, components
                            pasteGlyph.drawPoints(pen)
                            for contour in glyph[count:]:
                                contour.selected = True
                            # anchors
                            for anchor in pasteGlyph.anchors:
                                glyph.appendAnchor(dict(anchor))
                            # guidelines
                            for guideline in pasteGlyph.guidelines:
                                glyph.appendGuideline(dict(guideline))
                            glyph.releaseHeldNotifications()
                            glyph.endUndoGroup()
                    else:
                        glyph.deserialize(pickled)
            return
        if mimeData.hasFormat("image/svg+xml"):
            if len(glyphs) == 1:
                glyph = glyphs[0]
                try:
                    svgPath = SVGPath.fromstring(
                        mimeData.data("image/svg+xml"))
                except:
                    pass
                else:
                    glyph.beginUndoGroup()
                    if not isGlyphTab:
                        glyph.clear()
                    svgPath.draw(glyph.getPen())
                    glyph.endUndoGroup()
                    return
        if mimeData.hasText():
            if len(glyphs) == 1:
                glyph = glyphs[0]
                otherGlyph = glyph.__class__()
                text = mimeData.text()
                try:
                    readGlyphFromString(
                        text, otherGlyph, otherGlyph.getPointPen())
                except:
                    try:
                        svgPath = SVGPath.fromstring(text)
                        svgPath.draw(otherGlyph.getPen())
                    except:
                        return
                glyph.beginUndoGroup()
                if not isGlyphTab:
                    glyph.clear()
                otherGlyph.drawPoints(glyph.getPointPen())
                glyph.endUndoGroup()

    def selectAll(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            if glyph.selected:
                for anchor in glyph.anchors:
                    anchor.selected = True
                for component in glyph.components:
                    component.selected = True
            else:
                glyph.selected = True
        else:
            widget.selectAll()

    def deselect(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            for anchor in glyph.anchors:
                anchor.selected = False
            for component in glyph.components:
                component.selected = False
            glyph.selected = False
        else:
            widget.setSelection(set())

    def delete(self):
        modifiers = QApplication.keyboardModifiers()
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            # TODO: fuse more the two methods, they're similar and delete is
            # Cut except not putting in the clipboard
            if modifiers & Qt.AltModifier:
                deleteUISelection(glyph)
            else:
                preserveShape = not modifiers & Qt.ShiftModifier
                removeUIGlyphElements(glyph, preserveShape)
        else:
            erase = modifiers & Qt.ShiftModifier
            if self._proceedWithDeletion(erase):
                glyphs = widget.glyphsForIndexes(widget.selection())
                for glyph in glyphs:
                    font = glyph.font
                    for layer in font.layers:
                        if glyph.name in layer:
                            defaultLayer = layer[glyph.name] == glyph
                            if defaultLayer and not erase:
                                # TODO: clear in glyph.template setter?
                                glyph.clear()
                                glyph.template = True
                            else:
                                del layer[glyph.name]

    def findGlyph(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            newGlyph, ok = FindDialog.getNewGlyph(self, glyph)
            if ok and newGlyph is not None:
                widget.setActiveGlyph(newGlyph)
        else:
            pass  # XXX

    # View

    def zoom(self, step):
        if self.isGlyphTab():
            widget = self.stackWidget.currentWidget()
            newScale = widget.scale() * pow(1.2, step)
            widget.zoom(newScale)
            self.statusBar.setSize(widget.pointSize())
        else:
            value = self.statusBar.size()
            newValue = value + 10 * step
            self.statusBar.setSize(newValue)

    def resetZoom(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            widget.fitScaleBBox()
        else:
            settings.removeGlyphCellSize()
            cellSize = settings.glyphCellSize()
            self.statusBar.setSize(cellSize)

    def tabOffset(self, value):
        tab = self.tabWidget.currentTab()
        newTab = (tab + value) % len(self.tabWidget.tabs())
        self.tabWidget.setCurrentTab(newTab)

    def glyphOffset(self, value):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            currentGlyph = widget.activeGlyph()
            font = currentGlyph.font
            glyphOrder = font.glyphOrder
            # should be enforced in fontView already
            if not (glyphOrder and len(glyphOrder)):
                return
            index = glyphOrder.index(currentGlyph.name)
            newIndex = (index + value) % len(glyphOrder)
            glyph = font[glyphOrder[newIndex]]
            widget.setActiveGlyph(glyph)
        else:
            lastSelectedCell = widget.lastSelectedCell()
            if lastSelectedCell is None:
                return
            newIndex = lastSelectedCell + value
            if newIndex < 0 or newIndex >= len(widget.glyphs()):
                return
            widget.setSelection({newIndex})

    def layerOffset(self, value):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            currentGlyph = widget.activeGlyph()
            layerSet, layer = currentGlyph.layerSet, currentGlyph.layer
            if None in (layerSet, layer):
                return
            index = layerSet.layerOrder.index(layer.name)
            newIndex = (index + value) % len(layerSet)
            layer_ = layerSet[layerSet.layerOrder[newIndex]]
            if layer_ == layer:
                return
            # XXX: fix get
            # glyph = layer_.get(currentGlyph.name)
            if currentGlyph.name in layer_:
                glyph = layer_[currentGlyph.name]
            else:
                glyph = layer_.newGlyph(currentGlyph.name)
            widget.setActiveGlyph(glyph)

    # Font

    def fontInfo(self):
        # If a window is already opened, bring it to the front, else spawn one.
        # TODO: see about using widget.setAttribute(Qt.WA_DeleteOnClose)
        # otherwise it seems we're just leaking memory after each close...
        # (both raise_ and show allocate memory instead of using the hidden
        # widget it seems)
        if self._infoWindow is not None and self._infoWindow.isVisible():
            self._infoWindow.raise_()
        else:
            self._infoWindow = FontInfoWindow(self._font, self)
            self._infoWindow.show()

    def fontFeatures(self):
        # TODO: see up here
        if self._featuresWindow is not None and self._featuresWindow.isVisible(
                ):
            self._featuresWindow.raise_()
        else:
            self._featuresWindow = FontFeaturesWindow(self._font, self)
            self._featuresWindow.show()

    def addGlyphs(self):
        glyphs = self.glyphCellView.glyphs()
        newGlyphNames, params, ok = AddGlyphsDialog.getNewGlyphNames(
            self, glyphs)
        if ok:
            sortFont = params.pop("sortFont")
            for name in newGlyphNames:
                glyph = self._font.get(name, **params)
                if glyph is not None:
                    glyphs.append(glyph)
            self.glyphCellView.setGlyphs(glyphs)
            if sortFont:
                # TODO: when the user add chars from a glyphSet and no others,
                # should we try to sort according to that glyphSet?
                # The above would probably warrant some rearchitecturing.
                # kick-in the sort mechanism
                self._font.sortDescriptor = self._font.sortDescriptor

    def sortGlyphs(self):
        sortDescriptor, ok = SortDialog.getDescriptor(
            self, self._font.sortDescriptor)
        if ok:
            self._font.sortDescriptor = sortDescriptor

    # Window

    def groups(self):
        # TODO: see up here
        if self._groupsWindow is not None and self._groupsWindow.isVisible():
            self._groupsWindow.raise_()
        else:
            self._groupsWindow = GroupsWindow(self._font, self)
            self._groupsWindow.show()

    def kerning(self):
        # TODO: see up here
        if self._kerningWindow is not None and self._kerningWindow.isVisible():
            self._kerningWindow.raise_()
        else:
            self._kerningWindow = KerningWindow(self._font, self)
            self._kerningWindow.show()

    def metrics(self):
        # TODO: see up here
        if self._metricsWindow is not None and self._metricsWindow.isVisible():
            self._metricsWindow.raise_()
        else:
            self._metricsWindow = MetricsWindow(self._font)
            # XXX: need proper, fast windowForFont API!
            self._metricsWindow._fontWindow = self
            self.destroyed.connect(self._metricsWindow.close)
            self._metricsWindow.show()
        # TODO: default string kicks-in on the window before this. Figure out
        # how to make a clean interface
        selection = self.glyphCellView.selection()
        if selection:
            glyphs = self.glyphCellView.glyphsForIndexes(selection)
            self._metricsWindow.setGlyphs(glyphs)

    def properties(self):
        shouldBeVisible = self.propertiesView.isHidden()
        self.propertiesView.setVisible(shouldBeVisible)
        self.writeSettings()

    # update methods

    def _setGlyphPreview(self, value):
        index = self.stackWidget.currentIndex()
        if index:
            widget = self.stackWidget.currentWidget()
            widget.setPreviewEnabled(value)

    def _updateCurrentGlyph(self):
        # TODO: refactor this pattern...
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
        else:
            glyph = widget.lastSelectedGlyph()
        if glyph is not None:
            app = QApplication.instance()
            app.setCurrentGlyph(glyph)

    def _updateGlyphActions(self):
        if not hasattr(self, "_undoAction"):
            return
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            currentGlyph = widget.activeGlyph()
        else:
            currentGlyph = widget.lastSelectedGlyph()
        # disconnect eventual signal of previous glyph
        objects = (
            (self._undoAction, self.undo),
            (self._redoAction, self.redo),
        )
        for action, slot in objects:
            try:
                action.disconnect()
            except TypeError:
                pass
            action.triggered.connect(slot)
        # now update status
        if currentGlyph is None:
            self._undoAction.setEnabled(False)
            self._redoAction.setEnabled(False)
        else:
            undoManager = currentGlyph.undoManager
            self._undoAction.setEnabled(currentGlyph.canUndo())
            undoManager.canUndoChanged.connect(self._undoAction.setEnabled)
            self._redoAction.setEnabled(currentGlyph.canRedo())
            undoManager.canRedoChanged.connect(self._redoAction.setEnabled)
        # and other actions
        for action in self._clipboardActions:
            action.setEnabled(currentGlyph is not None)

    # helper

    def _proceedWithDeletion(self, erase=False):
            if not self.glyphCellView.selection():
                return
            tr = self.tr("Delete") if erase else self.tr("Clear")
            text = self.tr("Do you want to %s selected glyphs?") % tr.lower()
            closeDialog = QMessageBox(
                QMessageBox.Question, "",
                self.tr("%s glyphs") % tr,
                QMessageBox.Yes | QMessageBox.No, self)
            closeDialog.setInformativeText(text)
            closeDialog.setModal(True)
            ret = closeDialog.exec_()
            if ret == QMessageBox.Yes:
                return True
            return False

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

    def setWindowTitle(self, title):
        if platformSpecific.appNameInTitle():
            title += " – TruFont"
        super().setWindowTitle("[*]{}".format(title))

    def sizeHint(self):
        return QSize(1270, 800)

    def moveEvent(self, event):
        self.writeSettings()

    resizeEvent = moveEvent

    def showEvent(self, event):
        app = QApplication.instance()
        data = dict(
            font=self._font,
            window=self,
        )
        app.postNotification("fontWindowWillOpen", data)
        super().showEvent(event)
        app.postNotification("fontWindowOpened", data)

    def closeEvent(self, event):
        ok = self.maybeSaveBeforeExit()
        if ok:
            app = QApplication.instance()
            data = dict(
                font=self._font,
                window=self,
            )
            app.postNotification("fontWindowWillClose", data)
            self._font.removeObserver(self, "Font.Changed")
            app = QApplication.instance()
            app.dispatcher.removeObserver(self, "drawingToolRegistered")
            app.dispatcher.removeObserver(self, "drawingToolUnregistered")
            app.dispatcher.removeObserver(self, "glyphViewGlyphsChanged")
            event.accept()
        else:
            event.ignore()

    def event(self, event):
        if event.type() == QEvent.WindowActivate:
            app = QApplication.instance()
            app.setCurrentFontWindow(self)
            self._updateCurrentGlyph()
        return super().event(event)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.fillRect(event.rect(), QColor(212, 212, 212))
Beispiel #17
0
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.initUI()
        self.stacked_widget.currentChanged.connect(self.set_button_state)
        self.next_button.clicked.connect(self.next_page)
        self.prev_button.clicked.connect(self.prev_page)


    def initUI(self):
        self.next_button = QPushButton('Next')
        self.prev_button = QPushButton('Previous')
        self.next_button.setEnabled(False)
        self.prev_button.setEnabled(False)
        self.stacked_widget = QStackedWidget()
        
        widget = QtWidgets.QWidget()
        self.setCentralWidget(widget)

        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(self.prev_button)
        hbox.addWidget(self.next_button)

        vbox = QVBoxLayout()
        vbox.addWidget(self.stacked_widget)
        vbox.addLayout(hbox)
        
        h = HomeScreen(self)
        self.insert_page(h.label1)
        
        navigation = Navigation(sample, self)
        self.insert_page(navigation.horizontalGroupBox)

        #add the toolbar to the main window
        self.toolbar = navigation.toolbar
        self.addToolBar(self.toolbar)
        
        #start with the toolbar hidden
        self.toolbar.toggleViewAction().setChecked(True)
        self.toolbar.toggleViewAction().trigger()
        
        # create main layout
        widget.setLayout(vbox)


    def set_button_state(self, index):
        self.prev_button.setEnabled(index > 0)
        n_pages = len(self.stacked_widget)
        self.next_button.setEnabled( index % n_pages < n_pages - 1)
        
        
    def insert_page(self, widget, index=-1):
        self.stacked_widget.insertWidget(index, widget)
        self.set_button_state(self.stacked_widget.currentIndex())
        

    def next_page(self):
        new_index = self.stacked_widget.currentIndex()+1
        if new_index < len(self.stacked_widget):
            self.stacked_widget.setCurrentIndex(new_index)
            self.toolbar.toggleViewAction().trigger()
            

    def prev_page(self):
        new_index = self.stacked_widget.currentIndex()-1
        if new_index >= 0:
            self.stacked_widget.setCurrentIndex(new_index)
            self.toolbar.toggleViewAction().trigger()
Beispiel #18
0
class MainWin(QMainWindow):
    def __init__(self):
        super().__init__()
        self.wait_times = [10, 30, 60]
        self.push_wait = []  # list of wait buttons
        self.displaystat_hdr = []  # TODO replace with OrderedDict?
        self.displaystat_val = []
        self.statlabels = OrderedDict()  # statname: (namelabel, vallabel)
        # create widgets
        w = QWidget(self)
        self.central_widget = w
        self.time_lbl = QLabel(w)
        self.date_lbl = QLabel(w)
        self.location_lbl = QLabel(w)
        self.energy_bar = QProgressBar(w)
        self.arousal_bar = QProgressBar(w)
        self.widget_stack = QStackedWidget(self)
        self.location_view = LocationView(self)
        self.school_management = SchoolManagement(self)

        # create layout
        self.retranslateUi()
        grid = QGridLayout(self.central_widget)
        grid.setContentsMargins(0, 0, 0, 0)
        grid.addWidget(self.time_lbl, 0, 5, 1, 2)
        grid.addWidget(self.date_lbl, 0, 3, 1, 2)
        grid.addWidget(self.energy_bar, 0, 7, 1, 5)
        grid.addWidget(self.arousal_bar, 1, 7, 1, 5)
        grid.addWidget(self.location_lbl, 2, 0, 2, 3)

        for i, stat in enumerate(self.displaystat_hdr):
            val = self.displaystat_val[i]
            statlabel = QLabel(stat, w)
            vallabel = QLabel(val, w)
            self.statlabels[stat] = (statlabel, vallabel)
            grid.addWidget(statlabel, 2, 3 + i, 1, 1)
            grid.addWidget(vallabel, 3, 3 + i, 1, 1)
            # TODO: style
#            displaystat_hdr[i].setPalette(HHStyle::hdr_text);
#            displaystat_hdr[i].setAlignment(Qt::AlignCenter);
#            displaystat_val[i].setPalette(HHStyle::white_text);
#            displaystat_val[i].setAlignment(Qt::AlignCenter);

        self.widget_stack.addWidget(self.location_view)
        self.widget_stack.addWidget(self.school_management)

        grid.addWidget(self.widget_stack, 4, 0, 50, 12)

        self.setCentralWidget(w)

        # configure window
        self.setWindowTitle("pyprinciple")
        geom = QDesktopWidget().availableGeometry()
        self.setGeometry(100, 50, 800, 600)

        # configure widgets
        w.setContentsMargins(0, 0, 0, 0)
        w.setObjectName("central_widget")

        geom.setHeight(geom.height() * 0.98)

        self.time_lbl.setObjectName("text")  # use text style from style sheet
        self.time_lbl.setFont(style.big_font)
        self.time_lbl.setAlignment(Qt.AlignCenter)
        self.date_lbl.setObjectName("text")
        self.date_lbl.setFont(style.big_font)
        self.date_lbl.setAlignment(Qt.AlignRight)
        self.location_lbl.setObjectName("text")
        self.location_lbl.setFont(style.location_font)

        #        self.gridW.setContentsMargins(0, 0, 0, 0)
        #        gridW.setGeometry(geom)
        self.energy_bar.setValue(60)
        self.energy_bar.setObjectName("energy")
        self.arousal_bar.setValue(48)
        self.arousal_bar.setObjectName("arousal")

        geom.setHeight(geom.height() * 10 / 11)  # removes space for header
        self.location_lbl.setGeometry(geom)
        self.school_management.setGeometry(geom)

        self.widget_stack.setCurrentIndex(0)

        # TODO only shown if window contains locationview
        for i in range(0, len(self.wait_times)):
            pw = QPushButton("Wait %d min" % self.wait_times[i])
            self.push_wait.append(pw)
            grid.addWidget(pw, 0, i, 2, 1)

        for name in world.peopleAt("Your Home"):
            self.location_view.addPerson(Person(name))

        self.retranslateUi()

        self.show()

    def retranslateUi(self):
        tra = QApplication.translate
        ctxt = "MainWin"
        self.setWindowTitle(tra(ctxt, "Main window"))
        # TODO: use ordered dict instead
        displaystat = [
            "Education", "Happiness", "Loyalty", "Inhibition", "Lust",
            "Corruption", "Reputation", "Students", "Money"
        ]
        self.displaystat_hdr = [tra(ctxt, stat) for stat in displaystat]
        self.displaystat_val = [tra(ctxt, "30.1") for stat in displaystat[:-2]]
        self.displaystat_val.append(tra(ctxt, "91"))
        self.displaystat_val.append(tra(ctxt, "$10,000"))

        for waittime, pushbu in zip(self.wait_times, self.push_wait):
            pushbu.setText(tra(ctxt, "Wait %s min" % waittime))

        # TODO: translating dates and times is easier via datetime objects
        self.time_lbl.setText(tra(ctxt, "8:00"))
        self.date_lbl.setText(tra(ctxt, "Monday (1/1/2017)"))
        self.location_lbl.setText(tra(ctxt, "Home"))

    @pyqtSlot()
    def toggle_school_management(self):
        if self.widget_stack.currentIndex() is 1:
            self.widget_stack.setCurrentIndex(0)
        else:
            self.widget_stack.setCurrentIndex(1)
Beispiel #19
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setMinimumSize(
            int(QApplication.primaryScreen().size().width() * 0.1),
            int(QApplication.primaryScreen().size().height() * 0.2))
        self.resize(int(QApplication.primaryScreen().size().width() * 0.3),
                    int(QApplication.primaryScreen().size().height() * 0.5))
        window_width = int(QApplication.primaryScreen().size().width() * 0.3)
        id = QFontDatabase.addApplicationFont(variables.FONT_FILE)
        family = QFontDatabase.applicationFontFamilies(id)[0]
        variables.font = QFont(family, variables.FONT_SIZE)
        variables.font_small = QFont(family, variables.FONT_SIZE_SMALL)
        variables.nw = network.network()
        variables.signals = variables.DialogSignals()
        variables.signals.create_dialog.connect(self.createDialog)
        variables.signals.open_dialog.connect(self.openDialog)
        variables.signals.close_dialog.connect(self.closeDialog)
        variables.signals.message_sent.connect(self.messageSent)
        variables.nw.received.connect(self.receiveMessage)
        variables.nw.undelivered.connect(self.undelivered)
        variables.nw.delivered.connect(self.delivered)
        variables.nw.read.connect(self.read)
        variables.nw.reconnect.connect(self.clientReconnected)
        self.dialogs = []
        self.dialog_menu = DialogList()
        self.main_widget = QStackedWidget()
        self.main_widget.addWidget(self.dialog_menu)
        self.setCentralWidget(self.main_widget)
        self.setWindowTitle(variables.APP_NAME)
        self.setWindowIcon(QIcon(variables.LOGO_IMG))
        self.dialog_menu.ip_input.setFocus()

    def resizeEvent(self, event):
        super(MainWindow, self).resizeEvent(event)
        variables.window_width = self.size().width()
        for i in range(len(self.dialogs)):
            self.dialogs[i].width_changed.emit()

    def createDialog(self, ip):
        self.dialogs.append(Dialog(ip))

    def openDialog(self, id):
        self.main_widget.addWidget(self.dialogs[id])
        self.main_widget.setCurrentIndex(1)
        self.dialog_menu.model.set_status(id, variables.STATUS_READ)
        self.dialogs[id].send_input.setFocus()
        self.dialogs[id].model.send_read()

    def closeDialog(self):
        self.main_widget.setCurrentIndex(0)
        self.main_widget.removeWidget(self.main_widget.widget(1))

    def receiveMessage(self, msg, ip):
        for i in range(len(self.dialogs)):
            if self.dialogs[i].ip == ip:
                self.dialogs[i].message_from(msg)
                self.dialog_menu.model.last_msg(i, msg, variables.USER_THEM)
                if self.main_widget.currentIndex(
                ) == 1 and self.main_widget.currentWidget().ip == ip:
                    self.dialogs[i].model.send_read()
                else:
                    self.dialog_menu.model.set_status(i, variables.STATUS_NEW)

    def messageSent(self, msg, ip):
        for i in range(len(self.dialogs)):
            if self.dialogs[i].ip == ip:
                self.dialog_menu.model.last_msg(i, msg, variables.USER_THEM)

    def undelivered(self, id, ip):
        for i in range(len(self.dialogs)):
            if self.dialogs[i].ip == ip:
                self.dialogs[i].model.setStatus(id,
                                                variables.STATUS_UNDELIVERED)
                self.dialog_menu.model.set_status(i,
                                                  variables.STATUS_UNDELIVERED)

    def delivered(self, id, ip):
        for i in range(len(self.dialogs)):
            if self.dialogs[i].ip == ip:
                self.dialogs[i].model.setStatus(id, variables.STATUS_UNREAD)
                self.dialog_menu.model.set_status(i, variables.STATUS_UNREAD)

    def read(self, id, ip):
        for i in range(len(self.dialogs)):
            if self.dialogs[i].ip == ip:
                self.dialogs[i].model.setStatus(id, variables.STATUS_READ)
                self.dialog_menu.model.set_status(i, variables.STATUS_READ)

    def clientReconnected(self, ip):
        for i in range(len(self.dialogs)):
            if self.dialogs[i].ip == ip:
                self.dialogs[i].model.new_base()
Beispiel #20
0
class _ToolsDock(ui_tools.StyledBar):
    def __init__(self, parent=None):
        super().__init__(parent)
        # Register signals connections
        # connections = (
        #    {
        #        "target": "main_container",
        #        "signal_name": "runFile",
        #        "slot": self.execute_file
        #    },
        # )
        # Buttons Widget
        self._buttons_widget = ui_tools.StyledBar()
        self._buttons_widget.setProperty("border", True)
        self._buttons_widget.setFixedHeight(26)
        self._buttons_widget.setLayout(QHBoxLayout())
        self._buttons_widget.layout().setContentsMargins(2, 2, 0, 2)
        self._buttons_widget.layout().setSpacing(10)

        IDE.register_service('tools_dock', self)

    def install(self):
        self.setup_ui()
        ide = IDE.get_service('ide')
        ide.place_me_on('tools_dock', self, 'central')
        ui_tools.install_shortcuts(self, actions.ACTIONS, ide)
        ide.goingDown.connect(self._save_settings)
        settings = IDE.ninja_settings()
        index = int(settings.value("tools_dock/tool_visible", -1))
        if index == -1:
            self.hide()
        else:
            self.set_current_index(index)

    def setup_ui(self):
        self._stack = QStackedWidget()
        self.__current_widget = None
        self.__last_index = -1
        self.__buttons = []

        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(0)

        # toolbar = StyledBar()
        tool_layout = QVBoxLayout()
        tool_layout.setContentsMargins(0, 0, 0, 0)
        tool_layout.setSpacing(0)

        self._run_widget = run_widget.RunWidget()

        main_layout.addLayout(tool_layout)
        self._tool_stack = QStackedWidget()
        self._tool_stack.setMaximumHeight(22)
        # self._tool_stack.setMaximumWidth(22)

        tool_layout.addWidget(self._tool_stack)
        # FIXME: poner en stack
        # clear_btn = QToolButton()
        # clear_btn.setIcon(QIcon(self.style().standardIcon(QStyle.SP_LineEditClearButton)))
        # clear_btn.clicked.connect(self._run_widget.output.clear)
        # tool_layout.addWidget(clear_btn)
        # tool_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding))
        # tool_layout.addWidget(close_button)
        # main_layout.addWidget(toolbar)
        main_layout.addWidget(self._stack)

        # Widgets
        # errors_tree = IDE.get_service('errors_tree')
        from ninja_ide.gui.tools_dock import find_in_files
        self.widgets = [
            self._run_widget,
            console_widget.ConsoleWidget(),
            find_in_files.FindInFilesWidget(),
            # errors_tree
        ]
        # Intall widgets
        number = 1
        for wi in self.widgets:
            if wi is None:
                continue
            btn = ToolButton(number, wi.display_name())
            btn.setCheckable(True)
            # Action
            # action = QAction(wi.display_name(), self)
            # action.triggered.connect(self._triggered)
            number += 1
            self.__buttons.append(btn)
            self._buttons_widget.layout().addWidget(btn)
            self._stack.addWidget(wi)
            btn.clicked.connect(self._button_triggered)
            # Toolbar buttons
            container = QWidget(self._tool_stack)
            tool_buttons_layout = QHBoxLayout()
            tool_buttons_layout.setContentsMargins(0, 0, 0, 0)
            tool_buttons_layout.setSpacing(0)
            for b in wi.button_widgets():
                tool_buttons_layout.addWidget(b)
            tool_buttons_layout.addStretch(5)
            container.setLayout(tool_buttons_layout)
            self._tool_stack.addWidget(container)

        self._buttons_widget.layout().addItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding))
        self.__current_widget = self._stack.currentWidget()

        self.set_current_index(0)

    def execute_file(self):
        """Execute the current file"""

        main_container = IDE.get_service("main_container")
        editor_widget = main_container.get_current_editor()
        if editor_widget is not None and editor_widget.is_modified or \
                editor_widget.file_path:
            main_container.save_file(editor_widget)
            # FIXME: Emit a signal for plugin!
            # self.fileExecuted.emit(editor_widget.file_path)
            file_path = editor_widget.file_path
            extension = file_manager.get_file_extension(file_path)
            # TODO: Remove the IF statment and use Handlers
            if extension == "py":
                self._run_application(file_path)

    def _run_application(self, filename):
        """Execute the process to run the application"""

        self._show(0)  # Show widget in index = 0
        self._run_widget.start_process(filename)
        self._run_widget.input.setFocus()

    @pyqtSlot()
    def _button_triggered(self):
        # Get ToolButton index
        button = self.sender()
        index = self.__buttons.index(button)

        if index == self.current_index() and self._is_current_visible():
            self._hide()
        else:
            self._show(index)

    def _show(self, index):
        self.show()
        self.widgets[index].setVisible(True)
        self.__current_widget = self.widgets[index]
        self.set_current_index(index)

    def _hide(self):
        self.__current_widget.setVisible(False)
        index = self.current_index()
        self.__buttons[index].setChecked(False)
        self.widgets[index].setVisible(False)
        self.hide()

    def showEvent(self, event):
        super().showEvent(event)
        self._stack.currentWidget().setFocus()

    def set_current_index(self, index):

        if self.__last_index != -1:
            self.__buttons[self.__last_index].setChecked(False)
            self.__buttons[index].setChecked(True)

        if index != -1:
            self._stack.setCurrentIndex(index)
            self._tool_stack.setCurrentIndex(index)

            tool = self.widgets[index]
            tool.setVisible(True)
        self.__last_index = index

    def current_index(self):
        return self._stack.currentIndex()

    def _is_current_visible(self):
        return self.__current_widget and self.__current_widget.isVisible()

    def _save_settings(self):
        settings = IDE.ninja_settings()
        visible_index = self.current_index()
        if not self.isVisible():
            visible_index = -1
        settings.setValue("tools_dock/tool_visible", visible_index)
Beispiel #21
0
class TabBarWindow(TabWindow):
    """Implementation which uses a separate QTabBar and QStackedWidget.
    The Tab bar is placed next to the menu bar to save real estate."""
    def __init__(self, app, **kwargs):
        super().__init__(app, **kwargs)

    def _setupUi(self):
        self.setWindowTitle(self.app.NAME)
        self.resize(640, 480)
        self.tabBar = QTabBar()
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self._setupActions()
        self._setupMenu()

        self.centralWidget = QWidget(self)
        self.setCentralWidget(self.centralWidget)
        self.stackedWidget = QStackedWidget()
        self.centralWidget.setLayout(self.verticalLayout)
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.addWidget(self.menubar, 0, Qt.AlignTop)
        self.horizontalLayout.addWidget(self.tabBar, 0, Qt.AlignTop)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.verticalLayout.addWidget(self.stackedWidget)

        self.tabBar.currentChanged.connect(self.showTabIndex)
        self.tabBar.tabCloseRequested.connect(self.onTabCloseRequested)

        self.stackedWidget.currentChanged.connect(self.updateMenuBar)
        self.stackedWidget.widgetRemoved.connect(self.onRemovedWidget)

        self.tabBar.setTabsClosable(True)
        self.restoreGeometry()

    def addTab(self, page, title, switch=True):
        stack_index = self.stackedWidget.addWidget(page)
        self.tabBar.insertTab(stack_index, title)

        if isinstance(page, DirectoriesDialog):
            self.tabBar.setTabButton(
                stack_index, QTabBar.RightSide, None)
        if switch:  # switch to the added tab immediately upon creation
            self.setTabIndex(stack_index)
        return stack_index

    @pyqtSlot(int)
    def showTabIndex(self, index):
        # The tab bar's indices should be aligned with the stackwidget's
        if index >= 0 and index <= self.stackedWidget.count():
            self.stackedWidget.setCurrentIndex(index)

    def indexOfWidget(self, widget):
        # Warning: this may return -1 if widget is not a child of stackedwidget
        return self.stackedWidget.indexOf(widget)

    def setCurrentIndex(self, tab_index):
        self.setTabIndex(tab_index)
        # The signal will handle switching the stackwidget's widget
        # self.stackedWidget.setCurrentWidget(self.stackedWidget.widget(tab_index))

    def setCurrentWidget(self, widget):
        """Sets the current Tab on TabBar for this widget."""
        self.tabBar.setCurrentIndex(self.indexOfWidget(widget))

    @pyqtSlot(int)
    def setTabIndex(self, index):
        if index is None:
            return
        self.tabBar.setCurrentIndex(index)

    @pyqtSlot(int)
    def onRemovedWidget(self, index):
        self.removeTab(index)

    @pyqtSlot(int)
    def removeTab(self, index):
        """Remove the tab, but not the widget (it should already be removed)"""
        return self.tabBar.removeTab(index)

    @pyqtSlot(int)
    def removeWidget(self, widget):
        return self.stackedWidget.removeWidget(widget)

    def isTabVisible(self, index):
        return self.tabBar.isTabVisible(index)

    def getCurrentIndex(self):
        return self.stackedWidget.currentIndex()

    def getWidgetAtIndex(self, index):
        return self.stackedWidget.widget(index)

    def getCount(self):
        return self.stackedWidget.count()

    @pyqtSlot()
    def toggleTabBar(self):
        value = self.sender().isChecked()
        self.actionToggleTabs.setChecked(value)
        self.tabBar.setVisible(value)

    @pyqtSlot(int)
    def onTabCloseRequested(self, index):
        target_widget = self.getWidgetAtIndex(index)
        if isinstance(target_widget, DirectoriesDialog):
            # On MacOS, the tab has a close button even though we explicitely
            # set it to None in order to hide it. This should prevent
            # the "Directories" tab from closing by mistake.
            return
        # target_widget.close()  # seems unnecessary
        # Removing the widget should trigger tab removal via the signal
        self.removeWidget(self.getWidgetAtIndex(index))

    @pyqtSlot()
    def onDialogAccepted(self):
        """Remove tabbed dialog when Accepted/Done (close button clicked)."""
        widget = self.sender()
        self.removeWidget(widget)
Beispiel #22
0
class PyMultiPageWidget(QWidget):
    currentIndexChanged = pyqtSignal(int)
    pageTitleChanged = pyqtSignal(str)

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

        self.comboBox = QComboBox()
        self.comboBox.setObjectName('__qt__passive_comboBox')
        self.stackWidget = QStackedWidget()
        self.comboBox.activated.connect(self.setCurrentIndex)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.comboBox)
        self.layout.addWidget(self.stackWidget)
        self.setLayout(self.layout)

    def sizeHint(self):
        return QSize(200, 150)

    def count(self):
        return self.stackWidget.count()

    def widget(self, index):
        return self.stackWidget.widget(index)

    @pyqtSlot(QWidget)
    def addPage(self, page):
        self.insertPage(self.count(), page)

    @pyqtSlot(int, QWidget)
    def insertPage(self, index, page):
        page.setParent(self.stackWidget)
        self.stackWidget.insertWidget(index, page)
        title = page.windowTitle()
        if title == "":
            title = "Page %d" % (self.comboBox.count() + 1)
            page.setWindowTitle(title)
        self.comboBox.insertItem(index, title)

    @pyqtSlot(int)
    def removePage(self, index):
        widget = self.stackWidget.widget(index)
        self.stackWidget.removeWidget(widget)
        self.comboBox.removeItem(index)

    def getPageTitle(self):
        cw = self.stackWidget.currentWidget()
        return cw.windowTitle() if cw is not None else ''

    @pyqtSlot(str)
    def setPageTitle(self, newTitle):
        cw = self.stackWidget.currentWidget()
        if cw is not None:
            self.comboBox.setItemText(self.getCurrentIndex(), newTitle)
            cw.setWindowTitle(newTitle)
            self.pageTitleChanged.emit(newTitle)

    def getCurrentIndex(self):
        return self.stackWidget.currentIndex()

    @pyqtSlot(int)
    def setCurrentIndex(self, index):
        if index != self.getCurrentIndex():
            self.stackWidget.setCurrentIndex(index)
            self.comboBox.setCurrentIndex(index)
            self.currentIndexChanged.emit(index)

    pageTitle = pyqtProperty(str, fget=getPageTitle, fset=setPageTitle, stored=False)
    currentIndex = pyqtProperty(int, fget=getCurrentIndex, fset=setCurrentIndex)
Beispiel #23
0
class PyMultiPageWidget(QWidget):

    currentIndexChanged = pyqtSignal(int)

    pageTitleChanged = pyqtSignal(str)

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

        self.comboBox = QComboBox()
        # MAGIC
        # It is important that the combo box has an object name beginning
        # with '__qt__passive_', otherwise, it is inactive in the form editor
        # of the designer and you can't change the current page via the
        # combo box.
        # MAGIC
        self.comboBox.setObjectName('__qt__passive_comboBox')
        self.stackWidget = QStackedWidget()
        self.comboBox.activated.connect(self.setCurrentIndex)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.comboBox)
        self.layout.addWidget(self.stackWidget)
        self.setLayout(self.layout)

    def sizeHint(self):
        return QSize(200, 150)

    def count(self):
        return self.stackWidget.count()

    def widget(self, index):
        return self.stackWidget.widget(index)

    @pyqtSlot(QWidget)
    def addPage(self, page):
        self.insertPage(self.count(), page)

    @pyqtSlot(int, QWidget)
    def insertPage(self, index, page):
        page.setParent(self.stackWidget)
        self.stackWidget.insertWidget(index, page)
        title = page.windowTitle()
        if title == "":
            title = "Page %d" % (self.comboBox.count() + 1)
            page.setWindowTitle(title)
        self.comboBox.insertItem(index, title)

    @pyqtSlot(int)
    def removePage(self, index):
        widget = self.stackWidget.widget(index)
        self.stackWidget.removeWidget(widget)
        self.comboBox.removeItem(index)

    def getPageTitle(self):
        cw = self.stackWidget.currentWidget()
        return cw.windowTitle() if cw is not None else ''

    @pyqtSlot(str)
    def setPageTitle(self, newTitle):
        cw = self.stackWidget.currentWidget()
        if cw is not None:
            self.comboBox.setItemText(self.getCurrentIndex(), newTitle)
            cw.setWindowTitle(newTitle)
            self.pageTitleChanged.emit(newTitle)

    def getCurrentIndex(self):
        return self.stackWidget.currentIndex()

    @pyqtSlot(int)
    def setCurrentIndex(self, index):
        if index != self.getCurrentIndex():
            self.stackWidget.setCurrentIndex(index)
            self.comboBox.setCurrentIndex(index)
            self.currentIndexChanged.emit(index)

    pageTitle = pyqtProperty(str,
                             fget=getPageTitle,
                             fset=setPageTitle,
                             stored=False)
    currentIndex = pyqtProperty(int,
                                fget=getCurrentIndex,
                                fset=setCurrentIndex)
class CityWidget(QWidget):
    def __init__(self,
                 name: str,
                 units: UnitSystem = UnitSystem.metric,
                 foreground_color="#ffffff",
                 font_name=""):
        super(CityWidget, self).__init__()

        self.city_name = name
        self.units = units
        self.foreground_color = foreground_color
        self.font_name = font_name

        self.label_style = "QLabel { color : " + self.foreground_color + "; }"
        self.high_label_style = "QLabel { color : #ffba26; }"
        self.button_style = "QPushButton { background-color: " + self.foreground_color + "; border: 2px; " \
                                                                                         "border-radius: 20px; " \
                                                                                         "border-style: outset;}"
        self.line_style = "QLabel { background-color: " + self.foreground_color + "; border: 2px; border-radius: 4px;}"

        self.font = QFont(font_name, 30, QFont.Bold)
        self.font_small = QFont(font_name, 20, QFont.Bold)

        self.weather_data = {}
        self.main_layout = QVBoxLayout()
        self.view_stack = QStackedWidget()
        self.main_layout.addWidget(self.view_stack)

        self.temp_view = SimpleTempViewWidget(self, units,
                                              self.foreground_color,
                                              self.font_name)
        self.view_stack.addWidget(self.temp_view)
        self.wind_view = SimpleWindViewWidget(self, units,
                                              self.foreground_color,
                                              self.font_name)
        self.view_stack.addWidget(self.wind_view)

        self.separator_line = QHBoxLayout()
        self.main_layout.addLayout(self.separator_line)

        self.line = QLabel()
        self.line.setStyleSheet(self.line_style)
        self.line.setFixedSize(640, 10)
        self.separator_line.addStretch(1)
        self.separator_line.addWidget(self.line)
        self.separator_line.addStretch(1)

        self.bottom_line = QHBoxLayout()
        self.main_layout.addLayout(self.bottom_line)

        self.left_button = QPushButton("<")
        self.left_button.setFixedSize(40, 80)
        self.left_button.setStyleSheet(self.button_style)

        self.bottom_widget_name_backward = QLabel()
        self.set_label_style(self.bottom_widget_name_backward, self.font_small,
                             self.label_style)
        self.bottom_widget_name_current = QLabel()
        self.set_label_style(self.bottom_widget_name_current, self.font,
                             self.high_label_style)
        self.bottom_widget_name_foreward = QLabel()
        self.set_label_style(self.bottom_widget_name_foreward, self.font_small,
                             self.label_style)

        self.right_button = QPushButton(">")
        self.right_button.setFixedSize(40, 80)
        self.right_button.setStyleSheet(self.button_style)

        self.bottom_line.addWidget(self.left_button)
        self.bottom_line.addStretch(1)
        self.bottom_line.addWidget(self.bottom_widget_name_backward)
        self.bottom_line.addSpacing(30)
        self.bottom_line.addWidget(self.bottom_widget_name_current)
        self.bottom_line.addSpacing(30)
        self.bottom_line.addWidget(self.bottom_widget_name_foreward)
        self.bottom_line.addStretch(1)
        self.bottom_line.addWidget(self.right_button)

        self.setLayout(self.main_layout)
        self.set_bottom_line()

        # Timer
        self.switch_timer = QTimer(self)
        self.switch_timer.setInterval(10000)  # 10 seconds
        self.switch_timer.timeout.connect(lambda: self.toggle_main_widget(+1))
        self.switch_timer.start()

        self.left_button.clicked.connect(
            lambda state: self.toggle_main_widget(-1))
        self.right_button.clicked.connect(
            lambda state: self.toggle_main_widget(+1))

    def set_units(self, units: UnitSystem):
        self.units = units

    def get_units(self) -> UnitSystem:
        return self.units.name

    def switch_view(self, index: int = 0):
        max_value = self.self.view_stack.count()
        self.self.view_stack.setCurrentIndex(index % max_value)

    def set_data(self, data: dict):
        for i in range(0, self.view_stack.count()):
            self.view_stack.widget(i).set_data(data)

    def get_name(self) -> str:
        return self.city_name

    def set_label_style(self, label: QLabel, font: QFont, style: str):
        label.setStyleSheet(style)
        label.setFont(font)

    def set_bottom_line(self):
        max_value = self.view_stack.count()
        current = self.view_stack.currentIndex()
        if max_value == 1:
            self.bottom_widget_name_backward.setText("")
            self.bottom_widget_name_current.setText(
                self.view_stack.widget(current).get_name())
            self.bottom_widget_name_foreward.setText("")
            return
        self.bottom_widget_name_backward.setText(
            self.view_stack.widget((current - 1) % max_value).get_name())
        self.bottom_widget_name_current.setText(
            self.view_stack.widget(current).get_name())
        self.bottom_widget_name_foreward.setText(
            self.view_stack.widget((current + 1) % max_value).get_name())

    def toggle_main_widget(self, index: int):
        max_value = self.view_stack.count()
        current = self.view_stack.currentIndex()
        self.view_stack.setCurrentIndex((current + index) % max_value)
        self.set_bottom_line()
Beispiel #25
0
class E5SideBar(QWidget):
    """
    Class implementing a sidebar with a widget area, that is hidden or shown,
    if the current tab is clicked again.
    """
    Version = 1
    
    North = 0
    East = 1
    South = 2
    West = 3
    
    def __init__(self, orientation=None, delay=200, parent=None):
        """
        Constructor
        
        @param orientation orientation of the sidebar widget (North, East,
            South, West)
        @param delay value for the expand/shrink delay in milliseconds
            (integer)
        @param parent parent widget (QWidget)
        """
        super(E5SideBar, self).__init__(parent)
        
        self.__tabBar = QTabBar()
        self.__tabBar.setDrawBase(True)
        self.__tabBar.setShape(QTabBar.RoundedNorth)
        self.__tabBar.setUsesScrollButtons(True)
        self.__tabBar.setDrawBase(False)
        self.__stackedWidget = QStackedWidget(self)
        self.__stackedWidget.setContentsMargins(0, 0, 0, 0)
        self.__autoHideButton = QToolButton()
        self.__autoHideButton.setCheckable(True)
        self.__autoHideButton.setIcon(
            UI.PixmapCache.getIcon("autoHideOff.png"))
        self.__autoHideButton.setChecked(True)
        self.__autoHideButton.setToolTip(
            self.tr("Deselect to activate automatic collapsing"))
        self.barLayout = QBoxLayout(QBoxLayout.LeftToRight)
        self.barLayout.setContentsMargins(0, 0, 0, 0)
        self.layout = QBoxLayout(QBoxLayout.TopToBottom)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)
        self.barLayout.addWidget(self.__autoHideButton)
        self.barLayout.addWidget(self.__tabBar)
        self.layout.addLayout(self.barLayout)
        self.layout.addWidget(self.__stackedWidget)
        self.setLayout(self.layout)
        
        # initialize the delay timer
        self.__actionMethod = None
        self.__delayTimer = QTimer(self)
        self.__delayTimer.setSingleShot(True)
        self.__delayTimer.setInterval(delay)
        self.__delayTimer.timeout.connect(self.__delayedAction)
        
        self.__minimized = False
        self.__minSize = 0
        self.__maxSize = 0
        self.__bigSize = QSize()
        
        self.splitter = None
        self.splitterSizes = []
        
        self.__hasFocus = False
        # flag storing if this widget or any child has the focus
        self.__autoHide = False
        
        self.__tabBar.installEventFilter(self)
        
        self.__orientation = E5SideBar.North
        if orientation is None:
            orientation = E5SideBar.North
        self.setOrientation(orientation)
        
        self.__tabBar.currentChanged[int].connect(
            self.__stackedWidget.setCurrentIndex)
        e5App().focusChanged[QWidget, QWidget].connect(self.__appFocusChanged)
        self.__autoHideButton.toggled[bool].connect(self.__autoHideToggled)
    
    def setSplitter(self, splitter):
        """
        Public method to set the splitter managing the sidebar.
        
        @param splitter reference to the splitter (QSplitter)
        """
        self.splitter = splitter
        self.splitter.splitterMoved.connect(self.__splitterMoved)
        self.splitter.setChildrenCollapsible(False)
        index = self.splitter.indexOf(self)
        self.splitter.setCollapsible(index, False)
    
    def __splitterMoved(self, pos, index):
        """
        Private slot to react on splitter moves.
        
        @param pos new position of the splitter handle (integer)
        @param index index of the splitter handle (integer)
        """
        if self.splitter:
            self.splitterSizes = self.splitter.sizes()
    
    def __delayedAction(self):
        """
        Private slot to handle the firing of the delay timer.
        """
        if self.__actionMethod is not None:
            self.__actionMethod()
    
    def setDelay(self, delay):
        """
        Public method to set the delay value for the expand/shrink delay in
        milliseconds.
        
        @param delay value for the expand/shrink delay in milliseconds
            (integer)
        """
        self.__delayTimer.setInterval(delay)
    
    def delay(self):
        """
        Public method to get the delay value for the expand/shrink delay in
        milliseconds.
        
        @return value for the expand/shrink delay in milliseconds (integer)
        """
        return self.__delayTimer.interval()
    
    def __cancelDelayTimer(self):
        """
        Private method to cancel the current delay timer.
        """
        self.__delayTimer.stop()
        self.__actionMethod = None
    
    def shrink(self):
        """
        Public method to record a shrink request.
        """
        self.__delayTimer.stop()
        self.__actionMethod = self.__shrinkIt
        self.__delayTimer.start()
   
    def __shrinkIt(self):
        """
        Private method to shrink the sidebar.
        """
        self.__minimized = True
        self.__bigSize = self.size()
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            self.__minSize = self.minimumSizeHint().height()
            self.__maxSize = self.maximumHeight()
        else:
            self.__minSize = self.minimumSizeHint().width()
            self.__maxSize = self.maximumWidth()
        if self.splitter:
            self.splitterSizes = self.splitter.sizes()
        
        self.__stackedWidget.hide()
        
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            self.setFixedHeight(self.__tabBar.minimumSizeHint().height())
        else:
            self.setFixedWidth(self.__tabBar.minimumSizeHint().width())
        
        self.__actionMethod = None
    
    def expand(self):
        """
        Public method to record a expand request.
        """
        self.__delayTimer.stop()
        self.__actionMethod = self.__expandIt
        self.__delayTimer.start()
    
    def __expandIt(self):
        """
        Private method to expand the sidebar.
        """
        self.__minimized = False
        self.__stackedWidget.show()
        self.resize(self.__bigSize)
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            minSize = max(self.__minSize, self.minimumSizeHint().height())
            self.setMinimumHeight(minSize)
            self.setMaximumHeight(self.__maxSize)
        else:
            minSize = max(self.__minSize, self.minimumSizeHint().width())
            self.setMinimumWidth(minSize)
            self.setMaximumWidth(self.__maxSize)
        if self.splitter:
            self.splitter.setSizes(self.splitterSizes)
        
        self.__actionMethod = None
    
    def isMinimized(self):
        """
        Public method to check the minimized state.
        
        @return flag indicating the minimized state (boolean)
        """
        return self.__minimized
    
    def isAutoHiding(self):
        """
        Public method to check, if the auto hide function is active.
        
        @return flag indicating the state of auto hiding (boolean)
        """
        return self.__autoHide
    
    def eventFilter(self, obj, evt):
        """
        Public method to handle some events for the tabbar.
        
        @param obj reference to the object (QObject)
        @param evt reference to the event object (QEvent)
        @return flag indicating, if the event was handled (boolean)
        """
        if obj == self.__tabBar:
            if evt.type() == QEvent.MouseButtonPress:
                pos = evt.pos()
                for i in range(self.__tabBar.count()):
                    if self.__tabBar.tabRect(i).contains(pos):
                        break
                
                if i == self.__tabBar.currentIndex():
                    if self.isMinimized():
                        self.expand()
                    else:
                        self.shrink()
                    return True
                elif self.isMinimized():
                    self.expand()
            elif evt.type() == QEvent.Wheel:
                if qVersion() >= "5.0.0":
                    delta = evt.angleDelta().y()
                else:
                    delta = evt.delta()
                if delta > 0:
                    self.prevTab()
                else:
                    self.nextTab()
                return True
        
        return QWidget.eventFilter(self, obj, evt)
    
    def addTab(self, widget, iconOrLabel, label=None):
        """
        Public method to add a tab to the sidebar.
        
        @param widget reference to the widget to add (QWidget)
        @param iconOrLabel reference to the icon or the label text of the tab
            (QIcon, string)
        @param label the labeltext of the tab (string) (only to be
            used, if the second parameter is a QIcon)
        """
        if label:
            index = self.__tabBar.addTab(iconOrLabel, label)
            self.__tabBar.setTabToolTip(index, label)
        else:
            index = self.__tabBar.addTab(iconOrLabel)
            self.__tabBar.setTabToolTip(index, iconOrLabel)
        self.__stackedWidget.addWidget(widget)
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            self.__minSize = self.minimumSizeHint().height()
        else:
            self.__minSize = self.minimumSizeHint().width()
    
    def insertTab(self, index, widget, iconOrLabel, label=None):
        """
        Public method to insert a tab into the sidebar.
        
        @param index the index to insert the tab at (integer)
        @param widget reference to the widget to insert (QWidget)
        @param iconOrLabel reference to the icon or the labeltext of the tab
            (QIcon, string)
        @param label the labeltext of the tab (string) (only to be
            used, if the second parameter is a QIcon)
        """
        if label:
            index = self.__tabBar.insertTab(index, iconOrLabel, label)
            self.__tabBar.setTabToolTip(index, label)
        else:
            index = self.__tabBar.insertTab(index, iconOrLabel)
            self.__tabBar.setTabToolTip(index, iconOrLabel)
        self.__stackedWidget.insertWidget(index, widget)
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            self.__minSize = self.minimumSizeHint().height()
        else:
            self.__minSize = self.minimumSizeHint().width()
    
    def removeTab(self, index):
        """
        Public method to remove a tab.
        
        @param index the index of the tab to remove (integer)
        """
        self.__stackedWidget.removeWidget(self.__stackedWidget.widget(index))
        self.__tabBar.removeTab(index)
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            self.__minSize = self.minimumSizeHint().height()
        else:
            self.__minSize = self.minimumSizeHint().width()
    
    def clear(self):
        """
        Public method to remove all tabs.
        """
        while self.count() > 0:
            self.removeTab(0)
    
    def prevTab(self):
        """
        Public slot used to show the previous tab.
        """
        ind = self.currentIndex() - 1
        if ind == -1:
            ind = self.count() - 1
            
        self.setCurrentIndex(ind)
        self.currentWidget().setFocus()
    
    def nextTab(self):
        """
        Public slot used to show the next tab.
        """
        ind = self.currentIndex() + 1
        if ind == self.count():
            ind = 0
            
        self.setCurrentIndex(ind)
        self.currentWidget().setFocus()
    
    def count(self):
        """
        Public method to get the number of tabs.
        
        @return number of tabs in the sidebar (integer)
        """
        return self.__tabBar.count()
    
    def currentIndex(self):
        """
        Public method to get the index of the current tab.
        
        @return index of the current tab (integer)
        """
        return self.__stackedWidget.currentIndex()
    
    def setCurrentIndex(self, index):
        """
        Public slot to set the current index.
        
        @param index the index to set as the current index (integer)
        """
        self.__tabBar.setCurrentIndex(index)
        self.__stackedWidget.setCurrentIndex(index)
        if self.isMinimized():
            self.expand()
    
    def currentWidget(self):
        """
        Public method to get a reference to the current widget.
        
        @return reference to the current widget (QWidget)
        """
        return self.__stackedWidget.currentWidget()
    
    def setCurrentWidget(self, widget):
        """
        Public slot to set the current widget.
        
        @param widget reference to the widget to become the current widget
            (QWidget)
        """
        self.__stackedWidget.setCurrentWidget(widget)
        self.__tabBar.setCurrentIndex(self.__stackedWidget.currentIndex())
        if self.isMinimized():
            self.expand()
    
    def indexOf(self, widget):
        """
        Public method to get the index of the given widget.
        
        @param widget reference to the widget to get the index of (QWidget)
        @return index of the given widget (integer)
        """
        return self.__stackedWidget.indexOf(widget)
    
    def isTabEnabled(self, index):
        """
        Public method to check, if a tab is enabled.
        
        @param index index of the tab to check (integer)
        @return flag indicating the enabled state (boolean)
        """
        return self.__tabBar.isTabEnabled(index)
    
    def setTabEnabled(self, index, enabled):
        """
        Public method to set the enabled state of a tab.
        
        @param index index of the tab to set (integer)
        @param enabled enabled state to set (boolean)
        """
        self.__tabBar.setTabEnabled(index, enabled)
    
    def orientation(self):
        """
        Public method to get the orientation of the sidebar.
        
        @return orientation of the sidebar (North, East, South, West)
        """
        return self.__orientation
    
    def setOrientation(self, orient):
        """
        Public method to set the orientation of the sidebar.

        @param orient orientation of the sidebar (North, East, South, West)
        """
        if orient == E5SideBar.North:
            self.__tabBar.setShape(QTabBar.RoundedNorth)
            self.__tabBar.setSizePolicy(
                QSizePolicy.Expanding, QSizePolicy.Preferred)
            self.barLayout.setDirection(QBoxLayout.LeftToRight)
            self.layout.setDirection(QBoxLayout.TopToBottom)
            self.layout.setAlignment(self.barLayout, Qt.AlignLeft)
        elif orient == E5SideBar.East:
            self.__tabBar.setShape(QTabBar.RoundedEast)
            self.__tabBar.setSizePolicy(
                QSizePolicy.Preferred, QSizePolicy.Expanding)
            self.barLayout.setDirection(QBoxLayout.TopToBottom)
            self.layout.setDirection(QBoxLayout.RightToLeft)
            self.layout.setAlignment(self.barLayout, Qt.AlignTop)
        elif orient == E5SideBar.South:
            self.__tabBar.setShape(QTabBar.RoundedSouth)
            self.__tabBar.setSizePolicy(
                QSizePolicy.Expanding, QSizePolicy.Preferred)
            self.barLayout.setDirection(QBoxLayout.LeftToRight)
            self.layout.setDirection(QBoxLayout.BottomToTop)
            self.layout.setAlignment(self.barLayout, Qt.AlignLeft)
        elif orient == E5SideBar.West:
            self.__tabBar.setShape(QTabBar.RoundedWest)
            self.__tabBar.setSizePolicy(
                QSizePolicy.Preferred, QSizePolicy.Expanding)
            self.barLayout.setDirection(QBoxLayout.TopToBottom)
            self.layout.setDirection(QBoxLayout.LeftToRight)
            self.layout.setAlignment(self.barLayout, Qt.AlignTop)
        self.__orientation = orient
    
    def tabIcon(self, index):
        """
        Public method to get the icon of a tab.
        
        @param index index of the tab (integer)
        @return icon of the tab (QIcon)
        """
        return self.__tabBar.tabIcon(index)
    
    def setTabIcon(self, index, icon):
        """
        Public method to set the icon of a tab.
        
        @param index index of the tab (integer)
        @param icon icon to be set (QIcon)
        """
        self.__tabBar.setTabIcon(index, icon)
    
    def tabText(self, index):
        """
        Public method to get the text of a tab.
        
        @param index index of the tab (integer)
        @return text of the tab (string)
        """
        return self.__tabBar.tabText(index)
    
    def setTabText(self, index, text):
        """
        Public method to set the text of a tab.
        
        @param index index of the tab (integer)
        @param text text to set (string)
        """
        self.__tabBar.setTabText(index, text)
    
    def tabToolTip(self, index):
        """
        Public method to get the tooltip text of a tab.
        
        @param index index of the tab (integer)
        @return tooltip text of the tab (string)
        """
        return self.__tabBar.tabToolTip(index)
    
    def setTabToolTip(self, index, tip):
        """
        Public method to set the tooltip text of a tab.
        
        @param index index of the tab (integer)
        @param tip tooltip text to set (string)
        """
        self.__tabBar.setTabToolTip(index, tip)
    
    def tabWhatsThis(self, index):
        """
        Public method to get the WhatsThis text of a tab.
        
        @param index index of the tab (integer)
        @return WhatsThis text of the tab (string)
        """
        return self.__tabBar.tabWhatsThis(index)
    
    def setTabWhatsThis(self, index, text):
        """
        Public method to set the WhatsThis text of a tab.
        
        @param index index of the tab (integer)
        @param text WhatsThis text to set (string)
        """
        self.__tabBar.setTabWhatsThis(index, text)
    
    def widget(self, index):
        """
        Public method to get a reference to the widget associated with a tab.
        
        @param index index of the tab (integer)
        @return reference to the widget (QWidget)
        """
        return self.__stackedWidget.widget(index)
    
    def saveState(self):
        """
        Public method to save the state of the sidebar.
        
        @return saved state as a byte array (QByteArray)
        """
        if len(self.splitterSizes) == 0:
            if self.splitter:
                self.splitterSizes = self.splitter.sizes()
            self.__bigSize = self.size()
            if self.__orientation in [E5SideBar.North, E5SideBar.South]:
                self.__minSize = self.minimumSizeHint().height()
                self.__maxSize = self.maximumHeight()
            else:
                self.__minSize = self.minimumSizeHint().width()
                self.__maxSize = self.maximumWidth()
        
        data = QByteArray()
        stream = QDataStream(data, QIODevice.WriteOnly)
        stream.setVersion(QDataStream.Qt_4_6)
        
        stream.writeUInt16(self.Version)
        stream.writeBool(self.__minimized)
        stream << self.__bigSize
        stream.writeUInt16(self.__minSize)
        stream.writeUInt16(self.__maxSize)
        stream.writeUInt16(len(self.splitterSizes))
        for size in self.splitterSizes:
            stream.writeUInt16(size)
        stream.writeBool(self.__autoHide)
        
        return data
    
    def restoreState(self, state):
        """
        Public method to restore the state of the sidebar.
        
        @param state byte array containing the saved state (QByteArray)
        @return flag indicating success (boolean)
        """
        if state.isEmpty():
            return False
        
        if self.__orientation in [E5SideBar.North, E5SideBar.South]:
            minSize = self.layout.minimumSize().height()
            maxSize = self.maximumHeight()
        else:
            minSize = self.layout.minimumSize().width()
            maxSize = self.maximumWidth()
        
        data = QByteArray(state)
        stream = QDataStream(data, QIODevice.ReadOnly)
        stream.setVersion(QDataStream.Qt_4_6)
        stream.readUInt16()  # version
        minimized = stream.readBool()
        
        if minimized and not self.__minimized:
            self.shrink()
        
        stream >> self.__bigSize
        self.__minSize = max(stream.readUInt16(), minSize)
        self.__maxSize = max(stream.readUInt16(), maxSize)
        count = stream.readUInt16()
        self.splitterSizes = []
        for i in range(count):
            self.splitterSizes.append(stream.readUInt16())
        
        self.__autoHide = stream.readBool()
        self.__autoHideButton.setChecked(not self.__autoHide)
        
        if not minimized:
            self.expand()
        
        return True
    
    #######################################################################
    ## methods below implement the autohide functionality
    #######################################################################
    
    def __autoHideToggled(self, checked):
        """
        Private slot to handle the toggling of the autohide button.
        
        @param checked flag indicating the checked state of the button
            (boolean)
        """
        self.__autoHide = not checked
        if self.__autoHide:
            self.__autoHideButton.setIcon(
                UI.PixmapCache.getIcon("autoHideOn.png"))
        else:
            self.__autoHideButton.setIcon(
                UI.PixmapCache.getIcon("autoHideOff.png"))
    
    def __appFocusChanged(self, old, now):
        """
        Private slot to handle a change of the focus.
        
        @param old reference to the widget, that lost focus (QWidget or None)
        @param now reference to the widget having the focus (QWidget or None)
        """
        self.__hasFocus = self.isAncestorOf(now)
        if self.__autoHide and not self.__hasFocus and not self.isMinimized():
            self.shrink()
        elif self.__autoHide and self.__hasFocus and self.isMinimized():
            self.expand()
    
    def enterEvent(self, event):
        """
        Protected method to handle the mouse entering this widget.
        
        @param event reference to the event (QEvent)
        """
        if self.__autoHide and self.isMinimized():
            self.expand()
        else:
            self.__cancelDelayTimer()
    
    def leaveEvent(self, event):
        """
        Protected method to handle the mouse leaving this widget.
        
        @param event reference to the event (QEvent)
        """
        if self.__autoHide and not self.__hasFocus and not self.isMinimized():
            self.shrink()
        else:
            self.__cancelDelayTimer()
    
    def shutdown(self):
        """
        Public method to shut down the object.
        
        This method does some preparations so the object can be deleted
        properly. It disconnects from the focusChanged signal in order to
        avoid trouble later on.
        """
        e5App().focusChanged[QWidget, QWidget].disconnect(
            self.__appFocusChanged)
Beispiel #26
0
class StartWidget(QWidget):
    def __init__(self, parent, foreground_color="#ffffff", font_name=""):
        super(StartWidget, self).__init__()

        self.foreground_color = foreground_color
        self.font_name = font_name
        self.parent = parent

        self.key = ""
        self.language = "de"

        self.font = QFont(font_name, 40, QFont.Bold)
        self.font_small = QFont(font_name, 20, QFont.Bold)
        self.high_label_style = "QLabel { color : #ffba26; }"
        self.label_style = "QLabel { color : " + self.foreground_color + "; }"
        style = "QPushButton { background-color: " + self.foreground_color + "; border: 2px; border-radius: 20px; border-style: outset;}"
        self.line_style = "QLabel { background-color: " + self.foreground_color + "; border: 2px; border-radius: 4px;}"
        self.view_stack = QStackedWidget()
        self.view_stack.addWidget(SetupWidget(self, self.foreground_color))

        self.main_layout = QVBoxLayout()
        self.head_line = QHBoxLayout()
        self.separator_line = QHBoxLayout()
        self.main_line = QHBoxLayout()

        self.head_widget_name_backward = QLabel()
        self.set_label_style(self.head_widget_name_backward, self.font_small,
                             self.label_style)
        self.head_widget_name_current = QLabel()
        self.set_label_style(self.head_widget_name_current, self.font,
                             self.high_label_style)
        self.head_widget_name_foreward = QLabel()
        self.set_label_style(self.head_widget_name_foreward, self.font_small,
                             self.label_style)

        self.line = QLabel()
        self.line.setStyleSheet(self.line_style)
        self.line.setFixedSize(640, 10)
        self.separator_line.addStretch(1)
        self.separator_line.addWidget(self.line)
        self.separator_line.addStretch(1)

        self.left_button = QPushButton("<")
        self.left_button.setFixedSize(40, 80)
        self.left_button.setStyleSheet(style)

        self.right_button = QPushButton(">")
        self.right_button.setFixedSize(40, 80)
        self.right_button.setStyleSheet(style)

        self.head_line.addWidget(self.left_button)
        self.head_line.addStretch(1)
        self.head_line.addWidget(self.head_widget_name_backward)
        self.head_line.addSpacing(30)
        self.head_line.addWidget(self.head_widget_name_current)
        self.head_line.addSpacing(30)
        self.head_line.addWidget(self.head_widget_name_foreward)
        self.head_line.addStretch(1)
        self.head_line.addWidget(self.right_button)

        self.main_line.addWidget(self.view_stack)

        self.left_button.clicked.connect(
            lambda state: self.toggle_main_widget(-1))
        self.right_button.clicked.connect(
            lambda state: self.toggle_main_widget(+1))

        self.main_layout.addLayout(self.head_line)
        self.main_layout.addLayout(self.separator_line)
        self.main_layout.addLayout(self.main_line)

        self.set_headline()
        self.setLayout(self.main_layout)

    def set_headline(self):
        max_value = self.view_stack.count()
        current = self.view_stack.currentIndex()
        if max_value == 1:
            self.head_widget_name_backward.setText("")
            self.head_widget_name_current.setText(
                self.view_stack.widget(current).get_name())
            self.head_widget_name_foreward.setText("")
            return
        self.head_widget_name_backward.setText(
            self.view_stack.widget((current - 1) % max_value).get_name())
        self.head_widget_name_current.setText(
            self.view_stack.widget(current).get_name())
        self.head_widget_name_foreward.setText(
            self.view_stack.widget((current + 1) % max_value).get_name())

    def toggle_main_widget(self, index: int):
        max_value = self.view_stack.count()
        current = self.view_stack.currentIndex()
        self.view_stack.setCurrentIndex((current + index) % max_value)
        self.set_headline()

    def create_new_city(self, city_name: str, unit: UnitSystem):
        city_widget = CityWidget(city_name, unit, self.foreground_color,
                                 self.font_name)
        call_parameter = {
            "city": city_name,
            "units": unit,
            "language": self.language,
            "key": self.key
        }
        data = self.parent.get_data(call_parameter)
        city_widget.set_data(data)
        self.view_stack.addWidget(city_widget)
        current = self.view_stack.currentIndex()
        self.toggle_main_widget(current + 1)

        local = QDateTime(QDateTime.currentDateTime())
        print("UTC:", local.toTime_t())
        self.save_config()

    def set_label_style(self, label: QLabel, font: QFont, style: str):
        label.setStyleSheet(style)
        label.setFont(font)

    def update_data(self, config: dict) -> dict:
        call_parameter = {
            "city": config["city"],
            "units": config["unit"],
            "language": self.language,
            "key": self.key
        }  # "key": "36dcf663b6964439a18574709e1d6eef"}
        data = self.parent.get_data(call_parameter)

    def set_key(self, key: str):
        self.key = key

    def set_language(self, language: str):
        self.language = language

    def save_city_config(self) -> list:
        if self.view_stack.count() < 2:
            return []
        cities = []

        for pos in range(1, self.view_stack.count()):
            city = {"name": "", "last_update": "", "unit_system": ""}

            widget = self.view_stack.widget(pos)
            city["unit_system"] = str(widget.get_units())
            city["name"] = widget.get_name()
            cities.append(city)

        return cities

    def save_config(self):
        config = {
            "language": self.language,
            "key": self.key,
            "cities": self.save_city_config()
        }
        self.parent.save_config(config)
Beispiel #27
0
class PyMultiPageWidget(QWidget):

    currentIndexChanged = pyqtSignal(int)

    pageTitleChanged = pyqtSignal(str)

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

        self.comboBox = QComboBox()
        # MAGIC
        # It is important that the combo box has an object name beginning
        # with '__qt__passive_', otherwise, it is inactive in the form editor
        # of the designer and you can't change the current page via the
        # combo box.
        # MAGIC
        self.comboBox.setObjectName('__qt__passive_comboBox')        
        self.stackWidget = QStackedWidget()
        self.comboBox.activated.connect(self.setCurrentIndex)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.comboBox)
        self.layout.addWidget(self.stackWidget)
        self.setLayout(self.layout)

    def sizeHint(self):
        return QSize(200, 150)

    def count(self):
        return self.stackWidget.count()

    def widget(self, index):
        return self.stackWidget.widget(index)

    @pyqtSlot(QWidget)
    def addPage(self, page):
        self.insertPage(self.count(), page)

    @pyqtSlot(int, QWidget)
    def insertPage(self, index, page):
        page.setParent(self.stackWidget)
        self.stackWidget.insertWidget(index, page)
        title = page.windowTitle()
        if title == "":
            title = "Page %d" % (self.comboBox.count() + 1)
            page.setWindowTitle(title)
        self.comboBox.insertItem(index, title)

    @pyqtSlot(int)
    def removePage(self, index):
        widget = self.stackWidget.widget(index)
        self.stackWidget.removeWidget(widget)
        self.comboBox.removeItem(index)

    def getPageTitle(self):
        return self.stackWidget.currentWidget().windowTitle()
    
    @pyqtSlot(str)
    def setPageTitle(self, newTitle):
        self.comboBox.setItemText(self.getCurrentIndex(), newTitle)
        self.stackWidget.currentWidget().setWindowTitle(newTitle)
        self.pageTitleChanged.emit(newTitle)

    def getCurrentIndex(self):
        return self.stackWidget.currentIndex()

    @pyqtSlot(int)
    def setCurrentIndex(self, index):
        if index != self.getCurrentIndex():
            self.stackWidget.setCurrentIndex(index)
            self.comboBox.setCurrentIndex(index)
            self.currentIndexChanged.emit(index)

    pageTitle = pyqtProperty(str, fget=getPageTitle, fset=setPageTitle, stored=False)
    currentIndex = pyqtProperty(int, fget=getCurrentIndex, fset=setCurrentIndex)
Beispiel #28
0
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        
        self.setWindowTitle('Code Size Visualizer')
        
        self.stacked_widget = QStackedWidget() #to show different pages
        self.stacked_widget.currentChanged.connect(self.set_button_state)
        
        
        widget = QtWidgets.QWidget()
        self.setCentralWidget(widget)
     
        vbox = QVBoxLayout()
        vbox.addWidget(self.stacked_widget)
        
        
        #add the to the main window
        self.toolbar = QToolBar("Edit", self)
          
        #on file upload chart button gets enables and upon pressing it goes to next page  
        h = HomeScreen(self) #calling this first
        self.chartBtn = h.viewChartBtn
        self.lineEdit = h.lineEdit
        self.chartBtn.clicked.connect(self.next_page)
        h.pushButton.clicked.connect(self.file_open)
        self.insert_page(h.centralwidget)
        
        self.addToolBar(self.toolbar)
        
        #start with the toolbar hidden
        self.toolbar.toggleViewAction().setChecked(True)
        self.toolbar.toggleViewAction().trigger()
        
        # create main layout
        widget.setLayout(vbox)

    #FILE OPEN    
    def file_open(self):
        name, _ = QtWidgets.QFileDialog.getOpenFileName(None, 'Open File', "", "*.map",options=QtWidgets.QFileDialog.DontUseNativeDialog)
        self.lineEdit.setText(name)
        chartData, navData = FileOpen.openFile(name)
        navigation = Navigation(chartData, navData , self)
        self.addToolBar(navigation.toolbar)
        self.insert_page(navigation.horizontalGroupBox)
      
        
    def set_button_state(self, index):
        n_pages = len(self.stacked_widget)
        self.chartBtn.setEnabled( index % n_pages < n_pages - 1)
        
        
    def insert_page(self, widget, index=-1):
        self.stacked_widget.insertWidget(index, widget)
        self.set_button_state(self.stacked_widget.currentIndex())
        

    def next_page(self):
        new_index = self.stacked_widget.currentIndex()+1
        if new_index < len(self.stacked_widget):
            self.stacked_widget.setCurrentIndex(new_index)
            self.toolbar.toggleViewAction().trigger()