Beispiel #1
0
def init_application():
    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(False)
    mywindow = MainWindow()

    icon = QIcon('icon.png')
    # Create the tray
    tray = QSystemTrayIcon()
    tray.setIcon(icon)
    tray.setVisible(True)

    # Create the menu
    menu = QMenu()
    color_menu = QMenu('Player color')
    color_menu.addAction(mywindow.white_checkbox)
    color_menu.addAction(mywindow.black_checkbox)

    show_action = QAction('Show move')
    nothing_action = QAction('Nothing')
    exit_action = QAction('Exit')
    menu.addAction(show_action)
    menu.addMenu(color_menu)
    menu.addAction(nothing_action)
    menu.addAction(exit_action)
    show_action.triggered.connect(mywindow.show_move)
    exit_action.triggered.connect(QtWidgets.qApp.quit)

    # Add the menu to the tray
    tray.setContextMenu(menu)
    mywindow.show()
    app.exec_()
Beispiel #2
0
class Systray(QObject):
    trayIconMenu = None

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

        self.trayIconMenu = ContextMenu(None)

        icon = QIcon.fromTheme("xware-desktop")

        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setIcon(icon)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.setVisible(True)

        self.trayIcon.activated.connect(self.slotSystrayActivated)

    @pyqtSlot(QSystemTrayIcon.ActivationReason)
    def slotSystrayActivated(self, reason):
        if reason == QSystemTrayIcon.Context:  # right
            pass
        elif reason == QSystemTrayIcon.MiddleClick:  # middle
            pass
        elif reason == QSystemTrayIcon.DoubleClick:  # double click
            pass
        elif reason == QSystemTrayIcon.Trigger:  # left
            if app.mainWin.isHidden() or app.mainWin.isMinimized():
                app.mainWin.restore()
            else:
                app.mainWin.minimize()
Beispiel #3
0
class Systray(QObject):
    trayIconMenu = None

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

        self.trayIconMenu = ContextMenu(None)

        icon = QIcon(":/image/thunder.ico")

        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setIcon(icon)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.setVisible(True)

        self.trayIcon.activated.connect(self.slotSystrayActivated)

    @pyqtSlot(QSystemTrayIcon.ActivationReason)
    def slotSystrayActivated(self, reason):
        if reason == QSystemTrayIcon.Context:  # right
            pass
        elif reason == QSystemTrayIcon.MiddleClick:  # middle
            pass
        elif reason == QSystemTrayIcon.DoubleClick:  # double click
            pass
        elif reason == QSystemTrayIcon.Trigger:  # left
            if app.mainWin.isHidden() or app.mainWin.isMinimized():
                app.mainWin.restore()
            else:
                app.mainWin.minimize()
Beispiel #4
0
def main():
    app = QtWidgets.QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(False)

    # Create the icon
    if darkdetect.isDark():
        icon_image = "icon-light.png"
    else:
        icon_image = "icon-dark.png"
    icon = QIcon(icon_image)

    # Create the tray
    tray = QSystemTrayIcon()
    tray.setIcon(icon)
    tray.setVisible(True)

    # Create the menu
    menu = QMenu()
    action = QAction("A menu item")
    menu.addAction(action)

    # Add a Quit option to the menu.
    quit = QAction("Quit")
    quit.triggered.connect(app.quit)
    menu.addAction(quit)

    # Add the menu to the tray
    tray.setContextMenu(menu)
    sys.exit(app.exec_())
Beispiel #5
0
def main():
    # Create the icon
    if sys.platform.startswith("win32"):
        icon = QIcon("icon.ico")
    elif sys.platform.startswith("darwin"):
        icon = QIcon("icon.icns")

    # Create the tray
    tray = QSystemTrayIcon()
    tray.setIcon(icon)
    tray.setVisible(True)

    # Create the background thread
    thread = TimerThread(stopFlag)

    # Create the menu
    menu = QMenu()

    action_next = QAction("Next background")
    action_next.triggered.connect(next)
    menu.addAction(action_next)

    action_quit = QAction("Quit")
    action_quit.triggered.connect(quit)
    menu.addAction(action_quit)

    # Add the menu to the tray
    tray.setContextMenu(menu)

    thread.start()
    app.exec_()
Beispiel #6
0
class SystemTray(object):
    # 程序托盘类
    def __init__(self, w):
        self.app = app
        self.w = w
        QApplication.setQuitOnLastWindowClosed(
            False)  # 禁止默认的closed方法,只能使用qapp.quit()的方法退出程序
        self.w.show()  # 不设置显示则为启动最小化到托盘
        self.tp = QSystemTrayIcon(self.w)
        self.initUI()
        self.run()

    def initUI(self):
        # 设置托盘图标

        self.tp.setIcon(QIcon('./d.ico'))

    def quitApp(self):
        # 退出程序
        self.w.show()  # w.hide() #设置退出时是否显示主窗口
        re = QMessageBox.question(self.w, "提示", "退出系统",
                                  QMessageBox.Yes | QMessageBox.No,
                                  QMessageBox.No)
        if re == QMessageBox.Yes:
            self.tp.setVisible(False)  # 隐藏托盘控件,托盘图标刷新不及时,提前隐藏
            qApp.quit()  # 退出程序

    def message(self):
        # 提示信息被点击方法
        print("弹出的信息被点击了")

    def act(self, reason):
        # 主界面显示方法
        # 鼠标点击icon传递的信号会带有一个整形的值,1是表示单击右键,2是双击,3是单击左键,4是用鼠标中键点击
        if reason == 2 or reason == 3:
            self.w.show()

    def run(self):

        a1 = QAction('&显示(Show)', triggered=self.w.show)
        a2 = QAction('&退出(Exit)', triggered=self.quitApp)

        tpMenu = QMenu()
        tpMenu.addAction(a1)
        tpMenu.addAction(a2)
        self.tp.setContextMenu(tpMenu)
        self.tp.show()  # 不调用show不会显示系统托盘消息,图标隐藏无法调用

        # 信息提示
        # 参数1:标题
        # 参数2:内容
        # 参数3:图标(0没有图标 1信息图标 2警告图标 3错误图标),0还是有一个小图标
        self.tp.showMessage('Hello', '我藏好了', icon=0)
        # 绑定提醒信息点击事件
        self.tp.messageClicked.connect(self.message)
        # 绑定托盘菜单点击事件
        self.tp.activated.connect(self.act)
        sys.exit(self.app.exec_())  # 持续对app的连接
Beispiel #7
0
class Tray(QWidget):

    # noinspection PyArgumentList
    def __init__(self):
        super().__init__()
        self.menu = QMenu()
        self.icon = QIcon("assets/icon.png")
        self.tray = QSystemTrayIcon()

        self.obj = worker.Worker()
        self.thread = QThread()
        self.obj.update_config.connect(self.update_config)
        self.obj.moveToThread(self.thread)
        self.obj.finished.connect(self.thread.quit)
        self.thread.started.connect(self.obj.config_watcher)
        # self.thread.finished.connect(app.exit)

        self.thread.start()

        self.init_ui()

    def init_ui(self):

        self.tray.setIcon(self.icon)
        self.tray.setVisible(True)
        self.rebuild_menu()

    def update_config(self):
        if self.rebuild_menu():
            rumps.notification(title='Better LES',
                               message='Configuration Reloaded',
                               subtitle='')

    def rebuild_menu(self):
        config = None
        success = True
        try:
            with open('config.json') as f:
                config = json.load(f)
        except JSONDecodeError as e:

            print(e)
            error, location = str(e).split(':')
            rumps.notification(
                title='An error occurred while loading the configuration',
                message=location,
                subtitle=error)
            success = False
        self.menu.clear()
        iterate(self.menu, config)
        self.menu.addSeparator()
        self.menu.addAction('Quit Better LES', partial(exit))

        # Add the menu to the tray
        self.tray.setContextMenu(self.menu)
        return success
Beispiel #8
0
def tray_icon(app, win):
    tray = QSystemTrayIcon(QIcon("icon.png"), win)
    menu = QMenu()
    action = menu.addAction("Quit")
    action.triggered.connect(app.quit)
    menu.addAction(action)
    tray.setContextMenu(menu)
    tray.setVisible(True)
    tray.setToolTip("Cute Ninja")
    tray.show()
Beispiel #9
0
def main():

    d = getDateTime()

    # Global app
    app = QApplication(sys.argv)

    QApplication.setQuitOnLastWindowClosed(False)
    qIcon = QIcon('icons/shotty.png')
    app.setWindowIcon(qIcon)

    shotty = ShottyFullscreen()

    showNotification('Shotty', 'Running in the background')

    tray = QSystemTrayIcon()
    if tray.isSystemTrayAvailable():
        tray.setIcon(QIcon('icons/shotty.png'))
        tray.setVisible(True)
        tray.show()

        # Add a menu
        trayMenu = QMenu()
        region_screenshot_action = QAction(QIcon("icons/screenshot.png"),
                                           'Take region screenshot')
        full_screenshot_action = QAction(QIcon("icons/screenshot.png"),
                                         'Take screenshot')
        settings_action = QAction(QIcon("icons/settings.png"), 'Settings')
        about_action = QAction(QIcon("icons/info.png"), 'About')
        exit_action = QAction(QIcon("icons/exit.png"), 'Exit Shoty')

        exit_action.triggered.connect(app.exit)
        about_action.triggered.connect(shotty.showShottyAboutWindow)
        region_screenshot_action.triggered.connect(shotty.initUI)
        # We need to pass checked because connect passes
        # a bool arg as first param
        full_screenshot_action.triggered.connect(
            lambda checked, date=getDateTime(
            ), x1=-1, y1=-1, x2=-1, y2=-1, im=screenshot(
            ): shotty.saveScreenShot(date, x1, y1, x2, y2, im=im[:, :, :3]))
        trayMenu.addAction(region_screenshot_action)
        trayMenu.addAction(full_screenshot_action)
        trayMenu.addAction(settings_action)
        trayMenu.addAction(about_action)
        trayMenu.addAction(exit_action)

        tray.setContextMenu(trayMenu)
    else:
        print("[ERROR] Can't instantiate tray icon")

    sys.exit(app.exec_())
class Tray:  # create tray icon menu
    def __init__(self, ):
        # create systemtray UI
        self.icon = QIcon(ico)
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(self.icon)
        self.tray.setToolTip("Now Playing ▶")
        self.tray.setVisible(True)
        self.menu = QMenu()

        # create systemtray options and actions
        self.actTitle = QAction("Now Playing v1.4")
        self.menu.addAction(self.actTitle)
        self.actTitle.setEnabled(False)

        self.actConfig = QAction("Settings")
        self.actConfig.triggered.connect(win.show)
        self.menu.addAction(self.actConfig)
        self.menu.addSeparator()

        self.actPause = QAction()
        self.actPause.triggered.connect(self.pause)
        self.menu.addAction(self.actPause)
        self.actPause.setEnabled(False)

        self.actExit = QAction("Exit")
        self.actExit.triggered.connect(self.cleanquit)
        self.menu.addAction(self.actExit)

        # add menu to the systemtray UI
        self.tray.setContextMenu(self.menu)

    def unpause(self):  # unpause polling
        global paused
        paused = 0
        self.actPause.setText('Pause')
        self.actPause.triggered.connect(self.pause)

    def pause(self):  # pause polling
        global paused
        paused = 1
        self.actPause.setText('Resume')
        self.actPause.triggered.connect(self.unpause)

    def cleanquit(self):  # quit app and cleanup
        self.tray.setVisible(False)
        file = ConfigFile(config, config_file).file
        if file:
            writetrack(file)
        sys.exit()
Beispiel #11
0
class systemTray(QObject):
    cs = check_service

    def start_tray(self, ns, ip):
        # Initialize QApplication
        self.app = QApplication([])
        self.app.setQuitOnLastWindowClosed(False)

        # Check service status
        ipc, state = self.cs(ns, ip)

        # Set icon
        icon = QIcon(srcdir + ipc)
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(icon)
        self.tray.setVisible(True)
        self.tray.setToolTip(ns + " is " + state)

        # Set menu
        self.menu = QMenu()
        self.quit = QAction("Exit")
        self.quit.triggered.connect(self.app.quit)
        self.menu.addAction(self.quit)

        # Add menu to tray
        self.tray.setContextMenu(self.menu)

        # Create thread
        self.thread = QThread()
        # Create object which will be moved from main thread to worker thread
        self.worker = worker(ns, ip)
        # Move object to worker thread
        self.worker.moveToThread(self.thread)
        # Connect object to signal
        self.worker.newIcon.connect(self.updateIcon)
        self.worker.newToolTip.connect(self.updateToolTip)
        # Connect started signal to run method of object in worker thread
        self.thread.started.connect(self.worker.run)
        # Start thread
        self.thread.start()

        # Run tray
        self.app.exec_()

    def updateIcon(self, icon):
        self.tray.setIcon(icon)

    def updateToolTip(self, tooltip):
        self.tray.setToolTip(tooltip)
Beispiel #12
0
class SystemTray(object):
    # 程序托盘类
    def __init__(self, w):
        self.app = app
        self.w = w
        QApplication.setQuitOnLastWindowClosed(False)  # 禁止默认的closed方法,
        self.w.show()  # 不设置显示则为启动最小化到托盘
        self.tp = QSystemTrayIcon(self.w)
        self.initUI()
        self.run()

    def initUI(self):
        # 设置托盘图标
        self.tp.setIcon(QIcon('/etc/v2rayL/images/logo.ico'))

    def quitApp(self):
        # 退出程序
        self.w.show()  # w.hide() #设置退出时是否显示主窗口
        re = QMessageBox.question(self.w, "提示", "确认退出?",
                                  QMessageBox.Yes | QMessageBox.No,
                                  QMessageBox.No)
        if re == QMessageBox.Yes:
            self.tp.setVisible(False)  # 隐藏托盘控件
            qApp.quit()  # 退出程序
            self.w.v2rayL.disconnect()

    def act(self, reason):
        # 主界面显示方法
        if reason == 2 or reason == 3:
            self.w.show()

    def run(self):
        a1 = QAction('恢复(Show)', triggered=self.w.show)
        a3 = QAction('退出(Exit)', triggered=self.quitApp)
        tpMenu = QMenu()
        tpMenu.addAction(a1)
        tpMenu.addAction(a3)
        self.tp.setContextMenu(tpMenu)
        self.tp.show()  # 不调用show不会显示系统托盘消息,图标隐藏无法调用

        # 绑定提醒信息点击事件
        # self.tp.messageClicked.connect(self.message)
        # 绑定托盘菜单点击事件
        self.tp.activated.connect(self.act)
        sys.exit(self.app.exec_())  # 持续对app的连接
Beispiel #13
0
    def _create_systemtray(self):
        ''' Create & add systemtray icon with controls. '''
        if self._systemtray:
            return
        systemtray = QSystemTrayIcon(self)
        systemtray.setToolTip(app_data.app_name())
        systemtray.setIcon(QIcon(app_data.app_logo_path()))
        systemtray.setVisible(True)

        menu = QMenu(self)
        about_action = menu.addAction(QIcon(app_data.about_logo_path()), "About")
        about_action.triggered.connect(self._about_clicked)
        exclusions_action = menu.addAction(QIcon(app_data.exclusions_logo_path()), "Exclusions")
        exclusions_action.triggered.connect(self._exclusions_clicked)
        quit_action = menu.addAction(QIcon(app_data.quit_logo_path()), "Quit")
        quit_action.triggered.connect(self._quit_clicked)
        systemtray.setContextMenu(menu)
        self._systemtray = systemtray
Beispiel #14
0
    def run(self):
        app = self.app

        app.setQuitOnLastWindowClosed(False)

        # Create the icon
        icon = QIcon(self.img_icon)

        # Create the tray
        tray = QSystemTrayIcon()
        tray.setIcon(icon)
        tray.setVisible(True)

        # Create the menu
        menu = QMenu()
        self.open_site_link = open_site = QAction("Open")
        open_site.triggered.connect(self.open_site)

        menu.addAction(open_site)

        # Add a Quit option to the menu.
        quit = QAction("Quit")
        quit.triggered.connect(app.quit)
        menu.addAction(quit)

        # Add the menu to the tray
        tray.setContextMenu(menu)

        loop = QEventLoop(self.app)
        asyncio.set_event_loop(loop)

        web_app = web.Application()

        cors = aiohttp_cors.setup(web_app, defaults={
            "*": aiohttp_cors.ResourceOptions(
                    allow_credentials=True,
                    expose_headers="*",
                    allow_headers="*",
                )
        })
        cors.add(web_app.router.add_get('/', self.web_get_root))
        cors.add(web_app.router.add_get('/inc-link/', self.web_get_inc_link))

        return loop.run_until_complete(web._run_app(web_app, port=8766, host='127.0.0.1'))
Beispiel #15
0
class SystemTray(object):
    # 程序托盘类
    def __init__(self, w):
        self.app = app
        self.w = w
        QApplication.setQuitOnLastWindowClosed(
            False)  # 禁止默认的closed方法,只能使用qapp.quit()的方法退出程序
        self.tray = QSystemTrayIcon(self.w)
        self.initUI()
        self.run()

    def initUI(self):
        # 设置托盘图标
        self.tray.setIcon(QIcon('icon/tray.ico'))

    def quitApp(self):
        # 退出程序
        re = QMessageBox.question(self.w, "提示", "退出系统",
                                  QMessageBox.Yes | QMessageBox.No,
                                  QMessageBox.No)
        if re == QMessageBox.Yes:
            self.tray.setVisible(False)  # 隐藏托盘控件,托盘图标刷新不及时,提前隐藏
            app.quit()  # 退出程序

    def capture(self):
        print('capture')
        pass

    def run(self):
        a1 = QAction('&截图   Ctrl 1', triggered=self.capture)
        a2 = QAction('&退出', triggered=self.quitApp)

        _translate = QtCore.QCoreApplication.translate

        trayMenu = QMenu()
        trayMenu.addAction(a1)
        trayMenu.addAction(a2)
        self.tray.setContextMenu(trayMenu)
        self.tray.show()  # 不调用show不会显示系统托盘消息,图标隐藏无法调用

        # 信息提示
        sys.exit(self.app.exec_())  # 持续对app的连接
Beispiel #16
0
class Systray(QObject):
    app = None
    trayIconMenu = None

    def __init__(self, app):
        super().__init__()
        self.app = app
        self.mainWin = self.app.mainWin

        self.trayIconMenu = QMenu(None)

        icon = QIcon(":/image/thunder.ico")

        self.trayIcon = QSystemTrayIcon(None)
        self.trayIcon.setIcon(icon)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.setVisible(True)

        self.trayIcon.activated.connect(self.slotSystrayActivated)
        self.app.lastWindowClosed.connect(self.slotTeardown)

        self.trayIconMenu.addAction(self.mainWin.action_exit)

    @pyqtSlot()
    def slotTeardown(self):
        print("teardown Systray")
        # On Ubuntu 13.10, systrayicon won't destroy itself gracefully, stops the whole program from exiting.
        del self.trayIcon

    @pyqtSlot(QSystemTrayIcon.ActivationReason)
    def slotSystrayActivated(self, reason):
        if reason == QSystemTrayIcon.Context: # right
            pass
        elif reason == QSystemTrayIcon.MiddleClick: # middle
            pass
        elif reason == QSystemTrayIcon.DoubleClick: # double click
            pass
        elif reason == QSystemTrayIcon.Trigger: # left
            if self.mainWin.isHidden() or self.mainWin.isMinimized():
                self.mainWin.restore()
            else:
                self.mainWin.minimize()
Beispiel #17
0
    def start(self):
        icon = QIcon(self.data_repository.get_res_file_path("clock.svg"))

        tray = QSystemTrayIcon()
        tray.setIcon(icon)
        tray.setVisible(True)

        menu = QMenu()

        self.action_quit.triggered.connect(lambda x: self._on_quit())
        self.action_pause_continue.triggered.connect(
            lambda x: self._on_pause_continue())

        menu.addAction(self.action_pause_continue)
        self.add_statistics_to_menu(menu)

        self.setup_settings(menu)
        menu.addAction(self.action_quit)

        tray.setContextMenu(menu)
        self.app.exec_()
class IconoSistema:
    def __init__(self, parent=None):
        self.iniciar(parent=parent)

    def iniciar(self, parent):
        self.icon = QIcon(Const.ICONO)
        self.tray = QSystemTrayIcon(parent=parent)
        self.tray.setIcon(self.icon)
        self.iconoSalir = QIcon.fromTheme("exit")

        self.agregarMenu()

        self.tray.show()

    def agregarMenu(self):
        menu = QMenu()
        #ultimaAlertaAction = menu.addAction(self.icon, 'Ver &Ultima alerta')
        #verAlertasAction = menu.addAction(self.iconoLista, "&Ver Alertas")
        menu.addSeparator()
        exitAction = menu.addAction(self.iconoSalir, "&Salir")

        exitAction.triggered.connect(QCoreApplication.instance().quit)
        #verAlertasAction.triggered.connect(lambda: self.verVentanaAlertas())
        #ultimaAlertaAction.triggered.connect(lambda: self.verUltimaAlerta())

        self.tray.activated.connect(self.onTrayIconActivated)

        self.tray.setVisible(True)

        self.tray.setContextMenu(menu)
        #self.showMessage('Aplicacion iniciada')
        Const.SYSTRAY = self

    def onTrayIconActivated(self, reason):
        if reason == QSystemTrayIcon.Trigger:
            # ignorar
            pass
        elif reason == QSystemTrayIcon.DoubleClick:
            #self.verVentanaAlertas()
            pass
Beispiel #19
0
class QTrayIcon(object):

    __options = []

    def __init__(self, title=None, icon=None, parent=None):
        self.__trayIcon = QSystemTrayIcon(parent=parent)
        self.__context_menu = QMenu()
        self.__trayIcon.setContextMenu(self.__context_menu)

        self.set_icon(icon)
        self.set_title(title)

    def __add_option(self, text, function):
        option = QAction(text)
        option.triggered.connect(function)
        self.__context_menu.addAction(option)
        self.__options.append(option)

    def add_options(self, options):
        for option, function in options.items():
            self.__add_option(option, function)

    def disable_option(self, index):
        self.__options[index].setEnabled(False)

    def enable_option(self, index):
        self.__options[index].setEnabled(True)

    def set_icon(self, icon):
        self.__icon = QIcon(icon)
        self.__trayIcon.setIcon(self.__icon)
        self.__trayIcon.setVisible(True)

    def set_title(self, title):
        self.__trayIcon.setToolTip(title)

    def show_message(self, title, message, duration=5000, icon=None):
        icon = QIcon(icon) if icon else self.__icon
        self.__trayIcon.showMessage(title, message, self.__icon, duration)
Beispiel #20
0
def runcatCPU():
    app = QApplication(sys.argv)
    # 最后一个可视的窗口退出时程序不退出
    app.setQuitOnLastWindowClosed(False)
    icon = QSystemTrayIcon()
    icon.setIcon(QIcon('icons/0.png'))
    icon.setVisible(True)
    cpu_percent = psutil.cpu_percent(interval=1) / 100
    cpu_percent_update_fps = 20
    fps_count = 0
    while True:
        fps_count += 1
        if fps_count > cpu_percent_update_fps:
            cpu_percent = psutil.cpu_percent(interval=1) / 100
            fps_count = 0
        # 开口向上的抛物线, 左边递减
        time_interval = (cpu_percent * cpu_percent - 2 * cpu_percent + 2) / 20
        for i in range(5):
            icon.setIcon(QIcon('icons/%d.png' % i))
            icon.setToolTip('cpu: %.2f' % cpu_percent)
            time.sleep(time_interval)
    app.exec_()
Beispiel #21
0
def setTrayIcon(app, mainWindow):
    app.setQuitOnLastWindowClosed(False)
    icon = QIcon(APP_ICON)
    trayIcon = QSystemTrayIcon(parent=mainWindow, icon=icon)

    menu = QMenu(parent=mainWindow)

    settingsAction = QAction(text='Settings', parent=mainWindow)
    settingsAction.triggered.connect(mainWindow.showSettingsWindow)
    menu.addAction(settingsAction)

    aboutAction = QAction(text='About', parent=mainWindow)
    aboutAction.triggered.connect(mainWindow.showAboutWindow)
    menu.addAction(aboutAction)

    quitAction = QAction(text='Quit', parent=mainWindow)
    quitAction.triggered.connect(app.quit)
    menu.addAction(quitAction)

    trayIcon.activated.connect(mainWindow.setFocusOnWindow)

    trayIcon.setContextMenu(menu)
    trayIcon.setVisible(True)
Beispiel #22
0
def runcatMemory():
    app = QApplication(sys.argv)
    # 最后一个可视的窗口退出时程序不退出
    app.setQuitOnLastWindowClosed(False)
    icon = QSystemTrayIcon()
    icon.setIcon(QIcon('icons/0.png'))
    icon.setVisible(True)
    memory_percent = psutil.virtual_memory().percent / 100
    memory_percent_update_fps = 20
    fps_count = 0
    while True:
        fps_count += 1
        if fps_count > memory_percent_update_fps:
            memory_percent = psutil.virtual_memory().percent / 100
            fps_count = 0
        # 开口向上的抛物线, 左边递减
        time_interval = (memory_percent * memory_percent - 2 * memory_percent +
                         2) / 20
        for i in range(5):
            icon.setIcon(QIcon('icons/%d.png' % i))
            icon.setToolTip('memory: %.2f' % memory_percent)
            time.sleep(time_interval)
    app.exec_()
Beispiel #23
0
class Main(QMainWindow, form_class):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setIcon(QIcon('ui/icon.png'))
        self.trayIcon.activated.connect(self.restore_window)

        self.WM = WindowManager()
        self.pre_window = self.WM.get_fore_window()
        self.rnn = RNN()
        self.runState = False

        self.startButton.clicked.connect(self.btn_clk_start)
        self.startState = True
        self.trainButton.clicked.connect(self.btn_clk_train)
        self.helpButton.clicked.connect(self.btn_clk_help)
        self.helpState = True

        self.timer = QTimer(self)
        self.timer.start(200)
        self.timer.timeout.connect(self.run)

    def restore_window(self, reason):
        if reason == QSystemTrayIcon.DoubleClick:
            self.trayIcon.hide()
            self.showNormal()

    def btn_clk_start(self):
        if self.startState:
            self.startState = False
            self.startButton.setText('Stop')

            self.runState = True
            self.back = Background()
            self.back.show()
            self.trayIcon.setVisible(True)
            self.hide()
        else:
            self.startState = True
            self.startButton.setText('Start')

            self.runState = False
            self.back.close()
            self.timer.stop()

    def btn_clk_train(self):
        if os.path.isfile('params.pkl'):
            os.remove('params.pkl')
        time.sleep(2)
        self.rnn = RNN()
        self.rnn.train()
        QMessageBox.information(self, "RNN", "train finished")

    def btn_clk_help(self):
        if self.helpState:
            self.helpState = False
            self.helpButton.setText('go to tray icon')
            QMessageBox.information(self, "Help", "*****@*****.**")
        else:
            self.helpState = True
            self.helpButton.setText('Help')
            self.trayIcon.setVisible(True)
            self.hide()

    def get_hash(self, text):
        hash = hashlib.md5()
        hash.update(text.encode())

        return hash.hexdigest()

    def run(self):
        try:
            with open("data.txt", 'a') as f:
                cur_window = self.WM.get_fore_window().split()[0]
                if cur_window != self.pre_window:
                    self.pre_window = cur_window
                    cur_window_hash = self.get_hash(cur_window)
                    f.write(" " + cur_window_hash)
                    if self.runState:
                        target_windows = list(
                            self.rnn.test([cur_window_hash], 3))
                        target_windows.append(cur_window_hash)
                        print(target_windows)
                        self.WM.set_window(self.WM.find_window('dimmer'))
                        all_windows = list(self.WM.get_windows())
                        for t_window in target_windows:
                            for i, window in enumerate(all_windows):
                                if t_window == self.get_hash(
                                        window.split()[0]):
                                    self.WM.set_window(
                                        self.WM.find_window(all_windows[i]))
        except:
            pass
Beispiel #24
0
    return os.path.join(base_path, relative_path)


icon_start = QIcon(resource_path("icon/icon_start.png"))
icon_stop = QIcon(resource_path("icon/icon_stop.png"))
app.setWindowIcon(icon_stop)


def tray_icon_clicked():
    window.activateWindow()
    window.showNormal()


tray = QSystemTrayIcon()
tray.setIcon(icon_stop)
tray.setVisible(True)
# noinspection PyUnresolvedReferences
tray.activated.connect(tray_icon_clicked)

database = Database()
active_job: Union[Job, None] = None


def start_timer():
    timer.start()
    clock.setStyleSheet('font-size: 18px; color: green; font-weight: bold')
    app.setWindowIcon(icon_start)
    tray.setIcon(icon_start)


def stop_timer():
Beispiel #25
0
class QtApplication(QApplication, Application):
    pluginsLoaded = Signal()
    applicationRunning = Signal()

    def __init__(self, tray_icon_name=None, **kwargs):
        plugin_path = ""
        if sys.platform == "win32":
            if hasattr(sys, "frozen"):
                plugin_path = os.path.join(
                    os.path.dirname(os.path.abspath(sys.executable)), "PyQt5",
                    "plugins")
                Logger.log("i", "Adding QT5 plugin path: %s" % (plugin_path))
                QCoreApplication.addLibraryPath(plugin_path)
            else:
                import site
                for sitepackage_dir in site.getsitepackages():
                    QCoreApplication.addLibraryPath(
                        os.path.join(sitepackage_dir, "PyQt5", "plugins"))
        elif sys.platform == "darwin":
            plugin_path = os.path.join(Application.getInstallPrefix(),
                                       "Resources", "plugins")

        if plugin_path:
            Logger.log("i", "Adding QT5 plugin path: %s" % (plugin_path))
            QCoreApplication.addLibraryPath(plugin_path)

        os.environ["QSG_RENDER_LOOP"] = "basic"

        super().__init__(sys.argv, **kwargs)

        self.setAttribute(Qt.AA_UseDesktopOpenGL)
        major_version, minor_version, profile = OpenGLContext.detectBestOpenGLVersion(
        )

        if major_version is None and minor_version is None and profile is None:
            Logger.log(
                "e",
                "Startup failed because OpenGL version probing has failed: tried to create a 2.0 and 4.1 context. Exiting"
            )
            QMessageBox.critical(
                None, "Failed to probe OpenGL",
                "Could not probe OpenGL. This program requires OpenGL 2.0 or higher. Please check your video card drivers."
            )
            sys.exit(1)
        else:
            Logger.log(
                "d", "Detected most suitable OpenGL context version: %s" %
                (OpenGLContext.versionAsText(major_version, minor_version,
                                             profile)))
        OpenGLContext.setDefaultFormat(major_version,
                                       minor_version,
                                       profile=profile)

        self._plugins_loaded = False  # Used to determine when it's safe to use the plug-ins.
        self._main_qml = "main.qml"
        self._engine = None
        self._renderer = None
        self._main_window = None
        self._theme = None

        self._shutting_down = False
        self._qml_import_paths = []
        self._qml_import_paths.append(
            os.path.join(os.path.dirname(sys.executable), "qml"))
        self._qml_import_paths.append(
            os.path.join(Application.getInstallPrefix(), "Resources", "qml"))

        self.parseCommandLine()
        Logger.log("i", "Command line arguments: %s",
                   self._parsed_command_line)

        signal.signal(signal.SIGINT, signal.SIG_DFL)
        # This is done here as a lot of plugins require a correct gl context. If you want to change the framework,
        # these checks need to be done in your <framework>Application.py class __init__().

        i18n_catalog = i18nCatalog("uranium")

        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress", "Loading plugins..."))
        self._loadPlugins()
        self._plugin_registry.checkRequiredPlugins(self.getRequiredPlugins())
        self.pluginsLoaded.emit()

        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress", "Updating configuration..."))
        upgraded = UM.VersionUpgradeManager.VersionUpgradeManager.getInstance(
        ).upgrade()
        if upgraded:
            # Preferences might have changed. Load them again.
            # Note that the language can't be updated, so that will always revert to English.
            preferences = Preferences.getInstance()
            try:
                preferences.readFromFile(
                    Resources.getPath(Resources.Preferences,
                                      self._application_name + ".cfg"))
            except FileNotFoundError:
                pass

        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress", "Loading preferences..."))
        try:
            file_name = Resources.getPath(Resources.Preferences,
                                          self.getApplicationName() + ".cfg")
            Preferences.getInstance().readFromFile(file_name)
        except FileNotFoundError:
            pass

        self.getApplicationName()

        Preferences.getInstance().addPreference(
            "%s/recent_files" % self.getApplicationName(), "")

        self._recent_files = []
        file_names = Preferences.getInstance().getValue(
            "%s/recent_files" % self.getApplicationName()).split(";")
        for file_name in file_names:
            if not os.path.isfile(file_name):
                continue

            self._recent_files.append(QUrl.fromLocalFile(file_name))

        JobQueue.getInstance().jobFinished.connect(self._onJobFinished)

        # Initialize System tray icon and make it invisible because it is used only to show pop up messages
        self._tray_icon = None
        self._tray_icon_widget = None
        if tray_icon_name:
            self._tray_icon = QIcon(
                Resources.getPath(Resources.Images, tray_icon_name))
            self._tray_icon_widget = QSystemTrayIcon(self._tray_icon)
            self._tray_icon_widget.setVisible(False)

    recentFilesChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify=recentFilesChanged)
    def recentFiles(self):
        return self._recent_files

    def _onJobFinished(self, job):
        if (not isinstance(job, ReadMeshJob)
                and not isinstance(job, ReadFileJob)) or not job.getResult():
            return

        f = QUrl.fromLocalFile(job.getFileName())
        if f in self._recent_files:
            self._recent_files.remove(f)

        self._recent_files.insert(0, f)
        if len(self._recent_files) > 10:
            del self._recent_files[10]

        pref = ""
        for path in self._recent_files:
            pref += path.toLocalFile() + ";"

        Preferences.getInstance().setValue(
            "%s/recent_files" % self.getApplicationName(), pref)
        self.recentFilesChanged.emit()

    def run(self):
        pass

    def hideMessage(self, message):
        with self._message_lock:
            if message in self._visible_messages:
                self._visible_messages.remove(message)
                self.visibleMessageRemoved.emit(message)

    def showMessage(self, message):
        with self._message_lock:
            if message not in self._visible_messages:
                self._visible_messages.append(message)
                message.setLifetimeTimer(QTimer())
                message.setInactivityTimer(QTimer())
                self.visibleMessageAdded.emit(message)

        # also show toast message when the main window is minimized
        self.showToastMessage(self._application_name, message.getText())

    def _onMainWindowStateChanged(self, window_state):
        if self._tray_icon:
            visible = window_state == Qt.WindowMinimized
            self._tray_icon_widget.setVisible(visible)

    # Show toast message using System tray widget.
    def showToastMessage(self, title: str, message: str):
        if self.checkWindowMinimizedState() and self._tray_icon_widget:
            # NOTE: Qt 5.8 don't support custom icon for the system tray messages, but Qt 5.9 does.
            #       We should use the custom icon when we switch to Qt 5.9
            self._tray_icon_widget.showMessage(title, message)

    def setMainQml(self, path):
        self._main_qml = path

    def initializeEngine(self):
        # TODO: Document native/qml import trickery
        Bindings.register()

        self._engine = QQmlApplicationEngine()
        self._engine.setOutputWarningsToStandardError(False)
        self._engine.warnings.connect(self.__onQmlWarning)

        for path in self._qml_import_paths:
            self._engine.addImportPath(path)

        if not hasattr(sys, "frozen"):
            self._engine.addImportPath(
                os.path.join(os.path.dirname(__file__), "qml"))

        self._engine.rootContext().setContextProperty("QT_VERSION_STR",
                                                      QT_VERSION_STR)
        self._engine.rootContext().setContextProperty(
            "screenScaleFactor", self._screenScaleFactor())

        self.registerObjects(self._engine)

        self._engine.load(self._main_qml)
        self.engineCreatedSignal.emit()

    def exec_(self, *args, **kwargs):
        self.applicationRunning.emit()
        super().exec_(*args, **kwargs)

    @pyqtSlot()
    def reloadQML(self):
        # only reload when it is a release build
        if not self.getIsDebugMode():
            return
        self._engine.clearComponentCache()
        self._theme.reload()
        self._engine.load(self._main_qml)
        # Hide the window. For some reason we can't close it yet. This needs to be done in the onComponentCompleted.
        for obj in self._engine.rootObjects():
            if obj != self._engine.rootObjects()[-1]:
                obj.hide()

    @pyqtSlot()
    def purgeWindows(self):
        # Close all root objects except the last one.
        # Should only be called by onComponentCompleted of the mainWindow.
        for obj in self._engine.rootObjects():
            if obj != self._engine.rootObjects()[-1]:
                obj.close()

    @pyqtSlot("QList<QQmlError>")
    def __onQmlWarning(self, warnings):
        for warning in warnings:
            Logger.log("w", warning.toString())

    engineCreatedSignal = Signal()

    def isShuttingDown(self):
        return self._shutting_down

    def registerObjects(self, engine):
        engine.rootContext().setContextProperty("PluginRegistry",
                                                PluginRegistry.getInstance())

    def getRenderer(self):
        if not self._renderer:
            self._renderer = QtRenderer()

        return self._renderer

    @classmethod
    def addCommandLineOptions(self, parser, parsed_command_line={}):
        super().addCommandLineOptions(parser,
                                      parsed_command_line=parsed_command_line)
        parser.add_argument(
            "--disable-textures",
            dest="disable-textures",
            action="store_true",
            default=False,
            help=
            "Disable Qt texture loading as a workaround for certain crashes.")
        parser.add_argument("-qmljsdebugger",
                            help="For Qt's QML debugger compatibility")

    mainWindowChanged = Signal()

    def getMainWindow(self):
        return self._main_window

    def setMainWindow(self, window):
        if window != self._main_window:
            if self._main_window is not None:
                self._main_window.windowStateChanged.disconnect(
                    self._onMainWindowStateChanged)

            self._main_window = window
            if self._main_window is not None:
                self._main_window.windowStateChanged.connect(
                    self._onMainWindowStateChanged)

            self.mainWindowChanged.emit()

    def setVisible(self, visible):
        if self._engine is None:
            self.initializeEngine()

        if self._main_window is not None:
            self._main_window.visible = visible

    @property
    def isVisible(self):
        if self._main_window is not None:
            return self._main_window.visible

    def getTheme(self):
        if self._theme is None:
            if self._engine is None:
                Logger.log(
                    "e",
                    "The theme cannot be accessed before the engine is initialised"
                )
                return None

            self._theme = UM.Qt.Bindings.Theme.Theme.getInstance(self._engine)
        return self._theme

    #   Handle a function that should be called later.
    def functionEvent(self, event):
        e = _QtFunctionEvent(event)
        QCoreApplication.postEvent(self, e)

    #   Handle Qt events
    def event(self, event):
        if event.type() == _QtFunctionEvent.QtFunctionEvent:
            event._function_event.call()
            return True

        return super().event(event)

    def windowClosed(self):
        Logger.log("d", "Shutting down %s", self.getApplicationName())
        self._shutting_down = True

        try:
            Preferences.getInstance().writeToFile(
                Resources.getStoragePath(Resources.Preferences,
                                         self.getApplicationName() + ".cfg"))
        except Exception as e:
            Logger.log("e", "Exception while saving preferences: %s", repr(e))

        try:
            self.applicationShuttingDown.emit()
        except Exception as e:
            Logger.log("e", "Exception while emitting shutdown signal: %s",
                       repr(e))

        try:
            self.getBackend().close()
        except Exception as e:
            Logger.log("e", "Exception while closing backend: %s", repr(e))

        self.quit()

    def checkWindowMinimizedState(self):
        if self._main_window is not None and self._main_window.windowState(
        ) == Qt.WindowMinimized:
            return True
        else:
            return False

    ##  Get the backend of the application (the program that does the heavy lifting).
    #   The backend is also a QObject, which can be used from qml.
    #   \returns Backend \type{Backend}
    @pyqtSlot(result="QObject*")
    def getBackend(self):
        return self._backend

    ##  Property used to expose the backend
    #   It is made static as the backend is not supposed to change during runtime.
    #   This makes the connection between backend and QML more reliable than the pyqtSlot above.
    #   \returns Backend \type{Backend}
    @pyqtProperty("QVariant", constant=True)
    def backend(self):
        return self.getBackend()

    ##  Load a Qt translation catalog.
    #
    #   This method will locate, load and install a Qt message catalog that can be used
    #   by Qt's translation system, like qsTr() in QML files.
    #
    #   \param file_name The file name to load, without extension. It will be searched for in
    #                    the i18nLocation Resources directory. If it can not be found a warning
    #                    will be logged but no error will be thrown.
    #   \param language The language to load translations for. This can be any valid language code
    #                   or 'default' in which case the language is looked up based on system locale.
    #                   If the specified language can not be found, this method will fall back to
    #                   loading the english translations file.
    #
    #   \note When `language` is `default`, the language to load can be changed with the
    #         environment variable "LANGUAGE".
    def loadQtTranslation(self, file_name, language="default"):
        # TODO Add support for specifying a language from preferences
        path = None
        if language == "default":
            path = self._getDefaultLanguage(file_name)
        else:
            path = Resources.getPath(Resources.i18n, language, "LC_MESSAGES",
                                     file_name + ".qm")

        # If all else fails, fall back to english.
        if not path:
            Logger.log(
                "w",
                "Could not find any translations matching {0} for file {1}, falling back to english"
                .format(language, file_name))
            try:
                path = Resources.getPath(Resources.i18n, "en_US",
                                         "LC_MESSAGES", file_name + ".qm")
            except FileNotFoundError:
                Logger.log(
                    "w",
                    "Could not find English translations for file {0}. Switching to developer english."
                    .format(file_name))
                return

        translator = QTranslator()
        if not translator.load(path):
            Logger.log("e", "Unable to load translations %s", file_name)
            return

        # Store a reference to the translator.
        # This prevents the translator from being destroyed before Qt has a chance to use it.
        self._translators[file_name] = translator

        # Finally, install the translator so Qt can use it.
        self.installTranslator(translator)

    ## Create a class variable so we can manage the splash in the CrashHandler dialog when the Application instance
    # is not yet created, e.g. when an error occurs during the initialization
    splash = None

    def createSplash(self):
        if not self.getCommandLineOption("headless"):
            try:
                QtApplication.splash = self._createSplashScreen()
            except FileNotFoundError:
                QtApplication.splash = None
            else:
                if QtApplication.splash:
                    QtApplication.splash.show()
                    self.processEvents()

    ##  Display text on the splash screen.
    def showSplashMessage(self, message):
        if not QtApplication.splash:
            self.createSplash()

        if QtApplication.splash:
            QtApplication.splash.showMessage(message,
                                             Qt.AlignHCenter | Qt.AlignVCenter)
            self.processEvents()
        elif self.getCommandLineOption("headless"):
            Logger.log("d", message)

    ##  Close the splash screen after the application has started.
    def closeSplash(self):
        if QtApplication.splash:
            QtApplication.splash.close()
            QtApplication.splash = None

    ## Create a QML component from a qml file.
    #  \param qml_file_path: The absolute file path to the root qml file.
    #  \param context_properties: Optional dictionary containing the properties that will be set on the context of the
    #                              qml instance before creation.
    #  \return None in case the creation failed (qml error), else it returns the qml instance.
    #  \note If the creation fails, this function will ensure any errors are logged to the logging service.
    def createQmlComponent(
        self,
        qml_file_path: str,
        context_properties: Dict[str,
                                 "QObject"] = None) -> Optional["QObject"]:
        path = QUrl.fromLocalFile(qml_file_path)
        component = QQmlComponent(self._engine, path)
        result_context = QQmlContext(self._engine.rootContext())
        if context_properties is not None:
            for name, value in context_properties.items():
                result_context.setContextProperty(name, value)
        result = component.create(result_context)
        for err in component.errors():
            Logger.log("e", str(err.toString()))
        if result is None:
            return None

        # We need to store the context with the qml object, else the context gets garbage collected and the qml objects
        # no longer function correctly/application crashes.
        result.attached_context = result_context
        return result

    def _createSplashScreen(self):
        return QSplashScreen(
            QPixmap(
                Resources.getPath(Resources.Images,
                                  self.getApplicationName() + ".png")))

    def _screenScaleFactor(self):
        # OSX handles sizes of dialogs behind our backs, but other platforms need
        # to know about the device pixel ratio
        if sys.platform == "darwin":
            return 1.0
        else:
            # determine a device pixel ratio from font metrics, using the same logic as UM.Theme
            fontPixelRatio = QFontMetrics(
                QCoreApplication.instance().font()).ascent() / 11
            # round the font pixel ratio to quarters
            fontPixelRatio = int(fontPixelRatio * 4) / 4
            return fontPixelRatio

    def _getDefaultLanguage(self, file_name):
        # If we have a language override set in the environment, try and use that.
        lang = os.getenv("URANIUM_LANGUAGE")
        if lang:
            try:
                return Resources.getPath(Resources.i18n, lang, "LC_MESSAGES",
                                         file_name + ".qm")
            except FileNotFoundError:
                pass

        # Else, try and get the current language from preferences
        lang = Preferences.getInstance().getValue("general/language")
        if lang:
            try:
                return Resources.getPath(Resources.i18n, lang, "LC_MESSAGES",
                                         file_name + ".qm")
            except FileNotFoundError:
                pass

        # If none of those are set, try to use the environment's LANGUAGE variable.
        lang = os.getenv("LANGUAGE")
        if lang:
            try:
                return Resources.getPath(Resources.i18n, lang, "LC_MESSAGES",
                                         file_name + ".qm")
            except FileNotFoundError:
                pass

        # If looking up the language from the enviroment or preferences fails, try and use Qt's system locale instead.
        locale = QLocale.system()

        # First, try and find a directory for any of the provided languages
        for lang in locale.uiLanguages():
            try:
                return Resources.getPath(Resources.i18n, lang, "LC_MESSAGES",
                                         file_name + ".qm")
            except FileNotFoundError:
                pass

        # If that fails, see if we can extract a language code from the
        # preferred language, regardless of the country code. This will turn
        # "en-GB" into "en" for example.
        lang = locale.uiLanguages()[0]
        lang = lang[0:lang.find("-")]
        for subdirectory in os.path.listdir(Resources.getPath(Resources.i18n)):
            if subdirectory == "en_7S":  #Never automatically go to Pirate.
                continue
            if not os.path.isdir(
                    Resources.getPath(Resources.i18n, subdirectory)):
                continue
            if subdirectory.startswith(
                    lang +
                    "_"):  #Only match the language code, not the country code.
                return Resources.getPath(Resources.i18n, lang, "LC_MESSAGES",
                                         file_name + ".qm")

        return None
Beispiel #26
0
class SystemTray(object):
    # 程序托盘类
    def __init__(self, w):
        self.app = app
        self.thread = Thread()
        self.w = w
        QApplication.setQuitOnLastWindowClosed(
            False)  # 禁止默认的closed方法,只能使用qapp.quit()的方法退出程序
        self.w.hide()  # 不设置显示则为启动最小化到托盘
        self.tp = QSystemTrayIcon(self.w)
        self.initUI()
        self.run()

    def kill_v2ray_process(self, name="v2ray"):
        child = subprocess.Popen(["pgrep", "-f", name],
                                 stdout=subprocess.PIPE,
                                 shell=False)
        pid = int(child.communicate()[0])
        os.kill(pid, signal.SIGKILL)

    def initUI(self):
        # 设置托盘图标

        self.tp.setIcon(QIcon('icons.ico'))

    def quitApp(self):
        # 退出程序
        # self.w.show()  # w.hide() #设置退出时是否显示主窗口
        # re = QMessageBox.question(self.w, "Notification", "Quit ?",
        #                           QMessageBox.Yes | QMessageBox.No,
        #                           QMessageBox.No)
        # if re == QMessageBox.Yes:
        self.tp.setVisible(False)  # 隐藏托盘控件,托盘图标刷新不及时,提前隐藏
        # self.thread.quit()
        self.kill_v2ray_process()

        qApp.quit()  # 退出程序

    def message(self):
        # 提示信息被点击方法
        print("弹出的信息被点击了")

    def act(self, reason):
        # 主界面显示方法
        # 鼠标点击icon传递的信号会带有一个整形的值,1是表示单击右键,2是双击,3是单击左键,4是用鼠标中键点击
        if reason == 2 or reason == 3:
            self.w.show()

    def run(self):

        a1 = QAction('&显示(Show)', triggered=self.w.show)
        a2 = QAction('&退出(Exit)', triggered=self.quitApp)

        self.thread.start()
        # self.thread.trigger.connect("v2ray")
        #self.thread.quit()
        # self.thread.started()

        tpMenu = QMenu()

        tpMenu.addAction(a1)
        tpMenu.addAction(a2)
        self.tp.setContextMenu(tpMenu)
        self.tp.show()  # 不调用show不会显示系统托盘消息,图标隐藏无法调用

        # 信息提示
        # 参数1:标题
        # 参数2:内容
        # 参数3:图标(0没有图标 1信息图标 2警告图标 3错误图标),0还是有一个小图标
        # self.tp.showMessage('Hello', '我藏好了', icon=0)
        # 绑定提醒信息点击事件
        # self.tp.messageClicked.connect(self.message)
        # 绑定托盘菜单点击事件
        self.tp.activated.connect(self.act)
        sys.exit(self.app.exec_())  # 持续对app的连接
Beispiel #27
0
class GoTrayUI(QDialog):
 
    def __init__(self):

        super().__init__()
        self.GotoTrayUI()

    def GotoTrayUI(self):

        ### Font configure
        font = QtGui.QFont()
        font.setBold(True)

        ### remove title bar
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint|QtCore.Qt.WindowStaysOnBottomHint)

        ### background image and Fixed Size
        back_label = QLabel(self)
        back_label.setGeometry(QtCore.QRect(2,2,400,250))
        back = QPixmap('.\\img\\33_back.png')
        back_label.setPixmap(back)
        
        ### close Button ( check box checked = go tray )
        Close_btn = QPushButton('',self)
        Close_btn.setIcon(QIcon('.\\img\\Close_btn.png'))
        Close_btn.setFixedSize(20,20)
        Close_btn.setIconSize(QtCore.QSize(30,30))
        Close_btn.move(360,20)
        Close_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))

        ### check box ( checked = go tray | non_checked = exit )
        self.check_box = QCheckBox("Tray Icon으로 보내시겠습니까?",self)
        self.check_box.setFont(font)
        self.check_box.setStyleSheet("color : white")

        self.check_box.move(80,80)
        self.check_box.resize(300,50)

        ### Exit Button ( check box checked = go tray )
        Exit_btn = QPushButton('',self)
        Exit_btn.setText('종료')
        Exit_btn.setFixedSize(80,40)
        Exit_btn.move(100,160)
        Exit_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))

        ### Cancel Button
        Cancel_btn = QPushButton('',self)
        Cancel_btn.setText('취소')
        Cancel_btn.setFixedSize(80,40)
        Cancel_btn.move(240,160)
        Cancel_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        
        # TrayIcon Setting
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(QIcon('.\\img\\Icon.png'))
        show_action = QAction("실행", self)
        quit_action = QAction("종료", self)
        

        ### Tray Icon menu - 실행 - show process_UI
        show_action.triggered.connect(self.active_tray)
        ### Tray Icon menu - 종료 - exit
        quit_action.triggered.connect(QApplication.quit)
        tray_menu = QMenu()
        tray_menu.addAction(show_action)
        tray_menu.addAction(quit_action)

        ### Tray_menu set
        self.tray_icon.setContextMenu(tray_menu)

        Cancel_btn.clicked.connect(self.hide)
        Close_btn.clicked.connect(self.closeEvent)
        Exit_btn.clicked.connect(self.closeEvent)

        ### tray visible False
        self.tray_icon.setVisible(True)
        self.tray_icon.setVisible(False)

    ### Chkbox clicked & Close
    def closeEvent(self):
        if self.check_box.isChecked():
            self.hide()
            self.tray_icon.setVisible(True)

            ### show Message - Tray
            self.tray_icon.showMessage("Kx","Tray Icon has been set")
            ### processing UI hide set
            self = proc_self
            self.hide()
            
        else:
            
            exit()
    ### traymenu - execute
    def active_tray(self):
        self.tray_icon.setVisible(False)
        self = proc_self
        self.show()
        
    ### MousePressEvent & MouseMoveEvent = drag window
    def mousePressEvent(self, event):
        self.offset = event.pos()

    def mouseMoveEvent(self, event):
        x=event.globalX()
        y=event.globalY()
        x_w = self.offset.x()
        y_w = self.offset.y()
        self.move(x-x_w, y-y_w)
Beispiel #28
0
class QtApplication(QApplication, Application):
    pluginsLoaded = Signal()
    applicationRunning = Signal()

    def __init__(self, tray_icon_name: str = None, **kwargs) -> None:
        plugin_path = ""
        if sys.platform == "win32":
            if hasattr(sys, "frozen"):
                plugin_path = os.path.join(
                    os.path.dirname(os.path.abspath(sys.executable)), "PyQt5",
                    "plugins")
                Logger.log("i", "Adding QT5 plugin path: %s", plugin_path)
                QCoreApplication.addLibraryPath(plugin_path)
            else:
                import site
                for sitepackage_dir in site.getsitepackages():
                    QCoreApplication.addLibraryPath(
                        os.path.join(sitepackage_dir, "PyQt5", "plugins"))
        elif sys.platform == "darwin":
            plugin_path = os.path.join(self.getInstallPrefix(), "Resources",
                                       "plugins")

        if plugin_path:
            Logger.log("i", "Adding QT5 plugin path: %s", plugin_path)
            QCoreApplication.addLibraryPath(plugin_path)

        # use Qt Quick Scene Graph "basic" render loop
        os.environ["QSG_RENDER_LOOP"] = "basic"

        super().__init__(sys.argv, **kwargs)  # type: ignore

        self._qml_import_paths = []  #type: List[str]
        self._main_qml = "main.qml"  #type: str
        self._qml_engine = None  #type: Optional[QQmlApplicationEngine]
        self._main_window = None  #type: Optional[MainWindow]
        self._tray_icon_name = tray_icon_name  #type: Optional[str]
        self._tray_icon = None  #type: Optional[str]
        self._tray_icon_widget = None  #type: Optional[QSystemTrayIcon]
        self._theme = None  #type: Optional[Theme]
        self._renderer = None  #type: Optional[QtRenderer]

        self._job_queue = None  #type: Optional[JobQueue]
        self._version_upgrade_manager = None  #type: Optional[VersionUpgradeManager]

        self._is_shutting_down = False  #type: bool

        self._recent_files = []  #type: List[QUrl]

        self._configuration_error_message = None  #type: Optional[ConfigurationErrorMessage]

    def addCommandLineOptions(self) -> None:
        super().addCommandLineOptions()
        # This flag is used by QApplication. We don't process it.
        self._cli_parser.add_argument(
            "-qmljsdebugger", help="For Qt's QML debugger compatibility")

    def initialize(self) -> None:
        super().initialize()
        # Initialize the package manager to remove and install scheduled packages.
        self._package_manager = self._package_manager_class(self, parent=self)

        self._mesh_file_handler = MeshFileHandler(self)  #type: MeshFileHandler
        self._workspace_file_handler = WorkspaceFileHandler(
            self)  #type: WorkspaceFileHandler

        # Remove this and you will get Windows 95 style for all widgets if you are using Qt 5.10+
        self.setStyle("fusion")

        self.setAttribute(Qt.AA_UseDesktopOpenGL)
        major_version, minor_version, profile = OpenGLContext.detectBestOpenGLVersion(
        )

        if major_version is None or minor_version is None or profile is None:
            Logger.log(
                "e",
                "Startup failed because OpenGL version probing has failed: tried to create a 2.0 and 4.1 context. Exiting"
            )
            if not self.getIsHeadLess():
                QMessageBox.critical(
                    None, "Failed to probe OpenGL",
                    "Could not probe OpenGL. This program requires OpenGL 2.0 or higher. Please check your video card drivers."
                )
            sys.exit(1)
        else:
            opengl_version_str = OpenGLContext.versionAsText(
                major_version, minor_version, profile)
            Logger.log("d",
                       "Detected most suitable OpenGL context version: %s",
                       opengl_version_str)
        if not self.getIsHeadLess():
            OpenGLContext.setDefaultFormat(major_version,
                                           minor_version,
                                           profile=profile)

        self._qml_import_paths.append(
            os.path.join(os.path.dirname(sys.executable), "qml"))
        self._qml_import_paths.append(
            os.path.join(self.getInstallPrefix(), "Resources", "qml"))

        Logger.log("i", "Initializing job queue ...")
        self._job_queue = JobQueue()
        self._job_queue.jobFinished.connect(self._onJobFinished)

        Logger.log("i", "Initializing version upgrade manager ...")
        self._version_upgrade_manager = VersionUpgradeManager(self)

    def startSplashWindowPhase(self) -> None:
        super().startSplashWindowPhase()
        i18n_catalog = i18nCatalog("uranium")
        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress",
                               "Initializing package manager..."))
        self._package_manager.initialize()

        # Read preferences here (upgrade won't work) to get the language in use, so the splash window can be shown in
        # the correct language.
        try:
            preferences_filename = Resources.getPath(Resources.Preferences,
                                                     self._app_name + ".cfg")
            self._preferences.readFromFile(preferences_filename)
        except FileNotFoundError:
            Logger.log(
                "i",
                "Preferences file not found, ignore and use default language '%s'",
                self._default_language)

        signal.signal(signal.SIGINT, signal.SIG_DFL)
        # This is done here as a lot of plugins require a correct gl context. If you want to change the framework,
        # these checks need to be done in your <framework>Application.py class __init__().

        self._configuration_error_message = ConfigurationErrorMessage(
            self,
            i18n_catalog.i18nc("@info:status",
                               "Your configuration seems to be corrupt."),
            lifetime=0,
            title=i18n_catalog.i18nc("@info:title", "Configuration errors"))
        # Remove, install, and then loading plugins
        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress", "Loading plugins..."))
        # Remove and install the plugins that have been scheduled
        self._plugin_registry.initializeBeforePluginsAreLoaded()
        self._loadPlugins()
        self._plugin_registry.checkRequiredPlugins(self.getRequiredPlugins())
        self.pluginsLoaded.emit()

        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress", "Updating configuration..."))
        with self._container_registry.lockFile():
            VersionUpgradeManager.getInstance().upgrade()

        # Load preferences again because before we have loaded the plugins, we don't have the upgrade routine for
        # the preferences file. Now that we have, load the preferences file again so it can be upgraded and loaded.
        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress", "Loading preferences..."))
        try:
            preferences_filename = Resources.getPath(Resources.Preferences,
                                                     self._app_name + ".cfg")
            with open(preferences_filename, "r", encoding="utf-8") as f:
                serialized = f.read()
            # This performs the upgrade for Preferences
            self._preferences.deserialize(serialized)
            self._preferences.setValue("general/plugins_to_remove", "")
            self._preferences.writeToFile(preferences_filename)
        except (FileNotFoundError, UnicodeDecodeError):
            Logger.log(
                "i",
                "The preferences file cannot be found or it is corrupted, so we will use default values"
            )

        self.processEvents()
        # Force the configuration file to be written again since the list of plugins to remove maybe changed
        try:
            self._preferences_filename = Resources.getPath(
                Resources.Preferences, self._app_name + ".cfg")
            self._preferences.readFromFile(self._preferences_filename)
        except FileNotFoundError:
            Logger.log(
                "i",
                "The preferences file '%s' cannot be found, will use default values",
                self._preferences_filename)
            self._preferences_filename = Resources.getStoragePath(
                Resources.Preferences, self._app_name + ".cfg")

        # FIXME: This is done here because we now use "plugins.json" to manage plugins instead of the Preferences file,
        # but the PluginRegistry will still import data from the Preferences files if present, such as disabled plugins,
        # so we need to reset those values AFTER the Preferences file is loaded.
        self._plugin_registry.initializeAfterPluginsAreLoaded()

        # Check if we have just updated from an older version
        self._preferences.addPreference("general/last_run_version", "")
        last_run_version_str = self._preferences.getValue(
            "general/last_run_version")
        if not last_run_version_str:
            last_run_version_str = self._version
        last_run_version = Version(last_run_version_str)
        current_version = Version(self._version)
        if last_run_version < current_version:
            self._just_updated_from_old_version = True
        self._preferences.setValue("general/last_run_version",
                                   str(current_version))
        self._preferences.writeToFile(self._preferences_filename)

        # Preferences: recent files
        self._preferences.addPreference("%s/recent_files" % self._app_name, "")
        file_names = self._preferences.getValue("%s/recent_files" %
                                                self._app_name).split(";")
        for file_name in file_names:
            if not os.path.isfile(file_name):
                continue
            self._recent_files.append(QUrl.fromLocalFile(file_name))

        if not self.getIsHeadLess():
            # Initialize System tray icon and make it invisible because it is used only to show pop up messages
            self._tray_icon = None
            if self._tray_icon_name:
                self._tray_icon = QIcon(
                    Resources.getPath(Resources.Images, self._tray_icon_name))
                self._tray_icon_widget = QSystemTrayIcon(self._tray_icon)
                self._tray_icon_widget.setVisible(False)

    def initializeEngine(self) -> None:
        # TODO: Document native/qml import trickery
        self._qml_engine = QQmlApplicationEngine(self)
        self.processEvents()
        self._qml_engine.setOutputWarningsToStandardError(False)
        self._qml_engine.warnings.connect(self.__onQmlWarning)

        for path in self._qml_import_paths:
            self._qml_engine.addImportPath(path)

        if not hasattr(sys, "frozen"):
            self._qml_engine.addImportPath(
                os.path.join(os.path.dirname(__file__), "qml"))

        self._qml_engine.rootContext().setContextProperty(
            "QT_VERSION_STR", QT_VERSION_STR)
        self.processEvents()
        self._qml_engine.rootContext().setContextProperty(
            "screenScaleFactor", self._screenScaleFactor())

        self.registerObjects(self._qml_engine)

        Bindings.register()

        # Preload theme. The theme will be loaded on first use, which will incur a ~0.1s freeze on the MainThread.
        # Do it here, while the splash screen is shown. Also makes this freeze explicit and traceable.
        self.getTheme()
        self.processEvents()

        self.showSplashMessage(
            self._i18n_catalog.i18nc("@info:progress", "Loading UI..."))
        self._qml_engine.load(self._main_qml)
        self.engineCreatedSignal.emit()

    recentFilesChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify=recentFilesChanged)
    def recentFiles(self) -> List[QUrl]:
        return self._recent_files

    def _onJobFinished(self, job: Job) -> None:
        if isinstance(job, WriteFileJob):
            if not job.getResult() or not job.getAddToRecentFiles():
                # For a write file job, if it failed or it doesn't need to be added to the recent files list, we do not
                # add it.
                return
        elif (not isinstance(job, ReadMeshJob)
              and not isinstance(job, ReadFileJob)) or not job.getResult():
            return

        if isinstance(job, (ReadMeshJob, ReadFileJob, WriteFileJob)):
            self.addFileToRecentFiles(job.getFileName())

    def addFileToRecentFiles(self, file_name: str) -> None:
        file_path = QUrl.fromLocalFile(file_name)

        if file_path in self._recent_files:
            self._recent_files.remove(file_path)

        self._recent_files.insert(0, file_path)
        if len(self._recent_files) > 10:
            del self._recent_files[10]

        pref = ""
        for path in self._recent_files:
            pref += path.toLocalFile() + ";"

        self.getPreferences().setValue(
            "%s/recent_files" % self.getApplicationName(), pref)
        self.recentFilesChanged.emit()

    def run(self) -> None:
        super().run()

    def hideMessage(self, message: Message) -> None:
        with self._message_lock:
            if message in self._visible_messages:
                message.hide(
                    send_signal=False
                )  # we're in handling hideMessageSignal so we don't want to resend it
                self._visible_messages.remove(message)
                self.visibleMessageRemoved.emit(message)

    def showMessage(self, message: Message) -> None:
        with self._message_lock:
            if message not in self._visible_messages:
                self._visible_messages.append(message)
                message.setLifetimeTimer(QTimer())
                message.setInactivityTimer(QTimer())
                self.visibleMessageAdded.emit(message)

        # also show toast message when the main window is minimized
        self.showToastMessage(self._app_name, message.getText())

    def _onMainWindowStateChanged(self, window_state: int) -> None:
        if self._tray_icon and self._tray_icon_widget:
            visible = window_state == Qt.WindowMinimized
            self._tray_icon_widget.setVisible(visible)

    # Show toast message using System tray widget.
    def showToastMessage(self, title: str, message: str) -> None:
        if self.checkWindowMinimizedState() and self._tray_icon_widget:
            # NOTE: Qt 5.8 don't support custom icon for the system tray messages, but Qt 5.9 does.
            #       We should use the custom icon when we switch to Qt 5.9
            self._tray_icon_widget.showMessage(title, message)

    def setMainQml(self, path: str) -> None:
        self._main_qml = path

    def exec_(self, *args: Any, **kwargs: Any) -> None:
        self.applicationRunning.emit()
        super().exec_(*args, **kwargs)

    @pyqtSlot()
    def reloadQML(self) -> None:
        # only reload when it is a release build
        if not self.getIsDebugMode():
            return
        if self._qml_engine and self._theme:
            self._qml_engine.clearComponentCache()
            self._theme.reload()
            self._qml_engine.load(self._main_qml)
            # Hide the window. For some reason we can't close it yet. This needs to be done in the onComponentCompleted.
            for obj in self._qml_engine.rootObjects():
                if obj != self._qml_engine.rootObjects()[-1]:
                    obj.hide()

    @pyqtSlot()
    def purgeWindows(self) -> None:
        # Close all root objects except the last one.
        # Should only be called by onComponentCompleted of the mainWindow.
        if self._qml_engine:
            for obj in self._qml_engine.rootObjects():
                if obj != self._qml_engine.rootObjects()[-1]:
                    obj.close()

    @pyqtSlot("QList<QQmlError>")
    def __onQmlWarning(self, warnings: List[QQmlError]) -> None:
        for warning in warnings:
            Logger.log("w", warning.toString())

    engineCreatedSignal = Signal()

    def isShuttingDown(self) -> bool:
        return self._is_shutting_down

    def registerObjects(
        self, engine
    ) -> None:  #type: ignore #Don't type engine, because the type depends on the platform you're running on so it always gives an error somewhere.
        engine.rootContext().setContextProperty("PluginRegistry",
                                                PluginRegistry.getInstance())

    def getRenderer(self) -> QtRenderer:
        if not self._renderer:
            self._renderer = QtRenderer()

        return cast(QtRenderer, self._renderer)

    mainWindowChanged = Signal()

    def getMainWindow(self) -> Optional[MainWindow]:
        return self._main_window

    def setMainWindow(self, window: MainWindow) -> None:
        if window != self._main_window:
            if self._main_window is not None:
                self._main_window.windowStateChanged.disconnect(
                    self._onMainWindowStateChanged)

            self._main_window = window
            if self._main_window is not None:
                self._main_window.windowStateChanged.connect(
                    self._onMainWindowStateChanged)
            self.mainWindowChanged.emit()

    def setVisible(self, visible: bool) -> None:
        if self._main_window is not None:
            self._main_window.visible = visible

    @property
    def isVisible(self) -> bool:
        if self._main_window is not None:
            return self._main_window.visible  #type: ignore #MyPy doesn't realise that self._main_window cannot be None here.
        return False

    def getTheme(self) -> Optional[Theme]:
        if self._theme is None:
            if self._qml_engine is None:
                Logger.log(
                    "e",
                    "The theme cannot be accessed before the engine is initialised"
                )
                return None

            self._theme = UM.Qt.Bindings.Theme.Theme.getInstance(
                self._qml_engine)
        return self._theme

    #   Handle a function that should be called later.
    def functionEvent(self, event: QEvent) -> None:
        e = _QtFunctionEvent(event)
        QCoreApplication.postEvent(self, e)

    #   Handle Qt events
    def event(self, event: QEvent) -> bool:
        if event.type() == _QtFunctionEvent.QtFunctionEvent:
            event._function_event.call()
            return True

        return super().event(event)

    def windowClosed(self, save_data: bool = True) -> None:
        Logger.log("d", "Shutting down %s", self.getApplicationName())
        self._is_shutting_down = True

        # garbage collect tray icon so it gets properly closed before the application is closed
        self._tray_icon_widget = None

        if save_data:
            try:
                self.savePreferences()
            except Exception as e:
                Logger.log("e", "Exception while saving preferences: %s",
                           repr(e))

        try:
            self.applicationShuttingDown.emit()
        except Exception as e:
            Logger.log("e", "Exception while emitting shutdown signal: %s",
                       repr(e))

        try:
            self.getBackend().close()
        except Exception as e:
            Logger.log("e", "Exception while closing backend: %s", repr(e))

        if self._tray_icon_widget:
            self._tray_icon_widget.deleteLater()

        self.quit()

    def checkWindowMinimizedState(self) -> bool:
        if self._main_window is not None and self._main_window.windowState(
        ) == Qt.WindowMinimized:
            return True
        else:
            return False

    ##  Get the backend of the application (the program that does the heavy lifting).
    #   The backend is also a QObject, which can be used from qml.
    @pyqtSlot(result="QObject*")
    def getBackend(self) -> Backend:
        return self._backend

    ##  Property used to expose the backend
    #   It is made static as the backend is not supposed to change during runtime.
    #   This makes the connection between backend and QML more reliable than the pyqtSlot above.
    #   \returns Backend \type{Backend}
    @pyqtProperty("QVariant", constant=True)
    def backend(self) -> Backend:
        return self.getBackend()

    ## Create a class variable so we can manage the splash in the CrashHandler dialog when the Application instance
    # is not yet created, e.g. when an error occurs during the initialization
    splash = None  # type: Optional[QSplashScreen]

    def createSplash(self) -> None:
        if not self.getIsHeadLess():
            try:
                QtApplication.splash = self._createSplashScreen()
            except FileNotFoundError:
                QtApplication.splash = None
            else:
                if QtApplication.splash:
                    QtApplication.splash.show()
                    self.processEvents()

    ##  Display text on the splash screen.
    def showSplashMessage(self, message: str) -> None:
        if not QtApplication.splash:
            self.createSplash()

        if QtApplication.splash:
            self.processEvents(
            )  # Process events from previous loading phase before updating the message
            QtApplication.splash.showMessage(
                message,
                Qt.AlignHCenter | Qt.AlignVCenter)  # Now update the message
            self.processEvents()  # And make sure it is immediately visible
        elif self.getIsHeadLess():
            Logger.log("d", message)

    ##  Close the splash screen after the application has started.
    def closeSplash(self) -> None:
        if QtApplication.splash:
            QtApplication.splash.close()
            QtApplication.splash = None

    ## Create a QML component from a qml file.
    #  \param qml_file_path: The absolute file path to the root qml file.
    #  \param context_properties: Optional dictionary containing the properties that will be set on the context of the
    #                              qml instance before creation.
    #  \return None in case the creation failed (qml error), else it returns the qml instance.
    #  \note If the creation fails, this function will ensure any errors are logged to the logging service.
    def createQmlComponent(
        self,
        qml_file_path: str,
        context_properties: Dict[str,
                                 "QObject"] = None) -> Optional["QObject"]:
        if self._qml_engine is None:  # Protect in case the engine was not initialized yet
            return None
        path = QUrl.fromLocalFile(qml_file_path)
        component = QQmlComponent(self._qml_engine, path)
        result_context = QQmlContext(
            self._qml_engine.rootContext()
        )  #type: ignore #MyPy doens't realise that self._qml_engine can't be None here.
        if context_properties is not None:
            for name, value in context_properties.items():
                result_context.setContextProperty(name, value)
        result = component.create(result_context)
        for err in component.errors():
            Logger.log("e", str(err.toString()))
        if result is None:
            return None

        # We need to store the context with the qml object, else the context gets garbage collected and the qml objects
        # no longer function correctly/application crashes.
        result.attached_context = result_context
        return result

    ##  Delete all nodes containing mesh data in the scene.
    #   \param only_selectable. Set this to False to delete objects from all build plates
    @pyqtSlot()
    def deleteAll(self, only_selectable=True) -> None:
        self.getController().deleteAllNodesWithMeshData(only_selectable)

    ##  Get the MeshFileHandler of this application.
    def getMeshFileHandler(self) -> MeshFileHandler:
        return self._mesh_file_handler

    def getWorkspaceFileHandler(self) -> WorkspaceFileHandler:
        return self._workspace_file_handler

    @pyqtSlot(result=QObject)
    def getPackageManager(self) -> PackageManager:
        return self._package_manager

    ##  Gets the instance of this application.
    #
    #   This is just to further specify the type of Application.getInstance().
    #   \return The instance of this application.
    @classmethod
    def getInstance(cls, *args, **kwargs) -> "QtApplication":
        return cast(QtApplication, super().getInstance(**kwargs))

    def _createSplashScreen(self) -> QSplashScreen:
        return QSplashScreen(
            QPixmap(
                Resources.getPath(Resources.Images,
                                  self.getApplicationName() + ".png")))

    def _screenScaleFactor(self) -> float:
        # OSX handles sizes of dialogs behind our backs, but other platforms need
        # to know about the device pixel ratio
        if sys.platform == "darwin":
            return 1.0
        else:
            # determine a device pixel ratio from font metrics, using the same logic as UM.Theme
            fontPixelRatio = QFontMetrics(
                QCoreApplication.instance().font()).ascent() / 11
            # round the font pixel ratio to quarters
            fontPixelRatio = int(fontPixelRatio * 4) / 4
            return fontPixelRatio

    @pyqtProperty(str, constant=True)
    def applicationDisplayName(self) -> str:
        return self.getApplicationDisplayName()
Beispiel #29
0
class Parse99(QApplication):

    def __init__(self, *args):
        super(QApplication, self).__init__(*args)

        # Tray Icon
        self._system_tray = QSystemTrayIcon()
        self._system_tray.setIcon(QIcon('ui/icon.png'))
        self._system_tray.setToolTip("Parse99")
        self._system_tray.show()

        # Settings
        self.settings = settings.Settings("parse99")

        # Plugins
        self._plugins = {'maps': Maps(self.settings)}

        # Timer
        self._timer = QTimer()
        self._timer.timeout.connect(self._parse)

        # Thread
        self._thread = None

        # Menu
        self._system_tray.setContextMenu(self._get_menu())

        # File
        self._log_file = ""
        self._file_size = 0
        self._last_line_read = 0
        self._log_new_lines = []

        # Start
        self.toggle('on')

    def _settings_valid(self):
        valid = True
        if self.settings.get_value('general', 'first_run') is None:
            self._system_tray.showMessage(
                "Parse99",
                """It looks like this is the first time the program is being
                run. Please setup the application using the Settings option
                once you right click the system tray icon."""
            )
            self.edit_settings()
            self.settings.set_value('general', 'first_run', True)
            valid = False
        elif self.settings.get_value('general', 'eq_directory') is None:
            self._system_tray.showMessage(
                "Parse99",
                "Please enter the General settings and \
                choose the location of your Everquest Installation."
            )
            self.edit_settings()
            valid = False
        elif self.settings.get_value('characters', None) is None:
            self._system_tray.showMessage(
                "Parse99",
                "No characters have been made.  \
                Please create at least one character using settings."
            )
            self.edit_settings(tab="characters")
            valid = False
        elif self.settings.get_value('general', 'current_character') is None:
            self._system_tray.showMessage(
                "Parse99",
                "No character has been selected. \
                Please choose a character from the Character menu."
            )
            valid = False
        return valid

    def toggle(self, switch):
        if switch == 'off':
            if self._thread is not None:
                self._timer.stop()
                self._thread.stop()
                self._thread.join()
        elif switch == 'on':
            if self._settings_valid():
                characters = self.settings.get_value('characters', None)
                log_file = characters[
                    self.settings.get_value('general', 'current_character')
                ]['log_file']
                self._thread = FileReader(
                    log_file,
                    int(self.settings.get_value('general', 'parse_interval'))
                )
                self._thread.start()
                self._timer.start(
                    1000 *
                    int(self.settings.get_value('general', 'parse_interval'))
                )

    def _parse(self):
        for line in self._thread.get_new_lines():
            for plugin in self._plugins.keys():
                if self._plugins[plugin].is_active():
                    self._plugins[plugin].parse(line)

    def _get_menu(self):
        # main menu
        menu = QMenu()
        main_menu_action_group = QActionGroup(menu)
        main_menu_action_group.setObjectName("main")
        # character menu
        map_action = QAction(menu)
        map_action.setText("Toggle Map")
        main_menu_action_group.addAction(map_action)
        separator = QAction(menu)
        separator.setSeparator(True)
        main_menu_action_group.addAction(separator)
        characters_action = QAction(menu)
        characters_action.setText("Switch Characters")
        main_menu_action_group.addAction(characters_action)
        separator = QAction(menu)
        separator.setSeparator(True)
        main_menu_action_group.addAction(separator)
        settings_action = QAction(menu)
        settings_action.setText("Settings")
        main_menu_action_group.addAction(settings_action)
        quit_action = QAction(menu)
        quit_action.setText("Quit")
        main_menu_action_group.addAction(quit_action)
        menu.addActions(main_menu_action_group.actions())
        menu.triggered[QAction].connect(self._menu_actions)
        return menu

    def update_menu(self):
        self._system_tray.contextMenu().disconnect()
        self._system_tray.setContextMenu(self._get_menu())

    def _menu_actions(self, action):
        # ag = action group, at = action text
        ag = action.actionGroup().objectName()
        at = action.text().lower()
        if ag == "main":
            if at == "quit":
                try:
                    self.toggle('off')
                    self._system_tray.setVisible(False)
                    for plugin in self._plugins.keys():
                        self._plugins[plugin].close()
                    self.quit()
                except Exception as e:
                    print("menu actions, quit", e)
            elif at == "settings":
                self.edit_settings(tab="general")
            elif at == "switch characters":
                print("switch characters")
                self.edit_settings(tab="characters")
            elif at == "toggle map":
                self._plugins['maps'].toggle()

    def edit_settings(self, **kwargs):
        try:
            if not self.settings.gui.isVisible():
                self.toggle('off')
                self.settings.gui.set_show_tab(kwargs.get('tab', None))
                self.settings.gui.exec()
                self.toggle('on')
        except Exception as e:
            print("parse99.edit_settings():", e)
Beispiel #30
0
class QtApplication(QApplication, Application):
    pluginsLoaded = Signal()
    applicationRunning = Signal()
    
    def __init__(self, tray_icon_name: str = None, **kwargs) -> None:
        plugin_path = ""
        if sys.platform == "win32":
            if hasattr(sys, "frozen"):
                plugin_path = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "PyQt5", "plugins")
                Logger.log("i", "Adding QT5 plugin path: %s", plugin_path)
                QCoreApplication.addLibraryPath(plugin_path)
            else:
                import site
                for sitepackage_dir in site.getsitepackages():
                    QCoreApplication.addLibraryPath(os.path.join(sitepackage_dir, "PyQt5", "plugins"))
        elif sys.platform == "darwin":
            plugin_path = os.path.join(self.getInstallPrefix(), "Resources", "plugins")

        if plugin_path:
            Logger.log("i", "Adding QT5 plugin path: %s", plugin_path)
            QCoreApplication.addLibraryPath(plugin_path)

        # use Qt Quick Scene Graph "basic" render loop
        os.environ["QSG_RENDER_LOOP"] = "basic"

        super().__init__(sys.argv, **kwargs) # type: ignore

        self._qml_import_paths = [] #type: List[str]
        self._main_qml = "main.qml" #type: str
        self._qml_engine = None #type: Optional[QQmlApplicationEngine]
        self._main_window = None #type: Optional[MainWindow]
        self._tray_icon_name = tray_icon_name #type: Optional[str]
        self._tray_icon = None #type: Optional[str]
        self._tray_icon_widget = None #type: Optional[QSystemTrayIcon]
        self._theme = None #type: Optional[Theme]
        self._renderer = None #type: Optional[QtRenderer]

        self._job_queue = None #type: Optional[JobQueue]
        self._version_upgrade_manager = None #type: Optional[VersionUpgradeManager]

        self._is_shutting_down = False #type: bool

        self._recent_files = [] #type: List[QUrl]

        self._configuration_error_message = None #type: Optional[ConfigurationErrorMessage]

    def addCommandLineOptions(self) -> None:
        super().addCommandLineOptions()
        # This flag is used by QApplication. We don't process it.
        self._cli_parser.add_argument("-qmljsdebugger",
                                      help = "For Qt's QML debugger compatibility")

    def initialize(self) -> None:
        super().initialize()
        # Initialize the package manager to remove and install scheduled packages.
        self._package_manager = self._package_manager_class(self, parent = self)

        self._mesh_file_handler = MeshFileHandler(self) #type: MeshFileHandler
        self._workspace_file_handler = WorkspaceFileHandler(self) #type: WorkspaceFileHandler

        # Remove this and you will get Windows 95 style for all widgets if you are using Qt 5.10+
        self.setStyle("fusion")

        self.setAttribute(Qt.AA_UseDesktopOpenGL)
        major_version, minor_version, profile = OpenGLContext.detectBestOpenGLVersion()

        if major_version is None and minor_version is None and profile is None and not self.getIsHeadLess():
            Logger.log("e", "Startup failed because OpenGL version probing has failed: tried to create a 2.0 and 4.1 context. Exiting")
            QMessageBox.critical(None, "Failed to probe OpenGL",
                                 "Could not probe OpenGL. This program requires OpenGL 2.0 or higher. Please check your video card drivers.")
            sys.exit(1)
        else:
            opengl_version_str = OpenGLContext.versionAsText(major_version, minor_version, profile)
            Logger.log("d", "Detected most suitable OpenGL context version: %s", opengl_version_str)
        if not self.getIsHeadLess():
            OpenGLContext.setDefaultFormat(major_version, minor_version, profile = profile)

        self._qml_import_paths.append(os.path.join(os.path.dirname(sys.executable), "qml"))
        self._qml_import_paths.append(os.path.join(self.getInstallPrefix(), "Resources", "qml"))

        Logger.log("i", "Initializing job queue ...")
        self._job_queue = JobQueue()
        self._job_queue.jobFinished.connect(self._onJobFinished)

        Logger.log("i", "Initializing version upgrade manager ...")
        self._version_upgrade_manager = VersionUpgradeManager(self)

    def startSplashWindowPhase(self) -> None:
        super().startSplashWindowPhase()

        self._package_manager.initialize()

        # Read preferences here (upgrade won't work) to get the language in use, so the splash window can be shown in
        # the correct language.
        try:
            preferences_filename = Resources.getPath(Resources.Preferences, self._app_name + ".cfg")
            self._preferences.readFromFile(preferences_filename)
        except FileNotFoundError:
            Logger.log("i", "Preferences file not found, ignore and use default language '%s'", self._default_language)

        signal.signal(signal.SIGINT, signal.SIG_DFL)
        # This is done here as a lot of plugins require a correct gl context. If you want to change the framework,
        # these checks need to be done in your <framework>Application.py class __init__().

        i18n_catalog = i18nCatalog("uranium")

        self._configuration_error_message = ConfigurationErrorMessage(self,
              i18n_catalog.i18nc("@info:status", "Your configuration seems to be corrupt."),
              lifetime = 0,
              title = i18n_catalog.i18nc("@info:title", "Configuration errors")
              )
        # Remove, install, and then loading plugins
        self.showSplashMessage(i18n_catalog.i18nc("@info:progress", "Loading plugins..."))
        # Remove and install the plugins that have been scheduled
        self._plugin_registry.initializeBeforePluginsAreLoaded()
        self._loadPlugins()
        self._plugin_registry.checkRequiredPlugins(self.getRequiredPlugins())
        self.pluginsLoaded.emit()

        self.showSplashMessage(i18n_catalog.i18nc("@info:progress", "Updating configuration..."))
        with self._container_registry.lockFile():
            VersionUpgradeManager.getInstance().upgrade()

        # Load preferences again because before we have loaded the plugins, we don't have the upgrade routine for
        # the preferences file. Now that we have, load the preferences file again so it can be upgraded and loaded.
        try:
            preferences_filename = Resources.getPath(Resources.Preferences, self._app_name + ".cfg")
            with open(preferences_filename, "r", encoding = "utf-8") as f:
                serialized = f.read()
            # This performs the upgrade for Preferences
            self._preferences.deserialize(serialized)
            self._preferences.setValue("general/plugins_to_remove", "")
            self._preferences.writeToFile(preferences_filename)
        except FileNotFoundError:
            Logger.log("i", "The preferences file cannot be found, will use default values")

        # Force the configuration file to be written again since the list of plugins to remove maybe changed
        self.showSplashMessage(i18n_catalog.i18nc("@info:progress", "Loading preferences..."))
        try:
            self._preferences_filename = Resources.getPath(Resources.Preferences, self._app_name + ".cfg")
            self._preferences.readFromFile(self._preferences_filename)
        except FileNotFoundError:
            Logger.log("i", "The preferences file '%s' cannot be found, will use default values",
                       self._preferences_filename)
            self._preferences_filename = Resources.getStoragePath(Resources.Preferences, self._app_name + ".cfg")

        # FIXME: This is done here because we now use "plugins.json" to manage plugins instead of the Preferences file,
        # but the PluginRegistry will still import data from the Preferences files if present, such as disabled plugins,
        # so we need to reset those values AFTER the Preferences file is loaded.
        self._plugin_registry.initializeAfterPluginsAreLoaded()

        # Check if we have just updated from an older version
        self._preferences.addPreference("general/last_run_version", "")
        last_run_version_str = self._preferences.getValue("general/last_run_version")
        if not last_run_version_str:
            last_run_version_str = self._version
        last_run_version = Version(last_run_version_str)
        current_version = Version(self._version)
        if last_run_version < current_version:
            self._just_updated_from_old_version = True
        self._preferences.setValue("general/last_run_version", str(current_version))
        self._preferences.writeToFile(self._preferences_filename)

        # Preferences: recent files
        self._preferences.addPreference("%s/recent_files" % self._app_name, "")
        file_names = self._preferences.getValue("%s/recent_files" % self._app_name).split(";")
        for file_name in file_names:
            if not os.path.isfile(file_name):
                continue
            self._recent_files.append(QUrl.fromLocalFile(file_name))

        if not self.getIsHeadLess():
            # Initialize System tray icon and make it invisible because it is used only to show pop up messages
            self._tray_icon = None
            if self._tray_icon_name:
                self._tray_icon = QIcon(Resources.getPath(Resources.Images, self._tray_icon_name))
                self._tray_icon_widget = QSystemTrayIcon(self._tray_icon)
                self._tray_icon_widget.setVisible(False)

    def initializeEngine(self) -> None:
        # TODO: Document native/qml import trickery
        self._qml_engine = QQmlApplicationEngine(self)
        self._qml_engine.setOutputWarningsToStandardError(False)
        self._qml_engine.warnings.connect(self.__onQmlWarning)

        for path in self._qml_import_paths:
            self._qml_engine.addImportPath(path)

        if not hasattr(sys, "frozen"):
            self._qml_engine.addImportPath(os.path.join(os.path.dirname(__file__), "qml"))

        self._qml_engine.rootContext().setContextProperty("QT_VERSION_STR", QT_VERSION_STR)
        self._qml_engine.rootContext().setContextProperty("screenScaleFactor", self._screenScaleFactor())

        self.registerObjects(self._qml_engine)

        Bindings.register()
        self._qml_engine.load(self._main_qml)
        self.engineCreatedSignal.emit()

    recentFilesChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify=recentFilesChanged)
    def recentFiles(self) -> List[QUrl]:
        return self._recent_files

    def _onJobFinished(self, job: Job) -> None:
        if isinstance(job, WriteFileJob):
            if not job.getResult() or not job.getAddToRecentFiles():
                # For a write file job, if it failed or it doesn't need to be added to the recent files list, we do not
                # add it.
                return
        elif (not isinstance(job, ReadMeshJob) and not isinstance(job, ReadFileJob)) or not job.getResult():
            return

        if isinstance(job, (ReadMeshJob, ReadFileJob, WriteFileJob)):
            self.addFileToRecentFiles(job.getFileName())

    def addFileToRecentFiles(self, file_name: str) -> None:
        file_path = QUrl.fromLocalFile(file_name)

        if file_path in self._recent_files:
            self._recent_files.remove(file_path)

        self._recent_files.insert(0, file_path)
        if len(self._recent_files) > 10:
            del self._recent_files[10]

        pref = ""
        for path in self._recent_files:
            pref += path.toLocalFile() + ";"

        self.getPreferences().setValue("%s/recent_files" % self.getApplicationName(), pref)
        self.recentFilesChanged.emit()

    def run(self) -> None:
        super().run()

    def hideMessage(self, message: Message) -> None:
        with self._message_lock:
            if message in self._visible_messages:
                message.hide(send_signal = False)  # we're in handling hideMessageSignal so we don't want to resend it
                self._visible_messages.remove(message)
                self.visibleMessageRemoved.emit(message)

    def showMessage(self, message: Message) -> None:
        with self._message_lock:
            if message not in self._visible_messages:
                self._visible_messages.append(message)
                message.setLifetimeTimer(QTimer())
                message.setInactivityTimer(QTimer())
                self.visibleMessageAdded.emit(message)

        # also show toast message when the main window is minimized
        self.showToastMessage(self._app_name, message.getText())

    def _onMainWindowStateChanged(self, window_state: int) -> None:
        if self._tray_icon and self._tray_icon_widget:
            visible = window_state == Qt.WindowMinimized
            self._tray_icon_widget.setVisible(visible)

    # Show toast message using System tray widget.
    def showToastMessage(self, title: str, message: str) -> None:
        if self.checkWindowMinimizedState() and self._tray_icon_widget:
            # NOTE: Qt 5.8 don't support custom icon for the system tray messages, but Qt 5.9 does.
            #       We should use the custom icon when we switch to Qt 5.9
            self._tray_icon_widget.showMessage(title, message)

    def setMainQml(self, path: str) -> None:
        self._main_qml = path

    def exec_(self, *args: Any, **kwargs: Any) -> None:
        self.applicationRunning.emit()
        super().exec_(*args, **kwargs)
        
    @pyqtSlot()
    def reloadQML(self) -> None:
        # only reload when it is a release build
        if not self.getIsDebugMode():
            return
        if self._qml_engine and self._theme:
            self._qml_engine.clearComponentCache()
            self._theme.reload()
            self._qml_engine.load(self._main_qml)
            # Hide the window. For some reason we can't close it yet. This needs to be done in the onComponentCompleted.
            for obj in self._qml_engine.rootObjects():
                if obj != self._qml_engine.rootObjects()[-1]:
                    obj.hide()

    @pyqtSlot()
    def purgeWindows(self) -> None:
        # Close all root objects except the last one.
        # Should only be called by onComponentCompleted of the mainWindow.
        if self._qml_engine:
            for obj in self._qml_engine.rootObjects():
                if obj != self._qml_engine.rootObjects()[-1]:
                    obj.close()

    @pyqtSlot("QList<QQmlError>")
    def __onQmlWarning(self, warnings: List[QQmlError]) -> None:
        for warning in warnings:
            Logger.log("w", warning.toString())

    engineCreatedSignal = Signal()

    def isShuttingDown(self) -> bool:
        return self._is_shutting_down

    def registerObjects(self, engine) -> None: #type: ignore #Don't type engine, because the type depends on the platform you're running on so it always gives an error somewhere.
        engine.rootContext().setContextProperty("PluginRegistry", PluginRegistry.getInstance())

    def getRenderer(self) -> QtRenderer:
        if not self._renderer:
            self._renderer = QtRenderer()

        return cast(QtRenderer, self._renderer)

    mainWindowChanged = Signal()

    def getMainWindow(self) -> Optional[MainWindow]:
        return self._main_window

    def setMainWindow(self, window: MainWindow) -> None:
        if window != self._main_window:
            if self._main_window is not None:
                self._main_window.windowStateChanged.disconnect(self._onMainWindowStateChanged)

            self._main_window = window
            if self._main_window is not None:
                self._main_window.windowStateChanged.connect(self._onMainWindowStateChanged)

            self.mainWindowChanged.emit()

    def setVisible(self, visible: bool) -> None:
        if self._main_window is not None:
            self._main_window.visible = visible

    @property
    def isVisible(self) -> bool:
        if self._main_window is not None:
            return self._main_window.visible #type: ignore #MyPy doesn't realise that self._main_window cannot be None here.
        return False

    def getTheme(self) -> Optional[Theme]:
        if self._theme is None:
            if self._qml_engine is None:
                Logger.log("e", "The theme cannot be accessed before the engine is initialised")
                return None

            self._theme = UM.Qt.Bindings.Theme.Theme.getInstance(self._qml_engine)
        return self._theme

    #   Handle a function that should be called later.
    def functionEvent(self, event: QEvent) -> None:
        e = _QtFunctionEvent(event)
        QCoreApplication.postEvent(self, e)

    #   Handle Qt events
    def event(self, event: QEvent) -> bool:
        if event.type() == _QtFunctionEvent.QtFunctionEvent:
            event._function_event.call()
            return True

        return super().event(event)

    def windowClosed(self, save_data: bool = True) -> None:
        Logger.log("d", "Shutting down %s", self.getApplicationName())
        self._is_shutting_down = True

        # garbage collect tray icon so it gets properly closed before the application is closed
        self._tray_icon_widget = None

        if save_data:
            try:
                self.savePreferences()
            except Exception as e:
                Logger.log("e", "Exception while saving preferences: %s", repr(e))

        try:
            self.applicationShuttingDown.emit()
        except Exception as e:
            Logger.log("e", "Exception while emitting shutdown signal: %s", repr(e))

        try:
            self.getBackend().close()
        except Exception as e:
            Logger.log("e", "Exception while closing backend: %s", repr(e))

        if self._tray_icon_widget:
            self._tray_icon_widget.deleteLater()

        self.quit()

    def checkWindowMinimizedState(self) -> bool:
        if self._main_window is not None and self._main_window.windowState() == Qt.WindowMinimized:
            return True
        else:
            return False

    ##  Get the backend of the application (the program that does the heavy lifting).
    #   The backend is also a QObject, which can be used from qml.
    @pyqtSlot(result = "QObject*")
    def getBackend(self) -> Backend:
        return self._backend

    ##  Property used to expose the backend
    #   It is made static as the backend is not supposed to change during runtime.
    #   This makes the connection between backend and QML more reliable than the pyqtSlot above.
    #   \returns Backend \type{Backend}
    @pyqtProperty("QVariant", constant = True)
    def backend(self) -> Backend:
        return self.getBackend()

    ## Create a class variable so we can manage the splash in the CrashHandler dialog when the Application instance
    # is not yet created, e.g. when an error occurs during the initialization
    splash = None  # type: Optional[QSplashScreen]

    def createSplash(self) -> None:
        if not self.getIsHeadLess():
            try:
                QtApplication.splash = self._createSplashScreen()
            except FileNotFoundError:
                QtApplication.splash = None
            else:
                if QtApplication.splash:
                    QtApplication.splash.show()
                    self.processEvents()

    ##  Display text on the splash screen.
    def showSplashMessage(self, message: str) -> None:
        if not QtApplication.splash:
            self.createSplash()
        
        if QtApplication.splash:
            QtApplication.splash.showMessage(message, Qt.AlignHCenter | Qt.AlignVCenter)
            self.processEvents()
        elif self.getIsHeadLess():
            Logger.log("d", message)

    ##  Close the splash screen after the application has started.
    def closeSplash(self) -> None:
        if QtApplication.splash:
            QtApplication.splash.close()
            QtApplication.splash = None

    ## Create a QML component from a qml file.
    #  \param qml_file_path: The absolute file path to the root qml file.
    #  \param context_properties: Optional dictionary containing the properties that will be set on the context of the
    #                              qml instance before creation.
    #  \return None in case the creation failed (qml error), else it returns the qml instance.
    #  \note If the creation fails, this function will ensure any errors are logged to the logging service.
    def createQmlComponent(self, qml_file_path: str, context_properties: Dict[str, "QObject"] = None) -> Optional["QObject"]:
        if self._qml_engine is None: # Protect in case the engine was not initialized yet
            return None
        path = QUrl.fromLocalFile(qml_file_path)
        component = QQmlComponent(self._qml_engine, path)
        result_context = QQmlContext(self._qml_engine.rootContext()) #type: ignore #MyPy doens't realise that self._qml_engine can't be None here.
        if context_properties is not None:
            for name, value in context_properties.items():
                result_context.setContextProperty(name, value)
        result = component.create(result_context)
        for err in component.errors():
            Logger.log("e", str(err.toString()))
        if result is None:
            return None

        # We need to store the context with the qml object, else the context gets garbage collected and the qml objects
        # no longer function correctly/application crashes.
        result.attached_context = result_context
        return result

    ##  Delete all nodes containing mesh data in the scene.
    #   \param only_selectable. Set this to False to delete objects from all build plates
    @pyqtSlot()
    def deleteAll(self, only_selectable = True) -> None:
        Logger.log("i", "Clearing scene")
        if not self.getController().getToolsEnabled():
            return

        nodes = []
        for node in DepthFirstIterator(self.getController().getScene().getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
            if not isinstance(node, SceneNode):
                continue
            if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
                continue  # Node that doesnt have a mesh and is not a group.
            if only_selectable and not node.isSelectable():
                continue
            if not node.callDecoration("isSliceable") and not node.callDecoration("getLayerData") and not node.callDecoration("isGroup"):
                continue  # Only remove nodes that are selectable.
            if node.getParent() and cast(SceneNode, node.getParent()).callDecoration("isGroup"):
                continue  # Grouped nodes don't need resetting as their parent (the group) is resetted)
            nodes.append(node)
        if nodes:
            op = GroupedOperation()

            for node in nodes:
                op.addOperation(RemoveSceneNodeOperation(node))

                # Reset the print information
                self.getController().getScene().sceneChanged.emit(node)

            op.push()
            Selection.clear()

    ##  Get the MeshFileHandler of this application.
    def getMeshFileHandler(self) -> MeshFileHandler:
        return self._mesh_file_handler

    def getWorkspaceFileHandler(self) -> WorkspaceFileHandler:
        return self._workspace_file_handler

    @pyqtSlot(result = QObject)
    def getPackageManager(self) -> PackageManager:
        return self._package_manager

    ##  Gets the instance of this application.
    #
    #   This is just to further specify the type of Application.getInstance().
    #   \return The instance of this application.
    @classmethod
    def getInstance(cls, *args, **kwargs) -> "QtApplication":
        return cast(QtApplication, super().getInstance(**kwargs))

    def _createSplashScreen(self) -> QSplashScreen:
        return QSplashScreen(QPixmap(Resources.getPath(Resources.Images, self.getApplicationName() + ".png")))

    def _screenScaleFactor(self) -> float:
        # OSX handles sizes of dialogs behind our backs, but other platforms need
        # to know about the device pixel ratio
        if sys.platform == "darwin":
            return 1.0
        else:
            # determine a device pixel ratio from font metrics, using the same logic as UM.Theme
            fontPixelRatio = QFontMetrics(QCoreApplication.instance().font()).ascent() / 11
            # round the font pixel ratio to quarters
            fontPixelRatio = int(fontPixelRatio * 4) / 4
            return fontPixelRatio
Beispiel #31
0
def main():
    exitcode = 0
    try:
        menu = QMenu()
        # Proxy
        m_proxy = QAction("Proxy: Disabled")
        m_proxy.setShortcut('Ctrl+P')
        m_proxy.setCheckable(True)
        m_proxy.setDisabled(True)
        m_proxy.triggered.connect(lambda: current['proxy'].disable(m_proxy))
        menu.addAction(m_proxy)
        proxy_dict = {}
        proxy_group = QActionGroup(menu)
        for proxy in profile.get_items('Proxy'):
            proxyname = proxy[0]
            proxy_dict[proxyname] = Proxy(proxy, m_proxy)
            proxy_group.addAction(proxy_dict[proxyname].QAction)
            menu.addAction(proxy_dict[proxyname].QAction)

        # Bypass
        menu.addSeparator()
        m_bypass = QAction("Bypass: Disabled")
        m_bypass.setShortcut('Ctrl+B')
        m_bypass.setCheckable(True)
        m_bypass.setDisabled(True)
        m_bypass.triggered.connect(lambda: current['bypass'].disable(m_bypass))
        menu.addAction(m_bypass)
        bypass_dict = {}
        bypass_group = QActionGroup(menu)
        for bypass in profile.get_items('Bypass'):
            bypassname = bypass[0]
            bypass_dict[bypassname] = Bypass(bypass, m_bypass)
            bypass_group.addAction(bypass_dict[bypassname].QAction)
            menu.addAction(bypass_dict[bypassname].QAction)

        # Capture
        menu.addSeparator()
        m_capture = QAction("Capture: Disabled")
        m_capture.setShortcut('Ctrl+C')
        m_capture.setCheckable(True)
        m_capture.setDisabled(True)
        m_dashboard = QAction("Open Dashboard...")
        m_dashboard.setShortcut('Ctrl+D')
        m_dashboard.setDisabled(True)
        m_capture.triggered.connect(lambda: current['capture'].disable(m_capture, m_dashboard))
        menu.addAction(m_capture)
        capture_dict = {}
        capture_group = QActionGroup(menu)
        for capture in profile.get_items('Capture'):
            capturename = capture[0]
            capture_dict[capturename] = Capture(capture, m_capture, m_dashboard)
            capture_group.addAction(capture_dict[capturename].QAction)
            menu.addAction(capture_dict[capturename].QAction)
        menu.addAction(m_dashboard)

        # Common
        m_log = QAction("Log Folder")
        m_log.setShortcut('Ctrl+L')
        m_log.triggered.connect(lambda: subprocess.call(["open", log_path]))
        m_profile = QAction("Profile Folder")
        m_profile.setShortcut('Ctrl+F')
        m_profile.triggered.connect(lambda: subprocess.call(["open", profile_path]))
        m_extension = QAction("Extension Folder")
        m_extension.setShortcut('Ctrl+E')
        m_extension.triggered.connect(lambda: subprocess.call(["open", ext_path]))
        m_copy_shell = QAction("Copy Shell Command")
        m_copy_shell.setShortcut('Ctrl+S')
        m_set_system = QAction("As System Proxy: " + user_port)
        m_set_system.setShortcut('Ctrl+A')
        m_set_system.triggered.connect(lambda: setproxy_menu(m_set_system))
        m_copy_shell.triggered.connect(copy_shell)
        m_set_system.setCheckable(True)
        if system:
            m_set_system.setChecked(True)
            setproxy()
        menu.addSeparator()
        menu.addAction(m_set_system)
        menu.addSeparator()
        menu.addAction(m_log)
        menu.addAction(m_profile)
        menu.addAction(m_extension)
        menu.addAction(m_copy_shell)
        menu.addSeparator()
        m_quit = QAction('Quit V2Net (' + VERSION + ')')
        m_quit.setShortcut('Ctrl+Q')
        m_quit.triggered.connect(APP.quit)
        menu.addAction(m_quit)

        # Add Tray
        tray = QSystemTrayIcon()
        tray.setIcon(QIcon("icon.png"))
        tray.setVisible(True)
        tray.setContextMenu(menu)
        # sys.exit(app.exec_())
        exitcode = APP.exec_()
    finally:
        quitapp(exitcode)
Beispiel #32
0
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.running = False
        self.setWindowTitle('PySwicher v{}'.format(VERSION))

        # Logging config
        self.log_textbox = QPlainTextEditLogger(self)
        logging.getLogger().addHandler(self.log_textbox)
        self.log_textbox.setFormatter(logging.Formatter('[%(asctime)s][%(levelname)s]:    %(message)s'))
        self.log_textbox.setLevel(self.get_numeric_loglevel(options['log_level']))
        self.log_to_file = False

        # System tray configuration
        self.tray_menu = QMenu(self)
        self.systemTrayIcon = QSystemTrayIcon()
        self.systemTrayIcon.setVisible(False)
        self.systemTrayIcon.setIcon(QtGui.QIcon('C:\\Users\\Admin\\Pictures\\tray_stop.jpg'))
        self.systemTrayIcon.activated.connect(self.sys_tray)
        self.exit_action = self.tray_menu.addAction('Exit')
        self.exit_action.triggered.connect(self.exit_app)
        self.systemTrayIcon.setContextMenu(self.tray_menu)
        self.click_tray_timer = QtCore.QTimer(self)  # Fix for systemtray click trigger
        self.click_tray_timer.setSingleShot(True)
        self.click_tray_timer.timeout.connect(self.click_timeout)

        self.main_window_ui()
        self.starter()

    def set_log_to_file(self, state):
        logger = logging.getLogger(__name__)
        file = logging.FileHandler(HOME + '\\switcher.log')
        if state == QtCore.Qt.Checked:
            self.log_to_file = True
            file.setFormatter(
                logging.Formatter('%(filename)s[LINE:%(lineno)d]# %(levelname)-8s [%(asctime)s]  %(message)s'))
            file.setLevel(self.get_numeric_loglevel(options['log_level']))
            logger.addHandler(file)
        else:
            if 'file' in logger.handlers:
                logger.removeHandler(file)
            self.log_to_file = False

    def starter(self):
        if not self.running:
            self.running = True
            self.start_btn.setText('Stop switcher')
            self.systemTrayIcon.setIcon(QtGui.QIcon('C:\\Users\\Admin\\Pictures\\tray_stop.jpg'))
            start_app()
        elif self.running:
            self.running = False
            self.start_btn.setText('Start switcher')
            self.systemTrayIcon.setIcon(QtGui.QIcon('C:\\Users\\Admin\\Pictures\\tray_start.jpg'))
            stop_app()
        return

    def main_window_ui(self):
        grid = QGridLayout(self)
        self.setLayout(grid)
        grid.setSpacing(5)

        # Here goes options layout
        self.topleft = QFrame(self)
        self.topleft.setFrameShape(QFrame.StyledPanel)
        self.topleft_grid = QGridLayout(self)
        self.topleft.setLayout(self.topleft_grid)

        self.switch_comb_label = QLabel('Switch combination:')
        self.switch_comb_text = QLineEdit()
        self.switch_comb_text.setText(options['switch_combination'])

        self.hotkey_label = QLabel('Hotkey:')
        self.hotkey_comb_text = QLineEdit()
        self.hotkey_comb_text.setText(options['hotkey'])

        self.topleft_grid.addWidget(self.switch_comb_label, 0, 0)
        self.topleft_grid.addWidget(self.switch_comb_text, 1, 0)
        self.topleft_grid.addWidget(self.hotkey_label, 2, 0)
        self.topleft_grid.addWidget(self.hotkey_comb_text, 3, 0)

        grid.addWidget(self.topleft, 0, 0)

        self.topright = QFrame(self)
        self.topright.setFrameShape(QFrame.StyledPanel)
        self.topright_grid = QGridLayout(self)
        self.topright.setLayout(self.topright_grid)

        self.info_label = QLabel('===INFO===')
        self.info_label.setAlignment(QtCore.Qt.AlignHCenter)

        self.info_author = QLabel('Author: Kurashov Sergey')
        self.info_author.setAlignment(QtCore.Qt.AlignHCenter)

        self.info_contacts = QLabel('Contacts: [email protected]')
        self.info_contacts.setAlignment(QtCore.Qt.AlignHCenter)

        self.info_sourcecode = QLabel('<a href="https://github.com/shimielder/win_switcher">Sourcecode on GitHub</a>')
        self.info_sourcecode.setAlignment(QtCore.Qt.AlignHCenter)
        self.info_sourcecode.setOpenExternalLinks(True)

        self.topright_grid.addWidget(self.info_label, 0, 0)
        self.topright_grid.addWidget(self.info_author, 1, 0)
        self.topright_grid.addWidget(self.info_contacts, 2, 0)
        self.topright_grid.addWidget(self.info_sourcecode, 3, 0)

        grid.addWidget(self.topright, 0, 1)

        self.middle = QFrame(self)
        self.middle.setFrameShape(QFrame.StyledPanel)
        self.middle_grid = QGridLayout(self)
        self.middle.setLayout(self.middle_grid)

        self.dictionsries_label = QLabel('Dictionaries to switch:')
        self.dict_one = QPlainTextEdit()
        self.dict_one.clear()
        self.dict_one.appendPlainText(options['layouts'][0])
        self.dict_two = QPlainTextEdit()
        self.dict_two.clear()
        self.dict_two.appendPlainText(options['layouts'][1])

        self.middle_grid.addWidget(self.dictionsries_label, 0, 0, 1, 4)
        self.middle_grid.addWidget(self.dict_one, 1, 0, 1, 4)
        self.middle_grid.addWidget(self.dict_two, 2, 0, 1, 4)
        grid.addWidget(self.middle, 1, 0, 1, 2)

        self.bottom = QFrame(self)
        self.bottom.setFrameShape(QFrame.StyledPanel)
        self.bottom_grid = QGridLayout(self)
        self.bottom.setLayout(self.bottom_grid)

        self.loglevel_label = QLabel('Logging level:')
        self.loglevel_dropmenu = QComboBox(self)
        self.loglevel_dropmenu.addItems(LOGGING_LEVELS)
        self.loglevel_dropmenu.setCurrentIndex(LOGGING_LEVELS.index((options['log_level'].upper())))
        self.loglevel_dropmenu.activated[str].connect(self.set_logginglevel)

        # self.log_to_file_label = QLabel('Check to save logs to file')
        self.log_to_file_chk = QCheckBox('Check to save logs to file')
        self.log_to_file_chk.stateChanged.connect(self.set_log_to_file)

        self.logging_output_label = QLabel('Logging output:')
        self.logging_output_label.setAlignment(QtCore.Qt.AlignHCenter)

        self.bottom_grid.addWidget(self.loglevel_label, 0, 0)
        self.bottom_grid.addWidget(self.loglevel_dropmenu, 0, 1)
        self.bottom_grid.addWidget(self.log_to_file_chk, 0, 3, 1, 1)
        self.bottom_grid.addWidget(self.logging_output_label, 1, 0, 1, 4)
        self.bottom_grid.addWidget(self.log_textbox.widget, 2, 0, 2, 4)
        grid.addWidget(self.bottom, 2, 0, 1, 2)

        self.bottom_buttons = QFrame(self)
        # self.bottom_buttons.setFrameShape(QFrame.StyledPanel)
        self.bottom_buttons_grid = QGridLayout(self)
        self.bottom_buttons.setLayout(self.bottom_buttons_grid)

        self.start_btn = QPushButton('Start switcher')
        self.start_btn.clicked.connect(self.starter)
        self.apply_btn = QPushButton('Apply changes')
        self.apply_btn.clicked.connect(self.apply)
        self.exit_btn = QPushButton('Exit app')
        self.exit_btn.clicked.connect(self.exit_app)

        self.bottom_buttons_grid.addWidget(self.start_btn, 0, 0)
        self.bottom_buttons_grid.addWidget(self.apply_btn, 0, 1)
        self.bottom_buttons_grid.addWidget(self.exit_btn, 0, 2)
        grid.addWidget(self.bottom_buttons, 3, 0, 1, 2)

        self.resize(480, 320)
        self.show()

    def get_numeric_loglevel(self, loglevel):
        numeric_level = getattr(logging, loglevel.upper(), None)
        if not isinstance(numeric_level, int):
            numeric_level = 0
        return numeric_level

    def set_logginglevel(self, loglevel):
        return self.log_textbox.setLevel(self.get_numeric_loglevel(loglevel))

    @QtCore.pyqtSlot(QSystemTrayIcon.ActivationReason)
    def sys_tray(self, reason):
        """
        По-умолчанию, trigger срабатывает всегда. Для обхода этого
        я повесил таймер на событие.
        Взято отсюда:
        https://riverbankcomputing.com/pipermail/pyqt/2010-November/028394.html
        """
        if reason == QSystemTrayIcon.Trigger:
            self.click_tray_timer.start(QApplication.doubleClickInterval())
        elif reason == QSystemTrayIcon.DoubleClick:
            self.click_tray_timer.stop()
            if self.isHidden():
                self.showNormal()
                self.systemTrayIcon.setVisible(False)

    def click_timeout(self):
        self.starter()

    def apply(self):
        self.starter()
        options['layouts'] = []
        options['layouts'].append(self.dict_one.toPlainText())
        options['layouts'].append(self.dict_two.toPlainText())
        options['log_level'] = LOGGING_LEVELS[self.loglevel_dropmenu.currentIndex()]
        self.set_logginglevel(options['log_level'])
        options['switch_combination'] = self.switch_comb_text.text()
        options['hotkey'] = self.hotkey_comb_text.text()
        logging.debug('Options from GUI: {}'.format(options))
        save_to(options)
        self.starter()
        return options

    def exit_app(self):
        if self.running:
            self.starter()
        self.systemTrayIcon.setVisible(False)
        self.destroy()

    def closeEvent(self, event):
        self.exit_app()

    def changeEvent(self, event):
        if event.type() == QtCore.QEvent.WindowStateChange:
            if self.windowState() & QtCore.Qt.WindowMinimized:
                event.ignore()
                self.hide()
                self.systemTrayIcon.setVisible(True)
                self.systemTrayIcon.showMessage('', 'Running in the background.')
        super(MainWindow, self).changeEvent(event)
Beispiel #33
0
class NomnsParse(QApplication):
    """Application Control."""

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


        # Updates
        self._toggled = False
        self._log_reader = None

        # Load Parsers
        self._load_parsers()
        self._settings = SettingsWindow()

        # Tray Icon
        self._system_tray = QSystemTrayIcon()
        self._system_tray.setIcon(QIcon(resource_path('data/ui/icon.png')))
        self._system_tray.setToolTip("nParse")
        # self._system_tray.setContextMenu(self._create_menu())
        self._system_tray.activated.connect(self._menu)
        self._system_tray.show()

        # Turn On
        self._toggle()

        if self.new_version_available():
            self._system_tray.showMessage(
                "nParse Update".format(ONLINE_VERSION),
                "New version available!\ncurrent: {}\nonline: {}".format(
                    CURRENT_VERSION,
                    ONLINE_VERSION
                ),
                msecs=3000
            )

    def _load_parsers(self):
        self._parsers = [
            parsers.Maps(),
            parsers.Spells()
        ]
        for parser in self._parsers:
            if parser.name in config.data.keys() and 'geometry' in config.data[parser.name].keys():
                g = config.data[parser.name]['geometry']
                parser.setGeometry(g[0], g[1], g[2], g[3])
            if config.data[parser.name]['toggled']:
                parser.toggle()

    def _toggle(self):
        if not self._toggled:
            try:
                config.verify_paths()
            except ValueError as error:
                self._system_tray.showMessage(
                    error.args[0], error.args[1], msecs=3000)

            else:
                self._log_reader = logreader.LogReader(
                    config.data['general']['eq_log_dir'])
                self._log_reader.new_line.connect(self._parse)
                self._toggled = True
        else:
            if self._log_reader:
                self._log_reader.deleteLater()
                self._log_reader = None
            self._toggled = False

    def _parse(self, new_line):
        if new_line:
            timestamp, text = new_line  # (datetime, text)
            #  don't send parse to non toggled items, except maps.  always parse maps
            for parser in [parser for parser in self._parsers if config.data[parser.name]['toggled'] or parser.name == 'maps']:
                parser.parse(timestamp, text)

    def _menu(self, event):
        """Returns a new QMenu for system tray."""
        menu = QMenu()
        menu.setAttribute(Qt.WA_DeleteOnClose)
        # check online for new version
        new_version_text = ""
        if self.new_version_available():
            new_version_text = "Update Available {}".format(ONLINE_VERSION)
        else:
            new_version_text = "Version {}".format(CURRENT_VERSION)

        check_version_action = menu.addAction(new_version_text)
        menu.addSeparator()
        get_eq_dir_action = menu.addAction('Select EQ Logs Directory')
        menu.addSeparator()

        parser_toggles = set()
        for parser in self._parsers:
            toggle = menu.addAction(parser.name.title())
            toggle.setCheckable(True)
            toggle.setChecked(config.data[parser.name]['toggled'])
            parser_toggles.add(toggle)

        menu.addSeparator()
        settings_action = menu.addAction('Settings')
        menu.addSeparator()
        quit_action = menu.addAction('Quit')

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

        if action == check_version_action:
            webbrowser.open('https://github.com/nomns/nparse/releases')

        elif action == get_eq_dir_action:
            dir_path = str(QFileDialog.getExistingDirectory(
                None, 'Select Everquest Logs Directory'))
            if dir_path:
                config.data['general']['eq_log_dir'] = dir_path
                config.save()
                self._toggle()

        elif action == settings_action:
            if self._settings.exec_():
                # Update required settings
                for parser in self._parsers:
                    if parser.windowOpacity() != config.data['general']['parser_opacity']:
                        parser.setWindowOpacity(
                            config.data['general']['parser_opacity'] / 100)
                        parser.settings_updated()
            # some settings are saved within other settings automatically
            # force update
            for parser in self._parsers:
                if parser.name == "spells":
                    parser.load_custom_timers()

        elif action == quit_action:
            if self._toggled:
                self._toggle()

            # save parser geometry
            for parser in self._parsers:
                g = parser.geometry()
                config.data[parser.name]['geometry'] = [
                    g.x(), g.y(), g.width(), g.height()
                ]
                config.save()

            self._system_tray.setVisible(False)
            self.quit()

        elif action in parser_toggles:
            parser = [
                parser for parser in self._parsers if parser.name == action.text().lower()][0]
            parser.toggle()

    def new_version_available(self):
        # this will only work if numbers go up
        try:
            for (o, c) in zip(ONLINE_VERSION.split('.'), CURRENT_VERSION.split('.')):
                if int(o) > int(c):
                    return True
        except:
            return False
Beispiel #34
0
 def inittray(self):
     global tray
     tray=QSystemTrayIcon(self)
     tray.setIcon(QIcon(os.path.join(sys.path[0],'clock.png')))
     tray.setVisible(True)
     tray.activated.connect(self.activate)
class Application(QApplication):
    def __init__(self):
        super(Application, self).__init__([])
        self.setQuitOnLastWindowClosed(False)

        # Create ouath object to handle cached token data
        self.sp_oauth = SpotifyOAuth(spotify_config.client_id,
                                     spotify_config.client_secret,
                                     spotify_config.redirect_uri,
                                     scope='user-read-currently-playing',
                                     cache_path=join(FOLDER_PATH,
                                                     'spotifycache'))

        # Create the menu, connect class functions to the app's buttons
        self.menu = Menu()
        self.menu.login_info_action.triggered.connect(self._on_login_button)
        self.menu.toggle_wallpaper_action.triggered.connect(
            self._on_toggle_wallpaper_button)
        self.menu.quit_action.triggered.connect(self._on_quit_button)

        # Create the tray and make it visible
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(QIcon(resource_path(join('assets', 'icon.png'))))
        self.tray.setVisible(True)
        self.tray.setContextMenu(self.menu)

        # Create confirm account frame
        self.confirm_account_window = ConfirmAccountWindow()
        self.confirm_account_window.form_widget.button.clicked.connect(
            self._on_confirm_account_button)

        # Attempt login with cache
        self._attempt_login()

    def _attempt_login(self, code=None):
        """attempts to get token from cache if a code is not supplied"""

        if code:
            # Get access token from Spotify
            try:
                token_info = self.sp_oauth.get_access_token(code)
            except SpotifyOauthError:
                notify_bad_link()
                return
        else:
            # Get token from cache
            token_info = self.sp_oauth.get_cached_token()
            if not token_info:
                if IS_WINDOWS:
                    text = 'Find the WallSpotify icon in your tray to log into your Spotify account.'
                else:
                    text = 'Click the WallSpotify icon to log into your Spotify account.'
                show_notification('Not Logged In', text)
                return

        # Login with token
        self.spotify = login(token_info)

        if self.spotify:
            self._update_login_ui()
            show_notification('Login Success', 'You can now use WallSpotify.')
        else:
            show_notification('Login Failed',
                              'Something went wrong, try logging in again.')

    def _update_login_ui(self):
        """changes tray UI to reflect a successful login"""

        # Call to Spotify for user account info
        try:
            current_user_info = self.spotify.me()
        except Exception as e:
            print(e)
            return

        text = f'{current_user_info["display_name"]} - Logged into Spotify'

        self.menu.login_info_action.setText(text)
        self.menu.toggle_wallpaper_action.setEnabled(True)
        self.menu.login_info_action.setEnabled(False)

    def _on_confirm_account_button(self):
        """parse Spotify callback url for response code, then try to login"""

        # create regex for a valid url
        regex = compile(
            r'^(?:http|ftp)s?://'  # http:// or https://
            r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'  # domain...
            r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'  # ...or ip
            r'(?:/?|[/?]\S+)$',
            IGNORECASE)

        # get the contents of the text field
        text = self.confirm_account_window.form_widget.text_field.text()

        is_valid = True
        # check if text is a valid url
        if match(regex, text):
            code = self.sp_oauth.parse_response_code(text)

            if not code:
                is_valid = False
        else:
            is_valid = False

        self.confirm_account_window.hide()
        self.confirm_account_window.form_widget.text_field.setText('')

        if is_valid:
            # Attempt login using code
            self._attempt_login(code)
        else:
            notify_bad_link()

    def _on_login_button(self):
        """tries to open users web browser to prompt Spotify login, also shows window for callback url"""

        if not self.confirm_account_window.isVisible():
            self.confirm_account_window.show()
            auth_url = self.sp_oauth.get_authorize_url()
            try:
                open_webbrowser(auth_url)
            except Exception as e:
                # todo: show a prompt to give the user the auth url
                print(e)

    def _on_toggle_wallpaper_button(self):
        """starts and stops wallpaper thread from users input"""

        # check if the toggle wallpaper option was already selected
        if self.menu.toggle_wallpaper_action.checked:
            # stop the current song thread
            self.menu.toggle_wallpaper_action.checked = False
            self.wallpaper_thread.stop()
        else:
            # start a new thread for changing wallpaper
            self.menu.toggle_wallpaper_action.checked = True
            self._start_new_thread()

    def _on_quit_button(self):
        """stops thread then quits the application"""

        try:
            self.wallpaper_thread.stop()
        except AttributeError:
            pass

        self.quit()

    def _start_new_thread(self):
        """creates new thread object and starts it"""

        self.wallpaper_thread = ChangeWallpaperThread(self.spotify,
                                                      FOLDER_PATH)
        self.wallpaper_thread.start()