Ejemplo n.º 1
0
class MainWindow(QWidget):  # Inherits QWidget
    def __init__(self):
        super().__init__()
        self.label = QLabel(self)
        self.button = QPushButton('Очистить', self)
        self.line_edit = QLineEdit(self)
        self.initializeUI()

    def initializeUI(self):
        self.setGeometry(100, 100, 400, 200)
        self.setWindowTitle('QLineEdit Test')
        QLabel("Введите login", self).move(100, 10)
        name_label = QLabel("Login:"******"Очистить"
        if sender.text() == 'Очистить':
            self.line_edit.clear()  # очищаем текст
Ejemplo n.º 2
0
class LabeledSlider(QWidget):
    slider_min = 0
    slider_max = 64
    slider_step = 1
    line_edit_width = 70
    line_edit_heigth = 20

    def __init__(self):
        super().__init__()
        self.locale = QLocale()
        self.setContentsMargins(0, 0, 0, 0)
        hbox = QHBoxLayout(self)
        hbox.setContentsMargins(
            0,
            0,
            0,
            0,
        )
        self.slider = QSlider(Qt.Horizontal, self)
        self.slider.setMinimum(self.slider_min)
        self.slider.setMaximum(self.slider_max)
        self.slider.setSingleStep(self.slider_step)
        self.line_edit = QLineEdit('', self)
        self.line_edit.setAlignment(Qt.AlignCenter)
        self.line_edit.setMaximumWidth(self.line_edit_width)
        self.line_edit.setMaximumHeight(self.line_edit_heigth)
        self.line_edit.setMinimumHeight(self.line_edit_heigth)
        hbox.addWidget(self.slider)
        hbox.addWidget(self.line_edit)
        self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
Ejemplo n.º 3
0
 def createEditor(self, parent, option, index):
     """ Creates the double-click editor for renaming render setup entries. The override entry is right aligned. """
     editor = QLineEdit(parent)
     item = self._getItem(index)
     if item.type() == renderSetup.RENDER_OVERRIDE_TYPE:
         editor.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
     return editor
Ejemplo n.º 4
0
    def getData(self, currentAmount):

        amtTitle = QLabel('Total: $')
        amtTitle.setMaximumWidth(120)

        validator = QIntValidator(1, 99999, self)

        amtEdit = QLineEdit()
        amtEdit.setValidator(validator)
        amtEdit.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        amtEdit.returnPressed.connect(self.okClicked)
        amtEdit.setText(str(currentAmount))
        amtEdit.selectAll()

        okButton = QPushButton('OK')
        okButton.clicked.connect(self.okClicked)

        amtLayout = QHBoxLayout()
        amtLayout.addWidget(amtTitle)
        amtLayout.addWidget(amtEdit)
        amtLayout.addWidget(okButton)

        self.setLayout(amtLayout)
        retValue = self.exec()
        if retValue:
            return amtEdit.text()
        else:
            return 0
 def _add_line_edit(self, text=""):
     lineedit = QLineEdit(text)
     lineedit.setMaximumHeight(28)
     lineedit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
     lineedit.setAlignment(QtCore.Qt.AlignLeft)
     self.layout.addWidget(lineedit)
     return lineedit
Ejemplo n.º 6
0
    def _create_meas(self, name, layout, row, col, temp):
        label = QLabel(name, self)
        font = label.font()
        font.setPointSize(8)
        font.setBold(True)
        label.setFont(font)
        layout.addWidget(label, row, col)
        layout.setAlignment(label, Qt.AlignRight)

        meas_value = QLineEdit(self)
        meas_value.setReadOnly(True)
        meas_value.setMaximumSize(65, 32)
        font = QFont('Monospace')
        font.setStyleHint(QFont.TypeWriter)
        font.setPointSize(10)
        meas_value.setFont(font)
        meas_value.setAlignment(Qt.AlignCenter)
        if temp:
            meas_value.setText('0.00 C')
        else:
            meas_value.setText('0.00 V')
        layout.addWidget(meas_value, row, col + 1)
        layout.setAlignment(meas_value, Qt.AlignLeft)

        return meas_value
 def createEditor(self, parent, option, index):
     editor = QLineEdit(parent)
     editor.setAlignment(Qt.AlignRight)
     # Editor only accepts integers, period, or nothing
     regex = QRegExp('[0-9]*(\.[0-9]+)?')
     editor.setValidator(QRegExpValidator(regex, self))
     return editor
Ejemplo n.º 8
0
class Window(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.setWindowTitle('Calculator')
        self.setFixedSize(260, 320)
        self.layout = QGridLayout()
        self.setLayout(self.layout)

        col_nbr = 4
        self.line_edit = QLineEdit()
        self.line_edit.setAlignment(Qt.AlignRight)
        self.line_edit.setReadOnly(True)
        self.line_edit.setText('0')
        self.layout.addWidget(self.line_edit, 0, 0, 1, col_nbr)

        buttons_content = 'C CE 7 8 9 / 4 5 6 * 1 2 3 - 0 . = +'
        buttons_per_rows = [2, 4, 4, 4, 4]
        self.buttons = []
        for button_content in buttons_content.split(' '):
            self.buttons.append(QPushButton(button_content))
        added_buttons = 0
        for row, buttons_per_row in enumerate(buttons_per_rows):
            button_width = col_nbr / buttons_per_row
            for i in range(buttons_per_row):
                self.layout.addWidget(self.buttons[added_buttons], 1 + row,
                                      i * button_width, 1, button_width)
                added_buttons += 1
        for i, button in enumerate(self.buttons):
            button.setFixedHeight(50)
            button.clicked.connect(self.clicked_event)

    def clicked_event(self):
        button = self.sender().text()
        line_edit_content = self.line_edit.text()
        if self.line_edit.text() == '0':
            if button.isnumeric() or button in '-+':
                self.line_edit.setText(button)
            elif button == '.':
                self.line_edit.setText('0.')
        else:
            if button == 'C':
                self.line_edit.setText('0')
            elif button == 'CE':
                if line_edit_content == 'ERREUR': self.line_edit.setText('0')
                else:
                    self.line_edit.setText(line_edit_content[:-1] if
                                           len(line_edit_content) > 1 else '0')
            elif button.isnumeric():
                self.line_edit.setText(line_edit_content + button)
            elif button in '-+*/.' and line_edit_content[-1] not in '-+*/.':
                self.line_edit.setText(line_edit_content + button)
            elif button == '=':
                try:
                    self.line_edit.setText(str(eval(line_edit_content)))
                except Exception:
                    self.line_edit.setText('ERREUR')
Ejemplo n.º 9
0
class CommandParser(QWidget):
    def __init__(self, controller, parent=None):
        super(CommandParser, self).__init__(parent)
        self.controller = controller
        cmnd2send_label = QLabel('Command:')
        cmnd2send_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        self.cmnd2send_editor = QLineEdit()

        # cmnd2send_editor.setKeyboardTracking(False)
        self.cmnd2send_editor.setAlignment(Qt.AlignLeft)
        self.cmnd2send_editor.setSizePolicy(QSizePolicy.MinimumExpanding,
                                            QSizePolicy.Fixed)
        # cmnd2send_editor.setClearButtonEnabled()
        send_button = QPushButton('Send')
        send_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        separator = QFrame()
        separator.setFrameShape(QFrame.HLine)
        separator.setFrameShadow(QFrame.Sunken)
        separator.setSizePolicy(QSizePolicy.MinimumExpanding,
                                QSizePolicy.Fixed)

        scrollbar = QScrollBar()
        cmnd_received_label = QLabel('RESPONSE:')
        self.cmnd_received_window = QTextEdit()
        # self.cmnd_received_window.moveCursor(QTextCursor.Down)
        self.cmnd_received_window.setReadOnly(True)
        self.cmnd_received_window.setVerticalScrollBar(scrollbar)

        # Layout
        editor_layout = QGridLayout()
        editor_layout.addWidget(cmnd2send_label, 0, 0)
        editor_layout.addWidget(self.cmnd2send_editor, 0, 1)
        editor_layout.addWidget(send_button, 0, 2)

        editor_layout.addWidget(separator, 1, 0, 1, 3)

        editor_layout.addWidget(cmnd_received_label, 2, 0, 1, 3)
        editor_layout.addWidget(self.cmnd_received_window, 3, 0, 1, 3)

        layout = QHBoxLayout()
        layout.addLayout(editor_layout)
        self.setLayout(layout)

        # Signals and slots
        send_button.clicked.connect(self.parse_cmnd)

    @Slot()
    def parse_cmnd(self):
        request = self.cmnd2send_editor.text()
        response = self.controller.send_cmnd(request)
        self.cmnd_received_window.insertPlainText('REQUEST: ' + request + '\n')
        self.cmnd_received_window.insertPlainText('RESPONSE: ' + response +
                                                  '\n')
Ejemplo n.º 10
0
class EditTextModal(QDialog):
    def __init__(self, oldText, windowTitle):
        QDialog.__init__(self)
        self.setGeometry(125, 100, 200, 0)
        self.setModal(True)
        self.setWindowTitle(windowTitle)
        self.oldText = oldText
        self.show()

    def getData(self):

        titleLabel = QLabel('Title: ')

        self.titleEdit = QLineEdit()
        self.titleEdit.setAlignment(QtCore.Qt.AlignLeft
                                    | QtCore.Qt.AlignVCenter)
        self.titleEditSs = self.titleEdit.styleSheet()
        self.titleEdit.textChanged.connect(
            lambda text: self.titleEdit.setStyleSheet(self.titleEditSs))
        self.titleEdit.setMaxLength(20)
        self.titleEdit.returnPressed.connect(self.okClicked)
        self.titleEdit.setText(self.oldText)
        self.titleEdit.selectAll()

        okButton = QPushButton('OK')
        okButton.clicked.connect(self.okClicked)

        menuLayout = QVBoxLayout()

        topLine = QHBoxLayout()
        topLine.addWidget(titleLabel)
        topLine.addWidget(self.titleEdit)

        menuLayout.addLayout(topLine)
        menuLayout.addWidget(okButton)

        self.setLayout(menuLayout)
        retValue = self.exec()
        if retValue:
            return self.titleEdit.text()
        else:
            return 0

    def okClicked(self):
        validator = re.compile('.*\S.*')
        if not re.match(validator, self.titleEdit.text()):
            self.titleEdit.setStyleSheet("border: 2px solid red;")
        else:
            self.accept()

    def cancelClicked(self):
        self.reject()
Ejemplo n.º 11
0
class ApolloLabeledEntry(ApolloControl):
    def __init__(self, parent, text, value_text, lines=1, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        # Make sure the text is bottom aligned in the specified number of lines
        text = "\n" * (lines - text.count("\n") - 1) + text
        self.label = QLabel(text, self)
        self.addWidget(self.label)
        self.layout.addSpacing(3)
        self.value = QLineEdit(self)
        self.value.setFixedSize(65, 32)
        self.value.setText(value_text)
        self.value.setAlignment(Qt.AlignCenter)
        self.layout.addWidget(self.value)
Ejemplo n.º 12
0
class ScoreWidget(QWidget):
    normal_style = """
        background-color: black;
        color: green;
    """
    highlight_style = """
        background-color: green;
        color: white;
    """

    font = QFont("mono", 22)

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

        self.score = QLCDNumber()
        self.score.setMinimumHeight(80)
        self.score.setStyleSheet(self.normal_style)
        self.layout.addWidget(self.score)

        self.player = QLineEdit()
        self.player.setStyleSheet(self.normal_style)
        self.player.setFont(self.font)
        self.player.setAlignment(Qt.AlignCenter)
        self.layout.addWidget(self.player)

    def lock(self):
        self.player.setReadOnly(True)
        self.player.setFocusPolicy(Qt.NoFocus)

    def unlock(self):
        self.score.display(0)
        self.player.setReadOnly(False)
        self.player.setFocusPolicy(Qt.StrongFocus)

    def highlight(self):
        self.player.setStyleSheet(self.highlight_style)
        self.score.setStyleSheet(self.highlight_style)

    def unhighlight(self):
        self.player.setStyleSheet(self.normal_style)
        self.score.setStyleSheet(self.normal_style)

    def adjustScore(self, score_adjustment):
        score = self.score.intValue() + score_adjustment
        self.score.display(score)

    def getScore(self):
        return self.score.intValue()
Ejemplo n.º 13
0
class Form(QWidget):
    def __init__(self):
        super(Form, self).__init__()
        self.setWindowTitle("Grid layout 공부할거야")
        self.vb = QVBoxLayout()
        self.setLayout(self.vb)

        self.ln = QLineEdit()
        self.ln.setAlignment(Qt.AlignRight)
        ### 이야 이렇게 하면 스타일 바꿀수 있구낭
        self.ln.setStyleSheet("font-size: 24px;"
                              "font-weight: bold;")
        self.vb.addWidget(self.ln)



        self.gl = QGridLayout()
        self.vb.addLayout(self.gl)

        self.value = [
            [QPushButton('+-'), [0, 0]],
            [QPushButton('/'), [0, 1]],
            [QPushButton('*'), [0, 2]],
            [QPushButton('-'), [0, 3]],
            [QPushButton('1'), [1, 0]],
            [QPushButton('2'), [1, 1]],
            [QPushButton('3'), [1, 2]],
            [QPushButton('+'), [1, 3, 2, 1]],
            [QPushButton('4'), [2, 0]],
            [QPushButton('5'), [2, 1]],
            [QPushButton('6'), [2, 2]],
            [QPushButton('7'), [3, 0]],
            [QPushButton('8'), [3, 1]],
            [QPushButton('9'), [3, 2]],
            [QPushButton('='), [3, 3, 2, 1]],
            [QPushButton('0'), [4, 0, 1, 2]],
            [QPushButton('.'), [4, 2]],
        ]

        for i, p in self.value:
            if len(p) > 2:
                i.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
                self.gl.addWidget(i, p[0],p[1],p[2],p[3])
            else:
                self.gl.addWidget(i, p[0],p[1])
            i.setStyleSheet("font-size:20px;"
                            "font-weight: bold;")
            i.clicked.connect(self.clk)

    def clk(self):
        self.ln.setText(self.ln.text()+self.sender().text())
Ejemplo n.º 14
0
class MyWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)

        self.login = QPushButton("Login (anonymous)")
        self.logout = QPushButton("Logout")
        self.goto = QPushButton("Goto dir")
        self.server = QLineEdit("Server")
        self.dir = QLineEdit()
        self.status = QLabel()

        self.server.setAlignment(Qt.AlignCenter)

        self.layout = QVBoxLayout()
        self.layout.addWidget(self.login)
        self.layout.addWidget(self.logout)
        self.layout.addWidget(self.goto)
        self.layout.addWidget(self.server)
        self.layout.addWidget(self.dir)
        self.layout.addWidget(self.status)
        self.setLayout(self.layout)

        self.ftp = None
        # Connecting the signal
        self.login.clicked.connect(self.loginSlot)
        self.logout.clicked.connect(self.logoutSlot)
        self.goto.clicked.connect(self.goToDirSlot)

    @Slot()
    def loginSlot(self):
        self.ftp = FTP(QLineEdit.text(self.server))
        self.status.setText(self.ftp.login())

    @Slot()
    def goToDirSlot(self):
        self.ftp.cwd(QLineEdit.text(self.dir))
        a = self.ftp.nlst()
        self.status.setText("\n".join(a))

    @Slot()
    def logoutSlot(self):
        try:
            self.ftp.quit()
            self.status.setText("Connection closed successfuly")
        except (Exception) as e:
            self.status.setText(f"Error while terminating connecioin: {e}")
Ejemplo n.º 15
0
    def createEditor(self, parent, option, index):
        """
        Create the editor widget for the table
        """
        if index.column() == 0:
            # Customer Name Column
            cellWidget = QLineEdit(parent=parent)
            cellWidget.setText(index.data())
            cellWidget.selectAll()
        else:
            # Time Column
            cellWidget = QTimeEdit(parent=parent)
            cellWidget.setTime(
                dt.datetime.strptime(index.data(), '%H:%M').time())

        cellWidget.setAlignment(Qt.AlignCenter)
        return cellWidget
Ejemplo n.º 16
0
 def _int_field(
     self,
     min_val: int,
     max_val: int,
     placeholder: str = "",
     tooltip: str = "",
 ) -> QLineEdit:
     result = QLineEdit()
     # TODO derive the max width from the text repr of min_val, max_val.
     result.setMaximumWidth(self._FIELD_WIDTH)
     result.setAlignment(Qt.AlignRight)
     validator = QIntValidator(min_val, max_val)
     result.setValidator(validator)
     if placeholder:
         result.setPlaceholderText(placeholder)
     if tooltip:
         result.setToolTip(tooltip)
     return result
Ejemplo n.º 17
0
class JDaApp(QWidget):

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

    def iniciaUI(self):
        """
        Inicializa a janela e mostra seu conteuda na tela
        """

        self.setGeometry(100,100, 400, 200)
        self.setWindowTitle("Login")
        self.displayWidgets()

        self.show()

    def displayWidgets(self):
        """
        Configura os widgets da app
        """
        # Criando um label e um edit para o nome
        nome_label = QLabel("Nome:",self)
        nome_label.move(70, 50) # localiza o label na tela

        self.nome_edit = QLineEdit(self)
        self.nome_edit.setAlignment(Qt.AlignLeft)                           # Este é o padrão
        self.nome_edit.move(130, 50)
        self.nome_edit.resize(200, 20)                                      # mudando o tamanho da caixa de texto

        self.limpar_btn = QPushButton('Limpar', self)
        self.limpar_btn.clicked.connect(self.limparCxTxt)
        self.limpar_btn.move(160, 110)                                      # localizando o botão na tela

    def limparCxTxt(self):
        """
        Quando acionado o butão limpa a caixa de texto
        """

        sender = self.sender()
        if sender.text() == 'Limpar':
            self.nome_edit.clear()
Ejemplo n.º 18
0
class UserUI(QWidget):
    def __init__(self, *args, **kwargs):
        super(UserUI, self).__init__(*args, **kwargs)
        layout = QVBoxLayout()
        layout.setContentsMargins(QMargins(3, 3, 3, 3))

        opts_layout = QHBoxLayout()
        opts_layout.addWidget(QLabel("用户角色:", self))
        self.user_role_combobox = QComboBox(self)
        opts_layout.addWidget(self.user_role_combobox)

        self.search_input = QLineEdit(self)
        self.search_input.setPlaceholderText("搜索(关键字按Enter搜索)")
        self.search_input.setAlignment(Qt.AlignCenter)
        opts_layout.addWidget(self.search_input)

        layout.addLayout(opts_layout)

        self.user_table = QTableWidget(self)
        layout.addWidget(self.user_table)

        self.setLayout(layout)
Ejemplo n.º 19
0
    def _displayStudentQuestions(self):
        # Checking if we have a student
        if self.student == None:
            self.UI.studentQuestionsStack.setCurrentIndex(0)
            return
        self.UI.studentQuestionsStack.setCurrentIndex(1)

        # Questions panel
        if self.student.questions not in [None, {}]:
            # Setting our widget and clearing its contents
            studentDetailsWidget = self.UI.studentQuestions
            [
                studentDetailsWidget.removeRow(0)
                for _ in range(studentDetailsWidget.rowCount())
            ]

            # Iterating through our student's questions and adding them to our widget
            for currentRow, (label, field) in enumerate(
                    self.student.questions.items()):
                labelWidget = QLineEdit(label)
                labelWidget.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
                fieldWidget = QLineEdit(field)
                fieldWidget.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
                studentDetailsWidget.addRow(labelWidget, fieldWidget)
Ejemplo n.º 20
0
class DebugControlsWidget(QToolBar):
    def __init__(self, parent, name, data, debug_state):
        if not type(data) == BinaryView:
            raise Exception('expected widget data to be a BinaryView')

        self.bv = data
        self.debug_state = debug_state

        QToolBar.__init__(self, parent)

        # TODO: Is there a cleaner way to do this?
        self.setStyleSheet("""
		QToolButton{padding: 4px 14px 4px 14px; font-size: 14pt;}
		QToolButton:disabled{color: palette(alternate-base)}
		""")

        self.actionRun = QAction("Run", self)
        self.actionRun.triggered.connect(lambda: self.perform_run())
        self.actionRun.setIcon(load_icon('run.svg'))

        self.actionRestart = QAction("Restart", self)
        self.actionRestart.triggered.connect(lambda: self.perform_restart())
        self.actionRestart.setIcon(load_icon('restart.svg'))

        self.actionQuit = QAction("Quit", self)
        self.actionQuit.triggered.connect(lambda: self.perform_quit())
        self.actionQuit.setIcon(load_icon('cancel.svg'))

        self.actionAttach = QAction("Attach", self)
        self.actionAttach.triggered.connect(lambda: self.perform_attach())
        self.actionAttach.setIcon(load_icon('connect.svg'))

        self.actionDetach = QAction("Detach", self)
        self.actionDetach.triggered.connect(lambda: self.perform_detach())
        self.actionDetach.setIcon(load_icon('disconnect.svg'))

        self.actionSettings = QAction("Settings...", self)
        self.actionSettings.triggered.connect(lambda: self.perform_settings())

        self.actionPause = QAction("Pause", self)
        self.actionPause.triggered.connect(lambda: self.perform_pause())
        self.actionPause.setIcon(load_icon('pause.svg'))

        self.actionResume = QAction("Resume", self)
        self.actionResume.triggered.connect(lambda: self.perform_resume())
        self.actionResume.setIcon(load_icon('resume.svg'))

        self.actionStepIntoAsm = QAction("Step Into (Assembly)", self)
        self.actionStepIntoAsm.triggered.connect(
            lambda: self.perform_step_into_asm())
        self.actionStepIntoAsm.setIcon(load_icon('stepinto.svg'))

        self.actionStepIntoIL = QAction("Step Into", self)
        self.actionStepIntoIL.triggered.connect(
            lambda: self.perform_step_into_il())
        self.actionStepIntoIL.setIcon(load_icon('stepinto.svg'))

        self.actionStepOverAsm = QAction("Step Over (Assembly)", self)
        self.actionStepOverAsm.triggered.connect(
            lambda: self.perform_step_over_asm())
        self.actionStepOverAsm.setIcon(load_icon('stepover.svg'))

        self.actionStepOverIL = QAction("Step Over", self)
        self.actionStepOverIL.triggered.connect(
            lambda: self.perform_step_over_il())
        self.actionStepOverIL.setIcon(load_icon('stepover.svg'))

        self.actionStepReturn = QAction("Step Return", self)
        self.actionStepReturn.triggered.connect(
            lambda: self.perform_step_return())
        self.actionStepReturn.setIcon(load_icon('stepout.svg'))

        # session control menu
        self.controlMenu = QMenu("Process Control", self)
        self.controlMenu.addAction(self.actionRun)
        self.controlMenu.addAction(self.actionRestart)
        self.controlMenu.addAction(self.actionQuit)
        self.controlMenu.addSeparator()
        self.controlMenu.addAction(self.actionAttach)
        self.controlMenu.addAction(self.actionDetach)
        self.controlMenu.addSeparator()
        self.controlMenu.addAction(self.actionSettings)

        self.stepIntoMenu = QMenu("Step Into", self)
        self.stepIntoMenu.addAction(self.actionStepIntoIL)
        self.stepIntoMenu.addAction(self.actionStepIntoAsm)

        self.stepOverMenu = QMenu("Step Over", self)
        self.stepOverMenu.addAction(self.actionStepOverIL)
        self.stepOverMenu.addAction(self.actionStepOverAsm)

        self.btnControl = QToolButton(self)
        self.btnControl.setMenu(self.controlMenu)
        self.btnControl.setPopupMode(QToolButton.MenuButtonPopup)
        self.btnControl.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.btnControl.setDefaultAction(self.actionRun)
        self.addWidget(self.btnControl)

        # execution control buttons
        self.btnPauseResume = QToolButton(self)
        self.btnPauseResume.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.btnPauseResume.setDefaultAction(self.actionPause)
        self.addWidget(self.btnPauseResume)

        #self.addAction(self.actionPause)
        #self.addAction(self.actionResume)

        self.btnStepInto = QToolButton(self)
        self.btnStepInto.setMenu(self.stepIntoMenu)
        self.btnStepInto.setPopupMode(QToolButton.MenuButtonPopup)
        self.btnStepInto.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.btnStepInto.setDefaultAction(self.actionStepIntoIL)
        self.addWidget(self.btnStepInto)

        self.btnStepOver = QToolButton(self)
        self.btnStepOver.setMenu(self.stepOverMenu)
        self.btnStepOver.setPopupMode(QToolButton.MenuButtonPopup)
        self.btnStepOver.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.btnStepOver.setDefaultAction(self.actionStepOverIL)
        self.addWidget(self.btnStepOver)

        # TODO: Step until returning from current function
        self.btnStepReturn = QToolButton(self)
        self.btnStepReturn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.btnStepReturn.setDefaultAction(self.actionStepReturn)
        self.addWidget(self.btnStepReturn)

        #self.addAction(self.actionStepReturn)

        self.threadMenu = QMenu("Threads", self)

        self.btnThreads = QToolButton(self)
        self.btnThreads.setMenu(self.threadMenu)
        self.btnThreads.setPopupMode(QToolButton.InstantPopup)
        self.btnThreads.setToolButtonStyle(Qt.ToolButtonTextOnly)
        self.addWidget(self.btnThreads)

        self.set_thread_list([])

        self.editStatus = QLineEdit('INACTIVE', self)
        self.editStatus.setReadOnly(True)
        self.editStatus.setAlignment(QtCore.Qt.AlignCenter)
        self.addWidget(self.editStatus)

        # disable buttons
        self.set_actions_enabled(Run=self.can_exec(),
                                 Restart=False,
                                 Quit=False,
                                 Attach=self.can_connect(),
                                 Detach=False,
                                 Pause=False,
                                 Resume=False,
                                 StepInto=False,
                                 StepOver=False,
                                 StepReturn=False)
        self.set_resume_pause_action("Pause")
        self.set_default_process_action(
            "Attach" if self.can_connect() else "Run")

    def __del__(self):
        # TODO: Move this elsewhere
        # This widget is tasked with cleaning up the state after the view is closed
        # binjaplug.delete_state(self.bv)
        pass

    # -------------------------------------------------------------------------
    # Helpers
    # -------------------------------------------------------------------------
    def can_exec(self):
        return DebugAdapter.ADAPTER_TYPE.use_exec(
            self.debug_state.adapter_type)

    def can_connect(self):
        return DebugAdapter.ADAPTER_TYPE.use_connect(
            self.debug_state.adapter_type)

    def alert_need_install(self, proc):
        message = "Cannot start debugger: {} not found on {}.".format(
            proc,
            "the target machine" if self.can_connect() else "your machine")

        adapter_type = self.debug_state.adapter_type

        # TODO: detect remote os correctly, as gdb/lldb are compatible with both macos and linux
        if adapter_type == DebugAdapter.ADAPTER_TYPE.LOCAL_GDB or adapter_type == DebugAdapter.ADAPTER_TYPE.REMOTE_GDB:
            remote_os = "Linux"
        elif adapter_type == DebugAdapter.ADAPTER_TYPE.LOCAL_LLDB or adapter_type == DebugAdapter.ADAPTER_TYPE.REMOTE_LLDB:
            remote_os = "Darwin"
        elif adapter_type == DebugAdapter.ADAPTER_TYPE.LOCAL_DBGENG or adapter_type == DebugAdapter.ADAPTER_TYPE.REMOTE_DBGENG:
            remote_os = "Windows"
        else:
            # Uncertain
            remote_os = platform.system()

        if remote_os == "Linux":
            message += "\nYou can find this in your package manager or build it from source."
        elif remote_os == "Darwin":
            if proc == "lldb":
                message += "\nYou need to install it by running the following command in Terminal:\nxcode-select --install"
            elif proc == "gdbserver":
                message += "\nYou can find this in your package manager or build it from source."
            else:
                message += "\nYou need to install this manually."
        elif remote_os == "Windows":
            # TODO: dbgeng does not currently throw this
            message += "\nYou need to reinstall the debugger plugin."
        else:
            message += "\nYou need to install this manually."

        show_message_box("Cannot Start Debugger",
                         message,
                         icon=MessageBoxIcon.ErrorIcon)

    # -------------------------------------------------------------------------
    # UIActions
    # -------------------------------------------------------------------------

    def perform_run(self):
        def perform_run_thread():
            while True:
                try:
                    self.debug_state.run()
                    execute_on_main_thread_and_wait(perform_run_after)
                except ConnectionRefusedError:
                    execute_on_main_thread_and_wait(
                        lambda: perform_run_error('ERROR: Connection Refused'))
                except DebugAdapter.ProcessStartError as e:
                    execute_on_main_thread_and_wait(
                        lambda: perform_run_error(str(e)))
                except DebugAdapter.NotExecutableError as e:
                    fpath = e.args[0]
                    if platform.system() != 'Windows':
                        msg = '%s is not executable, would you like to set +x and retry?' % fpath
                        res = show_message_box(
                            'Error', msg, MessageBoxButtonSet.YesNoButtonSet,
                            MessageBoxIcon.ErrorIcon)
                        if res == MessageBoxButtonResult.YesButton:
                            os.chmod(fpath, os.stat(fpath).st_mode | 0o100)
                            continue
                    execute_on_main_thread_and_wait(lambda: perform_run_error(
                        'ERROR: Target Not Executable'))
                except DebugAdapter.NotInstalledError as e:
                    execute_on_main_thread_and_wait(
                        lambda: self.alert_need_install(e.args[0]))
                    execute_on_main_thread_and_wait(lambda: perform_run_error(
                        'ERROR: Debugger Not Installed'))
                except DebugAdapter.PermissionDeniedError as e:
                    execute_on_main_thread_and_wait(
                        lambda: perform_run_error('ERROR: Permission denied'))
                    if platform.system() == 'Darwin':
                        res = show_message_box(
                            'Error',
                            'Developer tools need to be enabled to debug programs. This can be authorized either from here or by starting a debugger in Xcode.',
                            MessageBoxButtonSet.OKButtonSet,
                            MessageBoxIcon.ErrorIcon)
                except Exception as e:
                    execute_on_main_thread_and_wait(lambda: perform_run_error(
                        'ERROR: ' + ' '.join(e.args)))
                    traceback.print_exc(file=sys.stderr)

                break

        def perform_run_after():
            self.state_stopped()
            self.debug_state.ui.on_step()

        def perform_run_error(e):
            self.state_error(e)

        self.state_starting('STARTING')

        threading.Thread(target=perform_run_thread).start()

    def perform_restart(self):
        def perform_restart_thread():
            try:
                self.debug_state.restart()
                execute_on_main_thread_and_wait(perform_restart_after)
            except ConnectionRefusedError:
                execute_on_main_thread_and_wait(
                    lambda: perform_restart_error('ERROR: Connection Refused'))
            except Exception as e:
                execute_on_main_thread_and_wait(lambda: perform_restart_error(
                    'ERROR: ' + ' '.join(e.args)))
                traceback.print_exc(file=sys.stderr)

        def perform_restart_after():
            self.state_stopped()
            self.debug_state.ui.on_step()

        def perform_restart_error(e):
            self.state_error(e)

        self.state_starting('RESTARTING')
        threading.Thread(target=perform_restart_thread).start()

    def perform_quit(self):
        self.debug_state.quit()
        self.state_inactive()
        self.debug_state.ui.on_step()

    def perform_attach(self):
        def perform_attach_thread():
            try:
                self.debug_state.attach()
                execute_on_main_thread_and_wait(perform_attach_after)
            except ConnectionRefusedError:
                execute_on_main_thread_and_wait(
                    lambda: perform_attach_error('ERROR: Connection Refused'))
            except TimeoutError:
                execute_on_main_thread_and_wait(
                    lambda: perform_attach_error('ERROR: Connection Refused'))
            except Exception as e:
                execute_on_main_thread_and_wait(
                    lambda: perform_attach_error('ERROR: ' + ' '.join(e.args)))
                traceback.print_exc(file=sys.stderr)

        def perform_attach_after():
            self.state_stopped()
            self.debug_state.ui.on_step()

        def perform_attach_error(e):
            self.state_error(e)

        self.state_starting('ATTACHING')
        threading.Thread(target=perform_attach_thread).start()

    def perform_detach(self):
        self.debug_state.detach()
        self.state_inactive()
        self.debug_state.ui.on_step()

    def perform_settings(self):
        def settings_finished():
            if self.debug_state.running:
                self.state_running()
            elif self.debug_state.connected:
                local_rip = self.debug_state.local_ip
                if self.debug_state.bv.read(local_rip, 1) and len(
                        self.debug_state.bv.get_functions_containing(
                            local_rip)) > 0:
                    self.state_stopped()
                else:
                    self.state_stopped_extern()
            else:
                self.state_inactive()

        dialog = AdapterSettingsDialog.AdapterSettingsDialog(self, self.bv)
        dialog.show()
        dialog.finished.connect(settings_finished)

    def perform_pause(self):
        self.debug_state.pause()
        # Don't update state here-- one of the other buttons is running in a thread and updating for us

    def perform_resume(self):
        def perform_resume_thread():
            (reason, data) = self.debug_state.go()
            execute_on_main_thread_and_wait(
                lambda: perform_resume_after(reason, data))

        def perform_resume_after(reason, data):
            self.handle_stop_return(reason, data)
            self.debug_state.ui.on_step()

        self.state_running()
        threading.Thread(target=perform_resume_thread).start()

    def perform_step_into_asm(self):
        def perform_step_into_asm_thread():
            (reason, data) = self.debug_state.step_into()
            execute_on_main_thread_and_wait(
                lambda: perform_step_into_asm_after(reason, data))

        def perform_step_into_asm_after(reason, data):
            self.handle_stop_return(reason, data)
            self.debug_state.ui.on_step()

        self.state_busy("STEPPING")
        threading.Thread(target=perform_step_into_asm_thread).start()

    def perform_step_into_il(self):

        disasm = self.debug_state.ui.debug_view.binary_editor.getDisassembly()
        graph_type = disasm.getGraphType()

        def perform_step_into_il_thread():
            (reason, data) = self.debug_state.step_into(graph_type)
            execute_on_main_thread_and_wait(
                lambda: perform_step_into_il_after(reason, data))

        def perform_step_into_il_after(reason, data):
            self.handle_stop_return(reason, data)
            self.debug_state.ui.on_step()

        self.state_busy("STEPPING")
        threading.Thread(target=perform_step_into_il_thread).start()

    def perform_step_over_asm(self):
        def perform_step_over_asm_thread():
            (reason, data) = self.debug_state.step_over()
            execute_on_main_thread_and_wait(
                lambda: perform_step_over_asm_after(reason, data))

        def perform_step_over_asm_after(reason, data):
            self.handle_stop_return(reason, data)
            self.debug_state.ui.on_step()

        self.state_busy("STEPPING")
        threading.Thread(target=perform_step_over_asm_thread).start()

    def perform_step_over_il(self):

        disasm = self.debug_state.ui.debug_view.binary_editor.getDisassembly()
        graph_type = disasm.getGraphType()

        def perform_step_over_il_thread():
            (reason, data) = self.debug_state.step_over(graph_type)
            execute_on_main_thread_and_wait(
                lambda: perform_step_over_il_after(reason, data))

        def perform_step_over_il_after(reason, data):
            self.handle_stop_return(reason, data)
            self.debug_state.ui.on_step()

        self.state_busy("STEPPING")
        threading.Thread(target=perform_step_over_il_thread).start()

    def perform_step_return(self):
        def perform_step_return_thread():
            (reason, data) = self.debug_state.step_return()
            execute_on_main_thread_and_wait(
                lambda: perform_step_return_after(reason, data))

        def perform_step_return_after(reason, data):
            self.handle_stop_return(reason, data)
            self.debug_state.ui.on_step()

        self.state_busy("STEPPING")
        threading.Thread(target=perform_step_return_thread).start()

    # -------------------------------------------------------------------------
    # Control state setters
    # -------------------------------------------------------------------------

    def set_actions_enabled(self, **kwargs):
        def enable_step_into(e):
            self.actionStepIntoAsm.setEnabled(e)
            self.actionStepIntoIL.setEnabled(e)

        def enable_step_over(e):
            self.actionStepOverAsm.setEnabled(e)
            self.actionStepOverIL.setEnabled(e)

        def enable_starting(e):
            self.actionRun.setEnabled(e and self.can_exec())
            self.actionAttach.setEnabled(e and self.can_connect())

        def enable_stopping(e):
            self.actionRestart.setEnabled(e)
            self.actionQuit.setEnabled(e)
            self.actionDetach.setEnabled(e)

        def enable_stepping(e):
            self.actionStepIntoAsm.setEnabled(e)
            self.actionStepIntoIL.setEnabled(e)
            self.actionStepOverAsm.setEnabled(e)
            self.actionStepOverIL.setEnabled(e)
            self.actionStepReturn.setEnabled(e)

        actions = {
            "Run": lambda e: self.actionRun.setEnabled(e),
            "Restart": lambda e: self.actionRestart.setEnabled(e),
            "Quit": lambda e: self.actionQuit.setEnabled(e),
            "Attach": lambda e: self.actionAttach.setEnabled(e),
            "Detach": lambda e: self.actionDetach.setEnabled(e),
            "Pause": lambda e: self.actionPause.setEnabled(e),
            "Resume": lambda e: self.actionResume.setEnabled(e),
            "StepInto": enable_step_into,
            "StepOver": enable_step_over,
            "StepReturn": lambda e: self.actionStepReturn.setEnabled(e),
            "Threads": lambda e: self.btnThreads.setEnabled(e),
            "Starting": enable_starting,
            "Stopping": enable_stopping,
            "Stepping": enable_stepping,
        }
        for (action, enabled) in kwargs.items():
            actions[action](enabled)

    def set_default_process_action(self, action):
        actions = {
            "Run": self.actionRun,
            "Restart": self.actionRestart,
            "Quit": self.actionQuit,
            "Attach": self.actionAttach,
            "Detach": self.actionDetach,
        }
        self.btnControl.setDefaultAction(actions[action])

    def set_resume_pause_action(self, action):
        lookup = {'Resume': self.actionResume, 'Pause': self.actionPause}
        self.btnPauseResume.setDefaultAction(lookup[action])

    def set_thread_list(self, threads):
        def select_thread_fn(tid):
            def select_thread(tid):
                stateObj = binjaplug.get_state(self.bv)
                if stateObj.connected and not stateObj.running:
                    stateObj.threads.current = tid
                    stateObj.ui.context_display()
                    stateObj.ui.on_step()
                else:
                    print('cannot set thread in current state')

            return lambda: select_thread(tid)

        self.threadMenu.clear()
        if len(threads) > 0:
            for thread in threads:
                item_name = "Thread {} at {}".format(thread['tid'],
                                                     hex(thread['ip']))
                action = self.threadMenu.addAction(
                    item_name, select_thread_fn(thread['tid']))
                if thread['selected']:
                    self.btnThreads.setDefaultAction(action)
        else:
            defaultThreadAction = self.threadMenu.addAction("Thread List")
            defaultThreadAction.setEnabled(False)
            self.btnThreads.setDefaultAction(defaultThreadAction)

    # -------------------------------------------------------------------------
    # State handling
    # -------------------------------------------------------------------------

    def state_starting(self, msg=None):
        self.editStatus.setText(msg or 'INACTIVE')
        self.set_actions_enabled(Starting=False,
                                 Stopping=False,
                                 Stepping=False,
                                 Pause=False,
                                 Resume=False,
                                 Threads=False)
        self.set_default_process_action(
            "Attach" if self.can_connect() else "Run")
        self.set_thread_list([])
        self.set_resume_pause_action("Pause")

    def state_inactive(self, msg=None):
        self.editStatus.setText(msg or 'INACTIVE')
        self.set_actions_enabled(Starting=True,
                                 Stopping=False,
                                 Stepping=False,
                                 Pause=False,
                                 Resume=False,
                                 Threads=False)
        self.set_default_process_action(
            "Attach" if self.can_connect() else "Run")
        self.set_thread_list([])
        self.set_resume_pause_action("Pause")

    def state_stopped(self, msg=None):
        self.editStatus.setText(msg or 'STOPPED')
        self.set_actions_enabled(Starting=False,
                                 Stopping=True,
                                 Stepping=True,
                                 Pause=True,
                                 Resume=True,
                                 Threads=True)
        self.set_default_process_action("Quit")
        self.set_resume_pause_action("Resume")

    def state_stopped_extern(self, msg=None):
        self.editStatus.setText(msg or 'STOPPED')
        self.set_actions_enabled(Starting=False,
                                 Stopping=True,
                                 Stepping=True,
                                 StepReturn=False,
                                 Pause=True,
                                 Resume=True,
                                 Threads=True)
        self.set_default_process_action("Quit")
        self.set_resume_pause_action("Resume")

    def state_running(self, msg=None):
        self.editStatus.setText(msg or 'RUNNING')
        self.set_actions_enabled(Starting=False,
                                 Stopping=True,
                                 Stepping=False,
                                 Pause=True,
                                 Resume=False,
                                 Threads=False)
        self.set_default_process_action("Quit")
        self.set_resume_pause_action("Pause")

    def state_busy(self, msg=None):
        self.editStatus.setText(msg or 'RUNNING')
        self.set_actions_enabled(Starting=False,
                                 Stopping=True,
                                 Stepping=False,
                                 Pause=True,
                                 Resume=False,
                                 Threads=False)
        self.set_default_process_action("Quit")
        self.set_resume_pause_action("Pause")

    def state_error(self, msg=None):
        self.editStatus.setText(msg or 'ERROR')
        self.set_actions_enabled(Starting=True,
                                 Stopping=False,
                                 Pause=False,
                                 Resume=False,
                                 Stepping=False,
                                 Threads=False)
        self.set_default_process_action(
            "Attach" if self.can_connect() else "Run")
        self.set_thread_list([])
        self.set_resume_pause_action("Resume")

    def handle_stop_return(self, reason, data):
        if reason == DebugAdapter.STOP_REASON.STDOUT_MESSAGE:
            self.state_stopped('stdout: ' + data)
        elif reason == DebugAdapter.STOP_REASON.PROCESS_EXITED:
            self.debug_state.quit()
            self.state_inactive('process exited, return code=%d' % data)
        elif reason == DebugAdapter.STOP_REASON.BACKEND_DISCONNECTED:
            self.debug_state.quit()
            self.state_inactive('backend disconnected (process exited?)')
Ejemplo n.º 21
0
class DebugControlsWidget(QToolBar):
    def __init__(self, parent, name, data, debug_state):
        if not type(data) == binaryninja.binaryview.BinaryView:
            raise Exception('expected widget data to be a BinaryView')

        self.bv = data
        self.debug_state = debug_state

        QToolBar.__init__(self, parent)

        # TODO: Is there a cleaner way to do this?
        self.setStyleSheet("""
		QToolButton{padding: 4px 14px 4px 14px; font-size: 14pt;}
		QToolButton:disabled{color: palette(alternate-base)}
		""")

        self.actionRun = QAction("Run", self)
        self.actionRun.triggered.connect(lambda: self.perform_run())
        self.actionRestart = QAction("Restart", self)
        self.actionRestart.triggered.connect(lambda: self.perform_restart())
        self.actionQuit = QAction("Quit", self)
        self.actionQuit.triggered.connect(lambda: self.perform_quit())
        self.actionAttach = QAction("Attach... (todo)", self)
        self.actionAttach.triggered.connect(lambda: self.perform_attach())
        self.actionDetach = QAction("Detach", self)
        self.actionDetach.triggered.connect(lambda: self.perform_detach())
        self.actionSettings = QAction("Settings...", self)
        self.actionSettings.triggered.connect(lambda: self.perform_settings())
        self.actionPause = QAction("Pause", self)
        self.actionPause.triggered.connect(lambda: self.perform_pause())
        self.actionResume = QAction("Resume", self)
        self.actionResume.triggered.connect(lambda: self.perform_resume())
        self.actionStepInto = QAction("Step Into", self)
        self.actionStepInto.triggered.connect(lambda: self.perform_step_into())
        self.actionStepOver = QAction("Step Over", self)
        self.actionStepOver.triggered.connect(lambda: self.perform_step_over())
        self.actionStepReturn = QAction("Step Return", self)
        self.actionStepReturn.triggered.connect(
            lambda: self.perform_step_return())

        # session control menu
        self.controlMenu = QMenu("Process Control", self)
        self.controlMenu.addAction(self.actionRun)
        self.controlMenu.addAction(self.actionRestart)
        self.controlMenu.addAction(self.actionQuit)
        self.controlMenu.addSeparator()
        # TODO: Attach to running process
        # self.controlMenu.addAction(self.actionAttach)
        self.controlMenu.addAction(self.actionDetach)
        # TODO: Switch adapter/etc (could go in regular settings)
        self.controlMenu.addSeparator()
        self.controlMenu.addAction(self.actionSettings)

        self.btnControl = QToolButton(self)
        self.btnControl.setMenu(self.controlMenu)
        self.btnControl.setPopupMode(QToolButton.MenuButtonPopup)
        self.btnControl.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.btnControl.setDefaultAction(self.actionRun)
        self.addWidget(self.btnControl)

        # execution control buttons
        self.addAction(self.actionPause)
        self.addAction(self.actionResume)
        self.addAction(self.actionStepInto)
        self.addAction(self.actionStepOver)
        # TODO: Step until returning from current function
        self.addAction(self.actionStepReturn)

        self.threadMenu = QMenu("Threads", self)

        self.btnThreads = QToolButton(self)
        self.btnThreads.setMenu(self.threadMenu)
        self.btnThreads.setPopupMode(QToolButton.InstantPopup)
        self.btnThreads.setToolButtonStyle(Qt.ToolButtonTextOnly)
        self.addWidget(self.btnThreads)

        self.set_thread_list([])

        self.editStatus = QLineEdit('INACTIVE', self)
        self.editStatus.setReadOnly(True)
        self.editStatus.setAlignment(QtCore.Qt.AlignCenter)
        self.addWidget(self.editStatus)

        # disable buttons
        self.set_actions_enabled(Run=True,
                                 Restart=False,
                                 Quit=False,
                                 Attach=True,
                                 Detach=False,
                                 Pause=False,
                                 Resume=False,
                                 StepInto=False,
                                 StepOver=False,
                                 StepReturn=False)
        self.set_resume_pause_action("Pause")

    def __del__(self):
        # TODO: Move this elsewhere
        # This widget is tasked with cleaning up the state after the view is closed
        # binjaplug.delete_state(self.bv)
        pass

    def perform_run(self):
        self.debug_state.run()
        self.state_stopped()
        self.debug_state.ui.context_display()

    def perform_restart(self):
        self.debug_state.restart()
        self.state_stopped()

    def perform_quit(self):
        self.debug_state.quit()
        self.state_inactive()

    def perform_attach(self):
        # TODO: Show dialog to select adapter/address/process
        pass

    def perform_detach(self):
        self.debug_state.detach()
        self.state_inactive()

    def perform_settings(self):
        dialog = AdapterSettingsDialog.AdapterSettingsDialog(self, self.bv)
        dialog.show()

    def perform_pause(self):
        self.debug_state.pause()

    def perform_resume(self):
        def perform_resume_thread():
            (reason, data) = self.debug_state.go()
            execute_on_main_thread_and_wait(
                lambda: self.handle_stop_return(reason, data))
            execute_on_main_thread_and_wait(
                lambda: self.debug_state.ui.context_display())

        self.state_running()
        threading.Thread(target=perform_resume_thread).start()

    def perform_step_into(self):
        def perform_step_into_thread():
            (reason, data) = self.debug_state.step_into()
            execute_on_main_thread_and_wait(
                lambda: self.handle_stop_return(reason, data))
            execute_on_main_thread_and_wait(
                lambda: self.debug_state.ui.context_display())

        self.state_busy("STEPPING")
        threading.Thread(target=perform_step_into_thread).start()

    def perform_step_over(self):
        def perform_step_over_thread():
            (reason, data) = self.debug_state.step_over()
            execute_on_main_thread_and_wait(
                lambda: self.handle_stop_return(reason, data))
            execute_on_main_thread_and_wait(
                lambda: self.debug_state.ui.context_display())

        self.state_busy("STEPPING")
        threading.Thread(target=perform_step_over_thread).start()

    def perform_step_return(self):
        def perform_step_return_thread():
            (reason, data) = self.debug_state.step_return()
            execute_on_main_thread_and_wait(
                lambda: self.handle_stop_return(reason, data))
            execute_on_main_thread_and_wait(
                lambda: self.debug_state.ui.context_display())

        self.state_busy("STEPPING")
        threading.Thread(target=perform_step_return_thread).start()

    def set_actions_enabled(self, **kwargs):
        def enable_starting(e):
            self.actionRun.setEnabled(e)
            self.actionAttach.setEnabled(e)

        def enable_stopping(e):
            self.actionRestart.setEnabled(e)
            self.actionQuit.setEnabled(e)
            self.actionDetach.setEnabled(e)

        def enable_stepping(e):
            self.actionStepInto.setEnabled(e)
            self.actionStepOver.setEnabled(e)
            self.actionStepReturn.setEnabled(e)

        actions = {
            "Run": lambda e: self.actionRun.setEnabled(e),
            "Restart": lambda e: self.actionRestart.setEnabled(e),
            "Quit": lambda e: self.actionQuit.setEnabled(e),
            "Attach": lambda e: self.actionAttach.setEnabled(e),
            "Detach": lambda e: self.actionDetach.setEnabled(e),
            "Pause": lambda e: self.actionPause.setEnabled(e),
            "Resume": lambda e: self.actionResume.setEnabled(e),
            "StepInto": lambda e: self.actionStepInto.setEnabled(e),
            "StepOver": lambda e: self.actionStepOver.setEnabled(e),
            "StepReturn": lambda e: self.actionStepReturn.setEnabled(e),
            "Threads": lambda e: self.btnThreads.setEnabled(e),
            "Starting": enable_starting,
            "Stopping": enable_stopping,
            "Stepping": enable_stepping,
        }
        for (action, enabled) in kwargs.items():
            actions[action](enabled)

    def set_default_process_action(self, action):
        actions = {
            "Run": self.actionRun,
            "Restart": self.actionRestart,
            "Quit": self.actionQuit,
            "Attach": self.actionAttach,
            "Detach": self.actionDetach,
        }
        self.btnControl.setDefaultAction(actions[action])

    def set_resume_pause_action(self, action):
        self.actionResume.setVisible(action == "Resume")
        self.actionPause.setVisible(action == "Pause")

    def set_thread_list(self, threads):
        def select_thread_fn(tid):
            def select_thread(tid):
                stateObj = binjaplug.get_state(self.bv)
                if stateObj.state == 'STOPPED':
                    adapter = stateObj.adapter
                    adapter.thread_select(tid)
                    self.debug_state.ui.context_display()
                else:
                    print('cannot set thread in state %s' % stateObj.state)

            return lambda: select_thread(tid)

        self.threadMenu.clear()
        if len(threads) > 0:
            for thread in threads:
                item_name = "Thread {} at {}".format(thread['tid'],
                                                     hex(thread['rip']))
                action = self.threadMenu.addAction(
                    item_name, select_thread_fn(thread['tid']))
                if thread['selected']:
                    self.btnThreads.setDefaultAction(action)
        else:
            defaultThreadAction = self.threadMenu.addAction("Thread List")
            defaultThreadAction.setEnabled(False)
            self.btnThreads.setDefaultAction(defaultThreadAction)

    def state_inactive(self, msg=None):
        debug_state = binjaplug.get_state(self.bv)

        # clear breakpoints
        debug_state.breakpoint_tag_del()
        debug_state.breakpoints = {}

        debug_state.state = 'INACTIVE'
        self.editStatus.setText(msg or debug_state.state)
        self.set_actions_enabled(Starting=True,
                                 Stopping=False,
                                 Stepping=False,
                                 Pause=False,
                                 Resume=False,
                                 Threads=False)
        self.set_default_process_action("Run")
        self.set_thread_list([])
        self.set_resume_pause_action("Pause")

    def state_stopped(self, msg=None):
        debug_state = binjaplug.get_state(self.bv)
        debug_state.state = 'STOPPED'
        self.editStatus.setText(msg or debug_state.state)
        self.set_actions_enabled(Starting=False,
                                 Stopping=True,
                                 Stepping=True,
                                 Pause=True,
                                 Resume=True,
                                 Threads=True)
        self.set_default_process_action("Quit")
        self.set_resume_pause_action("Resume")

    def state_stopped_extern(self, msg=None):
        debug_state = binjaplug.get_state(self.bv)
        debug_state.state = 'STOPPED (outside view)'
        self.editStatus.setText(msg or debug_state.state)
        self.set_actions_enabled(Starting=False,
                                 Stopping=True,
                                 Stepping=True,
                                 StepReturn=False,
                                 Pause=True,
                                 Resume=True,
                                 Threads=True)
        self.set_default_process_action("Quit")
        self.set_resume_pause_action("Resume")

    def state_running(self, msg=None):
        debug_state = binjaplug.get_state(self.bv)
        debug_state.state = 'RUNNING'
        self.editStatus.setText(msg or debug_state.state)
        self.set_actions_enabled(Starting=False,
                                 Stopping=True,
                                 Stepping=False,
                                 Pause=True,
                                 Resume=False,
                                 Threads=False)
        self.set_default_process_action("Quit")
        self.set_resume_pause_action("Pause")

    def state_busy(self, msg=None):
        debug_state = binjaplug.get_state(self.bv)
        debug_state.state = 'RUNNING'
        self.editStatus.setText(msg or debug_state.state)
        self.set_actions_enabled(Starting=False,
                                 Stopping=True,
                                 Stepping=False,
                                 Pause=True,
                                 Resume=False,
                                 Threads=False)
        self.set_default_process_action("Quit")
        self.set_resume_pause_action("Pause")

    def state_error(self, msg=None):
        debug_state = binjaplug.get_state(self.bv)
        debug_state.state = 'ERROR'
        self.editStatus.setText(msg or debug_state.state)
        self.set_actions_enabled(Run=True,
                                 Restart=True,
                                 Quit=True,
                                 Attach=True,
                                 Detach=True,
                                 Pause=True,
                                 Resume=True,
                                 StepInto=True,
                                 StepOver=True,
                                 StepReturn=True,
                                 Threads=True)
        self.set_default_process_action("Run")
        self.set_thread_list([])
        self.set_resume_pause_action("Resume")

    def handle_stop_return(self, reason, data):
        if reason == DebugAdapter.STOP_REASON.STDOUT_MESSAGE:
            self.state_stopped('stdout: ' + data)
        elif reason == DebugAdapter.STOP_REASON.PROCESS_EXITED:
            self.debug_state.quit()
            self.state_inactive('process exited, return code=%d' % data)
        elif reason == DebugAdapter.STOP_REASON.BACKEND_DISCONNECTED:
            self.debug_state.quit()
            self.state_inactive('backend disconnected (process exited?)')
Ejemplo n.º 22
0
class AddressRegister(QWidget):
    def __init__(self, parent, usbif, color):
        super().__init__(parent)
        self._setup_ui(color)

        usbif.poll("monitor", um.MonRegS())
        usbif.poll("monitor", um.MonRegBB())
        #usbif.poll("monitor", um.MonChanFEXT())

        usbif.listen(self)

    def handle_msg(self, msg):
        if isinstance(msg, um.MonRegS):
            self.set_s_value(msg.s)
        elif isinstance(msg, um.MonRegBB):
            self.set_bank_values(msg.eb, msg.fb)
        elif isinstance(msg, um.MonChanFEXT):
            self.set_fext_value(msg.fext)

    def set_bank_values(self, eb, fb):
        self.eb.set_value(eb)
        self.fb.set_value(fb)
        self._update_addr_value()

    def set_s_value(self, x):
        self.s.set_value(x)
        self._update_addr_value()

    def set_fext_value(self, x):
        self.fext.set_value(x)
        self._update_addr_value()

    def _setup_ui(self, color):
        # Set up our basic layout
        layout = QHBoxLayout(self)
        self.setLayout(layout)
        layout.setSpacing(3)
        layout.setMargin(1)

        # Construct register groups for EB, FEXT, FB, and S
        self.eb = SubRegister(self, "EBANK", 3, color)
        self.fext = SubRegister(self, "FEXT", 3, color)
        self.fb = SubRegister(self, "FBANK", 5, color)
        self.s = SubRegister(self, "", 12, color)
        layout.addWidget(self.eb)
        layout.addWidget(self.fext)
        layout.addWidget(self.fb)
        layout.addWidget(self.s)

        # Create a grouping widget for the S label and decoded octal value box
        label_value_layout = QHBoxLayout()
        label_value_layout.setSpacing(3)
        label_value_layout.setMargin(1)
        label_value_layout.setContentsMargins(0, 33, 0, 0)
        layout.addLayout(label_value_layout)

        # Create a value box for displaying the overall decoded address
        self._addr_value = QLineEdit(self)
        self._addr_value.setFixedSize(70, 32)
        self._addr_value.setReadOnly(True)
        self._addr_value.setAlignment(Qt.AlignCenter)
        self._addr_value.setText('0000')
        self._addr_value.setStyleSheet("QLineEdit { color: #555; }")
        label_value_layout.addWidget(self._addr_value)

        # Create a label to show 'S'
        label = QLabel('S', self)
        label.setFixedWidth(20)
        label_value_layout.addWidget(label)

    def _update_addr_value(self):
        # Get the values of all tracked registers
        s = self.s.value
        eb = self.eb.value
        fb = self.fb.value
        fext = self.fext.value

        self._addr_value.setText(agc.format_addr(s, eb, fb, fext))
Ejemplo n.º 23
0
class SettingsEditionDialog(QDialog):

    languages = {"Français": "fr", "English": "en"}

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

        self.setWindowTitle(tr("btn_config"))
        self.setFixedSize(QSize(700, 670))

        # Retrieve current settings
        self.settings = AssetManager.getInstance().config_to_dico(
            AssetManager.getInstance().get_config_parser())
        self.__restart_needed = False
        self.__restore_required = False

        # Version
        self.lab_version = QLabel(self.settings['main']['version'])

        # Language
        self.combo_language = QComboBox()
        self.combo_language.addItems(list(self.languages.keys()))
        for lang in self.languages:  # Look for the current language to select it
            if self.languages[lang] == self.settings['main']['language']:
                self.combo_language.setCurrentText(lang)
                break

        # CSV separator
        self.csv_sep_edit = QLineEdit()
        self.csv_sep_edit.setMaxLength(2)
        self.csv_sep_edit.setFixedWidth(25)
        self.csv_sep_edit.setAlignment(Qt.AlignCenter)
        self.csv_sep_edit.setText(self.settings['main']['csv_separator'])

        # BDD path
        self.btn_bdd_path = QPushButton(self.settings['main']['bdd_path'])
        self.btn_bdd_path.clicked.connect(self.choose_bdd_path)

        # Port
        self.wepapp_port = QSpinBox()
        self.wepapp_port.setMinimum(1024)
        self.wepapp_port.setMaximum(65535)
        self.wepapp_port.setValue(int(self.settings['webapp']['port']))

        # Colors
        self.tile_color = ColorChooser(self.settings['colors']['tile'])
        self.hovered_tile_color = ColorChooser(
            self.settings['colors']['hovered_tile'])
        self.hovered_empty_tile_color = ColorChooser(
            self.settings['colors']['hovered_empty_tile'])
        self.dragged_tile_color = ColorChooser(
            self.settings['colors']['dragged_tile'])
        self.drag_selected_tile_color = ColorChooser(
            self.settings['colors']['drag_selected_tile'])
        self.selected_tile_color = ColorChooser(
            self.settings['colors']['selected_tile'])
        self.tile_text_color = ColorChooser(
            self.settings['colors']['tile_text'])
        self.room_bg_color = ColorChooser(self.settings['colors']['room_bg'])
        self.room_grid_color = ColorChooser(
            self.settings['colors']['room_grid'])
        self.main_bg_color = ColorChooser(self.settings['colors']['main_bg'])
        self.board_bg_color = ColorChooser(self.settings['colors']['board_bg'])

        self.attr_colors = ""  # Chosen colors
        self.attributes_colors_chooser = AttrColorsChooser(
            self.settings['colors']['attr_colors'].split())

        # Sizes (unmodifiable)
        self.unmodifiable = QLabel(tr("unmodifiable_data"))
        self.unmodifiable.setAlignment(Qt.AlignCenter)
        self.desk_size = QLineEdit(self.settings['size']['desk'])
        self.desk_size.setEnabled(False)
        self.desk_size.setFixedWidth(50)
        self.grid_rows = QLineEdit(self.settings['size']['default_room_rows'])
        self.grid_rows.setEnabled(False)
        self.grid_rows.setFixedWidth(50)
        self.grid_cols = QLineEdit(
            self.settings['size']['default_room_columns'])
        self.grid_cols.setEnabled(False)
        self.grid_cols.setFixedWidth(50)

        # --- Buttons ---

        # Confirm button
        self.ok_btn = QPushButton(tr("btn_save"))
        self.ok_btn.clicked.connect(self.accept)
        self.ok_btn.setFocus()

        # Cancel button
        self.cancel_btn = QPushButton(tr("btn_cancel"))
        self.cancel_btn.clicked.connect(self.reject)

        # Restore defaults button
        self.restore_btn = QPushButton(tr("btn_restore"))
        self.restore_btn.clicked.connect(self.__restore)

        self.__set_layout()

    def __set_layout(self) -> None:
        """
        Sets the dialog layout
        """
        # Main layout
        layout = QVBoxLayout()
        layout.setMargin(0)
        layout.addSpacing(5)

        # Main section
        main_layout = QFormLayout()
        main_layout.addRow(tr("app_version"), self.lab_version)
        main_layout.addRow(tr("language"), self.combo_language)
        main_layout.addRow(tr("csv_sep"), self.csv_sep_edit)
        main_layout.addRow(tr("bdd_path"), self.btn_bdd_path)

        # Web app
        widget_port = QWidget()
        layout_port = QHBoxLayout()
        layout_port.setMargin(0)
        layout_port.addWidget(self.wepapp_port)
        layout_port.addWidget(ShutDownToolTip())
        widget_port.setLayout(layout_port)
        main_layout.addRow(tr("web_port"), widget_port)

        layout.addLayout(main_layout)
        Separator(self.width(), layout)

        # Colors
        colors_layout1 = QFormLayout()
        colors_layout1.addRow(tr("tile"), self.tile_color)
        colors_layout1.addRow(tr("hovered_tile"), self.hovered_tile_color)
        colors_layout1.addRow(tr("hovered_empty_tile"),
                              self.hovered_empty_tile_color)
        colors_layout1.addRow(tr("dragged_tile"), self.dragged_tile_color)
        colors_layout1.addRow(tr("drag_selected_tile"),
                              self.drag_selected_tile_color)
        colors_layout1.addRow(tr("selected_tile"), self.selected_tile_color)

        colors_layout2 = QFormLayout()
        colors_layout2.addRow(tr("tile_text"), self.tile_text_color)
        colors_layout2.addRow(tr("room_bg"), self.room_bg_color)
        colors_layout2.addRow(tr("room_grid"), self.room_grid_color)
        colors_layout2.addRow(tr("main_bg"), self.main_bg_color)
        colors_layout2.addRow(tr("board_bg"), self.board_bg_color)

        colors_layout = QHBoxLayout()
        colors_layout.setMargin(0)
        colors_layout.addLayout(colors_layout1)
        colors_layout.addLayout(colors_layout2)

        layout.addLayout(colors_layout)
        layout.addSpacing(15)

        colors_layout3 = QFormLayout()
        colors_layout3.setMargin(0)
        colors_layout3.addRow(tr("attr_colors"),
                              self.attributes_colors_chooser)
        layout.addLayout(colors_layout3)

        Separator(self.width(), layout)

        # Unmodifiable data
        sizes_layout = QFormLayout()
        sizes_layout.setMargin(0)
        sizes_layout.addRow(tr("desk_size"), self.desk_size)
        sizes_layout.addRow(tr("grid_rows"), self.grid_rows)
        sizes_layout.addRow(tr("grid_cols"), self.grid_cols)

        layout.addWidget(self.unmodifiable, alignment=Qt.AlignCenter)
        layout.addSpacing(5)
        layout.addLayout(sizes_layout)

        Separator(self.width(), layout)

        # Buttons
        layout_buttons = QHBoxLayout()
        layout_buttons.addWidget(self.ok_btn)
        layout_buttons.addWidget(self.restore_btn)
        layout_buttons.addWidget(self.cancel_btn)

        layout.addLayout(layout_buttons)
        self.setLayout(layout)

        self.setStyleSheet(get_stylesheet("dialog2"))

    def __restore(self) -> None:
        """
        Restore default parameters before closing dialog
        """
        self.__restart_needed = True
        self.__restore_required = True
        self.accept()

    def __new_settings(self) -> dict:
        """
        Retrieves the new settings to use
        """
        settings = self.settings

        # Language
        language = self.languages[self.combo_language.currentText()]
        if language != settings['main']['language']:
            settings['main']['language'] = language
            self.__restart_needed = True

        # CSV separator
        settings['main']['csv_separator'] = self.csv_sep_edit.text()

        # BDD path
        if self.btn_bdd_path.text() != settings['main']['bdd_path']:
            if self.btn_bdd_path.text().endswith("sdc_db"):
                settings['main']['bdd_path'] = self.btn_bdd_path.text()
            else:
                settings['main']['bdd_path'] = ""
            self.__restart_needed = True

        # Port
        if str(self.wepapp_port.value()) != settings['webapp']['port']:
            settings['webapp']['port'] = str(self.wepapp_port.value())

        # Colors
        settings['colors']['tile'] = self.tile_color.get_color()
        settings['colors']['hovered_tile'] = self.hovered_tile_color.get_color(
        )
        settings['colors'][
            'hovered_empty_tile'] = self.hovered_empty_tile_color.get_color()
        settings['colors']['dragged_tile'] = self.dragged_tile_color.get_color(
        )
        settings['colors'][
            'drag_selected_tile'] = self.drag_selected_tile_color.get_color()
        settings['colors'][
            'selected_tile'] = self.selected_tile_color.get_color()
        settings['colors']['tile_text'] = self.tile_text_color.get_color()
        settings['colors']['room_grid'] = self.room_grid_color.get_color()

        if self.room_bg_color.get_color() != settings['colors']['room_bg']:
            settings['colors']['room_bg'] = self.room_bg_color.get_color()
            self.__restart_needed = True

        if self.main_bg_color.get_color() != settings['colors']['main_bg']:
            settings['colors']['main_bg'] = self.main_bg_color.get_color()
            self.__restart_needed = True

        if self.board_bg_color.get_color() != settings['colors']['board_bg']:
            settings['colors']['board_bg'] = self.board_bg_color.get_color()
            self.__restart_needed = True

        settings['colors']['attr_colors'] = self.attr_colors

        return settings

    def new_config(self) -> ConfigParser:
        """
        Retrieves the new config parser object
        """
        conf = ConfigParser()
        conf.read_dict(self.__new_settings())

        return conf

    def need_restart(self):
        return self.__restart_needed

    def restore_default(self) -> bool:
        return self.__restore_required

    def accept(self) -> None:
        """
        Performs actions before calling parent's accept method
        """
        self.attr_colors = self.attributes_colors_chooser.get_colors_to_str()
        super().accept()

    def choose_bdd_path(self) -> None:
        """
        Opens a file chooser to select the bdd path. Then sets the name as button text.
        """
        bdd_path = QFileDialog.getOpenFileName(self, tr("bdd_path"),
                                               self.btn_bdd_path.text())[0]

        if bdd_path:
            self.btn_bdd_path.setText(bdd_path)
Ejemplo n.º 24
0
class SidePanel(QDockWidget):
    saved = Signal(dict)

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

        # Internal variables
        self._coverdir = path.join("data", "images", "covers")
        self._id = 0
        self._imagedata = ""
        size = [220, 16]  # Width, Height (for QLineEdits/QTextEdits)

        # QDockWidget settings
        self.setAllowedAreas(Qt.RightDockWidgetArea)
        self.setFeatures(QDockWidget.DockWidgetClosable)
        self.setFixedWidth(350)
        self.setVisible(False)

        # Fonts
        bold = QFont()
        bold.setBold(True)
        boldUnderline = QFont()
        boldUnderline.setBold(True)
        boldUnderline.setUnderline(True)

        # Horizontal line
        hline = QFrame()
        hline.setFrameShape(QFrame.HLine)
        hline.setFrameShadow(QFrame.Sunken)

        """Widgets"""
        # Cover
        self.cover = QLabel()
        self.cover.setAlignment(Qt.AlignCenter)
        self.cover.setMinimumHeight(200)
        self.cover.setMaximumSize(self.width(), 250)
        # Buttons
        self.fetchInfoButton = QPushButton("Fetch missing info")
        self.fetchInfoButton.setToolTip("Try to fetch info from MobyGames")
        self.fetchInfoButton.clicked.connect(self._fetchInfo)
        self.editDetailsButton = QPushButton("Edit")
        self.editDetailsButton.setCheckable(True)
        self.editDetailsButton.clicked.connect(self._editDetails)
        self.saveDetailsButton = QPushButton("Save")
        self.saveDetailsButton.clicked.connect(self._saveInfo)
        self.fetchPriceButton = QPushButton("Update price data")
        self.fetchPriceButton.setToolTip("Try to update price data from Pricecharting")
        self.fetchPriceButton.clicked.connect(self._fetchPriceData)
        self.savePriceButton = QPushButton("Save")
        self.savePriceButton.clicked.connect(self._saveInfo)

        # Info layout widgets
        self.nameInfoLabel = QLabel("Name:")
        self.nameInfoLabel.setFont(bold)
        self.nameDataLabel = QLabel()
        self.nameDataLabel.setWordWrap(True)
        self.platformInfoLabel = QLabel("Platform:")
        self.platformInfoLabel.setFont(bold)
        self.platformDataLabel = QLabel()
        self.publisherInfoLabel = QLabel("Publisher:")
        self.publisherInfoLabel.setFont(bold)
        self.publisherDataLabel = QLabel()
        self.developerInfoLabel = QLabel("Developer:")
        self.developerInfoLabel.setFont(bold)
        self.developerDataLabel = QLabel()
        self.regionInfoLabel = QLabel("Region:")
        self.regionInfoLabel.setFont(bold)
        self.regionDataLabel = QLabel()
        self.codeInfoLabel = QLabel("Code:")
        self.codeInfoLabel.setFont(bold)
        self.codeDataLabel = QLabel()
        self.codeDataLabel.setWordWrap(True)
        self.itemInfoLabel = QLabel("Game:")
        self.itemInfoLabel.setFont(bold)
        self.itemDataLabel = QLabel()
        self.boxInfoLabel = QLabel("Box:")
        self.boxInfoLabel.setFont(bold)
        self.boxDataLabel = QLabel()
        self.manualInfoLabel = QLabel("Manual:")
        self.manualInfoLabel.setFont(bold)
        self.manualDataLabel = QLabel()
        self.genreInfoLabel = QLabel("Genre:")
        self.genreInfoLabel.setFont(bold)
        self.genreDataLabel = QLabel()
        self.yearInfoLabel = QLabel("Year:")
        self.yearInfoLabel.setFont(bold)
        self.yearDataLabel = QLabel()
        self.commentInfoLabel = QLabel("Comment:")
        self.commentInfoLabel.setFont(bold)
        self.commentDataLabel = QLabel()
        self.commentDataLabel.setWordWrap(True)
        self.platformsInfoLabel = QLabel("Available for:")
        self.platformsInfoLabel.setFont(bold)
        self.platformsDataLabel = QLabel()
        self.platformsDataLabel.setWordWrap(True)
        self.platformsDataLabel.setMaximumHeight(50)  # Can get quite large otherwise

        # Edit layout widgets
        self.nameEditLabel = QLabel("Name:")
        self.nameEditLabel.setFont(bold)
        self.nameDataTE = QTextEdit()
        self.nameDataTE.setMaximumSize(size[0], size[1] * 2)
        self.platformEditLabel = QLabel("Platform:")
        self.platformEditLabel.setFont(bold)
        self.platformDataLE = QLineEdit()
        self.platformDataLE.setMaximumSize(size[0], size[1])
        self.publisherEditLabel = QLabel("Publisher:")
        self.publisherEditLabel.setFont(bold)
        self.publisherDataLE = QLineEdit()
        self.publisherDataLE.setMaximumSize(size[0], size[1])
        self.developerEditLabel = QLabel("Developer:")
        self.developerEditLabel.setFont(bold)
        self.developerDataLE = QLineEdit()
        self.developerDataLE.setMaximumSize(size[0], size[1])
        self.regionEditLabel = QLabel("Region:")
        self.regionEditLabel.setFont(bold)
        self.regionDataCoB = QComboBox()
        self.regionDataCoB.addItems(("NTSC (JP)", "NTSC (NA)", "PAL"))
        self.regionDataCoB.setMinimumWidth(size[0])  # If not set it will be too small
        self.regionDataCoB.setMaximumSize(size[0], size[1])
        self.codeEditLabel = QLabel("Code:")
        self.codeEditLabel.setFont(bold)
        self.codeDataLE = QLineEdit()
        self.codeDataLE.setMaximumSize(size[0], size[1])
        self.itemEditLabel = QLabel("Game:")
        self.itemEditLabel.setFont(bold)
        self.itemDataCB = QCheckBox()
        self.itemDataCB.setChecked(False)
        self.boxEditLabel = QLabel("Box:")
        self.boxEditLabel.setFont(bold)
        self.boxDataCB = QCheckBox()
        self.boxDataCB.setChecked(False)
        self.manualEditLabel = QLabel("Manual:")
        self.manualEditLabel.setFont(bold)
        self.manualDataCB = QCheckBox()
        self.manualDataCB.setChecked(False)
        self.genreEditLabel = QLabel("Genre:")
        self.genreEditLabel.setFont(bold)
        self.genreDataLE = QLineEdit()
        self.genreDataLE.setMaximumSize(size[0], size[1])
        self.yearEditLabel = QLabel("Year:")
        self.yearEditLabel.setFont(bold)
        self.yearDataLE = QLineEdit()
        self.yearDataLE.setMaximumSize(size[0], size[1])
        self.commentEditLabel = QLabel("Comment:")
        self.commentEditLabel.setFont(bold)
        self.commentDataTE = QTextEdit()
        self.commentDataTE.setMaximumSize(size[0], size[1] * 2)
        self.platformsEditLabel = QLabel("Available for:")
        self.platformsEditLabel.setFont(bold)
        self.platformsDataTE = QTextEdit()
        self.platformsDataTE.setMaximumSize(size[0], size[1] * 2)

        # Price widgets
        self.paidPriceLabel = QLabel("Paid:")
        self.paidPriceLabel.setFont(bold)
        self.paidPriceDataLE = QLineEdit()
        self.paidPriceDataLE.setMaximumSize(60, size[1])
        self.paidPriceDataLE.setAlignment(Qt.AlignRight)
        self.loosePriceLabel = QLabel("Loose:")
        self.loosePriceLabel.setFont(bold)
        self.loosePriceDataLabel = QLabel()
        self.loosePriceDataLabel.setAlignment(Qt.AlignRight)
        self.cibPriceLabel = QLabel("CIB:")
        self.cibPriceLabel.setFont(bold)
        self.cibPriceDataLabel = QLabel()
        self.cibPriceDataLabel.setAlignment(Qt.AlignRight)
        self.newPriceLabel = QLabel("New:")
        self.newPriceLabel.setFont(bold)
        self.newPriceDataLabel = QLabel()
        self.newPriceDataLabel.setAlignment(Qt.AlignRight)

        """Layouts"""
        # Cover
        self.coverVbox = QVBoxLayout()
        self.coverVbox.addWidget(self.cover, 1)
        self.coverVbox.addWidget(hline, 0)

        # Buttons
        self.detailsButtonHbox = QHBoxLayout()
        self.detailsButtonHbox.addWidget(self.fetchInfoButton, 0)
        self.detailsButtonHbox.addWidget(self.editDetailsButton, 0)
        self.detailsButtonHbox.addWidget(self.saveDetailsButton, 0)
        self.priceButtonHbox = QHBoxLayout()
        self.priceButtonHbox.addWidget(self.fetchPriceButton, 0)
        self.priceButtonHbox.addWidget(self.savePriceButton, 0)

        # Info layouts
        self.nameInfoHbox = QHBoxLayout()
        self.nameInfoHbox.addWidget(self.nameInfoLabel, 0)
        self.nameInfoHbox.addWidget(self.nameDataLabel, 0)
        self.platformInfoHbox = QHBoxLayout()
        self.platformInfoHbox.addWidget(self.platformInfoLabel, 0)
        self.platformInfoHbox.addWidget(self.platformDataLabel, 0)
        self.publisherInfoHbox = QHBoxLayout()
        self.publisherInfoHbox.addWidget(self.publisherInfoLabel, 0)
        self.publisherInfoHbox.addWidget(self.publisherDataLabel, 0)
        self.developerInfoHbox = QHBoxLayout()
        self.developerInfoHbox.addWidget(self.developerInfoLabel, 0)
        self.developerInfoHbox.addWidget(self.developerDataLabel, 0)
        self.regionInfoHbox = QHBoxLayout()
        self.regionInfoHbox.addWidget(self.regionInfoLabel, 0)
        self.regionInfoHbox.addWidget(self.regionDataLabel, 0)
        self.codeInfoHbox = QHBoxLayout()
        self.codeInfoHbox.addWidget(self.codeInfoLabel, 0)
        self.codeInfoHbox.addWidget(self.codeDataLabel, 0)
        self.itemInfoHbox = QHBoxLayout()
        self.itemInfoHbox.addWidget(self.itemInfoLabel, 0)
        self.itemInfoHbox.addWidget(self.itemDataLabel, 0)
        self.boxInfoHbox = QHBoxLayout()
        self.boxInfoHbox.addWidget(self.boxInfoLabel, 0)
        self.boxInfoHbox.addWidget(self.boxDataLabel, 0)
        self.manualInfoHbox = QHBoxLayout()
        self.manualInfoHbox.addWidget(self.manualInfoLabel, 0)
        self.manualInfoHbox.addWidget(self.manualDataLabel, 0)
        self.genreInfoHbox = QHBoxLayout()
        self.genreInfoHbox.addWidget(self.genreInfoLabel, 0)
        self.genreInfoHbox.addWidget(self.genreDataLabel, 0)
        self.yearInfoHbox = QHBoxLayout()
        self.yearInfoHbox.addWidget(self.yearInfoLabel, 0)
        self.yearInfoHbox.addWidget(self.yearDataLabel, 0)
        self.commentInfoHbox = QHBoxLayout()
        self.commentInfoHbox.addWidget(self.commentInfoLabel, 0)
        self.commentInfoHbox.addWidget(self.commentDataLabel, 0)
        self.platformsInfoHbox = QHBoxLayout()
        self.platformsInfoHbox.addWidget(self.platformsInfoLabel, 0)
        self.platformsInfoHbox.addWidget(self.platformsDataLabel, 0)

        # Edit layouts
        self.nameEditHbox = QHBoxLayout()
        self.nameEditHbox.addWidget(self.nameEditLabel, 0)
        self.nameEditHbox.addWidget(self.nameDataTE, 0)
        self.platformEditHbox = QHBoxLayout()
        self.platformEditHbox.addWidget(self.platformEditLabel, 0)
        self.platformEditHbox.addWidget(self.platformDataLE, 0)
        self.publisherEditHbox = QHBoxLayout()
        self.publisherEditHbox.addWidget(self.publisherEditLabel, 0)
        self.publisherEditHbox.addWidget(self.publisherDataLE, 0)
        self.developerEditHbox = QHBoxLayout()
        self.developerEditHbox.addWidget(self.developerEditLabel, 0)
        self.developerEditHbox.addWidget(self.developerDataLE, 0)
        self.regionEditHbox = QHBoxLayout()
        self.regionEditHbox.addWidget(self.regionEditLabel, 0)
        self.regionEditHbox.addWidget(self.regionDataCoB, 0)
        self.codeEditHbox = QHBoxLayout()
        self.codeEditHbox.addWidget(self.codeEditLabel, 0)
        self.codeEditHbox.addWidget(self.codeDataLE, 0)
        self.itemEditHbox = QHBoxLayout()
        self.itemEditHbox.addWidget(self.itemEditLabel, 0)
        self.itemEditHbox.addWidget(self.itemDataCB, 0)
        self.itemEditHbox.addSpacing(135)
        self.boxEditHbox = QHBoxLayout()
        self.boxEditHbox.addWidget(self.boxEditLabel, 0)
        self.boxEditHbox.addWidget(self.boxDataCB, 0)
        self.boxEditHbox.addSpacing(135)
        self.manualEditHbox = QHBoxLayout()
        self.manualEditHbox.addWidget(self.manualEditLabel, 0)
        self.manualEditHbox.addWidget(self.manualDataCB, 0)
        self.manualEditHbox.addSpacing(135)
        self.genreEditHbox = QHBoxLayout()
        self.genreEditHbox.addWidget(self.genreEditLabel, 0)
        self.genreEditHbox.addWidget(self.genreDataLE, 0)
        self.yearEditHbox = QHBoxLayout()
        self.yearEditHbox.addWidget(self.yearEditLabel, 0)
        self.yearEditHbox.addWidget(self.yearDataLE, 0)
        self.commentEditHbox = QHBoxLayout()
        self.commentEditHbox.addWidget(self.commentEditLabel, 0)
        self.commentEditHbox.addWidget(self.commentDataTE, 0)
        self.platformsEditHbox = QHBoxLayout()
        self.platformsEditHbox.addWidget(self.platformsEditLabel, 0)
        self.platformsEditHbox.addWidget(self.platformsDataTE, 0)

        # Price layouts
        self.paidPriceHbox = QHBoxLayout()
        self.paidPriceHbox.addWidget(self.paidPriceLabel, 0)
        self.paidPriceHbox.addWidget(self.paidPriceDataLE, 0)
        self.loosePriceHbox = QHBoxLayout()
        self.loosePriceHbox.addWidget(self.loosePriceLabel, 0)
        self.loosePriceHbox.addWidget(self.loosePriceDataLabel, 0)
        self.cibPriceHbox = QHBoxLayout()
        self.cibPriceHbox.addWidget(self.cibPriceLabel, 0)
        self.cibPriceHbox.addWidget(self.cibPriceDataLabel, 0)
        self.newPriceHbox = QHBoxLayout()
        self.newPriceHbox.addWidget(self.newPriceLabel, 0)
        self.newPriceHbox.addWidget(self.newPriceDataLabel, 0)

        # Info layout
        self.infoLayout = QVBoxLayout()
        self.infoLayout.addLayout(self.nameInfoHbox, 0)
        self.infoLayout.addLayout(self.platformInfoHbox, 0)
        self.infoLayout.addLayout(self.publisherInfoHbox, 0)
        self.infoLayout.addLayout(self.developerInfoHbox, 0)
        self.infoLayout.addLayout(self.genreInfoHbox, 0)
        self.infoLayout.addLayout(self.regionInfoHbox, 0)
        self.infoLayout.addLayout(self.yearInfoHbox, 0)
        self.infoLayout.addLayout(self.codeInfoHbox, 0)
        self.infoLayout.addLayout(self.itemInfoHbox, 0)
        self.infoLayout.addLayout(self.boxInfoHbox, 0)
        self.infoLayout.addLayout(self.manualInfoHbox, 0)
        self.infoLayout.addLayout(self.commentInfoHbox, 0)
        self.infoLayout.addLayout(self.platformsInfoHbox, 0)

        # Edit layout
        self.editLayout = QVBoxLayout()
        self.editLayout.addLayout(self.nameEditHbox, 0)
        self.editLayout.addLayout(self.platformEditHbox, 0)
        self.editLayout.addLayout(self.publisherEditHbox, 0)
        self.editLayout.addLayout(self.developerEditHbox, 0)
        self.editLayout.addLayout(self.genreEditHbox, 0)
        self.editLayout.addLayout(self.regionEditHbox, 0)
        self.editLayout.addLayout(self.yearEditHbox, 0)
        self.editLayout.addLayout(self.codeEditHbox, 0)
        self.editLayout.addLayout(self.itemEditHbox, 0)
        self.editLayout.addLayout(self.boxEditHbox, 0)
        self.editLayout.addLayout(self.manualEditHbox, 0)
        self.editLayout.addLayout(self.commentEditHbox, 0)
        self.editLayout.addLayout(self.platformsEditHbox, 0)

        # Price layout
        self.priceLayout = QVBoxLayout()
        self.priceLayout.addLayout(self.paidPriceHbox, 0)
        self.priceLayout.addLayout(self.loosePriceHbox, 0)
        self.priceLayout.addLayout(self.cibPriceHbox, 0)
        self.priceLayout.addLayout(self.newPriceHbox, 0)
        self.priceLayout.addStretch(1)
        self.priceLayout.addLayout(self.priceButtonHbox, 0)

        """Main layout"""
        self.infoWidget = QWidget()
        self.infoWidget.setLayout(self.infoLayout)
        self.editWidget = QWidget()
        self.editWidget.setLayout(self.editLayout)
        # Add info and edit widgets to a stacked layout so we can switch layouts when editing
        self.stackedLayout = QStackedLayout()
        self.stackedLayout.addWidget(self.infoWidget)
        self.stackedLayout.addWidget(self.editWidget)
        self.stackedLayout.setCurrentIndex(0)  # Default to info layout

        self.detailsLayout = QVBoxLayout()
        self.detailsLayout.addLayout(self.coverVbox, 1)
        self.detailsLayout.addLayout(self.stackedLayout, 0)
        self.detailsLayout.addLayout(self.detailsButtonHbox, 0)
        self.detailsWidget = QWidget()
        self.detailsWidget.setLayout(self.detailsLayout)

        self.priceWidget = QWidget()
        self.priceWidget.setLayout(self.priceLayout)

        self.tab = QTabWidget()
        self.tab.addTab(self.detailsWidget, "Details")
        self.tab.addTab(self.priceWidget, "Price info")
        self.setWidget(self.tab)

    def _editDetails(self):
        if self.editDetailsButton.isChecked():
            self._updateWidgetData(1)
            self.stackedLayout.setCurrentIndex(1)
        else:
            self._updateWidgetData(0)
            self.stackedLayout.setCurrentIndex(0)

    def _fetchInfo(self):
        name = self.nameDataLabel.text()
        platform = self.platformDataLabel.text()
        region = self.regionDataLabel.text()
        info = getMobyRelease(name, platform, region)
        if "image" in info.keys() and info["image"] != "":
            self._imagedata = requests.get(info["image"]).content
            pixmap = QPixmap()
            pixmap.loadFromData(self._imagedata)
            w = self.cover.width()
            h = self.cover.height()
            self.cover.setPixmap(pixmap.scaled(w, h, Qt.KeepAspectRatio, Qt.SmoothTransformation))

        if self.publisherDataLabel.text() == "":
            self.publisherDataLabel.setText(info["publisher"])
        if self.developerDataLabel.text() == "":
            self.developerDataLabel.setText(info["developer"])
        if self.genreDataLabel.text() == "":
            self.genreDataLabel.setText(info["genre"])
        if self.yearDataLabel.text() == "":
            self.yearDataLabel.setText(info["year"])
        if self.codeDataLabel.text() == "":
            self.codeDataLabel.setText(info["code"])
        if self.platformsDataLabel.text() == "":
            self.platformsDataLabel.setText(info["platforms"])
        # Update edit widgets if we're editing:
        if self.editDetailsButton.isChecked():
            self._updateWidgetData(1)

    def _fetchPriceData(self):
        name = self.nameDataLabel.text()
        platform = self.platformDataLabel.text()
        region = self.regionDataLabel.text()
        prices = getPriceData(name, platform, region)

        self.loosePriceDataLabel.setText(prices["loose"])
        self.cibPriceDataLabel.setText(prices["cib"])
        self.newPriceDataLabel.setText(prices["new"])

    def _saveInfo(self):
        if self.editDetailsButton.isChecked():
            self._updateWidgetData(0)

        paidPrice = str(self.paidPriceDataLE.text()).replace(",", ".")  # Better use '.' as decimal denominator

        info = {"id": self._id,
                "name": self.nameDataLabel.text(),
                "platform": self.platformDataLabel.text(),
                "publisher": self.publisherDataLabel.text(),
                "developer": self.developerDataLabel.text(),
                "genre": self.genreDataLabel.text(),
                "region": self.regionDataLabel.text(),
                "year": self.yearDataLabel.text(),
                "code": self.codeDataLabel.text(),
                "item": self.itemDataLabel.text(),
                "box": self.boxDataLabel.text(),
                "manual": self.manualDataLabel.text(),
                "comment": self.commentDataLabel.text(),
                "platforms": self.platformsDataLabel.text(),
                "price": ",".join((paidPrice, self.loosePriceDataLabel.text(),
                                   self.cibPriceDataLabel.text(), self.newPriceDataLabel.text()))}

        # Save imagedata to file
        if self._imagedata != "" and not path.exists(path.join(self._coverdir, str(self._id) + ".jpg")):
            with open(path.join(self._coverdir, str(self._id) + ".jpg"), "wb") as f:
                f.write(self._imagedata)

        self.saved.emit(info)

    def _updateWidgetData(self, layoutIndex: int):
        if layoutIndex == 0:
            self.nameDataLabel.setText(self.nameDataTE.toPlainText())
            self.platformDataLabel.setText(self.platformDataLE.text())
            self.publisherDataLabel.setText(self.publisherDataLE.text())
            self.developerDataLabel.setText(self.developerDataLE.text())
            self.genreDataLabel.setText(self.genreDataLE.text())
            self.regionDataLabel.setText(self.regionDataCoB.currentText())
            self.yearDataLabel.setText(self.yearDataLE.text())
            self.codeDataLabel.setText(self.codeDataLE.text())
            self.itemDataLabel.setText("Yes") if self.itemDataCB.isChecked() else self.itemDataLabel.setText("No")
            self.boxDataLabel.setText("Yes") if self.boxDataCB.isChecked() else self.boxDataLabel.setText("No")
            self.manualDataLabel.setText("Yes") if self.manualDataCB.isChecked() else self.manualDataLabel.setText("No")
            self.commentDataLabel.setText(self.commentDataTE.toPlainText())
            self.platformsDataLabel.setText(self.platformsDataTE.toPlainText())
        else:
            self.nameDataTE.setText(self.nameDataLabel.text())
            self.platformDataLE.setText(self.platformDataLabel.text())
            self.publisherDataLE.setText(self.publisherDataLabel.text())
            self.developerDataLE.setText(self.developerDataLabel.text())
            self.genreDataLE.setText(self.genreDataLabel.text())
            self.regionDataCoB.setCurrentIndex(0) if self.regionDataLabel.text == "NTSC (JP)"\
                else self.regionDataCoB.setCurrentIndex(1) if self.regionDataLabel.text == "NTSC (NA)"\
                else self.regionDataCoB.setCurrentIndex(2)
            self.yearDataLE.setText(self.yearDataLabel.text())
            self.codeDataLE.setText(self.codeDataLabel.text())
            self.itemDataCB.setChecked(True) if self.itemDataLabel.text() == "Yes" \
                else self.itemDataCB.setChecked("False")
            self.boxDataCB.setChecked(True) if self.boxDataLabel.text() == "Yes" \
                else self.boxDataCB.setChecked(False)
            self.manualDataCB.setChecked(True) if self.manualDataLabel.text() == "Yes" \
                else self.manualDataCB.setChecked(False)
            self.commentDataTE.setText(self.commentDataLabel.text())
            self.platformsDataTE.setText(self.platformsDataLabel.text())

    def showDetails(self, info: dict):
        if not self.isVisible():
            self.setVisible(True)
            self.tab.setCurrentIndex(0)  # Show details tab initially
        if self.editDetailsButton.isChecked():
            self.editDetailsButton.setChecked(False)
        self.stackedLayout.setCurrentIndex(0)  # Show info layout initially

        self._id = info["id"]
        self._imagedata = ""
        pixmap = path.join(self._coverdir, "none.png")
        if path.exists(path.join(self._coverdir, str(self._id) + ".jpg")) and info["table"] == "games":
            pixmap = path.join(self._coverdir, str(self._id) + ".jpg")
        p = QPixmap(pixmap)
        w = self.cover.width()
        h = self.cover.height()
        self.cover.setPixmap(p.scaled(w, h, Qt.KeepAspectRatio, Qt.SmoothTransformation))

        # Price data is stored in form $x,$x,$x,$x [paid, loose, cib, new]
        paidPrice, loosePrice, cibPrice, newPrice = info["price"].split(",")

        self.nameDataLabel.setText(info["name"])
        self.platformDataLabel.setText(info["platform"])
        self.regionDataLabel.setText(info["region"])
        self.boxDataLabel.setText(info["box"])
        self.manualDataLabel.setText(info["manual"])
        self.yearDataLabel.setText(str(info["year"]))
        self.commentDataLabel.setText(info["comment"])
        self.paidPriceDataLE.setText(paidPrice)
        self.loosePriceDataLabel.setText(loosePrice)
        self.cibPriceDataLabel.setText(cibPrice)
        self.newPriceDataLabel.setText(newPrice)
        if info["table"] == "games":
            self.publisherDataLabel.setText(info["publisher"])
            self.developerDataLabel.setText(info["developer"])
            self.genreInfoLabel.setText("Genre:")
            self.genreEditLabel.setText("Genre:")
            self.genreDataLabel.setText(info["genre"])
            self.codeInfoLabel.setText("Code:")
            self.codeInfoLabel.setVisible(True)
            self.codeEditLabel.setText("Code")
            self.codeEditLabel.setVisible(True)
            self.codeDataLabel.setText(info["code"])
            self.codeDataLabel.setVisible(True)
            self.codeDataLE.setVisible(True)
            self.itemInfoLabel.setText("Game:")
            self.itemEditLabel.setText("Game:")
            self.itemDataLabel.setText(info["game"])
            self.platformsDataLabel.setText(info["platforms"])
            self.platformsDataLabel.setVisible(True)
            self.platformsInfoLabel.setVisible(True)
            self.fetchInfoButton.setEnabled(True)
            self.fetchInfoButton.setVisible(True)
            self.fetchInfoButton.setToolTip("Try to fetch info from MobyGames")
            self.saveDetailsButton.setEnabled(True)
            self.saveDetailsButton.setVisible(True)
        elif info["table"] == "consoles":
            self.genreInfoLabel.setText("Country:")
            self.genreEditLabel.setText("Country:")
            self.genreDataLabel.setText(info["country"])
            self.codeInfoLabel.setText("Serial Number:")
            self.codeInfoLabel.setVisible(True)
            self.codeEditLabel.setText("Serial Number:")
            self.codeEditLabel.setVisible(True)
            self.codeDataLabel.setText(info["serial number"])
            self.codeDataLabel.setVisible(True)
            self.codeDataLE.setVisible(True)
            self.itemInfoLabel.setText("Console:")
            self.itemEditLabel.setText("Console:")
            self.itemDataLabel.setText(info["console"])
            self.platformsInfoLabel.setVisible(False)
            self.platformsDataLabel.setVisible(False)
            self.fetchInfoButton.setEnabled(False)
            self.fetchInfoButton.setToolTip("Not available for Consoles tab")
        elif info["table"] == "accessories":
            self.genreInfoLabel.setText("Country:")
            self.genreEditLabel.setText("Country:")
            self.genreDataLabel.setText(info["country"])
            self.itemInfoLabel.setText("Accessory:")
            self.itemEditLabel.setText("Accessory:")
            self.itemDataLabel.setText(info["accessory"])
            self.codeInfoLabel.setVisible(False)
            self.codeEditLabel.setVisible(False)
            self.codeDataLabel.setVisible(False)
            self.codeDataLE.setVisible(False)
            self.platformsInfoLabel.setVisible(False)
            self.platformsDataLabel.setVisible(False)
            self.fetchInfoButton.setEnabled(False)
            self.fetchInfoButton.setToolTip("Not avaiable for Accessories tab")

    def hideDetails(self):
        self.setVisible(False)
Ejemplo n.º 25
0
class IComparator(QWidget):
    def __init__(self, parent, usbif):
        super().__init__(parent)
        self._usbif = usbif

        self._br = 0
        self._st = 0
        self._sqext = 0
        self._sq = 0

        self._br_ign = 0
        self._st_ign = 0
        self._sqext_ign = 0
        self._sq_ign = 0

        self._setup_ui()

    def _setup_ui(self):
        layout = QHBoxLayout(self)
        self.setLayout(layout)
        layout.setSpacing(3)
        layout.setMargin(1)

        self.br = SubComparator(self, 2, on_change=self._update_instruction)
        layout.addWidget(self.br)

        self.st = SubComparator(self, 3, on_change=self._update_instruction)
        layout.addWidget(self.st)

        self.sq = SubComparator(self, 7, on_change=self._update_instruction)
        layout.addWidget(self.sq)

        self.status = SubComparator(self,
                                    7,
                                    on_change=self._update_status,
                                    include_values=False,
                                    item_width=35,
                                    separators=False)
        layout.addWidget(self.status)
        layout.setAlignment(self.status, Qt.AlignBottom)

        # Create a grouping widget for the I label and decoded octal value box
        label_value_layout = QHBoxLayout()
        label_value_layout.setSpacing(3)
        label_value_layout.setMargin(1)
        label_value_layout.setContentsMargins(0, 33, 0, 0)
        layout.addLayout(label_value_layout)

        # Create a value box for displaying the overall decoded address
        self._instr_value = QLineEdit(self)
        self._instr_value.setFixedSize(70, 32)
        self._instr_value.setReadOnly(True)
        self._instr_value.setAlignment(Qt.AlignCenter)
        self._instr_value.setText('TC0')
        self._instr_value.setStyleSheet("QLineEdit { color: #555; }")
        label_value_layout.addWidget(self._instr_value)

        # Create a label to show 'I'
        label = QLabel(f"I", self)
        label.setFixedWidth(20)
        label_value_layout.addWidget(label)

    def _update_instruction(self):
        br = self.br.reg_value
        st = self.st.reg_value
        sq = self.sq.reg_value
        sqext = (sq >> 6) & 0o1
        sq &= 0o77

        br_ign = self.br.ign_value
        st_ign = self.st.ign_value
        sq_ign = self.sq.ign_value
        sqext_ign = (sq_ign >> 6) & 0o1
        sq_ign &= 0o77

        if (self._br != br) or (self._st != st) or (self._sq != sq) or (
                self._sqext != sqext):
            self._br = br
            self._st = st
            self._sq = sq
            self._sqext = sqext
            msg = um.ControlICompVal(br=br, st=st, sqext=sqext, sq=sq)
            self._usbif.send(msg)

        if (self._br_ign != br_ign) or (self._st_ign != st_ign) or (
                self._sq_ign != sq_ign) or (self._sqext_ign != sqext_ign):
            self._br_ign = br_ign
            self._st_ign = st_ign
            self._sq_ign = sq_ign
            self._sqext_ign = sqext_ign
            msg = um.ControlICompIgnore(br=br_ign,
                                        st=st_ign,
                                        sqext=sqext_ign,
                                        sq=sq_ign)
            self._usbif.send(msg)

        self._instr_value.setText(agc.disassemble_subinst(sqext, sq, st))

    def _update_status(self):
        reg_states = [s.isChecked() for s in self.status._reg_switches]
        ign_states = [s.isChecked() for s in self.status._ign_switches]
        reg_keys = list(STATUS_INDS.keys())
        ign_keys = [k + "_ign" for k in reg_keys]
        status_reg = dict(zip(reg_keys, reversed(reg_states)))
        status_ign = dict(zip(ign_keys, reversed(ign_states)))
        status_bits = {**status_reg, **status_ign}
        msg = um.ControlICompStatus(**status_bits)
        self._usbif.send(msg)
Ejemplo n.º 26
0
class VOGView(AbstractView):
    def __init__(self,
                 name: str = "VOG_NONE",
                 log_handlers: [StreamHandler] = None):
        self._logger = getLogger(__name__)
        if log_handlers:
            for h in log_handlers:
                self._logger.addHandler(h)
        self._logger.debug("Initializing")
        super().__init__(name)
        """ Min size for the VOG window """
        self._subwindow_height = 222
        self._subwindow_width = 518
        """ min and max sizes for the configuration popup (width, height) """
        self._popup_min = (229, 409)
        self._popup_max = (300, 409)
        """ Set configuration value display area"""
        self._config_frame = EasyFrame()
        self._config_vertical_layout = QVBoxLayout(self._config_frame)
        self._config_label = QLabel(self._config_frame)
        self._config_label.setAlignment(Qt.AlignCenter)
        self._config_val_line_edit = QLineEdit(self._config_frame)
        self._config_val_line_edit.setAlignment(Qt.AlignCenter)

        self._config_vertical_layout.addWidget(self._config_label)
        self._config_vertical_layout.addWidget(self._config_val_line_edit)
        """ Set preset button selection area. """
        self._nhtsa_button = ClickAnimationButton()
        self._eblindfold_button = ClickAnimationButton()
        self._direct_control_button = ClickAnimationButton()
        """ Set open duration, close duration, and debounce time settings display area. """
        self._input_box_frame = EasyFrame()
        self._input_box_grid_layout = QGridLayout(self._input_box_frame)
        self._input_box_grid_layout.setContentsMargins(0, 6, 0, 6)

        self._open_dur_label = QLabel(self._input_box_frame)
        self._open_dur_line_edit = QLineEdit(self._input_box_frame)
        self._open_dur_line_edit.setFixedWidth(80)
        self._open_inf_check_box = QCheckBox(self._input_box_frame)

        self._close_dur_label = QLabel(self._input_box_frame)
        self._close_dur_line_edit = QLineEdit(self._input_box_frame)
        self._close_dur_line_edit.setFixedWidth(80)
        self._close_inf_check_box = QCheckBox(self._input_box_frame)

        self._debounce_label = QLabel(self._input_box_frame)
        self._debounce_time_line_edit = QLineEdit(self._input_box_frame)
        self._debounce_time_line_edit.setFixedWidth(80)

        self._input_box_grid_layout.addWidget(self._open_dur_label, 0, 0, 1, 1)
        self._input_box_grid_layout.addWidget(self._open_dur_line_edit, 0, 1,
                                              1, 1)
        self._input_box_grid_layout.addWidget(self._open_inf_check_box, 0, 2,
                                              1, 1)
        self._input_box_grid_layout.addWidget(self._close_dur_label, 1, 0, 1,
                                              1)
        self._input_box_grid_layout.addWidget(self._close_dur_line_edit, 1, 1,
                                              1, 1)
        self._input_box_grid_layout.addWidget(self._close_inf_check_box, 1, 2,
                                              1, 1)
        self._input_box_grid_layout.addWidget(self._debounce_label, 2, 0, 1, 1)
        self._input_box_grid_layout.addWidget(self._debounce_time_line_edit, 2,
                                              1, 1, 1)
        """ Set button mode setting display area. """
        self._button_mode_frame = EasyFrame()
        self._button_mode_horiz_layout = QGridLayout(self._button_mode_frame)
        self._button_mode_horiz_layout.setContentsMargins(0, 6, 0, 6)
        self._button_mode_label = QLabel(self._button_mode_frame)
        self._button_mode_selector = QComboBox(self._button_mode_frame)

        self._button_mode_selector.addItem("")
        self._button_mode_selector.addItem("")

        self._button_mode_horiz_layout.addWidget(self._button_mode_label, 0, 0,
                                                 1, 1)
        self._button_mode_horiz_layout.addWidget(self._button_mode_selector, 0,
                                                 1, 1, 1)
        """ Set control mode setting display area. """
        self._control_mode_label = QLabel(self._button_mode_frame)
        self._control_mode_selector = QComboBox(self._button_mode_frame)

        self._control_mode_selector.addItem("")
        self._control_mode_selector.addItem("")

        self._button_mode_horiz_layout.addWidget(self._control_mode_label, 1,
                                                 0, 1, 1)
        self._button_mode_horiz_layout.addWidget(self._control_mode_selector,
                                                 1, 1, 1, 1)
        """ Set upload button selection area. """
        self._upload_settings_button = ClickAnimationButton()
        """ Set manual control selection area. """
        self._manual_control_button_frame = EasyFrame()
        self._manual_control_button_layout = QHBoxLayout(
            self._manual_control_button_frame)

        self._manual_control_open_button = ClickAnimationButton()
        self._manual_control_close_button = ClickAnimationButton()

        self._manual_control_button_layout.addWidget(
            self._manual_control_open_button)
        self._manual_control_button_layout.addWidget(
            self._manual_control_close_button)
        self._manual_control_button_layout.setMargin(0)
        """ device settings display """
        self._dev_sets_frame = EasyFrame()

        self._dev_sets_layout = QVBoxLayout(self._dev_sets_frame)

        self.config_button = ClickAnimationButton()
        self.config_button.clicked.connect(self._config_button_handler)

        self._config_win = ConfigPopUp()
        self._config_win.setMinimumSize(self._popup_min[0], self._popup_min[1])
        self._config_win.setMaximumSize(self._popup_max[0], self._popup_max[1])
        self._config_win.setLayout(self._dev_sets_layout)
        """ Add widgets to layout. """
        self._dev_sets_layout.addWidget(self._config_frame)
        self._dev_sets_layout.addWidget(self._input_box_frame)
        self._dev_sets_layout.addWidget(self._button_mode_frame)
        self._dev_sets_layout.addWidget(self._nhtsa_button)
        self._dev_sets_layout.addWidget(self._eblindfold_button)
        self._dev_sets_layout.addWidget(self._direct_control_button)
        self._dev_sets_layout.addWidget(self._upload_settings_button)

        self.layout().addWidget(self.config_button, 0, 0,
                                Qt.AlignTop | Qt.AlignRight)
        self.config_button.setFixedSize(30, 25)

        self.layout().addWidget(self._manual_control_button_frame, 0, 0,
                                Qt.AlignBottom | Qt.AlignLeft)
        self._manual_control_open_button.setFixedSize(70, 25)
        self._manual_control_close_button.setFixedSize(70, 25)

        self.layout().setMargin(0)

        self._strings = dict()
        self._lang_enum = LangEnum.ENG
        self.setMinimumSize(self._subwindow_width, self._subwindow_height)
        self.resize(self._subwindow_width, self._subwindow_height)
        self._logger.debug("Initialized")

    def add_graph(self, graph) -> None:
        """
        Add Graph to view.
        :return None:
        """
        self._logger.debug("running")
        self.layout().addWidget(graph, 0, 0)
        self.config_button.raise_()
        self._manual_control_button_frame.raise_()
        self._logger.debug("done")

    def _config_button_handler(self) -> None:
        """
        handles the config button
        :return None:
        """
        self._logger.debug("running")
        self._config_win.exec_()
        self._logger.debug("done")

    def set_config_val_line_edit_handler(self, func: classmethod) -> None:
        """
        Sets the config val line handler.
        :param func: classmethod that handles the config val line.
        :return None:
        """
        self._logger.debug("running")
        self._config_val_line_edit.textChanged.connect(func)
        self._logger.debug("done")

    def set_nhtsa_button_handler(self, func: classmethod) -> None:
        """
        Sets NHTSA button press handler.
        :param func: classmethod that handles the NHTSA button.
        :return None:
        """
        self._logger.debug("running")
        self._nhtsa_button.clicked.connect(func)
        self._logger.debug("done")

    def set_eblindfold_button_handler(self, func: classmethod) -> None:
        """
        Sets eBlindfold button press handler.
        :param func: classmethod that handles the eBlindfold button.
        :return None:
        """
        self._logger.debug("running")
        self._eblindfold_button.clicked.connect(func)
        self._logger.debug("done")

    def set_direct_control_button_handler(self, func: classmethod) -> None:
        """
        Sets Direct Control button press handler.
        :param func: classmethod that handles the Direct Control button.
        :return None:
        """
        self._logger.debug("running")
        self._direct_control_button.clicked.connect(func)
        self._logger.debug("done")

    def set_open_dur_line_edit_handler(self, func: classmethod) -> None:
        """
        Sets open duration line edit handler.
        :param func: classmethod that handles the line edit.
        :return None:
        """
        self._logger.debug("running")
        self._open_dur_line_edit.textChanged.connect(func)
        self._logger.debug("done")

    def set_close_dur_line_edit_handler(self, func: classmethod) -> None:
        """
        Sets close duration line edit handler.
        :param func: classmethod that handles the line edit.
        :return None:
        """
        self._logger.debug("running")
        self._close_dur_line_edit.textChanged.connect(func)
        self._logger.debug("done")

    def set_open_inf_check_box_handler(self, func: classmethod) -> None:
        """
        Sets open INF checkbox handler.
        :param func: classmethod that handles the checkbox, requires bool param.
        :return None:
        """
        self._logger.debug("running")
        self._open_inf_check_box.stateChanged.connect(func)
        self._logger.debug("done")

    def set_close_inf_check_box_handler(self, func: classmethod) -> None:
        """
        Sets close INF checkbox handler.
        :param func: classmethod that handles the checkbox, requires bool param.
        :return None:
        """
        self._logger.debug("running")
        self._close_inf_check_box.stateChanged.connect(func)
        self._logger.debug("done")

    def set_debounce_time_line_edit_handler(self, func: classmethod) -> None:
        """
        Sets debounce time line edit handler.
        :param func: classmethod that handles the line edit.
        :return None:
        """
        self._logger.debug("running")
        self._debounce_time_line_edit.textChanged.connect(func)
        self._logger.debug("done")

    def set_button_mode_selector_handler(self, func: classmethod) -> None:
        """
        Sets button mode combo box handler.
        :param func: classmethod that handles the combo.
        :return None:
        """
        self._logger.debug("running")
        self._button_mode_selector.currentIndexChanged.connect(func)
        self._logger.debug("done")

    def set_control_mode_selector_handler(self, func: classmethod) -> None:
        """
        Sets button mode combo box handler.
        :param func: classmethod that handles the combo.
        :return None:
        """
        self._logger.debug("running")
        self._control_mode_selector.currentIndexChanged.connect(func)
        self._logger.debug("done")

    def set_upload_settings_button_handler(self, func: classmethod) -> None:
        """
        Sets upload settings button handler.
        :param func: classmethod that handles the button.
        :return None:
        """
        self._logger.debug("running")
        self._upload_settings_button.clicked.connect(func)
        self._logger.debug("done")

    def set_manual_control_open_button_handler(self,
                                               func: classmethod) -> None:
        """
        Sets manual open button handler.
        :param func: classmethod that handles the button.
        :return None:
        """
        self._logger.debug("running")
        self._manual_control_open_button.clicked.connect(func)
        self._logger.debug("done")

    def set_manual_control_close_button_handler(self,
                                                func: classmethod) -> None:
        """
        Sets manual close button handler.
        :param func: classmethod that handles the button.
        :return None:
        """
        self._logger.debug("running")
        self._manual_control_close_button.clicked.connect(func)
        self._logger.debug("done")

    @property
    def config_text(self) -> str:
        """
        Get the string value found in the config text box.
        :return str: string value in the text box.
        """
        return self._config_val_line_edit.text()

    @config_text.setter
    def config_text(self, val: str) -> None:
        """
        Set the string value found in the config text box.
        :param val: string value to set the config text box.
        :return None:
        """
        self._logger.debug("running")
        self._config_val_line_edit.setText(val)
        self._logger.debug("done")

    @property
    def open_duration(self) -> str:
        """
        Get the string value found in the open duration text box.
        :return str: string value in the text box.
        """
        return self._open_dur_line_edit.text()

    @open_duration.setter
    def open_duration(self, val: str) -> None:
        """
        Set the string value found in the open duration text box.
        :param val: string value to set the open duration text box.
        :return None:
        """
        self._logger.debug("running")
        self._open_dur_line_edit.setText(val)
        self._logger.debug("done")

    @property
    def close_duration(self) -> str:
        """
        Get the string value found in the close duration text box.
        :return str: string value in the text box.
        """
        return self._close_dur_line_edit.text()

    @close_duration.setter
    def close_duration(self, val: str) -> None:
        """
        Set the string value found in the close duration text box.
        :param val: string value to set the close duration text box.
        :return None:
        """
        self._logger.debug("running")
        self._close_dur_line_edit.setText(val)
        self._logger.debug("done")

    @property
    def debounce_val(self) -> str:
        """
        Get the string value found in the debounce time text box.
        :return str: string value in the text box.
        """
        return self._debounce_time_line_edit.text()

    @debounce_val.setter
    def debounce_val(self, val: str) -> None:
        """
        Set the string value found in the debounce time text box.
        :param val: string value to set the debounce text box.
        :return None:
        """
        self._logger.debug("running")
        self._debounce_time_line_edit.setText(val)
        self._logger.debug("done")

    @property
    def open_inf_check_box(self) -> bool:
        """
        Get the check box state.
        :return bool: returns true if the box is checked.
        """
        return self._open_inf_check_box.isChecked()

    @open_inf_check_box.setter
    def open_inf_check_box(self, val: bool) -> None:
        """
        Set the check box state.
        :param val: bool value to set the check box.
        :return None:
        """
        self._logger.debug("running")
        self._open_inf_check_box.setChecked(val)
        self._logger.debug("done")

    @property
    def close_inf_check_box(self) -> bool:
        """
        Get the check box state.
        :return bool: returns true if the box is checked.
        """
        return self._close_inf_check_box.isChecked()

    @close_inf_check_box.setter
    def close_inf_check_box(self, val: bool) -> None:
        """
        Set the check box state.
        :param val: bool value to set the check box.
        :return None:
        """
        self._logger.debug("running")
        self._close_inf_check_box.setChecked(val)
        self._logger.debug("done")

    @property
    def button_mode(self) -> int:
        """
        Get index of button mode.
        :return int: index of current button mode.
        """
        return self._button_mode_selector.currentIndex()

    @button_mode.setter
    def button_mode(self, val: int) -> None:
        """
        Set index of button mode.
        :return None:
        """
        self._logger.debug("running")
        self._button_mode_selector.setCurrentIndex(val)
        self._logger.debug("done")

    @property
    def control_mode(self) -> int:
        """
        Get index of button mode.
        :return int: index of current button mode.
        """
        return self._control_mode_selector.currentIndex()

    @control_mode.setter
    def control_mode(self, val: int) -> None:
        """
        Set index of button mode.
        :return None:
        """
        self._logger.debug("running")
        self._control_mode_selector.setCurrentIndex(val)
        self._logger.debug("done")

    def set_open_dur_err(self, err: bool) -> None:
        """
        Set this text entry to error style depending on err.
        :param err: If this text entry needs to be error styel
        :return None:
        """
        self._logger.debug("running")
        if err:
            self._open_dur_line_edit.setStyleSheet(tab_line_edit_error_style)
        else:
            self._open_dur_line_edit.setStyleSheet(
                tab_line_edit_compliant_style)
        self._logger.debug("done")

    def set_close_dur_err(self, err: bool) -> None:
        """
        Set this text entry to error style depending on err.
        :param err: If this text entry needs to be error styel
        :return None:
        """
        self._logger.debug("running")
        if err:
            self._close_dur_line_edit.setStyleSheet(tab_line_edit_error_style)
        else:
            self._close_dur_line_edit.setStyleSheet(
                tab_line_edit_compliant_style)
        self._logger.debug("done")

    def set_debounce_err(self, err: bool) -> None:
        """
        Set this text entry to error style depending on err.
        :param err: If this text entry needs to be error styel
        :return None:
        """
        self._logger.debug("running")
        if err:
            self._debounce_time_line_edit.setStyleSheet(
                tab_line_edit_error_style)
        else:
            self._debounce_time_line_edit.setStyleSheet(
                tab_line_edit_compliant_style)
        self._logger.debug("done")

    @property
    def language(self) -> LangEnum:
        """
        Get the current language setting
        :return LangEnum: The current language enumerator being used
        """
        return self._lang_enum

    @language.setter
    def language(self, lang: LangEnum) -> None:
        """
        Set the language for this view object and reload the text and tooltips.
        :param lang: the language to use.
        :return None:
        """
        self._logger.debug("running")
        self._strings = strings[lang]
        self._set_texts()
        self._set_tooltips()
        self._logger.debug("done")

    def set_upload_button(self, is_active: bool) -> None:
        """
        Set upload button activity to is_active.
        :param is_active: Whether this button should be active.
        :return None:
        """
        self._logger.debug("running")
        self._upload_settings_button.setEnabled(is_active)
        self._logger.debug("done")

    def _set_texts(self) -> None:
        """
        Set text fields of view object.
        :return None:
        """
        self._logger.debug("running")
        self._config_label.setText(self._strings[StringsEnum.CONFIG_LABEL])
        self._config_val_line_edit.setText(
            self._strings[StringsEnum.CONFIG_LABEL])
        self._nhtsa_button.setText(self._strings[StringsEnum.NHTSA_LABEL])
        self._eblindfold_button.setText(
            self._strings[StringsEnum.EBLIND_LABEL])
        self._direct_control_button.setText(
            self._strings[StringsEnum.DCON_LABEL])
        self._open_dur_label.setText(
            self._strings[StringsEnum.OPEN_DURATION_LABEL])
        self._open_inf_check_box.setText(self._strings[StringsEnum.INF_LABEL])
        self._close_dur_label.setText(
            self._strings[StringsEnum.CLOSE_DURATION_LABEL])
        self._close_inf_check_box.setText(self._strings[StringsEnum.INF_LABEL])
        self._debounce_label.setText(self._strings[StringsEnum.DEBOUNCE_LABEL])
        self._button_mode_label.setText(
            self._strings[StringsEnum.BUTTON_MODE_LABEL])
        self._button_mode_selector.setItemText(
            0, self._strings[StringsEnum.HOLD_VAL_LABEL])
        self._button_mode_selector.setItemText(
            1, self._strings[StringsEnum.CLICK_VAL_LABEL])
        self._control_mode_label.setText(
            self._strings[StringsEnum.CONTROL_MODE_LABEL])
        self._control_mode_selector.setItemText(
            0, self._strings[StringsEnum.LENS_VAL_LABEL])
        self._control_mode_selector.setItemText(
            1, self._strings[StringsEnum.TRIAL_VAL_LABEL])
        self._upload_settings_button.setText(
            self._strings[StringsEnum.UPLOAD_BUTTON_LABEL])
        self._manual_control_open_button.setText(
            self._strings[StringsEnum.MANUAL_OPEN_LABEL])
        self._manual_control_close_button.setText(
            self._strings[StringsEnum.MANUAL_CLOSE_LABEL])
        self.config_button.setText(self._strings[StringsEnum.CONFIG_TAB_LABEL])
        self.config_button.setText("...")
        self._config_win.setWindowTitle(
            self.get_name() + " " +
            self._strings[StringsEnum.CONFIG_TAB_LABEL])
        self._logger.debug("done")

    def _set_tooltips(self) -> None:
        """
        Set tooltip text fields of view object.
        :return None:
        """
        self._logger.debug("running")
        self._config_label.setToolTip(
            self._strings[StringsEnum.CONFIG_LABEL_TOOLTIP])
        self._config_val_line_edit.setToolTip(
            self._strings[StringsEnum.CONFIG_LABEL_TOOLTIP])
        self._open_dur_label.setToolTip(
            self._strings[StringsEnum.OPEN_DURATION_TOOLTIP])
        self._close_dur_label.setToolTip(
            self._strings[StringsEnum.CLOSE_DURATION_TOOLTIP])
        self._debounce_label.setToolTip(
            self._strings[StringsEnum.DEBOUNCE_TOOLTIP])
        self._button_mode_label.setToolTip(
            self._strings[StringsEnum.BUTTON_MODE_TOOLTIP])
        self._control_mode_label.setToolTip(
            self._strings[StringsEnum.CONTROL_MODE_TOOLTIP])
        self._upload_settings_button.setToolTip(
            self._strings[StringsEnum.UPLOAD_BUTTON_TOOLTIP])
        self._manual_control_open_button.setToolTip(
            self._strings[StringsEnum.MANUAL_OPEN_TOOLTIP])
        self._manual_control_close_button.setToolTip(
            self._strings[StringsEnum.MANUAL_CLOSE_TOOLTIP])
        self.config_button.setToolTip(
            self._strings[StringsEnum.CONFIG_TAB_TOOLTIP])
        self._logger.debug("done")
Ejemplo n.º 27
0
class main(QWidget):
    def __init__(self, parent=None):
        super(main, self).__init__(parent)
        self.setup()  # connections, widgets, layouts, etc.

        self.blksize = 2**20  # 1 MB; must be divisible by 16
        self.ext = '.enc'  # extension is appended to encrypted files
        self.path = ''
        self.encrypted = []  # to highlight done files in list
        self.decrypted = []

        self.clipboard = QApplication.clipboard()
        self.timeout = None  # to clear message label, see setMessage

        # this program was just an excuse to play with QprogressBar
        if not hash(os.urandom(11)) % 11:
            QTimer().singleShot(50, self.windDown)

        # various random hints
        hints = [
            'Freshly encrypted files can be renamed in the table!',
            'Clipboard is always cleared on program close!',
            'Keys can contain emoji if you <em>really</em> want: \U0001f4e6',
            'Keys can contain emoji if you <em>really</em> want: \U0001F511',
            'This isn\'t a tip, I just wanted to say hello!',
            'Keys can be anywhere from 8 to 4096 characters long!',
            'This program was just an excuse to play with the progress bars!',
            'Select \'Party\' in the hash button for progress bar fun!',
            ('Did you know you can donate one or all of your vital organs to '
             'the Aperture Science Self-Esteem Fund for Girls? It\'s true!'),
            ('It\'s been {:,} days since Half-Life 2: Episode '
             'Two'.format(int((time.time() - 1191988800) / 86400))),
            'I\'m version {}!'.format(VERSION),
            'I\'m version {}.whatever!'.format(VERSION.split('.')[0]),
            ('Brought to you by me, I\'m <a href="https://orthallelous.word'
             'press.com/">Orthallelous!</a>'),
            #'Brought to you by me, I\'m Htom Sirveaux!',
            'I wonder if there\'s beer on the sun',
            'Raspberry World: For all your raspberry needs. Off the beltline',
            #'I\'ve plummented to my death and I can\'t get up',
            '<em>NOT</em> compatible with the older version!',
            ('Hello there, fellow space travellers! Until somebody gives me '
             'some new lines in KAS, that is all I can say. - Bentusi Exchange'
             )
        ]
        if not hash(os.urandom(9)) % 4:
            self.extraLabel.setText(random.choice(hints))

    def genKey(self):
        "generate a random key"
        n = self.keySizeSB.value()
        char = string.printable.rstrip()  #map(chr, range(256))
        while len(char) < n:
            char += char
        key = ''.join(random.sample(char, n))
        self.keyInput.setText(key)

    def showKey(self, state=None):
        "hide/show key characters"
        if state is None: state = bool(self.showKeyCB.checkState())
        else: state = bool(state)
        if state: self.keyInput.setEchoMode(QLineEdit.Normal)
        else: self.keyInput.setEchoMode(QLineEdit.PasswordEchoOnEdit)

    def getFolder(self):
        "open file dialog and fill file table"
        path = QFileDialog(directory=self.path).getExistingDirectory()
        if not path: return
        self.path = str(path)
        self.populateTable(self.path)
        self.encrypted, self.decrypted = [], []
        return

    def resizeEvent(self, event):
        self.showFolder(self.path)  # update how the folder is shown

    def splitterChanged(self, pos):
        self.showFolder(self.path)  # likewise

    def showFolder(self, path):
        "displays current path, truncating as needed"
        if not path: return

        ell, sl = '\u2026', os.path.sep  # ellipsis, slash chars
        lfg, rfg = Qt.ElideLeft, Qt.ElideRight
        lst, wdh = os.path.basename(path), self.folderLabel.width()

        path = path.replace(os.path.altsep or '\\', sl)
        self.folderLabel.setToolTip(path)

        # truncate folder location
        fnt = QFontMetrics(self.folderLabel.font())
        txt = str(fnt.elidedText(path, lfg, wdh))

        if len(txt) <= 1:  # label is way too short
            self.folderLabel.setText('\u22ee' if txt != sl else txt)
            return  # but when would this happen?

        # truncate some more (don't show part of a folder name)
        if len(txt) < len(path) and txt[1] != sl:
            txt = ell + sl + txt.split(sl, 1)[-1]

            # don't truncate remaining folder name from the left
            if txt[2:] != lst and len(txt[2:]) < len(lst) + 2:
                txt = str(fnt.elidedText(ell + sl + lst, rfg, wdh))
        # you'd think len(txt) < len(lst) would work, but no; you'd be wrong

        self.folderLabel.setText(txt)

    def populateTable(self, path):
        "fill file table with file names"
        self.showFolder(path)

        names = []
        for n in os.listdir(path):
            if os.path.isdir(os.path.join(path, n)): continue  # folder
            names.append(n)

        self.folderTable.clearContents()
        self.folderTable.setRowCount(len(names))
        self.folderTable.setColumnCount(1)

        if not names:  # no files in this folder, inform user
            self.setMessage('This folder has no files')
            return

        self.folderTable.blockSignals(True)
        selEnab = Qt.ItemIsSelectable | Qt.ItemIsEnabled
        for i, n in enumerate(names):
            item = QTableWidgetItem()
            item.setText(n)
            item.setToolTip(n)
            item.setFlags(selEnab)

            # color code encrypted/decrypted files
            if n in self.encrypted:
                item.setTextColor(QColor(211, 70, 0))
                # allowed encrypted filenames to be changed
                item.setFlags(selEnab | Qt.ItemIsEditable)
            if n in self.decrypted:
                item.setForeground(QColor(0, 170, 255))
            self.folderTable.setItem(i, 0, item)
        if len(names) > 5:
            self.setMessage('{:,} files'.format(len(names)), 7)
        self.folderTable.blockSignals(False)
        return

    def editFileName(self, item):
        "change file name"
        new, old = str(item.text()), str(item.toolTip())

        result = QMessageBox.question(
            self, 'Renaming?',
            ("<p align='center'>Do you wish to rename<br>" +
             '<span style="color:#d34600;">{}</span>'.format(old) +
             "<br>to<br>" +
             '<span style="color:#ef4b00;">{}</span>'.format(new) +
             '<br>?</p>'))

        self.folderTable.blockSignals(True)
        if any(i in new for i in '/?<>:*|"^'):
            self.setMessage('Invalid character in name', 7)
            item.setText(old)
        elif result == QMessageBox.Yes:
            oold = os.path.join(self.path, old)
            try:
                os.rename(oold, os.path.join(self.path, new))
                self.encrypted.remove(old)
                self.encrypted.append(new)
                item.setToolTip(new)
            except Exception as err:
                self.setMessage(str(err), 9)
                item.setText(old)
                item.setToolTip(old)
                self.encrypted.remove(new)
                self.encrypted.append(old)
        else:
            item.setText(old)
        self.folderTable.blockSignals(False)

    def setMessage(self, message, secs=4, col=None):
        "show a message for a few seconds - col must be rgb triplet tuple"
        if self.timeout:  # https://stackoverflow.com/a/21081371
            self.timeout.stop()
            self.timeout.deleteLater()

        if col is None: color = 'rgb(255, 170, 127)'
        else:
            try:
                color = 'rgb({}, {}, {})'.format(*col)
            except:
                color = 'rgb(255, 170, 127)'

        self.messageLabel.setStyleSheet('background-color: {};'.format(color))
        self.messageLabel.setText(message)
        self.messageLabel.setToolTip(message)

        self.timeout = QTimer()
        self.timeout.timeout.connect(self.clearMessage)
        self.timeout.setSingleShot(True)
        self.timeout.start(secs * 1000)

    def clearMessage(self):
        self.messageLabel.setStyleSheet('')
        self.messageLabel.setToolTip('')
        self.messageLabel.setText('')

    def getName(self):
        "return file name of selected"
        items = self.folderTable.selectedItems()
        names = [str(i.text()) for i in items]
        if names: return names[0]  # only the first selected file
        else: return ''

    def showKeyLen(self, string):
        "displays a tooltip showing length of key"
        s = len(string)
        note = '{:,} character{}'.format(s, '' if s == 1 else 's')
        tip = QToolTip
        pos = self.genKeyButton.mapToGlobal(QPoint(0, 0))

        if s < self.minKeyLen:
            note = '<span style="color:#c80000;">{}</span>'.format(note)
        else:
            note = '<span style="color:#258f22;">{}</span>'.format(note)
        tip.showText(pos, note)

    def lock(self, flag=True):
        "locks buttons if True"
        stuff = [
            self.openButton,
            self.encryptButton,
            self.decryptButton,
            self.genKeyButton,
            self.hashButton,
            self.showKeyCB,
            self.copyButton,
            self.keyInput,
            self.keySizeSB,
            self.folderTable,
        ]
        for i in stuff:
            i.blockSignals(flag)
            i.setEnabled(not flag)
        return

    def _lerp(self, v1, v2, numPts=10):
        "linearly interpolate from v1 to v2\nFrom Orthallelous"
        if len(v1) != len(v2): raise ValueError("different dimensions")
        D, V, n = [], [], abs(numPts)
        for i, u in enumerate(v1):
            D.append(v2[i] - u)
        for i in range(n + 1):
            vn = []
            for j, u in enumerate(v1):
                vn.append(u + D[j] / float(n + 2) * i)
            V.append(tuple(vn))
        return V

    def weeeeeee(self):
        "party time"
        self.lock()
        self.setMessage('Party time!', 2.5)
        a, b, c = self.encryptPbar, self.decryptPbar, self.hashPbar
        process, sleep = app.processEvents, time.sleep

        am, bm, cm = a.minimum(), b.minimum(), c.minimum()
        ax, bx, cx = a.maximum(), b.maximum(), c.maximum()
        a.reset()
        b.reset()
        c.reset()

        loops = self._lerp((am, bm, cm), (ax, bx, cx), 100)
        ivops = loops[::-1]

        # up and up!
        for i in range(3):
            for j, k, l in loops:
                a.setValue(int(j))
                b.setValue(int(k))
                c.setValue(int(l))
                process()
                sleep(0.01)

        a.setValue(ax)
        b.setValue(bx)
        c.setValue(cx)
        sleep(0.25)
        a.setValue(am)
        b.setValue(bm)
        c.setValue(cm)

        # snake!
        self.setMessage('Snake time!')
        self.messageLabel.setStyleSheet('background-color: rgb(127,170,255);')
        for i in range(2):
            for j, k, l in loops:
                a.setValue(int(j))
                process()
                sleep(0.002)
            process()
            a.setInvertedAppearance(True)
            process()
            for j, k, l in ivops:
                a.setValue(int(j))
                process()
                sleep(0.002)

            for j, k, l in loops:
                b.setValue(int(k))
                process()
                sleep(0.002)
            process()
            b.setInvertedAppearance(False)
            process()
            for j, k, l in ivops:
                b.setValue(int(k))
                process()
                sleep(0.002)

            for j, k, l in loops:
                c.setValue(int(l))
                process()
                sleep(0.002)
            process()
            c.setInvertedAppearance(True)
            process()
            for j, k, l in ivops:
                c.setValue(int(l))
                process()
                sleep(0.002)

            process()
            b.setInvertedAppearance(True)
            process()
            for j, k, l in loops:
                b.setValue(int(k))
                process()
                sleep(0.002)
            process()
            b.setInvertedAppearance(False)
            process()
            for j, k, l in ivops:
                b.setValue(int(k))
                process()
                sleep(0.002)
            process()

            a.setInvertedAppearance(False)
            b.setInvertedAppearance(True)
            c.setInvertedAppearance(False)
        for j, k, l in loops:
            a.setValue(int(j))
            process()
            sleep(0.002)
        process()
        a.setInvertedAppearance(True)
        process()
        for j, k, l in ivops:
            a.setValue(int(j))
            process()
            sleep(0.002)

        # bars
        sleep(0.5)
        self.setMessage('Bars!')
        process()
        self.messageLabel.setStyleSheet('background-color: rgb(127,255,170);')
        for i in range(2):
            a.setValue(ax)
            time.sleep(0.65)
            a.setValue(am)
            sleep(0.25)
            process()
            b.setValue(bx)
            time.sleep(0.65)
            b.setValue(bm)
            sleep(0.25)
            process()
            c.setValue(cx)
            time.sleep(0.65)
            c.setValue(cm)
            sleep(0.25)
            process()
            b.setValue(bx)
            time.sleep(0.65)
            b.setValue(bm)
            sleep(0.25)
            process()

        # okay, enough
        process()
        a.setValue(ax)
        b.setValue(bx)
        c.setValue(cx)
        #a.setValue(am); b.setValue(bm); c.setValue(cm)
        a.setInvertedAppearance(False)
        b.setInvertedAppearance(True)
        c.setInvertedAppearance(False)
        self.lock(False)
        return

    def windDown(self, note=None):
        "silly deload on load"
        if note is None: note = 'Loading...'
        self.lock()
        self.setMessage(note)
        self.messageLabel.setStyleSheet('background-color: rgb(9, 190, 130);')
        a, b, c = self.encryptPbar, self.decryptPbar, self.hashPbar
        am, bm, cm = a.minimum(), b.minimum(), c.minimum()
        ax, bx, cx = a.maximum(), b.maximum(), c.maximum()
        a.reset()
        b.reset()
        c.reset()
        loops = self._lerp((ax, bx, cx), (am, bm, cm), 100)
        for j, k, l in loops:
            a.setValue(int(j))
            b.setValue(int(k))
            c.setValue(int(l))
            app.processEvents()
            time.sleep(0.02)
        a.reset()
        b.reset()
        c.reset()
        self.lock(False)
        self.clearMessage()

    def genHash(self, action):
        "generate hash of selected file and display it"
        name, t0 = self.getName(), time.perf_counter()

        # mark what hash was used in the drop-down menu
        for i in self.hashButton.menu().actions():
            if i == action: i.setIconVisibleInMenu(True)
            else: i.setIconVisibleInMenu(False)

        if str(action.text()) == 'Party':
            self.weeeeeee()
            self.windDown('Winding down...')
            return
        if not name:
            self.setMessage('No file selected')
            return
        if not os.path.exists(os.path.join(self.path, name)):
            self.setMessage('File does not exist')
            return

        self.lock()
        hsh = self.hashFile(os.path.join(self.path, name),
                            getattr(hashlib, str(action.text())))
        self.lock(False)
        #hsh = str(action.text()) + ': ' + hsh
        self.hashLabel.setText(hsh)
        self.hashLabel.setToolTip(hsh)
        self.extraLabel.setText(
            str(action.text()) + ' hash took ' +
            self.secs_fmt(time.perf_counter() - t0))

    def setCancel(self):
        "cancel operation"
        self._requestStop = True

    def showCancelButton(self, state=False):
        "show/hide cancel button"
        self.cancelButton.blockSignals(not state)
        self.cancelButton.setEnabled(state)
        if state:
            self.cancelButton.show()
            self.keyInput.hide()
            self.genKeyButton.hide()
            self.keySizeSB.hide()
        else:
            self.cancelButton.hide()
            self.keyInput.show()
            self.genKeyButton.show()
            self.keySizeSB.show()

    def hashFile(self, fn, hasher):
        "returns the hash value of a file"
        hsh, blksize = hasher(), self.blksize
        fsz, csz = os.path.getsize(fn), 0.0

        self.hashPbar.reset()
        self.showCancelButton(True)
        prog, title = '(# {:.02%}) {}', self.windowTitle()
        with open(fn, 'rb') as f:
            while 1:
                blk = f.read(blksize)
                if not blk: break
                hsh.update(blk)

                csz += blksize
                self.hashPbar.setValue(int(round(csz * 100.0 / fsz)))
                app.processEvents()
                self.setWindowTitle(prog.format(csz / fsz, title))
                if self._requestStop: break

        self.hashPbar.setValue(self.hashPbar.maximum())
        self.setWindowTitle(title)
        self.showCancelButton(False)

        if self._requestStop:
            self.setMessage('Hashing canceled!')
            self.hashPbar.setValue(self.hashPbar.minimum())
            self._requestStop = False
            return
        return hsh.hexdigest()

    def hashKey(self, key, salt=b''):
        "hashes a key for encrypting/decrypting file"
        salt = salt.encode() if type(salt) != bytes else salt
        key = key.encode() if type(key) != bytes else key
        p = app.processEvents
        self.setMessage('Key Hashing...', col=(226, 182, 249))
        p()
        key = hashlib.pbkdf2_hmac('sha512', key, salt, 444401)
        p()
        self.clearMessage()
        p()
        return hashlib.sha3_256(key).digest()  # AES requires a 32 char key

    def encrypt(self):
        "encrypt selected file with key"
        name, t0 = self.getName(), time.perf_counter()
        if not name:
            self.setMessage('No file selected')
            return
        if not os.path.exists(os.path.join(self.path, name)):
            self.setMessage('File does not exist')
            return
        key = str(self.keyInput.text())
        if len(key) < self.minKeyLen:
            self.setMessage(('Key must be at least '
                             '{} characters long').format(self.minKeyLen))
            return

        self.lock()
        gn = self.encryptFile(key, os.path.join(self.path, name))
        if not gn:
            self.lock(False)
            return
        self.encrypted.append(os.path.basename(gn))
        self.lock(False)

        self.populateTable(self.path)  # repopulate folder list
        bn, tt = os.path.basename(gn), time.perf_counter() - t0
        self.setMessage('Encrypted, saved "{}"'.format(bn, 13))
        self.extraLabel.setText('Encrypting took ' + self.secs_fmt(tt))

    def encryptFile(self, key, fn):
        "encrypts a file using AES (MODE_GCM)"
        chars = ''.join(map(chr, range(256))).encode()
        chk = AES.block_size
        sample = random.sample
        iv = bytes(sample(chars, chk * 2))
        salt = bytes(sample(chars * 2, 256))

        vault = AES.new(self.hashKey(key, salt), AES.MODE_GCM, iv)
        fsz = os.path.getsize(fn)
        del key
        blksize = self.blksize
        gn = fn + self.ext

        fne = os.path.basename(fn).encode()
        fnz = len(fne)
        if len(fne) % chk: fne += bytes(sample(chars, chk - len(fne) % chk))

        csz = 0.0  # current processed value
        self.encryptPbar.reset()
        prog, title = '({:.02%}) {}', self.windowTitle()
        self.showCancelButton(True)

        with open(fn, 'rb') as src, open(gn, 'wb') as dst:
            dst.write(bytes([0] * 16))  # spacer for MAC written at end
            dst.write(iv)
            dst.write(salt)  # store iv, salt
            # is it safe to store MAC, iv, salt plain right in file?
            # can't really store them encrypted,
            # or elsewhere in this model of single file encryption?
            # can't have another file for the file to lug around

            # store file size, file name length
            dst.write(vault.encrypt(struct.pack('<2Q', fsz, fnz)))
            dst.write(vault.encrypt(fne))  # store filename

            while 1:
                dat = src.read(blksize)
                if not dat: break
                elif len(dat) % chk:  # add padding
                    fil = chk - len(dat) % chk
                    dat += bytes(sample(chars, fil))
                dst.write(vault.encrypt(dat))

                csz += blksize  # show progress
                self.encryptPbar.setValue(int(round(csz * 100.0 / fsz)))
                self.setWindowTitle(prog.format(csz / fsz, title))
                app.processEvents()

                if self._requestStop: break
            if not self._requestStop:
                stuf = random.randrange(23)  # pack in more stuffing
                fing = b''.join(bytes(sample(chars, 16)) for i in range(stuf))
                dst.write(vault.encrypt(fing))  # and for annoyance

                dst.seek(0)
                dst.write(vault.digest())  # write MAC
                self.hashLabel.setText('MAC: ' + vault.hexdigest())

        self.encryptPbar.setValue(self.encryptPbar.maximum())
        self.setWindowTitle(title)
        self.showCancelButton(False)

        if self._requestStop:
            self.setMessage('Encryption canceled!')
            self.encryptPbar.setValue(self.encryptPbar.minimum())
            self._requestStop = False
            os.remove(gn)
            return
        return gn

    def decrypt(self):
        "encrypt selected file with key"
        name, t0 = self.getName(), time.perf_counter()
        if not name:
            self.setMessage('No file selected')
            return
        if not os.path.exists(os.path.join(self.path, name)):
            self.setMessage('File does not exist')
            return
        key = str(self.keyInput.text())
        if len(key) < self.minKeyLen:
            self.setMessage(('Key must be at least '
                             '{} characters long').format(self.minKeyLen))
            return

        self.lock()
        gn = self.decryptFile(key, os.path.join(self.path, name))
        if not gn:
            self.lock(False)
            return
        self.decrypted.append(os.path.basename(gn))
        self.lock(False)

        self.populateTable(self.path)  # repopulate folder list
        bn, tt = os.path.basename(gn), time.perf_counter() - t0
        self.setMessage('Decrypted, saved "{}"'.format(bn, 13))
        self.extraLabel.setText('Decrypting took ' + self.secs_fmt(tt))

    def decryptFile(self, key, fn):
        "decrypts a file using AES (MODE_GCM)"
        blksize = self.blksize
        gn = hashlib.md5(os.path.basename(fn).encode()).hexdigest()
        gn = os.path.join(self.path, gn)  # temporary name
        if os.path.exists(gn):
            self.setMessage('file already exists')
            return

        self.decryptPbar.reset()
        csz = 0.0  # current processed value
        chk, fnsz = AES.block_size, os.path.getsize(fn)
        prog, title = '({:.02%}) {}', self.windowTitle()
        try:
            with open(fn, 'rb') as src, open(gn, 'wb') as dst:
                # extract iv, salt
                MAC = src.read(16)
                iv = src.read(AES.block_size * 2)
                salt = src.read(256)
                vault = AES.new(self.hashKey(key, salt), AES.MODE_GCM, iv)
                self.showCancelButton(True)

                # extract file size, file name length
                sizes = src.read(struct.calcsize('<2Q'))
                fsz, fnz = struct.unpack('<2Q', vault.decrypt(sizes))

                # extract filename; round up fnz to nearest chk
                rnz = fnz if not fnz % chk else fnz + chk - fnz % chk
                rfn = vault.decrypt(src.read(rnz))[:fnz].decode()
                self.setMessage('Found "{}"'.format(rfn), 13, (255, 211, 127))

                while 1:
                    dat = src.read(blksize)
                    if not dat: break
                    dst.write(vault.decrypt(dat))

                    csz += blksize  # show progress
                    self.decryptPbar.setValue(int(round(csz * 100.0 / fnsz)))
                    self.setWindowTitle(prog.format(1 - (csz / fnsz), title))
                    app.processEvents()
                    if self._requestStop: break

                if not self._requestStop: dst.truncate(fsz)  # remove padding
            if not self._requestStop:
                vault.verify(MAC)
                self.hashLabel.setText('')

        except (ValueError, KeyError) as err:
            os.remove(gn)
            self.setMessage('Invalid decryption!')
            self.setWindowTitle(title)
            self.showCancelButton(False)
            return
        except Exception as err:
            os.remove(gn)
            self.setMessage('Invalid key or file!')
            self.setWindowTitle(title)
            self.showCancelButton(False)
            return
        self.decryptPbar.setValue(self.decryptPbar.maximum())
        self.setWindowTitle(title)
        self.showCancelButton(False)

        if self._requestStop:
            self.setMessage('Decryption canceled!')
            self.decryptPbar.setValue(self.decryptPbar.minimum())
            self._requestStop = False
            os.remove(gn)
            return

        # restore original file name
        name, ext = os.path.splitext(rfn)
        count = 1
        fn = os.path.join(self.path, name + ext)
        while os.path.exists(fn):
            fn = os.path.join(self.path, name + '_{}'.format(count) + ext)
            count += 1
        os.rename(gn, fn)  # restore original name
        return fn  # saved name

    def copyKeyHash(self, action):
        "copies either the key or the hash to clipboard"
        act = str(action.text()).lower()

        if 'key' in act: txt = str(self.keyInput.text())
        elif 'hash' in act: txt = str(self.hashLabel.text())
        else:
            self.setMessage('Invalid copy selection')
            return

        if not txt:
            self.setMessage('Empty text; Nothing to copy')
            return

        if 'key' in act: self.setMessage('Key copied to clipboard')
        elif 'hash' in act: self.setMessage('Hash copied to clipboard')
        else:
            self.setMessage('Invalid copy selection')
            return

        self.clipboard.clear()
        self.clipboard.setText(txt)

    def secs_fmt(self, s):
        "6357 -> '1h 45m 57s'"
        Y, D, H, M = 31556952, 86400, 3600, 60
        y = int(s // Y)
        s -= y * Y
        d = int(s // D)
        s -= d * D
        h = int(s // H)
        s -= h * H
        m = int(s // M)
        s -= m * M

        r = (str(int(s)) if int(s) == s else str(round(s, 3))) + 's'

        if m: r = str(m) + 'm ' + r
        if h: r = str(h) + 'h ' + r
        if d: r = str(d) + 'd ' + r
        if y: r = str(y) + 'y ' + r
        return r.strip()

    def closeEvent(self, event):
        self.clipboard.clear()

    def setup(self):
        "constructs the gui"
        Fixed = QSizePolicy()
        MinimumExpanding = QSizePolicy(QSizePolicy.MinimumExpanding,
                                       QSizePolicy.MinimumExpanding)
        self.minKeyLen = 8
        self.maxKeyLen = 4096

        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Horizontal)
        self.splitter.splitterMoved.connect(self.splitterChanged)

        # left column
        self.leftColumn = QWidget()
        self.vl01 = QVBoxLayout()

        # left column - first item (0; horizonal layout 0)
        self.hl00 = QHBoxLayout()
        self.hl00.setSpacing(5)

        self.openButton = QPushButton('&Open')
        self.openButton.setToolTip('Open folder')
        self.openButton.setMinimumSize(60, 20)
        self.openButton.setMaximumSize(60, 20)
        self.openButton.setSizePolicy(Fixed)
        self.openButton.clicked.connect(self.getFolder)
        #ico = self.style().standardIcon(QStyle.SP_DirIcon)
        #self.openButton.setIcon(ico)

        self.folderLabel = QLabel()
        self.folderLabel.setMinimumSize(135, 20)
        self.folderLabel.setMaximumSize(16777215, 20)
        self.folderLabel.setSizePolicy(MinimumExpanding)
        self.hl00.insertWidget(0, self.openButton)
        self.hl00.insertWidget(1, self.folderLabel)

        # left column - second item (1)
        self.folderTable = QTableWidget()
        self.folderTable.setMinimumSize(200, 32)
        self.folderTable.horizontalHeader().setVisible(False)
        self.folderTable.horizontalHeader().setStretchLastSection(True)
        self.folderTable.verticalHeader().setVisible(False)
        self.folderTable.verticalHeader().setDefaultSectionSize(15)
        self.folderTable.itemChanged.connect(self.editFileName)

        # left column - third item (2)
        self.extraLabel = QLabel()
        self.extraLabel.setMinimumSize(200, 20)
        self.extraLabel.setMaximumSize(16777215, 20)
        self.extraLabel.setSizePolicy(MinimumExpanding)
        self.extraLabel.setTextInteractionFlags(Qt.LinksAccessibleByMouse)

        # finalize left column
        self.vl01.insertLayout(0, self.hl00)
        self.vl01.insertWidget(1, self.folderTable)
        self.vl01.insertWidget(2, self.extraLabel)
        self.leftColumn.setLayout(self.vl01)

        # right column
        self.rightColumn = QWidget()
        self.vl02 = QVBoxLayout()

        # right column - first item (0)
        self.messageLabel = QLabel()
        self.messageLabel.setMinimumSize(290, 20)
        self.messageLabel.setMaximumSize(16777215, 20)
        self.messageLabel.setSizePolicy(MinimumExpanding)
        self.messageLabel.setAlignment(Qt.AlignCenter)

        # right column - second item (2; horizontal layout 1)
        self.hl01 = QHBoxLayout()
        self.hl01.setSpacing(5)

        self.encryptButton = QPushButton('&Encrypt')  #\U0001F512
        self.encryptButton.setToolTip('Encrypt selected file')
        self.encryptButton.setMinimumSize(60, 20)
        self.encryptButton.setMaximumSize(60, 20)
        self.encryptButton.setSizePolicy(Fixed)
        self.encryptButton.clicked.connect(self.encrypt)

        self.encryptPbar = QProgressBar()
        self.encryptPbar.setMinimumSize(225, 20)
        self.encryptPbar.setMaximumSize(16777215, 20)
        self.encryptPbar.setSizePolicy(MinimumExpanding)
        self.encryptPbar.setTextVisible(False)

        palette = self.encryptPbar.palette()  # color of progress bar
        color = QColor(211, 70, 0)
        palette.setColor(QPalette.Highlight, color)
        self.encryptPbar.setPalette(palette)

        self.hl01.insertWidget(0, self.encryptButton)
        self.hl01.insertWidget(1, self.encryptPbar)

        # right column - third item (3; horizontal layout 2)
        self.hl02 = QHBoxLayout()
        self.hl02.setSpacing(5)

        self.cancelButton = QPushButton('C&ANCEL')
        self.cancelButton.setToolTip('Cancels current operation')
        self.cancelButton.setMinimumSize(70, 24)
        self.cancelButton.setMaximumSize(70, 24)
        self.cancelButton.setSizePolicy(Fixed)
        self.cancelButton.clicked.connect(self.setCancel)
        font = self.cancelButton.font()
        font.setBold(True)
        self.cancelButton.setFont(font)
        self.cancelButton.blockSignals(True)
        self.cancelButton.setEnabled(False)
        self.cancelButton.hide()
        self._requestStop = False

        self.keyInput = QLineEdit()
        self.keyInput.setMinimumSize(225, 20)
        self.keyInput.setMaximumSize(16777215, 20)
        self.keyInput.setSizePolicy(MinimumExpanding)
        self.keyInput.setPlaceholderText('key')
        self.keyInput.setMaxLength(self.maxKeyLen)
        self.keyInput.setAlignment(Qt.AlignCenter)
        self.keyInput.textEdited.connect(self.showKeyLen)

        self.genKeyButton = QPushButton('&Gen Key')  #\U0001F511
        self.genKeyButton.setToolTip('Generate a random key')
        self.genKeyButton.setMinimumSize(60, 20)
        self.genKeyButton.setMaximumSize(60, 20)
        self.genKeyButton.setSizePolicy(Fixed)
        self.genKeyButton.clicked.connect(self.genKey)

        self.keySizeSB = QSpinBox()
        self.keySizeSB.setToolTip('Length of key to generate')
        self.keySizeSB.setRange(32, 1024)
        self.keySizeSB.setMinimumSize(40, 20)
        self.keySizeSB.setMaximumSize(40, 20)
        self.keySizeSB.setSizePolicy(Fixed)
        self.keySizeSB.setAlignment(Qt.AlignCenter)
        self.keySizeSB.setButtonSymbols(QSpinBox.NoButtons)
        self.keySizeSB.setWrapping(True)

        self.hl02.insertWidget(0, self.cancelButton)
        self.hl02.insertWidget(1, self.keyInput)
        self.hl02.insertWidget(2, self.genKeyButton)
        self.hl02.insertWidget(3, self.keySizeSB)

        # right column - fourth item (4; horizontal layout 3)
        self.hl03 = QHBoxLayout()
        self.hl03.setSpacing(5)

        self.decryptButton = QPushButton('&Decrypt')  #\U0001F513
        self.decryptButton.setToolTip('Decrypt selected file')
        self.decryptButton.setMinimumSize(60, 20)
        self.decryptButton.setMaximumSize(60, 20)
        self.decryptButton.setSizePolicy(Fixed)
        self.decryptButton.clicked.connect(self.decrypt)

        self.decryptPbar = QProgressBar()
        self.decryptPbar.setMinimumSize(225, 20)
        self.decryptPbar.setMaximumSize(16777215, 20)
        self.decryptPbar.setSizePolicy(MinimumExpanding)
        self.decryptPbar.setTextVisible(False)
        self.decryptPbar.setInvertedAppearance(True)

        palette = self.decryptPbar.palette()  # color of progress bar
        color = QColor(0, 170, 255)
        palette.setColor(QPalette.Highlight, color)
        self.decryptPbar.setPalette(palette)

        self.hl03.insertWidget(0, self.decryptButton)
        self.hl03.insertWidget(1, self.decryptPbar)

        # right column - fifth item (7; horizontal layout 4)
        self.hl04 = QHBoxLayout()
        self.hl04.setSpacing(5)

        self.showKeyCB = QCheckBox('&Show Key')
        self.showKeyCB.setToolTip('Show/Hide key value')
        self.showKeyCB.setMinimumSize(75, 20)
        self.showKeyCB.setMaximumSize(75, 20)
        self.showKeyCB.setSizePolicy(Fixed)
        self.showKeyCB.clicked.connect(self.showKey)
        self.showKeyCB.setChecked(True)

        self.hashPbar = QProgressBar()
        self.hashPbar.setMinimumSize(150, 20)
        self.hashPbar.setMaximumSize(16777215, 20)
        self.hashPbar.setSizePolicy(MinimumExpanding)
        self.hashPbar.setTextVisible(False)

        palette = self.hashPbar.palette()  # color of progress bar
        color = QColor(31, 120, 73)
        palette.setColor(QPalette.Highlight, color)
        self.hashPbar.setPalette(palette)

        self.hashButton = QPushButton('&Hash')
        self.hashButton.setToolTip('Determine file hash')
        self.hashButton.setMinimumSize(60, 20)
        self.hashButton.setMaximumSize(60, 20)
        self.hashButton.setSizePolicy(Fixed)

        menu = QMenu(self.hashButton)
        ico = self.style().standardIcon(QStyle.SP_DialogYesButton)
        for alg in sorted(
                filter(lambda x: 'shake' not in x,
                       hashlib.algorithms_guaranteed),
                key=lambda n:
            (len(n), sorted(hashlib.algorithms_guaranteed).index(n))):
            menu.addAction(
                ico, alg
            )  # drop shake algs as their .hexdigest requires an argument - the rest don't
        menu.addAction(ico, 'Party')
        for i in menu.actions():
            i.setIconVisibleInMenu(False)
        self.hashButton.setMenu(menu)
        menu.triggered.connect(self.genHash)

        self.hl04.insertWidget(0, self.showKeyCB)
        self.hl04.insertWidget(1, self.hashPbar)
        self.hl04.insertWidget(2, self.hashButton)

        # right column - sixth item (8; horizontal layout 5)
        self.hl05 = QHBoxLayout()
        self.hl05.setSpacing(5)

        self.copyButton = QPushButton('&Copy')  #\U0001F4CB
        self.copyButton.setToolTip('Copy key or hash to clipboard')
        self.copyButton.setMinimumSize(60, 20)
        self.copyButton.setMaximumSize(60, 20)
        self.copyButton.setSizePolicy(Fixed)

        menu2 = QMenu(self.copyButton)
        menu2.addAction('Copy Key')
        menu2.addAction('Copy Hash')
        self.copyButton.setMenu(menu2)
        menu2.triggered.connect(self.copyKeyHash)

        self.hashLabel = QLabel()
        self.hashLabel.setMinimumSize(225, 20)
        self.hashLabel.setMaximumSize(16777215, 20)
        self.hashLabel.setSizePolicy(MinimumExpanding)
        self.hashLabel.setTextFormat(Qt.PlainText)
        self.hashLabel.setAlignment(Qt.AlignCenter)
        self.hashLabel.setTextInteractionFlags(Qt.TextSelectableByMouse)

        self.hl05.insertWidget(0, self.copyButton)
        self.hl05.insertWidget(1, self.hashLabel)

        # finalize right column
        self.vl02.insertWidget(0, self.messageLabel)
        self.vl02.insertSpacerItem(1, QSpacerItem(0, 0))
        self.vl02.insertLayout(2, self.hl01)
        self.vl02.insertLayout(3, self.hl02)
        self.vl02.insertLayout(4, self.hl03)
        self.vl02.insertSpacerItem(5, QSpacerItem(0, 0))
        self.vl02.insertWidget(6, QFrame())
        self.vl02.insertLayout(7, self.hl04)
        self.vl02.insertLayout(8, self.hl05)
        self.rightColumn.setLayout(self.vl02)

        # finalize main window
        self.splitter.insertWidget(0, self.leftColumn)
        self.splitter.insertWidget(1, self.rightColumn)

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

        self.setWindowTitle('Simple File Encryptor/Decryptor')
        self.resize(self.sizeHint())
Ejemplo n.º 28
0
def createReadOnlyLineEdit(content: str):
    lineEdit = QLineEdit(content)
    lineEdit.setAlignment(Qt.AlignCenter)
    lineEdit.setReadOnly(True)
    return lineEdit
Ejemplo n.º 29
0
class PyCalcUi(QMainWindow):
    """PyCalc's View (GUI)."""

    def __init__(self,
                 win_title: str = 'PyCalc',
                 win_size: Tuple[int, int] = (235, 235),
                 icon_path: Union[Path, str, None] = Path(R'.\coffeebean.ico')
                 ) -> None:
        """View initializer."""
        super().__init__()

        # Set some main window's properties
        self.setWindowTitle(win_title)
        self.setFixedSize(win_size[0], win_size[1])
        if isinstance(icon_path, Path):
            icon_path = str(icon_path.resolve())
        self.setWindowIcon(QIcon(icon_path))

        # Set the central widget
        self._centralWidget = QWidget(self)
        self.setCentralWidget(self._centralWidget)
        self.generalLayout = QVBoxLayout()
        self._centralWidget.setLayout(self.generalLayout)

        # Create the display and the buttons
        self._createDisplay()
        self._createButtons()

    def _createDisplay(self) -> None:
        """Create the display."""
        # Create the display widget
        self.display = QLineEdit()
        # Set some display's properties
        self.display.setFixedHeight(35)
        self.display.setAlignment(Qt.AlignRight)
        self.display.setReadOnly(True)
        # Add the display to the general layout
        self.generalLayout.addWidget(self.display)

    def _createButtons(self) -> None:
        """Create the buttons."""
        self.buttons: Dict[str, QPushButton] = {}
        buttonsLayout = QGridLayout()
        # Button text | position on the QGridLayout
        buttons: Dict[str, Tuple[int, int]] = {'7': (0, 0), '8': (0, 1), '9': (0, 2),
                                               '/': (0, 3),
                                               'C': (0, 4),
                                               '4': (1, 0), '5': (1, 1), '6': (1, 2),
                                               '*': (1, 3),
                                               '(': (1, 4),
                                               '1': (2, 0), '2': (2, 1), '3': (2, 2),
                                               '-': (2, 3),
                                               ')': (2, 4),
                                               '0': (3, 0), '00': (3, 1), '.': (3, 2),
                                               '+': (3, 3),
                                               '=': (3, 4),
                                               }
        # Create the buttons and add them to the grid layout
        for btnText, pos in buttons.items():
            self.buttons[btnText] = QPushButton(btnText)
            self.buttons[btnText].setFixedSize(40, 40)
            buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1])
            # Add buttonsLayout to the general layout
            self.generalLayout.addLayout(buttonsLayout)

    def setDisplayText(self, text: str) -> None:
        """Set display's text."""
        self.display.setText(text)
        self.display.setFocus()

    def displayText(self) -> str:
        """Get display's text."""
        return cast(str, self.display.text())

    def clearDisplay(self) -> None:
        """Clear the display."""
        self.setDisplayText('')
Ejemplo n.º 30
0
class InstructionRegister(QWidget):
    def __init__(self, parent, usbif, color):
        super().__init__(parent)
        self._status_inds = {}
        self._setup_ui(color)

        usbif.poll("monitor", um.MonRegI())
        usbif.listen(self)

    def handle_msg(self, msg):
        if isinstance(msg, um.MonRegI):
            self.set_i_values(msg.br, msg.st, msg.sqext, msg.sq)
        elif isinstance(msg, um.MonRegStatus):
            self._status_inds['iip'].indicator.set_on(msg.iip)
            self._status_inds['inhl'].indicator.set_on(msg.inhl)
            self._status_inds['inkl'].indicator.set_on(msg.inkl)
        #elif isinstance(msg, um.StatusPeripheral):
        #    self._status_inds['ld'].indicator.set_on(msg.ld)
        #    self._status_inds['chld'].indicator.set_on(msg.chld)
        #    self._status_inds['rd'].indicator.set_on(msg.rd)
        #    self._status_inds['chrd'].indicator.set_on(msg.chrd)

    def set_i_values(self, br, st, sqext, sq):
        self.br.set_value(br)
        self.st.set_value(st)
        self.sq.set_value(sqext << 6 | sq)

        self._inst_value.setText(agc.disassemble_subinst(sqext, sq, st))

    def _setup_ui(self, color):
        # Set up our basic layout
        layout = QHBoxLayout(self)
        self.setLayout(layout)
        layout.setSpacing(3)
        layout.setMargin(1)

        # Construct register groups for BR, ST, and SQ
        self.br = SubRegister(self, "BR", 2, color)
        self.st = SubRegister(self, "ST", 3, color)
        self.sq = SubRegister(self, "SQ", 7, color)
        layout.addWidget(self.br)
        layout.addWidget(self.st)
        layout.addWidget(self.sq)

        # Status indicators
        wl = QHBoxLayout()
        wl.setSpacing(1)
        wl.setContentsMargins(2, 3, 3, 2)
        layout.addLayout(wl)

        for name, label in STATUS_INDS.items():
            w = ApolloLabeledIndicator(self,
                                       label,
                                       QColor(0, 255, 255),
                                       lines=2,
                                       labelwidth=35)
            wl.addWidget(w)
            self._status_inds[name] = w

        # Create a grouping widget for the I label and decoded instruction value box
        label_value_layout = QHBoxLayout()
        label_value_layout.setSpacing(3)
        label_value_layout.setContentsMargins(0, 33, 0, 0)
        layout.addLayout(label_value_layout)

        # Create a value box for displaying the overall decoded instruction
        self._inst_value = QLineEdit(self)
        self._inst_value.setFixedSize(70, 32)
        self._inst_value.setReadOnly(True)
        self._inst_value.setAlignment(Qt.AlignCenter)
        self._inst_value.setText('TC0')
        self._inst_value.setStyleSheet("QLineEdit { color: #555; }")
        label_value_layout.addWidget(self._inst_value)

        # Create a label to show 'I'
        label = QLabel('I', self)
        label.setFixedWidth(20)
        label_value_layout.addWidget(label)