Exemple #1
0
def main():
    basePath = os.path.dirname(os.path.realpath(__file__))
    app = QApplication(sys.argv)
    contextMenu = QMenu()
    fixAction = QAction(
        "Run 'create__ap --fix-unmanaged' in the terminal as root to fix possible issues"
    )
    contextMenu.addAction(fixAction)
    activeIcon = QIcon()
    activeIcon.addFile(os.path.join(basePath, "wifi.svg"))
    inactiveIcon = QIcon()
    inactiveIcon.addFile(os.path.join(basePath, "wifi-off.svg"))
    trayIcon = QSystemTrayIcon(inactiveIcon)
    trayIcon.setContextMenu(contextMenu)
    trayIcon.activated.connect(handleTrayIconClick)

    def syncIcon():
        if serviceIsActive():
            trayIcon.setIcon(activeIcon)
        else:
            trayIcon.setIcon(inactiveIcon)

    timer = QTimer()
    timer.setInterval(1000)
    timer.timeout.connect(syncIcon)
    timer.start()
    trayIcon.show()
    sys.exit(app.exec_())
Exemple #2
0
class SystemTray(QWidget):
    """只有托盘"""
    def __init__(self, config_list):
        super().__init__()
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(QIcon('icons/app.ico'))
        self.add_menu(config_list)

    def add_menu(self, config_list):
        """托盘菜单"""
        tray_menu = QMenu()
        # 添加菜单
        for config in config_list:
            params = config.get('params')
            sys_name = params.get('name')
            tray_menu.addAction(OpenAction(params, self))
            stop_bat = params.get('stop_bat', None)
            if stop_bat:
                tray_menu.addAction(StopAction(sys_name, stop_bat, self))
        tray_menu.addAction(ExitAction(self))
        self.tray.setContextMenu(tray_menu)

    def display(self):
        """icon的值: 0-没有图标  1-是提示  2-是警告  3-是错误"""
        self.tray.show()
        self.tray.showMessage(u"启动成功", '请通过右键操作')
Exemple #3
0
def setup_app(app):
    # Stop window closing
    app.setQuitOnLastWindowClosed(False)

    # Init QSystemTrayIcon
    icon = QIcon("icon.png")
    tray_icon = QSystemTrayIcon(app)
    tray_icon.setIcon(icon)

    # Add Menu Actions to App
    new_action = QAction("New", app)
    show_action = QAction("Show All", app)
    hide_action = QAction("Hide All", app)
    quit_action = QAction("Exit", app)

    new_action.triggered.connect(new_note)
    show_action.triggered.connect(show_all)
    hide_action.triggered.connect(hide_all)
    quit_action.triggered.connect(quit_app)

    # Add Tray Menu
    tray_menu = QMenu()
    tray_menu.addAction(new_action)
    tray_menu.addAction(show_action)
    tray_menu.addAction(hide_action)
    tray_menu.addAction(quit_action)
    tray_icon.setContextMenu(tray_menu)
    tray_icon.activated.connect(systemIcon)
    tray_icon.show()
Exemple #4
0
class SystemTray(QSystemTrayIcon):
    def __init__(self, app):
        super(SystemTray, self).__init__()
        self.app = app
        self.systemTray = QSystemTrayIcon(
            QIcon(f"{sys.argv[0]}/assets/app_icon.png"))
        self.trayMenu = QMenu()
        self.systemTray.setContextMenu(self.trayMenu)
        self.menu_title_setup()
        self.open_action_setup()
        self.exit_action_setup()
        self.systemTray.show()

    def menu_title_setup(self):
        self.trayMenu.menuTitle = self.trayMenu.addAction("Saharah Paper")
        self.trayMenu.menuTitle.setEnabled(False)
        self.trayMenu.addSeparator()

    def open_action_setup(self):
        self.trayMenu.openAction = self.trayMenu.addAction("Settings")
        self.trayMenu.openAction.triggered.connect(self.handle_open)

    def handle_open(self):
        self.app.mainWindow.show()

    def exit_action_setup(self):
        self.trayMenu.exitAction = self.trayMenu.addAction("Exit")
        self.trayMenu.exitAction.triggered.connect(self.app.quit)
Exemple #5
0
class App:
    def __init__(self):
        # Create a Qt application
        self.app = QApplication(sys.argv)
        self.main = BT('main.ui')

        self.splash = QSplashScreen(QPixmap('../images/boot.jpg'))
        self.splash.show()

        menu = QMenu()
        showAction = menu.addAction("显示")
        hideAction = menu.addAction("隐藏")
        closeAction = menu.addAction("退出")

        showAction.triggered.connect(
            lambda event: self.show(showAction, hideAction))
        hideAction.triggered.connect(
            lambda event: self.hide(showAction, hideAction))
        closeAction.triggered.connect(self.exit)

        self.tray = QSystemTrayIcon()
        self.tray.setIcon(QIcon("../icon/icon.ico"))
        self.tray.setContextMenu(menu)
        self.tray.show()
        self.tray.setToolTip("")

    def run(self):
        # Enter Qt application main loop

        self.app.processEvents()

        self.main.window.show()

        self.splash.finish(self.main.window)

        sys.exit(self.app.exec_())

    def hide(self, show_action, hiden_actoin):
        hiden_actoin.setDisabled(True)
        show_action.setEnabled(True)
        self.main.window.hide()
        return

    def show(self, show_action, hiden_actoin):
        hiden_actoin.setEnabled(True)
        show_action.setDisabled(True)
        self.main.window.show()
        return

    def exit(self):
        self.main.window.close()
        return
Exemple #6
0
def start(_exit: bool = False, _show_ui: bool = True) -> None:
    app = QApplication(sys.argv)

    logo = QIcon(LOGO)
    main_window = MainWindow()
    ui = main_window.ui
    main_window.setWindowIcon(logo)
    tray = QSystemTrayIcon(logo, app)
    tray.activated.connect(main_window.systray_clicked)

    menu = QMenu()
    action_exit = QAction("Exit")
    action_exit.triggered.connect(app.exit)
    menu.addAction(action_exit)

    tray.setContextMenu(menu)

    ui.text.textChanged.connect(partial(queue_text_change, ui))
    ui.command.textChanged.connect(partial(update_button_command, ui))
    ui.keys.textChanged.connect(partial(update_button_keys, ui))
    ui.write.textChanged.connect(partial(update_button_write, ui))
    ui.change_brightness.valueChanged.connect(
        partial(update_change_brightness, ui))
    ui.switch_page.valueChanged.connect(partial(update_switch_page, ui))
    ui.imageButton.clicked.connect(partial(select_image, main_window))
    ui.brightness.valueChanged.connect(partial(set_brightness, ui))
    for deck_id, deck in api.open_decks().items():
        ui.device_list.addItem(f"{deck['type']} - {deck_id}", userData=deck_id)

    build_device(ui)
    ui.device_list.currentIndexChanged.connect(partial(build_device, ui))

    ui.pages.currentChanged.connect(partial(change_page, ui))

    ui.actionExport.triggered.connect(partial(export_config, main_window))
    ui.actionImport.triggered.connect(partial(import_config, main_window))

    timer = QTimer()
    timer.timeout.connect(partial(sync, ui))
    timer.start(1000)

    api.render()
    tray.show()

    if _show_ui:
        main_window.show()

    if _exit:
        return
    else:
        sys.exit(app.exec_())
Exemple #7
0
class SystrayIcon(QObject):
    show_window = Signal()
    exit = Signal()

    def __init__(self, parent: QWidget):
        super().__init__(parent)

        # Bold font
        bold = QFont()
        bold.setBold(True)

        # Systray icon
        icon: QIcon = QApplication.instance().windowIcon()
        self._systray_icon = QSystemTrayIcon(icon, self)
        self._systray_icon.setToolTip("Noteeds")

        # Show action
        self._show_action = QAction("&Show", self)
        self._show_action.setFont(bold)

        # Exit action
        self._exit_action = QAction("E&xit", self)

        # Menu
        self._menu = QMenu(parent)
        self._menu.addAction(self._show_action)
        self._menu.addAction(self._exit_action)
        self._systray_icon.setContextMenu(self._menu)

        # Connections
        self._systray_icon.activated.connect(self._activated)
        self._show_action.triggered.connect(self.show_window)
        self._exit_action.triggered.connect(self.exit)

    def _activated(self, reason: QSystemTrayIcon.ActivationReason):
        if reason == QSystemTrayIcon.Trigger:
            self.show_window.emit()

    def show(self):
        self._systray_icon.show()

    def hide(self):
        self._systray_icon.hide()
class TrayIcon:
    def __init__(self):
        self.icons = {
            True: QIcon('icons/rotate_disabled.png'),
            False: QIcon('icons/rotate_enabled.png'),
        }

        self.tray = QSystemTrayIcon()
        self.tray.activated.connect(self.action)
        self.update_status()
        self.tray.show()

    def toggle_lock(self):
        global locked
        locked = not locked

    def update_status(self):
        self.tray.setToolTip(
            "Auto rotate ({})".format('enabled' if not locked else 'disabled'))
        self.tray.setIcon(self.icons[locked])

    def action(self, signal):
        self.toggle_lock()
        self.update_status()
Exemple #9
0
class UIMainWindow(object):
    """Container class to hold all UI-related creation methods. Must be sublcassed.
    """

    def create_ui(self):
        """Setup main UI elements, dock widgets, UI-related elements, etc.
        """

        log.debug('Loading UI')

        # Undo Stack
        self.undo_stack = QUndoStack(self)
        self.undo_stack.setUndoLimit(100)

        # Object navigation history
        self.obj_history = deque([], config.MAX_OBJ_HISTORY)

        app = QApplication.instance()
        base_font = QFont()
        base_font.fromString(self.prefs['base_font'])
        app.setFont(base_font)

        # Object class table widget
        # classTable = QTableView(self)
        classTable = classtable.TableView(self)
        classTable.setObjectName("classTable")
        classTable.setAlternatingRowColors(True)
        classTable.setFrameShape(QFrame.StyledPanel)
        classTable_font = QFont()
        classTable_font.fromString(self.prefs['class_table_font'])
        classTable.setFont(classTable_font)
        fm = classTable.fontMetrics()
        classTable.setWordWrap(True)
        classTable.setEditTriggers(QAbstractItemView.EditKeyPressed |
                                   QAbstractItemView.DoubleClicked |
                                   QAbstractItemView.AnyKeyPressed |
                                   QAbstractItemView.SelectedClicked)
        # classTable.horizontalHeader().setMovable(True)
        # classTable.verticalHeader().setMovable(False)
        classTable.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
        classTable.verticalHeader().setSectionResizeMode(QHeaderView.Interactive)
        classTable.horizontalHeader().setDefaultSectionSize(self.prefs['default_column_width'])
        classTable.verticalHeader().setDefaultSectionSize(fm.height() + 0)
        classTable.setSelectionMode(QAbstractItemView.ExtendedSelection)
        classTable.setContextMenuPolicy(Qt.CustomContextMenu)
        classTable.customContextMenuRequested.connect(self.custom_table_context_menu)

        # Create table model and proxy layers for transposing and filtering
        self.classTableModel = classtable.IDFObjectTableModel(classTable)
        self.transposeableModel = classtable.TransposeProxyModel(self.classTableModel)
        self.transposeableModel.setSourceModel(self.classTableModel)
        self.sortableModel = classtable.SortFilterProxyModel(self.transposeableModel)
        self.sortableModel.setSourceModel(self.transposeableModel)

        # Assign model to table (enable sorting FIRST)
        # table.setSortingEnabled(True) # Disable for now, CRUD actions won't work!
        classTable.setModel(self.sortableModel)

        # Connect some signals
        selection_model = classTable.selectionModel()
        selection_model.selectionChanged.connect(self.table_selection_changed)
        scroll_bar = classTable.verticalScrollBar()
        scroll_bar.valueChanged.connect(self.scroll_changed)

        # These are currently broken
        # classTable.horizontalHeader().sectionMoved.connect(self.moveObject)
        # classTable.verticalHeader().sectionMoved.connect(self.moveObject)

        # Object class tree widget
        classTreeDockWidget = QDockWidget("Object Classes and Counts", self)
        classTreeDockWidget.setObjectName("classTreeDockWidget")
        classTreeDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas)

        classTree = QTreeView(classTreeDockWidget)
        classTree.setUniformRowHeights(True)
        classTree.setAllColumnsShowFocus(True)
        classTree.setRootIsDecorated(False)
        classTree.setExpandsOnDoubleClick(True)
        classTree.setIndentation(15)
        classTree.setAnimated(True)
        classTree_font = QFont()
        classTree_font.fromString(self.prefs['class_tree_font'])
        classTree.setFont(classTree_font)
        classTree.setAlternatingRowColors(True)
        classTree.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        palette = classTree.palette()
        palette.setColor(QPalette.Highlight, Qt.darkCyan)
        classTree.setPalette(palette)

        class_tree_window = QWidget(classTreeDockWidget)
        class_tree_dock_layout_v = QVBoxLayout()
        class_tree_dock_layout_h = QHBoxLayout()
        class_tree_dock_layout_v.setContentsMargins(0, 8, 0, 0)
        class_tree_dock_layout_h.setContentsMargins(0, 0, 0, 0)

        class_tree_filter_edit = QLineEdit(classTreeDockWidget)
        class_tree_filter_edit.setPlaceholderText("Filter Classes")
        class_tree_filter_edit.textChanged.connect(self.treeFilterRegExpChanged)

        class_tree_filter_cancel = QPushButton("Clear", classTreeDockWidget)
        class_tree_filter_cancel.setMaximumWidth(45)
        class_tree_filter_cancel.clicked.connect(self.clearTreeFilterClicked)

        class_tree_dock_layout_h.addWidget(class_tree_filter_edit)
        class_tree_dock_layout_h.addWidget(class_tree_filter_cancel)
        class_tree_dock_layout_v.addLayout(class_tree_dock_layout_h)
        class_tree_dock_layout_v.addWidget(classTree)
        class_tree_window.setLayout(class_tree_dock_layout_v)

        classTreeDockWidget.setWidget(class_tree_window)
        classTreeDockWidget.setContentsMargins(0,0,0,0)

        # Comments widget
        commentDockWidget = QDockWidget("Comments", self)
        commentDockWidget.setObjectName("commentDockWidget")
        commentDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas)
        commentView = UndoRedoTextEdit(commentDockWidget, self)
        commentView.setLineWrapMode(QTextEdit.FixedColumnWidth)
        commentView.setLineWrapColumnOrWidth(499)
        commentView.setFrameShape(QFrame.StyledPanel)
        commentView_font = QFont()
        commentView_font.fromString(self.prefs['comments_font'])
        commentView.setFont(commentView_font)
        commentDockWidget.setWidget(commentView)

        # Info and help widget
        infoDockWidget = QDockWidget("Info", self)
        infoDockWidget.setObjectName("infoDockWidget")
        infoDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas)
        infoView = QTextEdit(infoDockWidget)
        infoView.setFrameShape(QFrame.StyledPanel)
        infoView.setReadOnly(True)
        infoDockWidget.setWidget(infoView)

        # Node list and jump menu widget
        refDockWidget = QDockWidget("Field References", self)
        refDockWidget.setObjectName("refDockWidget")
        refDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas)
        ref_model = reftree.ReferenceTreeModel(None, refDockWidget)
        refView = QTreeView(refDockWidget)
        refView.setModel(ref_model)
        refView.setUniformRowHeights(True)
        refView.setRootIsDecorated(False)
        refView.setIndentation(15)
        refView.setColumnWidth(0, 160)
        refView.setFrameShape(QFrame.StyledPanel)
        refDockWidget.setWidget(refView)
        refView.doubleClicked.connect(self.ref_tree_double_clicked)

        # Logging and debugging widget
        logDockWidget = QDockWidget("Log Viewer", self)
        logDockWidget.setObjectName("logDockWidget")
        logDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas)
        logView = QPlainTextEdit(logDockWidget)
        logView.setLineWrapMode(QPlainTextEdit.NoWrap)
        logView.setReadOnly(True)
        logView_font = QFont()
        logView_font.fromString(self.prefs['base_font'])
        logView.setFont(logView_font)
        logView.ensureCursorVisible()
        logDockWidget.setWidget(logView)

        # Undo view widget
        undoDockWidget = QDockWidget("Undo History", self)
        undoDockWidget.setObjectName("undoDockWidget")
        undoDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas)
        undoView = QUndoView(self.undo_stack)
        undoDockWidget.setWidget(undoView)

        # Define corner docking behaviour
        self.setDockNestingEnabled(True)
        self.setCorner(Qt.TopLeftCorner,
                       Qt.LeftDockWidgetArea)
        self.setCorner(Qt.TopRightCorner,
                       Qt.RightDockWidgetArea)
        self.setCorner(Qt.BottomLeftCorner,
                       Qt.LeftDockWidgetArea)
        self.setCorner(Qt.BottomRightCorner,
                       Qt.RightDockWidgetArea)

        # Assign main widget and dock widgets to QMainWindow
        self.setCentralWidget(classTable)
        self.addDockWidget(Qt.LeftDockWidgetArea, classTreeDockWidget)
        self.addDockWidget(Qt.RightDockWidgetArea, commentDockWidget)
        self.addDockWidget(Qt.RightDockWidgetArea, infoDockWidget)
        self.addDockWidget(Qt.RightDockWidgetArea, refDockWidget)
        self.addDockWidget(Qt.RightDockWidgetArea, logDockWidget)
        self.addDockWidget(Qt.RightDockWidgetArea, undoDockWidget)

        # Store widgets for access by other objects
        self.classTable = classTable
        self.commentView = commentView
        self.infoView = infoView
        self.classTree = classTree
        self.logView = logView
        self.undoView = undoView
        self.refView = refView
        self.filterTreeBox = class_tree_filter_edit

        # Store docks for access by other objects
        self.commentDockWidget = commentDockWidget
        self.infoDockWidget = infoDockWidget
        self.classTreeDockWidget = classTreeDockWidget
        self.logDockWidget = logDockWidget
        self.undoDockWidget = undoDockWidget
        self.refDockWidget = refDockWidget

        # Perform other UI-related initialization tasks
        self.center()
        self.setUnifiedTitleAndToolBarOnMac(True)
        self.setWindowIcon(QIcon(':/images/logo.png'))

        # Status bar setup
        self.statusBar().showMessage('Status: Ready')
        self.unitsLabel = QLabel()
        self.unitsLabel.setAlignment(Qt.AlignCenter)
        self.unitsLabel.setMinimumSize(self.unitsLabel.sizeHint())
        self.unitsLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.statusBar().addPermanentWidget(self.unitsLabel)
        self.pathLabel = QLabel()
        self.pathLabel.setAlignment(Qt.AlignCenter)
        self.pathLabel.setMinimumSize(self.pathLabel.sizeHint())
        self.pathLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.statusBar().addPermanentWidget(self.pathLabel)
        self.versionLabel = QLabel()
        self.versionLabel.setAlignment(Qt.AlignCenter)
        self.versionLabel.setMinimumSize(self.versionLabel.sizeHint())
        self.versionLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.statusBar().addPermanentWidget(self.versionLabel)
        self.progressBarIDF = QProgressBar()
        self.progressBarIDF.setAlignment(Qt.AlignCenter)
        self.progressBarIDF.setMaximumWidth(200)
        self.statusBar().addPermanentWidget(self.progressBarIDF)

        self.clipboard = QApplication.instance().clipboard()
        self.obj_clipboard = []

        self.setStyleSheet("""
            QToolTip {
               background-color: gray;
               color: white;
               border: black solid 1px
            } 
            # QMenu {
            #     background-color: rgbf(0.949020, 0.945098, 0.941176);
            #     color: rgb(255,255,255);
            # }
            # QMenu::item::selected {
            #     background-color: rgbf(0.949020, 0.945098, 0.941176);
            # }
            """)

    def create_tray_menu(self):
        """Creates an icon and menu for the system tray
        """

        # Menu for the system tray
        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.exitAct)

        # System tray itself
        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.setIcon(QIcon(':/images/logo.png'))
        self.trayIcon.setToolTip('IDF+')
        self.trayIcon.show()

    def create_actions(self):
        """Creates appropriate actions for use in menus and toolbars.
        """

        self.newAct = QAction(QIcon(':/images/new1.png'), "&New", self,
                                    shortcut=QKeySequence.New,
                                    statusTip="Create a new file",
                                    iconVisibleInMenu=True,
                                    triggered=self.new_file)

        self.openAct = QAction(QIcon(':/images/open.png'), "&Open...", self,
                                     shortcut=QKeySequence.Open,
                                     statusTip="Open an existing file",
                                     iconVisibleInMenu=True,
                                     triggered=self.open_file)

        self.saveAct = QAction(QIcon(':/images/save.png'), "&Save", self,
                                     shortcut=QKeySequence.Save,
                                     statusTip="Save the document to disk",
                                     iconVisibleInMenu=True,
                                     triggered=self.save)

        self.saveFormatAct = QAction(QIcon(':/images/save.png'), "&Format && Save",
                                     self, shortcut=QKeySequence('Ctrl+Shift+F'),
                                     statusTip="Format File and Save to disk",
                                     iconVisibleInMenu=True,
                                     triggered=self.format_save)

        self.saveAsAct = QAction(QIcon(':/images/saveas.png'), "Save &As...", self,
                                       shortcut=QKeySequence.SaveAs,
                                       statusTip="Save the document under a new name",
                                       iconVisibleInMenu=True,
                                       triggered=self.save_as)

        self.exitAct = QAction(QIcon(':/images/quit.png'), "E&xit", self,
                                     shortcut=QKeySequence('Ctrl+Q'),
                                     iconVisibleInMenu=True,
                                     statusTip="Exit the application",
                                     triggered=self.closeAllWindows)

        self.cutObjAct = QAction(QIcon(':/images/cut.png'), "Cu&t Object", self,
                                       shortcut=QKeySequence.Cut,
                                       statusTip="Cut current selection's contents to clipboard",
                                       iconVisibleInMenu=True,
                                       triggered=self.cutObject,
                                       iconText='Cut Obj')

        self.copyAct = QAction(QIcon(':/images/copy.png'),
                                     "&Copy Selected Values", self,
                                     statusTip="Copy current selection's contents to clipboard",
                                     iconVisibleInMenu=True,
                                     triggered=self.copySelected)

        self.pasteAct = QAction(QIcon(':/images/paste.png'),
                                      "&Paste Selected Values", self,
                                      statusTip="Paste clipboard into current selection",
                                      iconVisibleInMenu=True,
                                      triggered=self.pasteSelected)

        self.pasteExtAct = QAction(QIcon(':/images/paste.png'),
                                      "&Paste from External", self,
                                      shortcut=QKeySequence('Ctrl+Shift+v'),
                                      statusTip="Paste from external program",
                                      iconVisibleInMenu=True,
                                      triggered=self.paste_from_external)

        self.transposeAct = QAction("Transpose", self,
                                          shortcut=QKeySequence('Ctrl+t'),
                                          statusTip="Transpose rows and columns in object display",
                                          triggered=self.transpose_table)

        self.newObjAct = QAction(QIcon(':/images/new2.png'), "New Object", self,
                                       shortcut=QKeySequence('Ctrl+Shift+n'),
                                       statusTip="Create new object in current class",
                                       iconVisibleInMenu=True,
                                       triggered=self.newObject,
                                       iconText='New Obj')

        self.copyObjAct = QAction(QIcon(':/images/copy.png'), "Copy Object", self,
                                        shortcut=QKeySequence.Copy,
                                        statusTip="Copy the current Object(s)",
                                        iconVisibleInMenu=True,
                                        triggered=self.copyObject,
                                        iconText='Copy Obj')

        self.pasteObjAct = QAction(QIcon(':/images/paste.png'), "Paste Object", self,
                                         shortcut=QKeySequence.Paste,
                                         statusTip="Paste the currently copies Object(s)",
                                         iconVisibleInMenu=True,
                                         triggered=self.pasteObject,
                                         iconText='Paste Obj')

        self.dupObjAct = QAction(QIcon(':/images/copy.png'), "Duplicate Object", self,
                                       shortcut=QKeySequence('Shift+Ctrl+d'),
                                       statusTip="Duplicate the current Object(s)",
                                       iconVisibleInMenu=True,
                                       triggered=self.duplicateObject,
                                       iconText='Dup Obj')

        self.delObjAct = QAction(QIcon(':/images/delete.png'), "Delete Object", self,
                                       shortcut=QKeySequence.Delete,
                                       statusTip="Delete the current Object(s)",
                                       iconVisibleInMenu=True,
                                       triggered=self.deleteObject,
                                       iconText='Del Obj')

        self.undoAct = QAction(QIcon(':/images/undo.png'), "&Undo", self,
                                     shortcut=QKeySequence.Undo,
                                     statusTip="Undo previous action",
                                     iconVisibleInMenu=True,
                                     triggered=self.undo_stack.undo)

        self.redoAct = QAction(QIcon(':/images/redo.png'), "&Redo", self,
                                     shortcut=QKeySequence.Redo,
                                     statusTip="Redo previous action",
                                     iconVisibleInMenu=True,
                                     triggered=self.undo_stack.redo)

        self.groupAct = QAction("Hide Groups in Class Tree", self,
                                      shortcut=QKeySequence('Ctrl+g'),
                                      triggered=self.toggle_groups,
                                      checkable=True)

        # self.navForwardAct = QAction("Forward", self,
        #         shortcut=QKeySequence('Ctrl+Plus'),
        #         statusTip="Go forward to the next object",
        #         triggered=self.navForward)
        #
        # self.navBackAct = QAction("Back", self,
        #         shortcut=QKeySequence('Ctrl+Minus'),
        #         statusTip="Go back to the previous object",
        #         triggered=self.navBack)

        self.showInFolderAct = QAction(QIcon(':/images/new.png'), "&Show in folder",
                                             self, shortcut=QKeySequence('Ctrl+Shift+t'),
                                             statusTip="Open location of current file",
                                             iconVisibleInMenu=True)
        self.showInFolderAct.triggered.connect(lambda: self.show_in_folder())

        self.epDocGettingStartedAction = QAction("EnergyPlus Getting Started", self,
                                                       triggered=self.energy_plus_docs)

        self.epDocIORefAction = QAction("EnergyPlus I/O Reference", self,
                                              triggered=self.energy_plus_docs)

        self.epDocOutputDetailsAction = QAction("EnergyPlus Output Details and Examples",
                                                      self, triggered=self.energy_plus_docs)

        self.epDocEngineeringRefAction = QAction("EnergyPlus Engineering Reference", self,
                                                       triggered=self.energy_plus_docs)

        self.epDocAuxiliaryProgsAction = QAction("EnergyPlus Auxiliary Programs", self,
                                                       triggered=self.energy_plus_docs)

        self.epDocEMSGuideAction = QAction("EnergyPlus EMS Application Guide", self,
                                                 triggered=self.energy_plus_docs)

        self.epDocComplianceAction = QAction("Using EnergyPlus for Compliance", self,
                                                   triggered=self.energy_plus_docs)

        self.epDocInterfaceAction = QAction("External Interface Application Guide", self,
                                                  triggered=self.energy_plus_docs)

        self.epDocTipsTricksAction = QAction("Tips and Tricks Using EnergyPlus", self,
                                                   triggered=self.energy_plus_docs)

        self.epDocPlantGuideAction = QAction("EnergyPlus Plant Application Guide", self,
                                                   triggered=self.energy_plus_docs)

        self.epDocAcknowledgmentsAction = QAction("EnergyPlus Acknowledgments", self,
                                                        triggered=self.energy_plus_docs)

        self.openInEditorAct = QAction(QIcon(':/images/new.png'),
                                             "&Open in text editor", self,
                                             shortcut=QKeySequence('Ctrl+e'),
                                             statusTip="Open current file in default editor",
                                             iconVisibleInMenu=True,
                                             triggered=self.open_in_text_editor)

        self.helpAct = QAction("&EnergyPlus Help (Online)", self,
                                     statusTip="Show the EnergyPlus' help",
                                     triggered=self.energyplus_help)

        self.aboutAct = QAction("&About IDF+", self,
                                      statusTip="Show the application's About box",
                                      triggered=self.about)

        self.clearRecentAct = QAction("Clear Recent", self,
                                            statusTip="Clear recent files",
                                            triggered=self.clear_recent)

        self.minimizeAction = QAction("Mi&nimize", self,
                                            triggered=self.hide)

        self.maximizeAction = QAction("Ma&ximize", self,
                                            triggered=self.showMaximized)

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

        self.showPrefsAction = QAction("&Preferences", self,
                                             triggered=self.show_prefs_dialog)

        self.showSearchAction = QAction("&Search && Replace", self,
                                              shortcut=QKeySequence('Ctrl+f'),
                                              triggered=self.show_search_dialog)

        self.findThisAct = QAction("Find This", self,
                                         triggered=self.find_this)

        self.jumpFilterGeometry = QAction("Include Geometry", self,
                                                triggered=self.jump_to_filter_geometry,
                                                checkable=True)

        self.setIPUnitsAction = QAction("&IP Units", self,
                                              triggered=self.toggle_units,
                                              checkable=True)

        self.setSIUnitsAction = QAction("&SI Units", self,
                                              triggered=self.toggle_units,
                                              checkable=True)

        self.classWithObjsAction = QAction("Show Only Classes With Objects", self,
                                                 shortcut=QKeySequence('Ctrl+l'),
                                                 statusTip="Show Only Classes With Objects",
                                                 triggered=self.toggle_full_tree,
                                                 checkable=True)

        self.fillRightAction = QAction("Fill right", self,
                                             shortcut=QKeySequence('Ctrl+d'),
                                             statusTip="Fill right",
                                             triggered=self.fill_right)

        self.logDockWidgetAct = self.logDockWidget.toggleViewAction()
        self.transposeAct.setEnabled(False)
        self.setSIUnitsAction.setChecked(True)
        self.undoAct.setEnabled(False)
        self.redoAct.setEnabled(False)
        self.saveAct.setEnabled(False)
        self.undo_stack.canUndoChanged.connect(self.toggle_can_undo)
        self.undo_stack.canRedoChanged.connect(self.toggle_can_redo)
        self.logDockWidgetAct.toggled.connect(self.start_log_watcher)

    def toggle_can_undo(self):
        if self.undo_stack.canUndo():
            new_state = True
        else:
            new_state = False
        self.undoAct.setEnabled(new_state)
        self.set_dirty(new_state)

    def toggle_can_redo(self):
        if self.undo_stack.canRedo():
            new_state = True
        else:
            new_state = False
        self.redoAct.setEnabled(new_state)

    def create_menus(self):
        """Create all required items for menus.
        """

        # File Menu
        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenuActions = (self.newAct, self.openAct, self.saveAct,
                                self.saveAsAct, self.saveFormatAct, None, self.exitAct)
        self.update_file_menu()
        self.fileMenu.aboutToShow.connect(self.update_file_menu)

        # Edit Menu
        self.editMenu = self.menuBar().addMenu("&Edit")
        self.editMenu.addAction(self.undoAct)
        self.editMenu.addAction(self.redoAct)
        self.editMenu.addSeparator().setText('Objects')
        self.editMenu.addAction(self.newObjAct)
        self.editMenu.addAction(self.dupObjAct)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.cutObjAct)
        self.editMenu.addAction(self.copyObjAct)
        self.editMenu.addAction(self.pasteObjAct)
        self.editMenu.addAction(self.pasteExtAct)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.delObjAct)
        self.editMenu.addSeparator().setText('Values')
        self.editMenu.addAction(self.copyAct)
        self.editMenu.addAction(self.pasteAct)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.fillRightAction)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.showSearchAction)

        # Tools Menu
        self.toolsMenu = self.menuBar().addMenu("&Tools")
        self.toolsMenu.addAction(self.showInFolderAct)
        self.toolsMenu.addAction(self.openInEditorAct)
        self.toolsMenu.addSeparator()
        self.toolsMenu.addAction(self.showPrefsAction)

        # View Menu
        self.viewMenu = self.menuBar().addMenu("&View")
        action_group = QActionGroup(self)
        self.viewMenu.addAction(action_group.addAction(self.setSIUnitsAction))
        self.viewMenu.addAction(action_group.addAction(self.setIPUnitsAction))
        self.viewMenu.addSeparator().setText('Dockable Widgets')
        self.viewMenu.addAction(self.classTreeDockWidget.toggleViewAction())
        self.viewMenu.addAction(self.infoView.parent().toggleViewAction())
        self.viewMenu.addAction(self.commentView.parent().toggleViewAction())
        self.viewMenu.addAction(self.logDockWidgetAct)
        self.viewMenu.addAction(self.undoView.parent().toggleViewAction())
        self.viewMenu.addSeparator().setText('Toolbars')
        self.viewMenu.addAction(self.fileToolBar.toggleViewAction())
        self.viewMenu.addAction(self.editToolBar.toggleViewAction())
        # self.viewMenu.addAction(self.navToolBar.toggleViewAction())
        self.viewMenu.addAction(self.filterToolBar.toggleViewAction())
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.classWithObjsAction)
        self.viewMenu.addAction(self.groupAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.transposeAct)

        # Jump Menu
        self.jumpToMenu = self.menuBar().addMenu("&Jump")
        self.update_jump_menu()
        self.jumpToMenu.aboutToShow.connect(self.update_jump_menu)
        self.jumpFilterGeometry.setEnabled(False)

        # Help Menu
        self.helpMenu = self.menuBar().addMenu("&Help")
        self.helpMenu.addAction(self.helpAct)
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addSeparator()
        self.helpMenu.addAction(self.epDocGettingStartedAction)
        self.helpMenu.addAction(self.epDocIORefAction)
        self.helpMenu.addAction(self.epDocOutputDetailsAction)
        self.helpMenu.addAction(self.epDocEngineeringRefAction)
        self.helpMenu.addAction(self.epDocAuxiliaryProgsAction)
        self.helpMenu.addAction(self.epDocEMSGuideAction)
        self.helpMenu.addAction(self.epDocComplianceAction)
        self.helpMenu.addAction(self.epDocInterfaceAction)
        self.helpMenu.addAction(self.epDocTipsTricksAction)
        self.helpMenu.addAction(self.epDocPlantGuideAction)
        self.helpMenu.addAction(self.epDocAcknowledgmentsAction)

    def create_tool_bars(self):
        """Creates the necessary toolbars.
        """

        # File Toolbar
        self.fileToolBar = self.addToolBar("File Toolbar")
        self.fileToolBar.setObjectName('fileToolbar')
        self.fileToolBar.addAction(self.newAct)
        self.fileToolBar.addAction(self.openAct)
        self.fileToolBar.addAction(self.saveAct)
        self.fileToolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        # Edit Toolbar
        self.editToolBar = self.addToolBar("Edit Toolbar")
        self.editToolBar.setObjectName('editToolbar')
        self.editToolBar.addAction(self.undoAct)
        self.editToolBar.addAction(self.redoAct)
        self.editToolBar.addAction(self.newObjAct)
        self.editToolBar.addAction(self.dupObjAct)
        self.editToolBar.addAction(self.delObjAct)
        self.editToolBar.addAction(self.cutObjAct)
        self.editToolBar.addAction(self.copyObjAct)
        self.editToolBar.addAction(self.pasteObjAct)
        self.editToolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        # Object history navigation toolbar
        # self.navToolBar = self.addToolBar("Navigation Toolbar")
        # self.navToolBar.setObjectName('viewToolBar')
        # self.navToolBar.addAction(self.navForwardAct)
        # self.navToolBar.addAction(self.navBackAct)
        # self.navToolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        # Object filter toolbar
        self.filterToolBar = self.addToolBar("Filter Toolbar")
        self.filterToolBar.setObjectName('filterToolBar')
        self.filterBox = QLineEdit()
        self.filterBox.setPlaceholderText("Filter Objects")
        self.filterBox.setMaximumWidth(160)
        self.filterBox.setFixedWidth(160)
        # filterLabel = QLabel("Filter Obj:", self)
        # filterLabel.setBuddy(self.filterBox)
        # self.filterToolBar.addWidget(filterLabel)
        self.filterBox.textChanged.connect(self.tableFilterRegExpChanged)
        self.filterBox.textChanged.connect(self.treeFilterRegExpChanged)
        clearFilterButton = QPushButton('Clear')
        clearFilterButton.setMaximumWidth(45)
        clearFilterButton.clicked.connect(self.clearFilterClicked)
        self.filterToolBar.addWidget(self.filterBox)
        self.filterToolBar.addWidget(clearFilterButton)
        self.caseSensitivity = QCheckBox('Case Sensitive')
        self.caseSensitivity.stateChanged.connect(self.caseSensitivityChanged)
        self.filterToolBar.addWidget(self.caseSensitivity)
        self.filterToolBar.addSeparator()
        self.filterToolBar.addAction(self.transposeAct)

    def create_shortcuts(self):
        """Creates keyboard shortcuts.
        """

        # QShortcut(QKeySequence('Ctrl+l'), self).activated.connect(self.toggle_full_tree)
        # QShortcut(QKeySequence('Ctrl+d'), self).activated.connect(self.fill_right)
        # QShortcut(QKeySequence('Ctrl+d'), self).activated.connect(self.fill_right)

#    def createAction(self, text, slot=None, shortcut=None, icon=None,
#                     tip=None, checkable=False, signal="triggered()"):
#        action = QAction(text, self)
#        if icon is not None:
#            action.setIcon(QIcon(":/%s.png" % icon))
#        if shortcut is not None:
#            action.setShortcut(shortcut)
#        if tip is not None:
#            action.setToolTip(tip)
#            action.setStatusTip(tip)
#        if slot is not None:
#            self.connect(action, QtCore.SIGNAL(signal), slot)
#        if checkable:
#            action.setCheckable(True)
#        return action
#

    def custom_table_context_menu(self, position):

        # Create a menu and populate it with actions
        menu = QMenu(self)
        menu.addAction(self.undoAct)
        menu.addAction(self.redoAct)
        menu.addSeparator()
        menu.addAction(self.copyObjAct)
        menu.addAction(self.dupObjAct)
        menu.addAction(self.delObjAct)
        menu.addAction(self.newObjAct)
        menu.addAction(self.cutObjAct)
        menu.addSeparator()
        menu.addMenu(self.jumpToMenu)
        menu.addSeparator()
        menu.addAction(self.findThisAct)
        menu.popup(self.classTable.viewport().mapToGlobal(position))
        self.mouse_position = position

    def reset_progress_bar(self):
        self.progressBarIDF.hide()

    def center(self):
        """Called to center the window on the screen on startup.
        """

        screen = QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move((screen.width() - size.width()) / 2,
                  (screen.height() - size.height()) / 2)

    def show_prefs_dialog(self):
        """Handles showing the settings dialog and setting its values.
        """

        dlg = PrefsDialog(self, self.prefs)
        if dlg.exec_():
            # Refresh the table view to take into account any new prefs
            self.load_table_view(self.current_obj_class)

            # Clear the idd cache if requested
            if self.prefs.get('clear_idd_cache', False) == True:
                self.clear_idd_cache()

            # Update preferences if various flags apply
            if not self.idf:
                return
            options_dict = dict()
            if self.prefs.get('apply_default_save_behaviour', False) == True:
                options_dict.update({'sort_order': self.prefs.get('sort_order'),
                                     'special_formatting': self.prefs.get('special_formatting')})
            # if self.prefs.get('apply_default_units_behaviour', False) == True:
            #     options_dict.update({'save_units': self.prefs.get('save_units')})
            # if self.prefs.get('apply_default_hidden_class_behaviour', False) == True:
            #     options_dict.update({'save_hidden_classes': self.prefs.get('save_hidden_classes')})
            if options_dict:
                self.idf.set_options(options_dict)
                self.set_dirty(True)

    def show_search_dialog(self):
        """Opens the search dialog.
        """

        SearchReplaceDialog(self).show()

    def find_this(self):
        """Searches for fields with similar content.
        """

        index = self.classTable.indexAt(self.mouse_position)
        text = self.classTable.model().data(index, Qt.EditRole)
        if text:
            SearchReplaceDialog(self, initial_query=text).show()
Exemple #10
0
def start(_exit: bool = False) -> None:
    app = QApplication(sys.argv)

    first_start = False
    if not os.path.isfile(STATE_FILE):
        first_start = True

    logo = QIcon(LOGO)
    main_window = MainWindow()
    ui = main_window.ui
    main_window.setWindowIcon(logo)
    tray = QSystemTrayIcon(logo, app)
    tray.activated.connect(main_window.systray_clicked)

    menu = QMenu()
    action_show = QAction("Show")
    action_show.triggered.connect(main_window.show)
    action_exit = QAction("Exit")
    action_exit.triggered.connect(app.exit)
    menu.addAction(action_show)
    menu.addAction(action_exit)

    tray.setContextMenu(menu)

    ui.text.textChanged.connect(partial(queue_text_change, ui))
    ui.command.textChanged.connect(partial(update_button_command, ui))
    ui.keys.textChanged.connect(partial(update_button_keys, ui))
    ui.write.textChanged.connect(partial(update_button_write, ui))
    ui.change_brightness.valueChanged.connect(
        partial(update_change_brightness, ui))
    ui.switch_page.valueChanged.connect(partial(update_switch_page, ui))
    ui.imageButton.clicked.connect(partial(select_image, main_window))
    ui.brightness.valueChanged.connect(partial(set_brightness, ui))
    ui.information.currentIndexChanged.connect(partial(set_information, ui))

    items = api.open_decks().items()
    print("wait for device(s)")

    while len(items) == 0:
        time.sleep(3)
        items = api.open_decks().items()

    print("found " + str(len(items)) + ": " +
          ",".join(str(i) for i in list(items)))

    for deck_id, deck in items:
        ui.device_list.addItem(f"{deck['type']} - {deck_id}", userData=deck_id)

    build_device(ui)
    ui.device_list.currentIndexChanged.connect(partial(build_device, ui))

    ui.pages.currentChanged.connect(partial(change_page, ui))

    ui.actionExport.triggered.connect(partial(export_config, main_window))
    ui.actionImport.triggered.connect(partial(import_config, main_window))
    ui.actionExit.triggered.connect(app.exit)

    timer = QTimer()
    timer.timeout.connect(partial(sync, ui))
    timer.start(1000)

    api.render()
    tray.show()
    if first_start:
        main_window.show()

    if _exit:
        return
    else:
        sys.exit(app.exec_())
Exemple #11
0
class UI:
    """
    WSL2 端口自动转发
    """
    def __init__(self, qt_application=None):
        self.qt_application = qt_application
        # 实例化配置管理类
        self.settings_manage = SettingsManage()
        self.__setting = self.settings_manage.get()

        # 实例化windows命令处理类
        self.wsl2 = WinCmd()

        # 初始化启动脚本
        if not isfile(self.wsl2.WSL_VBS_PATH):
            copyfile(self.wsl2.WSL_VBS_PATH_TEMP, self.wsl2.WSL_VBS_PATH)
        if not isfile(self.wsl2.WSL_BAT_PATH):
            self.settings_manage.save_file_content(
                self.wsl2.WSL_BAT_PATH,
                self.__setting.get('wsl_bat_content', ''))
        # 加载UI文件
        self.ui = QUiLoader().load(ResourcePath.resource_path('lib/wsl2.ui'))

        # 设置界面图标
        app_icon = QIcon(ResourcePath.resource_path("lib/logo.ico"))
        self.ui.setWindowIcon(app_icon)

        # 设置选中状态
        self.ui.auto_start_wsl.setChecked(
            self.__setting.get('auto_start_wsl', False))
        self.ui.fire_wall_open.setChecked(
            self.__setting.get('fire_wall_open', False))
        self.ui.fire_wall_close.setChecked(
            self.__setting.get('fire_wall_close', False))

        # 设置文本框的值
        self.ui.port_text.appendPlainText('\n'.join(
            self.__setting.get('ports', [])))
        self.ui.bat_text.appendPlainText(self.wsl2.get_bat_script())

        # 按钮监听
        self.ui.get_wsl2_ip.clicked.connect(self.__get_wsl2_ip)
        self.ui.port_add.clicked.connect(self.__port_add)
        self.ui.port_del.clicked.connect(self.__port_del)
        self.ui.port_info.clicked.connect(self.__port_info)
        self.ui.wsl_l_v.clicked.connect(self.__wsl_l_v)
        self.ui.port_reset.clicked.connect(self.__port_reset)
        self.ui.end_wsl.clicked.connect(self.__end_wsl)
        self.ui.start_wsl.clicked.connect(self.__start_wsl)
        self.ui.start_wsl_all.clicked.connect(self.start_wsl_all)
        self.ui.save_settings.clicked.connect(self.__save_settings)
        self.ui.save_settings_ports.clicked.connect(self.__save_settings)

        # 设置系统托盘图标的菜单
        tp_icon = QIcon(ResourcePath.resource_path("lib/logo.ico"))
        self.tp = QSystemTrayIcon(self.ui)
        self.tp.setIcon(tp_icon)

        self.ui_hide = QAction(icon=tp_icon,
                               text='隐藏(Hide)',
                               triggered=self.ui.hide)
        self.ui_show = QAction(icon=tp_icon,
                               text='显示(Show)',
                               triggered=self.ui.show)
        self.ui_exit = QAction(icon=tp_icon,
                               text='退出(Exit)',
                               triggered=self.__quit_app)
        self.tp_menu = QMenu()
        self.tp_menu.addAction(self.ui_hide)
        self.tp_menu.addAction(self.ui_show)
        self.tp_menu.addAction(self.ui_exit)
        self.tp.setContextMenu(self.tp_menu)
        self.tp.activated.connect(self.__tp_connect_action)
        self.tp.show()
        self.tp.showMessage('WSL2AutoPortForward', 'WSL2端口自动转发工具已启动',
                            QSystemTrayIcon.MessageIcon.Information)

    def __tp_connect_action(self, activation_reason):
        """
        监听托盘图标点击
        :param activation_reason: 点击类型
        :return:
        """
        if activation_reason == QSystemTrayIcon.ActivationReason.Trigger:
            # 左单击
            if self.ui.isHidden():
                self.ui.show()
            else:
                self.ui.hide()
        elif activation_reason == QSystemTrayIcon.ActivationReason.Context:
            # 右单击
            self.tp_menu.show()
        elif activation_reason == QSystemTrayIcon.ActivationReason.DoubleClick:
            # 双击
            self.ui.show()

    def __quit_app(self):
        """
        退出APP
        :return:
        """
        re = QMessageBox.question(self.ui, "提示", "退出系统",
                                  QMessageBox.Yes | QMessageBox.No,
                                  QMessageBox.No)
        if re == QMessageBox.Yes:
            # 关闭窗体程序
            self.qt_application.quit()
            # 在应用程序全部关闭后,TrayIcon其实还不会自动消失,
            # 直到你的鼠标移动到上面去后,才会消失,
            # 这是个问题,(如同你terminate一些带TrayIcon的应用程序时出现的状况),
            # 这种问题的解决我是通过在程序退出前将其setVisible(False)来完成的。
            self.tp.setVisible(False)

    def __wsl_l_v(self):
        """
        获取wsl信息
        :return:
        """
        wsl_l_v_txt = '  ' + self.wsl2.wsl_l_v(exec_run=True).replace(
            '\x00', '').strip()
        if not wsl_l_v_txt:
            # 未查询到wsl信息提示
            wsl_l_v_txt = '未查询到wsl信息!'
            QMessageBox.information(self.ui, '系统提示', wsl_l_v_txt)
        self.ui.wsl_l_v_text.setPlainText(wsl_l_v_txt)

    def __get_wsl2_ip(self):
        wsl2_ip_info = self.wsl2.get_wsl2_ip()
        if not wsl2_ip_info:
            # 未查询到端口转发信息提示
            QMessageBox.information(self.ui, '系统提示', '未查询到IP信息!')
        else:
            wsl2_ip_info = 'WSL2当前IP为:' + wsl2_ip_info
            self.ui.wsl_l_v_text.setPlainText(wsl2_ip_info)

    def __port_add(self):
        wsl2_ip_info = self.wsl2.get_wsl2_ip()
        if not wsl2_ip_info:
            # 未查询到端口转发信息提示
            QMessageBox.information(self.ui, '系统提示', '未查询到IP信息!')
        else:
            self.ui.result_text.clear()
            ports = self.ui.port_text.toPlainText()
            port_str = ''
            for port in ports.splitlines():
                if not port.strip():
                    continue
                self.__port_add_one(port, wsl2_ip_info)
                port_str += (',' + port if port_str else port)
            self.__fire_wall_rule_add(port_str)
            self.ui.result_text.appendPlainText('Succeed!')

    def __port_del(self, del_port=True, del_fire=True):
        self.ui.result_text.clear()
        ports = self.ui.port_text.toPlainText()
        if del_port:
            for port in ports.splitlines():
                if not port.strip():
                    continue
                self.__port_del_one(port)
        if del_fire:
            self.__fire_wall_rule_del()
        self.ui.result_text.appendPlainText('Succeed!')

    def __port_reset(self):
        port_info = self.wsl2.port_info()
        self.ui.result_text.clear()
        for port in port_info:
            self.__port_del_one(port['port'])
        self.__fire_wall_rule_del()
        self.ui.result_text.appendPlainText('Succeed!')

    def __port_info(self):
        """
        获取端口转发信息
        :return:
        """
        info_str = self.wsl2.port_info(True).strip()
        if not info_str:
            # 未查询到端口转发信息提示
            info_str = '未查询到端口转发信息!'
            QMessageBox.information(self.ui, '系统提示', info_str)
        self.ui.result_text.setPlainText(info_str)

    def __wsl2_auto_port_forward(self):
        """
        一键自动转发
        @return:
        """

        self.__port_del(del_port=False, del_fire=True)
        self.__port_add()

    def __end_wsl(self):
        """
        停止wsl
        :return:
        """
        self.start_qt_process(self.wsl2.end_wsl(exec_run=False))
        info_str = 'wsl 已全部停止'
        QMessageBox.information(self.ui, '系统提示', info_str)

    def __start_wsl(self):
        """
        启动wsl
        :return:
        """
        self.start_qt_process(self.wsl2.start_wsl(exec_run=False))

    def start_wsl_all(self):
        """
        启动wsl并转发端口
        :return:
        """
        self.__start_wsl()
        self.__wsl2_auto_port_forward()

    def __save_settings(self):
        """
        保存全部配置
        :return:
        """
        # 保存脚本
        self.__save_bat_script()

        # 保存配置信息
        self.settings_manage.set('fire_wall_open',
                                 self.ui.fire_wall_open.isChecked())
        self.settings_manage.set('fire_wall_close',
                                 self.ui.fire_wall_close.isChecked())
        self.settings_manage.set('auto_start_wsl',
                                 self.ui.auto_start_wsl.isChecked())
        self.settings_manage.set('ports',
                                 self.ui.port_text.toPlainText().splitlines())

        # 保存成功提示
        QMessageBox.information(self.ui, '系统提示', '配置保存成功!')

    def __save_bat_script(self):
        """
        保存启动脚本
        :return:
        """
        content = self.ui.bat_text.toPlainText()
        self.settings_manage.set('wsl_bat_content', content)
        self.wsl2.save_bat_script(content)

    def __fire_wall_rule_add(self, port):
        """
        添加防火墙
        :param port: 端口号,多个端口逗号隔开
        :return:
        """
        if self.ui.fire_wall_open.isChecked():
            self.start_qt_process(
                self.wsl2.fire_wall_rule_add(
                    wsl_port=port,
                    wall_type=self.wsl2.FireWallRuleIn,
                    exec_run=False))
            self.ui.result_text.appendPlainText('>>> 添加防火墙:【' +
                                                self.wsl2.FireWallRuleIn +
                                                '】...')
            self.start_qt_process(
                self.wsl2.fire_wall_rule_add(
                    wsl_port=port,
                    wall_type=self.wsl2.FireWallRuleOut,
                    exec_run=False))
            self.ui.result_text.appendPlainText('>>> 添加防火墙:【' +
                                                self.wsl2.FireWallRuleOut +
                                                '】...')

    def __fire_wall_rule_del(self):
        """
        删除防火墙
        :return:
        """
        if self.ui.fire_wall_close.isChecked():
            self.start_qt_process(
                self.wsl2.fire_wall_rule_del(
                    wall_type=self.wsl2.FireWallRuleIn, exec_run=False))
            self.ui.result_text.appendPlainText('>>> 删除防火墙:【' +
                                                self.wsl2.FireWallRuleIn +
                                                '】...')
            self.start_qt_process(
                self.wsl2.fire_wall_rule_del(
                    wall_type=self.wsl2.FireWallRuleOut, exec_run=False))
            self.ui.result_text.appendPlainText('>>> 删除防火墙:【' +
                                                self.wsl2.FireWallRuleOut +
                                                '】...')

    def __port_add_one(self, port, wsl2_ip_info):
        """
        添加单个端口
        :param port: 端口号
        :param wsl2_ip_info: 转发的IP
        :return:
        """
        self.start_qt_process(
            self.wsl2.port_add(wsl_ip=wsl2_ip_info,
                               wsl_port=port,
                               exec_run=False))
        self.ui.result_text.appendPlainText('>>> 添加端口:【' + port + '】...')

    def __port_del_one(self, port):
        """
        删除单个端口
        :param port: 端口号
        :return:
        """
        self.start_qt_process(self.wsl2.port_del(wsl_port=port,
                                                 exec_run=False))
        self.ui.result_text.appendPlainText('>>> 删除端口:【' + port + '】...')

    def start_qt_process(self, cmd):
        """
        启动子进程执行耗时命令
        :param cmd:
        :return:
        """
        process = QProcess(self.ui)
        process.start(cmd)
        result = process.waitForStarted()
        return result
Exemple #12
0
class ControlMainWindow(QMainWindow):
    def __init__(self, crypter):
        super(ControlMainWindow, self).__init__(None)
        self.icon = QSystemTrayIcon()
        self.icon.setIcon(QtGui.QIcon('./eve_tray.png'))
        self.icon.show()
        self.setWindowIcon(QtGui.QIcon('./eve_tray.png'))
        self.setWindowTitle('Pve Launcher')
        self.ui = Ui_main_window()
        self.ui.setupUi(self)
        self.icon.activated.connect(self.activate)
        self.account_list_model = QStringListModel()
        self.ui.listView.setModel(self.account_list_model)

        self.login_manager = EveLoginManager(crypter)

        self.init_none_ui(crypter)

        self.settings = None
        self.load_settings()
        self.ui.txt_client_path.setText(self.settings['eve_path'])


    def init_none_ui(self, crypter):

        self.login_manager.load()
        acc_list = []
        for account_name in self.login_manager.accounts:
            acc_list.append(account_name)

        self.account_list_model.setStringList(acc_list)
        version_thread = Thread(target=self.check_eve_version)
        version_thread.start()

    def load_settings(self):
        try:
            with open('pvesettings.json', 'r') as settings_file:
                self.settings = json.load(settings_file)
        except FileNotFoundError:
            self.settings = dict()
            self.settings['eve_path'] = ""

    def save_settings(self):
        with open('pvesettings.json', 'w') as settings_file:
            json.dump(self.settings, settings_file)

    def closeEvent(self, event):
        self.login_manager.save()
        self.save_settings()

    def changeEvent(self, event):
        if event.type() == QEvent.WindowStateChange:
            if self.windowState() & Qt.WindowMinimized:
                self.icon.show()
                QTimer.singleShot(0, self, SLOT('hide()'))
                event.ignore()

    def func_launch(self):
        indexes = self.ui.listView.selectedIndexes()
        # i get QModelIndex here
        for idx in indexes:
            try:
                self.login_manager.login(idx.data(), self.get_auth_code,
                                         self.get_charname,
                                         self.ui.txt_client_path.text(),
                                         self.ui.cbox_server.currentText())
            except Exception as e:
                logger.exception('Failed to launch')
                invoke_in_main_thread(QMessageBox.critical, self, "Launch Error",
                                      e.__str__(), QMessageBox.Ok)

    def func_edit(self):
        indexes = self.ui.listView.selectedIndexes()
        # i get QModelIndex here
        for idx in indexes:
            account = self.login_manager.accounts[idx.data()]
            dialog = AccountDialog("Edit Account", account.login_name,
                                   account.plain_password(self.login_manager.coder),
                                   account.direct_x, account.profile_name)
            if dialog.show():
                # result = [name, password, path, dx]:
                path = dialog.result[2]
                if not path.endswith(os.sep):
                    path = path + os.sep
                account = EveAccount(dialog.result[0], dialog.result[1], self.login_manager.coder,
                                     None, None, dialog.result[3], dialog.result[2])
                self.login_manager.add_account(account)

    def func_add(self):
        dialog = AccountDialog("Create Account")
        if dialog.show():
            # [name, password, profile_name, dx]
            account = EveAccount(dialog.result[0], dialog.result[1], self.login_manager.coder,
                                 None, None, dialog.result[3], dialog.result[2])
            self.login_manager.add_account(account)
            acc_list = self.account_list_model.stringList()
            acc_list.append(account.login_name)
            self.account_list_model.setStringList(acc_list)

    def func_delete(self):
        indexes = self.ui.listView.selectedIndexes()
        # i get QModelIndex here
        model = self.ui.listView.model()
        for idx in indexes:
            self.login_manager.del_account(idx.data())
            model.removeRow(idx.row())

    def func_clear_cache(self):
        self.login_manager.clear_cache()

    def activate(self, reason):
        if reason == QSystemTrayIcon.Trigger or reason == QSystemTrayIcon.DoubleClick:
            self.showNormal()
            self.raise_()
            self.activateWindow()
            # self.setWindowState(Qt.WindowNoState)
            # self.activateWindow()

    def func_browse_eve(self):
        folder = QDir.toNativeSeparators(
            QFileDialog.getExistingDirectory(None, "Eve Directory", "", QFileDialog.ShowDirsOnly))
        if not folder.endswith(os.sep):
            folder += os.sep
        self.ui.txt_client_path.setText(folder)
        self.settings['eve_path'] = folder

    def check_eve_version(self):
        headers = {'User-Agent': EveLoginManager.useragent}
        #version_url = "http://client.eveonline.com/patches/premium_patchinfoTQ_inc.txt"
        #req = request.Request(version_url, headers=headers)
        #response = request.urlopen(req)
        #version_data = response.read().decode('utf-8')
        #match = re.match("BUILD:(\\d+)", version_data)
        server_status = EveApi.get_server_status()
        if server_status.version is None:
            return None

        version_string = str(server_status.version)
        eve_version_okay: bool = check_eve_version(
            version_string,
            self.ui.txt_client_path.text())
        if not eve_version_okay:
            invoke_in_main_thread(QtWidgets.QMessageBox.information, self, "Eve Clients out of date",
                                  "Your eve client is out of date.", QtWidgets.QMessageBox.Ok)

    def set_server_status(self, text, number):
        self.ui.label_server_status.setText(
            QApplication.translate("main_window", text, None)
            + "({0:d})".format(number))

    def get_auth_code(self, opener, request):
        """
        :param opener: urllib.request.build_opener for sending an authcode per mail
        :param request: request to send using the given opener
        :return: the authcode
        """
        inputDialog = QInputDialog(self)
        inputDialog.setInputMode(QInputDialog.TextInput)
        inputDialog.setCancelButtonText("Cancel")
        inputDialog.setLabelText("Please enter your Authcode")
        inputDialog.setWindowTitle("TwoFactorAuth")
        inputDialog.setModal(True)

        response = None

        if inputDialog.exec_() == QInputDialog.Rejected:  # send mail
            return None, None
        else:
            return response, inputDialog.textValue().strip()

        inputDialog.setCancelButtonText("Cancel")
        if inputDialog.exec_() == QInputDialog.Rejected:
            return response, None
        return response, inputDialog.textValue().strip()

    def get_charname(self):
        """
        :param mailurl: url to call for sending an authcode per mail
        :return: the authcode
        """
        inputDialog = QInputDialog(self)
        inputDialog.setInputMode(QInputDialog.TextInput)
        inputDialog.setLabelText("Please enter a Charname")
        inputDialog.setWindowTitle("Charname Challange")
        inputDialog.setModal(True)

        if inputDialog.exec_() == QInputDialog.Rejected:
            return None

        return inputDialog.textValue().strip()
Exemple #13
0
class MainWindow(QMainWindow):
    """The main window of the application

    Currently just displays "Hello, world!".
    """
    def __init__(self):
        QMainWindow.__init__(self)

        icon = QIcon(ICON_IMAGE)

        self.setMinimumSize(300, 50)
        self.setWindowTitle("Echo VR Tray Tool")
        self.setWindowIcon(icon)

        main_widget = QWidget(self)
        self.setCentralWidget(main_widget)

        main_layout = QGridLayout(main_widget)

        discord_status_header = QLabel("Discord status:")
        discord_status_header.setFont(_HEADER_FONT)
        main_layout.addWidget(discord_status_header, 0, 0, Qt.AlignRight)

        self._discord_status_label = QLabel("Unknown")
        main_layout.addWidget(self._discord_status_label, 0, 1, Qt.AlignLeft)

        echo_vr_status_header = QLabel("Echo VR client status:")
        echo_vr_status_header.setFont(_HEADER_FONT)
        main_layout.addWidget(echo_vr_status_header, 1, 0, Qt.AlignRight)

        self._echo_vr_client_status_label = QLabel("Unknown")
        main_layout.addWidget(self._echo_vr_client_status_label, 1, 1,
                              Qt.AlignLeft)

        main_layout.setRowStretch(2, 1)

        self.tray_icon = QSystemTrayIcon(icon, self)

        tray_menu = QMenu()

        show_action = QAction("Show", self)
        show_action.triggered.connect(self.show)
        tray_menu.addAction(show_action)

        quit_action = QAction("Exit", self)
        quit_action.triggered.connect(self._quit)
        tray_menu.addAction(quit_action)

        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

        self._discord_presence_thread = None
        self._start_discord_presence_thread()

    def closeEvent(self, event):
        """Overridden to minimize to tray instead of exiting"""

        event.ignore()
        self.hide()
        self.tray_icon.showMessage(
            "Application is still running",
            "Echo VR Tray Tool was minimized to tray. Right-click and press 'exit' to quit.",
            QSystemTrayIcon.Information,
            3000,
        )

    def _quit(self):
        if self._discord_presence_thread:
            self._discord_presence_thread.exit()
            self._discord_presence_thread.wait()

        qApp.quit()

    def _start_discord_presence_thread(self):
        if self._discord_presence_thread:
            return

        self._discord_presence_thread = DiscordPresenceThread()
        self._discord_presence_thread.connection_status_changed.connect(
            self._discord_connection_status_changed)
        self._discord_presence_thread.game_client_status_changed.connect(
            self._game_client_status_changed)
        self._discord_presence_thread.start()

    def _discord_connection_status_changed(self, connected):
        if connected:
            self._discord_status_label.setText("Connected")
            self._discord_status_label.setStyleSheet(_GREEN_LABEL)
        else:
            self._discord_status_label.setText("Disconnected")
            self._discord_status_label.setStyleSheet(_RED_LABEL)

    def _game_client_status_changed(self, connected):
        if connected:
            self._echo_vr_client_status_label.setText("Connected")
            self._echo_vr_client_status_label.setStyleSheet(_GREEN_LABEL)
        else:
            self._echo_vr_client_status_label.setText("Disconnected")
            self._echo_vr_client_status_label.setStyleSheet(_RED_LABEL)
Exemple #14
0
class MainWindow(Ui_Form):
    def __init__(self, parent=None, args=None):
        super(MainWindow, self).__init__(parent)
        self.appname = "poliBeePsync"
        self.settings_fname = 'pbs-settings.ini'
        self.data_fname = 'pbs.data'
        self.setupUi(self)
        self.w = QWidget()

        self.status_signal = MySignal()
        self.status_signal.sig.connect(self.update_status_bar)

        self.logging_signal = MySignal()
        self.logging_signal.sig.connect(self.myStream_message)
        logging_console_hdl = SignalLoggingHandler(self.logging_signal)
        logger.addHandler(logging_console_hdl)
        commonlogger.addHandler(logging_console_hdl)

        self.timer = QTimer(self)

        # settings_path is a string containing the path to settings
        self.settings_path = None
        # settings is a dictionary of settings
        self.settings = None
        # load_settings() sets settings_path and settings
        self.load_settings()
        self.load_data()
        if args.default_timeout:
            self.user.default_timeout = args.default_timeout

        self.timer.timeout.connect(self.syncfiles)
        self.timer.start(1000 * 60 * int(self.settings['UpdateEvery']))

        self.loginthread = LoginThread(self.user, self)

        self.loginthread.signal_error.sig.connect(self.update_status_bar)
        self.loginthread.signal_ok.sig.connect(self.update_status_bar)

        self.refreshcoursesthread = RefreshCoursesThread(self.user, self)
        self.refreshcoursesthread.dumpuser.sig.connect(self.dumpUser)
        self.refreshcoursesthread.newcourses.sig.connect(self.addtocoursesview)
        self.refreshcoursesthread.newcourses.sig.connect(self.syncnewcourses)
        self.refreshcoursesthread.removable.sig.connect(self.rmfromcoursesview)

        self.downloadthread = DownloadThread(self.user,
                                             self.settings['RootFolder'], self)
        self.downloadthread.dumpuser.sig.connect(self.dumpUser)
        self.downloadthread.download_signal.connect(
            self.update_course_download)
        self.downloadthread.initial_sizes.connect(self.setinizialsizes)
        self.downloadthread.date_signal.connect(self.update_file_localtime)

        self._window.userCode.setText(str(self.user.username))
        self._window.userCode.editingFinished.connect(self.setusercode)
        self._window.password.setText(self.user.password)
        self._window.password.editingFinished.connect(self.setpassword)
        self._window.trylogin.clicked.connect(self.testlogin)

        self._window.courses_model = CoursesListModel(
            self.user.available_courses)
        self._window.coursesView.setModel(self._window.courses_model)
        self._resizeview()
        self._window.refreshCourses.clicked.connect(self.refreshcourses)

        self._window.syncNow.clicked.connect(self.syncfiles)

        self._window.rootfolder.setText(self.settings['RootFolder'])
        self._window.rootfolder.textChanged.connect(self.rootfolderslot)

        init_checkbox(self._window.addSyncNewCourses,
                      self.settings,
                      'SyncNewCourses',
                      state_slot=self.syncnewslot)

        init_checkbox(self._window.startupSync,
                      self.settings,
                      'SyncOnStartup',
                      state_slot=self.sync_on_startup_slot)

        self._window.timerMinutes.setValue(int(self.settings['UpdateEvery']))
        self._window.timerMinutes.valueChanged.connect(self.updateminuteslot)

        self._window.changeRootFolder.clicked.connect(self.chooserootdir)
        self._window.version_label.setText(
            "Current version: {}".format(__version__))
        self._window.check_version.clicked.connect(self.checknewversion)

        self._window.about.clicked.connect(self.showabout)

        self.trayIconMenu = QMenu()
        self.trayIcon = QSystemTrayIcon(self.icon, self.w)
        self.trayIcon.activated.connect(self._activate_traymenu)
        self.createTray()

        try:
            if args.sync_on_startup or \
               self.settings['SyncOnStartup'] == str(True):
                self.syncfiles()
        except KeyError:
            pass
        if args.sync_interval:
            logger.info('Sync interval overridden with '
                        f'{args.sync_interval} minutes')
            self.timer.start(1000 * 60 * args.sync_interval)

    @Slot()
    def showabout(self, **kwargs):
        msgBox = QMessageBox(self._window)
        msgBox.setTextFormat(Qt.RichText)
        msgBox.setWindowTitle('About poliBeePSync')
        text = """
<html>
<head/>
<body>
  <p>
    poliBeePsync is a program written by Davide Olianas and Raffaele Di Campli
    released under GNU GPLv3+.
    More information is available on the
    <a href=\"https://github.com/Jacotsu/polibeepsync\">
    <span style=\" text-decoration: underline; color:#0000ff;\">
    official github</span></a>.
    Feel free to contact us at
    <a href=\"mailto:[email protected]\">[email protected]</a> for
    suggestions and bug reports.
  </p>
  <p>
  Want to learn how to make softwares like this? Then join <br>
    <a href='https://poul.org/'>
      <img src=':/root/imgs/PinguiniStilNovoFullLogoBlack.svg'>
    </a>
  </p>
  <p>
    <a href='https://liberapay.com/jacotsu/donate'>
        Want to offer me a sandwich?
    </a>
  </p>
</body>
</html>
"""
        msgBox.setInformativeText(text)
        msgBox.exec()

    @Slot()
    def _resizeview(self, **kwargs):
        self._window.coursesView.setColumnWidth(3, 160)
        self._window.coursesView.resizeColumnToContents(1)
        self._window.coursesView.setColumnWidth(0, 320)

    def checknewversion(self):
        rawdata = requests.get('https://pypi.python.org/pypi/'
                               'poliBeePsync/json')
        latest = json.loads(rawdata.text)['info']['version']
        if latest != __version__:
            newtext = 'Current version: {}. Latest version: {}. '\
                'Click <a href="https://jacotsu.github.io/polibeepsync/build/'\
                'html/installation.html">here</a>'\
                ' to find out how to upgrade'.format(__version__, latest)
        else:
            newtext = "Current version: {} up-to-date.".format(__version__)
        self._window.version_label.setText(newtext)

    def _update_time(self, folder, file, path_list):
        logger.debug(f'inside {folder.name}')
        for path in path_list:
            logger.debug(f'namegoto: {path}')
            folder_dict = {'name': path}
            fakefolder = Folder(folder_dict)
            logger.debug(f'contained folders:  {folder.folders}')
            ind = folder.folders.index(fakefolder)
            goto = folder.folders[ind]
            self._update_time(goto, file, path_list)

        if file in folder.files:
            ind = folder.files.index(file)
            thisfile = folder.files[ind]
            thisfile.local_creation_time = file.local_creation_time

    @Slot(tuple)
    def update_file_localtime(self, data, **kwargs):
        course, coursefile, path = data
        rootpath = os.path.join(self.settings['RootFolder'],
                                course.save_folder_name)
        if path.startswith(rootpath):
            partial = path[len(rootpath):]
        path_list = filter(None, partial.split(os.path.sep))
        self._update_time(course.documents, coursefile, path_list)

    @Slot(Course)
    def update_course_download(self, course, **kwargs):
        if course in self.user.available_courses:
            updating = self.user.available_courses[course.name]
            updating.downloaded_size = course.downloaded_size
            row = self._window.courses_model.courses.index(updating)
            where = self._window.courses_model.index(row, 3)
            self._window.courses_model.dataChanged.emit(where, where)

    @Slot(Course)
    def setinizialsizes(self, course, **kwargs):
        if course in self.user.available_courses:
            updating = self.user.available_courses[course.name]
            updating.downloaded_size = course.downloaded_size
            updating.size = course.size
            row = self._window.courses_model.courses.index(updating)
            where = self._window.courses_model.index(row, 3)
            self._window.courses_model.dataChanged.emit(where, where)
            self.dumpUser()

    @Slot(list)
    def syncnewcourses(self, newlist):
        if self.settings['SyncNewCourses'] == 'True':
            for elem in newlist:
                elem.sync = True

    def load_settings(self):
        for path in [
                user_config_dir(self.appname),
                user_data_dir(self.appname)
        ]:
            try:
                os.makedirs(path, exist_ok=True)
            except OSError:
                logger.critical('OSError while calling os.makedirs.',
                                exc_info=True)
                logger.critical(f"I couldn't create {path}.\nStart"
                                " poliBeePsync with --log-level=debug "
                                "error to get more details.")
        self.settings_path = os.path.join(user_config_dir(self.appname),
                                          self.settings_fname)
        defaults = {
            # Update every 8 hours
            'UpdateEvery': '480',
            'RootFolder': os.path.join(os.path.expanduser('~'), self.appname),
            'SyncNewCourses': 'False',
            'SyncOnStartup': 'False'
        }
        self.settings = filesettings.settingsFromFile(self.settings_path,
                                                      defaults)

    def load_data(self):
        try:
            with open(
                    os.path.join(user_data_dir(self.appname), self.data_fname),
                    'rb') as f:
                self.user = pickle.load(f)
                self.user.password = keyring\
                    .get_password('beep.metid.polimi.it',
                                  self.user.username)
                logger.info("Data has been loaded successfully.")
        except (EOFError, pickle.PickleError):
            logger.error('Settings corrupted', exc_info=True)
            self.user = User('', '')

        except FileNotFoundError:
            logger.error('Settings file not found.')
            self.user = User('', '')
            logger.error("I couldn't find data in the"
                         " predefined directory. Ignore this"
                         "message if you're using poliBeePsync"
                         " for the first time.")

    @Slot(str)
    def update_status_bar(self, status):
        self._window.statusbar.showMessage(status)

    @Slot(int)
    def syncnewslot(self, state):
        if state == 2:
            self.settings['SyncNewCourses'] = 'True'
            logger.info('New courses will now be automatically synced')
        else:
            self.settings['SyncNewCourses'] = 'False'
            logger.info('New courses will NOT be automatically synced')
        filesettings.settingsToFile(self.settings, self.settings_path)

    @Slot(int)
    def sync_on_startup_slot(self, state):
        if state == 2:
            self.settings['SyncOnStartup'] = 'True'
            logger.info('All courses will be synced at startup')
        else:
            self.settings['SyncOnStartup'] = 'False'
            logger.info('No course will be synced at startup')
        filesettings.settingsToFile(self.settings, self.settings_path)

    @Slot(int)
    def updateminuteslot(self, minutes):
        self.settings['UpdateEvery'] = str(minutes)
        filesettings.settingsToFile(self.settings, self.settings_path)
        self.timer.start(1000 * 60 * int(self.settings['UpdateEvery']))
        logger.info('All courses will be automatically synced every '
                    f'{self.settings["UpdateEvery"]} minutes')

    @Slot(str)
    def rootfolderslot(self, path):
        self.settings['RootFolder'] = path
        filesettings.settingsToFile(self.settings, self.settings_path)
        logger.info(f'Root folder set to: {path}')

    @Slot()
    def chooserootdir(self):
        currentdir = self.settings['RootFolder']
        flags = QFileDialog.DontResolveSymlinks | QFileDialog.ShowDirsOnly
        newroot = QFileDialog.getExistingDirectory(None, "Open Directory",
                                                   currentdir, flags)
        if newroot != "" and str(newroot) != currentdir:
            self.settings['RootFolder'] = str(newroot)
            filesettings.settingsToFile(self.settings, self.settings_path)
            self._window.rootfolder.setText(newroot)
            # we delete the already present downloadthread and recreate it
            # because otherwise it uses the old download folder. I don't know
            # if there's a cleaner approach
            del self.downloadthread
            self.downloadthread = DownloadThread(self.user,
                                                 self.settings['RootFolder'],
                                                 self)
            self.downloadthread.dumpuser.sig.connect(self.dumpUser)
            self.dumpUser()

    @Slot()
    def setusercode(self):
        newcode = self._window.userCode.text()
        try:
            if len(newcode) == 8:
                self.user.username = newcode
                logger.info(f'User code changed to {newcode}.')
                keyring.set_password('beep.metid.polimi.it',
                                     self.user.username, self.user.password)
        except OSError:
            logger.critical("I couldn't save data to disk. Run"
                            " poliBeePsync with option --log-level=debug"
                            " error to get more details.")
            logger.error(
                'OSError raised while trying to write the User'
                'instance to disk.',
                exc_info=True)

    @Slot()
    def setpassword(self):
        newpass = self._window.password.text()
        self.user.password = newpass
        try:
            keyring.set_password('beep.metid.polimi.it', self.user.username,
                                 self.user.password)
            logger.info("Password changed.")
        except OSError:
            logger.critical("I couldn't save data to disk. Run"
                            " poliBeePsync with option --log-level=debug"
                            " error to get more details.")
            logger.error(
                'OSError raised while trying to write the User'
                'instance to disk.',
                exc_info=True)

    @Slot()
    def testlogin(self):
        if not self.loginthread.isRunning():
            self.loginthread.exiting = False
            self.loginthread.start()
            self.status_signal.sig.emit("Logging in, please wait.")

    @Slot(list)
    def addtocoursesview(self, addlist):
        for elem in addlist:
            self._window.courses_model.insertRows(0, 1, elem)

    @Slot(list)
    def rmfromcoursesview(self, removelist):
        for elem in removelist:
            index = self._window.courses_model.courses.index(elem)
            self._window.courses_model.removeRows(index, 1)

    @Slot()
    def dumpUser(self):
        # we don't use the message...
        with open(os.path.join(user_data_dir(self.appname), self.data_fname),
                  'wb') as f:
            tmp_pw = self.user.password
            self.user.password = ''
            pickle.dump(self.user, f)
            self.user.password = tmp_pw

    @Slot()
    def refreshcourses(self):
        self.status_signal.sig.emit('Searching for online updates...'
                                    'this may take a while.')
        if not self.loginthread.isRunning():
            self.loginthread.exiting = False
            self.loginthread.signal_ok.sig.connect(self.do_refreshcourses)
            self.loginthread.start()

    def do_refreshcourses(self):
        self.loginthread.signal_ok.sig.disconnect(self.do_refreshcourses)
        if not self.refreshcoursesthread.isRunning():
            self.refreshcoursesthread.start()

    @Slot()
    def syncfiles(self):
        # we delete the already present downloadthread and recreate it
        # because otherwise it uses the old download folder. I don't know
        # if there's a cleaner approach
        del self.downloadthread
        self.downloadthread = DownloadThread(self.user,
                                             self.settings['RootFolder'], self)
        self.downloadthread.dumpuser.sig.connect(self.dumpUser)

        self.refreshcoursesthread.finished.connect(self.do_syncfiles)
        self.refreshcourses()

    @Slot()
    def do_syncfiles(self):
        self.refreshcoursesthread.finished.disconnect(self.do_syncfiles)
        self.status_signal.sig.emit('Started syncing.')
        self.downloadthread.start()

    @Slot(str)
    def myStream_message(self, message):
        self._window.status.moveCursor(QTextCursor.End)
        self._window.status.insertPlainText(message + "\n")

    def restore_window(self):
        self._window.setWindowState(self.windowState() & ~Qt.WindowMinimized
                                    | Qt.WindowActive)
        self._window.show()

    def createTray(self):
        restoreAction = QAction("&Restore",
                                self,
                                triggered=self.restore_window)
        quitAction = QAction("&Quit",
                             self,
                             triggered=QApplication.instance().quit)
        self.trayIconMenu.addAction(restoreAction)
        self.trayIconMenu.addAction(quitAction)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.show()

    @Slot(str)
    def _activate_traymenu(self, reason):
        if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
            self.restore_window()
        else:
            self.trayIconMenu.activateWindow()
            self.trayIconMenu.popup(QCursor.pos())

    def closeEvent(self, event):
        self._window.hide()
        event.ignore()
Exemple #15
0
def start(_exit: bool = False) -> None:
    show_ui = True
    if "-h" in sys.argv or "--help" in sys.argv:
        print(f"Usage: {os.path.basename(sys.argv[0])}")
        print("Flags:")
        print("  -h, --help\tShow this message")
        print("  -n, --no-ui\tRun the program without showing a UI")
        return
    elif "-n" in sys.argv or "--no-ui" in sys.argv:
        show_ui = False

    app = QApplication(sys.argv)

    logo = QIcon(LOGO)
    main_window = MainWindow()
    ui = main_window.ui
    main_window.setWindowIcon(logo)
    tray = QSystemTrayIcon(logo, app)
    tray.activated.connect(main_window.systray_clicked)

    menu = QMenu()
    action_exit = QAction("Exit")
    action_exit.triggered.connect(app.exit)
    menu.addAction(action_exit)

    tray.setContextMenu(menu)

    ui.text.textChanged.connect(partial(queue_text_change, ui))
    ui.command.textChanged.connect(partial(update_button_command, ui))
    ui.keys.textChanged.connect(partial(update_button_keys, ui))
    ui.write.textChanged.connect(partial(update_button_write, ui))
    ui.change_brightness.valueChanged.connect(partial(update_change_brightness, ui))
    ui.switch_page.valueChanged.connect(partial(update_switch_page, ui))
    ui.imageButton.clicked.connect(partial(select_image, main_window))
    ui.removeButton.clicked.connect(partial(remove_image, main_window))
    ui.settingsButton.clicked.connect(partial(show_settings, main_window))

    api.streamdesk_keys.key_pressed.connect(handle_keypress)

    items = api.open_decks().items()
    if len(items) == 0:
        print("Waiting for Stream Deck(s)...")
        while len(items) == 0:
            time.sleep(3)
            items = api.open_decks().items()

    for deck_id, deck in items:
        ui.device_list.addItem(f"{deck['type']} - {deck_id}", userData=deck_id)
        dimmers[deck_id] = Dimmer(
            api.get_display_timeout(deck_id),
            api.get_brightness(deck_id),
            partial(change_brightness, deck_id),
        )
        dimmers[deck_id].reset()

    build_device(ui)
    ui.device_list.currentIndexChanged.connect(partial(build_device, ui))

    ui.pages.currentChanged.connect(partial(change_page, ui))

    ui.actionExport.triggered.connect(partial(export_config, main_window))
    ui.actionImport.triggered.connect(partial(import_config, main_window))
    ui.actionExit.triggered.connect(app.exit)

    timer = QTimer()
    timer.timeout.connect(partial(sync, ui))
    timer.start(1000)

    api.render()
    tray.show()

    if show_ui:
        main_window.show()

    if _exit:
        return
    else:
        app.exec_()
        api.close_decks()
        sys.exit()
Exemple #16
0
    configuration_file_path = config_path.ConfigPath('pynfinitton',
                                                     'creatingfuture.eu',
                                                     '.ini')

    device_manager = logic.DeviceManager(configuration_file_path)

    # If graphical
    app = QApplication(sys.argv)

    translator = QTranslator()
    translator.load(QLocale(device_manager.locale),
                    'pynfinitton',
                    '.',
                    directory=os.path.join(path, 'resources', 'translations'))
    app.installTranslator(translator)

    window = gui.MainWindow(device_manager)
    window.resize(800, 600)
    if device_manager.open_on_start:
        window.show()

    tray_icon_menu = gui.TrayIconMenu(window)

    tray_icon = QSystemTrayIcon(QIcon('resources/ui/light/app_icon.png'),
                                window)
    tray_icon.setToolTip("Pynfinitton")
    tray_icon.setContextMenu(tray_icon_menu)
    tray_icon.show()

    sys.exit(app.exec_())
Exemple #17
0
class MainWidget(QWidget):
    def __init__(self, app):
        QWidget.__init__(self)
        log.info('Creating MainWidget')

        # Configurar janela e sessão requests
        self.setWindowTitle('Desafio Upload')
        self.setWindowIcon(QIcon("icon.png"))
        self.session = requests.Session()

        # Configurar telas mostradas em StackedLayout
        self.stackedLayout = QStackedLayout()

        self.login_layout = LoginWidget(self.session)
        self.login_layout.did_login.connect(self.go_to_main_ui)
        self.setFixedSize(self.login_layout.size())

        self.main_layout = UploadViewWidget(self.session)

        self.stackedLayout.addWidget(self.login_layout)
        self.stackedLayout.addWidget(self.main_layout)

        self.setLayout(self.stackedLayout)

        # Inicializar variáveis relacionadas ao comportamento de system tray
        self.closeEvent = self.on_close
        self.system_tray = QSystemTrayIcon()
        self.system_tray.setContextMenu(QMenu('Hi!', self))

        # Tray menu
        self.tray = QSystemTrayIcon(QIcon("icon.png"), self)
        self.tray_menu = QMenu(self)

        action_show_window = QAction("Mostrar janela principal", self)
        action_show_window.triggered.connect(self.on_show_main_window)
        self.tray_menu.addAction(action_show_window)

        action_exit = QAction("Fechar", self)
        action_exit.triggered.connect(app.exit)
        self.tray_menu.addAction(action_exit)

        self.tray.setContextMenu(self.tray_menu)
        self.tray.activated.connect(self.on_tray_activated)
        self.tray.hide()

    # # # # # # # # # # # # # # # # # # # #
    # Transição entre telas

    def go_to_main_ui(self, username: str):
        self.main_layout.label_welcome_username.setText(
            f'Bem-vindo, {username}.')
        self.stackedLayout.setCurrentIndex(1)

    # # # # # # # # # # # # # # # # # # # #
    # Interação com o system tray e visibilidade da janela principal

    def on_close(self, event):
        log.info('Moving program to tray')
        self.tray.show()

    def on_show_main_window(self):
        log.info('Moving program from tray to window')
        self.show()
        self.tray.hide()

    def on_tray_activated(self, event: QSystemTrayIcon.ActivationReason):
        if event == QSystemTrayIcon.ActivationReason.Trigger:
            self.on_show_main_window()
Exemple #18
0
class App:
    killSignal = signal.SIGUSR1

    def __init__(self):
        # Qt application creation.
        self.qApp = QApplication(sys.argv)

        # System tray icon menu creation.
        menu = QMenu()
        showMainWindowAction = menu.addAction(_('Show %s window') % (APP_NAME))
        showMainWindowAction.triggered.connect(self.showMainWindow)
        menu.addSeparator()
        exitAction = menu.addAction(_('Exit'))
        exitAction.triggered.connect(self._quit)

        # System tray icon creation.
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(QIcon(self.getResourceFile(APP_ICON)))
        self.tray.setContextMenu(menu)
        self.tray.show()
        self.tray.setToolTip(APP_NAME)
        self.tray.activated.connect(self._iconActivated)

        # QWebEngineView to handle external urls opening.
        self.wevExternalUrl = QWebEngineView()

    def _download(self, download):
        filename = QFileDialog.getSaveFileName(None, _('Save as'),
                                               download.path(), "")

        if (filename[0] == ''):
            download.cancel()

        else:
            download.setPath(filename[0])
            download.accept()

    def _iconActivated(self, reason):
        if (reason == QSystemTrayIcon.Trigger):
            self.toggleVisible()

    def _kill(self):
        if (self.killSignal == signal.SIGUSR1):
            self.killSignal = signal.SIGTERM

        elif (self.killSignal == signal.SIGTERM):
            self.killSignal = signal.SIGKILL

        print('Killing...')
        os.killpg(0, self.killSignal)

    def _launchExternalUrl(self, url):
        self.wevExternalUrl.urlChanged.disconnect(self._launchExternalUrl)
        _EOU(url.toString())

    def _quit(self, checked):
        # Sometimes QtWebEngineProcess hangs and next code is a work around.
        self.timer = QTimer()
        self.timer.timeout.connect(self._kill)
        self.timer.start(5000 * 10)  # Two seconds for mercy.
        sys.exit()

    def getResourceFile(self, fileName):
        fileNameAux = fileName.lower()
        if (fileNameAux == APP_ICON):
            fileName = os.path.join(_PR2A('desktop'), 'Juasap.png')

        return fileName

    def hideMainWindow(self):
        self.mainWindow.hide()

    def run(self):
        self.mainWindow = MainWindow()
        availableGeometry = self.qApp.desktop().availableGeometry(
            self.mainWindow)
        self.mainWindow.resize(availableGeometry.width() * 0.40,
                               availableGeometry.height() * 0.90)
        self.mainWindow.show()

    def showMainWindow(self):
        self.mainWindow.show()

    def toggleVisible(self):
        if self.mainWindow.isVisible():
            self.hideMainWindow()
        else:
            self.showMainWindow()
Exemple #19
0
class MyWidget(QWidget):
    def __init__(self):
        super().__init__()

        self.streamfile = ""
        self.streams = {}
        scriptdir = os.path.dirname(os.path.realpath(__file__))
        icon = (scriptdir + os.path.sep + "icon/pyradio.ico")
        self.setWindowIcon(QtGui.QIcon(icon))
        self.setStuff()
        # Tray
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(QtGui.QIcon(icon))
        self.tray.activated.connect(self.call)

        self.icon = QtGui.QIcon()
        self.icon.addFile(icon)
        self.setWindowIcon(self.icon)

        # tray menu
        self.trayIconMenu = QtWidgets.QMenu()
        self.quitAction = QtWidgets.QAction("&Quit", triggered=self.close)
        self.trayIconMenu.addAction(self.quitAction)
        self.tray.setContextMenu(self.trayIconMenu)
        self.trayIconMenu.setStyleSheet(open("css/main.css", "r").read())

        # Media player
        self.radio = vlc.MediaPlayer()
        self.playing = False

        self.pal = QtGui.QPalette(self.palette())

        self.playing_label = QLabel("Stopped")
        self.label = QLabel("Radios:")

        self.label.setAlignment(Qt.AlignCenter)
        self.playing_label.setAlignment(Qt.AlignCenter)
        self.btn = QPushButton("Play/Stop")
        self.btn.clicked.connect(self.control)
        self.list = QListWidget()
        self.list.itemDoubleClicked.connect(self.control)

        self.edit = QPushButton("Edit Radios")
        self.edit.clicked.connect(self.openfile)
        self.refresh = QPushButton("Refresh")
        self.refresh.clicked.connect(self.refreshstreams)

        self.slider = QSlider(QtGui.Qt.Horizontal)
        self.slider.setMaximum(100)
        self.slider.setValue(self.volume)
        self.slider.valueChanged.connect(self.changeVolume)

        self.setStyleSheet(open("css/main.css", "r").read())

        self.refreshstreams()

        self.current = ""
        self.buttons = QHBoxLayout()

        self.layout = QVBoxLayout()
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.list)
        self.layout.addWidget(self.playing_label)
        self.layout.addWidget(self.slider)
        self.buttons.addWidget(self.btn)
        self.buttons.addWidget(self.edit)
        self.buttons.addWidget(self.refresh)

        self.layout.addLayout(self.buttons)
        self.setLayout(self.layout)

    def setStuff(self):
        info = self.readInfo()
        print(info)
        if len(info) == 0:
            info = ["", "", "", "", ""]
        if (info[0] == ""):
            self.volume = 80
        else:
            self.volume = int(info[0])
        if info[3].strip() == "false" or info[3] == "":
            if info[1] == "":
                self.resize(800, 600)
            else:
                w, h = info[1].split(" ")
                self.resize(int(w), int(h))
            if info[2] != "":
                x, y = info[2].split(" ")
                self.move(int(x), int(y))
        else:
            self.showMaximized()
        if len(info) < 5:
            # show dialog
            self.chooseStreamfile()
        else:
            self.streamfile = info[4].strip()

    def chooseStreamfile(self):
        self.dialog = QFileDialog(self)
        self.dialog.setFileMode(QFileDialog.AnyFile)
        if self.dialog.exec_():
            filename = self.dialog.selectedFiles()
            self.streamfile = filename[0]

    def changeVolume(self):
        self.volume = self.slider.value()
        self.radio.audio_set_volume(self.volume)

    def getVolume(self):
        try:
            with open("data", "r") as file:
                return int(file.readline())
        except:
            with open("data", "w") as file:
                file.write(str(80))
                return 80

    def control(self):
        if self.playing and self.current == self.streams[
                self.list.currentItem().text()]:
            self.stop()
        else:
            self.radio.stop()
            self.play()

    def stop(self):
        self.radio.stop()
        self.playing_label.setText("Stopped")
        self.playing = False

    def play(self):
        self.current = self.list.currentItem().text()
        for i in self.streams:
            if self.current == i:
                self.current = self.streams[i]
                break
        self.radio = vlc.MediaPlayer(self.current)
        self.radio.play()
        self.radio.audio_set_volume(self.slider.value())
        self.playing_label.setText("Playing")
        self.playing = True
        self.tray.showMessage(self.list.currentItem().text(), "",
                              self.tray.icon(), 1000)

    def next(self):
        isthis = False
        self.current = self.list.currentItem().text()
        for n, i in enumerate(self.streams):
            if isthis:
                self.list.setCurrentRow(n)
                break
            else:
                if self.current == i:
                    isthis = True
                    if n + 1 >= len(self.streams):
                        self.list.setCurrentRow(0)
        self.stop()
        self.play()

    def previous(self):
        isthis = False
        self.current = self.list.currentItem().text()
        for n, i in enumerate(self.streams):
            if isthis:
                self.list.setCurrentRow(n - 2)
                break
            else:
                if self.current == i:
                    isthis = True
                    if n - 1 < 0:
                        self.list.setCurrentRow(len(self.streams) - 1)
                        break
                    elif n == len(self.streams) - 1:
                        self.list.setCurrentRow(n - 1)

        self.stop()
        self.play()

    def openfile(self):
        # Opens radios.txt
        webbrowser.open(self.streamfile)

    def refreshstreams(self):

        # Refreshes the stream list when button pressed
        if self.list.currentItem():
            current = self.list.currentItem().text()
        else:
            current = None
        self.streams = {}
        try:
            with open(self.streamfile, "r") as file:
                lines = file.readlines()
                for line in lines:
                    nline = line.strip().split(":", 1)
                    self.streams[nline[0]] = nline[1].split("#")[0]
        except:
            self.chooseStreamfile()
            self.refreshstreams()
            return

        self.list.clear()

        for i, n in enumerate(self.streams):
            self.list.addItem(n)
            if n == current:
                self.list.setCurrentRow(i)
        if not self.list.currentItem():
            self.list.setCurrentRow(0)

    def changeEvent(self, event):

        # This minimizes the program to tray when Minimize button pressed

        if event.type() == QEvent.WindowStateChange:
            if self.windowState() & Qt.WindowMinimized:
                print(QSystemTrayIcon.isSystemTrayAvailable())
                if QSystemTrayIcon.isSystemTrayAvailable(
                ) and self.isActiveWindow():
                    event.ignore()
                    self.tray.show()
                    self.hide()
                    self.listener = keyboard.Listener(
                        on_release=self.on_release)
                    self.listener.start()

    def closeEvent(self, event):
        file = open("data", "w+")
        info = str(self.volume) + "\n" + str(self.size().width()) + " " + str(self.size().height()) + "\n" +\
               str(self.pos().x()) + " " + str(self.pos().y()) + "\n"
        if (self.isMaximized()):
            info += "true"
        else:
            info += "false"
        info += "\n"
        info += self.streamfile + "\n"
        file.write(info)
        file.close()

    def readInfo(self):
        try:
            with open("data", "r", encoding="utf-8") as file:
                info = file.readlines()
                return info
        except:
            with open("data", "w", encoding="utf-8") as file:
                file.write("")
                return ""

    def keyReleaseEvent(self, event):

        # This is for media controls when radio is opened

        key = event.key()
        if key == Qt.Key_MediaPlay or key == Qt.Key_MediaTogglePlayPause or \
                key == Qt.Key_MediaPause:
            self.control()
        elif key == Qt.Key_MediaNext:
            self.next()
        elif key == Qt.Key_MediaPrevious:
            self.previous()

    def call(self, reason):
        # This is caled when tray icon is pressed
        if reason == QSystemTrayIcon.ActivationReason.Trigger:
            self.show()
            self.setFocus()
            self.listener.stop()
            del self.listener
            self.tray.hide()
            self.setWindowState(Qt.WindowActive)
        elif reason == QSystemTrayIcon.ActivationReason.Context:
            self.tray.contextMenu().show()
        elif reason == QSystemTrayIcon.ActivationReason.MiddleClick:
            print("Middle click on tray icon")
        else:
            print("Unknown reason")

    def on_release(self, key):
        # This is for media controls when program in tray.
        try:
            if key == keyboard.Key.media_play_pause:  # might need a different key
                self.control()
            elif keyboard.Key.media_next == key:  # might need a different key
                self.next()
            elif keyboard.Key.media_previous == key:  # might need a different key
                self.previous()
        except AttributeError as e:
            print(e)
Exemple #20
0
def start(_exit: bool = False) -> None:
    show_ui = True
    if "-h" in sys.argv or "--help" in sys.argv:
        print(f"Usage: {os.path.basename(sys.argv[0])}")
        print("Flags:")
        print("  -h, --help\tShow this message")
        print("  -n, --no-ui\tRun the program without showing a UI")
        return
    elif "-n" in sys.argv or "--no-ui" in sys.argv:
        show_ui = False

    app = QApplication(sys.argv)

    logo = QIcon(LOGO)
    main_window = MainWindow()
    ui = main_window.ui
    main_window.setWindowIcon(logo)
    tray = QSystemTrayIcon(logo, app)
    tray.activated.connect(main_window.systray_clicked)

    menu = QMenu()
    action_dim = QAction("Dim display (toggle)")
    action_dim.triggered.connect(dim_all_displays)
    action_configure = QAction("Configure...")
    action_configure.triggered.connect(main_window.bring_to_top)
    menu.addAction(action_dim)
    menu.addAction(action_configure)
    menu.addSeparator()
    action_exit = QAction("Exit")
    action_exit.triggered.connect(app.exit)
    menu.addAction(action_exit)

    tray.setContextMenu(menu)

    ui.text.textChanged.connect(partial(queue_text_change, ui))
    ui.font_Size.valueChanged.connect(partial(update_font_size, ui))
    ui.command.textChanged.connect(partial(update_button_command, ui))
    ui.keys.textChanged.connect(partial(update_button_keys, ui))
    ui.write.textChanged.connect(partial(update_button_write, ui))
    ui.change_brightness.valueChanged.connect(
        partial(update_change_brightness, ui))
    ui.switch_page.valueChanged.connect(partial(update_switch_page, ui))
    ui.imageButton.clicked.connect(partial(select_image, main_window))
    ui.removeButton.clicked.connect(partial(remove_image, main_window))
    ui.settingsButton.clicked.connect(partial(show_settings, main_window))

    ui.font_Color.addItem("white")
    ui.font_Color.addItem("black")
    ui.font_Color.addItem("blue")
    ui.font_Color.addItem("red")
    ui.font_Color.addItem("green")
    ui.font_Color.addItem("purple")
    ui.font_Color.addItem("cyan")
    ui.font_Color.addItem("magenta")
    ui.font_Color.currentTextChanged.connect(partial(update_font_color, ui))

    ui.selected_font.addItem("Goblin_One")
    ui.selected_font.addItem("Open_Sans")
    ui.selected_font.addItem("Roboto")
    ui.selected_font.addItem("Lobster")
    ui.selected_font.addItem("Anton")
    ui.selected_font.addItem("Pacifico")
    ui.selected_font.currentTextChanged.connect(
        partial(update_selected_font, ui))

    ui.text_Align.addItem("left")
    ui.text_Align.addItem("center")
    ui.text_Align.addItem("right")
    ui.text_Align.currentTextChanged.connect(partial(update_text_align, ui))

    api.streamdesk_keys.key_pressed.connect(handle_keypress)

    items = api.open_decks().items()
    if len(items) == 0:
        print("Waiting for Stream Deck(s)...")
        while len(items) == 0:
            time.sleep(3)
            items = api.open_decks().items()

    for deck_id, deck in items:
        ui.device_list.addItem(f"{deck['type']} - {deck_id}", userData=deck_id)
        ui.target_device.addItem(deck_id)
        dimmers[deck_id] = Dimmer(
            api.get_display_timeout(deck_id),
            api.get_brightness(deck_id),
            partial(change_brightness, deck_id),
        )
        dimmers[deck_id].reset()

    build_device(ui)
    ui.device_list.currentIndexChanged.connect(partial(build_device, ui))

    ui.target_device.currentTextChanged.connect(
        partial(update_target_device, ui))

    ui.pages.currentChanged.connect(partial(change_page, ui))

    ui.actionExport.triggered.connect(partial(export_config, main_window))
    ui.actionImport.triggered.connect(partial(import_config, main_window))

    ui.actionCut.triggered.connect(partial(cut_button, main_window))
    ui.actionCopy.triggered.connect(partial(copy_button, main_window))

    ui.actionCut.setShortcuts([QKeySequence.Cut, QKeySequence("Shift+Del")])
    ui.actionCopy.setShortcuts(
        [QKeySequence.Copy, QKeySequence("Ctrl+Insert")])
    ui.actionPaste.setShortcuts(
        [QKeySequence.Paste, QKeySequence("Shift+Insert")])
    ui.actionDelete.setShortcuts([QKeySequence.Delete])

    ui.actionPaste.triggered.connect(partial(paste_button, main_window))
    ui.actionDelete.triggered.connect(partial(delete_button, main_window))
    ui.actionMultiPaste.triggered.connect(
        partial(multi_paste_Button, main_window))

    ui.actionExit.triggered.connect(app.exit)

    timer = QTimer()
    timer.timeout.connect(partial(sync, ui))
    timer.start(1000)

    api.render()
    tray.show()

    if show_ui:
        main_window.show()

    if _exit:
        return
    else:
        app.exec_()
        api.close_decks()
        sys.exit()
Exemple #21
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.setWindowTitle("ctpbee桌面端")
        # self.setWindowFlag(Qt.FramelessWindowHint)  # 去边框 可能会导致闪屏异常
        self.setStyleSheet(qss)
        self.animation_show()
        #
        G.mainwindow = self
        self.exit_ = False
        self._page_history = []
        self._page_maxsize = 10
        self.job = Job()
        self.kline_job = KInterfaceObject()
        self.record_work = RecordWorker()
        self.bee_ext = None
        self.tray_init()
        self.shortcut_init()
        ##
        self.status_msg = QLabel("实时信息")
        self.market_msg = QLabel("最新行情")
        self.statusbar.addPermanentWidget(self.status_msg, stretch=5)
        self.statusbar.addPermanentWidget(self.market_msg, stretch=5)
        # btn
        self.pre_page_btn.clicked.connect(self.pre_page_slot)
        self.home_btn.clicked.connect(self.home_handle)
        self.market_btn.clicked.connect(self.market_handle)
        self.order_btn.clicked.connect(self.order_handle)
        self.strategy_btn.clicked.connect(self.strategy_handle)
        self.setting_btn.clicked.connect(self.config_handle)
        self.log_btn.clicked.connect(self.log_handle)
        self.order_btn.clicked.connect(self.order_handle)
        self.backtrack_btn.clicked.connect(self.backtrack_handle)
        self.kline_btn.clicked.connect(self.kline_handle)
        #
        # self.menuBar.triggered.connect(self.menu_triggered)
        # widgets
        self.map_ = []
        self.home_widget = None
        self.strategy_widget = None
        self.account_widget = None
        self.market_widget = None
        self.order_widget = None
        self.kline_widget = None
        self.log_dialog = None
        self.cfg_dialog = None
        self.backtrack_widget = None

    def sign_in_success(self):
        self.bee_ext = CtpbeeApi('default_setting', current_app)
        self.bee_ext.map[EVENT_ACCOUNT] = self.on_account
        self.bee_ext.map[EVENT_CONTRACT] = self.on_contract
        self.bee_ext.map[EVENT_BAR] = self.on_bar
        self.bee_ext.map[EVENT_ORDER] = self.on_order
        self.bee_ext.map[EVENT_POSITION] = self.on_position
        self.bee_ext.map[EVENT_TICK] = self.on_tick
        self.bee_ext.map[EVENT_SHARED] = self.on_shared
        self.bee_ext.map[EVENT_TRADE] = self.on_trade
        self.bee_ext.map[EVENT_TIMER] = self.on_realtime
        #
        contracts = {
            contract.local_symbol: contract.name
            for contract in self.bee_ext.app.recorder.get_all_contracts()
        }
        G.all_contracts = contracts
        self.home_handle()

    def menu_triggered(self, q):
        q = q.text()
        if q == "退出应用":
            self.quit()

    def animation_show(self):
        self.animation = QPropertyAnimation(self, b'windowOpacity')
        self.animation.stop()
        self.animation.setDuration(500)
        self.animation.setStartValue(0)
        self.animation.setEndValue(1)
        self.animation.start()

    @property
    def page_history(self):
        return self._page_history

    @page_history.setter
    def page_history(self, val):
        if len(self._page_history) == self._page_maxsize:  # 达到最大容量
            self._page_history.pop(0)  # 弹出第一项
        self._page_history.append(val)

    def pre_page_slot(self):
        try:
            i = self.page_history.pop()
            while i == self.stackedWidget.currentIndex():
                i = self.page_history.pop()
            self.stackedWidget.setCurrentIndex(i)
        except IndexError:
            TipDialog("到底啦~")

    def shortcut_init(self):
        sc = G.config.SHORTCUT
        for name, sho in sc.items():
            if sho == '--':
                continue
            temp = QShortcut(QKeySequence(self.tr(sho)), self)
            temp.activated.connect(getattr(self, f"{name}_handle"))
            setattr(self, f"{name}_sc", temp)

    def update_shortcut(self):
        sc = G.config.SHORTCUT
        for name, sho in sc.items():
            getattr(self, f"{name}_sc").setKey(QKeySequence(self.tr(sho)))

    def page_map(self, w):
        name = w.__class__.__name__
        if name not in self.map_:
            self.map_.append(name)
        i = self.map_.index(name)
        self.page_history = i
        return i

    def home_handle(self):
        if self.home_widget is None:
            self.home_widget = HomeWidget(self)
            self.stackedWidget.addWidget(self.home_widget)
        self.stackedWidget.setCurrentIndex(self.page_map(self.home_widget))

    def strategy_handle(self):
        if self.strategy_widget is None:
            self.strategy_widget = StrategyWidget(self)
            self.stackedWidget.addWidget(self.strategy_widget)
        self.stackedWidget.setCurrentIndex(self.page_map(self.strategy_widget))

    def backtrack_handle(self):
        if self.backtrack_widget is None:
            self.backtrack_widget = BacktrackWidget(self)
            self.stackedWidget.addWidget(self.backtrack_widget)
        self.stackedWidget.setCurrentIndex(self.page_map(
            self.backtrack_widget))

    def market_handle(self):
        if self.market_widget is None:
            self.market_widget = MarketWidget(self)
            self.stackedWidget.addWidget(self.market_widget)
        self.stackedWidget.setCurrentIndex(self.page_map(self.market_widget))
        G.current_page = "market"

    def kline_handle(self):
        if self.kline_widget is None:
            self.kline_widget = KlineWidget(self)
            self.stackedWidget.addWidget(self.kline_widget)
        self.stackedWidget.setCurrentIndex(self.page_map(self.kline_widget))
        if G.choice_local_symbol:
            self.kline_widget.symbol_list.setCurrentText(G.choice_local_symbol)
        self.kline_widget.k_line_reload()

    def order_handle(self):
        if self.order_widget is None:
            self.order_widget = OrderWidget(self)
        self.order_widget.show()
        self.order_widget.raise_()

    def config_handle(self):
        if self.cfg_dialog is None:
            self.cfg_dialog = ConfigDialog(self)
        self.cfg_dialog.show()
        self.cfg_dialog.raise_()

    def log_handle(self):
        if self.log_dialog is None:
            self.log_dialog = LogDialog(self)
        self.log_dialog.show()
        self.log_dialog.raise_()

    def on_account(self, ext, account: AccountData) -> None:
        account = account._to_dict()
        G.account = account
        self.job.account_signal.emit(account)

    def on_contract(self, ext, contract: ContractData):
        pass

    def on_bar(self, ext, bar: BarData) -> None:
        """ vue kline"""
        # timestamp = round(bar.datetime.timestamp() * 1000)
        # info = [timestamp, bar.open_price, bar.high_price, bar.low_price,
        #         bar.close_price, bar.volume]
        """ echarts kline """
        timestamp = bar.datetime.strftime("%Y/%m/%d %H:%M:%S")
        info = [
            timestamp, bar.open_price, bar.close_price, bar.low_price,
            bar.high_price, bar.volume
        ]
        #
        if bar.local_symbol == G.choice_local_symbol:
            data = {bar.local_symbol: info}
            self.kline_job.qt_to_js.emit(json.dumps(data))
        # 存入文件
        self.record_work.record_sig.emit(bar.local_symbol, info)

    def on_order(self, ext, order: OrderData) -> None:
        active_orders = []
        for order1 in self.bee_ext.app.recorder.get_all_active_orders():
            o1 = order1._to_dict()
            active_orders.append(o1)
        self.job.order_activate_signal.emit(active_orders)

        orders = []
        for order2 in self.bee_ext.app.recorder.get_all_orders():
            o2 = order2._to_dict()
            orders.append(o2)
        self.job.order_order_signal.emit(orders)

    def on_realtime(*args):
        self = args[0]
        all_positions = self.bee_ext.app.recorder.get_all_positions()
        self.job.order_position_signal.emit(all_positions)

    def on_position(self, ext, position: PositionData) -> None:
        pass

    def on_tick(self, ext, tick: TickData) -> None:
        self.market_msg.setText(f"最新行情:{tick.name}   {tick.last_price}")
        tick = tick._to_dict()
        local_symbol = tick['local_symbol']
        G.ticks[local_symbol] = tick
        if G.current_page == "market":
            self.job.market_signal.emit(tick)
        self.job.kline_tick_signal.emit(tick)

    def on_shared(self, ext, shared: SharedData) -> None:
        pass

    def on_trade(self, ext, trade: TradeData) -> None:
        trades = []
        for trade in self.bee_ext.app.recorder.get_all_trades():
            t = trade._to_dict()
            trades.append(t)
        self.job.order_trade_signal.emit(trades)

    def on_init(self, ext, init):
        pass

    def tray_init(self):
        icon = QIcon(":menu/images/bee_temp_grey.png")
        menu = QMenu()
        openAction = menu.addAction("🍯 界面")
        settingAction = menu.addAction("⚙ 设置")
        exitAction = menu.addAction("❎ 退出")
        settingAction.triggered.connect(self.config_handle)
        openAction.triggered.connect(self.show)
        exitAction.triggered.connect(self.quit)
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(icon)
        self.tray.setContextMenu(menu)
        self.tray.activated.connect(self.iconActivated)
        self.tray.show()
        self.tray.setToolTip("ctpbee桌面端")

    def quit(self):
        self.exit_ = True
        self.close()

    def iconActivated(self, reason):
        if reason is QSystemTrayIcon.Trigger:
            self.show()
            self.raise_()

    def closeEvent(self, event: QCloseEvent):
        if self.exit_:
            G.pool_done = True
            self.tray.deleteLater()
            try:
                for k, v in current_app.extensions.items():
                    current_app.suspend_extension(k)
                current_app.release()
            except:
                pass
            if self.cfg_dialog:
                self.cfg_dialog.close()
            if self.log_dialog:
                self.log_dialog.close()
            if self.order_widget:
                self.order_widget.close()
            event.accept()
        else:
            self.tray.showMessage("ctpbee", "以最小化隐藏在托盘", msecs=1)
            self.hide()
            event.ignore()
Exemple #22
0
class SystemTrayIcon(QObject):
    clicked = pyqtSignal()
    double_clicked = pyqtSignal()

    def __init__(self, parent, menu, is_logging=False):
        QObject.__init__(self)

        def getIcon(name):
            return QIcon(':/images/tray_icons/' + name + '.png')

        self._icons = {
            STATUS_INIT: getIcon("disconnected") if is_logging \
                else getIcon("sync"),
            STATUS_DISCONNECTED: getIcon("disconnected"),
            STATUS_WAIT: getIcon("default"),
            STATUS_PAUSE: getIcon("pause"),
            STATUS_IN_WORK: getIcon("sync"),
            STATUS_INDEXING: getIcon("sync"),
        }
        self._statuses = {
            STATUS_INIT: tr("Pvtbox"),
            STATUS_DISCONNECTED: tr("Pvtbox connecting..."),
            STATUS_WAIT: tr("Pvtbox"),
            STATUS_PAUSE: tr("Pvtbox paused"),
            STATUS_IN_WORK: tr("Pvtbox syncing..."),
            STATUS_INDEXING: tr("Pvtbox indexing...")
        }
        self._tray = QSystemTrayIcon(self._icons[STATUS_INIT], parent)
        self.set_tool_tip(self._statuses[STATUS_INIT])
        self._tray.setContextMenu(menu)
        menu.aboutToShow.connect(self.clicked.emit)

        self._tray.activated.connect(self._on_activated)
        self._tray.installEventFilter(self)
        self._tray.setVisible(True)
        self._tray.show()

        self._tray_show_timer = QTimer(self)
        self._tray_show_timer.setInterval(3000)
        self._tray_show_timer.setSingleShot(False)
        self._tray_show_timer.timeout.connect(self.show)

    def eventFilter(self, obj, ev):
        if ev.type() == QEvent.ToolTip:
            self.clicked.emit()
        return False

    def _on_activated(self, reason):
        '''
        Slot for system tray icon activated signal.
        See http://doc.qt.io/qt-5/qsystemtrayicon.html

        @param reason Tray activation reason
        '''

        if reason == QSystemTrayIcon.Trigger:
            # This is usually when left mouse button clicked on tray icon
            self.clicked.emit()
        elif reason == QSystemTrayIcon.DoubleClick:
            self.double_clicked.emit()

    @property
    def menu(self):
        return self._tray.contextMenu()

    def set_tool_tip(self, tip):
        self._tray.setToolTip(tip)

    def show_tray_notification(self, text, title=""):
        if not title:
            title = tr('Pvtbox')
        # Convert strings to unicode
        if type(text) in (str, str):
            text = ensure_unicode(text)
        if type(title) in (str, str):
            title = ensure_unicode(title)

        logger.info("show_tray_notification: %s, title: %s", text, title)
        if self._tray.supportsMessages():
            self._tray.showMessage(title, text)
        else:
            logger.warning("tray does not supports messages")

    def request_to_user(
            self, dialog_id, text, buttons=("Yes", "No"), title="",
            close_button_index=-1, close_button_off=False, parent=None,
            on_clicked_cb=None,
            details=''):

        msg_box = QMessageBox(parent)
        # msg_box = QMessageBox()
        if not title:
            title = tr('Pvtbox')
        msg_box.setWindowTitle(title)
        msg_box.setText(str(text))

        pvtboxIcon = QIcon(':/images/icon.png')
        msg_box.setWindowIcon(pvtboxIcon)

        if details:
            msg_box.setDetailedText(details)

        if close_button_off:
            if get_platform() == 'Darwin':
                msg_box.setWindowFlags(Qt.Tool)
            else:
                msg_box.setWindowFlags(Qt.Dialog |
                                       Qt.CustomizeWindowHint |
                                       Qt.WindowTitleHint)
        msg_box.setAttribute(Qt.WA_MacFrameworkScaled)
        msg_box.setModal(True)

        buttons = list(buttons)
        for button in buttons:
            msg_box.addButton(button, QMessageBox.ActionRole)
        msg_box.show()
        msg_box.raise_()
        msg_box.exec_()
        try:
            button_index = buttons.index(msg_box.clickedButton().text())
        except (ValueError, AttributeError):
            button_index = -1
           # message box was closed with close button
            if len(buttons) == 1 and close_button_index == -1:
                # if only one button then call callback
                close_button_index = 0

            if len(buttons) > close_button_index >= 0:
                button_index = close_button_index

        if button_index >= 0 and callable(on_clicked_cb):
            on_clicked_cb(dialog_id, button_index)

    def update_status_icon(self, new_status, new_substatus):
        icon = self._icons[STATUS_DISCONNECTED] if new_status == STATUS_INIT \
            else self._icons[new_status]
        self._tray.setIcon(icon)
        tool_tip = self._statuses[new_status]
        if new_status == STATUS_IN_WORK and new_substatus == SUBSTATUS_SHARE:
            tool_tip = tr("Pvtbox downloading share...")
        self.set_tool_tip(tool_tip)
        self.show()

    def show(self):
        self._tray.setVisible(True)
        self._tray.show()

    def hide(self):
        if self._tray_show_timer.isActive():
            self._tray_show_timer.stop()
        self._tray.hide()

    def __del__(self):
        self._tray.removeEventFilter(self)
        self._tray.activated.disconnect()
        self.hide()
Exemple #23
0
class MainWindow(Ui_Form):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.appname = "poliBeePsync"
        self.settings_fname = 'pbs-settings.ini'
        self.data_fname = 'pbs.data'
        self.setupUi(self)
        self.w = QWidget()

        self.status_signal = MySignal()
        self.status_signal.sig.connect(self.update_status_bar)

        self.logging_signal = MySignal()
        self.logging_signal.sig.connect(self.myStream_message)
        logging_console_hdl = SignalLoggingHandler(self.logging_signal)
        logger.addHandler(logging_console_hdl)
        commonlogger.addHandler(logging_console_hdl)

        self.about_text()
        self.timer = QTimer(self)

        # settings_path is a string containing the path to settings
        self.settings_path = None
        # settings is a dictionary of settings
        self.settings = None
        # load_settings() sets settings_path and settings
        self.load_settings()
        self.load_data()

        self.timer.timeout.connect(self.syncfiles)
        self.timer.start(1000 * 60 * int(self.settings['UpdateEvery']))

        self.loginthread = LoginThread(self.user, self)
        self.loginthread.signal_error.sig.connect(self.update_status_bar)
        self.loginthread.signal_ok.sig.connect(self.update_status_bar)

        self.refreshcoursesthread = RefreshCoursesThread(self.user, self)
        self.refreshcoursesthread.dumpuser.sig.connect(self.dumpUser)
        self.refreshcoursesthread.newcourses.sig.connect(self.addtocoursesview)
        self.refreshcoursesthread.newcourses.sig.connect(self.syncnewcourses)
        self.refreshcoursesthread.removable.sig.connect(self.rmfromcoursesview)

        self.downloadthread = DownloadThread(self.user,
                                             self.settings['RootFolder'],
                                             self)
        self.downloadthread.dumpuser.sig.connect(self.dumpUser)
        self.downloadthread.download_signal.connect(
            self.update_course_download)
        self.downloadthread.initial_sizes.connect(self.setinizialsizes)
        self.downloadthread.date_signal.connect(self.update_file_localtime)

        self._window.userCode.setText(str(self.user.username))
        self._window.userCode.editingFinished.connect(self.setusercode)
        self._window.password.setText(self.user.password)
        self._window.password.editingFinished.connect(self.setpassword)
        self._window.trylogin.clicked.connect(self.testlogin)

        self._window.courses_model = CoursesListModel(self.user.
                                                      available_courses)
        self._window.coursesView.setModel(self._window.courses_model)
        self._resizeview()
        self._window.refreshCourses.clicked.connect(self.refreshcourses)

        self._window.syncNow.clicked.connect(self.syncfiles)

        if self.settings['SyncNewCourses'] == str(True):
            self._window.sync_new = Qt.Checked
        else:
            self._window.sync_new = Qt.Unchecked

        self._window.rootfolder.setText(self.settings['RootFolder'])
        self._window.rootfolder.textChanged.connect(self.rootfolderslot)

        self._window.addSyncNewCourses.setCheckState(self._window.sync_new)
        self._window.addSyncNewCourses.stateChanged.connect(self.syncnewslot)

        self._window.timerMinutes.setValue(int(self.settings['UpdateEvery']))
        self._window.timerMinutes.valueChanged.connect(self.updateminuteslot)

        self._window.changeRootFolder.clicked.connect(self.chooserootdir)
        self._window.version_label.setText("Current version: {}."
                                           .format(__version__))
        self._window.check_version.clicked.connect(self.checknewversion)

        self.trayIconMenu = QMenu()
        self.trayIcon = QSystemTrayIcon(self.icon, self.w)
        self.trayIcon.activated.connect(self._activate_traymenu)
        self.createTray()

    @Slot()
    def _resizeview(self, **kwargs):
        self._window.coursesView.setColumnWidth(3, 160)
        self._window.coursesView.resizeColumnToContents(1)
        self._window.coursesView.setColumnWidth(0, 320)

    def checknewversion(self):
        rawdata = requests.get('https://pypi.python.org/pypi/'
                               'poliBeePsync/json')
        latest = json.loads(rawdata.text)['info']['version']
        self._window.version_label.setTextFormat(Qt.RichText)
        self._window.version_label.setOpenExternalLinks(True)
        self._window.version_label.setLocale(QLocale(QLocale.English,
                                                     QLocale.UnitedStates))
        self._window.version_label.setScaledContents(True)
        self._window.version_label.setWordWrap(True)
        if latest != __version__:
            newtext = """<p>Current version: {}.<br>
Latest version: {}. </p>
<p>Visit <a
href='https://jacotsu.github.io/polibeepsync/dirhtml/index.html\
        #how-to-install-upgrade-remove'>here</a> to find out how to upgrade.
""".format(__version__, latest)
        else:
            newtext = "Current version: {} up-to-date.".format(__version__)
        self._window.version_label.setText(newtext)

    def _update_time(self, folder, file, path_list):
        logger.debug(f'inside {folder.name}')
        for path in path_list:
            logger.debug(f'namegoto: {path}')
            folder_dict = {'name': path}
            fakefolder = Folder(folder_dict)
            logger.debug(f'contained folders:  {folder.folders}')
            ind = folder.folders.index(fakefolder)
            goto = folder.folders[ind]
            self._update_time(goto, file, path_list)

        if file in folder.files:
            ind = folder.files.index(file)
            thisfile = folder.files[ind]
            thisfile.local_creation_time = file.local_creation_time

    @Slot(tuple)
    def update_file_localtime(self, data, **kwargs):
        course, coursefile, path = data
        rootpath = os.path.join(self.settings['RootFolder'],
                                course.save_folder_name)
        if path.startswith(rootpath):
            partial = path[len(rootpath):]
        path_list = filter(None, partial.split(os.path.sep))
        self._update_time(course.documents, coursefile, path_list)

    @Slot(Course)
    def update_course_download(self, course, **kwargs):
        if course in self.user.available_courses:
            updating = self.user.available_courses[course.name]
            updating.downloaded_size = course.downloaded_size
            row = self._window.courses_model.courses.index(updating)
            where = self._window.courses_model.index(row, 3)
            self._window.courses_model.dataChanged.emit(where, where)

    @Slot(Course)
    def setinizialsizes(self, course, **kwargs):
        if course in self.user.available_courses:
            updating = self.user.available_courses[course.name]
            updating.downloaded_size = course.downloaded_size
            updating.total_file_size = course.total_file_size
            row = self._window.courses_model.courses.index(updating)
            where = self._window.courses_model.index(row, 3)
            self._window.courses_model.dataChanged.emit(where, where)
            self.dumpUser()

    @Slot(list)
    def syncnewcourses(self, newlist):
        if self.settings['SyncNewCourses'] == 'True':
            for elem in newlist:
                elem.sync = True

    def load_settings(self):
        for path in [user_config_dir(self.appname),
                     user_data_dir(self.appname)]:
            try:
                os.makedirs(path, exist_ok=True)
            except OSError:
                logger.critical('OSError while calling os.makedirs.',
                                exc_info=True)
                logger.critical(f"I couldn't create {path}.\nStart"
                                " poliBeePsync with --debug "
                                "error to get more details.")
        self.settings_path = os.path.join(user_config_dir(self.appname),
                                          self.settings_fname)
        defaults = {
            'UpdateEvery': '60',
            'RootFolder': os.path.join(os.path.expanduser('~'), self.appname),
            'SyncNewCourses': 'False'
        }
        self.settings = filesettings.settingsFromFile(self.settings_path,
                                                      defaults)

    def load_data(self):
        try:
            with open(os.path.join(user_data_dir(self.appname),
                                   self.data_fname), 'rb') as f:
                self.user = pickle.load(f)
                self.user.password = keyring\
                        .get_password('beep.metid.polimi.it',
                                      self.user.username)
                logger.info("Data has been loaded successfully.")
        except (EOFError, pickle.PickleError):
            logger.error('Settings corrupted', exc_info=True)
            self.user = User('', '')

        except FileNotFoundError:
            logger.error('Settings file not found.')
            self.user = User('', '')
            logger.error("I couldn't find data in the"
                         " predefined directory. Ignore this"
                         "message if you're using poliBeePsync"
                         " for the first time.")

    @Slot(str)
    def update_status_bar(self, status):
        self._window.statusbar.showMessage(status)

    @Slot(int)
    def syncnewslot(self, state):
        if state == 2:
            self.settings['SyncNewCourses'] = 'True'
        else:
            self.settings['SyncNewCourses'] = 'False'
        filesettings.settingsToFile(self.settings, self.settings_path)

    @Slot(int)
    def updateminuteslot(self, minutes):
        self.settings['UpdateEvery'] = str(minutes)
        filesettings.settingsToFile(self.settings, self.settings_path)
        self.timer.start(1000 * 60 * int(self.settings['UpdateEvery']))

    @Slot(str)
    def rootfolderslot(self, path):
        self.settings['RootFolder'] = path
        filesettings.settingsToFile(self.settings, self.settings_path)

    @Slot()
    def chooserootdir(self):
        currentdir = self.settings['RootFolder']
        flags = QFileDialog.DontResolveSymlinks | QFileDialog.ShowDirsOnly
        newroot = QFileDialog.getExistingDirectory(None,
                                                   "Open Directory",
                                                   currentdir, flags)
        if newroot != "" and str(newroot) != currentdir:
            self.settings['RootFolder'] = str(newroot)
            filesettings.settingsToFile(self.settings, self.settings_path)
            self._window.rootfolder.setText(newroot)
            # we delete the already present downloadthread and recreate it
            # because otherwise it uses the old download folder. I don't know
            # if there's a cleaner approach
            del self.downloadthread
            self.downloadthread = DownloadThread(self.user,
                                                 self.settings['RootFolder'],
                                                 self)
            self.downloadthread.dumpuser.sig.connect(self.dumpUser)
            self.dumpUser()

    @Slot()
    def setusercode(self):
        newcode = self._window.userCode.text()
        try:
            if len(newcode) == 8:
                self.user.username = newcode
                logger.info(f'User code changed to {newcode}.')
                keyring.set_password('beep.metid.polimi.it',
                                     self.user.username,
                                     self.user.password)
        except OSError:
            logger.critical("I couldn't save data to disk. Run"
                            " poliBeePsync with option --debug"
                            " error to get more details.")
            logger.error('OSError raised while trying to write the User'
                         'instance to disk.', exc_info=True)

    @Slot()
    def setpassword(self):
        newpass = self._window.password.text()
        self.user.password = newpass
        try:
            keyring.set_password('beep.metid.polimi.it',
                                 self.user.username,
                                 self.user.password)
            logger.info("Password changed.")
        except OSError:
            logger.critical("I couldn't save data to disk. Run"
                            " poliBeePsync with option --debug"
                            " error to get more details.")
            logger.error('OSError raised while trying to write the User'
                         'instance to disk.', exc_info=True)

    @Slot()
    def testlogin(self):
        if not self.loginthread.isRunning():
            self.loginthread.exiting = False
            self.loginthread.start()
            self.status_signal.sig.emit("Logging in, please wait.")

    @Slot(list)
    def addtocoursesview(self, addlist):
        for elem in addlist:
            self._window.courses_model.insertRows(0, 1, elem)

    @Slot(list)
    def rmfromcoursesview(self, removelist):
        for elem in removelist:
            index = self._window.courses_model.courses.index(elem)
            self._window.courses_model.removeRows(index, 1)

    @Slot()
    def dumpUser(self):
        # we don't use the message...
        with open(os.path.join(user_data_dir(self.appname),
                               self.data_fname), 'wb') as f:
            tmp_pw = self.user.password
            self.user.password = ''
            pickle.dump(self.user, f)
            self.user.password = tmp_pw

    @Slot()
    def refreshcourses(self):
        self.status_signal.sig.emit('Searching for online updates...'
                                'this may take a while.')
        if not self.loginthread.isRunning():
            self.loginthread.exiting = False
            self.loginthread.signal_ok.sig.connect(self.do_refreshcourses)
            self.loginthread.start()

    def do_refreshcourses(self):
        self.loginthread.signal_ok.sig.disconnect(self.do_refreshcourses)
        if not self.refreshcoursesthread.isRunning():
            self.refreshcoursesthread.start()

    @Slot()
    def syncfiles(self):
        # we delete the already present downloadthread and recreate it
        # because otherwise it uses the old download folder. I don't know
        # if there's a cleaner approach
        del self.downloadthread
        self.downloadthread = DownloadThread(self.user,
                                             self.settings['RootFolder'],
                                             self)
        self.downloadthread.dumpuser.sig.connect(self.dumpUser)

        self.refreshcoursesthread.finished.connect(self.do_syncfiles)
        self.refreshcourses()

    @Slot()
    def do_syncfiles(self):
        self.refreshcoursesthread.finished.disconnect(self.do_syncfiles)
        self.status_signal.sig.emit('Started syncing.')
        self.downloadthread.start()

    @Slot(str)
    def myStream_message(self, message):
        self._window.status.moveCursor(QTextCursor.End)
        self._window.status.insertPlainText(message + "\n")

    def restore_window(self):
        self._window.setWindowState(self.windowState() & ~Qt.WindowMinimized |
                                Qt.WindowActive)
        self._window.show()

    def createTray(self):
        restoreAction = QAction("&Restore", self, triggered=self.restore_window)
        quitAction = QAction("&Quit", self, triggered=qApp.quit)
        self.trayIconMenu.addAction(restoreAction)
        self.trayIconMenu.addAction(quitAction)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.show()

    @Slot(str)
    def _activate_traymenu(self, reason):
        if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
            self.restore_window()
        else:
            self.trayIconMenu.activateWindow()
            self.trayIconMenu.popup(QCursor.pos())

    def closeEvent(self, event):
        self._window.hide()
        event.ignore()

    def about_text(self):
        self._window.label_3 = QLabel()
        self._window.label_3.setTextFormat(Qt.RichText)
        self._window.label_3.setOpenExternalLinks(True)
        self._window.label_3.setLocale(QLocale(QLocale.English,
                                               QLocale.UnitedStates))
        self._window.label_3.setScaledContents(True)
        self._window.label_3.setWordWrap(True)
        text = """
<html>
<head/>
<body>
  <p>poliBeePsync is a program written by Davide Olianas,
released under GNU GPLv3+.</p>
  <p>Feel free to contact me at <a
  href=\"mailto:[email protected]\">[email protected]</a> for
  suggestions and bug reports.</p>
  <p>More information is available on the
  <a href=\"http://www.davideolianas.com/polibeepsync\">
  <span style=\" text-decoration: underline; color:#0000ff;\">
  official website</span></a>.
  </p>
</body>
</html>
"""

        self._window.label_3.setText(QApplication.translate("Form", text,
                                                            None))
Exemple #24
0
class Chrono(QMainWindow):
    def __init__(self, parent=None):
        super(Chrono, self).__init__(parent)

        self.createMenus()
        self.createSystemTrayIcon()

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.tick)
        self.isRunning = False
        self.refresh_rate = 100  # ms

        self.progressBar = QProgressBar()
        self.progressBar.setValue(0)
        self.begin_time = self.end_time = 0

        self.label = QLabel(" ")
        self.button = QPushButton()
        self.button.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
        self.end_delay = self.begin_delay = 0

        bottomLayout = QHBoxLayout()
        bottomLayout.addWidget(self.progressBar)
        bottomLayout.addWidget(self.button)
        self.button.clicked.connect(self.pause)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.label)
        mainLayout.addLayout(bottomLayout)
        centralWidget = QWidget()
        centralWidget.setLayout(mainLayout)
        self.setCentralWidget(centralWidget)

        self.statusBar = QStatusBar(self)
        self.setStatusBar(self.statusBar)

        self.notification = self.notification_popup = self.notification_tray = self.notification_sound = True
        self.notification_soundfile = os.path.dirname(
            sys.argv[0]) + '/notification.mp3'  # os.path.dirname(__file__) +

        self.setWindowTitle(TITLE)
        self.resize(400, self.sizeHint().height())
        self.setFixedHeight(self.sizeHint().height())

    def createMenus(self):
        menus = QMenuBar()
        fileMenu = menus.addMenu("&Fichier")
        file_newMenu = fileMenu.addMenu(
            self.style().standardIcon(QStyle.SP_FileIcon), "Nouveau")
        file_newMenu.addAction("Date", self.createDateDialog, 'CTRL+D')
        file_newMenu.addAction("Durée", self.createDurationDialog, 'CTRL+N')
        fileMenu.addSeparator()
        fileMenu.addAction(self.style().standardIcon(QStyle.SP_BrowserStop),
                           "Quitter", sys.exit, 'CTRL+Q')

        optionMenu = menus.addMenu("&Options")
        optionMenu.addAction(
            self.style().standardIcon(QStyle.SP_MessageBoxInformation),
            "Évènements", self.createNotificationPopup, 'CTRL+O')
        optionMenu.addAction(
            QAction("Rester au premier plan",
                    optionMenu,
                    triggered=self.stayOnTop,
                    checkable=True))
        aideMenu = menus.addMenu("&Aide")
        aideMenu.addAction(
            self.style().standardIcon(QStyle.SP_DialogHelpButton),
            "À propos", lambda: QMessageBox.information(
                self, "À propos", TITLE + " " + str(VERSION)), 'CTRL+H')
        aideMenu.addSeparator()
        aideMenu.addAction(
            self.style().standardIcon(QStyle.SP_TitleBarMenuButton),
            "À propos de Qt", QApplication.aboutQt, 'CTRL+A')

        self.setMenuBar(menus)

    def createSystemTrayIcon(self):
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(QIcon(os.path.dirname(sys.argv[0]) +
                                '/icon.svg'))  # os.path.dirname(__file__) +
        self.tray.setToolTip(TITLE)
        self.tray.show()

        systemTrayMenu = QMenu()
        pauseAction = QAction(self.style().standardIcon(QStyle.SP_MediaPause),
                              "Pause / Reprendre", systemTrayMenu)
        pauseAction.triggered.connect(self.pause)

        systemTrayMenu.addAction(pauseAction)

        systemTrayMenu.addSeparator()
        systemTrayMenu.addAction(
            self.style().standardIcon(QStyle.SP_BrowserStop), "Quitter",
            sys.exit)
        self.tray.setContextMenu(systemTrayMenu)
        self.tray.activated.connect(self.show)

    def stayOnTop(self):
        self.setWindowFlags(self.windowFlags() ^ Qt.WindowStaysOnTopHint)
        # self.windowFlags() | Qt.CustomizeWindowHint | Qt.Window | Qt.WindowStaysOnTopHint | Qt.X11BypassWindowManagerHint)  # Qt.Dialog | Qt.WindowStaysOnTopHint | Qt.X11BypassWindowManagerHint)
        self.show()

    def createNotificationPopup(self):
        popup = QDialog(self)
        popup.setFixedSize(popup.sizeHint().height(), popup.sizeHint().width())
        popup.setWindowTitle("Évènements")
        innerLayout = QVBoxLayout()

        GroupBox = QGroupBox("Activer les notifications")
        GroupBox.setCheckable(True)
        GroupBox.setChecked(self.notification)

        checkBox_popup = QCheckBox("Afficher une popup")
        checkBox_notification = QCheckBox("Afficher une notification")
        checkBox_sound = QCheckBox("Jouer un son")

        if self.notification_popup:
            checkBox_popup.setCheckState(Qt.Checked)
        if self.notification_tray:
            checkBox_notification.setCheckState(Qt.Checked)
        if self.notification_sound:
            checkBox_sound.setCheckState(Qt.Checked)

        innerLayout.addWidget(checkBox_popup)
        innerLayout.addWidget(checkBox_notification)
        innerLayout.addWidget(checkBox_sound)
        innerLayout.addStretch(1)
        GroupBox.setLayout(innerLayout)

        button = QPushButton("Ok")
        button.clicked.connect(lambda: self.changeNotificationSettings(
            popup, GroupBox, checkBox_popup, checkBox_notification,
            checkBox_sound))

        outerLayout = QVBoxLayout()
        outerLayout.addWidget(GroupBox)
        outerLayout.addWidget(button)
        popup.setLayout(outerLayout)

        popup.exec_()

    def changeNotificationSettings(self, popup, GroupBox, checkBox_popup,
                                   checkBox_notification, checkBox_sound):
        self.notification, self.notification_popup, self.notification_tray, self.notification_sound = GroupBox.isChecked(
        ), checkBox_popup.isChecked(), checkBox_notification.isChecked(
        ), checkBox_sound.isChecked()
        if not any([
                self.notification_popup, self.notification_tray,
                self.notification_sound
        ]):
            self.notification = False
        popup.close()

    def createDateDialog(self):
        popup = QDialog(self)
        popup.setFixedSize(270, 60)
        popup.setWindowTitle("Nouvelle date")
        layout = QHBoxLayout()

        prefix = QLabel("Heure cible: ")
        layout.addWidget(prefix)

        qline = QTimeEdit()
        qline.setDisplayFormat("hh:mm:ss")
        qline.setTime(QTime.currentTime())
        layout.addWidget(qline)

        button = QPushButton("Ok")
        button.clicked.connect(lambda: self.createDate(popup,
                                                       qline.time().hour(),
                                                       qline.time().minute(),
                                                       qline.time().second()))

        layout.addWidget(button)

        popup.setLayout(layout)
        popup.exec_()

    def createDurationDialog(self):
        popup = QDialog(self)
        popup.setFixedSize(150, 150)
        popup.setWindowTitle("Nouvelle durée")
        layout = QVBoxLayout()

        hourLayout = QHBoxLayout()
        hourLabel = QLabel("Heures:")
        hourSpin = QSpinBox()
        hourLayout.addWidget(hourLabel)
        hourLayout.addWidget(hourSpin)

        minuteLayout = QHBoxLayout()
        minuteLabel = QLabel("Minutes:")
        minuteSpin = QSpinBox()
        minuteLayout.addWidget(minuteLabel)
        minuteLayout.addWidget(minuteSpin)

        secondLayout = QHBoxLayout()
        secondLabel = QLabel("Secondes:")
        secondSpin = QSpinBox()
        secondLayout.addWidget(secondLabel)
        secondLayout.addWidget(secondSpin)

        layout.addLayout(hourLayout)
        layout.addLayout(minuteLayout)
        layout.addLayout(secondLayout)

        button = QPushButton("Ok")
        button.clicked.connect(lambda: self.createDuration(
            popup, hourSpin.value(), minuteSpin.value(), secondSpin.value()))
        layout.addWidget(button)

        popup.setLayout(layout)
        popup.exec_()

    def createDuration(self, popup: QDialog, hours: int, minutes: int,
                       seconds: int):
        popup.close()

        self.begin_time = datetime.timestamp(datetime.now())
        self.end_time = self.begin_time + seconds + minutes * 60 + hours * 3600
        self.progressBar.setRange(0, 100)
        self.progressBar.setValue(0)

        self.isRunning = True
        self.timer.stop()
        self.timer.start(self.refresh_rate)

    def createDate(self, popup: QDialog, hours: int, minutes: int,
                   seconds: int):
        popup.close()

        self.begin_time = datetime.timestamp(datetime.now())

        now = datetime.now().time()
        target = time(hours, minutes, seconds)
        now_delta = timedelta(hours=now.hour,
                              minutes=now.minute,
                              seconds=now.second)
        target_delta = timedelta(hours=target.hour,
                                 minutes=target.minute,
                                 seconds=target.second)

        if target_delta == now_delta:
            self.end_time = self.begin_time + 60 * 60 * 24
        else:
            d = target_delta - now_delta
            self.end_time = self.begin_time + d.seconds

        self.progressBar.setRange(0, 100)
        self.progressBar.setValue(0)

        self.isRunning = True
        self.timer.stop()
        self.timer.start(self.refresh_rate)

    def tick(self):
        self.progressBar.setValue(
            100 * (datetime.timestamp(datetime.now()) - self.begin_time) /
            (self.end_time - self.begin_time))

        seconds = int(
            ceil(self.end_time - datetime.timestamp(datetime.now())) % 60)
        minutes = int(
            ceil(self.end_time - datetime.timestamp(datetime.now())) / 60 % 60)
        hours = int(
            ceil(self.end_time - datetime.timestamp(datetime.now())) / 3600)

        self.label.setText(f'{hours:02}:{minutes:02}:{seconds:02}')
        self.setWindowTitle(f'{TITLE} - {hours:02}:{minutes:02}:{seconds:02}')
        self.tray.setToolTip(f'{hours:02}:{minutes:02}:{seconds:02}')

        if datetime.timestamp(datetime.now()) >= self.end_time:
            self.isRunning = False
            self.timer.stop()
            self.progressBar.setRange(0, 0)
            self.show()
            self.notify()

    def notify(self):
        if not self.notification:
            return
        if self.notification_tray:
            self.tray.showMessage(
                "Finished", "Le décompte est terminé",
                self.style().standardIcon(QStyle.SP_MessageBoxInformation))
        if self.notification_sound:
            test = QMediaPlayer()
            test.setMedia(QUrl.fromLocalFile(self.notification_soundfile))
            test.play()
        if self.notification_popup:
            QMessageBox.information(self, "Finished",
                                    "Le décompte est terminé")

    def pause(self):
        if not self.isRunning:
            return
        self.progressBar.setDisabled(self.timer.isActive())
        if self.timer.isActive():
            self.end_delay = self.end_time - datetime.timestamp(datetime.now())
            self.begin_delay = datetime.timestamp(
                datetime.now()) - self.begin_time
            print(self.begin_time)
            print(self.end_time)
            print(self.end_delay)
            self.statusBar.showMessage("Pause")
            self.tray.setToolTip(self.tray.toolTip() + ' - Pause')
            self.timer.stop()
            self.button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        else:
            self.begin_time = datetime.timestamp(
                datetime.now()) - self.begin_delay
            self.end_time = datetime.timestamp(datetime.now()) + self.end_delay
            print(self.begin_time)
            print(self.end_time)
            self.statusBar.clearMessage()
            self.timer.start()
            self.button.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPause))

    # Override
    def closeEvent(self, event):
        self.hide()
        event.ignore()
Exemple #25
0
class MainWindow(QObject):
    def __init__(self, parent=None):
        """Main window, holding all user interface including.

        Args:
          parent: parent class of main window
        Returns:
          None
        Raises:
          None
        """
        super(MainWindow, self).__init__()

        self._window = None
        self._old_pos = None

        self._tray = QSystemTrayIcon(self._window)
        if self._tray.isSystemTrayAvailable():
            self._tray.setIcon(QIcon('./media/dekban.png'))
        else:
            self._tray = None

        self.setup_ui()

    @property
    def window(self):
        """The main window object"""
        return self._window

    def setup_ui(self):
        loader = QUiLoader()
        file = QFile('./main_window.ui')
        file.open(QFile.ReadOnly)
        self._window = loader.load(file)
        file.close()

        self._window.setWindowFlags(Qt.Window | Qt.FramelessWindowHint)
        self._window.installEventFilter(self)

        self.center()
        self._old_pos = self._window.pos()

        self.set_title()
        self.set_buttons()
        self.set_edits()
        self.set_icon_combo()
        self.set_tray()

        self._tray.show()

    def set_title(self):
        """Setup label"""
        self._window.title.setText('Welcome to PySide2 Tutorial')

        font = QFont("Arial", 20, QFont.Black)

        self._window.title.setFont(font)
        # set widget size (x, y, width, height)
        self._window.title.setGeometry(0, 0, 600, 30)
        # set alignment
        self._window.title.setAlignment(Qt.AlignBottom | Qt.AlignCenter)

    def set_buttons(self):
        """Setup buttons"""
        self._window.send_btn.setText('Send Msg')
        self._window.exit_btn.setText('Exit')

        self._window.send_btn.setIcon(QIcon('./media/import.svg'))

        self._window.send_btn.clicked.connect(self.send_message)
        self._window.exit_btn.clicked.connect(self.exit)

    def set_edits(self):
        """Setup line edit and text edit"""
        self._window.title_line.setPlaceholderText('Input Msg Title')
        self._window.msg_edit.setPlaceholderText('Input Msg')

    def set_icon_combo(self):
        """Setup options in icon select combobox."""
        self._window.icon_combo.addItem(QIcon('./media/font.png'), 'font')
        self._window.icon_combo.addItem(QIcon('./media/paint.png'), 'paint')
        self._window.icon_combo.addItem(QIcon('./media/dekban.png'), 'default')
        self._window.icon_combo.currentIndexChanged.connect(self.set_icon)

    def set_tray(self):
        menu = QMenu(self._window)
        action_show = menu.addAction("Show/Hide")
        action_show.triggered.connect(
            lambda: self._window.hide()
            if self._window.isVisible() else self._window.show())
        action_quit = menu.addAction("Quit")
        action_quit.triggered.connect(self._window.close)

        self._tray.setContextMenu(menu)

    def eventFilter(self, obj, event):
        if obj is self._window:
            if event.type() == QtCore.QEvent.MouseButtonPress:
                self.mouse_press_event(event)
            elif event.type() == QtCore.QEvent.MouseMove:
                self.mouse_move_event(event)
        return super(MainWindow, self).eventFilter(obj, event)

    def center(self):
        qr = self._window.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self._window.move(qr.topLeft())

    def mouse_press_event(self, event):
        self._old_pos = event.globalPos()

    def mouse_move_event(self, event):
        delta_x = int(event.globalPos().x()) - self._old_pos.x()
        delta_y = int(event.globalPos().y()) - self._old_pos.y()
        self._window.move(self._window.x() + delta_x,
                          self._window.y() + delta_y)
        self._old_pos = event.globalPos()

    @QtCore.Slot(int)
    def set_icon(self, index):
        icon = self._window.icon_combo.itemIcon(index)
        self._tray.setIcon(icon)

    @QtCore.Slot()
    def send_message(self):
        title = self._window.title_line.text()
        msg = self._window.msg_edit.toPlainText()

        self._tray.showMessage(title, msg,
                               QIcon('./media/dekban.png'))

    @QtCore.Slot()
    def exit(self):
        self._window.close()
Exemple #26
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(self.__class__, self).__init__()
        self.quit_ = False
        self.setupUi(self)
        self.setWindowFlags(Qt.WindowStaysOnTopHint)
        self.setStyleSheet(qss)
        self.job = Job()
        self.layout_init()
        self.tray_init()
        self.job.msg_box_signal.connect(self.msg_box_slot)
        # 主界面
        self.initial = InitialWidget(self)
        self.setCentralWidget(self.initial)

    @Slot(dict)
    def msg_box_slot(self, data):
        """消息弹窗插槽"""
        QMessageBox.information(self, "bee box", data['msg'], QMessageBox.Ok, QMessageBox.Ok)

    def layout_init(self):
        # self.setWindowFlags(Qt.FramelessWindowHint)  # 隐藏整个头部
        self.desktop = QDesktopWidget()
        taskbar_height = self.desktop.screenGeometry().height() - self.desktop.availableGeometry().height()  # 任务栏高度
        self.move((self.desktop.availableGeometry().width() - self.width() - 10),
                  self.desktop.availableGeometry().height() - self.height() - taskbar_height)  # 初始化位置到右下角
        """边缘圆角"""
        # self.bmp = QBitmap(self.size())
        # self.bmp.fill()
        # self.ppp = QPainter(self.bmp)
        # self.ppp.setPen(Qt.black)
        # self.ppp.setBrush(Qt.black)
        # self.ppp.drawRoundedRect(self.bmp.rect(), 10, 10)
        # self.ppp.end()
        # self.setMask(self.bmp)

    def tray_init(self):
        icon = QIcon(":/icon/icon/CTPBEE.png")
        menu = QMenu()
        exitAction = menu.addAction("❎  退出")
        exitAction.triggered.connect(self.quit_action)
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(icon)
        self.tray.setContextMenu(menu)
        self.tray.activated.connect(self.iconActivated)
        self.tray.show()
        self.tray.setToolTip("bee box")

    def home_handler(self):
        self.widget = HomeWidget(self)
        self.setCentralWidget(self.widget)

    def iconActivated(self, reason):
        """托盘点击插槽"""
        if reason is QSystemTrayIcon.Trigger:
            self.show()
            self.raise_()

    def quit_action(self):
        self.quit_ = True
        self.close()

    def closeEvent(self, event: QCloseEvent):
        if self.quit_:
            G.pool_done = True
            try:
                self.widget.close()
            except:
                pass
            event.accept()
        else:
            self.hide()
            event.ignore()
Exemple #27
0
class GUI:
    def __init__(self, root):
        self.root = root
        self.app = QApplication([])
        self.icons = {
            "timelapse": QIcon(resource_path('icons/timelapse.png')),
            "sync": QIcon(resource_path('icons/sync.png')),
            "sync_disabled": QIcon(resource_path('icons/sync_disabled.png')),
            "logo": QIcon(resource_path('icons/logo.png')),
            "settings": QIcon(resource_path('icons/settings.png')),
            "github": QIcon(resource_path('icons/github.png'))
        }

        self.main_window = MainWindow(self)
        self.offset_window = None
        self.settings_window = None
        self.messages = [[], []]
        self.section_labels = [
            self.main_window.ui.right_status,
            self.main_window.ui.right_status_2
        ]

        menu = QMenu('FS Time Sync')
        menu.setStyleSheet("""
        QMenu {
            background-color: #151515;
            color: #ffffff;
        }
        QMenu::item {
            padding: 5px 10px 5px 10px;
        }
        QMenu::item:selected {
            background-color: #ffffff;
            color: #151515;
        }
        """)
        self.tray_actions = {}
        self.tray_actions["sync_now"] = menu.addAction("Sync Now")
        self.tray_actions["sync_now"].triggered.connect(
            lambda: self.root.sync_sim(force=True))
        self.tray_actions["hide_show"] = menu.addAction("Hide")
        self.tray_actions["hide_show"].triggered.connect(self.hide)
        self.tray_actions["exit"] = menu.addAction("Exit")
        self.tray_actions["exit"].triggered.connect(self.exit)

        self.tray = QSystemTrayIcon()
        self.tray.setIcon(self.icons['logo'])
        self.tray.setToolTip("FS Time Sync")
        self.tray.setContextMenu(menu)
        self.tray.activated.connect(self.trayActivated)
        self.tray.show()

    def main_window_act(self, func, *args, **kwargs):
        self.main_window.act.emit([func, args, kwargs])

    def hide(self):
        self.tray_actions["hide_show"].setText("Show")
        self.tray_actions["hide_show"].triggered.connect(self.show)
        if self.offset_window:
            self.offset_window.close()
        self.main_window.saveState()
        self.main_window.hide()

    def trayActivated(self, reason):
        if reason == self.tray.ActivationReason.Trigger:
            self.show()

    def single_instance_triggered(self):
        self.tray.showMessage("FS Time Sync",
                              "FS Time Sync is already running.")
        self.show()

    def show(self):
        self.tray_actions["hide_show"].setText("Hide")
        self.tray_actions["hide_show"].triggered.connect(self.hide)
        self.main_window.show()
        # Brings window forward, but doesn't force it to stay active.
        self.main_window.setWindowState(Qt.WindowActive)
        self.main_window.setWindowState(Qt.WindowNoState)

    def exit(self):
        self.app.quit()

    def start(self):
        # Add single instance trigger.
        self.root.si.add_trigger(self.single_instance_triggered)

        # Settings are triggered here.
        if not self.root.settings.get(
                "startup", "tray"):  # Start regularly or as tray icon
            self.main_window.show()
        else:
            self.tray.showMessage("FS Time Sync",
                                  "FS Time Sync Started in Tray.")
            self.tray_actions["hide_show"].setText("Show")
            self.tray_actions["hide_show"].triggered.connect(self.show)

        if self.root.settings.get("startup", "auto_sync"):
            self.root.enable_live_sync()

        self.app.exec_()

    def add_message(self, section, code, msg):
        for message in self.messages[section]:
            if message["code"] == code:
                message["msg"] = msg
                break
        else:
            self.messages[section].append({"code": code, "msg": msg})

        self.main_window.act.emit([self.section_labels[section].setText, msg])

    def remove_message(self, section, code):
        for message in self.messages[section]:
            if message["code"] == code:
                self.messages[section].remove(message)
                break
        else:
            return  # No action just return

        if len(self.messages[section]) > 0:
            print(self.messages[section])
            self.main_window.act.emit([
                self.section_labels[section].setText,
                self.messages[section][-1]["msg"]
            ])
        else:
            self.main_window.act.emit(
                [self.section_labels[section].setText, ""])
Exemple #28
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.green_color = None
        self.yellow_color = None
        self.red_color = None
        self.tray = QSystemTrayIcon(self)
        self.connected_port = None
        self.config_filename = None
        self.base_output_filename = None
        self.output_hex_filename = None
        self.output_binary_filename = None
        self.port_read_thread = PortReadThread(self.read_port, self.stop_read)

        self.log = Logger().create_log()
        self.log.info("Launched MinXSS Beacon Decoder.")

        self.setup_ui()
        self.connect_ui_to_functions()
        self.setup_output_files()

        QApplication.instance().aboutToQuit.connect(self.prepare_to_exit)
        self.show()

    def setup_ui(self):
        self.setupUi(self)
        self.setup_colors()
        self.setup_tray_icon()
        self.setup_available_ports()
        self.setup_last_used_settings()

    def setup_colors(self):
        green = QColor(55, 195, 58)
        palette_green = QtGui.QPalette()
        palette_green.setColor(QtGui.QPalette.Text, green)
        palette_green.setColor(QtGui.QPalette.Foreground, green)
        self.green_color = palette_green

        yellow = QColor(244, 212, 66)
        palette_yellow = QtGui.QPalette()
        palette_yellow.setColor(QtGui.QPalette.Text, yellow)
        palette_yellow.setColor(QtGui.QPalette.Foreground, yellow)
        self.yellow_color = palette_yellow

        red = QColor(242, 86, 77)
        palette_red = QtGui.QPalette()
        palette_red.setColor(QtGui.QPalette.Text, red)
        palette_red.setColor(QtGui.QPalette.Foreground, red)
        self.red_color = palette_red

    def setup_tray_icon(self):
        if self.tray.isSystemTrayAvailable():
            self.tray.setIcon(QIcon('assets/icon_256.png'))
            menu = QMenu()
            action_quit = menu.addAction("Quit")
            action_quit.triggered.connect(self.close)

            self.tray.setContextMenu(menu)
            self.tray.show()
        else:
            self.tray = None

    def setup_available_ports(self):
        """
        Determine what ports are available for serial reading and populate the combo box with these options
        """
        self.comboBox_serialPort.clear()
        list_port_info_objects = list_ports.comports()
        port_names = [x[0] for x in list_port_info_objects]
        self.comboBox_serialPort.addItems(port_names)

    def connect_ui_to_functions(self):
        self.actionConnect.triggered.connect(self.connect_clicked)
        self.checkBox_saveData.stateChanged.connect(self.save_data_toggled)
        self.checkBox_forwardData.stateChanged.connect(self.forward_data_toggled)
        self.checkBox_decodeKiss.stateChanged.connect(self.decode_kiss_toggled)
        self.lineEdit_callsign.editingFinished.connect(self.ground_station_config_changed)
        self.lineEdit_latitude.editingFinished.connect(self.ground_station_config_changed)
        self.lineEdit_longitude.editingFinished.connect(self.ground_station_config_changed)
        self.actionCompletePass.triggered.connect(self.complete_pass_clicked)

    def ground_station_config_changed(self):
        self.setup_output_files()
        self.write_gui_config_options_to_config_file()

    def setup_output_files(self):
        self.set_base_output_filename()
        self.setup_output_file_decoded_data_as_hex()
        self.setup_output_file_decoded_data_as_binary()

    def set_base_output_filename(self):
        callsign = self.lineEdit_callsign.text()
        latitude = self.lineEdit_latitude.text()
        longitude = self.lineEdit_longitude.text()
        self.base_output_filename = os.path.join(os.path.expanduser("~"), "MinXSS_Beacon_Decoder", "output",
                                                 datetime.datetime.utcnow().isoformat().replace(':',
                                                                                                '_')) + '_' + callsign + '_' + latitude + '_' + longitude

    def setup_output_file_decoded_data_as_hex(self):
        self.ensure_output_folder_exists()
        self.set_output_hex_filename()

        with open(self.output_hex_filename, 'w') as output_hex_file:
            self.log.info("Opening new .txt file to output decoded data as hex.")
            self.display_gui_output_hex_is_saving()
        output_hex_file.close()  # Will later append to file as needed

    @staticmethod
    def ensure_output_folder_exists():
        if not os.path.exists(os.path.join(os.path.expanduser("~"), "MinXSS_Beacon_Decoder", "output")):
            os.makedirs(os.path.join(os.path.expanduser("~"), "MinXSS_Beacon_Decoder", "output"))

    def set_output_hex_filename(self):
        self.output_hex_filename = self.base_output_filename + ".txt"

    def display_gui_output_hex_is_saving(self):
        self.textBrowser_savingDataToFile.setText("Saving data to files: {} and .dat.".format(self.output_hex_filename))
        self.textBrowser_savingDataToFile.setPalette(self.green_color)

    def setup_output_file_decoded_data_as_binary(self):
        self.ensure_output_folder_exists()
        self.set_output_binary_filename()

        with open(self.output_binary_filename, 'w') as buffer_output_binary_file:
            self.log.info("Opening new binary file to output decoded data.")
        buffer_output_binary_file.close()

    def set_output_binary_filename(self):
        self.output_binary_filename = self.base_output_filename + ".dat"

    def write_gui_config_options_to_config_file(self):
        config = configparser.ConfigParser()
        config.read(self.config_filename)
        config.set('input_properties', 'serial_port', self.comboBox_serialPort.currentText())
        config.set('input_properties', 'baud_rate', self.lineEdit_baudRate.text())
        config.set('input_properties', 'ip_address', self.lineEdit_ipAddress.text())
        config.set('input_properties', 'port', self.lineEdit_ipPort.text())
        config.set('input_properties', 'decode_kiss', str(self.checkBox_decodeKiss.isChecked()))
        config.set('input_properties', 'forward_data', str(self.checkBox_forwardData.isChecked()))
        config.set('input_properties', 'callsign', self.lineEdit_callsign.text())
        config.set('input_properties', 'latitude', self.lineEdit_latitude.text())
        config.set('input_properties', 'longitude', self.lineEdit_longitude.text())
        with open(os.path.join(os.path.expanduser("~"), "MinXSS_Beacon_Decoder", "input_properties.cfg"), 'w') as configfile:
            config.write(configfile)
        self.log.info('Updated input_properties.cfg file with new settings.')

    def setup_last_used_settings(self):
        config = configparser.ConfigParser()
        self.config_filename = os.path.join(os.path.expanduser("~"), "MinXSS_Beacon_Decoder", "input_properties.cfg")
        if self.need_new_config_file(config):
            self.log.info('No input_properties.cfg file found. Creating the default one.')
            self.write_default_config()

        config.read(self.config_filename)
        self.set_instance_variables_from_config(config)

    def need_new_config_file(self, config):
        if not os.path.isfile(self.config_filename):
            return True
        if os.stat(self.config_filename).st_size == 0:
            return True

        try:
            config.read(self.config_filename)
        except configparser.MissingSectionHeaderError:
            return True

        if not config.has_option('input_properties', 'serial_port'):
            return True
        if not config.has_option('input_properties', 'baud_rate'):
            return True
        if not config.has_option('input_properties', 'ip_address'):
            return True
        if not config.has_option('input_properties', 'port'):
            return True
        if not config.has_option('input_properties', 'decode_kiss'):
            return True
        if not config.has_option('input_properties', 'forward_data'):
            return True
        if not config.has_option('input_properties', 'callsign'):
            return True
        if not config.has_option('input_properties', 'latitude'):
            return True
        if not config.has_option('input_properties', 'longitude'):
            return True

    def write_default_config(self):
        with open(self.config_filename, "w") as config_file:
            print("[input_properties]", file=config_file)
            print("serial_port = 3", file=config_file)
            print("baud_rate = 19200", file=config_file)
            print("ip_address = localhost", file=config_file)
            print("port = 10000", file=config_file)
            print("decode_kiss = True", file=config_file)
            print("forward_data = True", file=config_file)
            print("callsign = SFJPM86", file=config_file)
            print("latitude = 40.240", file=config_file)
            print("longitude = -105.2353", file=config_file)

    def set_instance_variables_from_config(self, config):
        self.tabWidget_serialIp.setCurrentIndex(1)  # TODO: Make this an actual config parameter
        self.comboBox_serialPort.insertItem(0, config.get('input_properties', 'serial_port'))
        self.comboBox_serialPort.setCurrentIndex(0)
        self.lineEdit_baudRate.setText(config.get('input_properties', 'baud_rate'))
        self.lineEdit_ipAddress.setText(config.get('input_properties', 'ip_address'))
        self.lineEdit_ipPort.setText(config.get('input_properties', 'port'))
        self.lineEdit_callsign.setText(config.get('input_properties', 'callsign'))
        self.lineEdit_latitude.setText(config.get('input_properties', 'latitude'))
        self.lineEdit_longitude.setText(config.get('input_properties', 'longitude'))
        self.checkBox_decodeKiss.setChecked(self.str2bool(config.get('input_properties', 'decode_kiss')))
        self.checkBox_forwardData.setChecked(self.str2bool(config.get('input_properties', 'forward_data')))
        # Intentionally don't do saveData -- always defaults to on to avoid frustration of lost data

    @staticmethod
    def str2bool(bool_string):
        if bool_string == 'True':
            return True
        if bool_string == 'False':
            return False
        raise ValueError('Can only accept exact strings "True" or "False". Was passed {}'.format(bool_string))

    def connect_clicked(self):
        self.write_gui_config_options_to_config_file()

        connect_button_text = str(self.actionConnect.iconText())
        if connect_button_text == "Connect":
            self.connect_to_port()
        else:
            self.disconnect_from_port()

    def connect_to_port(self):
        self.log.info("Attempting to connect to port.")

        self.toggle_connect_button(is_currently_connect=True)

        if self.user_chose_serial_port():
            self.connected_port, port_readable = self.connect_to_serial_port()
        else:  # user chose TCP/IP socket
            self.connected_port, port_readable = self.connect_to_socket_port()

        if port_readable:
            self.port_read_thread.start()
            self.display_gui_reading()
        else:
            self.display_gui_read_failed()

    def toggle_connect_button(self, is_currently_connect):
        if is_currently_connect:
            connect_button_text = 'Disconnect'
        else:
            connect_button_text = 'Connect'
        self.actionConnect.setText(QApplication.translate("MainWindow", connect_button_text, None, -1))

    def user_chose_serial_port(self):
        if self.tabWidget_serialIp.currentIndex() == self.tabWidget_serialIp.indexOf(self.serial):
            return True
        else:
            return False  # implying user chose TCP/IP socket

    def connect_to_serial_port(self):
        port = self.comboBox_serialPort.currentText()
        baud_rate = self.lineEdit_baudRate.text()

        connect_serial = connect_port_get_packet.ConnectSerial(port, baud_rate, self.log)
        connected_port = connect_serial.connect_to_port()
        port_readable = connected_port.port_readable

        return connected_port, port_readable

    def connect_to_socket_port(self):
        ip_address = self.lineEdit_ipAddress.text()
        port = self.lineEdit_ipPort.text()

        connect_socket = connect_port_get_packet.ConnectSocket(ip_address, port)
        connected_port = connect_socket.connect_to_port()
        port_readable = connect_socket.port_readable

        return connected_port, port_readable

    def display_gui_reading(self):
        reading = QApplication.translate("MainWindow", "Reading", None, -1)
        if self.user_chose_serial_port():
            self.label_serialStatus.setText(reading)
            self.label_serialStatus.setPalette(self.green_color)
        else:
            self.label_socketStatus.setText(reading)
            self.label_socketStatus.setPalette(self.green_color)

    def display_gui_read_failed(self):
        read_failed = QApplication.translate("MainWindow", "Read failed", None, -1)
        if self.user_chose_serial_port:
            self.label_serialStatus.setText(read_failed)
            self.label_serialStatus.setPalette(self.red_color)
        else:
            self.label_socketStatus.setText(read_failed)
            self.label_socketStatus.setPalette(self.red_color)

    def disconnect_from_port(self):
        self.log.info("Attempting to disconnect from port.")

        self.toggle_connect_button(is_currently_connect=False)
        self.display_gui_port_closed()

        self.stop_read()

    def display_gui_port_closed(self):
        port_closed = QApplication.translate("MainWindow", "Port closed", None, -1)
        if self.user_chose_serial_port():
            self.label_serialStatus.setText(port_closed)
            self.label_serialStatus.setPalette(self.red_color)
        else:
            self.label_socketStatus.setText(port_closed)
            self.label_socketStatus.setPalette(self.red_color)

    def complete_pass_clicked(self):
        self.upload_data()

    def upload_data(self):
        if self.do_forward_data():
            self.display_gui_uploading()
            file_upload.upload(self.output_binary_filename)
            self.display_gui_upload_complete()

    def do_forward_data(self):
        return self.checkBox_forwardData.isChecked()

    def display_gui_uploading(self):
        self.label_uploadStatus.setText("Upload status: Uploading")

    def display_gui_upload_complete(self):
        self.label_uploadStatus.setText("Upload status: Complete")

    def read_port(self):
        while True:
            buffer_data = self.connected_port.read_packet()
            if len(buffer_data) == 0:
                continue

            buffer_data = self.decode_kiss(buffer_data)

            buffer_data_hex_string = self.convert_buffer_data_to_hex_string(buffer_data)
            self.display_gui_hex(buffer_data_hex_string)

            self.save_data_to_disk(buffer_data_hex_string, buffer_data)

            minxss_parser = MinxssParser(buffer_data)
            telemetry = minxss_parser.parse_packet()
            self.display_gui_telemetry(telemetry)

    def decode_kiss(self, buffer_data):
        if self.do_decode_kiss():
            # C0 is a special KISS character that get replaced; unreplace it
            buffer_data = buffer_data.replace(bytearray([0xdb, 0xdc]), bytearray([0xc0]))
            # DB is a special KISS character that get replaced; unreplace it
            buffer_data = buffer_data.replace(bytearray([0xdb, 0xdd]), bytearray([0xdb]))
        return buffer_data

    def do_decode_kiss(self):
        return self.checkBox_decodeKiss.isChecked()

    @staticmethod
    def convert_buffer_data_to_hex_string(buffer_data):
        return ' '.join('0x{:02x}'.format(x) for x in buffer_data)

    def display_gui_hex(self, buffer_data_hex_string):
        self.textBrowser_serialOutput.append(buffer_data_hex_string)
        scroll_to_bottom = self.textBrowser_serialOutput.verticalScrollBar().maximum()
        self.textBrowser_serialOutput.verticalScrollBar().setValue(scroll_to_bottom)

    def save_data_to_disk(self, buffer_data_hex_string, buffer_data):
        if self.do_save_data():
            output_hex_file = open(self.output_hex_filename, 'a')
            output_hex_file.write(buffer_data_hex_string)
            output_hex_file.close()

            output_binary_file = open(self.output_binary_filename, 'ab')
            output_binary_file.write(buffer_data)
            output_binary_file.close()

    def do_save_data(self):
        return self.checkBox_saveData.isChecked()

    def display_gui_telemetry(self, telemetry):
        if not telemetry:
            return

        self.label_lastPacketTime.setText(
            "Last packet at: {} local    /    {} UTC".format(self.get_local_time(), self.get_utc_time()))

        self.display_gui_telemetry_spacecraft_state(telemetry)
        self.display_gui_telemetry_solar_data(telemetry)
        self.display_gui_telemetry_power(telemetry)
        self.display_gui_telemetry_temperature(telemetry)

        self.label_spacecraftMode.setPalette(self.green_color)
        self.color_code_telemetry(telemetry)

    @staticmethod
    def get_local_time():
        local_time = datetime.datetime.now().replace(microsecond=0).isoformat(' ')
        return local_time

    @staticmethod
    def get_utc_time():
        utc_time = datetime.datetime.utcnow().replace(microsecond=0).isoformat(' ')
        return utc_time

    def display_gui_telemetry_spacecraft_state(self, telemetry):
        self.label_flightModel.setText("{0:0=1d}".format(telemetry['FlightModel']))
        self.label_commandAcceptCount.setText("{0:0=1d}".format(telemetry['CommandAcceptCount']))
        if telemetry['SpacecraftMode'] == 0:
            self.label_spacecraftMode.setText("Unknown")
        elif telemetry['SpacecraftMode'] == 1:
            self.label_spacecraftMode.setText("Phoenix")
        elif telemetry['SpacecraftMode'] == 2:
            self.label_spacecraftMode.setText("Safe")
        elif telemetry['SpacecraftMode'] == 4:
            self.label_spacecraftMode.setText("Science")
        if telemetry['PointingMode'] == 0:
            self.label_pointingMode.setText("Coarse Point")
        elif telemetry['PointingMode'] == 1:
            self.label_pointingMode.setText("Fine Point")
        if telemetry['EnableX123'] == 1:
            self.label_enableX123.setText("Yes")
        else:
            self.label_enableX123.setText("No")
        if telemetry['EnableSps'] == 1:
            self.label_enableSps.setText("Yes")
        else:
            self.label_enableSps.setText("No")
        if telemetry['Eclipse'] == 1:
            self.label_eclipse.setText("Eclipse")
        else:
            self.label_eclipse.setText("In Sun")

    def display_gui_telemetry_solar_data(self, telemetry):
        self.label_spsX.setText("{0:.2f}".format(round(telemetry['SpsX'], 2)))
        self.label_spsY.setText("{0:.2f}".format(round(telemetry['SpsY'], 2)))
        self.label_xp.setText("{0:.2f}".format(round(telemetry['Xp'], 2)))

    def display_gui_telemetry_power(self, telemetry):
        self.label_batteryVoltage.setText("{0:.2f}".format(round(telemetry['BatteryVoltage'], 2)))
        battery_current = self.get_battery_current(telemetry)
        self.label_batteryCurrent.setText("{0:.2f}".format(round(battery_current, 2)))

        solar_panel_minus_y_power = telemetry['SolarPanelMinusYVoltage'] * telemetry['SolarPanelMinusYCurrent'] / 1e3
        solar_panel_plus_x_power = telemetry['SolarPanelPlusXVoltage'] * telemetry['SolarPanelPlusXCurrent'] / 1e3
        solar_panel_plus_y_power = telemetry['SolarPanelPlusYVoltage'] * telemetry['SolarPanelPlusYCurrent'] / 1e3
        self.label_solarPanelMinusYPower.setText("{0:.2f}".format(round(solar_panel_minus_y_power, 2)))
        self.label_solarPanelPlusXPower.setText("{0:.2f}".format(round(solar_panel_plus_x_power, 2)))
        self.label_solarPanelPlusYPower.setText("{0:.2f}".format(round(solar_panel_plus_y_power, 2)))

    def get_battery_current(self, telemetry):
        if telemetry['BatteryChargeCurrent'] > telemetry['BatteryDischargeCurrent']:
            battery_current = telemetry['BatteryChargeCurrent'] / 1e3
            self.label_batteryCurrentText.setText("Battery Charge Current")
        else:
            battery_current = telemetry['BatteryDischargeCurrent'] / 1e3
            self.label_batteryCurrentText.setText("Battery Discharge Current")
        return battery_current

    def display_gui_telemetry_temperature(self, telemetry):
        self.label_commBoardTemperature.setText("{0:.2f}".format(round(telemetry['CommBoardTemperature'], 2)))
        self.label_batteryTemperature.setText("{0:.2f}".format(round(telemetry['BatteryTemperature'], 2)))
        self.label_epsBoardTemperature.setText("{0:.2f}".format(round(telemetry['EpsBoardTemperature'], 2)))
        self.label_cdhTemperature.setText("{0:.2f}".format(round(telemetry['CdhBoardTemperature'], 2)))
        self.label_motherboardTemperature.setText("{0:.2f}".format(round(telemetry['MotherboardTemperature'], 2)))
        self.label_solarPanelMinusYTemperature.setText("{0:.2f}".format(round(telemetry['SolarPanelMinusYTemperature'], 2)))
        self.label_solarPanelPlusXTemperature.setText("{0:.2f}".format(round(telemetry['SolarPanelPlusXTemperature'], 2)))
        self.label_solarPanelPlusYTemperature.setText("{0:.2f}".format(round(telemetry['SolarPanelPlusYTemperature'], 2)))

    def color_code_telemetry(self, telemetry):
        self.color_code_spacecraft_state(telemetry)
        self.color_code_solar_data(telemetry)
        self.color_code_power(telemetry)
        self.color_code_temperature(telemetry)

    def color_code_spacecraft_state(self, telemetry):
        if telemetry['SpacecraftMode'] == 0:
            self.label_spacecraftMode.setPalette(self.red_color)
        elif telemetry['SpacecraftMode'] == 1:
            self.label_spacecraftMode.setPalette(self.red_color)
        elif telemetry['SpacecraftMode'] == 2:
            self.label_spacecraftMode.setPalette(self.yellow_color)
        elif telemetry['SpacecraftMode'] == 4:
            self.label_spacecraftMode.setPalette(self.green_color)
        if telemetry['PointingMode'] == 0:
            self.label_pointingMode.setPalette(self.yellow_color)
        elif telemetry['PointingMode'] == 1:
            self.label_pointingMode.setPalette(self.green_color)

    def color_code_solar_data(self, telemetry):
        if abs(telemetry['SpsX']) <= 3.0:
            self.label_spsX.setPalette(self.green_color)
        else:
            self.label_spsX.setPalette(self.red_color)

        if abs(telemetry['SpsY']) <= 3.0:
            self.label_spsY.setPalette(self.green_color)
        else:
            self.label_spsY.setPalette(self.red_color)

        if 0 <= telemetry['Xp'] <= 24860.0:
            self.label_xp.setPalette(self.green_color)
        else:
            self.label_xp.setPalette(self.red_color)

    def color_code_power(self, telemetry):
        solar_panel_minus_y_power = telemetry['SolarPanelMinusYVoltage'] * telemetry['SolarPanelMinusYCurrent'] / 1e3
        solar_panel_plus_x_power = telemetry['SolarPanelPlusXVoltage'] * telemetry['SolarPanelPlusXCurrent'] / 1e3
        solar_panel_plus_y_power = telemetry['SolarPanelPlusYVoltage'] * telemetry['SolarPanelPlusYCurrent'] / 1e3
        battery_current = self.get_battery_current(telemetry)

        if -1.0 <= solar_panel_minus_y_power <= 10.4:
            self.label_solarPanelMinusYPower.setPalette(self.green_color)
        else:
            self.label_solarPanelMinusYPower.setPalette(self.red_color)
        if -1.0 <= solar_panel_plus_x_power <= 5.9:
            self.label_solarPanelPlusXPower.setPalette(self.green_color)
        else:
            self.label_solarPanelPlusXPower.setPalette(self.red_color)
        if -1.0 <= solar_panel_plus_y_power <= 10.4:
            self.label_solarPanelPlusYPower.setPalette(self.green_color)
        else:
            self.label_solarPanelPlusYPower.setPalette(self.red_color)
        if telemetry['BatteryVoltage'] >= 7.2:
            self.label_batteryVoltage.setPalette(self.green_color)
        elif telemetry['BatteryVoltage'] >= 6.6:
            self.label_batteryVoltage.setPalette(self.yellow_color)
        else:
            self.label_batteryVoltage.setPalette(self.red_color)
        if 0 <= battery_current <= 2.9:
            self.label_batteryCurrent.setPalette(self.green_color)
        else:
            self.label_batteryCurrent.setPalette(self.red_color)

    def color_code_temperature(self, telemetry):
        if -8.0 <= telemetry['CommBoardTemperature'] <= 60.0:
            self.label_commBoardTemperature.setPalette(self.green_color)
        else:
            self.label_commBoardTemperature.setPalette(self.red_color)

        if 5.0 <= telemetry['BatteryTemperature'] <= 25:
            self.label_batteryTemperature.setPalette(self.green_color)
        elif 2.0 <= telemetry['BatteryTemperature'] < 5.0 or telemetry['BatteryTemperature'] > 25.0:
            self.label_batteryTemperature.setPalette(self.yellow_color)
        else:
            self.label_batteryTemperature.setPalette(self.red_color)

        if -8.0 <= telemetry['EpsBoardTemperature'] <= 45.0:
            self.label_epsBoardTemperature.setPalette(self.green_color)
        else:
            self.label_epsBoardTemperature.setPalette(self.red_color)

        if -8.0 <= telemetry['CdhBoardTemperature'] <= 29.0:
            self.label_cdhTemperature.setPalette(self.green_color)
        else:
            self.label_cdhTemperature.setPalette(self.red_color)

        if -13.0 <= telemetry['MotherboardTemperature'] <= 28.0:
            self.label_motherboardTemperature.setPalette(self.green_color)
        else:
            self.label_motherboardTemperature.setPalette(self.red_color)

        if -75.0 <= telemetry['SolarPanelMinusYTemperature'] <= 85.0:
            self.label_solarPanelMinusYTemperature.setPalette(self.green_color)
        else:
            self.label_solarPanelMinusYTemperature.setPalette(self.red_color)

        if -75.0 <= telemetry['SolarPanelPlusXTemperature'] <= 85.0:
            self.label_solarPanelPlusXTemperature.setPalette(self.green_color)
        else:
            self.label_solarPanelPlusXTemperature.setPalette(self.red_color)

        if -75.0 <= telemetry['SolarPanelPlusYTemperature'] <= 85.0:
            self.label_solarPanelPlusYTemperature.setPalette(self.green_color)
        else:
            self.label_solarPanelPlusYTemperature.setPalette(self.red_color)

    def stop_read(self):
        self.connected_port.close()

    def save_data_toggled(self):
        if self.do_save_data():
            self.setup_output_files()
        else:
            self.display_gui_no_output_data()

    def display_gui_no_output_data(self):
        self.textBrowser_savingDataToFile.setText("Not saving data to file.")
        self.textBrowser_savingDataToFile.setPalette(self.red_color)

    def forward_data_toggled(self):
        if self.do_forward_data():
            self.display_gui_upload_idle()
        else:
            self.display_gui_upload_disabled()

        self.write_gui_config_options_to_config_file()

    def display_gui_upload_idle(self):
        self.label_uploadStatus.setText("Upload status: Idle")

    def display_gui_upload_disabled(self):
        self.label_uploadStatus.setText("Upload status: Disabled")

    def decode_kiss_toggled(self):
        self.write_gui_config_options_to_config_file()

    def prepare_to_exit(self):
        self.log.info("About to quit.")
        self.upload_data()  # Only occurs if forward data is toggled on
        self.log.info("Closing MinXSS Beacon Decoder.")
Exemple #29
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 #30
0
class PingIndicator(QMainWindow):
    def __init__(self, address="8.8.8.8"):
        super(PingIndicator, self).__init__()

        self.icon = QImage(QSize(packet_amount, indicator_image_height),
                           QImage.Format_RGBA8888)

        self.online = True

        self.destination = address

        self.packets = deque([], packet_amount)

        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setToolTip(address)

        self.update_timer = QTimer(self)
        self.update_timer.setInterval(int(timeout * 1.05))
        self.update_timer.timeout.connect(self.update_indicator)

        self.update_timer.start()

        self.last_time_online = strftime("%H:%M:%S")

        self.reset()

    def update_icon(self):
        self.icon.fill(QColor(0, 0, 0, 0))

        painter = QPainter(self.icon)

        (width, height) = self.icon.size().toTuple()
        width -= 1
        height -= 1

        painter.fillRect(QRect(0, 0, width, height), QColor(0, 0, 0, 0))

        try:
            scale = min(1.0 / max(self.packets), min_scale)
        except ValueError:
            scale = min_scale

        for index, ping in enumerate(list(reversed(self.packets))):
            x = ping / float(timeout)

            color = QColor(
                int(-324 * x**2 + 390 * x + 138),  # R
                int(-480 * x**2 + 254 * x + 226),  # G
                int(-212 * x**2 + 160 * x + 52),  # B
                255,
            )

            scaled_height = ceil(scale * ping * height)

            painter.fillRect(
                QRect(width - index, height - scaled_height, 1, scaled_height),
                color)

        self.tray_icon.setIcon(QIcon(QPixmap(self.icon)))
        self.tray_icon.show()

    def update_indicator(self):
        try:
            new_env = dict(os.environ)
            new_env['LC_ALL'] = 'C'

            output = check_output(
                [
                    "ping", "-c", "1", "-W",
                    str(timeout / 1000), self.destination
                ],
                stderr=STDOUT,
                env=new_env,
            ).decode("ascii")  # man ping

            for line in output.splitlines():
                pos = line.find("time=")
                if pos != -1:
                    new_label = line[pos + 5:-3].center(4)
                    self.packets.append(round(float(new_label), 2))

                    if not self.online:
                        self.online = True
                        self.tray_icon.contextMenu().actions()[0].setText(
                            "Last disconnect: " + self.last_time_online)
                    else:
                        self.last_time_online = strftime("%H:%M:%S")

                    break
            else:
                raise ValueError("No time could be parsed.")

        except CalledProcessError as cpe:
            self.packets.append(timeout)

            if self.online:
                self.online = False
                self.tray_icon.contextMenu().actions()[0].setText(
                    "Offline since: " + strftime("%H:%M:%S"))

            print(cpe)
        except KeyboardInterrupt:
            self.close()

        self.update_icon()
        self.update_menu()

        return True

    def reset(self):
        self.packets.clear()
        self.update_icon()

        menu = QMenu()

        menu.addAction("Online since: " + strftime("%H:%M:%S"))
        menu.addAction("Lost: -, Avg: -")
        menu.addAction("Max: -, Min: -")
        menu.addSeparator()
        menu.addAction("Reset").triggered.connect(self.reset)
        menu.addAction("Quit").triggered.connect(self.close)

        self.tray_icon.setContextMenu(menu)

    def update_menu(self):
        self.tray_icon.contextMenu().actions()[1].setText(
            "Lost: %d, Avg: %dms" %
            (self.packets.count(timeout), avg(self.packets)), )
        self.tray_icon.contextMenu().actions()[2].setText(
            "Max: %dms, Min: %dms" % (max(self.packets), min(self.packets)), )
Exemple #31
0
class App(QApplication):
    def __init__(self, argv):
        super().__init__(argv)

        self._create_tray_icon()
        self._create_ui()
        self._create_interaction_server()

        self._session = None

    def open_preferences(self):
        prefs_dialog = PreferencesDialog()
        prefs_dialog.exec()

    def _mode_changed(self):
        action = self._mode_group.checkedAction()
        if action == self._mode_off:
            self._stop_session()
        elif action == self._mode_enabled:
            self._interaction_server.train = False
            self._start_session()
        elif action == self._mode_training:
            self._interaction_server.train = True
            self._start_session()

    def _start_session(self):
        if self._session is not None:
            return

        self._session = QProcess(self)
        self._session.finished.connect(self._session_ended)
        self._session.readyReadStandardOutput.connect(self._log_append_stdout)
        self._session.readyReadStandardError.connect(self._log_append_stderr)

        settings = QSettings()
        self._session.start(sys.executable, [
            'run_session.py',
            settings.value('CyKitAddress', app.DEFAULT_CYKIT_ADDRESS),
            str(settings.value('CyKitPort', app.DEFAULT_CYKIT_PORT)),
            str(self._interaction_server.port)
        ])

    def _stop_session(self):
        if self._session is not None:
            self._session.close()

    # TODO: Handle non-null exit codes
    def _session_ended(self):
        self._session = None
        self._mode_off.setChecked(True)

    def _log_append_stdout(self):
        process = self.sender()
        self._log_window.moveCursor(QTextCursor.End)
        self._log_window.insertPlainText(
            process.readAllStandardOutput().data().decode('utf-8'))
        self._log_window.moveCursor(QTextCursor.End)

    def _log_append_stderr(self):
        process = self.sender()
        self._log_window.moveCursor(QTextCursor.End)
        self._log_window.insertPlainText(
            process.readAllStandardError().data().decode('utf-8'))
        self._log_window.moveCursor(QTextCursor.End)

    def _select_letter(self, letter):
        self._letter_ui.setText(letter)

    def _create_tray_icon(self):
        menu = QMenu()

        self._mode_group = QActionGroup(menu)
        self._mode_group.triggered.connect(self._mode_changed)

        self._mode_off = QAction("&Off", parent=menu)
        self._mode_off.setCheckable(True)
        self._mode_off.setChecked(True)
        self._mode_group.addAction(self._mode_off)
        menu.addAction(self._mode_off)

        self._mode_enabled = QAction("&Enabled", parent=menu)
        self._mode_enabled.setCheckable(True)
        self._mode_group.addAction(self._mode_enabled)
        menu.addAction(self._mode_enabled)

        self._mode_training = QAction("&Training mode", parent=menu)
        self._mode_training.setCheckable(True)
        self._mode_group.addAction(self._mode_training)
        menu.addAction(self._mode_training)

        menu.addSeparator()
        menu.addAction("&Preferences", self.open_preferences)
        menu.addSeparator()
        menu.addAction("E&xit", self.exit)

        pixmap = QPixmap(32, 32)
        pixmap.fill(Qt.white)
        icon = QIcon(pixmap)

        self._tray_icon = QSystemTrayIcon(parent=self)
        self._tray_icon.setContextMenu(menu)
        self._tray_icon.setIcon(icon)
        self._tray_icon.show()

    def _create_ui(self):
        self._keyboard_ui = KeyboardUI()
        self._keyboard_ui.show()

        # TODO: Get rid of this in favor of os_interaction
        self._letter_ui = QLabel("-")
        self._letter_ui.setWindowTitle("Selected letter")
        self._letter_ui.setStyleSheet('font-size: 72pt')
        self._letter_ui.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self._letter_ui.setGeometry(600, 0, 100, 100)
        self._letter_ui.show()

        # TODO: Replace with more user-friendly log
        self._log_window = QTextBrowser()
        self._log_window.setWindowTitle("Session Log")
        self._log_window.setGeometry(700, 0, 500, 500)
        self._log_window.show()

    def _create_interaction_server(self):
        self._interaction_server = InteractionServer(self)
        self._interaction_server.keyboard_flash_row.connect(
            self._keyboard_ui.flash_row)
        self._interaction_server.keyboard_flash_col.connect(
            self._keyboard_ui.flash_col)
        self._interaction_server.keyboard_highlight_letter.connect(
            self._keyboard_ui.highlight_letter)
        self._interaction_server.keyboard_select_letter.connect(
            self._select_letter)
Exemple #32
0
class Form(QDialog):
    def __init__(self, parent=None):
        self.round = 0
        self.lcd = QLCDNumber(5)
        self.lcd2 = QLCDNumber(5)
        self.clock = QLCDNumber(5)
        super(Form, self).__init__(parent)
        self.setWindowTitle("Pomodoro")
        # Create widgets
        self.slider = QSlider(Qt.Horizontal)
        self.slider.setRange(1, 99)
        self.slider.setValue(25)
        self.slider2 = QSlider(Qt.Horizontal)
        self.slider2.setRange(1, 99)
        self.slider2.setValue(5)
        self.count = self.slider.value() * 60
        self.rest = self.slider2.value() * 60
        self.taskbar_count = 0
        self.taskbar2_count = 0
        self.text = QLabel("How long should the work period be?")
        self.text2 = QLabel("How long should the rest period be?")
        self.work = QLabel("WORK")
        self.pause = QLabel("REST")
        self.rounds = QLabel("Number of rounds: " + str(self.round))
        self.work.setAlignment(Qt.AlignHCenter)
        self.work.setFont(QFont("Times", 18, QFont.Bold))
        self.pause.setAlignment(Qt.AlignHCenter)
        self.pause.setFont(QFont("Times", 18, QFont.Bold))
        self.button = QPushButton("Start timer")
        self.button2 = QPushButton("Stop timer")
        self.reset = QPushButton("Reset rounds")
        self.lcd.display("25:00")
        self.lcd2.display("05:00")
        mins = 25
        secs = "00"
        self.clock.display(f"{mins}:{secs}")
        self.slider.valueChanged.connect(self.first_display)
        self.slider2.valueChanged.connect(self.second_display)
        self.slider.valueChanged.connect(self.clock_display)
        self.button2.hide()
        self.work.hide()
        self.pause.hide()
        self.clock.hide()
        # Create layout and add widgets
        layout = QVBoxLayout()
        layout.addWidget(self.text)
        layout.addWidget(self.lcd)
        layout.addWidget(self.slider)
        layout.addWidget(self.text2)
        layout.addWidget(self.lcd2)
        layout.addWidget(self.slider2)
        layout.addWidget(self.button)
        layout.addWidget(self.button2)
        layout.addWidget(self.work)
        layout.addWidget(self.pause)
        layout.addWidget(self.clock)
        layout.addWidget(self.rounds)
        layout.addWidget(self.reset)
        # Set dialog layout
        self.setLayout(layout)
        self.systemtray_icon = QSystemTrayIcon(QIcon("snake.png"))
        self.systemtray_icon.show()
        self.systemtray_icon.activated.connect(self.icon_activated)
        self.menu = QMenu(parent)
        self.exit_action = self.menu.addAction("Exit")
        self.systemtray_icon.setContextMenu(self.menu)
        self.exit_action.triggered.connect(self.slot_exit)
        # Add signals
        self.slider.valueChanged.connect(self.count_func)
        self.slider2.valueChanged.connect(self.count_func)
        self.button.clicked.connect(self.button_update)
        self.button.clicked.connect(self.timer_func)
        self.button.clicked.connect(self.round_count)
        self.button2.clicked.connect(self.stop)
        self.reset.clicked.connect(self.reset_rounds)

    def reset_rounds(self):
        self.round = 0
        self.rounds.setText("Number of rounds: " + str(self.round))

    def round_count(self):
        self.round += 1
        self.rounds.setText("Number of rounds: " + str(self.round))

    def icon_activated(self, reason):
        if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick):
            self.show()

    def closeEvent(self, event):
        self.hide()
        event.ignore()

    def slot_exit(self):
        QApplication.exit(0)

    def first_display(self):
        minute = str(self.slider.sliderPosition())
        second = ":00"
        leading_zero = "0"
        if self.slider.sliderPosition() >= 10:
            self.lcd.display(minute + second)
        else:
            self.lcd.display(leading_zero + minute + second)

    def second_display(self):
        minute = str(self.slider2.sliderPosition())
        second = ":00"
        leading_zero = "0"
        if self.slider2.sliderPosition() >= 10:
            self.lcd2.display(minute + second)
        else:
            self.lcd2.display(leading_zero + minute + second)

    def clock_display(self):
        minute = str(self.slider.sliderPosition())
        second = ":00"
        leading_zero = "0"
        if self.slider.sliderPosition() >= 10:
            self.clock.display(minute + second)
        else:
            self.clock.display(leading_zero + minute + second)

    def count_func(self):
        self.count = self.slider.sliderPosition() * 60
        self.rest = self.slider2.sliderPosition() * 60

    def countdown(self):
        minute, second = divmod(self.count, 60)
        zero = "0"
        show = self.work.show()
        if second < 10 and minute < 10:
            self.clock.display(zero + str(minute) + ":" + zero + str(second))
        elif second < 10:
            self.clock.display(str(minute) + ":" + zero + str(second))
        elif minute < 10:
            self.clock.display(zero + str(minute) + ":" + str(second))
        else:
            self.clock.display(str(minute) + ":" + str(second))
        self.count -= 1
        if self.count < -1:
            self.work.hide()
            self.taskbar_rest()
            show = self.pause.show()
            minute, second = divmod(self.rest, 60)
            zero = "0"
            if self.rest == self.slider2.value() * 60:
                self.show()
            if second < 10 and minute < 10:
                self.clock.display(zero + str(minute) + ":" + zero +
                                   str(second))
            elif second < 10:
                self.clock.display(str(minute) + ":" + zero + str(second))
            elif minute < 10:
                self.clock.display(zero + str(minute) + ":" + str(second))
            else:
                self.clock.display(str(minute) + ":" + str(second))
            self.rest -= 1
            if self.rest < -1:
                self.clock.display("00:00")
                self.taskbar_work()
                self.timer.stop()
                self.stop()
        show

    def timer_func(self):
        timer = QTimer()
        self.timer = timer
        self.timer.timeout.connect(self.countdown)
        self.timer.start(1000)

    def button_update(self):
        self.button.hide()
        self.text.hide()
        self.lcd.hide()
        self.slider.hide()
        self.text2.hide()
        self.lcd2.hide()
        self.slider2.hide()
        self.reset.hide()
        self.clock.show()
        self.button2.show()
        self.work.show()

    def taskbar_rest(self):
        if self.taskbar_count == 0:
            self.systemtray_icon.showMessage("PAUSE", "Time to rest!",
                                             QSystemTrayIcon.Information,
                                             500000)
            self.taskbar_count = 1

    def taskbar_work(self):
        if self.taskbar2_count == 0:
            self.systemtray_icon.showMessage("WORK", "Break over!",
                                             QSystemTrayIcon.Information,
                                             500000)
            self.taskbar2_count = 1

    def stop(self):
        self.timer.stop()
        self.button2.hide()
        self.work.hide()
        self.pause.hide()
        self.clock.hide()
        self.count = self.slider.value() * 60
        self.rest = self.slider2.value() * 60
        self.clock.display(str(self.slider.value()) + ":00")
        self.button.show()
        self.text.show()
        self.lcd.show()
        self.slider.show()
        self.text2.show()
        self.lcd2.show()
        self.slider2.show()
        self.reset.show()
        self.show()
        self.taskbar_count = 0
        self.taskbar2_count = 0