Exemple #1
0
class CodeTextEditor(QWidget):
    codeChanged = Signal()

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

        self.textEdit = QTextEdit()
        self.textEdit.setTabStopDistance(20)
        self.textEdit.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
        self.textEdit.textChanged.connect(self.codeChanged.emit)

        self.textEdit.setAcceptRichText(False)

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

        self._highlighter = PythonSyntaxHighlighter(self.textEdit.document())

    def SetCode(self, code: str):
        self.textEdit.setPlainText(code)

    def Code(self):
        return self.textEdit.toPlainText()
Exemple #2
0
class BuilderWidget(QWidget):
    def __init__(self, parent=None) -> None:
        super().__init__(parent)

        self.verticalLayout = QVBoxLayout(self)

        self.compileButton = QPushButton('Compile', self)
        self.compileButton.setMinimumSize(QSize(200, 50))
        self.verticalLayout.addWidget(self.compileButton, 0, Qt.AlignHCenter)

        self.tidyButton = QPushButton('Tidy', self)
        self.verticalLayout.addWidget(self.tidyButton, 0, Qt.AlignHCenter)

        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Vertical)

        self.widget = QWidget(self.splitter)
        self.widget.setObjectName(u"widget")
        self.verticalLayout2 = QVBoxLayout(self.widget)
        self.stdoutLabel = QLabel(self.widget)
        self.stdoutLabel.setText('stdout:')

        self.verticalLayout2.addWidget(self.stdoutLabel)

        self.stdoutText = QTextEdit(self.widget)
        self.stdoutText.setEnabled(False)
        self.stdoutText.setReadOnly(True)
        self.stdoutText.setPlainText("Output will appear here")

        self.verticalLayout2.addWidget(self.stdoutText)

        self.splitter.addWidget(self.widget)

        self.widget1 = QWidget(self.splitter)

        self.verticalLayout3 = QVBoxLayout(self.widget1)
        self.stderrLabel = QLabel(self.widget1)
        self.stderrLabel.setText('stderr:')

        self.verticalLayout3.addWidget(self.stderrLabel)

        self.stderrText = QTextEdit(self.widget1)
        self.stderrText.setEnabled(False)
        self.stderrText.setReadOnly(True)
        self.stderrText.setPlainText('Errors will appear here')

        self.verticalLayout3.addWidget(self.stderrText)

        self.splitter.addWidget(self.widget1)

        self.verticalLayout.addWidget(self.splitter)

        # Logic

        # Use QProcess to start a program and get its outputs https://stackoverflow.com/a/22110924
        self.process = QProcess(self)

        self.process.readyReadStandardOutput.connect(self.readStdout)
        self.process.readyReadStandardError.connect(self.readStderr)
        self.process.started.connect(self.processStarted)
        self.process.finished.connect(self.processFinished)
        self.process.errorOccurred.connect(self.errorOccurred)
        self.compileButton.clicked.connect(self.doCompile)
        self.tidyButton.clicked.connect(self.doTidy)

    def doCompile(self):
        self.cleanupUI()
        self.process.setWorkingDirectory(settings.get_repo_location())
        self.process.startCommand(settings.get_build_command())

    def doTidy(self):
        self.cleanupUI()
        self.process.setWorkingDirectory(settings.get_repo_location())
        self.process.startCommand(settings.get_tidy_command())

    def cleanupUI(self):
        self.stdoutText.setEnabled(True)
        self.stderrText.setEnabled(True)
        self.stdoutText.setPlainText('')
        self.stderrText.setPlainText('')

    def readStdout(self):
        lines = self.process.readAllStandardOutput().data().decode(
        )[:-1].split('\n')
        for line in lines:
            if line == 'tmc.gba: FAILED':
                line = 'tmc.gba: <b style="color:red">FAILED</b>'
            elif line == 'tmc.gba: OK':
                line = 'tmc.gba: <b style="color:lime">OK</b>'
            self.stdoutText.append(line)

    def readStderr(self):
        lines = self.process.readAllStandardError().data().decode()[:-1].split(
            '\n')
        for line in lines:
            if 'error' in line.lower():
                line = f'<span style="color:red">{line}</span>'
            elif 'warning' in line.lower():
                line = f'<span style="color:orange">{line}</span>'

            self.stderrText.append(line)

    def processStarted(self):
        self.compileButton.setEnabled(False)
        self.tidyButton.setEnabled(False)

    def processFinished(self):
        self.compileButton.setEnabled(True)
        self.tidyButton.setEnabled(True)

    def errorOccurred(self):
        self.stderrText.insertPlainText(self.process.errorString())
Exemple #3
0
class AboutWindow_Ui(QWidget):
    def __init__(self, persepolis_setting):
        super().__init__()

        self.persepolis_setting = persepolis_setting

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        icons = ':/' + \
            str(self.persepolis_setting.value('settings/icons')) + '/'

        self.setMinimumSize(QSize(545, 375))
        self.setWindowIcon(QIcon.fromTheme('persepolis', QIcon(':/persepolis.svg')))

        verticalLayout = QVBoxLayout(self)

        self.about_tabWidget = QTabWidget(self)

        # about tab
        self.about_tab = QWidget(self)

        about_tab_horizontalLayout = QHBoxLayout(self.about_tab)

        about_tab_verticalLayout = QVBoxLayout()

        # persepolis icon
        if qtsvg_available:
            persepolis_icon_verticalLayout = QVBoxLayout()
            self.persepolis_icon = QtSvgWidget.QSvgWidget(':/persepolis.svg')
            self.persepolis_icon.setFixedSize(QSize(64, 64))

            persepolis_icon_verticalLayout.addWidget(self.persepolis_icon)
            persepolis_icon_verticalLayout.addStretch(1)

            about_tab_horizontalLayout.addLayout(persepolis_icon_verticalLayout)

        self.title_label = QLabel(self.about_tab)
        font = QFont()
        font.setBold(True)
        font.setWeight(QFont.Weight.Bold)
        self.title_label.setFont(font)
        self.title_label.setAlignment(Qt.AlignCenter)
        about_tab_verticalLayout.addWidget(self.title_label)

        self.version_label = QLabel(self.about_tab)
        self.version_label.setAlignment(Qt.AlignCenter)

        about_tab_verticalLayout.addWidget(self.version_label)

        self.site2_label = QLabel(self.about_tab)
        self.site2_label.setTextFormat(Qt.RichText)
        self.site2_label.setAlignment(Qt.AlignCenter)
        self.site2_label.setOpenExternalLinks(True)
        self.site2_label.setTextInteractionFlags(
            Qt.TextBrowserInteraction)
        about_tab_verticalLayout.addWidget(self.site2_label)

        self.telegram_label = QLabel(self.about_tab)
        self.telegram_label.setTextFormat(Qt.RichText)
        self.telegram_label.setAlignment(Qt.AlignCenter)
        self.telegram_label.setOpenExternalLinks(True)
        self.telegram_label.setTextInteractionFlags(
            Qt.TextBrowserInteraction)
        about_tab_verticalLayout.addWidget(self.telegram_label)

        self.twitter_label = QLabel(self.about_tab)
        self.twitter_label.setTextFormat(Qt.RichText)
        self.twitter_label.setAlignment(Qt.AlignCenter)
        self.twitter_label.setOpenExternalLinks(True)
        self.twitter_label.setTextInteractionFlags(
            Qt.TextBrowserInteraction)
        about_tab_verticalLayout.addWidget(self.twitter_label)

        about_tab_verticalLayout.addStretch(1)

        about_tab_horizontalLayout.addLayout(about_tab_verticalLayout)

        # developers_tab
        # developers
        self.developers_tab = QWidget(self)
        developers_verticalLayout = QVBoxLayout(self.developers_tab)

        self.developers_title_label = QLabel(self.developers_tab)
        font.setBold(True)
        font.setWeight(QFont.Weight.Bold)
        self.developers_title_label.setFont(font)
        self.developers_title_label.setAlignment(Qt.AlignCenter)
        developers_verticalLayout.addWidget(self.developers_title_label)

        self.name_label = QLabel(self.developers_tab)
        self.name_label.setAlignment(Qt.AlignCenter)

        developers_verticalLayout.addWidget(self.name_label)

        # contributors
        self.contributors_thank_label = QLabel(self.developers_tab)
        self.contributors_thank_label.setFont(font)
        self.contributors_thank_label.setAlignment(Qt.AlignCenter)

        developers_verticalLayout.addWidget(self.contributors_thank_label)

        self.contributors_link_label = QLabel(self.developers_tab)
        self.contributors_link_label.setTextFormat(Qt.RichText)
        self.contributors_link_label.setAlignment(Qt.AlignCenter)
        self.contributors_link_label.setOpenExternalLinks(True)
        self.contributors_link_label.setTextInteractionFlags(
            Qt.TextBrowserInteraction)
        developers_verticalLayout.addWidget(self.contributors_link_label)

        developers_verticalLayout.addStretch(1)

        # translators tab
        self.translators_tab = QWidget(self)
        translators_tab_verticalLayout = QVBoxLayout(self.translators_tab)

        # translators
        self.translators_textEdit = QTextEdit(self.translators_tab)
        self.translators_textEdit.setReadOnly(True)
        translators_tab_verticalLayout.addWidget(self.translators_textEdit)

        # License tab
        self.license_tab = QWidget(self)
        license_tab_verticalLayout = QVBoxLayout(self.license_tab)

        self.license_text = QTextEdit(self.license_tab)
        self.license_text.setReadOnly(True)

        license_tab_verticalLayout.addWidget(self.license_text)

        verticalLayout.addWidget(self.about_tabWidget)

        # buttons
        button_horizontalLayout = QHBoxLayout()
        button_horizontalLayout.addStretch(1)

        self.pushButton = QPushButton(self)
        self.pushButton.setIcon(QIcon(icons + 'ok'))
        self.pushButton.clicked.connect(self.close)

        button_horizontalLayout.addWidget(self.pushButton)

        verticalLayout.addLayout(button_horizontalLayout)

        self.setWindowTitle(QCoreApplication.translate("about_ui_tr", "About Persepolis"))

        # about_tab
        self.title_label.setText(QCoreApplication.translate("about_ui_tr", "Persepolis Download Manager"))
        self.version_label.setText(QCoreApplication.translate("about_ui_tr", "Version 3.2.0"))
        self.site2_label.setText(QCoreApplication.translate("about_ui_tr",
                                                            "<a href=https://persepolisdm.github.io>https://persepolisdm.github.io</a>",
                                                            "TRANSLATORS NOTE: YOU REALLY DON'T NEED TO TRANSLATE THIS PART!"))

        self.telegram_label.setText(QCoreApplication.translate("about_ui_tr",
                                                               "<a href=https://telegram.me/persepolisdm>https://telegram.me/persepolisdm</a>",
                                                               "TRANSLATORS NOTE: YOU REALLY DON'T NEED TO TRANSLATE THIS PART!"))

        self.twitter_label.setText(QCoreApplication.translate("about_ui_tr",
                                                              "<a href=https://twitter.com/persepolisdm>https://twitter.com/persepolisdm</a>",
                                                              "TRANSLATORS NOTE: YOU REALLY DON'T NEED TO TRANSLATE THIS PART!"))

        # developers_tab
        self.developers_title_label.setText(QCoreApplication.translate('about_ui_tr', 'Developers:'))

        self.name_label.setText(QCoreApplication.translate("about_ui_tr",
                                                           "\nAliReza AmirSamimi\nMohammadreza Abdollahzadeh\nSadegh Alirezaie\nMostafa Asadi\nMohammadAmin Vahedinia\nJafar Akhondali\nH.Rostami\nEhsan Titish",
                                                           "TRANSLATORS NOTE: YOU REALLY DON'T NEED TO TRANSLATE THIS PART!"))

        self.contributors_thank_label.setText(QCoreApplication.translate('about_ui_tr', 'Special thanks to:'))
        self.contributors_link_label.setText(
            "<a href=https://github.com/persepolisdm/persepolis/graphs/contributors>our contributors</a>")

        # License
        self.license_text.setPlainText("""
            This program is free software: you can redistribute it and/or modify
            it under the terms of the GNU General Public License as published by
            the Free Software Foundation, either version 3 of the License, or
            (at your option) any later version.

            This program is distributed in the hope that it will be useful,
            but WITHOUT ANY WARRANTY; without even the implied warranty of
            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
            GNU General Public License for more details.

            You should have received a copy of the GNU General Public License
            along with this program.  If not, see http://www.gnu.org/licenses/.
            """)

        # tabs
        self.about_tabWidget.addTab(self.about_tab, QCoreApplication.translate("about_ui_tr", "About Persepolis"))
        self.about_tabWidget.addTab(self.developers_tab, QCoreApplication.translate("about_ui_tr", "Developers"))
        self.about_tabWidget.addTab(self.translators_tab, QCoreApplication.translate("about_ui_tr", "Translators"))
        self.about_tabWidget.addTab(self.license_tab, QCoreApplication.translate("about_ui_tr", "License"))

        # button
        self.pushButton.setText(QCoreApplication.translate("about_ui_tr", "OK"))
Exemple #4
0
class Window(QDialog):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self.iconGroupBox = QGroupBox()
        self.iconLabel = QLabel()
        self.iconComboBox = QComboBox()
        self.showIconCheckBox = QCheckBox()

        self.messageGroupBox = QGroupBox()
        self.typeLabel = QLabel()
        self.durationLabel = QLabel()
        self.durationWarningLabel = QLabel()
        self.titleLabel = QLabel()
        self.bodyLabel = QLabel()

        self.typeComboBox = QComboBox()
        self.durationSpinBox = QSpinBox()
        self.titleEdit = QLineEdit()
        self.bodyEdit = QTextEdit()
        self.showMessageButton = QPushButton()

        self.minimizeAction = QAction()
        self.maximizeAction = QAction()
        self.restoreAction = QAction()
        self.quitAction = QAction()

        self.trayIcon = QSystemTrayIcon()
        self.trayIconMenu = QMenu()

        self.createIconGroupBox()
        self.createMessageGroupBox()

        self.iconLabel.setMinimumWidth(self.durationLabel.sizeHint().width())

        self.createActions()
        self.createTrayIcon()

        self.showMessageButton.clicked.connect(self.showMessage)
        self.showIconCheckBox.toggled.connect(self.trayIcon.setVisible)
        self.iconComboBox.currentIndexChanged.connect(self.setIcon)
        self.trayIcon.messageClicked.connect(self.messageClicked)
        self.trayIcon.activated.connect(self.iconActivated)

        self.mainLayout = QVBoxLayout()
        self.mainLayout.addWidget(self.iconGroupBox)
        self.mainLayout.addWidget(self.messageGroupBox)
        self.setLayout(self.mainLayout)

        self.iconComboBox.setCurrentIndex(1)
        self.trayIcon.show()

        self.setWindowTitle("Systray")
        self.resize(400, 300)

    def setVisible(self, visible):
        self.minimizeAction.setEnabled(visible)
        self.maximizeAction.setEnabled(not self.isMaximized())
        self.restoreAction.setEnabled(self.isMaximized() or not visible)
        super().setVisible(visible)

    def closeEvent(self, event):
        if not event.spontaneous() or not self.isVisible():
            return
        if self.trayIcon.isVisible():
            QMessageBox.information(
                self, "Systray",
                "The program will keep running in the system tray. "
                "To terminate the program, choose <b>Quit</b> in the context "
                "menu of the system tray entry.")
            self.hide()
            event.ignore()

    @Slot(int)
    def setIcon(self, index):
        icon = self.iconComboBox.itemIcon(index)
        self.trayIcon.setIcon(icon)
        self.setWindowIcon(icon)
        self.trayIcon.setToolTip(self.iconComboBox.itemText(index))

    @Slot(str)
    def iconActivated(self, reason):
        if reason == QSystemTrayIcon.Trigger:
            pass
        if reason == QSystemTrayIcon.DoubleClick:
            self.iconComboBox.setCurrentIndex(
                (self.iconComboBox.currentIndex() + 1) %
                self.iconComboBox.count())
        if reason == QSystemTrayIcon.MiddleClick:
            self.showMessage()

    @Slot()
    def showMessage(self):
        self.showIconCheckBox.setChecked(True)
        selectedIcon = self.typeComboBox.itemData(
            self.typeComboBox.currentIndex())
        msgIcon = QSystemTrayIcon.MessageIcon(selectedIcon)

        if selectedIcon == -1:  # custom icon
            icon = QIcon(
                self.iconComboBox.itemIcon(self.iconComboBox.currentIndex()))
            self.trayIcon.showMessage(
                self.titleEdit.text(),
                self.bodyEdit.toPlainText(),
                icon,
                self.durationSpinBox.value() * 1000,
            )
        else:
            self.trayIcon.showMessage(
                self.titleEdit.text(),
                self.bodyEdit.toPlainText(),
                msgIcon,
                self.durationSpinBox.value() * 1000,
            )

    @Slot()
    def messageClicked(self):
        QMessageBox.information(
            None, "Systray", "Sorry, I already gave what help I could.\n"
            "Maybe you should try asking a human?")

    def createIconGroupBox(self):
        self.iconGroupBox = QGroupBox("Tray Icon")

        self.iconLabel = QLabel("Icon:")

        self.iconComboBox = QComboBox()
        self.iconComboBox.addItem(QIcon(":/images/bad.png"), "Bad")
        self.iconComboBox.addItem(QIcon(":/images/heart.png"), "Heart")
        self.iconComboBox.addItem(QIcon(":/images/trash.png"), "Trash")

        self.showIconCheckBox = QCheckBox("Show icon")
        self.showIconCheckBox.setChecked(True)

        iconLayout = QHBoxLayout()
        iconLayout.addWidget(self.iconLabel)
        iconLayout.addWidget(self.iconComboBox)
        iconLayout.addStretch()
        iconLayout.addWidget(self.showIconCheckBox)
        self.iconGroupBox.setLayout(iconLayout)

    def createMessageGroupBox(self):
        self.messageGroupBox = QGroupBox("Balloon Message")

        self.typeLabel = QLabel("Type:")

        self.typeComboBox = QComboBox()
        self.typeComboBox.addItem("None", QSystemTrayIcon.NoIcon)
        self.typeComboBox.addItem(
            self.style().standardIcon(QStyle.SP_MessageBoxInformation),
            "Information",
            QSystemTrayIcon.Information,
        )
        self.typeComboBox.addItem(
            self.style().standardIcon(QStyle.SP_MessageBoxWarning),
            "Warning",
            QSystemTrayIcon.Warning,
        )
        self.typeComboBox.addItem(
            self.style().standardIcon(QStyle.SP_MessageBoxCritical),
            "Critical",
            QSystemTrayIcon.Critical,
        )
        self.typeComboBox.addItem(QIcon(), "Custom icon", -1)
        self.typeComboBox.setCurrentIndex(1)

        self.durationLabel = QLabel("Duration:")

        self.durationSpinBox = QSpinBox()
        self.durationSpinBox.setRange(5, 60)
        self.durationSpinBox.setSuffix(" s")
        self.durationSpinBox.setValue(15)

        self.durationWarningLabel = QLabel(
            "(some systems might ignore this hint)")
        self.durationWarningLabel.setIndent(10)

        self.titleLabel = QLabel("Title:")
        self.titleEdit = QLineEdit("Cannot connect to network")
        self.bodyLabel = QLabel("Body:")

        self.bodyEdit = QTextEdit()
        self.bodyEdit.setPlainText(
            "Don't believe me. Honestly, I don't have a clue."
            "\nClick this balloon for details.")

        self.showMessageButton = QPushButton("Show Message")
        self.showMessageButton.setDefault(True)

        messageLayout = QGridLayout()
        messageLayout.addWidget(self.typeLabel, 0, 0)
        messageLayout.addWidget(self.typeComboBox, 0, 1, 1, 2)
        messageLayout.addWidget(self.durationLabel, 1, 0)
        messageLayout.addWidget(self.durationSpinBox, 1, 1)
        messageLayout.addWidget(self.durationWarningLabel, 1, 2, 1, 3)
        messageLayout.addWidget(self.titleLabel, 2, 0)
        messageLayout.addWidget(self.titleEdit, 2, 1, 1, 4)
        messageLayout.addWidget(self.bodyLabel, 3, 0)
        messageLayout.addWidget(self.bodyEdit, 3, 1, 2, 4)
        messageLayout.addWidget(self.showMessageButton, 5, 4)
        messageLayout.setColumnStretch(3, 1)
        messageLayout.setRowStretch(4, 1)
        self.messageGroupBox.setLayout(messageLayout)

    def createActions(self):
        self.minimizeAction = QAction("Minimize", self)
        self.minimizeAction.triggered.connect(self.hide)

        self.maximizeAction = QAction("Maximize", self)
        self.maximizeAction.triggered.connect(self.showMaximized)

        self.restoreAction = QAction("Restore", self)
        self.restoreAction.triggered.connect(self.showNormal)

        self.quitAction = QAction("Quit", self)
        self.quitAction.triggered.connect(qApp.quit)

    def createTrayIcon(self):
        self.trayIconMenu = QMenu(self)
        self.trayIconMenu.addAction(self.minimizeAction)
        self.trayIconMenu.addAction(self.maximizeAction)
        self.trayIconMenu.addAction(self.restoreAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.quitAction)

        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setContextMenu(self.trayIconMenu)
Exemple #5
0
class Window(QDialog):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.createTrayIcon()
        self.createProgramsList()
        self.createCodeEditPage()
        self.logsPage = QTextBrowser()
        self.documentation = QTextBrowser()

        self.tabWidget = QTabWidget()
        self.tabWidget.setIconSize(QSize(64, 64))
        self.tabWidget.addTab(self.programsListPage, QIcon(":/images/Adventure-Map-icon.png"), "Programs")
        self.tabWidget.addTab(self.codeEditPage, QIcon(":/images/Sword-icon.png"), "Edit Program")
        self.tabWidget.addTab(self.logsPage, QIcon(":/images/Spell-Scroll-icon.png"), "Logs")
        self.tabWidget.addTab(self.documentation, QIcon(":/images/Spell-Book-icon.png"), "Documentation")

        self.mainLayout = QVBoxLayout()
        self.mainLayout.addWidget(self.tabWidget)
        self.setLayout(self.mainLayout)

        self.setWindowTitle(APP_NAME)
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.Dialog)
        self.resize(800, 600)

        self.systrayHintMsgShowed = False
        self.firstShow = True
        self.fromQuit = False

    def createProgramsList(self):
        self.programsListModel = QStandardItemModel(0, 1, self)
        self.programsList = QListView()
        self.programsList.setModel(self.programsListModel)
        self.programsListPage = QWidget()
        self.programsListLayout = QVBoxLayout()
        self.programsListButtons = QHBoxLayout()
        self.programsListButtonNew = QPushButton("New")
        self.programsListButtonDelete = QPushButton("Delete")
        self.programsListButtonEdit = QPushButton("Edit")
        self.programsListButtonSet = QPushButton("Set")
        self.programsListButtons.addWidget(self.programsListButtonNew)
        self.programsListButtons.addWidget(self.programsListButtonSet)
        self.programsListButtons.addWidget(self.programsListButtonEdit)
        self.programsListButtons.addWidget(self.programsListButtonDelete)
        self.programsListLayout.addLayout(self.programsListButtons)
        self.programsListLayout.addWidget(self.programsList)
        self.programsListPage.setLayout(self.programsListLayout)
        self.programsListButtonNew.clicked.connect(self.newProgram)
        self.programsListButtonEdit.clicked.connect(self.editProgram)
        self.programsListButtonDelete.clicked.connect(self.deleteProgram)
        self.programsListButtonSet.clicked.connect(self.setProgram)

    def createCodeEditPage(self):
        self.codeEditPage = QWidget()
        self.codeEditLayout = QVBoxLayout()
        self.codeEditNameBox = QHBoxLayout()
        self.codeEditNameBoxNameLabel = QLabel("Name:")
        self.codeEditNameBoxNameInput = QLineEdit()
        self.codeEditNameBoxSaveButton = QPushButton("Save")
        self.codeEditNameBoxCancelButton = QPushButton("Cancel")
        self.codeEditNameBox.addWidget(self.codeEditNameBoxNameLabel)
        self.codeEditNameBox.addWidget(self.codeEditNameBoxNameInput)
        self.codeEditNameBox.addWidget(self.codeEditNameBoxSaveButton)
        self.codeEditNameBox.addWidget(self.codeEditNameBoxCancelButton)
        self.codeEdit = QTextEdit()
        self.codeEditLayout.addLayout(self.codeEditNameBox)
        self.codeEditLayout.addWidget(self.codeEdit)
        self.codeEditPage.setLayout(self.codeEditLayout)
        self.codeEditLastCode = ''
        self.codeEditNameBoxSaveButton.clicked.connect(self.saveEditProgram)
        self.codeEditNameBoxCancelButton.clicked.connect(self.cancelEditProgram)

    def showEvent(self, event):
        super().showEvent(event)
        if self.firstShow:
            self.firstShow = False
            self.createWaitDialog()
            self.findKeyboard()

    def closeWaitDialog(self):
        time.sleep(1)
        self.waitDialog.close()

    @Slot()
    def newProgram(self):
        self.showNormal()
        self.tabWidget.setCurrentWidget(self.codeEditPage)
        self.codeEdit.setPlainText("")
        self.codeEditLastCode = ''
        self.codeEditNameBoxNameInput.setText("")
    
    @Slot()
    def editProgram(self):
        selected = self.programsList.selectedIndexes()
        if not selected:
            return
        selected = selected[0].data()
        loadProgram = LoadProgram(self.cmdSocket, selected)
        loadProgram.loaded.connect(self.programLoaded)
        loadProgram.start()
        self.showWaitDialog("Loading program ...")

    @Slot()
    def saveEditProgram(self):
        name = self.codeEditNameBoxNameInput.text()
        program = self.codeEdit.toPlainText()
        if not name:
            return
        editProgramWorker = EditProgram(self.cmdSocket, name, program)
        editProgramWorker.edited.connect(self.programSaved)
        editProgramWorker.start()
        self.showWaitDialog("Saving program ...")

    @Slot()
    def cancelEditProgram(self):
        self.codeEdit.setPlainText(self.codeEditLastCode)

    @Slot()
    def deleteProgram(self):
        selected = self.programsList.selectedIndexes()
        if not selected:
            return
        selected = selected[0].data()
        deleteWorker = DeleteProgram(self.cmdSocket, selected)
        deleteWorker.deleted.connect(self.programDeleted)
        deleteWorker.start()
        self.showWaitDialog("Deleting program ...")

    @Slot()
    def setProgram(self):
        selected = self.programsList.selectedIndexes()
        if not selected:
            return
        selected = selected[0].data()
        setWorker = SetProgram(self.cmdSocket, selected)
        setWorker.setDone.connect(self.programSet)
        setWorker.start()
        self.showWaitDialog("Setting program ...")

    @Slot(str, str)
    def programLoaded(self, name, program):
        self.closeWaitDialog()
        self.tabWidget.setCurrentWidget(self.codeEditPage)
        self.codeEdit.setPlainText(program)
        self.codeEditLastCode = program
        self.codeEditNameBoxNameInput.setText(name)

    @Slot(str)
    def programDeleted(self, name):
        self.updateProgramsList()

    @Slot(str)
    def programSet(self, name):
        self.updateProgramsList()

    @Slot(str)
    def programSaved(self, name):
        self.updateProgramsList()
        self.tabWidget.setCurrentWidget(self.programsListPage)

    def createWaitDialog(self):
        self.waitDialog = QDialog(self)
        self.waitDialogLayout = QHBoxLayout()
        self.waitDialogLabel = QLabel()
        self.waitDialogLayout.addWidget(self.waitDialogLabel)
        self.waitDialog.setLayout(self.waitDialogLayout)
        self.waitDialog.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)

    def showWaitDialog(self, text='Please wait...'):
        self.waitDialogLabel.setText(text)
        self.waitDialog.exec_()

    def findKeyboard(self):
        find = ConnectKeyboard()
        find.connected.connect(self.keyboardConnected)
        find.start()
        self.showWaitDialog('Finding and connecting Fruit2Pi Keyboard ...')

    @Slot(BluetoothSocket)
    def keyboardConnected(self, socket):
        self.cmdSocket = socket
        self.updateProgramsList()
    
    def updateProgramsList(self):
        self.listProgram = ListProgram(self.cmdSocket)
        self.listProgram.listed.connect(self.programListUpdated)
        self.listProgram.start()
    
    @Slot(list, str)
    def programListUpdated(self, programs, current_program):
        print(programs)
        print(current_program)
        self.closeWaitDialog()
        self.programsListModel.clear()
        for p in programs:
            item = QStandardItem(p)
            if p == current_program:
                item.setForeground(QBrush(QColor(0, 0, 255, 127)))
            self.programsListModel.appendRow(item)

    def setVisible(self, visible):
        super().setVisible(visible)

    def closeEvent(self, event):
        if self.fromQuit:
            return
        if not event.spontaneous() or not self.isVisible():
            return
        if not self.systrayHintMsgShowed:
            self.systrayHintMsgShowed = True
            icon = QIcon(":/images/yammi-banana-icon.png")
            self.trayIcon.showMessage(APP_NAME,
                                      "Running on background"
                                      "To quit, choose <b>Quit</b> in the icon menu",
                                      icon,
                                      5000
                                      )
        self.hide()
        event.ignore()

    @Slot(str)
    def iconActivated(self, reason):
        print(reason)
        if reason == QSystemTrayIcon.Trigger:
            self.showNormal()
        if reason == QSystemTrayIcon.DoubleClick:
            self.showNormal()

    @Slot()
    def showProgramsPage(self):
        self.showNormal()
        self.tabWidget.setCurrentWidget(self.programsListPage)

    @Slot()
    def showLogsPage(self):
        self.showNormal()
        self.tabWidget.setCurrentWidget(self.logsPage)
    
    @Slot()
    def showDocumentation(self):
        self.showNormal()
        self.tabWidget.setCurrentWidget(self.documentation)

    @Slot()
    def quit(self):
        self.fromQuit = True
        qApp.quit()

    def createTrayIcon(self):
        self.showProgramsAction = QAction("Programs", self)
        self.showProgramsAction.triggered.connect(self.showProgramsPage)
        self.showNewProgramAction = QAction("New Program", self)
        self.showNewProgramAction.triggered.connect(self.newProgram)
        self.showSetProgramAction = QAction("Logs", self)
        self.showSetProgramAction.triggered.connect(self.showLogsPage)
        self.showDocumentationAction = QAction("Documentation", self)
        self.showDocumentationAction.triggered.connect(self.showDocumentation)
        self.quitAction = QAction("Quit", self)
        self.quitAction.triggered.connect(self.quit)

        self.trayIconMenu = QMenu(self)
        self.trayIconMenu.addAction(self.showProgramsAction)
        self.trayIconMenu.addAction(self.showSetProgramAction)
        self.trayIconMenu.addAction(self.showNewProgramAction)
        self.trayIconMenu.addAction(self.showDocumentationAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.quitAction)
        self.trayIcon = QSystemTrayIcon(self)

        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.activated.connect(self.iconActivated)
        self.trayIcon.setIcon(QIcon(":/images/yammi-banana-icon.png"))
        self.trayIcon.show()