def _initSystemTray(self, withMenu: bool = False):
        LOG.info("Initializing the system tray: available: %s, messages: %s",
                 QSystemTrayIcon.isSystemTrayAvailable(),
                 QSystemTrayIcon.supportsMessages())

        if not QSystemTrayIcon.isSystemTrayAvailable():
            return

        systemTrayIcon = QSystemTrayIcon(QIcon('assets/logo.png'), self._app)
        systemTrayIcon.setToolTip("The Deep Visualization Toolbox")
        systemTrayIcon.activated.connect(self.onSystemTrayActivated)

        if withMenu:
            contextMenu = QMenu()
            contextMenu.aboutToShow.connect(self.onSystemTrayMenuAboutToShow)

            actionTest = QAction('Test', contextMenu)
            actionTest.triggered.connect(self.onSystemTrayMenuActionTriggered)
            systemTrayIcon.setContextMenu(contextMenu)

        if QSystemTrayIcon.supportsMessages():
            systemTrayIcon.\
                messageClicked.connect(self.onSystemTrayMessageClicked)

        systemTrayIcon.show()
Beispiel #2
0
class TrayIcon(QThread):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.main = parent
        self.trayIcon = QSystemTrayIcon(QIcon('image/icon.png'))
        self.trayIcon.setToolTip('Laptop Security Program v1.0 is Running')
        self.trayIcon.show()
        # noinspection PyUnresolvedReferences
        self.trayIcon.activated.connect(self.on_systray_activated)

    @staticmethod
    def on_systray_activated():
        buttons = qApp.mouseButtons()
        if not buttons:
            msgBox = QMessageBox()
            msgBox.setWindowTitle('Exit')
            msgBox.setText('Are you sure to quit?')
            msgBox.setStandardButtons(QMessageBox.No | QMessageBox.Yes)
            msgBox.setIcon(QMessageBox.Information)
            msgBox.setDefaultButton(QMessageBox.No)
            msgBox.setWindowIcon(QIcon('image/icon.png'))
            msgBox.setWindowFlags(Qt.WindowStaysOnTopHint)
            msgBox.setWindowFlags(msgBox.windowFlags()
                                  & ~Qt.WindowCloseButtonHint
                                  & ~Qt.WindowMaximizeButtonHint
                                  & ~Qt.WindowMinimizeButtonHint)
            msgBox.activateWindow()
            msgBox.raise_()
            result = msgBox.exec_()

            if result == QMessageBox.Yes:
                WifiServer.EXIT = True
                quit(0)
                sys.exit(0)
Beispiel #3
0
class App:
  def __init__(self):
    # Create a Qt application
    self.app = QApplication(sys.argv)
    
    icon = QIcon("favicon.ico")
    menu = QMenu()
    settingAction = menu.addAction("setting")
    settingAction.triggered.connect(self.setting)
    exitAction = menu.addAction("exit")
    exitAction.triggered.connect(sys.exit)
    
    self.tray = QSystemTrayIcon()
    self.tray.setIcon(icon)
    self.tray.setContextMenu(menu)
    self.tray.show()
    self.tray.setToolTip("unko!")
    self.tray.showMessage("hoge", "moge")
    self.tray.showMessage("fuga", "moge")
    
  def run(self):
    # Enter Qt application main loop
    self.app.exec_()
    sys.exit()

  def setting(self):
    self.dialog = QDialog()
    self.dialog.setWindowTitle("Setting Dialog")
    self.dialog.show()
Beispiel #4
0
class AppUi(QtCore.QObject):
    reload_data = QtCore.pyqtSignal()

    def __init__(self, parent: QApplication, conf: Config,
                 build_icons: BuildIcons):
        super(AppUi, self).__init__(parent)
        self.widget = QWidget()
        self.build_icons = build_icons
        self.tray = QSystemTrayIcon(self.build_icons.for_status(None),
                                    self.widget)
        self.tray.show()
        self.app_menu = AppMenu(self.widget, conf, self.build_icons)
        self.app_menu.reload_data.connect(self.reload_data)  # type: ignore
        self.tray.setContextMenu(self.app_menu.menu)
        self.tray.activated.connect(self.show_menu)

    def show_menu(self, reason):
        if not sys.platform.startswith(
                'darwin') and reason == QSystemTrayIcon.Trigger:
            self.app_menu.menu.popup(QCursor.pos())

    def update_projects(self, integration_status: OverallIntegrationStatus):
        count = len(integration_status.get_failing_builds())
        self.tray.setIcon(
            self.build_icons.for_aggregate_status(
                integration_status.get_build_status(), count))
        self.app_menu.update(integration_status.get_projects())
        self.tray.setToolTip("Last checked: " + strftime("%Y-%m-%d %H:%M:%S"))
Beispiel #5
0
class Ui_New(QWidget):
    def __init__(self):
        super().__init__()
        self.setupUi()

    def about(self):
        sys.exit()

    def see(self):
        path = os.path.abspath(filename)
        os.startfile(path)
        self.tp.setIcon(QIcon('./nothing_logo.jpg'))
        tpMenu = QMenu()
        a1 = QAction(QtGui.QIcon('exit.png'), u'退出', self)  # 添加一级菜单动作选项(关于程序)
        a1.triggered.connect(self.about)
        tpMenu.addAction(a1)
        self.tp.setContextMenu(tpMenu)
        self.work()

    def work(self):
        global filemt
        while 1:
            time.sleep(10)
            if time.localtime(os.stat(filename).st_mtime) != filemt:
                filemt = time.localtime(os.stat(filename).st_mtime)
                print(filemt)
                self.tp.setIcon(QIcon('./Yes_logo.jpg'))
                tpMenu = QMenu()
                a1 = QAction(QtGui.QIcon('exit.png'), u'查看',
                             self)  # 添加一级菜单动作选项(关于程序)
                a1.triggered.connect(self.see)
                tpMenu.addAction(a1)
                self.tp.setContextMenu(tpMenu)
                break

    def setupUi(self):
        global filemt
        self.resize(250, 250)
        # move()方法移动了窗口到屏幕坐标x=300, y=300的位置.
        self.move(300, 300)
        # 在这里我们设置了窗口的标题.标题会被显示在标题栏上.
        self.setWindowTitle('Simple')
        # show()方法将窗口显示在屏幕上.一个窗口是先在内存中被创建,然后显示在屏幕上的.
        #self.show()
        print(1)
        self.tp = QSystemTrayIcon(self)
        self.tp.setIcon(QIcon('./nothing_logo.jpg'))
        self.tp.setToolTip(u'智乃酱真棒(*^▽^*)!')
        self.tp.showMessage('tp', 'tpContent')
        tpMenu = QMenu()
        a1 = QAction(QtGui.QIcon('exit.png'), u'退出', self)  # 添加一级菜单动作选项(关于程序)
        a1.triggered.connect(self.about)
        tpMenu.addAction(a1)
        self.tp.setContextMenu(tpMenu)
        self.tp.show()
        filemt = time.localtime(os.stat(filename).st_mtime)
        print(filemt)
        #self.tp.messageClicked.connect(self.message)
        self.work()
Beispiel #6
0
class QTGui(QWidget):


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

    def changeEvent(self, QEvent):
        if QEvent.type() == QEvent.WindowStateChange:
            if self.isMinimized():
                print("minimized")
                self.minimizetotray()
        super().changeEvent(QEvent)

    def showWindow(self):
        self.setGeometry(300, 300, 300, 63)
        self.setFixedSize(self.size())
        self.setWindowIcon(QIcon("icon.png"))
        self.setWindowTitle("pyWall UI")

        global btn
        btn = QPushButton("Change", self)
        btn.resize(75, 23)
        btn.move(0, self.height() - btn.height())
        btn.setToolTip("Change the wallpaper right now.")
        btn.clicked.connect(newWallpaperInNewThread)
        global txtinterval
        txtinterval = QTextEdit("100", self)
        txtinterval.setToolTip("Time interval in seconds between wallpaper changes.")
        txtinterval.resize(70, 23)
        txtinterval.move(0, btn.y() - txtinterval.height())
        global chkbox
        chkbox = QCheckBox("Timer", self)
        chkbox.setToolTip("Use timer for auto wallpaper change.")
        chkbox.resize(49, 17)
        chkbox.move(0, txtinterval.y() - chkbox.height())
        chkbox.stateChanged.connect(checkBoxStateChanged)
        global label
        label = QLabel("", self)
        label.setFont(QFont("Times", 8, QFont.Bold))
        label.move(btn.width() + 5, 0)
        label.resize(self.width()-btn.width(),self.height())
        label.setWordWrap(True)
        self.show()

    def minimizetotray(self):
        self.hide()
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(QIcon("icon.png"))
        self.tray.setToolTip("pyWall Tray")
        self.tray.show()
        self.tray.showMessage("pyWall", "pyWall will run in background.", msecs=500)
        self.tray.activated.connect(self.trayiconactivated)

    def trayiconactivated(self, reason):
        if reason == QSystemTrayIcon.Trigger:
            self.tray.hide()
            self.show()
Beispiel #7
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 #8
0
 def setTrayIcon(self):
     tray_icon = QSystemTrayIcon(QIcon(real_path("img/logo.svg")), self)
     tray_icon.show()
     menu = QMenu()
     menu.addAction("Burst!").triggered.connect(self.clickBurst)
     menu.addAction("Sync").triggered.connect(self.clickSync)
     menu.addAction("Setting").triggered.connect(self.clickSetting)
     menu.addAction("Exit").triggered.connect(self.clickExit)
     tray_icon.setContextMenu(menu)
     tray_icon.activated.connect(self.clickTrayIcon)
     tray_icon.setToolTip("DWords")
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 #10
0
class tray():
    
    def __init__(self):
        super().__init__()
        self.initUI()
    
    def turn_off(self,device):
        is_list = isinstance(device,list)
        if is_list == False:
            return device.turn_off()
        else:
            return [i.turn_off() for i in device]

    def turn_on(self,device):
        is_list = isinstance(device,list)
        if is_list == False:
            return device.turn_on()
        else:
            return[ i.turn_on() for i in device] 
    
    def initUI(self):
        self.app = QApplication(sys.argv)
        api.init('LOGIN','PASSWORD',"44","tuya")
        self.device_ids = api.get_all_devices()
        self.devices = dict((i.name(),i) for i in self.device_ids if i.obj_type == 'light' or i.obj_type == 'switch')
        self.lights = dict((i.name(),i) for i in self.device_ids if i.obj_type == 'light')
        self.devices['All Lights'] = list(self.lights.values())
        self.tray_icon = QSystemTrayIcon(QIcon('lightbulb.png'),parent=self.app)
        self.tray_icon.setToolTip('Tuya - Lights')
        self.menu = QMenu()
        self.menus = dict()
        self.buttons = dict()

        for j in self.devices.keys():
            self.menus[f"{j}_Action"] = self.menu.addMenu(j)
            self.menus[f"{j}_Action"].addAction('On')
            self.menus[f"{j}_Action"].addAction('Off')
            self.buttons = self.menus[f"{j}_Action"].actions()
            for i in self.buttons:
                if i.iconText() == 'On':
                    i.triggered.connect(partial(self.turn_on,self.devices[j]))
                elif i.iconText() == 'Off':
                    i.triggered.connect(partial(self.turn_off,self.devices[j]))

        self.exitaction = self.menu.addAction('Exit')
        self.exitaction.triggered.connect(self.app.quit)
        self.tray_icon.setContextMenu(self.menu)
        self.tray_icon.show()
        self.app.exec_()
        sys.exit(self.app.exec_())
Beispiel #11
0
class TrayWindow(QDialog):
    def __init__(self, main_window, *args, **kwargs):
        super(TrayWindow, self).__init__(*args, **kwargs)

        self.createActions()
        self.createTrayIcon()

        self.trayIcon.show()

        self.settingsWindow = main_window

    def createActions(self):
        self.settingsAction = QAction("Settings", self, triggered=self.showSettings)
        self.quitAction = QAction("Quit", self, triggered=QApplication.instance().quit)

    def createTrayIcon(self):
        self.trayIconMenu = QMenu(self)
        self.trayIconMenu.addAction(self.settingsAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.quitAction)

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

        self.trayIcon.activated.connect(self.iconActivated)

        icon = QIcon(getResource("speechOn.png"))
        self.trayIcon.setIcon(icon)
        self.setWindowIcon(icon)

        self.trayIcon.setToolTip("MicroCommander")

    def iconActivated(self, reason):
        self.showNotification("Micro commander", "Status: running")

    def showSettings(self):
        self.settingsWindow.show()

    @staticmethod
    def showNotification(title, body):
        trayIcon = QSystemTrayIcon()
        trayIcon.setIcon(QIcon(getResource("speech.png")))
        trayIcon.show()
        icon = QSystemTrayIcon.MessageIcon(QSystemTrayIcon.Information)
        trayIcon.showMessage(title, body, icon)

    @staticmethod
    def show_message(tray, title, body):
        icon = QSystemTrayIcon.MessageIcon(QSystemTrayIcon.Information)
        tray.showMessage(title, body, icon)
Beispiel #12
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 #13
0
def set_systray():
    systray = QSystemTrayIcon()
    systray.setIcon(
        QIcon(
            "E:\MyWorkPlace\pythonwork\devices\windows_system_tray\systray.png"
        ))
    # systray.setToolTip("Warframe")
    while True:
        systray.setToolTip("""
    本地时间:{}
    地球平原时间:{},倒计时:{}
    金星平原天气:{},倒计时:{}
    """.format(1, 2, 3, 4, 5))
        systray.show()
        time.sleep(1)
Beispiel #14
0
class TrayIcon(QWidget): #child class name
    def __init__(self): #class constructor
        super().__init__(); #inheritance from parent attributes

        self.tray=QSystemTrayIcon(QtGui.QIcon('chidalu.jpg')); #create a systemtrayicon
        self.tray.setToolTip('Click Me'); #set tooltip of trayicon
        self.tray.show(); #show trayicon

        menu=QMenu(self); #create a menu object

        exitAction=QAction('Exit'); #create an exit action
        exitAction.triggered.connect(app.quit); #connect a signal to a slot
        menu.addAction(exitAction); #add action to menu

        self.tray.setContextMenu(menu); #set context menu of trayicon
Beispiel #15
0
def init_tray_icon(app_window):
    if not QSystemTrayIcon.isSystemTrayAvailable():
        QMessageBox.critical(
            None, APP_NAME, 'System tray is not supported, app will exit'
        )
        sys.exit(1)

    icon = QIcon(':/images/app.png')
    icon.setIsMask(True)

    tray_icon = QSystemTrayIcon(icon, app_window)
    tray_icon.activated.connect(
        lambda reason: tray_icon_activated(reason, app_window, tray_icon)
    )
    tray_icon.setToolTip(APP_NAME)
    return tray_icon
Beispiel #16
0
 def set_tray(self):
     tray_icon = QSystemTrayIcon(self)
     icon = QIcon("icon.png")
     tray_icon.setIcon(icon)
     show_action = QAction("Show", self)
     quit_action = QAction("Exit", self)
     hide_action = QAction("Hide", self)
     show_action.triggered.connect(self.show)
     hide_action.triggered.connect(self.hide)
     quit_action.triggered.connect(self.close)
     tray_menu = QMenu()
     tray_menu.addAction(show_action)
     tray_menu.addAction(hide_action)
     tray_menu.addAction(quit_action)
     tray_icon.setContextMenu(tray_menu)
     tray_icon.show()
     tray_icon.setToolTip('Solar combine')
Beispiel #17
0
def apiTray():
    app = QtWidgets.QApplication(sys.argv)
    scriptDir = os.path.dirname(os.path.realpath(__file__))
    trayIcon = QSystemTrayIcon(QtGui.QIcon(scriptDir + "/resources/logo.png"))
    trayIcon.setToolTip("AmazonWatch")
    trayIcon.show()
    menu = QMenu()
    openAction = menu.addAction("Open GUI")
    openAction.triggered.connect(startGuiProcess)
    menu.addSeparator()
    actionGithub = menu.addAction("Visit Github")
    actionGithub.triggered.connect(openBrowser)
    menu.addSeparator()
    exitAction = menu.addAction("Exit")
    exitAction.triggered.connect(app.quit)
    trayIcon.setContextMenu(menu)
    sys.exit(app.exec_())
Beispiel #18
0
 def set_tray(self):
     tray_icon = QSystemTrayIcon(self)
     icon = QIcon("icon.png")
     tray_icon.setIcon(icon)
     show_action = QAction("Показать", self)
     quit_action = QAction("Выйти", self)
     hide_action = QAction("Спрятать", self)
     show_action.triggered.connect(self.show)
     hide_action.triggered.connect(self.hide)
     quit_action.triggered.connect(self.close)
     tray_menu = QMenu()
     tray_menu.addAction(show_action)
     tray_menu.addAction(hide_action)
     tray_menu.addAction(quit_action)
     tray_icon.setContextMenu(tray_menu)
     tray_icon.show()
     tray_icon.setToolTip('Address book')
Beispiel #19
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 #20
0
class AppUi(QtCore.QObject):
    reload_data = QtCore.pyqtSignal()

    def __init__(self, parent, conf, build_icons):
        super(AppUi, self).__init__(parent)
        self.widget = QWidget()
        self.build_icons = build_icons
        self.tray = QSystemTrayIcon(self.build_icons.for_status(None), self.widget)
        self.tray.show()
        self.app_menu = AppMenu(self.widget, conf, self.build_icons)
        self.app_menu.reload_data.connect(self.reload_data)
        self.tray.setContextMenu(self.app_menu.menu)

    def update_projects(self, integration_status):
        count = len(integration_status.get_failing_builds())
        self.tray.setIcon(self.build_icons.for_aggregate_status(integration_status.get_build_status(), count))
        self.app_menu.update(integration_status.get_projects())
        self.tray.setToolTip("Last checked: " + strftime("%Y-%m-%d %H:%M:%S"))
Beispiel #21
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 #22
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_()
def tray_icon_handler():

    app = QApplication(sys.argv)

    tray_icon = QSystemTrayIcon(QIcon('stocks.ico'), parent=app)
    tray_icon.setToolTip('Price Alert Running!')
    tray_icon.show()

    menu = QMenu()
    global last_update
    last_update = menu.addAction('Last update: - - - ')

    open_excel = menu.addAction('Open Price Alert Excel Sheet')
    open_excel.triggered.connect(
        lambda: os.startfile(os.getcwd() + '\\Price_Alert.xlsm'))

    exit_action = menu.addAction('Exit')
    exit_action.triggered.connect(lambda: os._exit(0))

    tray_icon.setContextMenu(menu)

    sys.exit(app.exec_())
Beispiel #24
0
class EmojiBoard(QMainWindow):
    def __init__(self):
        super(EmojiBoard, self).__init__(None, Qt.WindowStaysOnTopHint)

        self.icon = QIcon(ICON_PLACE)

        self.setGeometry(X_POS, Y_POS, WIDTH, HEIGHT)
        self.setWindowTitle(NAME)
        self.setWindowIcon(self.icon)
        self.setWindowFlag(Qt.FramelessWindowHint)

        if sys.platform == "win32":
            set_taskbar_icon()

        self.populate()

        self.tray_icon = None
        self.set_tray()

        self.ready()

    def populate(self):
        TitleBar(self)
        EmojiGrid(self)

    def set_tray(self):
        self.tray_icon = QSystemTrayIcon(self.icon)
        self.tray_icon.setToolTip(NAME)

        menu = QMenu()
        exit_action = menu.addAction("Exit")
        exit_action.triggered.connect(app.quit)

        self.tray_icon.setContextMenu(menu)

    def ready(self):
        self.tray_icon.show()
        self.show()
Beispiel #25
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 #26
0
class TaryWiondw(QWidget):
    def __init__(self):
        super().__init__()
        # 创建托盘对象
        self.tray = QSystemTrayIcon()

        # 创建QIcon对象,用于设置图标(图片过大会出错)
        self.trayIconPix = QPixmap(16, 16)
        self.trayIconPix.fill(QColor(100, 100, 100))
        self.Icon = QIcon(self.trayIconPix)

        # 设置托盘图标(QIcon图标过大或者出错会导致托盘显示不出来)
        self.tray.setIcon(self.Icon)

        # 创建QAction
        showAction = QAction("&Show", self, triggered=self.Show)
        quitAction = QAction("&Quit", self, triggered=self.Exit)
        # 创建菜单对象
        self.trayMenu = QMenu(self)
        # 将动作对象添加到菜单
        self.trayMenu.addAction(showAction)
        # 增加分割线
        self.trayMenu.addSeparator()
        self.trayMenu.addAction(quitAction)
        # 将菜单栏加入到右键按钮中
        self.tray.setContextMenu(self.trayMenu)

    def Exit(self):
        # 点击关闭按钮或者点击退出事件会出现图标无法消失的bug,需要手动将图标内存清除
        self.tray = None
        sys.exit(app.exec_())

    def Show(self):
        self.show()
        print(self.tray.showMessage("test", "test", icon=0, msecs=100))
        self.tray.show
        self.tray.setToolTip("这是一个气泡提示信息!")
Beispiel #27
0
class Magneto(MagnetoCore):

    """
    Magneto Updates Notification Applet class.
    """

    def __init__(self):
        self._app = QApplication([sys.argv[0]])

        from dbus.mainloop.pyqt5 import DBusQtMainLoop
        super(Magneto, self).__init__(main_loop_class = DBusQtMainLoop)

        self._window = QSystemTrayIcon(self._app)
        icon_name = self.icons.get("okay")
        self._window.setIcon(QIcon.fromTheme(icon_name))
        self._window.activated.connect(self._applet_activated)

        self._menu = QMenu(_("Magneto Entropy Updates Applet"))
        self._window.setContextMenu(self._menu)

        self._menu_items = {}
        for item in self._menu_item_list:
            if item is None:
                self._menu.addSeparator()
                continue

            myid, _unused, mytxt, myslot_func = item
            name = self.get_menu_image(myid)
            action_icon = QIcon.fromTheme(name)

            w = QAction(action_icon, mytxt, self._window,
                        triggered=myslot_func)
            self._menu_items[myid] = w
            self._menu.addAction(w)

        self._menu.hide()

    def _first_check(self):
        def _do_check():
            self.send_check_updates_signal(startup_check = True)
            return False

        if self._dbus_service_available:
            const_debug_write("_first_check", "spawning check.")
            QTimer.singleShot(10000, _do_check)

    def startup(self):
        self._dbus_service_available = self.setup_dbus()
        if config.settings["APPLET_ENABLED"] and \
            self._dbus_service_available:
            self.enable_applet(do_check = False)
            const_debug_write("startup", "applet enabled, dbus service available.")
        else:
            const_debug_write("startup", "applet disabled.")
            self.disable_applet()
        if not self._dbus_service_available:
            const_debug_write("startup", "dbus service not available.")
            QTimer.singleShot(30000, self.show_service_not_available)
        else:
            const_debug_write("startup", "spawning first check.")
            self._first_check()

        # Notice Window instance
        self._notice_window = AppletNoticeWindow(self)

        self._window.show()

        # Enter main loop
        self._app.exec_()

    def close_service(self):
        super(Magneto, self).close_service()
        self._app.quit()

    def change_icon(self, icon_name):
        name = self.icons.get(icon_name)
        self._window.setIcon(QIcon.fromTheme(name))

    def disable_applet(self, *args):
        super(Magneto, self).disable_applet()
        self._menu_items["disable_applet"].setEnabled(False)
        self._menu_items["enable_applet"].setEnabled(True)

    def enable_applet(self, w = None, do_check = True):
        done = super(Magneto, self).enable_applet(do_check = do_check)
        if done:
            self._menu_items["disable_applet"].setEnabled(True)
            self._menu_items["enable_applet"].setEnabled(False)

    def show_alert(self, title, text, urgency = None, force = False,
                   buttons = None):

        # NOTE: there is no support for buttons via QSystemTrayIcon.

        if ((title, text) == self.last_alert) and not force:
            return

        def _action_activate_cb(action_num):
            if not buttons:
                return

            try:
                action_info = buttons[action_num - 1]
            except IndexError:
                return

            _action_id, _button_name, button_callback = action_info
            button_callback()

        def do_show():
            if not self._window.supportsMessages():
                const_debug_write("show_alert", "messages not supported.")
                return

            icon_id = QSystemTrayIcon.Information
            if urgency == "critical":
                icon_id = QSystemTrayIcon.Critical

            self._window.showMessage(title, text, icon_id)
            self.last_alert = (title, text)

        QTimer.singleShot(0, do_show)

    def update_tooltip(self, tip):
        def do_update():
            self._window.setToolTip(tip)
        QTimer.singleShot(0, do_update)

    def applet_context_menu(self):
        """No action for now."""

    def _applet_activated(self, reason):
        const_debug_write("applet_activated", "Applet activated: %s" % reason)
        if reason == QSystemTrayIcon.DoubleClick:
            const_debug_write("applet_activated", "Double click event.")
            self.applet_doubleclick()

    def hide_notice_window(self):
        self.notice_window_shown = False
        self._notice_window.hide()

    def show_notice_window(self):

        if self.notice_window_shown:
            const_debug_write("show_notice_window", "Notice window already shown.")
            return

        if not self.package_updates:
            const_debug_write("show_notice_window", "No computed updates.")
            return

        entropy_ver = None
        packages = []
        for atom in self.package_updates:

            key = entropy.dep.dep_getkey(atom)
            avail_rev = entropy.dep.dep_get_entropy_revision(atom)
            avail_tag = entropy.dep.dep_gettag(atom)
            my_pkg = entropy.dep.remove_entropy_revision(atom)
            my_pkg = entropy.dep.remove_tag(my_pkg)
            pkgcat, pkgname, pkgver, pkgrev = entropy.dep.catpkgsplit(my_pkg)
            ver = pkgver
            if pkgrev != "r0":
                ver += "-%s" % (pkgrev,)
            if avail_tag:
                ver += "#%s" % (avail_tag,)
            if avail_rev:
                ver += "~%s" % (avail_tag,)

            if key == "sys-apps/entropy":
                entropy_ver = ver

            packages.append("%s (%s)" % (key, ver,))

        critical_msg = ""
        if entropy_ver is not None:
            critical_msg = "%s <b>sys-apps/entropy</b> %s, %s <b>%s</b>. %s." % (
                _("Your system currently has an outdated version of"),
                _("installed"),
                _("the latest available version is"),
                entropy_ver,
                _("It is recommended that you upgrade to "
                  "the latest before updating any other packages")
            )

        self._notice_window.populate(packages, critical_msg)

        self._notice_window.show()
        self.notice_window_shown = True
class Qt4SysTrayIcon:
    def __init__(self):
        self.snapshots = snapshots.Snapshots()
        self.config = self.snapshots.config
        self.decode = None

        if len(sys.argv) > 1:
            if not self.config.setCurrentProfile(sys.argv[1]):
                logger.warning("Failed to change Profile_ID %s"
                               %sys.argv[1], self)

        self.qapp = qt4tools.createQApplication(self.config.APP_NAME)
        translator = qt4tools.translator()
        self.qapp.installTranslator(translator)
        self.qapp.setQuitOnLastWindowClosed(False)

        import icon
        self.icon = icon
        self.qapp.setWindowIcon(icon.BIT_LOGO)

        self.status_icon = QSystemTrayIcon(icon.BIT_LOGO)
        #self.status_icon.actionCollection().clear()
        self.contextMenu = QMenu()

        self.menuProfileName = self.contextMenu.addAction(_('Profile: "%s"') % self.config.profileName())
        qt4tools.setFontBold(self.menuProfileName)
        self.contextMenu.addSeparator()

        self.menuStatusMessage = self.contextMenu.addAction(_('Done'))
        self.menuProgress = self.contextMenu.addAction('')
        self.menuProgress.setVisible(False)
        self.contextMenu.addSeparator()

        self.btnPause = self.contextMenu.addAction(icon.PAUSE, _('Pause snapshot process'))
        action = lambda: os.kill(self.snapshots.pid(), signal.SIGSTOP)
        self.btnPause.triggered.connect(action)

        self.btnResume = self.contextMenu.addAction(icon.RESUME, _('Resume snapshot process'))
        action = lambda: os.kill(self.snapshots.pid(), signal.SIGCONT)
        self.btnResume.triggered.connect(action)
        self.btnResume.setVisible(False)

        self.btnStop = self.contextMenu.addAction(icon.STOP, _('Stop snapshot process'))
        self.btnStop.triggered.connect(self.onBtnStop)
        self.contextMenu.addSeparator()

        self.btnDecode = self.contextMenu.addAction(icon.VIEW_SNAPSHOT_LOG, _('decode paths'))
        self.btnDecode.setCheckable(True)
        self.btnDecode.setVisible(self.config.snapshotsMode() == 'ssh_encfs')
        self.btnDecode.toggled.connect(self.onBtnDecode)

        self.openLog = self.contextMenu.addAction(icon.VIEW_LAST_LOG, _('View Last Log'))
        self.openLog.triggered.connect(self.onOpenLog)
        self.startBIT = self.contextMenu.addAction(icon.BIT_LOGO, _('Start BackInTime'))
        self.startBIT.triggered.connect(self.onStartBIT)
        self.status_icon.setContextMenu(self.contextMenu)

        self.pixmap = icon.BIT_LOGO.pixmap(24)
        self.progressBar = QProgressBar()
        self.progressBar.setMinimum(0)
        self.progressBar.setMaximum(100)
        self.progressBar.setValue(0)
        self.progressBar.setTextVisible(False)
        self.progressBar.resize(24, 6)
        self.progressBar.render(self.pixmap, sourceRegion = QRegion(0, -14, 24, 6), flags = QWidget.RenderFlags(QWidget.DrawChildren))

        self.first_error = self.config.notify()
        self.popup = None
        self.last_message = None

        self.timer = QTimer()
        self.timer.timeout.connect(self.updateInfo)

        self.ppid = os.getppid()

    def prepairExit(self):
        self.timer.stop()

        if not self.status_icon is None:
            self.status_icon.hide()
            self.status_icon = None

        if not self.popup is None:
            self.popup.deleteLater()
            self.popup = None

        self.qapp.processEvents()

    def run(self):
        self.status_icon.show()
        self.timer.start(500)

        logger.info("[qt4systrayicon] begin loop", self)

        self.qapp.exec_()

        logger.info("[qt4systrayicon] end loop", self)

        self.prepairExit()

    def updateInfo(self):
        if not tools.processAlive(self.ppid):
            self.prepairExit()
            self.qapp.exit(0)
            return

        paused = tools.processPaused(self.snapshots.pid())
        self.btnPause.setVisible(not paused)
        self.btnResume.setVisible(paused)

        message = self.snapshots.takeSnapshotMessage()
        if message is None and self.last_message is None:
            message = (0, _('Working...'))

        if not message is None:
            if message != self.last_message:
                self.last_message = message
                if self.decode:
                    message = (message[0], self.decode.log(message[1]))
                self.menuStatusMessage.setText('\n'.join(tools.wrapLine(message[1],\
                                                                         size = 80,\
                                                                         delimiters = '',\
                                                                         new_line_indicator = '') \
                                                                       ))
                self.status_icon.setToolTip(message[1])

        pg = progress.ProgressFile(self.config)
        if pg.fileReadable():
            pg.load()
            percent = pg.intValue('percent')
            if percent != self.progressBar.value():
                self.progressBar.setValue(percent)
                self.progressBar.render(self.pixmap, sourceRegion = QRegion(0, -14, 24, 6), flags = QWidget.RenderFlags(QWidget.DrawChildren))
                self.status_icon.setIcon(QIcon(self.pixmap))

            self.menuProgress.setText(' | '.join(self.getMenuProgress(pg)))
            self.menuProgress.setVisible(True)
        else:
            self.status_icon.setIcon(self.icon.BIT_LOGO)
            self.menuProgress.setVisible(False)


    def getMenuProgress(self, pg):
        d = (('sent',   _('Sent:')), \
             ('speed',  _('Speed:')),\
             ('eta',    _('ETA:')))
        for key, txt in d:
            value = pg.strValue(key, '')
            if not value:
                continue
            yield txt + ' ' + value

    def onStartBIT(self):
        profileID = self.config.currentProfile()
        cmd = ['backintime-qt4',]
        if not profileID == '1':
            cmd += ['--profile-id', profileID]
        proc = subprocess.Popen(cmd)

    def onOpenLog(self):
        dlg = logviewdialog.LogViewDialog(self, systray = True)
        dlg.decode = self.decode
        dlg.cbDecode.setChecked(self.btnDecode.isChecked())
        dlg.exec_()

    def onBtnDecode(self, checked):
        if checked:
            self.decode = encfstools.Decode(self.config)
            self.last_message = None
            self.updateInfo()
        else:
            self.decode = None

    def onBtnStop(self):
        os.kill(self.snapshots.pid(), signal.SIGKILL)
        self.btnStop.setEnabled(False)
        self.btnPause.setEnabled(False)
        self.btnResume.setEnabled(False)
        self.snapshots.setTakeSnapshotMessage(0, 'Snapshot terminated')
Beispiel #29
0
class DemoImpl(QDialog):
	
	
    def __init__(self, *args):
        super(DemoImpl, self).__init__(*args)

        loadUi('dict2.ui',self)
		
        self.setLayout(self.verticalLayout)
        self.plainTextEdit.setReadOnly(True)
        self.setWindowFlags(self.windowFlags() |
                            Qt.WindowSystemMenuHint |
                            Qt.WindowMinMaxButtonsHint)
        self.trayicon = QSystemTrayIcon()
        self.traymenu = QMenu()

        self.quitAction = QAction('GQuit', self)
        self.quitAction.triggered.connect(self.close)
        self.quitAction.setShortcut(QKeySequence('Ctrl+q'))
        self.addAction(self.quitAction)

        self.traymenu.addAction('&Normal', self.showNormal, QKeySequence('Ctrl+n'))
        self.traymenu.addAction('Mi&nimize', self.showMinimized, QKeySequence('Ctrl+i'))
        self.traymenu.addAction('&Maximum', self.showMaximized, QKeySequence('Ctrl+m'))
        self.traymenu.addAction('&Quit',self.close, QKeySequence('Ctrl+q'))

        self.trayicon.setContextMenu(self.traymenu)

        self.ticon = QIcon('icon_dict2.ico')
        self.trayicon.setIcon(self.ticon)
        self.trayicon.setToolTip('YYDict')
        self.trayicon.activated.connect(self.on_systemTrayIcon_activated)
        self.traymsg_firstshow = True

        self.button1.clicked.connect(self.searchword)
        self.comboBox.activated.connect(self.searchword)

    def changeEvent(self, event):
        if event.type() == QEvent.WindowStateChange:
            if self.isMinimized():
                self.hide()
                self.trayicon.show()
                if self.traymsg_firstshow:
                    self.trayicon.showMessage('', 'YYDict is running', QSystemTrayIcon.Information, 2000)
                    self.traymsg_firstshow = False
            else:
                self.trayicon.hide()
				
    def closeEvent(self, event):
        self.trayicon.hide()

    @pyqtSlot(QSystemTrayIcon.ActivationReason)
    def on_systemTrayIcon_activated(self, reason):
        if reason == QSystemTrayIcon.DoubleClick:
            self.activateWindow()	
            self.showNormal()



    @pyqtSlot()
    def searchword(self):
        word = self.lineEdit.text().strip()
        if not len(word):
            self.plainTextEdit.setPlainText('')
            self.lineEdit.setFocus(Qt.MouseFocusReason)
        else:
            self.workThread = WorkThread(word, self.comboBox.currentIndex())
            self.workThread.received.connect(self.updateResult)
            self.workThread.start()
            self.workThread.wait()


    @pyqtSlot('QString')
    def updateResult(self, rt):
        self.plainTextEdit.setPlainText(rt)
        self.lineEdit.selectAll()
        self.lineEdit.setFocus(Qt.MouseFocusReason)
Beispiel #30
0
class XNova_MainWindow(QWidget):

    STATE_NOT_AUTHED = 0
    STATE_AUTHED = 1

    def __init__(self, parent=None):
        super(XNova_MainWindow, self).__init__(parent, Qt.Window)
        # state vars
        self.config_store_dir = './cache'
        self.cfg = configparser.ConfigParser()
        self.cfg.read('config/net.ini', encoding='utf-8')
        self.state = self.STATE_NOT_AUTHED
        self.login_email = ''
        self.cookies_dict = {}
        self._hidden_to_tray = False
        #
        # init UI
        self.setWindowIcon(QIcon(':/i/xnova_logo_64.png'))
        self.setWindowTitle('XNova Commander')
        # main layouts
        self._layout = QVBoxLayout()
        self._layout.setContentsMargins(0, 2, 0, 0)
        self._layout.setSpacing(3)
        self.setLayout(self._layout)
        self._horizontal_layout = QHBoxLayout()
        self._horizontal_layout.setContentsMargins(0, 0, 0, 0)
        self._horizontal_layout.setSpacing(6)
        # flights frame
        self._fr_flights = QFrame(self)
        self._fr_flights.setMinimumHeight(22)
        self._fr_flights.setFrameShape(QFrame.NoFrame)
        self._fr_flights.setFrameShadow(QFrame.Plain)
        # planets bar scrollarea
        self._sa_planets = QScrollArea(self)
        self._sa_planets.setMinimumWidth(125)
        self._sa_planets.setMaximumWidth(125)
        self._sa_planets.setFrameShape(QFrame.NoFrame)
        self._sa_planets.setFrameShadow(QFrame.Plain)
        self._sa_planets.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self._sa_planets.setWidgetResizable(True)
        self._panel_planets = QWidget(self._sa_planets)
        self._layout_pp = QVBoxLayout()
        self._panel_planets.setLayout(self._layout_pp)
        self._lbl_planets = QLabel(self.tr('Planets:'), self._panel_planets)
        self._lbl_planets.setMaximumHeight(32)
        self._layout_pp.addWidget(self._lbl_planets)
        self._layout_pp.addStretch()
        self._sa_planets.setWidget(self._panel_planets)
        #
        # tab widget
        self._tabwidget = XTabWidget(self)
        self._tabwidget.enableButtonAdd(False)
        self._tabwidget.tabCloseRequested.connect(self.on_tab_close_requested)
        self._tabwidget.addClicked.connect(self.on_tab_add_clicked)
        #
        # create status bar
        self._statusbar = XNCStatusBar(self)
        self.set_status_message(self.tr('Not connected: Log in!'))
        #
        # tab widget pages
        self.login_widget = None
        self.flights_widget = None
        self.overview_widget = None
        self.imperium_widget = None
        #
        # settings widget
        self.settings_widget = SettingsWidget(self)
        self.settings_widget.settings_changed.connect(self.on_settings_changed)
        self.settings_widget.hide()
        #
        # finalize layouts
        self._horizontal_layout.addWidget(self._sa_planets)
        self._horizontal_layout.addWidget(self._tabwidget)
        self._layout.addWidget(self._fr_flights)
        self._layout.addLayout(self._horizontal_layout)
        self._layout.addWidget(self._statusbar)
        #
        # system tray icon
        self.tray_icon = None
        show_tray_icon = False
        if 'tray' in self.cfg:
            if (self.cfg['tray']['icon_usage'] == 'show') or \
                    (self.cfg['tray']['icon_usage'] == 'show_min'):
                self.create_tray_icon()
        #
        # try to restore last window size
        ssz = self.load_cfg_val('main_size')
        if ssz is not None:
            self.resize(ssz[0], ssz[1])
        #
        # world initialization
        self.world = XNovaWorld_instance()
        self.world_timer = QTimer(self)
        self.world_timer.timeout.connect(self.on_world_timer)

    # overrides QWidget.closeEvent
    # cleanup just before the window close
    def closeEvent(self, close_event: QCloseEvent):
        logger.debug('closing')
        if self.tray_icon is not None:
            self.tray_icon.hide()
            self.tray_icon = None
        if self.world_timer.isActive():
            self.world_timer.stop()
        self.world.script_command = 'stop'  # also stop possible running scripts
        if self.world.isRunning():
            self.world.quit()
            logger.debug('waiting for world thread to stop (5 sec)...')
            wait_res = self.world.wait(5000)
            if not wait_res:
                logger.warn('wait failed, last chance, terminating!')
                self.world.terminate()
        # store window size
        ssz = (self.width(), self.height())
        self.store_cfg_val('main_size', ssz)
        # accept the event
        close_event.accept()

    def showEvent(self, evt: QShowEvent):
        super(XNova_MainWindow, self).showEvent(evt)
        self._hidden_to_tray = False

    def changeEvent(self, evt: QEvent):
        super(XNova_MainWindow, self).changeEvent(evt)
        if evt.type() == QEvent.WindowStateChange:
            if not isinstance(evt, QWindowStateChangeEvent):
                return
            # make sure we only do this for minimize events
            if (evt.oldState() != Qt.WindowMinimized) and self.isMinimized():
                # we were minimized! explicitly hide settings widget
                #     if it is open, otherwise it will be lost forever :(
                if self.settings_widget is not None:
                    if self.settings_widget.isVisible():
                        self.settings_widget.hide()
                # should we minimize to tray?
                if self.cfg['tray']['icon_usage'] == 'show_min':
                    if not self._hidden_to_tray:
                        self._hidden_to_tray = True
                        self.hide()

    def create_tray_icon(self):
        if QSystemTrayIcon.isSystemTrayAvailable():
            logger.debug('System tray icon is available, showing')
            self.tray_icon = QSystemTrayIcon(QIcon(':/i/xnova_logo_32.png'), self)
            self.tray_icon.setToolTip(self.tr('XNova Commander'))
            self.tray_icon.activated.connect(self.on_tray_icon_activated)
            self.tray_icon.show()
        else:
            self.tray_icon = None

    def hide_tray_icon(self):
        if self.tray_icon is not None:
            self.tray_icon.hide()
            self.tray_icon.deleteLater()
            self.tray_icon = None

    def set_tray_tooltip(self, tip: str):
        if self.tray_icon is not None:
            self.tray_icon.setToolTip(tip)

    def set_status_message(self, msg: str):
        self._statusbar.set_status(msg)

    def store_cfg_val(self, category: str, value):
        pickle_filename = '{0}/{1}.dat'.format(self.config_store_dir, category)
        try:
            cache_dir = pathlib.Path(self.config_store_dir)
            if not cache_dir.exists():
                cache_dir.mkdir()
            with open(pickle_filename, 'wb') as f:
                pickle.dump(value, f)
        except pickle.PickleError as pe:
            pass
        except IOError as ioe:
            pass

    def load_cfg_val(self, category: str, default_value=None):
        value = None
        pickle_filename = '{0}/{1}.dat'.format(self.config_store_dir, category)
        try:
            with open(pickle_filename, 'rb') as f:
                value = pickle.load(f)
                if value is None:
                    value = default_value
        except pickle.PickleError as pe:
            pass
        except IOError as ioe:
            pass
        return value

    @pyqtSlot()
    def on_settings_changed(self):
        self.cfg.read('config/net.ini', encoding='utf-8')
        # maybe show/hide tray icon now?
        show_tray_icon = False
        if 'tray' in self.cfg:
            icon_usage = self.cfg['tray']['icon_usage']
            if (icon_usage == 'show') or (icon_usage == 'show_min'):
                show_tray_icon = True
        # show if needs show and hidden, or hide if shown and needs to hide
        if show_tray_icon and (self.tray_icon is None):
            logger.debug('settings changed, showing tray icon')
            self.create_tray_icon()
        elif (not show_tray_icon) and (self.tray_icon is not None):
            logger.debug('settings changed, hiding tray icon')
            self.hide_tray_icon()
        # also notify world about changed config!
        self.world.reload_config()

    def add_tab(self, widget: QWidget, title: str, closeable: bool = True) -> int:
        tab_index = self._tabwidget.addTab(widget, title, closeable)
        return tab_index

    def remove_tab(self, index: int):
        self._tabwidget.removeTab(index)

    # called by main application object just after main window creation
    # to show login widget and begin login process
    def begin_login(self):
        # create flights widget
        self.flights_widget = FlightsWidget(self._fr_flights)
        self.flights_widget.load_ui()
        install_layout_for_widget(self._fr_flights, Qt.Vertical, margins=(1, 1, 1, 1), spacing=1)
        self._fr_flights.layout().addWidget(self.flights_widget)
        self.flights_widget.set_online_state(False)
        self.flights_widget.requestShowSettings.connect(self.on_show_settings)
        # create and show login widget as first tab
        self.login_widget = LoginWidget(self._tabwidget)
        self.login_widget.load_ui()
        self.login_widget.loginError.connect(self.on_login_error)
        self.login_widget.loginOk.connect(self.on_login_ok)
        self.login_widget.show()
        self.add_tab(self.login_widget, self.tr('Login'), closeable=False)
        # self.test_setup_planets_panel()
        # self.test_planet_tab()

    def setup_planets_panel(self, planets: list):
        layout = self._panel_planets.layout()
        layout.setSpacing(0)
        remove_trailing_spacer_from_layout(layout)
        # remove all previous planet widgets from planets panel
        if layout.count() > 0:
            for i in range(layout.count()-1, -1, -1):
                li = layout.itemAt(i)
                if li is not None:
                    wi = li.widget()
                    if wi is not None:
                        if isinstance(wi, PlanetSidebarWidget):
                            layout.removeWidget(wi)
                            wi.close()
                            wi.deleteLater()  # fix possible mem leak
                            del wi
        for pl in planets:
            pw = PlanetSidebarWidget(self._panel_planets)
            pw.setPlanet(pl)
            layout.addWidget(pw)
            pw.show()
            # connections from each planet bar widget
            pw.requestOpenGalaxy.connect(self.on_request_open_galaxy_tab)
            pw.requestOpenPlanet.connect(self.on_request_open_planet_tab)
        append_trailing_spacer_to_layout(layout)

    def update_planets_panel(self):
        """
        Calls QWidget.update() on every PlanetBarWidget
        embedded in ui.panel_planets, causing repaint
        """
        layout = self._panel_planets.layout()
        if layout.count() > 0:
            for i in range(layout.count()):
                li = layout.itemAt(i)
                if li is not None:
                    wi = li.widget()
                    if wi is not None:
                        if isinstance(wi, PlanetSidebarWidget):
                            wi.update()

    def add_tab_for_planet(self, planet: XNPlanet):
        # construct planet widget and setup signals/slots
        plw = PlanetWidget(self._tabwidget)
        plw.requestOpenGalaxy.connect(self.on_request_open_galaxy_tab)
        plw.setPlanet(planet)
        # construct tab title
        tab_title = '{0} {1}'.format(planet.name, planet.coords.coords_str())
        # add tab and make it current
        tab_index = self.add_tab(plw, tab_title, closeable=True)
        self._tabwidget.setCurrentIndex(tab_index)
        self._tabwidget.tabBar().setTabIcon(tab_index, QIcon(':/i/planet_32.png'))
        return tab_index

    def add_tab_for_galaxy(self, coords: XNCoords = None):
        gw = GalaxyWidget(self._tabwidget)
        tab_title = '{0}'.format(self.tr('Galaxy'))
        if coords is not None:
            tab_title = '{0} {1}'.format(self.tr('Galaxy'), coords.coords_str())
            gw.setCoords(coords.galaxy, coords.system)
        idx = self.add_tab(gw, tab_title, closeable=True)
        self._tabwidget.setCurrentIndex(idx)
        self._tabwidget.tabBar().setTabIcon(idx, QIcon(':/i/galaxy_32.png'))

    @pyqtSlot(int)
    def on_tab_close_requested(self, idx: int):
        # logger.debug('tab close requested: {0}'.format(idx))
        if idx <= 1:  # cannot close overview or imperium tabs
            return
        self.remove_tab(idx)

    @pyqtSlot()
    def on_tab_add_clicked(self):
        pos = QCursor.pos()
        planets = self.world.get_planets()
        # logger.debug('tab bar add clicked, cursor pos = ({0}, {1})'.format(pos.x(), pos.y()))
        menu = QMenu(self)
        # galaxy view
        galaxy_action = QAction(menu)
        galaxy_action.setText(self.tr('Add galaxy view'))
        galaxy_action.setData(QVariant('galaxy'))
        menu.addAction(galaxy_action)
        # planets
        menu.addSection(self.tr('-- Planet tabs: --'))
        for planet in planets:
            action = QAction(menu)
            action.setText('{0} {1}'.format(planet.name, planet.coords.coords_str()))
            action.setData(QVariant(planet.planet_id))
            menu.addAction(action)
        action_ret = menu.exec(pos)
        if action_ret is not None:
            # logger.debug('selected action data = {0}'.format(str(action_ret.data())))
            if action_ret == galaxy_action:
                logger.debug('action_ret == galaxy_action')
                self.add_tab_for_galaxy()
                return
            # else consider this is planet widget
            planet_id = int(action_ret.data())
            self.on_request_open_planet_tab(planet_id)

    @pyqtSlot(str)
    def on_login_error(self, errstr):
        logger.error('Login error: {0}'.format(errstr))
        self.state = self.STATE_NOT_AUTHED
        self.set_status_message(self.tr('Login error: {0}').format(errstr))
        QMessageBox.critical(self, self.tr('Login error:'), errstr)

    @pyqtSlot(str, dict)
    def on_login_ok(self, login_email, cookies_dict):
        # logger.debug('Login OK, login: {0}, cookies: {1}'.format(login_email, str(cookies_dict)))
        # save login data: email, cookies
        self.state = self.STATE_AUTHED
        self.set_status_message(self.tr('Login OK, loading world'))
        self.login_email = login_email
        self.cookies_dict = cookies_dict
        #
        # destroy login widget and remove its tab
        self.remove_tab(0)
        self.login_widget.close()
        self.login_widget.deleteLater()
        self.login_widget = None
        #
        # create overview widget and add it as first tab
        self.overview_widget = OverviewWidget(self._tabwidget)
        self.overview_widget.load_ui()
        self.add_tab(self.overview_widget, self.tr('Overview'), closeable=False)
        self.overview_widget.show()
        self.overview_widget.setEnabled(False)
        #
        # create 2nd tab - Imperium
        self.imperium_widget = ImperiumWidget(self._tabwidget)
        self.add_tab(self.imperium_widget, self.tr('Imperium'), closeable=False)
        self.imperium_widget.setEnabled(False)
        #
        # initialize XNova world updater
        self.world.initialize(cookies_dict)
        self.world.set_login_email(self.login_email)
        # connect signals from world
        self.world.world_load_progress.connect(self.on_world_load_progress)
        self.world.world_load_complete.connect(self.on_world_load_complete)
        self.world.net_request_started.connect(self.on_net_request_started)
        self.world.net_request_finished.connect(self.on_net_request_finished)
        self.world.flight_arrived.connect(self.on_flight_arrived)
        self.world.build_complete.connect(self.on_building_complete)
        self.world.loaded_overview.connect(self.on_loaded_overview)
        self.world.loaded_imperium.connect(self.on_loaded_imperium)
        self.world.loaded_planet.connect(self.on_loaded_planet)
        self.world.start()

    @pyqtSlot(str, int)
    def on_world_load_progress(self, comment: str, progress: int):
        self._statusbar.set_world_load_progress(comment, progress)

    @pyqtSlot()
    def on_world_load_complete(self):
        logger.debug('main: on_world_load_complete()')
        # enable adding new tabs
        self._tabwidget.enableButtonAdd(True)
        # update statusbar
        self._statusbar.set_world_load_progress('', -1)  # turn off progress display
        self.set_status_message(self.tr('World loaded.'))
        # update account info
        if self.overview_widget is not None:
            self.overview_widget.setEnabled(True)
            self.overview_widget.update_account_info()
            self.overview_widget.update_builds()
        # update flying fleets
        self.flights_widget.set_online_state(True)
        self.flights_widget.update_flights()
        # update planets
        planets = self.world.get_planets()
        self.setup_planets_panel(planets)
        if self.imperium_widget is not None:
            self.imperium_widget.setEnabled(True)
            self.imperium_widget.update_planets()
        # update statusbar
        self._statusbar.update_online_players_count()
        # update tray tooltip, add account name
        self.set_tray_tooltip(self.tr('XNova Commander') + ' - '
                              + self.world.get_account_info().login)
        # set timer to do every-second world recalculation
        self.world_timer.setInterval(1000)
        self.world_timer.setSingleShot(False)
        self.world_timer.start()

    @pyqtSlot()
    def on_loaded_overview(self):
        logger.debug('on_loaded_overview')
        # A lot of things are updated when overview is loaded
        #  * Account information and stats
        if self.overview_widget is not None:
            self.overview_widget.update_account_info()
        #  * flights will be updated every second anyway in on_world_timer(), so no need to call
        #    self.flights_widget.update_flights()
        #  * messages count also, is updated with flights
        #  * current planet may have changed
        self.update_planets_panel()
        #  * server time is updated also
        self._statusbar.update_online_players_count()

    @pyqtSlot()
    def on_loaded_imperium(self):
        logger.debug('on_loaded_imperium')
        # need to update imperium widget
        if self.imperium_widget is not None:
            self.imperium_widget.update_planets()
        # The important note here is that imperium update is the only place where
        # the planets list is read, so number of planets, their names, etc may change here
        # Also, imperium update OVERWRITES full planets array, so, all prev
        # references to planets in all GUI elements must be invalidated, because
        # they will point to unused, outdated planets
        planets = self.world.get_planets()
        # re-create planets sidebar
        self.setup_planets_panel(planets)
        # update all builds in overview widget
        if self.overview_widget:
            self.overview_widget.update_builds()
        # update all planet tabs with new planet references
        cnt = self._tabwidget.count()
        if cnt > 2:
            for index in range(2, cnt):
                tab_page = self._tabwidget.tabWidget(index)
                if tab_page is not None:
                    try:
                        tab_type = tab_page.get_tab_type()
                        if tab_type == 'planet':
                            tab_planet = tab_page.planet()
                            new_planet = self.world.get_planet(tab_planet.planet_id)
                            tab_page.setPlanet(new_planet)
                    except AttributeError:  # not all pages may have method get_tab_type()
                        pass

    @pyqtSlot(int)
    def on_loaded_planet(self, planet_id: int):
        logger.debug('Got signal on_loaded_planet({0}), updating overview '
                     'widget and planets panel'.format(planet_id))
        if self.overview_widget:
            self.overview_widget.update_builds()
        self.update_planets_panel()
        # update also planet tab, if any
        planet = self.world.get_planet(planet_id)
        if planet is not None:
            tab_idx = self.find_tab_for_planet(planet_id)
            if tab_idx != -1:
                tab_widget = self._tabwidget.tabWidget(tab_idx)
                if isinstance(tab_widget, PlanetWidget):
                    logger.debug('Updating planet tab #{}'.format(tab_idx))
                    tab_widget.setPlanet(planet)

    @pyqtSlot()
    def on_world_timer(self):
        if self.world:
            self.world.world_tick()
        self.update_planets_panel()
        if self.flights_widget:
            self.flights_widget.update_flights()
        if self.overview_widget:
            self.overview_widget.update_builds()
        if self.imperium_widget:
            self.imperium_widget.update_planet_resources()

    @pyqtSlot()
    def on_net_request_started(self):
        self._statusbar.set_loading_status(True)

    @pyqtSlot()
    def on_net_request_finished(self):
        self._statusbar.set_loading_status(False)

    @pyqtSlot(int)
    def on_tray_icon_activated(self, reason):
        # QSystemTrayIcon::Unknown	0	Unknown reason
        # QSystemTrayIcon::Context	1	The context menu for the system tray entry was requested
        # QSystemTrayIcon::DoubleClick	2	The system tray entry was double clicked
        # QSystemTrayIcon::Trigger	3	The system tray entry was clicked
        # QSystemTrayIcon::MiddleClick	4	The system tray entry was clicked with the middle mouse button
        if reason == QSystemTrayIcon.Trigger:
            # left-click
            self.setWindowState((self.windowState() & ~Qt.WindowMinimized) | Qt.WindowActive)
            self.show()
            return

    def show_tray_message(self, title, message, icon_type=None, timeout_ms=None):
        """
        Shows message from system tray icon, if system supports it.
        If no support, this is just a no-op
        :param title: message title
        :param message: message text
        :param icon_type: one of:
        QSystemTrayIcon.NoIcon      0   No icon is shown.
        QSystemTrayIcon.Information 1   An information icon is shown.
        QSystemTrayIcon.Warning     2   A standard warning icon is shown.
        QSystemTrayIcon.Critical    3   A critical warning icon is shown
        """
        if self.tray_icon is None:
            return
        if self.tray_icon.supportsMessages():
            if icon_type is None:
                icon_type = QSystemTrayIcon.Information
            if timeout_ms is None:
                timeout_ms = 10000
            self.tray_icon.showMessage(title, message, icon_type, timeout_ms)
        else:
            logger.info('This system does not support tray icon messages.')

    @pyqtSlot()
    def on_show_settings(self):
        if self.settings_widget is not None:
            self.settings_widget.show()
            self.settings_widget.showNormal()

    @pyqtSlot(XNFlight)
    def on_flight_arrived(self, fl: XNFlight):
        logger.debug('main: flight arrival: {0}'.format(fl))
        mis_str = flight_mission_for_humans(fl.mission)
        if fl.direction == 'return':
            mis_str += ' ' + self.tr('return')
        short_fleet_info = self.tr('{0} {1} => {2}, {3} ship(s)').format(
            mis_str, fl.src, fl.dst, len(fl.ships))
        self.show_tray_message(self.tr('XNova: Fleet arrived'), short_fleet_info)

    @pyqtSlot(XNPlanet, XNPlanetBuildingItem)
    def on_building_complete(self, planet: XNPlanet, bitem: XNPlanetBuildingItem):
        logger.debug('main: build complete: on planet {0}: {1}'.format(
            planet.name, str(bitem)))
        # update also planet tab, if any
        if isinstance(planet, XNPlanet):
            tab_idx = self.find_tab_for_planet(planet.planet_id)
            if tab_idx != -1:
                tab_widget = self._tabwidget.tabWidget(tab_idx)
                if isinstance(tab_widget, PlanetWidget):
                    logger.debug('Updating planet tab #{}'.format(tab_idx))
                    tab_widget.setPlanet(planet)
        # construct message to show in tray
        if bitem.is_shipyard_item:
            binfo_str = '{0} x {1}'.format(bitem.quantity, bitem.name)
        else:
            binfo_str = self.tr('{0} lv.{1}').format(bitem.name, bitem.level)
        msg = self.tr('{0} has built {1}').format(planet.name, binfo_str)
        self.show_tray_message(self.tr('XNova: Building complete'), msg)

    @pyqtSlot(XNCoords)
    def on_request_open_galaxy_tab(self, coords: XNCoords):
        tab_index = self.find_tab_for_galaxy(coords.galaxy, coords.system)
        if tab_index == -1:  # create new tab for these coords
            self.add_tab_for_galaxy(coords)
            return
        # else switch to that tab
        self._tabwidget.setCurrentIndex(tab_index)

    @pyqtSlot(int)
    def on_request_open_planet_tab(self, planet_id: int):
        tab_index = self.find_tab_for_planet(planet_id)
        if tab_index == -1: # create new tab for planet
            planet = self.world.get_planet(planet_id)
            if planet is not None:
                self.add_tab_for_planet(planet)
                return
        # else switch to that tab
        self._tabwidget.setCurrentIndex(tab_index)

    def find_tab_for_planet(self, planet_id: int) -> int:
        """
        Finds tab index where specified planet is already opened
        :param planet_id: planet id to search for
        :return: tab index, or -1 if not found
        """
        cnt = self._tabwidget.count()
        if cnt < 3:
            return -1  # only overview and imperium tabs are present
        for index in range(2, cnt):
            tab_page = self._tabwidget.tabWidget(index)
            if tab_page is not None:
                try:
                    tab_type = tab_page.get_tab_type()
                    if tab_type == 'planet':
                        tab_planet = tab_page.planet()
                        if tab_planet.planet_id == planet_id:
                            # we have found tab index where this planet is already opened
                            return index
                except AttributeError:  # not all pages may have method get_tab_type()
                    pass
        return -1

    def find_tab_for_galaxy(self, galaxy: int, system: int) -> int:
        """
        Finds tab index where specified galaxy view is already opened
        :param galaxy: galaxy target coordinate
        :param system: system target coordinate
        :return: tab index, or -1 if not found
        """
        cnt = self._tabwidget.count()
        if cnt < 3:
            return -1  # only overview and imperium tabs are present
        for index in range(2, cnt):
            tab_page = self._tabwidget.tabWidget(index)
            if tab_page is not None:
                try:
                    tab_type = tab_page.get_tab_type()
                    if tab_type == 'galaxy':
                        coords = tab_page.coords()
                        if (coords[0] == galaxy) and (coords[1] == system):
                            # we have found galaxy tab index where this place is already opened
                            return index
                except AttributeError:  # not all pages may have method get_tab_type()
                    pass
        return -1

    def test_setup_planets_panel(self):
        """
        Testing only - add 'fictive' planets to test planets panel without loading data
        :return: None
        """
        pl1 = XNPlanet('Arnon', XNCoords(1, 7, 6))
        pl1.pic_url = 'skins/default/planeten/small/s_normaltempplanet08.jpg'
        pl1.fields_busy = 90
        pl1.fields_total = 167
        pl1.is_current = True
        pl2 = XNPlanet('Safizon', XNCoords(1, 232, 7))
        pl2.pic_url = 'skins/default/planeten/small/s_dschjungelplanet05.jpg'
        pl2.fields_busy = 84
        pl2.fields_total = 207
        pl2.is_current = False
        test_planets = [pl1, pl2]
        self.setup_planets_panel(test_planets)

    def test_planet_tab(self):
        """
        Testing only - add 'fictive' planet tab to test UI without loading world
        :return:
        """
        # construct planet
        pl1 = XNPlanet('Arnon', coords=XNCoords(1, 7, 6), planet_id=12345)
        pl1.pic_url = 'skins/default/planeten/small/s_normaltempplanet08.jpg'
        pl1.fields_busy = 90
        pl1.fields_total = 167
        pl1.is_current = True
        pl1.res_current.met = 10000000
        pl1.res_current.cry = 50000
        pl1.res_current.deit = 250000000  # 250 mil
        pl1.res_per_hour.met = 60000
        pl1.res_per_hour.cry = 30000
        pl1.res_per_hour.deit = 15000
        pl1.res_max_silos.met = 6000000
        pl1.res_max_silos.cry = 3000000
        pl1.res_max_silos.deit = 1000000
        pl1.energy.energy_left = 10
        pl1.energy.energy_total = 1962
        pl1.energy.charge_percent = 92
        # planet building item
        bitem = XNPlanetBuildingItem()
        bitem.gid = 1
        bitem.name = 'Рудник металла'
        bitem.level = 29
        bitem.remove_link = ''
        bitem.build_link = '?set=buildings&cmd=insert&building={0}'.format(bitem.gid)
        bitem.seconds_total = 23746
        bitem.cost_met = 7670042
        bitem.cost_cry = 1917510
        bitem.is_building_item = True
        # second bitem
        bitem2 = XNPlanetBuildingItem()
        bitem2.gid = 2
        bitem2.name = 'Рудник кристалла'
        bitem2.level = 26
        bitem2.remove_link = ''
        bitem2.build_link = '?set=buildings&cmd=insert&building={0}'.format(bitem2.gid)
        bitem2.seconds_total = 13746
        bitem2.cost_met = 9735556
        bitem2.cost_cry = 4667778
        bitem2.is_building_item = True
        bitem2.is_downgrade = True
        bitem2.seconds_left = bitem2.seconds_total // 2
        bitem2.calc_end_time()
        # add bitems
        pl1.buildings_items = [bitem, bitem2]
        # add
        self.add_tab_for_planet(pl1)
Beispiel #31
0
class MainWindow(QMainWindow):

    """ A window that shows the current status of the encrypted container
        and a button to unlock/close it. Open containers
        leave an icon in the systray as a reminder to close them eventually.
    """

    def __init__(self, device_name=None, container_path=None, key_file=None, mount_point=None):
        """ Command line arguments checks are done here to be able to display a graphical dialog with error messages .
            If no arguments were supplied on the command line a setup dialog will be shown.
            All commands will be executed from a separate worker process with administrator privileges that gets initialized here.
            :param device_name: The device mapper name
            :type device_name: str/unicode or None
            :param container_path: The path of the container file
            :type container_path: str/unicode or None
            :param key_file: The path of an optional key file
            :type key_file: str/unicode or None
            :param mount_point: The path of an optional mount point
            :type mount_point: str/unicode or None
        """
        super(MainWindow, self).__init__()

        self.luks_device_name = device_name
        self.encrypted_container = container_path
        self.key_file = key_file
        self.mount_point = mount_point

        self.worker = None
        self.is_waiting_for_worker = False
        self.is_unlocked = False
        self.is_initialized = False
        self.has_tray = QSystemTrayIcon.isSystemTrayAvailable()

        # L10n: program name - translatable for startmenu titlebar etc
        self.setWindowTitle(_('luckyLUKS'))
        self.setWindowIcon(QIcon.fromTheme('dialog-password', QApplication.style().standardIcon(QStyle.SP_DriveHDIcon)))

        # check if cryptsetup and sudo are installed
        not_installed_msg = _('{program_name} executable not found!\nPlease install, eg for Debian/Ubuntu\n`apt-get install {program_name}`')
        if not utils.is_installed('cryptsetup'):
            show_alert(self, not_installed_msg.format(program_name='cryptsetup'), critical=True)
        if not utils.is_installed('sudo'):
            show_alert(self, not_installed_msg.format(program_name='sudo'), critical=True)
        # quick sanity checks before asking for passwd
        if os.getuid() == 0:
            show_alert(self, _('Graphical programs should not be run as root!\nPlease call as normal user.'), critical=True)
        if self.encrypted_container and not os.path.exists(self.encrypted_container):
            show_alert(self, _('Container file not accessible\nor path does not exist:\n\n{file_path}').format(file_path=self.encrypted_container), critical=True)

        # only either encrypted_container or luks_device_name supplied
        if bool(self.encrypted_container) != bool(self.luks_device_name):
            show_alert(self, _('Invalid arguments:\n'
                               'Please call without any arguments\n'
                               'or supply both container and name.\n\n'
                               '<b>{executable} -c CONTAINER -n NAME [-m MOUNTPOINT]</b>\n\n'
                               'CONTAINER = Path of the encrypted container file\n'
                               'NAME = A (unique) name to identify the unlocked container\n'
                               'Optional: MOUNTPOINT = where to mount the encrypted filesystem\n\n'
                               'If automatic mounting is configured on your system,\n'
                               'explicitly setting a mountpoint is not required\n\n'
                               'For more information, visit\n'
                               '<a href="{project_url}">{project_url}</a>'
                               ).format(executable=os.path.basename(sys.argv[0]),
                                        project_url=PROJECT_URL), critical=True)

        # spawn worker process with root privileges
        try:
            self.worker = utils.WorkerMonitor(self)
            # start communication thread
            self.worker.start()
        except utils.SudoException as se:
            show_alert(self, format_exception(se), critical=True)
            return

        # if no arguments supplied, display dialog to gather this information
        if self.encrypted_container is None and self.luks_device_name is None:

            from luckyLUKS.setupUI import SetupDialog
            sd = SetupDialog(self)

            if sd.exec_() == QDialog.Accepted:
                self.luks_device_name = sd.get_luks_device_name()
                self.encrypted_container = sd.get_encrypted_container()
                self.mount_point = sd.get_mount_point()
                self.key_file = sd.get_keyfile()

                self.is_unlocked = True  # all checks in setup dialog -> skip initializing state
            else:
                # user closed dialog -> quit program
                # and check if a keyfile create thread has to be stopped
                # the worker process terminates itself when its parent dies
                if hasattr(sd, 'create_thread') and sd.create_thread.isRunning():
                    sd.create_thread.terminate()
                QApplication.instance().quit()
                return

        # center window on desktop
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

        # widget content
        main_grid = QGridLayout()
        main_grid.setSpacing(10)
        icon = QLabel()
        icon.setPixmap(QIcon.fromTheme('dialog-password', QApplication.style().standardIcon(QStyle.SP_DriveHDIcon)).pixmap(32))
        main_grid.addWidget(icon, 0, 0)
        main_grid.addWidget(QLabel('<b>' + _('Handle encrypted container') + '</b>\n'), 0, 1, alignment=Qt.AlignCenter)

        main_grid.addWidget(QLabel(_('Name:')), 1, 0)
        main_grid.addWidget(QLabel('<b>{dev_name}</b>'.format(dev_name=self.luks_device_name)), 1, 1, alignment=Qt.AlignCenter)

        main_grid.addWidget(QLabel(_('File:')), 2, 0)
        main_grid.addWidget(QLabel(self.encrypted_container), 2, 1, alignment=Qt.AlignCenter)

        if self.key_file is not None:
            main_grid.addWidget(QLabel(_('Key:')), 3, 0)
            main_grid.addWidget(QLabel(self.key_file), 3, 1, alignment=Qt.AlignCenter)

        if self.mount_point is not None:
            main_grid.addWidget(QLabel(_('Mount:')), 4, 0)
            main_grid.addWidget(QLabel(self.mount_point), 4, 1, alignment=Qt.AlignCenter)

        main_grid.addWidget(QLabel(_('Status:')), 5, 0)
        self.label_status = QLabel('')
        main_grid.addWidget(self.label_status, 5, 1, alignment=Qt.AlignCenter)

        self.button_toggle_status = QPushButton('')
        self.button_toggle_status.setMinimumHeight(34)
        self.button_toggle_status.clicked.connect(self.toggle_container_status)
        main_grid.setRowMinimumHeight(6, 10)
        main_grid.addWidget(self.button_toggle_status, 7, 1)

        widget = QWidget()
        widget.setLayout(main_grid)
        widget.setContentsMargins(10, 10, 10, 10)
        self.setCentralWidget(widget)

        # tray popup menu
        if self.has_tray:
            tray_popup = QMenu(self)
            tray_popup.addAction(QIcon.fromTheme('dialog-password', QApplication.style().standardIcon(QStyle.SP_DriveHDIcon)), self.luks_device_name).setEnabled(False)
            tray_popup.addSeparator()
            self.tray_toggle_action = QAction(QApplication.style().standardIcon(QStyle. SP_DesktopIcon), _('Hide'), self)
            self.tray_toggle_action.triggered.connect(self.toggle_main_window)
            tray_popup.addAction(self.tray_toggle_action)
            quit_action = QAction(QApplication.style().standardIcon(QStyle.SP_MessageBoxCritical), _('Quit'), self)
            quit_action.triggered.connect(self.tray_quit)
            tray_popup.addAction(quit_action)
            # systray
            self.tray = QSystemTrayIcon(self)
            self.tray.setIcon(QIcon.fromTheme('dialog-password', QApplication.style().standardIcon(QStyle.SP_DriveHDIcon)))
            self.tray.setContextMenu(tray_popup)
            self.tray.activated.connect(self.toggle_main_window)
            self.tray.show()

        self.init_status()

    def refresh(self):
        """ Update widgets to reflect current container status. Adds systray icon if needed """
        if self.is_unlocked:
            self.label_status.setText(_('Container is {unlocked_green_bold}').format(
                unlocked_green_bold='<font color="#006400"><b>' + _('unlocked') + '</b></font>'))
            self.button_toggle_status.setText(_('Close Container'))
            if self.has_tray:
                self.tray.setToolTip(_('{device_name} is unlocked').format(device_name=self.luks_device_name))
        else:
            self.label_status.setText(_('Container is {closed_red_bold}').format(
                closed_red_bold='<font color="#b22222"><b>' + _('closed') + '</b></font>'))
            self.button_toggle_status.setText(_('Unlock Container'))
            if self.has_tray:
                self.tray.setToolTip(_('{device_name} is closed').format(device_name=self.luks_device_name))

        self.show()
        self.setFixedSize(self.sizeHint())

    def tray_quit(self):
        """ Triggered by clicking on `quit` in the systray popup: asks to close an unlocked container """
        if not self.is_unlocked:
            QApplication.instance().quit()
        elif not self.is_waiting_for_worker:
            self.show()
            self.confirm_close()

    def toggle_main_window(self, tray_icon_clicked):
        """ Triggered by clicking on the systray icon: show/hide main window """
        if not tray_icon_clicked or tray_icon_clicked == QSystemTrayIcon.Trigger:  # don't activate on rightclick/contextmenu
            if self.isVisible():
                self.hide()
                self.tray_toggle_action.setText(_('Show'))
            else:
                self.show()
                self.tray_toggle_action.setText(_('Hide'))

    def closeEvent(self, event):
        """ Triggered by closing the window: If the container is unlocked, the program won't quit but remain in the systray. """
        if not self.is_waiting_for_worker:
            if self.is_unlocked:
                if self.has_tray:
                    self.hide()
                    self.tray_toggle_action.setText(_('Show'))
                else:
                    self.confirm_close()
                event.ignore()
            else:
                event.accept()

    def confirm_close(self):
        """ Inform about opened container and ask for confirmation to close & quit """
        message = _('<b>{device_name}</b> >> {container_path}\n'
                    'is currently <b>unlocked</b>,\n'
                    'Close Container now and quit?').format(device_name=self.luks_device_name,
                                                            container_path=self.encrypted_container)
        mb = QMessageBox(QMessageBox.Question, '', message, QMessageBox.Ok | QMessageBox.Cancel, self)
        mb.button(QMessageBox.Ok).setText(_('Quit'))
        if mb.exec_() == QMessageBox.Ok:
            self.do_close_container(shutdown=True)

    def customEvent(self, event):
        """ Receives response from worker and calls supplied callback function """
        event.callback(event.response)

    def toggle_container_status(self):
        """ Unlock or close container """
        if self.is_unlocked:
            self.do_close_container()
        else:
            try:
                UnlockContainerDialog(self, self.worker, self.luks_device_name, self.encrypted_container, self.key_file, self.mount_point).communicate()
                self.is_unlocked = True
            except UserInputError as uie:
                show_alert(self, format_exception(uie))
                self.is_unlocked = False
            self.refresh()

    def do_close_container(self, shutdown=False):
        """ Send close command to worker and supply callbacks
            :param shutdown: Quit application after container successfully closed? (default=False)
            :type shutdown: bool
        """
        self.disable_ui(_('Closing Container ..'))
        self.worker.execute(command={'type': 'request',
                                     'msg': 'close',
                                     'device_name': self.luks_device_name,
                                     'container_path': self.encrypted_container
                                     },
                            success_callback=lambda msg: self.on_container_closed(msg, error=False, shutdown=shutdown),
                            error_callback=lambda msg: self.on_container_closed(msg, error=True, shutdown=shutdown))

    def on_container_closed(self, message, error, shutdown):
        """ Callback after worker closed container
            :param message: Contains an error description if error=True, otherwise the current state of the container (unlocked/closed)
            :type message: str
            :param error: Error during closing of container
            :type error: bool
            :param shutdown: Quit application after container successfully closed?
            :type shutdown: bool
        """
        if error:
            show_alert(self, message)
        else:
            self.is_unlocked = False
        if not error and shutdown:  # automatic shutdown only if container successfully closed
            QApplication.instance().quit()
        else:
            self.enable_ui()

    def init_status(self):
        """ Request current status of container from worker if needed """
        if not self.is_unlocked:
            self.disable_ui(_('Initializing ..'))
            self.worker.execute(command={'type': 'request',
                                         'msg': 'status',
                                         'device_name': self.luks_device_name,
                                         'container_path': self.encrypted_container,
                                         'key_file': self.key_file,
                                         'mount_point': self.mount_point
                                         },
                                success_callback=self.on_initialized,
                                error_callback=lambda msg: self.on_initialized(msg, error=True))
        else:  # unlocked by setup-dialog -> just refresh UI
            self.enable_ui()
        self.is_initialized = True  # qt event loop can start now

    def on_initialized(self, message, error=False):
        """ Callback after worker send current state of container
            :param message: Contains an error description if error=True, otherwise the current state of the container (unlocked/closed)
            :type message: str
            :param critical: Error during initialization (default=False)
            :type critical: bool
        """
        if error:
            show_alert(self, message, critical=True)
        else:
            self.is_unlocked = (True if message == 'unlocked' else False)
            self.enable_ui()

    def enable_ui(self):
        """ Enable buttons and refresh state """
        self.refresh()
        self.is_waiting_for_worker = False
        self.button_toggle_status.setEnabled(True)

    def disable_ui(self, reason):
        """ Disable buttons and display waiting message
            :param reason: A waiting message that gets displayed
            :type reason: str/unicode
        """
        self.is_waiting_for_worker = True
        self.button_toggle_status.setText(reason)
        self.button_toggle_status.setEnabled(False)
Beispiel #32
0
class TrayIcon(QObject):

    def __init__(self):
        super().__init__()
        self._supported = QSystemTrayIcon.isSystemTrayAvailable()
        self._context_menu = None
        self._enabled = False
        self._trayicon = None
        self._state_icons = {}
        for state in (
            'disconnected',
            'disabled',
            'enabled',
        ):
            icon = QIcon(':/state-%s.svg' % state)
            if hasattr(icon, 'setIsMask'):
                icon.setIsMask(True)
            self._state_icons[state] = icon
        self._machine = None
        self._machine_state = 'disconnected'
        self._is_running = False
        self._update_state()

    def set_menu(self, menu):
        self._context_menu = menu
        if self._enabled:
            self._trayicon.setContextMenu(menu)

    def log(self, level, message):
        if self._enabled:
            if level <= log.INFO:
                icon = QSystemTrayIcon.Information
                timeout = 10
            elif level <= log.WARNING:
                icon = QSystemTrayIcon.Warning
                timeout = 15
            else:
                icon = QSystemTrayIcon.Critical
                timeout = 25
            self._trayicon.showMessage(__software_name__.capitalize(),
                                       message, icon, timeout * 1000)
        else:
            if level <= log.INFO:
                icon = QMessageBox.Information
            elif level <= log.WARNING:
                icon = QMessageBox.Warning
            else:
                icon = QMessageBox.Critical
            msgbox = QMessageBox()
            msgbox.setText(message)
            msgbox.setIcon(icon)
            msgbox.exec_()

    def is_supported(self):
        return self._supported

    def enable(self):
        if not self._supported:
            return
        self._trayicon = QSystemTrayIcon()
        # On OS X, the context menu is activated with either mouse buttons,
        # and activation messages are still sent, so ignore those...
        if not sys.platform.startswith('darwin'):
            self._trayicon.activated.connect(self._on_activated)
        if self._context_menu is not None:
            self._trayicon.setContextMenu(self._context_menu)
        self._enabled = True
        self._update_state()
        self._trayicon.show()

    def disable(self):
        if not self._enabled:
            return
        self._trayicon.hide()
        self._trayicon = None
        self._enabled = False

    def is_enabled(self):
        return self._enabled

    def update_machine_state(self, machine, state):
        self._machine = machine
        self._machine_state = state
        self._update_state()

    def update_output(self, enabled):
        self._is_running = enabled
        self._update_state()

    clicked = pyqtSignal()

    def _update_state(self):
        if self._machine_state not in ('initializing', 'connected'):
            state = 'disconnected'
        else:
            state = 'enabled' if self._is_running else 'disabled'
        icon = self._state_icons[state]
        if not self._enabled:
            return
        machine_state = _('{machine} is {state}').format(
            machine=_(self._machine),
            state=_(self._machine_state),
        )
        if self._is_running:
            output_state = _('output is enabled')
        else:
            output_state = _('output is disabled')
        self._trayicon.setIcon(icon)
        self._trayicon.setToolTip(
            'Plover:\n- %s\n- %s' % (output_state, machine_state)
        )

    def _on_activated(self, reason):
        if reason == QSystemTrayIcon.Trigger:
            self.clicked.emit()
class MainWidget(QTabWidget):

    """Custom main widget."""

    def __init__(self, parent=None, *args, **kwargs):
        """Init class custom tab widget."""
        super(MainWidget, self).__init__(parent=None, *args, **kwargs)
        self.parent = parent
        self.setTabBar(TabBar(self))
        self.setMovable(False)
        self.setTabsClosable(False)
        self.setTabShape(QTabWidget.Triangular)
        self.init_preview()
        self.init_corner_menus()
        self.init_tray()
        self.addTab(TabSearch(self), "Search")
        self.addTab(TabTool(self), "Tools")
        self.addTab(TabHtml(self), "HTML")
        self.addTab(TabSymbols(self), "Symbols")
        self._recent_tab = TabRecent(self)
        self.recentify = self._recent_tab.recentify  # shortcut
        self.addTab(self._recent_tab, "Recent")
        self.widgets_to_tabs(self.json_to_widgets(UNICODEMOTICONS))
        self.make_trayicon()
        self.setMinimumSize(QDesktopWidget().screenGeometry().width() // 1.5,
                            QDesktopWidget().screenGeometry().height() // 1.5)
        # self.showMaximized()

    def init_preview(self):
        self.previews, self.timer = [], QTimer(self)
        self.fader, self.previous_pic = FaderWidget(self), None
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(lambda: [_.close() for _ in self.previews])
        self.taimer, self.preview = QTimer(self), QLabel("Preview")
        self.taimer.setSingleShot(True)
        self.taimer.timeout.connect(lambda: self.preview.hide())
        font = self.preview.font()
        font.setPixelSize(100)
        self.preview.setFont(font)
        self.preview.setDisabled(True)
        self.preview.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
        self.preview.setAttribute(Qt.WA_TranslucentBackground, True)

    def init_corner_menus(self):
        self.menu_1, self.menu_0 = QToolButton(self), QToolButton(self)
        self.menu_1.setText(" ⚙ ")
        self.menu_1.setToolTip("<b>Options, Extras")
        self.menu_0.setText(" ? ")
        self.menu_0.setToolTip("<b>Help, Info")
        font = self.menu_1.font()
        font.setBold(True)
        self.menu_1.setFont(font)
        self.menu_0.setFont(font)
        self.menu_tool, self.menu_help = QMenu("Tools Extras"), QMenu("Help")
        self.menu_tool.addAction(" Tools & Extras ").setDisabled(True)
        self.menu_help.addAction(" Help & Info ").setDisabled(True)
        self.menu_0.setMenu(self.menu_help)
        self.menu_1.setMenu(self.menu_tool)
        self.menu_tool.addAction("Explain Unicode", self.make_explain_unicode)
        self.menu_tool.addAction("Alternate Case Clipboard",
                                 self.alternate_clipboard)
        self.menu_tool.addSeparator()
        self.menu_tool.addAction("AutoCenter Window", self.center)
        self.menu_tool.addAction("Set Icon", self.set_icon)
        self.menu_tool.addAction(  # force recreate desktop file
            "Add Launcher to Desktop", lambda: set_desktop_launcher(
                "unicodemoticon", AUTOSTART_DESKTOP_FILE, True))
        self.menu_tool.addAction("Move to Mouse position",
                                 self.move_to_mouse_position)
        self.menu_tool.addSeparator()
        self.menu_tool.addAction("Minimize", self.showMinimized)
        self.menu_tool.addAction("Hide", self.hide)
        self.menu_tool.addAction("Quit", exit)
        self.menu_help.addAction("About Qt 5",
                                 lambda: QMessageBox.aboutQt(None))
        self.menu_help.addAction("About Unicodemoticon",
                                 lambda: open_new_tab(__url__))
        self.setCornerWidget(self.menu_1, 1)
        self.setCornerWidget(self.menu_0, 0)
        self.currentChanged.connect(self.make_tabs_previews)
        self.currentChanged.connect(self.make_tabs_fade)

    def init_tray(self):
        self.tray, self.menu = QSystemTrayIcon(self), QMenu(__doc__)
        self.menu.addAction("    Emoticons").setDisabled(True)
        self.menu.setIcon(self.windowIcon())
        self.menu.addSeparator()
        self.menu.setProperty("emoji_menu", True)
        list_of_labels = sorted(UNICODEMOTICONS.keys())  # menus
        menus = [self.menu.addMenu(_.title()) for _ in list_of_labels]
        self.menu.addSeparator()
        log.debug("Building Emoticons SubMenus.")
        for item, label in zip(menus, list_of_labels):
            item.setStyleSheet("padding:0;margin:0;border:0;menu-scrollable:1")
            font = item.font()
            font.setPixelSize(20)
            item.setFont(font)
            self.build_submenu(UNICODEMOTICONS[label.lower()], item)
        self.menu.addSeparator()
        self.menu.addAction("Alternate Case Clipboard",
                            self.alternate_clipboard)
        self.menu.addSeparator()
        self.menu.addAction("Quit", exit)
        self.menu.addAction("Show", self.showMaximized)
        self.menu.addAction("Minimize", self.showMinimized)
        self.tray.setContextMenu(self.menu)

    def build_submenu(self, char_list: (str, tuple), submenu: QMenu) -> QMenu:
        """Take a list of characters and a submenu and build actions on it."""
        submenu.setProperty("emoji_menu", True)
        submenu.setWindowOpacity(0.9)
        submenu.setToolTipsVisible(True)
        for _char in sorted(char_list):
            action = submenu.addAction(_char.strip())
            action.setToolTip(self.get_description(_char))
            action.hovered.connect(lambda _, ch=_char: self.make_preview(ch))
            action.triggered.connect(
                lambda _, char=_char: QApplication.clipboard().setText(char))
        return submenu

    def make_trayicon(self):
        """Make a Tray Icon."""
        if self.windowIcon() and __doc__:
            self.tray.setIcon(self.windowIcon())
            self.tray.setToolTip(__doc__)
            self.tray.activated.connect(
                lambda: self.hide() if self.isVisible()
                else self.showMaximized())
            return self.tray.show()

    def make_explain_unicode(self) -> tuple:
        """Make an explanation from unicode entered,if at least 1 chars."""
        explanation, uni = "", None
        uni = str(QInputDialog.getText(
            None, __doc__, "<b>Type Unicode character to explain?")[0]).strip()
        if uni and len(uni):
            explanation = ", ".join([self.get_description(_) for _ in uni])
            QMessageBox.information(None, __doc__, str((uni, explanation)))
        log.debug((uni, explanation))
        return (uni, explanation)

    def alternate_clipboard(self) -> str:
        """Make alternating camelcase clipboard."""
        return QApplication.clipboard().setText(
            self.make_alternate_case(str(QApplication.clipboard().text())))

    def make_alternate_case(self, stringy: str) -> str:
        """Make alternating camelcase string."""
        return "".join([_.lower() if i % 2 else _.upper()
                        for i, _ in enumerate(stringy)])

    def get_description(self, emote: str):
        description = ""
        try:
            description = unicodedata.name(str(emote).strip()).title()
        except ValueError:
            log.debug("Description not found for Unicode: " + emote)
        finally:
            return description

    def make_preview(self, emoticon_text: str):
        """Make Emoticon Previews for the current Hovered one."""
        log.debug(emoticon_text)
        if self.taimer.isActive():  # Be Race Condition Safe
            self.taimer.stop()
        self.preview.setText(" " + emoticon_text + " ")
        self.preview.move(QCursor.pos())
        self.preview.show()
        self.taimer.start(1000)  # how many time display the previews

    def json_to_widgets(self, jotason: dict):
        """Take a json string object return QWidgets."""
        dict_of_widgets, row = {}, 0
        for titlemotes in tuple(sorted(jotason.items())):
            tit = str(titlemotes[0]).strip()[:9].title()
            area = ScrollGroup(tit)
            layout = area.layout()

            grid_cols = 2 if tit.lower() == "multichar" else 8
            for index, emote in enumerate(tuple(set(list(titlemotes[1])))):
                button = QPushButton(emote, self)
                button.clicked.connect(lambda _, c=emote:
                                       QApplication.clipboard().setText(c))
                button.released.connect(self.hide)
                button.released.connect(lambda c=emote: self.recentify(c))
                button.pressed.connect(lambda c=emote: self.make_preview(c))
                button.setToolTip("<center><h1>{0}<br>{1}".format(
                    emote, self.get_description(emote)))
                button.setFlat(True)
                font = button.font()
                font.setPixelSize(50)
                button.setFont(font)
                row = row + 1 if not index % grid_cols else row
                layout.addWidget(button, row, index % grid_cols)

            dict_of_widgets[tit] = area
        return dict_of_widgets

    def widgets_to_tabs(self, dict_of_widgets: dict):
        """Take a dict of widgets and build tabs from them."""
        for title, widget in tuple(sorted(dict_of_widgets.items())):
            self.addTab(widget, title)

    def center(self):
        """Center Window on the Current Screen,with Multi-Monitor support."""
        self.showNormal()
        self.resize(QDesktopWidget().screenGeometry().width() // 1.5,
                    QDesktopWidget().screenGeometry().height() // 1.5)
        window_geometry = self.frameGeometry()
        mousepointer_position = QApplication.desktop().cursor().pos()
        screen = QApplication.desktop().screenNumber(mousepointer_position)
        centerPoint = QApplication.desktop().screenGeometry(screen).center()
        window_geometry.moveCenter(centerPoint)
        return bool(not self.move(window_geometry.topLeft()))

    def move_to_mouse_position(self):
        """Center the Window on the Current Mouse position."""
        self.showNormal()
        self.resize(QDesktopWidget().screenGeometry().width() // 1.5,
                    QDesktopWidget().screenGeometry().height() // 1.5)
        window_geometry = self.frameGeometry()
        window_geometry.moveCenter(QApplication.desktop().cursor().pos())
        return bool(not self.move(window_geometry.topLeft()))

    def set_icon(self, icon: (None, str)=None) -> str:
        """Return a string with opendesktop standard icon name for Qt."""
        if not icon:
            try:
                cur_idx = STD_ICON_NAMES.index(self.windowIcon().name())
            except ValueError:
                cur_idx = 0
            icon = QInputDialog.getItem(None, __doc__, "<b>Choose Icon name?:",
                                        STD_ICON_NAMES, cur_idx, False)[0]
        if icon:
            log.debug("Setting Tray and Window Icon name to:{}.".format(icon))
            self.tray.setIcon(QIcon.fromTheme("{}".format(icon)))
            self.setWindowIcon(QIcon.fromTheme("{}".format(icon)))
        return icon

    def make_tabs_fade(self, index):
        """Make tabs fading transitions."""
        self.fader.fade(
            self.previous_pic, self.widget(index).geometry(),
            1 if self.tabPosition() else self.tabBar().tabRect(0).height())
        self.previous_pic = self.currentWidget().grab()

    def make_undock(self):
        """Undock a Tab from TabWidget and promote to a Dialog."""
        dialog, index = QDialog(self), self.currentIndex()
        widget_from_tab = self.widget(index)
        dialog_layout = QVBoxLayout(dialog)
        dialog.setWindowTitle(self.tabText(index))
        dialog.setToolTip(self.tabToolTip(index))
        dialog.setWhatsThis(self.tabWhatsThis(index))
        dialog.setWindowIcon(self.tabIcon(index))
        dialog.setFont(widget_from_tab.font())
        dialog.setStyleSheet(widget_from_tab.styleSheet())
        dialog.setMinimumSize(widget_from_tab.minimumSize())
        dialog.setMaximumSize(widget_from_tab.maximumSize())
        dialog.setGeometry(widget_from_tab.geometry())

        def closeEvent_override(event):
            """Re-dock back from Dialog to a new Tab."""
            msg = "<b>Close this Floating Tab Window and Re-Dock as a new Tab?"
            conditional = QMessageBox.question(
                self, "Undocked Tab", msg, QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No) == QMessageBox.Yes
            if conditional:
                index_plus_1 = self.count() + 1
                self.insertTab(index_plus_1, widget_from_tab,
                               dialog.windowIcon(), dialog.windowTitle())
                self.setTabToolTip(index_plus_1, dialog.toolTip())
                self.setTabWhatsThis(index_plus_1, dialog.whatsThis())
                return event.accept()
            else:
                return event.ignore()

        dialog.closeEvent = closeEvent_override
        self.removeTab(index)
        widget_from_tab.setParent(self.parent if self.parent else dialog)
        dialog_layout.addWidget(widget_from_tab)
        dialog.setLayout(dialog_layout)
        widget_from_tab.show()
        dialog.show()  # exec_() for modal dialog, show() for non-modal dialog
        dialog.move(QCursor.pos())

    def make_tabs_previews(self, index):
        """Make Tabs Previews for all tabs except current, if > 3 Tabs."""
        if self.count() < 4 or not self.tabBar().tab_previews:
            return False  # At least 4Tabs to use preview,and should be Enabled
        if self.timer.isActive():  # Be Race Condition Safe
            self.timer.stop()
        for old_widget in self.previews:
            old_widget.close()  # Visually Hide the Previews closing it
            old_widget.setParent(None)  # Orphan the old previews
            old_widget.destroy()  # Destroy to Free Resources
        self.previews = [QLabel(self) for i in range(self.count())]  # New Ones
        y_pos = self.size().height() - self.tabBar().tabRect(0).size().height()
        for i, widget in enumerate(self.previews):  # Iterate,set QPixmaps,Show
            if i != index:  # Dont make a pointless preview for the current Tab
                widget.setScaledContents(True)  # Auto-Scale QPixmap contents
                tabwidth = self.tabBar().tabRect(i).size().width()
                tabwidth = 200 if tabwidth > 200 else tabwidth  # Limit sizes
                widget.setPixmap(self.widget(i).grab().scaledToWidth(tabwidth))
                widget.resize(tabwidth - 1, tabwidth)
                if self.tabPosition():  # Move based on Top / Bottom positions
                    widget.move(self.tabBar().tabRect(i).left() * 1.1,
                                y_pos - tabwidth - 3)
                else:
                    widget.move(self.tabBar().tabRect(i).bottomLeft() * 1.1)
                widget.show()
        self.timer.start(1000)  # how many time display the previews
        return True
Beispiel #34
0
class Dialog(QDialog):
  def __init__(self):
    super().__init__()
    # Создание парсера
    self._dotaParser = parser.DotaParser()
    # Инициализация GUI
    self.initUi()
    self.initTray()
    self.initTimer()
    # Первоначальный парсинг
    self.startParser()
    self.fillDialog()
    
  def initUi(self):
    self._ongoingTitle = QLabel("<h3>Ongoing matches</h3>")
    self._ongoingLabel = QLabel("")
    self._upcomingTitle = QLabel("<h3>Upcoming matches</h3>")
    self._upcomingLabel = QLabel("")
    self._quitButton = QPushButton("Quit")
    
    self._layout = QVBoxLayout()
    self._layout.addWidget(self._ongoingTitle)
    self._layout.addWidget(self._ongoingLabel)
    self._layout.addWidget(self._upcomingTitle)
    self._layout.addWidget(self._upcomingLabel)
    self._layout.addWidget(self._quitButton)
    self.setLayout(self._layout)
    
    self._quitButton.clicked.connect(qApp.quit)
    self.setWindowTitle("Dota-2-Widget")
    
  def initTray(self):
    self._tray = QSystemTrayIcon()
    self._tray.setToolTip("dota-2-widget")
    self._tray.setIcon(QIcon("./dota2.gif"))
    self._tray.activated.connect(self.showOrHideDialog)
    self._tray.show()
    
  def initTimer(self):
    self._timer = QTimer()
    self._timer.timeout.connect(self.startParser)
    self._timer.timeout.connect(self.fillDialog)
    self._timer.start(5000)
    
  def startParser(self):
    self._dotaParser.startParser()
    #print("startParser")
    
  def fillDialog(self):
    # Получение результатов
    ongoingMatches = self._dotaParser.getOngoingMatches()
    upcomingMatches = self._dotaParser.getUpcomingMatches()
    # Запись результатов
    resultString = ""
    for i in range(0, len(ongoingMatches)):
      resultString += ongoingMatches[i] + '\n'
    self._ongoingLabel.setText(resultString)
    
    resultString = ""
    for i in range(0, len(upcomingMatches)):
      resultString += upcomingMatches[i] + '\n'
      if (i > 10):
        break
    self._upcomingLabel.setText(resultString)
    #print("fillDialog")
    
  def closeEvent(self, ce):
    if (self._tray.isVisible()):
      self.hide()
      
  def showOrHideDialog(self, result):
    if (result == QSystemTrayIcon.Trigger):
      if (not self.isVisible()):
        self.show()
      else:
        self.hide()
class ElectrumGui(Logger):

    network_dialog: Optional['NetworkDialog']

    @profiler
    def __init__(self, config: 'SimpleConfig', daemon: 'Daemon',
                 plugins: 'Plugins'):
        set_language(config.get('language', get_default_language()))
        Logger.__init__(self)
        self.logger.info(
            f"Qt GUI starting up... Qt={QtCore.QT_VERSION_STR}, PyQt={QtCore.PYQT_VERSION_STR}"
        )
        # Uncomment this call to verify objects are being properly
        # GC-ed when windows are closed
        #network.add_jobs([DebugMem([Abstract_Wallet, SPV, Synchronizer,
        #                            ElectrumWindow], interval=5)])
        QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
        if hasattr(QtCore.Qt, "AA_ShareOpenGLContexts"):
            QtCore.QCoreApplication.setAttribute(
                QtCore.Qt.AA_ShareOpenGLContexts)
        if hasattr(QGuiApplication, 'setDesktopFileName'):
            QGuiApplication.setDesktopFileName('electrum-dash.desktop')
        self.gui_thread = threading.current_thread()
        self.config = config
        self.daemon = daemon
        self.plugins = plugins
        self.windows = []  # type: List[ElectrumWindow]
        self.efilter = OpenFileEventFilter(self.windows)
        self.app = QElectrumApplication(sys.argv)
        self.app.installEventFilter(self.efilter)
        self.app.setWindowIcon(read_QIcon("electrum-dash.png"))
        self._cleaned_up = False
        # timer
        self.timer = QTimer(self.app)
        self.timer.setSingleShot(False)
        self.timer.setInterval(500)  # msec

        self.network_dialog = None
        self.dash_net_dialog = None
        self.network_updated_signal_obj = QNetworkUpdatedSignalObject()
        self.dash_net_sobj = QDashNetSignalsObject()
        self._num_wizards_in_progress = 0
        self._num_wizards_lock = threading.Lock()
        self.dark_icon = self.config.get("dark_icon", False)
        self.tray = None
        self._init_tray()
        self.app.new_window_signal.connect(self.start_new_window)
        self.set_dark_theme_if_needed()
        run_hook('init_qt', self)

    def _init_tray(self):
        self.tray = QSystemTrayIcon(self.tray_icon(), None)
        self.tray.setToolTip('Dash Electrum')
        self.tray.activated.connect(self.tray_activated)
        self.build_tray_menu()
        self.tray.show()

    def set_dark_theme_if_needed(self):
        use_dark_theme = self.config.get('qt_gui_color_theme',
                                         'default') == 'dark'
        self.app.setStyle('Fusion')
        if use_dark_theme:
            from .dark_dash_style import dash_stylesheet
            self.app.setStyleSheet(dash_stylesheet)
        else:
            from .dash_style import dash_stylesheet
            self.app.setStyleSheet(dash_stylesheet)
        # Apply any necessary stylesheet patches
        patch_qt_stylesheet(use_dark_theme=use_dark_theme)
        # Even if we ourselves don't set the dark theme,
        # the OS/window manager/etc might set *a dark theme*.
        # Hence, try to choose colors accordingly:
        ColorScheme.update_from_widget(QWidget(), force_dark=use_dark_theme)

    def build_tray_menu(self):
        if not self.tray:
            return
        # Avoid immediate GC of old menu when window closed via its action
        if self.tray.contextMenu() is None:
            m = QMenu()
            self.tray.setContextMenu(m)
        else:
            m = self.tray.contextMenu()
            m.clear()
        m.addAction(_("Network"), self.show_network_dialog)
        for window in self.windows:
            name = window.wallet.basename()
            submenu = m.addMenu(name)
            submenu.addAction(_("Show/Hide"), window.show_or_hide)
            submenu.addAction(_("Close"), window.close)
        m.addAction(_("Dark/Light"), self.toggle_tray_icon)
        m.addSeparator()
        m.addAction(_("Exit Dash Electrum"), self.app.quit)

    def tray_icon(self):
        if self.dark_icon:
            return read_QIcon('electrum_dark_icon.png')
        else:
            return read_QIcon('electrum_light_icon.png')

    def toggle_tray_icon(self):
        if not self.tray:
            return
        self.dark_icon = not self.dark_icon
        self.config.set_key("dark_icon", self.dark_icon, True)
        self.tray.setIcon(self.tray_icon())

    def tray_activated(self, reason):
        if reason == QSystemTrayIcon.DoubleClick:
            if all([w.is_hidden() for w in self.windows]):
                for w in self.windows:
                    w.bring_to_top()
            else:
                for w in self.windows:
                    w.hide()

    def _cleanup_before_exit(self):
        if self._cleaned_up:
            return
        self._cleaned_up = True
        self.app.new_window_signal.disconnect()
        self.efilter = None
        # If there are still some open windows, try to clean them up.
        for window in list(self.windows):
            window.close()
            window.clean_up()
        if self.network_dialog:
            self.network_dialog.close()
            self.network_dialog.clean_up()
            self.network_dialog = None
        self.network_updated_signal_obj = None
        if self.dash_net_dialog:
            self.dash_net_dialog.close()
            self.dash_net_dialog.clean_up()
            self.dash_net_dialog = None
        self.dash_net_sobj = None
        # Shut down the timer cleanly
        self.timer.stop()
        self.timer = None
        # clipboard persistence. see http://www.mail-archive.com/[email protected]/msg17328.html
        event = QtCore.QEvent(QtCore.QEvent.Clipboard)
        self.app.sendEvent(self.app.clipboard(), event)
        if self.tray:
            self.tray.hide()
            self.tray.deleteLater()
            self.tray = None

    def _maybe_quit_if_no_windows_open(self) -> None:
        """Check if there are any open windows and decide whether we should quit."""
        # keep daemon running after close
        if self.config.get('daemon'):
            return
        # check if a wizard is in progress
        with self._num_wizards_lock:
            if self._num_wizards_in_progress > 0 or len(self.windows) > 0:
                return
        self.app.quit()

    def new_window(self, path, uri=None):
        # Use a signal as can be called from daemon thread
        self.app.new_window_signal.emit(path, uri)

    def show_network_dialog(self):
        if self.network_dialog:
            self.network_dialog.on_update()
            self.network_dialog.show()
            self.network_dialog.raise_()
            return
        self.network_dialog = NetworkDialog(
            network=self.daemon.network,
            config=self.config,
            network_updated_signal_obj=self.network_updated_signal_obj)
        self.network_dialog.show()

    def show_dash_net_dialog(self):
        if self.dash_net_dialog:
            self.dash_net_dialog.on_updated()
            self.dash_net_dialog.show()
            self.dash_net_dialog.raise_()
            return
        self.dash_net_dialog = DashNetDialog(network=self.daemon.network,
                                             config=self.config,
                                             dash_net_sobj=self.dash_net_sobj)
        self.dash_net_dialog.show()

    def _create_window_for_wallet(self, wallet):
        w = ElectrumWindow(self, wallet)
        self.windows.append(w)
        self.build_tray_menu()
        w.warn_if_testnet()
        w.warn_if_watching_only()
        return w

    def count_wizards_in_progress(func):
        def wrapper(self: 'ElectrumGui', *args, **kwargs):
            with self._num_wizards_lock:
                self._num_wizards_in_progress += 1
            try:
                return func(self, *args, **kwargs)
            finally:
                with self._num_wizards_lock:
                    self._num_wizards_in_progress -= 1
                self._maybe_quit_if_no_windows_open()

        return wrapper

    @count_wizards_in_progress
    def start_new_window(self,
                         path,
                         uri,
                         *,
                         app_is_starting=False) -> Optional[ElectrumWindow]:
        '''Raises the window for the wallet if it is open.  Otherwise
        opens the wallet and creates a new window for it'''
        wallet = None
        if self.config.get('tor_auto_on', True):
            network = self.daemon.network
            if network:
                proxy_modifiable = self.config.is_modifiable('proxy')
                if not proxy_modifiable or not network.detect_tor_proxy():
                    warn_d = TorWarnDialog(self, path)
                    warn_d.exec_()
                    if warn_d.result() < 0:
                        return
        try:
            wallet = self.daemon.load_wallet(path, None)
        except Exception as e:
            self.logger.exception('')
            custom_message_box(icon=QMessageBox.Warning,
                               parent=None,
                               title=_('Error'),
                               text=_('Cannot load wallet') + ' (1):\n' +
                               repr(e))
            # if app is starting, still let wizard to appear
            if not app_is_starting:
                return
        if not wallet:
            try:
                wallet = self._start_wizard_to_select_or_create_wallet(path)
            except (WalletFileException, BitcoinException) as e:
                self.logger.exception('')
                custom_message_box(icon=QMessageBox.Warning,
                                   parent=None,
                                   title=_('Error'),
                                   text=_('Cannot load wallet') + ' (2):\n' +
                                   repr(e))
        if not wallet:
            return
        # create or raise window
        try:
            for window in self.windows:
                if window.wallet.storage.path == wallet.storage.path:
                    break
            else:
                window = self._create_window_for_wallet(wallet)
        except Exception as e:
            self.logger.exception('')
            custom_message_box(icon=QMessageBox.Warning,
                               parent=None,
                               title=_('Error'),
                               text=_('Cannot create window for wallet') +
                               ':\n' + repr(e))
            if app_is_starting:
                wallet_dir = os.path.dirname(path)
                path = os.path.join(wallet_dir,
                                    get_new_wallet_name(wallet_dir))
                self.start_new_window(path, uri)
            return
        if uri:
            window.pay_to_URI(uri)
        window.bring_to_top()
        window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized
                              | QtCore.Qt.WindowActive)

        window.activateWindow()
        return window

    def _start_wizard_to_select_or_create_wallet(
            self, path) -> Optional[Abstract_Wallet]:
        wizard = InstallWizard(self.config,
                               self.app,
                               self.plugins,
                               gui_object=self)
        try:
            path, storage = wizard.select_storage(path, self.daemon.get_wallet)
            # storage is None if file does not exist
            if storage is None:
                wizard.path = path  # needed by trustedcoin plugin
                wizard.run('new')
                storage, db = wizard.create_storage(path)
                if db.check_unfinished_multisig():
                    wizard.show_message(_('Saved unfinished multisig wallet'))
                    return
            else:
                db = WalletDB(storage.read(), manual_upgrades=False)
                if db.upgrade_done:
                    storage.backup_old_version()
                wizard.run_upgrades(storage, db)
            if getattr(storage, 'backup_message', None):
                custom_message_box(icon=QMessageBox.Warning,
                                   parent=None,
                                   title=_('Information'),
                                   text=storage.backup_message)
                storage.backup_message = ''
            if db.check_unfinished_multisig():
                wizard.continue_multisig_setup(storage)
                storage, db = wizard.create_storage(storage.path)
                if db.check_unfinished_multisig():
                    wizard.show_message(_('Saved unfinished multisig wallet'))
                    return
        except (UserCancelled, GoBack):
            return
        except WalletAlreadyOpenInMemory as e:
            return e.wallet
        finally:
            wizard.terminate()
        # return if wallet creation is not complete
        if storage is None or db.get_action():
            return
        wallet = Wallet(db, storage, config=self.config)
        wallet.start_network(self.daemon.network)
        self.daemon.add_wallet(wallet)
        return wallet

    def close_window(self, window: ElectrumWindow):
        if window in self.windows:
            self.windows.remove(window)
        self.build_tray_menu()
        # save wallet path of last open window
        if not self.windows:
            self.config.save_last_wallet(window.wallet)
        run_hook('on_close_window', window)
        self.daemon.stop_wallet(window.wallet.storage.path)

    def init_network(self):
        # Show network dialog if config does not exist
        if self.daemon.network:
            if self.config.get('auto_connect') is None:
                wizard = InstallWizard(self.config,
                                       self.app,
                                       self.plugins,
                                       gui_object=self)
                wizard.init_network(self.daemon.network)
                wizard.terminate()

    def main(self):
        # setup Ctrl-C handling and tear-down code first, so that user can easily exit whenever
        self.app.setQuitOnLastWindowClosed(
            False)  # so _we_ can decide whether to quit
        self.app.lastWindowClosed.connect(self._maybe_quit_if_no_windows_open)
        self.app.aboutToQuit.connect(self._cleanup_before_exit)
        signal.signal(signal.SIGINT, lambda *args: self.app.quit())
        # hook for crash reporter
        Exception_Hook.maybe_setup(config=self.config)
        # first-start network-setup
        try:
            self.init_network()
        except UserCancelled:
            return
        except GoBack:
            return
        except Exception as e:
            self.logger.exception('')
            return
        # start wizard to select/create wallet
        self.timer.start()
        path = self.config.get_wallet_path(use_gui_last_wallet=True)
        try:
            if not self.start_new_window(
                    path, self.config.get('url'), app_is_starting=True):
                return
        except Exception as e:
            self.logger.error(
                "error loading wallet (or creating window for it)")
            send_exception_to_crash_reporter(e)
            # Let Qt event loop start properly so that crash reporter window can appear.
            # We will shutdown when the user closes that window, via lastWindowClosed signal.
        # main loop
        self.logger.info("starting Qt main loop")
        self.app.exec_()
        # on some platforms the exec_ call may not return, so use _cleanup_before_exit

    def stop(self):
        self.logger.info('closing GUI')
        self.app.quit()
Beispiel #36
0
class ElectrumGui(PrintError):

    @profiler
    def __init__(self, config, daemon, plugins):
        set_language(config.get('language', get_default_language()))
        # Uncomment this call to verify objects are being properly
        # GC-ed when windows are closed
        #network.add_jobs([DebugMem([Abstract_Wallet, SPV, Synchronizer,
        #                            ElectrumWindow], interval=5)])
        QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
        if hasattr(QtCore.Qt, "AA_ShareOpenGLContexts"):
            QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
        if hasattr(QGuiApplication, 'setDesktopFileName'):
            QGuiApplication.setDesktopFileName('electrum.desktop')
        self.gui_thread = threading.current_thread()
        self.config = config
        self.daemon = daemon
        self.plugins = plugins
        self.windows = []
        self.efilter = OpenFileEventFilter(self.windows)
        self.app = QElectrumApplication(sys.argv)
        self.app.installEventFilter(self.efilter)
        self.app.setWindowIcon(read_QIcon("electrum.png"))
        # timer
        self.timer = QTimer(self.app)
        self.timer.setSingleShot(False)
        self.timer.setInterval(500)  # msec

        self.nd = None
        self.network_updated_signal_obj = QNetworkUpdatedSignalObject()
        self._num_wizards_in_progress = 0
        self._num_wizards_lock = threading.Lock()
        # init tray
        self.dark_icon = self.config.get("dark_icon", False)
        self.tray = QSystemTrayIcon(self.tray_icon(), None)
        self.tray.setToolTip('Electrum')
        self.tray.activated.connect(self.tray_activated)
        self.build_tray_menu()
        self.tray.show()
        self.app.new_window_signal.connect(self.start_new_window)
        self.set_dark_theme_if_needed()
        run_hook('init_qt', self)

    def set_dark_theme_if_needed(self):
        use_dark_theme = self.config.get('qt_gui_color_theme', 'default') == 'dark'
        if use_dark_theme:
            try:
                import qdarkstyle
                self.app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
            except BaseException as e:
                use_dark_theme = False
                self.print_error('Error setting dark theme: {}'.format(repr(e)))
        # Even if we ourselves don't set the dark theme,
        # the OS/window manager/etc might set *a dark theme*.
        # Hence, try to choose colors accordingly:
        ColorScheme.update_from_widget(QWidget(), force_dark=use_dark_theme)

    def build_tray_menu(self):
        # Avoid immediate GC of old menu when window closed via its action
        if self.tray.contextMenu() is None:
            m = QMenu()
            self.tray.setContextMenu(m)
        else:
            m = self.tray.contextMenu()
            m.clear()
        for window in self.windows:
            submenu = m.addMenu(window.wallet.basename())
            submenu.addAction(_("Show/Hide"), window.show_or_hide)
            submenu.addAction(_("Close"), window.close)
        m.addAction(_("Dark/Light"), self.toggle_tray_icon)
        m.addSeparator()
        m.addAction(_("Exit Electrum"), self.close)

    def tray_icon(self):
        if self.dark_icon:
            return read_QIcon('electrum_dark_icon.png')
        else:
            return read_QIcon('electrum_light_icon.png')

    def toggle_tray_icon(self):
        self.dark_icon = not self.dark_icon
        self.config.set_key("dark_icon", self.dark_icon, True)
        self.tray.setIcon(self.tray_icon())

    def tray_activated(self, reason):
        if reason == QSystemTrayIcon.DoubleClick:
            if all([w.is_hidden() for w in self.windows]):
                for w in self.windows:
                    w.bring_to_top()
            else:
                for w in self.windows:
                    w.hide()

    def close(self):
        for window in self.windows:
            window.close()

    def new_window(self, path, uri=None):
        # Use a signal as can be called from daemon thread
        self.app.new_window_signal.emit(path, uri)

    def show_network_dialog(self, parent):
        if not self.daemon.network:
            parent.show_warning(_('You are using Electrum in offline mode; restart Electrum if you want to get connected'), title=_('Offline'))
            return
        if self.nd:
            self.nd.on_update()
            self.nd.show()
            self.nd.raise_()
            return
        self.nd = NetworkDialog(self.daemon.network, self.config,
                                self.network_updated_signal_obj)
        self.nd.show()

    def _create_window_for_wallet(self, wallet):
        w = ElectrumWindow(self, wallet)
        self.windows.append(w)
        self.build_tray_menu()
        # FIXME: Remove in favour of the load_wallet hook
        run_hook('on_new_window', w)
        w.warn_if_watching_only()
        return w

    def count_wizards_in_progress(func):
        def wrapper(self: 'ElectrumGui', *args, **kwargs):
            with self._num_wizards_lock:
                self._num_wizards_in_progress += 1
            try:
                return func(self, *args, **kwargs)
            finally:
                with self._num_wizards_lock:
                    self._num_wizards_in_progress -= 1
        return wrapper

    @count_wizards_in_progress
    def start_new_window(self, path, uri, *, app_is_starting=False):
        '''Raises the window for the wallet if it is open.  Otherwise
        opens the wallet and creates a new window for it'''
        wallet = None
        try:
            wallet = self.daemon.load_wallet(path, None)
        except BaseException as e:
            traceback.print_exc(file=sys.stdout)
            QMessageBox.warning(None, _('Error'),
                                _('Cannot load wallet') + ' (1):\n' + str(e))
            # if app is starting, still let wizard to appear
            if not app_is_starting:
                return
        if not wallet:
            wallet = self._start_wizard_to_select_or_create_wallet(path)
        if not wallet:
            return
        # create or raise window
        try:
            for window in self.windows:
                if window.wallet.storage.path == wallet.storage.path:
                    break
            else:
                window = self._create_window_for_wallet(wallet)
        except BaseException as e:
            traceback.print_exc(file=sys.stdout)
            QMessageBox.warning(None, _('Error'),
                                _('Cannot create window for wallet') + ':\n' + str(e))
            if app_is_starting:
                wallet_dir = os.path.dirname(path)
                path = os.path.join(wallet_dir, get_new_wallet_name(wallet_dir))
                self.start_new_window(path, uri)
            return
        if uri:
            window.pay_to_URI(uri)
        window.bring_to_top()
        window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)

        window.activateWindow()
        return window

    def _start_wizard_to_select_or_create_wallet(self, path) -> Optional[Abstract_Wallet]:
        wizard = InstallWizard(self.config, self.app, self.plugins)
        try:
            path, storage = wizard.select_storage(path, self.daemon.get_wallet)
            # storage is None if file does not exist
            if storage is None:
                wizard.path = path  # needed by trustedcoin plugin
                wizard.run('new')
                storage = wizard.create_storage(path)
            else:
                wizard.run_upgrades(storage)
        except (UserCancelled, GoBack):
            return
        except WalletAlreadyOpenInMemory as e:
            return e.wallet
        except (WalletFileException, BitcoinException) as e:
            traceback.print_exc(file=sys.stderr)
            QMessageBox.warning(None, _('Error'),
                                _('Cannot load wallet') + ' (2):\n' + str(e))
            return
        finally:
            wizard.terminate()
        # return if wallet creation is not complete
        if storage is None or storage.get_action():
            return
        wallet = Wallet(storage)
        wallet.start_network(self.daemon.network)
        self.daemon.add_wallet(wallet)
        return wallet

    def close_window(self, window):
        if window in self.windows:
           self.windows.remove(window)
        self.build_tray_menu()
        # save wallet path of last open window
        if not self.windows:
            self.config.save_last_wallet(window.wallet)
        run_hook('on_close_window', window)
        self.daemon.stop_wallet(window.wallet.storage.path)

    def init_network(self):
        # Show network dialog if config does not exist
        if self.daemon.network:
            if self.config.get('auto_connect') is None:
                wizard = InstallWizard(self.config, self.app, self.plugins)
                wizard.init_network(self.daemon.network)
                wizard.terminate()

    def main(self):
        try:
            self.init_network()
        except UserCancelled:
            return
        except GoBack:
            return
        except BaseException as e:
            traceback.print_exc(file=sys.stdout)
            return
        self.timer.start()
        self.config.open_last_wallet()
        path = self.config.get_wallet_path()
        if not self.start_new_window(path, self.config.get('url'), app_is_starting=True):
            return
        signal.signal(signal.SIGINT, lambda *args: self.app.quit())

        def quit_after_last_window():
            # keep daemon running after close
            if self.config.get('daemon'):
                return
            # check if a wizard is in progress
            with self._num_wizards_lock:
                if self._num_wizards_in_progress > 0 or len(self.windows) > 0:
                    return
            self.app.quit()
        self.app.setQuitOnLastWindowClosed(False)  # so _we_ can decide whether to quit
        self.app.lastWindowClosed.connect(quit_after_last_window)

        def clean_up():
            # Shut down the timer cleanly
            self.timer.stop()
            # clipboard persistence. see http://www.mail-archive.com/[email protected]/msg17328.html
            event = QtCore.QEvent(QtCore.QEvent.Clipboard)
            self.app.sendEvent(self.app.clipboard(), event)
            self.tray.hide()
        self.app.aboutToQuit.connect(clean_up)

        # main loop
        self.app.exec_()
        # on some platforms the exec_ call may not return, so use clean_up()

    def stop(self):
        self.print_error('closing GUI')
        self.app.quit()
Beispiel #37
0
class PymodoroGUI(QWidget):
    """
        GUI for Pymodoro
    """
    def __init__(self):
        """
            Initializer for the Pomodoro GUI class
        """
        super(PymodoroGUI, self).__init__()

        self.res_dir = os.path.join("../ext/")
        self.green_tomato_icon = os.path.join(self.res_dir, "greentomato.png")
        self.red_tomato_icon = os.path.join(self.res_dir, "redtomato.png")
        self.tray = QSystemTrayIcon(QIcon(self.green_tomato_icon))
        self.pom = Pomodoro()
        self.pom.ringer.connect(self.signal)
        self.init_ui()

    def signal(self, pomodori):
        """
            Callback given to the Pomodoro class.
            Called when a pomodoro is up
        """
        if pomodori % 4 == 0 and pomodori != 0:
            self.tray.showMessage("4 Pomodori has passed!",
                                   "Take a long break: 15-30min",
                                   QSystemTrayIcon.Information)
        else:
            self.tray.showMessage("Pomodoro's up!",
                                   "Take a short break: 3-5min",
                                   QSystemTrayIcon.Information)
        self.tray.setIcon(QIcon(self.green_tomato_icon))

    def init_tray(self):
        """
            Initializes the systray menu
        """
        traymenu = QMenu("Menu")
        self.tray.setContextMenu(traymenu)
        self.tray.show()
        self.tray.activated.connect(self.tray_click)
        self.tray.setToolTip("Pomodori: "+str(self.pom.pomodori))

        set_timer_tray = QLineEdit()
        set_timer_tray.setPlaceholderText("Set timer")
        set_timer_tray.textChanged.connect(lambda:
                                           self.update_timer_text(set_timer_tray.text()))
        traywidget = QWidgetAction(set_timer_tray)
        traywidget.setDefaultWidget(set_timer_tray)
        traymenu.addAction(traywidget)

        start_timer_action = QAction("&Start Timer", self)
        start_timer_action.triggered.connect(self.start_timer_click)
        traymenu.addAction(start_timer_action)

        exit_action = QAction("&Exit", self)
        exit_action.triggered.connect(QCoreApplication.instance().quit)
        traymenu.addAction(exit_action)

    def tray_click(self, activation):
        """
            Method called when clicking the tray icon
        """
        if activation == QSystemTrayIcon.Trigger:
            if self.isVisible():
                self.hide()
            else:
                self.show()
        elif activation == QSystemTrayIcon.Context:
            self._tray.show()

    def close_event(self, event):
        self._tray.showMessage("Running in system tray",
                                """The program will keep running in the system tray.\n
                                To terminate the program choose exit from the context menu""",
                                QSystemTrayIcon.Information)
        self.hide()
        event.ignore()

    def wheel_event(self, event):
        if event.delta() > 0:
            timervalue = int(self.settimertext.text())
            timervalue = timervalue + 1
            self.settimertext.setText(str(timervalue))
        else:
            timervalue = int(self.settimertext.text())
            timervalue = timervalue - 1
            self.settimertext.setText(str(timervalue))

    def init_ui(self):
        """
            Initializes the GUI
        """
        self.init_tray()
        resolution = QApplication.desktop().availableGeometry()
        width = 150
        height = 100

        # place exactly in center of screen
        self.setGeometry((resolution.width() / 2) - (width / 2),
                         (resolution.height() / 2) - (height / 2),
                         width, height)
        self.setWindowTitle("Pomodoro")
        self.setWindowIcon(QIcon(os.path.join(self.res_dir, "redtomato.png")))

        grid = QGridLayout()
        grid.setSpacing(5)
        self.settimertext = QLineEdit()
        grid.addWidget(self.settimertext, 1, 0)

        self.errortext = QLabel()
        grid.addWidget(self.errortext, 2, 0)
        self.errortext.hide()

        self.settimertext.setText(str(25))
        self.settimertext.textChanged.connect(lambda:
                                              self.update_timer_text(
                                                 self.settimertext.text()))

        self.start_timerbutton = QPushButton("start timer")
        grid.addWidget(self.start_timerbutton, 3, 0)
        self.start_timerbutton.clicked.connect(self.start_timer_click)

        self.setLayout(grid)
        self.show()

    def start_timer_click(self):
        """
            Method run when starting the pomodoro timer
        """
        self.pom.start_timer()
        self.tray.setIcon(QIcon(self.red_tomato_icon))
        self.hide()

    def update_timer_text(self, number):
        """
            Method run when setting the number of minutes in the timer
        """
        try:
            self.pom.set_timer_minutes(int(number))
            self.errortext.hide()
        except ValueError:
            self.errortext.setText("Please input a number")
            self.errortext.show()
Beispiel #38
0
class MarksTimeTracker(QMainWindow, Ui_MainWindow):
    runningEvent = None

    def __init__(self, parent=None):
        super(MarksTimeTracker, self).__init__(parent)
        self.setupUi(self)
        self.tabWidget.tabBar().hide()
        self.setupStatusIcon()

        # config
        self.config_path = os.path.join(os.path.expanduser('~'), '.config', 'markstimetracker')
        dir_util.mkpath(self.config_path)
        self.readConfig()

        # Setup DB
        engine = create_engine('sqlite:///' + os.path.join(self.config_path, 'markstimetracker.db'))
        init_db(engine)
        self.db = sessionmaker(bind=engine)()

        self.updateTaskList()
        self.updateTasksComboBox()
        self.checkForRunningTask()

        # Timers
        timer = QTimer(self)
        timer.timeout.connect(self.updateTimeSpent)
        timer.start(1000)

        self.idleTimeTimer = QTimer()
        self.idleTimeTimer.timeout.connect(self.detectIdleTime)
        self.checkIdleTime()

        self.remindTimer = QTimer()
        self.remindTimer.timeout.connect(self.remindTracking)
        self.checkRemind()

        self.redmineSyncTimer = QTimer()
        self.redmineSyncTimer.timeout.connect(self.doRedmineSync)
        self.checkRedmineSync()

        # Events
        self.startButton.clicked.connect(self.toggleEventButton)
        self.eventsPeriodComboBox.currentIndexChanged.connect(self.eventsPeriodChanged)
        self.editDurationSpinBox.valueChanged.connect(self.updateEditStartEndTime)
        self.editStartDateTimeEdit.dateTimeChanged.connect(self.updateDurationSpinBoxEndTime)
        self.editEndDateTimeEdit.dateTimeChanged.connect(self.updateDurationSpinBox)
        self.editButtonBox.accepted.connect(self.saveEvent)
        self.editButtonBox.rejected.connect(lambda: self.tabWidget.setCurrentIndex(TAB_MAIN))
        self.settingsButtonBox.accepted.connect(self.saveSettings)
        self.settingsButtonBox.rejected.connect(lambda: self.tabWidget.setCurrentIndex(TAB_MAIN))
        self.settingsPushButton.clicked.connect(
            lambda: self.tabWidget.setCurrentIndex(TAB_SETTINGS))
        self.redmineSyncPushButton.clicked.connect(lambda: self.doRedmineSync(check=False))
        self.addEventPushButton.clicked.connect(self.addEvent)

        self.setupDbus()

    def setupStatusIcon(self):
        icon = QIcon()
        icon.addPixmap(QPixmap(":/clock.svg"), QIcon.Normal, QIcon.Off)
        self.statusIcon = QSystemTrayIcon(self)
        self.statusIcon.setIcon(icon)
        self.statusIcon.activated.connect(lambda: self.hide()
                                          if self.isVisible()
                                          else self.show())
        self.statusIcon.setToolTip("Mark's Time Tracker")
        self.statusIcon.show()

    def setupDbus(self):
        dbus_loop = DBusQtMainLoop(set_as_default=True)
        self.bus = dbus.SessionBus(mainloop=dbus_loop)
        signals = [('org.freedesktop.ScreenSaver', '/org/freedesktop/ScreenSaver', 'ActiveChanged'),
                   ('com.canonical.Unity', '/com/canonical/Unity/Session', 'Locked')]
        for org, path, event in signals:
            screensaver = self.bus.get_object(org, path)
            screensaver.connect_to_signal(event, self.checkLockScreen)

    def updateTasksComboBox(self):
        self.tasksComboBox.clear()
        self.editTaskListComboBox.clear()
        self.tasksComboBox.addItem('')
        self.tasksComboBox.lineEdit().setPlaceholderText("What are you going to do?")
        for task in self.db.query(Task).all():
            if task.active:
                self.tasksComboBox.addItem(task.description)
            self.editTaskListComboBox.addItem(task.description)

    def updateTimeSpent(self):
        if self.runningEvent:
            spent_time = self.runningEvent.spent_time
            m, s = divmod(spent_time, 60)
            h, m = divmod(m, 60)
            self.timeLabel.setText("{h:02d}:{m:02d}:{s:02d}".format(h=h, m=m, s=s))

            period = self.eventsPeriodComboBox.currentText()
            start, end = self.getStartEndForPeriod(period)
            total = Event.get_spent_time_period(self.db, start, end)
            self.totalTimeLabel.setText("{}h".format(total))

    def getStartEndForPeriod(self, period):
        if period == "Today":
            start = datetime.datetime.now().replace(hour=0, minute=0)
            end = start + relativedelta.relativedelta(days=1)
        elif period == "Yesterday":
            end = datetime.datetime.now().replace(hour=0, minute=0)
            start = end - relativedelta.relativedelta(days=1)
        elif period == "This week":
            today = datetime.datetime.now().replace(hour=0, minute=0)
            start = today - relativedelta.relativedelta(days=today.weekday())
            end = today + relativedelta.relativedelta(days=6 - today.weekday())
        else:
            raise Exception("Don't know this period {}".format(period))

        return start, end

    def updateTaskList(self):
        while self.timeEntriesLayout.count() > 0:
            self.timeEntriesLayout.takeAt(0).widget().deleteLater()

        period = self.eventsPeriodComboBox.currentText()
        start, end = self.getStartEndForPeriod(period)

        events = self.db.query(Event).filter(Event.start.between(start, end))\
            .order_by(Event.start.desc())

        for event in events:
            if not event.end:
                continue
            widget = EventWidget(event.id, event.task.description, event.spent_time, parent=self)
            widget.clicked.connect(self.eventClicked)
            widget.show()
            self.timeEntriesLayout.addWidget(widget)

    def updateEditStartEndTime(self):
        hours = self.editDurationSpinBox.value()
        startTime = self.editStartDateTimeEdit.dateTime().toPyDateTime()
        newEndTime = startTime + relativedelta.relativedelta(hours=hours)
        self.editEndDateTimeEdit.setDateTime(newEndTime)

    def updateDurationSpinBox(self):
        seconds = float((self.editEndDateTimeEdit.dateTime().toPyDateTime() -
                         self.editStartDateTimeEdit.dateTime().toPyDateTime()).seconds)
        hours = seconds / 3600
        self.editDurationSpinBox.setValue(hours)

    def updateDurationSpinBoxEndTime(self):
        self.updateDurationSpinBox()
        self.updateEditStartEndTime()

    def checkForRunningTask(self):
        self.runningEvent = self.db.query(Event).filter(Event.end == None).first()
        if self.runningEvent:
            self.tasksComboBox.setCurrentIndex(
                [self.tasksComboBox.itemText(x) for x in range(self.tasksComboBox.count())]
                .index(self.runningEvent.task.description))
            self.startButton.setText("Stop")
            self.tasksComboBox.setEnabled(False)

    def toggleEventButton(self):
        if self.runningEvent:
            self.stopEvent()
        else:
            self.startEvent()

    def eventsPeriodChanged(self):
        self.updateTaskList()

    def eventClicked(self, event_id):
        event = self.db.query(Event).get(event_id)
        self.editTaskListComboBox.setCurrentIndex(
            [self.editTaskListComboBox.itemText(x)
             for x in range(self.editTaskListComboBox.count())]
            .index(event.task.description))

        self.editDurationSpinBox.setValue(float(event.spent_time) / 3600)
        self.editStartDateTimeEdit.setDateTime(event.start_date)
        self.editEndDateTimeEdit.setDateTime(event.end_date)

        self.tabWidget.setCurrentIndex(TAB_EDIT_EVENT)

        self.editingEvent = event

    def startEvent(self, event=None):
        if not event:
            event = self.tasksComboBox.currentText()

        self.tasksComboBox.setEnabled(False)

        self.startButton.setText("Stop")

        if not event:
            return

        if re.match(r'\d+ - .+', event):
            tracker_id, name = re.findall(r'(\d+) - (.+)', event)[0]
        else:
            tracker_id = None
            name = event

        # Update DB
        task = Task.get_or_create(self.db, task_id=tracker_id, name=name, parent=None)

        if self.runningEvent:
            self.runningEvent.end = datetime.datetime.now()

        self.runningEvent = Event(task_id=task.task_id, comment="", start=datetime.datetime.now())
        self.db.add(self.runningEvent)
        self.db.commit()

        self.tasksComboBox.lineEdit().setText(self.runningEvent.task.description)

        self.checkForRunningTask()

    def addEvent(self):
        self.editDurationSpinBox.setValue(1)
        self.editStartDateTimeEdit.setDateTime(datetime.datetime.now())
        self.editEndDateTimeEdit.setDateTime(datetime.datetime.now() +
                                             relativedelta.relativedelta(hours=1))

        self.tabWidget.setCurrentIndex(TAB_EDIT_EVENT)
        self.editingEvent = Event()
        self.db.add(self.editingEvent)

    def stopEvent(self):
        self.tasksComboBox.setEnabled(True)

        self.runningEvent.end = datetime.datetime.now()
        self.db.commit()

        self.runningEvent = None

        self.updateTaskList()

        self.startButton.setText("Start")
        self.timeLabel.setText("00:00:00")

        self.updateTasksComboBox()

    def saveEvent(self):

        self.editingEvent.task_id = self.editTaskListComboBox.currentText().split(' - ')[0]
        self.editingEvent.start = self.editStartDateTimeEdit.dateTime().toPyDateTime()
        self.editingEvent.end = self.editEndDateTimeEdit.dateTime().toPyDateTime()

        self.db.commit()

        self.tabWidget.setCurrentIndex(TAB_MAIN)
        self.updateTaskList()

    def saveSettings(self):
        self.config = {'enable_detect_idle_time': self.detectIdleTimecheckBox.checkState(),
                       'detect_idle_time': self.detectIdleTimeSpinBox.value(),
                       'enable_remind': self.remindCheckBox.checkState(),
                       'remind_time': self.remindSpinBox.value(),
                       'stop_on_lock_screen': self.stopLockScreencheckBox.checkState(),
                       'enabled_redmine_sync': self.syncRedmineCheckBox.checkState(),
                       'redmine_sync_time': self.redmineSyncTimeSpinBox.value(),
                       'redmine_apikey': self.redmineApikeyLineEdit.text(),
                       'redmine_url': self.redmineUrlLineEdit.text(),
                       'redmine_user': self.redmineUserLineEdit.text()}
        self.writeConfig()

    def readConfig(self):
        config_file_path = os.path.join(self.config_path, 'config.json')
        if os.path.exists(config_file_path):
            with open(config_file_path, 'r') as f:
                self.config = json.loads(f.read())
        else:
            self.config = {}

        self.detectIdleTimecheckBox.setCheckState(self.config.get('enable_detect_idle_time', True))
        self.detectIdleTimeSpinBox.setValue(self.config.get('detect_idle_time'))
        self.remindCheckBox.setCheckState(self.config.get('enable_remind', True))
        self.remindSpinBox.setValue(self.config.get('remind_time'))
        self.stopLockScreencheckBox.setCheckState(self.config.get('stop_on_lock_screen', True))
        self.syncRedmineCheckBox.setCheckState(self.config.get('enabled_redmine_sync'))
        self.redmineSyncTimeSpinBox.setValue(self.config.get('redmine_sync_time'))
        self.redmineApikeyLineEdit.setText(self.config.get('redmine_apikey'))
        self.redmineUrlLineEdit.setText(self.config.get('redmine_url'))
        self.redmineUserLineEdit.setText(self.config.get('redmine_user'))

    def writeConfig(self):
        with open(os.path.join(self.config_path, 'config.json'), 'w') as f:
            f.write(json.dumps(self.config))
        self.tabWidget.setCurrentIndex(TAB_MAIN)

        self.checkIdleTime()
        self.checkRemind()
        self.checkRedmineSync()

    def checkIdleTime(self):
        self.idleTimeTimer.stop()
        if self.config.get("enable_detect_idle_time", True):
            self.idleTimeTimer.start(self.config.get("detect_idle_time", 5) * 60000)

    def detectIdleTime(self):

        # do something

        self.checkIdleTime()

    def checkRemind(self):
        self.remindTimer.stop()
        if self.config.get("enable_remind", True):
            self.remindTimer.start(self.config.get("remind_time", 5) * 60000)

    def remindTracking(self):

        # do something

        self.checkRemind()

    def checkRedmineSync(self):
        self.redmineSyncTimer.stop()
        if self.config.get("enabled_redmine_sync"):
            self.redmineSyncTimer.start(self.config.get("redmine_sync_time", 5) * 60000)

        self.redmineSyncPushButton.setVisible(self.config.get("enabled_redmine_sync", False))

    def doRedmineSync(self, check=True):
        logging.info("Doing redmine sync")
        thread = RedmineSyncThread(self.config,
                                   'sqlite:///' +
                                   os.path.join(self.config_path, 'markstimetracker.db'))

        def updateTaskWidgets():
            self.updateTaskList()
            self.updateTasksComboBox()
            self.checkForRunningTask()

        thread.finished.connect(updateTaskWidgets)
        thread.start()

        self.checkRedmineSync()

    def checkLockScreen(self, is_locked=True):
        if is_locked and self.config.get("stop_on_lock_screen"):
            self.stopEvent()
Beispiel #39
0
class ZhaoChaFrame(QWidget):
    game_hwnd = 0  # 游戏的窗体句柄
    bgpixmap = None
    pixmap = None
    my_visible = False

    # GAME_CLASS = "#32770"
    # GAME_TITLE = "大家来找茬"
    GAME_CLASS = "MozillaWindowClass"
    GAME_TITLE = "游戏全屏 - Mozilla Firefox"

    WIDTH = 500  # 大图宽
    HEIGHT = 450  # 大图高
    ANCHOR_LEFT_X = 8  # 左图X起点
    ANCHOR_RIGHT_X = 517  # 右图X起点
    ANCHOR_Y = 190  # Y起点
    CLIP_WIDTH = 10
    CLIP_HEIGHT = 10
    DIFF_LIMIT = 2000  # 差异阀值,两片图形对比差异差异超过此值视为不一样

    # 查找区域
    # 大图版 1024 x 738
    BIG_WIDTH = 498  # 大图宽
    BIG_HEIGHT = 448  # 大图高
    BIG_ANCHOR_LEFT_X = 8  # 左图X起点
    BIG_ANCHOR_RIGHT_X = 517  # 右图X起点
    BIG_ANCHOR_Y = 190  # Y起点
    BIG_CLIP_WIDTH = 10
    BIG_CLIP_HEIGHT = 10
    BIG_DIFF_LIMIT = 2000  # 差异阀值,两片图形对比差异差异超过此值视为不一样

    # 小图版 800 x 600
    SMALL_WIDTH = 381  # 大图宽
    SMALL_HEIGHT = 286  # 大图高
    SMALL_ANCHOR_LEFT_X = 10  # 左图X起点
    SMALL_ANCHOR_RIGHT_X = 403  # 右图X起点
    SMALL_ANCHOR_Y = 184  # Y起点
    SMALL_CLIP_WIDTH = 10
    SMALL_CLIP_HEIGHT = 10
    SMALL_DIFF_LIMIT = 2000  # 差异阀值,两片图形对比差异差异超过此值视为不一样


    # 存储对比结果 二位数组,映射每一个基块
    result = []

    clock = 0

    def __init__(self, parent=None):
        QWidget.__init__(self)
        # QWidget.__init__(self, parent, flags=Qt.FramelessWindowHint | Qt.Window | Qt.WindowStaysOnTopHint)
        # 设置背景透明,这样按钮不会太难看
        # self.setAttribute(Qt.WA_TranslucentBackground, True)

        # 这些属性让程序不在任务栏出现标题
        # self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Popup | Qt.Tool)

        # 托盘
        self.icon = QIcon(":\icon.png")

        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setIcon(self.icon)
        self.trayIcon.setToolTip(u"QQ找茬助手")
        self.trayIcon.show()
        self.trayIcon.showMessage(u"QQ找茬助手", u"QQ找茬助手已经待命,进入游戏即可激活")

        self.action = QAction(u"退出QQ找茬助手", self, triggered=sys.exit)
        self.menu = QMenu(self)
        self.menu.addAction(self.action)
        self.trayIcon.setContextMenu(self.menu)

        # 定时探测游戏
        self.stick_timer = QTimer()
        self.stick_timer.start(20)
        # self.connect(self.stick_timer, SIGNAL('timeout()'), self.StickTarget)

        # 这个QLabel其实就是中间绘图区的背景
        self.label = QLabel(self)

        self.pixmap = QPixmap(self.size())

        # 刷新按钮
        self.btn_compare = QPushButton(self)
        self.btn_compare.setText(u"对比")
        # self.connect(self.btn_compare, SIGNAL('clicked()'), self.Compare)

        # 开关
        self.btn_toggle = QPushButton(self)
        self.btn_toggle.setText(u"擦除")
        # self.connect(self.btn_toggle, SIGNAL('clicked()'), self.Clear)

        self.HideMe()


    def StickTarget(self):
        '''让本窗体粘附在目标窗体上'''
        # 找到目标窗口句柄
        game_hwnd = win32gui.FindWindow(self.GAME_CLASS, self.GAME_TITLE)
        if game_hwnd == 0:
            if self.my_visible:
                # 如果游戏窗体不可见,比如最小化、关闭了,隐藏自己
                self.HideMe()
            return
        else:
            self.game_hwnd = game_hwnd

        try:
            window_rect = win32gui.GetWindowRect(self.game_hwnd)
            if self.game_hwnd == win32gui.GetForegroundWindow() and window_rect[0] > 0:
                point = QPoint(window_rect[0], window_rect[1])
                size = QSize(window_rect[2] - window_rect[0], window_rect[3] - window_rect[1])

                if self.size() != size:
                    self.SyncSize(size)

                if self.pos() != point:
                    self.move(point)

                if not self.my_visible:
                    self.ShowMe()
                    # self.FindAndShow()
            elif win32gui.GetForegroundWindow() != int(self.winId()) and self.my_visible:
                # 游戏窗口隐藏时,同时隐藏找碴助手
                self.HideMe()
        except:
            if self.my_visible:
                self.HideMe()


    def paintEvent(self, event):
        if not self.my_visible:
            self.move(-2000, -2000)

        self.pixmap.fill()
        p = QPainter(self.pixmap)
        p.setPen(QPen(QBrush(QColor(0, 0, 0)), 2))

        for row in range(len(self.result)):
            for col in range(len(self.result[0])):
                if self.result[row][col] != 0:
                    # 定一个基点,避免算数太难看
                    base_l_x = self.ANCHOR_LEFT_X + self.CLIP_WIDTH * col
                    base_r_x = self.ANCHOR_RIGHT_X + self.CLIP_WIDTH * col
                    base_y = self.ANCHOR_Y + self.CLIP_HEIGHT * row

                    if row == 0 or self.result[row - 1][col] == 0:
                        # 如果是第一行,或者上面的格子为空,画一条上边
                        p.drawLine(base_l_x, base_y, base_l_x + self.CLIP_WIDTH, base_y)
                        p.drawLine(base_r_x, base_y, base_r_x + self.CLIP_WIDTH, base_y)
                    if row == len(self.result) - 1 or self.result[row + 1][col] == 0:
                        # 如果是最后一行,或者下面的格子为空,画一条下边
                        p.drawLine(base_l_x, base_y + self.CLIP_HEIGHT, base_l_x + self.CLIP_WIDTH,
                                   base_y + self.CLIP_HEIGHT)
                        p.drawLine(base_r_x, base_y + self.CLIP_HEIGHT, base_r_x + self.CLIP_WIDTH,
                                   base_y + self.CLIP_HEIGHT)
                    if col == 0 or self.result[row][col - 1] == 0:
                        # 如果是第一列,或者左边的格子为空,画一条左边
                        p.drawLine(base_l_x, base_y, base_l_x, base_y + self.CLIP_HEIGHT)
                        p.drawLine(base_r_x, base_y, base_r_x, base_y + self.CLIP_HEIGHT)
                    if col == len(self.result[0]) - 1 or self.result[row][col + 1] == 0:
                        # 如果是第一列,或者右边的格子为空,画一条右边
                        p.drawLine(base_l_x + self.CLIP_WIDTH, base_y, base_l_x + self.CLIP_WIDTH,
                                   base_y + self.CLIP_HEIGHT)
                        p.drawLine(base_r_x + self.CLIP_WIDTH, base_y, base_r_x + self.CLIP_WIDTH,
                                   base_y + self.CLIP_HEIGHT)
        p.fillRect(self.btn_compare.geometry(), QBrush(QColor(0, 0, 0)))
        p.fillRect(self.btn_toggle.geometry(), QBrush(QColor(0, 0, 0)))
        self.setMask(QBitmap(self.pixmap))


    def Clear(self):
        self.ResetResult()
        self.repaint()


    def ShowMe(self):
        self.my_visible = True
        self.repaint()


    def HideMe(self):
        self.my_visible = False
        self.repaint()


    def Compare(self):
        # 对比
        if self.stick_timer.isActive():
            self.FindAndShow()
        else:
            self.stick_timer.start()


    def ResetResult(self):
        # 清楚之前计算的结果
        self.result = [[0 for a in range(0, self.WIDTH / self.CLIP_WIDTH)] for b in
                       range(0, self.HEIGHT / self.CLIP_HEIGHT)]


    def SyncSize(self, size):
        self.resize(size)

        if self.width() == 1024 and self.height() == 738:
            self.WIDTH = self.BIG_WIDTH
            self.HEIGHT = self.BIG_HEIGHT
            self.ANCHOR_LEFT_X = self.BIG_ANCHOR_LEFT_X
            self.ANCHOR_RIGHT_X = self.BIG_ANCHOR_RIGHT_X
            self.ANCHOR_Y = self.BIG_ANCHOR_Y
            self.CLIP_WIDTH = self.BIG_CLIP_WIDTH
            self.CLIP_HEIGHT = self.BIG_CLIP_HEIGHT
            self.DIFF_LIMIT = self.BIG_DIFF_LIMIT
            self.btn_compare.setGeometry(611, 650, 100, 40)
            self.btn_toggle.setGeometry(715, 650, 100, 40)
        elif self.width() == 800 and self.height() == 600:
            self.WIDTH = self.SMALL_WIDTH
            self.HEIGHT = self.SMALL_HEIGHT
            self.ANCHOR_LEFT_X = self.SMALL_ANCHOR_LEFT_X
            self.ANCHOR_RIGHT_X = self.SMALL_ANCHOR_RIGHT_X
            self.ANCHOR_Y = self.SMALL_ANCHOR_Y
            self.CLIP_WIDTH = self.SMALL_CLIP_WIDTH
            self.CLIP_HEIGHT = self.SMALL_CLIP_HEIGHT
            self.DIFF_LIMIT = self.SMALL_DIFF_LIMIT
            self.btn_compare.setGeometry(472, 496, 100, 40)
            self.btn_toggle.setGeometry(576, 496, 100, 40)
        else:
            print("游戏窗体大小匹配错误")
            return

        self.pixmap = QPixmap(self.size())
        self.bgpixmap = QPixmap(self.width(), self.HEIGHT)
        self.bgpixmap.fill(QColor(0, 0, 255))
        self.label.setGeometry(0, self.ANCHOR_Y, self.width(), self.HEIGHT)
        self.label.setPixmap(self.bgpixmap)


    def FindAndShow(self):
        # 截取游戏窗口内容
        self.my_visible = True
        self.DebugTime("init")

        ## 裁剪得到左右的内容图片
        win32gui.ShowWindow(self.game_hwnd, win32con.SW_RESTORE)  # 强行显示界面后才好截图
        win32gui.SetForegroundWindow(self.game_hwnd)  # 将游戏窗口提到最前
        src_image = ImageGrab.grab((self.x(), self.y() + self.ANCHOR_Y, self.x() + self.ANCHOR_RIGHT_X + self.WIDTH,
                                    self.y() + self.ANCHOR_Y + self.HEIGHT))
        left_box = (self.ANCHOR_LEFT_X, 0, self.ANCHOR_LEFT_X + self.WIDTH, self.HEIGHT)
        right_box = (self.ANCHOR_RIGHT_X, 0, self.ANCHOR_RIGHT_X + self.WIDTH, self.HEIGHT)
        image_left = src_image.crop(left_box)
        image_right = src_image.crop(right_box)
        # image_left.show()
        # image_right.show()
        self.DebugTime("拆图完成")

        # 将左右大图裁剪成多个小图分别进行对比
        self.ResetResult()
        for col in range(0, self.WIDTH / self.CLIP_WIDTH):
            for row in range(0, self.HEIGHT / self.CLIP_HEIGHT):
                clip_box = (col * self.CLIP_WIDTH, row * self.CLIP_HEIGHT, (col + 1) * self.CLIP_WIDTH,
                            (row + 1) * self.CLIP_HEIGHT)
                clip_image_left = image_left.crop(clip_box)
                clip_image_right = image_right.crop(clip_box)
                clip_diff = self.compare(clip_image_left, clip_image_right)

                if sum(clip_diff) > self.DIFF_LIMIT:
                    self.result[row][col] = 1

        self.DebugTime("对比")
        self.repaint()
        self.DebugTime("绘制")
        # print "----------------------"
        # for i in range(len(self.result)):        # Y轴循环
        #for j in range(len(self.result[i])):    # X轴循环
        #print self.result[i][j],
        #print
        #print "----------------------"


    def compare(self, image_a, image_b):
        '''返回两图的差异值
        返回两图红绿蓝差值万分比之和'''
        histogram_a = image_a.histogram()
        histogram_b = image_b.histogram()
        if len(histogram_a) != 768 or len(histogram_b) != 768:
            return None

        red_a = 0
        red_b = 0
        for i in range(0, 256):
            red_a += histogram_a[i + 0] * i
            red_b += histogram_b[i + 0] * i
        diff_red = 0
        if red_a + red_b > 0:
            diff_red = abs(red_a - red_b) * 10000 / max(red_a, red_b)

        green_a = 0
        green_b = 0
        for i in range(0, 256):
            green_a += histogram_a[i + 256] * i
            green_b += histogram_b[i + 256] * i
        diff_green = 0
        if green_a + green_b > 0:
            diff_green = abs(green_a - green_b) * 10000 / max(green_a, green_b)

        blue_a = 0
        blue_b = 0
        for i in range(0, 256):
            blue_a += histogram_a[i + 512] * i
            blue_b += histogram_b[i + 512] * i
        diff_blue = 0
        if blue_a + blue_b > 0:
            diff_blue = abs(blue_a - blue_b) * 10000 / max(blue_a, blue_b)

        return diff_red, diff_green, diff_blue

    def DebugTime(self, text=""):
        return

        if self.clock > 0:
            print
            time.clock() - self.clock, text
        self.clock = time.clock()
Beispiel #40
0
class Sansimera(QMainWindow):
    def __init__(self, parent=None):
        super(Sansimera, self).__init__(parent)
        self.settings = QSettings()
        self.timer = QTimer(self)
        self.timer_reminder = QTimer(self)
        self.timer_reminder.timeout.connect(self.reminder_tray)
        interval = self.settings.value('Interval') or '1'
        if interval != '0':
            self.timer_reminder.start(int(interval) * 60 * 60 * 1000)
        self.tentatives = 0
        self.gui()
        self.lista = []
        self.lista_pos = 0
        self.eortazontes_shown = False
        self.eortazontes_names = ''

    def gui(self):
        self.systray = QSystemTrayIcon()
        self.icon = QIcon(':/sansimera.png')
        self.systray.setIcon(self.icon)
        self.systray.setToolTip('Σαν σήμερα...')
        self.menu = QMenu()
        self.exitAction = QAction('&Έξοδος', self)
        self.refreshAction = QAction('&Ανανέωση', self)
        self.aboutAction = QAction('&Σχετικά', self)
        self.notification_interval = QAction('Ει&δοποίηση εορταζόντων', self)
        self.menu.addAction(self.notification_interval)
        self.menu.addAction(self.refreshAction)
        self.menu.addAction(self.aboutAction)
        self.menu.addAction(self.exitAction)
        self.systray.setContextMenu(self.menu)
        self.notification_interval.triggered.connect(self.interval_namedays)
        self.exitAction.triggered.connect(exit)
        self.refreshAction.triggered.connect(self.refresh)
        self.aboutAction.triggered.connect(self.about)
        self.browser = QTextBrowser()
        self.browser.setOpenExternalLinks(True)
        self.setGeometry(600, 500, 400, 300)
        self.setWindowIcon(self.icon)
        self.setWindowTitle('Σαν σήμερα...')
        self.setCentralWidget(self.browser)
        self.systray.show()
        self.systray.activated.connect(self.activate)
        self.browser.append('Λήψη...')
        nicon = QIcon(':/next')
        picon = QIcon(':/previous')
        ricon = QIcon(':/refresh')
        iicon = QIcon(':/info')
        qicon = QIcon(':/exit')
        inicon = QIcon(':/notifications')
        self.nextAction = QAction('Επόμενο', self)
        self.nextAction.setIcon(nicon)
        self.previousAction = QAction('Προηγούμενο', self)
        self.refreshAction.triggered.connect(self.refresh)
        self.nextAction.triggered.connect(self.nextItem)
        self.previousAction.triggered.connect(self.previousItem)
        self.previousAction.setIcon(picon)
        self.refreshAction.setIcon(ricon)
        self.exitAction.setIcon(qicon)
        self.aboutAction.setIcon(iicon)
        self.notification_interval.setIcon(inicon)
        controls = QToolBar()
        self.addToolBar(Qt.BottomToolBarArea, controls)
        controls.setObjectName('Controls')
        controls.addAction(self.previousAction)
        controls.addAction(self.nextAction)
        controls.addAction(self.refreshAction)
        self.restoreState(self.settings.value("MainWindow/State", QByteArray()))
        self.refresh()

    def interval_namedays(self):
        dialog = sansimera_reminder.Reminder(self)
        dialog.applied_signal['QString'].connect(self.reminder)
        if dialog.exec_() == 1:
            print('Apply namedays reminder interval...')

    def reminder(self, time):
        self.settings.setValue('Interval', time)
        if time != '0':
            self.timer_reminder.start(int(time) * 60 * 60 * 1000)
            print('Reminder = ' + time + ' hour(s)')
        else:
            print('Reminder = None')

    def nextItem(self):
        if len(self.lista) >= 1:
            self.browser.clear()
            if self.lista_pos != len(self.lista)-1:
                self.lista_pos += 1
            else:
                self.lista_pos = 0
            self.browser.append(self.lista[self.lista_pos])
            self.browser.moveCursor(QTextCursor.Start)
        else:
            return

    def previousItem(self):
        if len(self.lista) >= 1:
            self.browser.clear()
            if self.lista_pos == 0:
                self.lista_pos = len(self.lista)-1
            else:
                self.lista_pos -= 1
            self.browser.append(self.lista[self.lista_pos])
            self.browser.moveCursor(QTextCursor.Start)
        else:
            return

    def refresh(self):
        try:
            if self.workThread.isRunning():
                return
        except AttributeError:
            pass
        self.menu.hide()
        self.browser.clear()
        self.lista = []
        self.systray.setToolTip('Σαν σήμερα...')
        self.browser.append('Λήψη...')
        self.tentatives = 0
        self.eortazontes_shown = False
        self.download()

    def activate(self, reason):
        self.menu.hide()
        state = self.isVisible()
        if reason == 3:
            if state:
                self.hide()
                return
            else:
                self.show()
                return
        if reason == 1:
            self.menu.hide()
            self.menu.popup(QCursor.pos())

    def download(self):
        self.workThread = WorkThread()
        self.workThread.online_signal[bool].connect(self.status)
        self.workThread.finished.connect(self.window)
        self.workThread.event['QString'].connect(self.addlist)
        self.workThread.names['QString'].connect(self.nameintooltip)
        self.workThread.start()

    def addlist(self, text):
        self.lista.append(text)

    def status(self, status):
        self.status_online = status

    def reminder_tray(self):
        text = self.eortazontes_names.replace('<br/>', '\n')
        urltexts = re.findall('(<a [\S]+php">)', text)
        urltexts.extend(['</a>', '<p>', '<div>'])
        show_notifier_text = text
        for i in urltexts:
            show_notifier_text = show_notifier_text.replace(i, '')
        show_notifier_text = show_notifier_text.replace('\n\n', '\n')
        show_notifier_text = show_notifier_text.replace('www.eortologio.gr)', 'www.eortologio.gr)\n')
        self.systray.showMessage('Εορτάζουν:\n', show_notifier_text)
        self.systray.setToolTip('Εορτάζουν:\n' + show_notifier_text)

    def nameintooltip(self, text):
        self.eortazontes_names = text
        for i in ['<br/>', '<div>']:
            text = text.replace(i, '')
        self.eortazontes_in_window = text
        if self.eortazontes_shown:
            return
        self.reminder_tray()
        self.eortazontes_shown = True

    def window(self):
        self.lista.append('<div class=""></div>' + self.eortazontes_in_window)
        if self.status_online:
            self.browser.clear()
            self.browser.append(self.lista[0])
            self.lista_pos = 0
            return
        else:
            if self.tentatives == 10:
                return
            self.timer.singleShot(5000, self.refresh)
            self.tentatives += 1

    def closeEvent(self, event):
        self.settings.setValue("MainWindow/State", self.saveState())

    def about(self):
        self.menu.hide()
        QMessageBox.about(self, "Εφαρμογή «Σαν σήμερα...»",
                        """<b>sansimera-qt</b> v{0}
                        <p>Δημήτριος Γλενταδάκης <a href="mailto:[email protected]">[email protected]</a>
                        <br/>Ιστοσελίδα: <a href="https://github.com/dglent/sansimera-qt">
                        github sansimera-qt</a>
                        <p>Εφαρμογή πλαισίου συστήματος για την προβολή
                        <br/>των γεγονότων από την ιστοσελίδα <a href="http://www.sansimera.gr">
                        www.sansimera.gr</a><br/>
                        Πηγή εορτολογίου: <a href="http://www.eortologio.gr">
                        www.eortologio.gr</a>, <a href="http://www.synaxari.gr">
                        www.synaxari.gr</a>
                        <p>Άδεια χρήσης: GPLv3 <br/>Python {1} - Qt {2} - PyQt {3} σε {4}""".format(
                        __version__, platform.python_version(),
                        QT_VERSION_STR, PYQT_VERSION_STR, platform.system()))
class MainWindow(QMainWindow):

    """Voice Changer main window."""

    def __init__(self, parent=None):
        super(MainWindow, self).__init__()
        self.statusBar().showMessage("Move Dial to Deform Microphone Voice !.")
        self.setWindowTitle(__doc__)
        self.setMinimumSize(240, 240)
        self.setMaximumSize(480, 480)
        self.resize(self.minimumSize())
        self.setWindowIcon(QIcon.fromTheme("audio-input-microphone"))
        self.tray = QSystemTrayIcon(self)
        self.center()
        QShortcut("Ctrl+q", self, activated=lambda: self.close())
        self.menuBar().addMenu("&File").addAction("Quit", lambda: exit())
        self.menuBar().addMenu("Sound").addAction(
            "STOP !", lambda: call('killall rec', shell=True))
        windowMenu = self.menuBar().addMenu("&Window")
        windowMenu.addAction("Hide", lambda: self.hide())
        windowMenu.addAction("Minimize", lambda: self.showMinimized())
        windowMenu.addAction("Maximize", lambda: self.showMaximized())
        windowMenu.addAction("Restore", lambda: self.showNormal())
        windowMenu.addAction("FullScreen", lambda: self.showFullScreen())
        windowMenu.addAction("Center", lambda: self.center())
        windowMenu.addAction("Top-Left", lambda: self.move(0, 0))
        windowMenu.addAction("To Mouse", lambda: self.move_to_mouse_position())
        # widgets
        group0 = QGroupBox("Voice Deformation")
        self.setCentralWidget(group0)
        self.process = QProcess(self)
        self.process.error.connect(
            lambda: self.statusBar().showMessage("Info: Process Killed", 5000))
        self.control = QDial()
        self.control.setRange(-10, 20)
        self.control.setSingleStep(5)
        self.control.setValue(0)
        self.control.setCursor(QCursor(Qt.OpenHandCursor))
        self.control.sliderPressed.connect(
            lambda: self.control.setCursor(QCursor(Qt.ClosedHandCursor)))
        self.control.sliderReleased.connect(
            lambda: self.control.setCursor(QCursor(Qt.OpenHandCursor)))
        self.control.valueChanged.connect(
            lambda: self.control.setToolTip(f"<b>{self.control.value()}"))
        self.control.valueChanged.connect(
            lambda: self.statusBar().showMessage(
                f"Voice deformation: {self.control.value()}", 5000))
        self.control.valueChanged.connect(self.run)
        self.control.valueChanged.connect(lambda: self.process.kill())
        # Graphic effect
        self.glow = QGraphicsDropShadowEffect(self)
        self.glow.setOffset(0)
        self.glow.setBlurRadius(99)
        self.glow.setColor(QColor(99, 255, 255))
        self.control.setGraphicsEffect(self.glow)
        self.glow.setEnabled(False)
        # Timer to start
        self.slider_timer = QTimer(self)
        self.slider_timer.setSingleShot(True)
        self.slider_timer.timeout.connect(self.on_slider_timer_timeout)
        # an icon and set focus
        QLabel(self.control).setPixmap(
            QIcon.fromTheme("audio-input-microphone").pixmap(32))
        self.control.setFocus()
        QVBoxLayout(group0).addWidget(self.control)
        self.menu = QMenu(__doc__)
        self.menu.addAction(__doc__).setDisabled(True)
        self.menu.setIcon(self.windowIcon())
        self.menu.addSeparator()
        self.menu.addAction(
            "Show / Hide",
            lambda: self.hide() if self.isVisible() else self.showNormal())
        self.menu.addAction("STOP !", lambda: call('killall rec', shell=True))
        self.menu.addSeparator()
        self.menu.addAction("Quit", lambda: exit())
        self.tray.setContextMenu(self.menu)
        self.make_trayicon()

    def run(self):
        """Run/Stop the QTimer."""
        if self.slider_timer.isActive():
            self.slider_timer.stop()
        self.glow.setEnabled(True)
        call('killall rec ; killall play', shell=True)
        self.slider_timer.start(3000)

    def on_slider_timer_timeout(self):
        """Run subprocess to deform voice."""
        self.glow.setEnabled(False)
        value = int(self.control.value()) * 100
        command = f'play -q -V0 "|rec -q -V0 -n -d -R riaa bend pitch {value} "'
        print(f"Voice Deformation Value: {value}")
        print(f"Voice Deformation Command: {command}")
        self.process.start(command)
        if self.isVisible():
            self.statusBar().showMessage("Minimizing to System TrayIcon", 3000)
            print("Minimizing Main Window to System TrayIcon now...")
            sleep(3)
            self.hide()

    def center(self):
        """Center Window on the Current Screen,with Multi-Monitor support."""
        window_geometry = self.frameGeometry()
        mousepointer_position = QApplication.desktop().cursor().pos()
        screen = QApplication.desktop().screenNumber(mousepointer_position)
        centerPoint = QApplication.desktop().screenGeometry(screen).center()
        window_geometry.moveCenter(centerPoint)
        self.move(window_geometry.topLeft())

    def move_to_mouse_position(self):
        """Center the Window on the Current Mouse position."""
        window_geometry = self.frameGeometry()
        window_geometry.moveCenter(QApplication.desktop().cursor().pos())
        self.move(window_geometry.topLeft())

    def make_trayicon(self):
        """Make a Tray Icon."""
        if self.windowIcon() and __doc__:
            self.tray.setIcon(self.windowIcon())
            self.tray.setToolTip(__doc__)
            self.tray.activated.connect(
                lambda: self.hide() if self.isVisible()
                else self.showNormal())
            return self.tray.show()
Beispiel #42
0
class TriblerWindow(QMainWindow):
    resize_event = pyqtSignal()
    escape_pressed = pyqtSignal()
    tribler_crashed = pyqtSignal(str)
    received_search_completions = pyqtSignal(object)

    def on_exception(self, *exc_info):
        if self.exception_handler_called:
            # We only show one feedback dialog, even when there are two consecutive exceptions.
            return

        self.exception_handler_called = True

        exception_text = "".join(traceback.format_exception(*exc_info))
        logging.error(exception_text)
        self.tribler_crashed.emit(exception_text)

        self.delete_tray_icon()

        # Stop the download loop
        self.downloads_page.stop_loading_downloads()

        # Add info about whether we are stopping Tribler or not
        os.environ['TRIBLER_SHUTTING_DOWN'] = str(self.core_manager.shutting_down)

        if not self.core_manager.shutting_down:
            self.core_manager.stop(stop_app_on_shutdown=False)

        self.setHidden(True)

        if self.debug_window:
            self.debug_window.setHidden(True)

        dialog = FeedbackDialog(self, exception_text, self.core_manager.events_manager.tribler_version,
                                self.start_time)
        dialog.show()

    def __init__(self, core_args=None, core_env=None, api_port=None):
        QMainWindow.__init__(self)

        QCoreApplication.setOrganizationDomain("nl")
        QCoreApplication.setOrganizationName("TUDelft")
        QCoreApplication.setApplicationName("Tribler")
        QCoreApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)

        self.gui_settings = QSettings()
        api_port = api_port or int(get_gui_setting(self.gui_settings, "api_port", DEFAULT_API_PORT))
        dispatcher.update_worker_settings(port=api_port)

        self.navigation_stack = []
        self.tribler_started = False
        self.tribler_settings = None
        self.debug_window = None
        self.core_manager = CoreManager(api_port)
        self.pending_requests = {}
        self.pending_uri_requests = []
        self.download_uri = None
        self.dialog = None
        self.new_version_dialog = None
        self.start_download_dialog_active = False
        self.request_mgr = None
        self.search_request_mgr = None
        self.search_suggestion_mgr = None
        self.selected_torrent_files = []
        self.vlc_available = True
        self.has_search_results = False
        self.last_search_query = None
        self.last_search_time = None
        self.start_time = time.time()
        self.exception_handler_called = False
        self.token_refresh_timer = None
        self.shutdown_timer = None
        self.add_torrent_url_dialog_active = False

        sys.excepthook = self.on_exception

        uic.loadUi(get_ui_file_path('mainwindow.ui'), self)
        TriblerRequestManager.window = self
        self.tribler_status_bar.hide()

        self.token_balance_widget.mouseReleaseEvent = self.on_token_balance_click

        def on_state_update(new_state):
            self.loading_text_label.setText(new_state)

        self.core_manager.core_state_update.connect(on_state_update)

        self.magnet_handler = MagnetHandler(self.window)
        QDesktopServices.setUrlHandler("magnet", self.magnet_handler, "on_open_magnet_link")

        self.debug_pane_shortcut = QShortcut(QKeySequence("Ctrl+d"), self)
        self.debug_pane_shortcut.activated.connect(self.clicked_menu_button_debug)
        self.import_torrent_shortcut = QShortcut(QKeySequence("Ctrl+o"), self)
        self.import_torrent_shortcut.activated.connect(self.on_add_torrent_browse_file)
        self.add_torrent_url_shortcut = QShortcut(QKeySequence("Ctrl+i"), self)
        self.add_torrent_url_shortcut.activated.connect(self.on_add_torrent_from_url)

        # Remove the focus rect on OS X
        for widget in self.findChildren(QLineEdit) + self.findChildren(QListWidget) + self.findChildren(QTreeWidget):
            widget.setAttribute(Qt.WA_MacShowFocusRect, 0)

        self.menu_buttons = [self.left_menu_button_home, self.left_menu_button_search, self.left_menu_button_my_channel,
                             self.left_menu_button_subscriptions, self.left_menu_button_video_player,
                             self.left_menu_button_downloads, self.left_menu_button_discovered]

        self.video_player_page.initialize_player()
        self.search_results_page.initialize_search_results_page(self.gui_settings)
        self.settings_page.initialize_settings_page()
        self.subscribed_channels_page.initialize()
        self.edit_channel_page.initialize_edit_channel_page(self.gui_settings)
        self.downloads_page.initialize_downloads_page()
        self.home_page.initialize_home_page()
        self.loading_page.initialize_loading_page()
        self.discovering_page.initialize_discovering_page()
        self.discovered_page.initialize_discovered_page(self.gui_settings)
        self.channel_page.initialize_channel_page(self.gui_settings)
        self.trust_page.initialize_trust_page()
        self.token_mining_page.initialize_token_mining_page()

        self.stackedWidget.setCurrentIndex(PAGE_LOADING)

        # Create the system tray icon
        if QSystemTrayIcon.isSystemTrayAvailable():
            self.tray_icon = QSystemTrayIcon()
            use_monochrome_icon = get_gui_setting(self.gui_settings, "use_monochrome_icon", False, is_bool=True)
            self.update_tray_icon(use_monochrome_icon)

            # Create the tray icon menu
            menu = self.create_add_torrent_menu()
            show_downloads_action = QAction('Show downloads', self)
            show_downloads_action.triggered.connect(self.clicked_menu_button_downloads)
            token_balance_action = QAction('Show token balance', self)
            token_balance_action.triggered.connect(lambda: self.on_token_balance_click(None))
            quit_action = QAction('Quit Tribler', self)
            quit_action.triggered.connect(self.close_tribler)
            menu.addSeparator()
            menu.addAction(show_downloads_action)
            menu.addAction(token_balance_action)
            menu.addSeparator()
            menu.addAction(quit_action)
            self.tray_icon.setContextMenu(menu)
        else:
            self.tray_icon = None

        self.hide_left_menu_playlist()
        self.left_menu_button_debug.setHidden(True)
        self.top_menu_button.setHidden(True)
        self.left_menu.setHidden(True)
        self.token_balance_widget.setHidden(True)
        self.settings_button.setHidden(True)
        self.add_torrent_button.setHidden(True)
        self.top_search_bar.setHidden(True)

        # Set various icons
        self.top_menu_button.setIcon(QIcon(get_image_path('menu.png')))

        self.search_completion_model = QStringListModel()
        completer = QCompleter()
        completer.setModel(self.search_completion_model)
        completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        self.item_delegate = QStyledItemDelegate()
        completer.popup().setItemDelegate(self.item_delegate)
        completer.popup().setStyleSheet("""
        QListView {
            background-color: #404040;
        }

        QListView::item {
            color: #D0D0D0;
            padding-top: 5px;
            padding-bottom: 5px;
        }

        QListView::item:hover {
            background-color: #707070;
        }
        """)
        self.top_search_bar.setCompleter(completer)

        # Toggle debug if developer mode is enabled
        self.window().left_menu_button_debug.setHidden(
            not get_gui_setting(self.gui_settings, "debug", False, is_bool=True))

        # Start Tribler
        self.core_manager.start(core_args=core_args, core_env=core_env)

        self.core_manager.events_manager.torrent_finished.connect(self.on_torrent_finished)
        self.core_manager.events_manager.new_version_available.connect(self.on_new_version_available)
        self.core_manager.events_manager.tribler_started.connect(self.on_tribler_started)
        self.core_manager.events_manager.events_started.connect(self.on_events_started)
        self.core_manager.events_manager.low_storage_signal.connect(self.on_low_storage)
        self.core_manager.events_manager.credit_mining_signal.connect(self.on_credit_mining_error)
        self.core_manager.events_manager.tribler_shutdown_signal.connect(self.on_tribler_shutdown_state_update)

        self.core_manager.events_manager.upgrader_tick.connect(
            lambda text: self.show_status_bar("Upgrading Tribler database: " + text))
        self.core_manager.events_manager.upgrader_finished.connect(
            lambda _: self.hide_status_bar())

        self.core_manager.events_manager.received_search_result.connect(
            self.search_results_page.received_search_result)

        # Install signal handler for ctrl+c events
        def sigint_handler(*_):
            self.close_tribler()

        signal.signal(signal.SIGINT, sigint_handler)

        self.installEventFilter(self.video_player_page)

        # Resize the window according to the settings
        center = QApplication.desktop().availableGeometry(self).center()
        pos = self.gui_settings.value("pos", QPoint(center.x() - self.width() * 0.5, center.y() - self.height() * 0.5))
        size = self.gui_settings.value("size", self.size())

        self.move(pos)
        self.resize(size)

        self.show()

    def update_tray_icon(self, use_monochrome_icon):
        if not QSystemTrayIcon.isSystemTrayAvailable() or not self.tray_icon:
            return

        if use_monochrome_icon:
            self.tray_icon.setIcon(QIcon(QPixmap(get_image_path('monochrome_tribler.png'))))
        else:
            self.tray_icon.setIcon(QIcon(QPixmap(get_image_path('tribler.png'))))
        self.tray_icon.show()

    def delete_tray_icon(self):
        if self.tray_icon:
            try:
                self.tray_icon.deleteLater()
            except RuntimeError:
                # The tray icon might have already been removed when unloading Qt.
                # This is due to the C code actually being asynchronous.
                logging.debug("Tray icon already removed, no further deletion necessary.")
            self.tray_icon = None

    def on_low_storage(self):
        """
        Dealing with low storage space available. First stop the downloads and the core manager and ask user to user to
        make free space.
        :return:
        """
        self.downloads_page.stop_loading_downloads()
        self.core_manager.stop(False)
        close_dialog = ConfirmationDialog(self.window(), "<b>CRITICAL ERROR</b>",
                                          "You are running low on disk space (<100MB). Please make sure to have "
                                          "sufficient free space available and restart Tribler again.",
                                          [("Close Tribler", BUTTON_TYPE_NORMAL)])
        close_dialog.button_clicked.connect(lambda _: self.close_tribler())
        close_dialog.show()

    def on_torrent_finished(self, torrent_info):
        self.tray_show_message("Download finished", "Download of %s has finished." % torrent_info["name"])

    def show_loading_screen(self):
        self.top_menu_button.setHidden(True)
        self.left_menu.setHidden(True)
        self.token_balance_widget.setHidden(True)
        self.settings_button.setHidden(True)
        self.add_torrent_button.setHidden(True)
        self.top_search_bar.setHidden(True)
        self.stackedWidget.setCurrentIndex(PAGE_LOADING)

    def tray_set_tooltip(self, message):
        """
        Set a tooltip message for the tray icon, if possible.

        :param message: the message to display on hover
        """
        if self.tray_icon:
            try:
                self.tray_icon.setToolTip(message)
            except RuntimeError as e:
                logging.error("Failed to set tray tooltip: %s", str(e))

    def tray_show_message(self, title, message):
        """
        Show a message at the tray icon, if possible.

        :param title: the title of the message
        :param message: the message to display
        """
        if self.tray_icon:
            try:
                self.tray_icon.showMessage(title, message)
            except RuntimeError as e:
                logging.error("Failed to set tray message: %s", str(e))

    def on_tribler_started(self):
        self.tribler_started = True

        self.top_menu_button.setHidden(False)
        self.left_menu.setHidden(False)
        self.token_balance_widget.setHidden(False)
        self.settings_button.setHidden(False)
        self.add_torrent_button.setHidden(False)
        self.top_search_bar.setHidden(False)

        # fetch the settings, needed for the video player port
        self.request_mgr = TriblerRequestManager()
        self.fetch_settings()

        self.downloads_page.start_loading_downloads()
        self.home_page.load_popular_torrents()
        if not self.gui_settings.value("first_discover", False) and not self.core_manager.use_existing_core:
            self.window().gui_settings.setValue("first_discover", True)
            self.discovering_page.is_discovering = True
            self.stackedWidget.setCurrentIndex(PAGE_DISCOVERING)
        else:
            self.clicked_menu_button_home()

        self.setAcceptDrops(True)

    def on_events_started(self, json_dict):
        self.setWindowTitle("Tribler %s" % json_dict["version"])

    def show_status_bar(self, message):
        self.tribler_status_bar_label.setText(message)
        self.tribler_status_bar.show()

    def hide_status_bar(self):
        self.tribler_status_bar.hide()

    def process_uri_request(self):
        """
        Process a URI request if we have one in the queue.
        """
        if len(self.pending_uri_requests) == 0:
            return

        uri = self.pending_uri_requests.pop()
        if uri.startswith('file') or uri.startswith('magnet'):
            self.start_download_from_uri(uri)

    def perform_start_download_request(self, uri, anon_download, safe_seeding, destination, selected_files,
                                       total_files=0, callback=None):
        # Check if destination directory is writable
        is_writable, error = is_dir_writable(destination)
        if not is_writable:
            gui_error_message = "Insufficient write permissions to <i>%s</i> directory. Please add proper " \
                                "write permissions on the directory and add the torrent again. %s" \
                                % (destination, error)
            ConfirmationDialog.show_message(self.window(), "Download error <i>%s</i>" % uri, gui_error_message, "OK")
            return

        selected_files_list = []
        if len(selected_files) != total_files:  # Not all files included
            selected_files_list = [filename for filename in selected_files]

        anon_hops = int(self.tribler_settings['download_defaults']['number_hops']) if anon_download else 0
        safe_seeding = 1 if safe_seeding else 0
        post_data = {
            "uri": uri,
            "anon_hops": anon_hops,
            "safe_seeding": safe_seeding,
            "destination": destination,
            "selected_files": selected_files_list
        }
        request_mgr = TriblerRequestManager()
        request_mgr.perform_request("downloads", callback if callback else self.on_download_added,
                                    method='PUT', data=post_data)

        # Save the download location to the GUI settings
        current_settings = get_gui_setting(self.gui_settings, "recent_download_locations", "")
        recent_locations = current_settings.split(",") if len(current_settings) > 0 else []
        if isinstance(destination, six.text_type):
            destination = destination.encode('utf-8')
        encoded_destination = hexlify(destination)
        if encoded_destination in recent_locations:
            recent_locations.remove(encoded_destination)
        recent_locations.insert(0, encoded_destination)

        if len(recent_locations) > 5:
            recent_locations = recent_locations[:5]

        self.gui_settings.setValue("recent_download_locations", ','.join(recent_locations))

    def on_new_version_available(self, version):
        if version == str(self.gui_settings.value('last_reported_version')):
            return

        self.new_version_dialog = ConfirmationDialog(self, "New version available",
                                                     "Version %s of Tribler is available.Do you want to visit the "
                                                     "website to download the newest version?" % version,
                                                     [('IGNORE', BUTTON_TYPE_NORMAL), ('LATER', BUTTON_TYPE_NORMAL),
                                                      ('OK', BUTTON_TYPE_NORMAL)])
        self.new_version_dialog.button_clicked.connect(lambda action: self.on_new_version_dialog_done(version, action))
        self.new_version_dialog.show()

    def on_new_version_dialog_done(self, version, action):
        if action == 0:  # ignore
            self.gui_settings.setValue("last_reported_version", version)
        elif action == 2:  # ok
            import webbrowser
            webbrowser.open("https://tribler.org")
        if self.new_version_dialog:
            self.new_version_dialog.close_dialog()
            self.new_version_dialog = None

    def on_search_text_change(self, text):
        self.search_suggestion_mgr = TriblerRequestManager()
        self.search_suggestion_mgr.perform_request(
            "search/completions", self.on_received_search_completions, url_params={'q': sanitize_for_fts(text)})

    def on_received_search_completions(self, completions):
        if completions is None:
            return
        self.received_search_completions.emit(completions)
        self.search_completion_model.setStringList(completions["completions"])

    def fetch_settings(self):
        self.request_mgr = TriblerRequestManager()
        self.request_mgr.perform_request("settings", self.received_settings, capture_errors=False)

    def received_settings(self, settings):
        if not settings:
            return
        # If we cannot receive the settings, stop Tribler with an option to send the crash report.
        if 'error' in settings:
            raise RuntimeError(TriblerRequestManager.get_message_from_error(settings))

        self.tribler_settings = settings['settings']

        # Set the video server port
        self.video_player_page.video_player_port = settings["ports"]["video_server~port"]

        # Disable various components based on the settings
        if not self.tribler_settings['video_server']['enabled']:
            self.left_menu_button_video_player.setHidden(True)
        self.downloads_creditmining_button.setHidden(not self.tribler_settings["credit_mining"]["enabled"])
        self.downloads_all_button.click()

        # process pending file requests (i.e. someone clicked a torrent file when Tribler was closed)
        # We do this after receiving the settings so we have the default download location.
        self.process_uri_request()

        # Set token balance refresh timer and load the token balance
        self.token_refresh_timer = QTimer()
        self.token_refresh_timer.timeout.connect(self.load_token_balance)
        self.token_refresh_timer.start(60000)

        self.load_token_balance()

    def on_top_search_button_click(self):
        current_ts = time.time()
        current_search_query = self.top_search_bar.text()

        if self.last_search_query and self.last_search_time \
                and self.last_search_query == self.top_search_bar.text() \
                and current_ts - self.last_search_time < 1:
            logging.info("Same search query already sent within 500ms so dropping this one")
            return

        self.left_menu_button_search.setChecked(True)
        self.has_search_results = True
        self.clicked_menu_button_search()
        self.search_results_page.perform_search(current_search_query)
        self.last_search_query = current_search_query
        self.last_search_time = current_ts

    def on_settings_button_click(self):
        self.deselect_all_menu_buttons()
        self.stackedWidget.setCurrentIndex(PAGE_SETTINGS)
        self.settings_page.load_settings()
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def on_token_balance_click(self, _):
        self.raise_window()
        self.deselect_all_menu_buttons()
        self.stackedWidget.setCurrentIndex(PAGE_TRUST)
        self.load_token_balance()
        self.trust_page.load_blocks()
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def load_token_balance(self):
        self.request_mgr = TriblerRequestManager()
        self.request_mgr.perform_request("trustchain/statistics", self.received_trustchain_statistics,
                                         capture_errors=False)

    def received_trustchain_statistics(self, statistics):
        if not statistics or "statistics" not in statistics:
            return

        self.trust_page.received_trustchain_statistics(statistics)

        statistics = statistics["statistics"]
        if 'latest_block' in statistics:
            balance = (statistics["latest_block"]["transaction"]["total_up"] -
                       statistics["latest_block"]["transaction"]["total_down"])
            self.set_token_balance(balance)
        else:
            self.token_balance_label.setText("0 MB")

        # If trust page is currently visible, then load the graph as well
        if self.stackedWidget.currentIndex() == PAGE_TRUST:
            self.trust_page.load_blocks()

    def set_token_balance(self, balance):
        if abs(balance) > 1024 ** 4:  # Balance is over a TB
            balance /= 1024.0 ** 4
            self.token_balance_label.setText("%.1f TB" % balance)
        elif abs(balance) > 1024 ** 3:  # Balance is over a GB
            balance /= 1024.0 ** 3
            self.token_balance_label.setText("%.1f GB" % balance)
        else:
            balance /= 1024.0 ** 2
            self.token_balance_label.setText("%d MB" % balance)

    def raise_window(self):
        self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive)
        self.raise_()
        self.activateWindow()

    def create_add_torrent_menu(self):
        """
        Create a menu to add new torrents. Shows when users click on the tray icon or the big plus button.
        """
        menu = TriblerActionMenu(self)

        browse_files_action = QAction('Import torrent from file', self)
        browse_directory_action = QAction('Import torrent(s) from directory', self)
        add_url_action = QAction('Import torrent from magnet/URL', self)
        add_mdblob_action = QAction('Import Tribler metadata from file', self)

        browse_files_action.triggered.connect(self.on_add_torrent_browse_file)
        browse_directory_action.triggered.connect(self.on_add_torrent_browse_dir)
        add_url_action.triggered.connect(self.on_add_torrent_from_url)
        add_mdblob_action.triggered.connect(self.on_add_mdblob_browse_file)

        menu.addAction(browse_files_action)
        menu.addAction(browse_directory_action)
        menu.addAction(add_url_action)
        menu.addAction(add_mdblob_action)

        return menu

    def on_add_torrent_button_click(self, pos):
        self.create_add_torrent_menu().exec_(self.mapToGlobal(self.add_torrent_button.pos()))

    def on_add_torrent_browse_file(self):
        filenames = QFileDialog.getOpenFileNames(self,
                                                 "Please select the .torrent file",
                                                 QDir.homePath(),
                                                 "Torrent files (*.torrent)")
        if len(filenames[0]) > 0:
            [self.pending_uri_requests.append(u"file:%s" % filename) for filename in filenames[0]]
            self.process_uri_request()

    def on_add_mdblob_browse_file(self):
        filenames = QFileDialog.getOpenFileNames(self,
                                                 "Please select the .mdblob file",
                                                 QDir.homePath(),
                                                 "Tribler metadata files (*.mdblob)")
        if len(filenames[0]) > 0:
            for filename in filenames[0]:
                self.pending_uri_requests.append(u"file:%s" % filename)
            self.process_uri_request()

    def start_download_from_uri(self, uri):
        self.download_uri = uri

        if get_gui_setting(self.gui_settings, "ask_download_settings", True, is_bool=True):
            # If tribler settings is not available, fetch the settings and inform the user to try again.
            if not self.tribler_settings:
                self.fetch_settings()
                ConfirmationDialog.show_error(self, "Download Error", "Tribler settings is not available yet. "
                                                                      "Fetching it now. Please try again later.")
                return
            # Clear any previous dialog if exists
            if self.dialog:
                self.dialog.close_dialog()
                self.dialog = None

            self.dialog = StartDownloadDialog(self, self.download_uri)
            self.dialog.button_clicked.connect(self.on_start_download_action)
            self.dialog.show()
            self.start_download_dialog_active = True
        else:
            # In the unlikely scenario that tribler settings are not available yet, try to fetch settings again and
            # add the download uri back to self.pending_uri_requests to process again.
            if not self.tribler_settings:
                self.fetch_settings()
                if self.download_uri not in self.pending_uri_requests:
                    self.pending_uri_requests.append(self.download_uri)
                return

            self.window().perform_start_download_request(self.download_uri,
                                                         self.window().tribler_settings['download_defaults'][
                                                             'anonymity_enabled'],
                                                         self.window().tribler_settings['download_defaults'][
                                                             'safeseeding_enabled'],
                                                         self.tribler_settings['download_defaults']['saveas'], [], 0)
            self.process_uri_request()

    def on_start_download_action(self, action):
        if action == 1:
            if self.dialog and self.dialog.dialog_widget:
                self.window().perform_start_download_request(
                    self.download_uri, self.dialog.dialog_widget.anon_download_checkbox.isChecked(),
                    self.dialog.dialog_widget.safe_seed_checkbox.isChecked(),
                    self.dialog.dialog_widget.destination_input.currentText(),
                    self.dialog.get_selected_files(),
                    self.dialog.dialog_widget.files_list_view.topLevelItemCount())
            else:
                ConfirmationDialog.show_error(self, "Tribler UI Error", "Something went wrong. Please try again.")
                logging.exception("Error while trying to download. Either dialog or dialog.dialog_widget is None")

        if self.dialog:
            self.dialog.close_dialog()
            self.dialog = None
            self.start_download_dialog_active = False

        if action == 0:  # We do this after removing the dialog since process_uri_request is blocking
            self.process_uri_request()

    def on_add_torrent_browse_dir(self):
        chosen_dir = QFileDialog.getExistingDirectory(self,
                                                      "Please select the directory containing the .torrent files",
                                                      QDir.homePath(),
                                                      QFileDialog.ShowDirsOnly)

        if len(chosen_dir) != 0:
            self.selected_torrent_files = [torrent_file for torrent_file in glob.glob(chosen_dir + "/*.torrent")]
            self.dialog = ConfirmationDialog(self, "Add torrents from directory",
                                             "Are you sure you want to add %d torrents to Tribler?" %
                                             len(self.selected_torrent_files),
                                             [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)])
            self.dialog.button_clicked.connect(self.on_confirm_add_directory_dialog)
            self.dialog.show()

    def on_confirm_add_directory_dialog(self, action):
        if action == 0:
            for torrent_file in self.selected_torrent_files:
                escaped_uri = u"file:%s" % pathname2url(torrent_file.encode('utf-8'))
                self.perform_start_download_request(escaped_uri,
                                                    self.window().tribler_settings['download_defaults'][
                                                        'anonymity_enabled'],
                                                    self.window().tribler_settings['download_defaults'][
                                                        'safeseeding_enabled'],
                                                    self.tribler_settings['download_defaults']['saveas'], [], 0)

        if self.dialog:
            self.dialog.close_dialog()
            self.dialog = None

    def on_add_torrent_from_url(self):
        # Make sure that the window is visible (this action might be triggered from the tray icon)
        self.raise_window()

        if self.video_player_page.isVisible():
            # If we're adding a torrent from the video player page, go to the home page.
            # This is necessary since VLC takes the screen and the popup becomes invisible.
            self.clicked_menu_button_home()

        if not self.add_torrent_url_dialog_active:
            self.dialog = ConfirmationDialog(self, "Add torrent from URL/magnet link",
                                             "Please enter the URL/magnet link in the field below:",
                                             [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)],
                                             show_input=True)
            self.dialog.dialog_widget.dialog_input.setPlaceholderText('URL/magnet link')
            self.dialog.dialog_widget.dialog_input.setFocus()
            self.dialog.button_clicked.connect(self.on_torrent_from_url_dialog_done)
            self.dialog.show()
            self.add_torrent_url_dialog_active = True

    def on_torrent_from_url_dialog_done(self, action):
        self.add_torrent_url_dialog_active = False
        if self.dialog and self.dialog.dialog_widget:
            uri = self.dialog.dialog_widget.dialog_input.text().strip()

            # If the URI is a 40-bytes hex-encoded infohash, convert it to a valid magnet link
            if len(uri) == 40:
                valid_ih_hex = True
                try:
                    int(uri, 16)
                except ValueError:
                    valid_ih_hex = False

                if valid_ih_hex:
                    uri = "magnet:?xt=urn:btih:" + uri

            # Remove first dialog
            self.dialog.close_dialog()
            self.dialog = None

            if action == 0:
                self.start_download_from_uri(uri)

    def on_download_added(self, result):
        if not result:
            return
        if len(self.pending_uri_requests) == 0:  # Otherwise, we first process the remaining requests.
            self.window().left_menu_button_downloads.click()
        else:
            self.process_uri_request()

    def on_top_menu_button_click(self):
        if self.left_menu.isHidden():
            self.left_menu.show()
        else:
            self.left_menu.hide()

    def deselect_all_menu_buttons(self, except_select=None):
        for button in self.menu_buttons:
            if button == except_select:
                button.setEnabled(False)
                continue
            button.setEnabled(True)

            if button == self.left_menu_button_search and not self.has_search_results:
                button.setEnabled(False)

            button.setChecked(False)

    def clicked_menu_button_home(self):
        self.deselect_all_menu_buttons(self.left_menu_button_home)
        self.stackedWidget.setCurrentIndex(PAGE_HOME)
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def clicked_menu_button_search(self):
        self.deselect_all_menu_buttons(self.left_menu_button_search)
        self.stackedWidget.setCurrentIndex(PAGE_SEARCH_RESULTS)
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def clicked_menu_button_discovered(self):
        self.deselect_all_menu_buttons(self.left_menu_button_discovered)
        self.stackedWidget.setCurrentIndex(PAGE_DISCOVERED)
        self.discovered_page.load_discovered_channels()
        self.discovered_channels_list.setFocus()
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def clicked_menu_button_my_channel(self):
        self.deselect_all_menu_buttons(self.left_menu_button_my_channel)
        self.stackedWidget.setCurrentIndex(PAGE_EDIT_CHANNEL)
        self.edit_channel_page.load_my_channel_overview()
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def clicked_menu_button_video_player(self):
        self.deselect_all_menu_buttons(self.left_menu_button_video_player)
        self.stackedWidget.setCurrentIndex(PAGE_VIDEO_PLAYER)
        self.navigation_stack = []
        self.show_left_menu_playlist()

    def clicked_menu_button_downloads(self):
        self.deselect_all_menu_buttons(self.left_menu_button_downloads)
        self.raise_window()
        self.left_menu_button_downloads.setChecked(True)
        self.stackedWidget.setCurrentIndex(PAGE_DOWNLOADS)
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def clicked_menu_button_debug(self):
        if not self.debug_window:
            self.debug_window = DebugWindow(self.tribler_settings, self.core_manager.events_manager.tribler_version)
        self.debug_window.show()

    def clicked_menu_button_subscriptions(self):
        self.deselect_all_menu_buttons(self.left_menu_button_subscriptions)
        self.stackedWidget.setCurrentIndex(PAGE_SUBSCRIBED_CHANNELS)
        self.subscribed_channels_page.load_subscribed_channels()
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def hide_left_menu_playlist(self):
        self.left_menu_seperator.setHidden(True)
        self.left_menu_playlist_label.setHidden(True)
        self.left_menu_playlist.setHidden(True)

    def show_left_menu_playlist(self):
        self.left_menu_seperator.setHidden(False)
        self.left_menu_playlist_label.setHidden(False)
        self.left_menu_playlist.setHidden(False)

    def on_channel_clicked(self, channel_info):
        self.channel_page.initialize_with_channel(channel_info)
        self.navigation_stack.append(self.stackedWidget.currentIndex())
        self.stackedWidget.setCurrentIndex(PAGE_CHANNEL_DETAILS)

    def on_page_back_clicked(self):
        try:
            prev_page = self.navigation_stack.pop()
            self.stackedWidget.setCurrentIndex(prev_page)
        except IndexError:
            logging.exception("Unknown page found in stack")

    def on_credit_mining_error(self, error):
        ConfirmationDialog.show_error(self, "Credit Mining Error", error[u'message'])

    def on_edit_channel_clicked(self):
        self.stackedWidget.setCurrentIndex(PAGE_EDIT_CHANNEL)
        self.navigation_stack = []
        self.channel_page.on_edit_channel_clicked()

    def resizeEvent(self, _):
        # Resize home page cells
        cell_width = self.home_page_table_view.width() / 3 - 3  # We have some padding to the right
        max_height = self.home_page_table_view.height() / 3 - 4
        cell_height = min(cell_width / 2 + 60, max_height)

        for i in range(0, 3):
            self.home_page_table_view.setColumnWidth(i, cell_width)
            self.home_page_table_view.setRowHeight(i, cell_height)
        self.resize_event.emit()

    def exit_full_screen(self):
        self.top_bar.show()
        self.left_menu.show()
        self.video_player_page.is_full_screen = False
        self.showNormal()

    def close_tribler(self):
        if not self.core_manager.shutting_down:
            def show_force_shutdown():
                self.window().force_shutdown_btn.show()

            self.delete_tray_icon()
            self.show_loading_screen()
            self.hide_status_bar()
            self.loading_text_label.setText("Shutting down...")
            if self.debug_window:
                self.debug_window.setHidden(True)

            self.shutdown_timer = QTimer()
            self.shutdown_timer.timeout.connect(show_force_shutdown)
            self.shutdown_timer.start(SHUTDOWN_WAITING_PERIOD)

            self.gui_settings.setValue("pos", self.pos())
            self.gui_settings.setValue("size", self.size())

            if self.core_manager.use_existing_core:
                # Don't close the core that we are using
                QApplication.quit()

            self.core_manager.stop()
            self.core_manager.shutting_down = True
            self.downloads_page.stop_loading_downloads()
            request_queue.clear()

            # Stop the token balance timer
            if self.token_refresh_timer:
                self.token_refresh_timer.stop()

    def closeEvent(self, close_event):
        self.close_tribler()
        close_event.ignore()

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.escape_pressed.emit()
            if self.isFullScreen():
                self.exit_full_screen()

    def dragEnterEvent(self, e):
        file_urls = [_qurl_to_path(url) for url in e.mimeData().urls()] if e.mimeData().hasUrls() else []

        if any(os.path.isfile(filename) for filename in file_urls):
            e.accept()
        else:
            e.ignore()

    def dropEvent(self, e):
        file_urls = ([(_qurl_to_path(url), url.toString()) for url in e.mimeData().urls()]
                     if e.mimeData().hasUrls() else [])

        for filename, fileurl in file_urls:
            if os.path.isfile(filename):
                self.start_download_from_uri(fileurl)

        e.accept()

    def clicked_force_shutdown(self):
        process_checker = ProcessChecker()
        if process_checker.already_running:
            core_pid = process_checker.get_pid_from_lock_file()
            os.kill(int(core_pid), 9)
        # Stop the Qt application
        QApplication.quit()

    def on_tribler_shutdown_state_update(self, state):
        self.loading_text_label.setText(state)
 def __init__(self):
     """Init class."""
     super(MainWindow, self).__init__()
     self.statusBar().showMessage(getoutput(SYNCTHING + ' --version'))
     self.setWindowTitle(__doc__.strip().capitalize())
     self.setMinimumSize(640, 480)
     self.setMaximumSize(1280, 1024)
     self.resize(self.minimumSize())
     self.setWindowIcon(QIcon.fromTheme("text-x-python"))
     self.center()
     self.view = QWebView(self)
     self.view.loadFinished.connect(self.finishLoading)
     self.view.page().linkHovered.connect(
         lambda link_txt: self.statusBar().showMessage(link_txt[:99], 3000))
     QShortcut("Ctrl++", self, activated=lambda:
               self.view.setZoomFactor(self.view.zoomFactor() + 0.2))
     QShortcut("Ctrl+-", self, activated=lambda:
               self.view.setZoomFactor(self.view.zoomFactor() - 0.2))
     QShortcut("Ctrl+0", self, activated=lambda: self.view.setZoomFactor(1))
     QShortcut("Ctrl+q", self, activated=lambda: self.close())
     self.menuBar().addMenu("File").addAction("Exit", lambda: self.close())
     syncMenu = self.menuBar().addMenu("Sync")
     syncMenu.addAction("Stop Syncronization", lambda: self.process.kill())
     syncMenu.addAction("Start Syncronization", lambda: self.run())
     viewMenu = self.menuBar().addMenu("View")
     viewMenu.addAction(
         "Zoom In",
         lambda: self.view.setZoomFactor(self.view.zoomFactor() + .2))
     viewMenu.addAction(
         "Zoom Out",
         lambda: self.view.setZoomFactor(self.view.zoomFactor() - .2))
     viewMenu.addAction(
         "Zoom To...", lambda: self.view.setZoomFactor(QInputDialog.getInt(
             self, __doc__, "<b>Zoom factor ?:", 1, 1, 9)[0]))
     viewMenu.addAction("Zoom Reset", lambda: self.view.setZoomFactor(1))
     viewMenu.addSeparator()
     viewMenu.addAction("Page Source", self.viewSource)
     viewMenu.addAction("Open Web", lambda: open_new_tab(URL))
     windowMenu = self.menuBar().addMenu("&Window")
     windowMenu.addAction("Minimize", lambda: self.showMinimized())
     windowMenu.addAction("Maximize", lambda: self.showMaximized())
     windowMenu.addAction("Restore", lambda: self.showNormal())
     windowMenu.addAction("Center", lambda: self.center())
     windowMenu.addAction("Top-Left", lambda: self.move(0, 0))
     windowMenu.addAction("To Mouse", lambda: self.move_to_mouse_position())
     windowMenu.addAction("Fullscreen", lambda: self.showFullScreen())
     windowMenu.addSeparator()
     windowMenu.addAction("Increase size", lambda: self.resize(
         self.size().width() * 1.2, self.size().height() * 1.2))
     windowMenu.addAction("Decrease size", lambda: self.resize(
         self.size().width() // 1.2, self.size().height() // 1.2))
     windowMenu.addAction("Minimum size", lambda:
                          self.resize(self.minimumSize()))
     windowMenu.addAction("Maximum size", lambda:
                          self.resize(self.maximumSize()))
     windowMenu.addAction("Horizontal Wide", lambda: self.resize(
         self.maximumSize().width(), self.minimumSize().height()))
     windowMenu.addAction("Vertical Tall", lambda: self.resize(
         self.minimumSize().width(), self.maximumSize().height()))
     windowMenu.addSeparator()
     windowMenu.addAction("Disable Resize",
                          lambda: self.setFixedSize(self.size()))
     helpMenu = self.menuBar().addMenu("&Help")
     helpMenu.addAction("Support Forum", lambda: open_new_tab(HELP_URL_0))
     helpMenu.addAction("Lastest Release", lambda: open_new_tab(HELP_URL_1))
     helpMenu.addAction("Documentation", lambda: open_new_tab(HELP_URL_2))
     helpMenu.addAction("Bugs", lambda: open_new_tab(HELP_URL_3))
     helpMenu.addAction("Source Code", lambda: open_new_tab(HELP_URL_4))
     helpMenu.addSeparator()
     helpMenu.addAction("About Qt 5", lambda: QMessageBox.aboutQt(self))
     helpMenu.addAction("About Python 3",
                        lambda: open_new_tab('https://www.python.org'))
     helpMenu.addAction("About" + __doc__,
                        lambda: QMessageBox.about(self, __doc__, HELPMSG))
     helpMenu.addSeparator()
     helpMenu.addAction("Keyboard Shortcuts", lambda:
                        QMessageBox.information(self, __doc__, SHORTCUTS))
     helpMenu.addAction("View GitHub Repo", lambda: open_new_tab(__url__))
     if not sys.platform.startswith("win"):
         helpMenu.addAction("Show Source Code", lambda: call(
             ('xdg-open ' if sys.platform.startswith("linux") else 'open ')
             + __file__, shell=True))
     helpMenu.addSeparator()
     helpMenu.addAction("Check Updates", lambda: self.check_for_updates())
     # process
     self.process = QProcess()
     self.process.error.connect(self._process_failed)
     # backend options
     self.chrt = QCheckBox("Smooth CPU ", checked=True)
     self.ionice = QCheckBox("Smooth HDD ", checked=True)
     self.chrt.setToolTip("Use Smooth CPUs priority (recommended)")
     self.ionice.setToolTip("Use Smooth HDDs priority (recommended)")
     self.chrt.setStatusTip(self.chrt.toolTip())
     self.ionice.setStatusTip(self.ionice.toolTip())
     # main toolbar
     self.toolbar = self.addToolBar("SyncthinGUI Toolbar")
     self.toolbar.addAction(QIcon.fromTheme("media-playback-stop"),
                            "Stop Sync", lambda: self.process.kill())
     self.toolbar.addAction(QIcon.fromTheme("media-playback-start"),
                            "Restart Sync", lambda: self.run())
     self.toolbar.addSeparator()
     self.toolbar.addWidget(self.chrt)
     self.toolbar.addWidget(self.ionice)
     self.setCentralWidget(self.view)
     # Tray Icon
     tray = QSystemTrayIcon(QIcon.fromTheme("text-x-python"), self)
     tray.setToolTip(__doc__.strip().capitalize())
     traymenu = QMenu(self)
     traymenu.addAction(__doc__).setDisabled(True)
     traymenu.addSeparator()
     traymenu.addAction("Stop Sync", lambda: self.process.kill())
     traymenu.addAction("Restart Sync", lambda: self.run())
     traymenu.addSeparator()
     traymenu.addAction("Show", lambda: self.show_gui())
     traymenu.addAction("Hide", lambda: self.hide())
     traymenu.addSeparator()
     traymenu.addAction("Open Web", lambda: open_new_tab(URL))
     traymenu.addAction("Quit All", lambda: self.close())
     tray.setContextMenu(traymenu)
     tray.show()
     self.run()
Beispiel #44
0
class MainWindow(Ui_MainWindow, QMainWindow):

    def __init__(self):

        QMainWindow.__init__(self)
        Ui_MainWindow.setupUi(self, self)

        self.playlistDrop.dragEnterEvent = self.playlistDragEnterEvent
        self.playlistDrop.dropEvent = self.playlistDropEvent

        self.playlistModel = PlaylistModel()
        self.playlistTable.setModel(self.playlistModel)
        self.playlistTable.customContextMenuRequested.connect(self.playlistContextMenu)
        self.playlistTable.doubleClicked.connect(self.onPlaylistDoubleClick)

        self.playback = PlaybackWidget(self)
        self.playbackLayout.addWidget(self.playback)
        self.playback.playButton.clicked.connect(self.onPlaySelected)

        self.playback.nextButton.clicked.connect(self.onNextClicked)
        self.playback.prevButton.clicked.connect(self.onPrevClicked)

        self.libraryDock.setTitleBarWidget(QWidget())
        self.playlistsDock.setTitleBarWidget(QWidget())
        self.toggleLibraryButton.clicked.connect(self.onToggleLibrary)
        self.togglePlaylistsButton.clicked.connect(self.onTogglePlaylists)

        self.library = LibraryWidget(self)
        self.libraryDock.setWidget(self.library)
        self.libraryDock.hide()
        self.library.itemsActivated.connect(self.onLibraryItemActivated)

        self.playlists = PlaylistsWidget(self)
        self.playlistsDock.setWidget(self.playlists)
        self.playlistsDock.hide()
        self.playlists.loadPlaylist.connect(self.onPlaylistLoad)

        self.dockState = 0

        self.settings = QSettings('txplaya', 'txplaya')

        if u'geometry/main' in self.settings.allKeys():
            self.setGeometry(self.settings.value(u'geometry/main'))

            for col in range(self.playlistModel.columnCount()):
                width = self.settings.value(u'geometry/playlist/col/%d' % col)
                self.playlistTable.setColumnWidth(col, int(width))

            dockState = int(self.settings.value(u'geometry/dock/state'))
            self.dockShow(dockState)

        self.systemTray = QSystemTrayIcon(self.windowIcon())
        self.systemTray.setToolTip('Playa')
        self.systemTray.show()
        self.systemTray.activated.connect(self.systemTrayToggle)
        systemTrayMenu = QMenu()
        systemTrayMenu.addAction(self.restore)
        systemTrayMenu.addAction(self.quit)
        self.systemTray.setContextMenu(systemTrayMenu)
        self.restore.triggered.connect(self.restoreWindow)
        self.quit.triggered.connect(self.quitEvent)
        self.quitButton.clicked.connect(self.quitEvent)
        self.quitFlag = False

        # keyboard shortcuts
        focusLibraryShortcut = QShortcut(QKeySequence('Ctrl+F'), self)
        focusLibraryShortcut.activated.connect(self.onFocusLibrary)
        deleteTrackShortcut = QShortcut(QKeySequence('Del'), self.playlistTable)
        deleteTrackShortcut.setContext(Qt.WidgetShortcut)
        deleteTrackShortcut.activated.connect(self.onDeleteTrack)
        togglePlaybackShortcut = QShortcut(QKeySequence('Space'), self)
        togglePlaybackShortcut.activated.connect(self.onTogglePlayback)
        startShortcut = QShortcut(QKeySequence(Qt.Key_Return), self.playlistTable)
        startShortcut.setContext(Qt.WidgetShortcut)
        startShortcut.activated.connect(self.onPlaySelected)
        undoShortcut = QShortcut(QKeySequence('Ctrl+Z'), self)
        undoShortcut.activated.connect(self.onPlaylistUndo)
        redoShortcut = QShortcut(QKeySequence('Ctrl+Shift+Z'), self)
        redoShortcut.activated.connect(self.onPlaylistRedo)
        saveShortcut = QShortcut(QKeySequence('Ctrl+S'), self)
        saveShortcut.activated.connect(self.onPlaylistSave)

        self.infoStreamStart()
        QTimer.singleShot(200, self.fetchLibrary)

    def infoStreamStart(self):
        self.infoStream = QInfoStream()
        self.infoStream.trackStarted.connect(self.playback.trackStarted)
        self.infoStream.trackStarted.connect(self.playlistModel.trackActivated)
        self.infoStream.playbackFinished.connect(self.playback.finished)
        self.infoStream.playbackFinished.connect(self.playlistModel.trackActivated)
        self.infoStream.playbackPaused.connect(self.playback.paused)
        self.infoStream.playlistChanged.connect(self.onPlaylistChanged)
        self.infoStream.disconnected.connect(self.reconnectDialog)
        self.infoStream.timerUpdated.connect(self.playback.timerUpdated)
        self.infoStream.playlistRegistryUpdated.connect(self.playlistRegistryUpdated)

    def fetchLibrary(self):
        from txplayagui.client import getLibrary
        onHttpResponse(getLibrary(), self.onLibraryLoaded)

    def playlistDragEnterEvent(self, event):
        self._playlistDragDropHandle(event, isDropped=False)

    def playlistDropEvent(self, event):
        self._playlistDragDropHandle(event, isDropped=True)

    def _playlistDragDropHandle(self, event, isDropped):
        from txplayagui.client import moveTrack, libraryInsert

        mimeData = event.mimeData()

        # get row
        rowPosition = event.pos().y() - self.playlistTable.rowHeight(0)
        rowTarget = self.playlistTable.rowAt(rowPosition)

        if rowTarget == -1:
            # new row
            rowTarget = self.playlistModel.rowCount()

        if mimeData.hasUrls():
            urls = mimeData.urls()
            if len(urls) > 0:
                url = urls[0]
                if url.isLocalFile():
                    if not isDropped:
                        event.acceptProposedAction()
                        return

                    # file dropped
                    filepath = url.toLocalFile()

                    from txplayagui.client import insert
                    _ = insert(filepath, rowTarget)
                    return

        # no urls or not local file
        if not mimeData.hasText():
            return

        try:
            data = unwrapMime(mimeData)
        except ValueError:
            # invalid data passed
            return

        # check for proper flag
        source = data.get('source')
        if source not in ('playlist', 'library'):
            return

        if not isDropped:
            # drag entered
            event.acceptProposedAction()
            return

        if source == 'playlist':
            rowSource = data['row']
            moveTrack(rowSource, rowTarget)

        elif source == 'library':
            hashes = [item['hash'] for item in data['items']]
            libraryInsert(hashes, position=rowTarget)

    def onLibraryLoaded(self, response):
        try:
            data = json.loads(response.data)
            self.library.rescanFinished(data['library'])

            if 'msg' in data:
                print data['msg']
        except Exception, err:
            print 'Library load error:', repr(err)
Beispiel #45
0
class MyApp(QtWidgets.QMainWindow):
	mouseLeaveTimer=0

	def __init__(self):
		# Ui_MainWindow.__init__(self)
		#自己有__init__函数时,不会默认调用基类的__init__函数
		# 因为这里重写了__init__将基类的覆盖掉了,故需要主动调用之
		
		# QtWidgets.QMainWindow.__init__(self) 
		# super(MyApp,self).__init__()
		#上面两句的作用是相同的,下面这句是python3的新写法
		super().__init__()
		# 	Get the Screen size
		self.screenWidth=QDesktopWidget().availableGeometry().width()
		self.screenHeight=QDesktopWidget().availableGeometry().height()
		#初始化字体
		font=QFont('黑体')
		font.setPointSize(12)
		app.setFont(font)
		#  ColorSetting
		self.bgColor=QColor(66,66,77,88)

		#
		# self.setupUi(self)
		self.initUI()
		#用来控制半透明的bg面板自动消失
		self.timer=QTimer()
		self.timer.start(30)
		self.setGeometry(0,30,self.screenWidth,self.screenHeight//3)

		#Flagsq
		self.IsMouseHover=False
		self.MouseOver=False
		self.Locked=False
		self.Hidden=False
		self.isDrag=False
		self.isResize=False
		#变量初始化
		GLOBAL.WINDOWWIDTH=self.width()
		GLOBAL.WINDOWHEIGHT=self.height()
		self.bullets=[]
		self.dragPos=QPoint(22,22)
		self.savedName=''
		# self.screenBuffer=QBitmap(GLOBAL.WINDOWWIDTH,GLOBAL.WINDOWHEIGHT)
		# self.bufferPainter=QPainter(self.screenBuffer)
		# self.picture=QPicture()
		# 建立connection和slot的回调连接
		self.createConnections()
		# 连接到nodejs建立的服务器
		self.connect2Server()

	def initUI(self):
		#构建托盘
		self.trayIcon=QSystemTrayIcon(self)
		self.trayIcon.setIcon(QtGui.QIcon("tmpIcon.ico"))
		self.trayIcon.show()
		self.trayIcon.setToolTip('BulletGo')

		# 构建托盘菜单
		action_quit=QAction('退出',self)
		action_quit.triggered.connect(self.exitApp)
		action_switchLock=QAction('锁定/解锁(F6)',self)
		action_switchLock.triggered.connect(self.switchLock)
		action_showHide=QAction('显示/隐藏(F7)',self)
		action_showHide.triggered.connect(lambda:self.switchVisible(self))
		action_Settings=QAction('设置',self)
		action_Settings.triggered.connect(lambda:self.switchVisible(self.settingWindow))
		trayIconMenu=QtWidgets.QMenu(self)
		trayIconMenu.addAction(action_switchLock)
		trayIconMenu.addAction(action_showHide)
		trayIconMenu.addSeparator()
		trayIconMenu.addAction(action_Settings)
		trayIconMenu.addAction(action_quit)

		#设定快捷键
		QtWidgets.QShortcut(QtGui.QKeySequence(\
			QtCore.Qt.Key_F7),self,\
		(lambda:self.switchVisible(self.settingWindow)))
		QtWidgets.QShortcut(QtGui.QKeySequence(\
			QtCore.Qt.Key_F6),self,\
		(self.switchLock))

		self.trayIcon.setContextMenu(trayIconMenu)
		# 保障不按下鼠标也追踪mouseMove事件
		self.setMouseTracking(True)
		self.setMinimumSize(600,260)
		self.setWindowTitle("BulletGo")
		sizeGrip=QtWidgets.QSizeGrip(self)
		self.setWindowFlags(Qt.FramelessWindowHint\
			|Qt.WindowStaysOnTopHint|Qt.Window|\
			Qt.X11BypassWindowManagerHint)
		#Plan A
		self.setAttribute(Qt.WA_TranslucentBackground,True)
		#这一句是给Mac系统用的,防止它绘制(很黯淡的)背景
		self.setAutoFillBackground(False)
		QSizeGrip(self).setVisible(True)
		sizeGrip.setVisible(True)
		#Plan B  失败
		# palette=QPalette()
		# color=QColor(190, 230, 250)
		# color.setAlphaF(0.6)
		# palette.setBrush(self.backgroundRole(), color)
		# self.setPalette(palette)
		# self.setAutoFillBackground(True)
		# self.setBackgroundRole(QPalette.Window)

		#创建房间的Button和 输入框

		self.roomName=QPlainTextEdit()
		self.roomName.setPlaceholderText('请输入房间名')
		# self.roomName.resize(50,20)
		# self.roomName.move(0,0)
		# self.roomName.setBackgroundVisible(False)
		self.createBtn=QPushButton("创建/进入")
		self.hideBtn=QPushButton('隐藏本设置窗口')
		self.hideBtn.clicked.connect(self.joinRoom)
			# lambda:self.switchVisible(self.settingWindow))
		# self.createBtn.resize(50,20)
		# self.move(0,100)
		# self.d
		settingLayout=QVBoxLayout()
		hLayout=QHBoxLayout()
		settingLayout.addWidget(self.roomName)
		hLayout.addWidget(self.hideBtn)
		hLayout.addWidget(self.createBtn)
		self.settingWindow=QWidget()
		# self.hideBtn=setShortcut(QtGui.QKeySequence('Ctrl+B'))
		settingLayout.addLayout(hLayout)
		self.settingWindow.setLayout(settingLayout)
		# Qt.Tool的作用是  不在任务栏显示
		self.settingWindow.setWindowFlags(Qt.FramelessWindowHint|Qt.Tool\
			|Qt.X11BypassWindowManagerHint|Qt.Popup)
		self.roomName.show()
		self.createBtn.show()
		self.settingWindow.resize(160,26)
		self.settingWindow.show()


		# self.btnFire=QPushButton("Fire",self)
		# self.btnFire.resize(60,60)
		# self.btnFire.move(100,30)
		# self.btnFire.show()

		# self.btnLock=QPushButton("Lock",self)
		# self.btnLock.resize(40,40)
		# self.btnLock.move(self.screenWidth/2,30)
		# self.btnLock.setFlat(True)
		# self.btnLock.setIcon(QtGui.QIcon("tmpIcon.png"))
		# self.btnLock.show()



		# self.danmakuEditText=QPlainTextEdit(self)
		# self.danmakuEditText.resize(200,100)
		# self.danmakuEditText.move(100,100)
		# self.danmakuEditText.setBackgroundVisible(False)
		# self.danmakuEditText.show()

	def joinRoom(self):
		name=self.roomName.toPlainText()
		self.socketio.emit('join',name)

	def connect2Server(self):
		self.socketio=SocketIO('115.159.102.76/bulletgo_client',80,LoggingNamespace)
		self.registerEvents()
		# 开新线程监听服务器发来的消息,否则主线程被阻塞
		_thread.start_new_thread(self.socketio.wait,())

	def registerEvents(self):
		self.socketio.on('create_rsp',lambda rsp:self.handleIncomeBullet(rsp))
		self.socketio.on('bullet',lambda msg:self.handleIncomeBullet(msg))
		self.socketio.on('control_msg',lambda msg:print\
			('---control message---  : '+msg))
		self.socketio.on('sys_notification',lambda msg:print\
			('---system notification---  : '+msg))

	def handleIncomeBullet(self,bulletMsg):
		textsAndInfo=self.preProcessText(bulletMsg)
		if(len(textsAndInfo)>1):
			self.fireABullet(textsAndInfo[0],self.genQColorFromStr(textsAndInfo[1]))

	def createRoom_Nodejs(self,name):
		self.socketio.emit('create_room',name)

	def fireBtn(self):
		txt=self.danmakuEditText.toPlainText()
		tmpbullet=Bullet(txt,GLOBAL.ORANGE,random.randrange(9,16,2))
		self.bullets.append(tmpbullet)
		tmpbullet.prepare()
		# print(len(self.bullets))
		# testStr="line1\nline2\nline3"
		# textsAndInfo=self.preProcessText(testStr)
		# print(len(textsAndInfo))
		# print

	


	def fireABullet(self,txt,color=GLOBAL.ORANGE):
		tmpbullet=Bullet(txt,color,random.randrange(12,22,2))
		self.bullets.append(tmpbullet)
		tmpbullet.prepare()

	def createConnections(self):
		self.timer.timeout.connect(self.update)
		# self.btnFire.clicked.connect(self.fireBtn)
		self.createBtn.clicked.connect(\
			lambda:self.createRoom_Nodejs\
				(self.roomName.toPlainText()))
		# self.btnLock.clicked.connect(self.switchLock)
		# self.btnLock.clicked.connect(self.pullMsg)
		self.trayIcon.activated.connect(self.trayClick)

	def switchVisible(self,handle):
		if(handle.isHidden()):
			handle.activateWindow()
		handle.setHidden(not handle.isHidden())

	def trayClick(self,reason):
		#单击事件还没设计好
		# if(reason==QSystemTrayIcon.Trigger):
		# 	self.switchVisible(self)
		if(reason==QSystemTrayIcon.DoubleClick):
			self.switchVisible(self.settingWindow)


	def switchLock(self):
		self.Locked=not self.Locked

	'''这个神奇的用法, 在js中也可用'''
	'''博客搞好后, 这个要单独写个文章'''
	def genQColorFromStr(self,color):
		# print(color)
		return{
			'white':GLOBAL.WHITE,
			'green':GLOBAL.GREEN,
			'red':GLOBAL.RED,
			'pink':GLOBAL.PINK,
			'purple':GLOBAL.PURPLE,
			'darkblue':GLOBAL.DARKBLUE,
			'blue':GLOBAL.BLUE,
			'yellow':GLOBAL.YELLOW,
			'cyan':GLOBAL.CYAN,
			'orange':GLOBAL.ORANGE,
			'':GLOBAL.ORANGE
		}[color]

	def preProcessText(self,string):
		return string.split('`<')

	'''---[deprecated]---'''
	def realPullMsg(self):
		url='http://danmaku.applinzi.com/message.php'
		r =  requests.post(url,data=self.savedName)
		r.encoding='utf-8'
		#预处理收到的字符串  
		# print(r.text)
		# r.te
		textsAndInfo=self.preProcessText(r.text)
		i=0
		# print(textsAndInfo)
		# print(r.text)
		if(len(textsAndInfo)>1):
			while(i<len(textsAndInfo)-1):
				# print(len(textsAndInfo))
				# print('ddddd')
				# print(i)
				self.fireABullet(textsAndInfo[i],self.genQColorFromStr(textsAndInfo[i+1]))
				i+=2

	'''---[deprecated]---'''
	def pullMsg(self):
		_thread.start_new_thread(self.realPullMsg,())

	'''---[deprecated]---'''
	def createRoom(self):
		#编码问题实在天坑!!!
		self.savedName=self.roomName.toPlainText().encode('utf-8')#保存自己的房间号
		postData=self.roomName.toPlainText().encode('utf-8')
		r = requests.post('http://danmaku.applinzi.com/createroom.php',data=postData)
		r.encoding='utf-8'
		self.fireABullet(r.text)
		# print(r.encoding)
		if(len(r.text)==7):
			# 开始自动获取服务器上的消息内容
			self.pullTimer=QTimer()
			self.pullTimer.start(2000)
			self.pullTimer.timeout.connect(self.pullMsg)
		# print(r.content)
		# print(r.text)


	def closeEvent(self,e):
		e.accept()

	def mouseReleaseEvent(self,e):
		if(e.button()==Qt.LeftButton):
			self.isDrag=False
			self.isResize=False

	def mousePressEvent(self,e):
		if e.button()==Qt.LeftButton:
			self.LDown=True
			# self.dragPos=e.globalPos()-self.frameGeometry().topLeft()
			self.dragPos=e.pos()#效果同上,鼠标相对窗口左上角的位置
			
			if(GLOBAL.WINDOWWIDTH-e.pos().x()<16\
			and GLOBAL.WINDOWHEIGHT-e.pos().y()<16):
				self.topLeft=self.frameGeometry().topLeft()
				self.isResize=True
			else:
				if(not self.Locked):
					self.isDrag=True
		# else:
		# 	if e.button()==Qt.RightButton:
		# 		self.exitApp()
		e.accept()

	def mouseMoveEvent(self,e):
		if(GLOBAL.WINDOWWIDTH-e.pos().x()<16\
			and GLOBAL.WINDOWHEIGHT-e.pos().y()<16):
			#更改鼠标样式
			self.setCursor(Qt.SizeFDiagCursor)
		else:
			self.setCursor(Qt.ArrowCursor)
		#如果是Resize,改变窗口大小
		if(self.isResize):
			tmp=e.globalPos()-self.topLeft
			self.move(self.topLeft)
			self.resize(tmp.x(),tmp.y())
		if (self.isDrag):
			self.move(e.globalPos()-self.dragPos)
		e.accept();



	def enterEvent(self,e):
		self.MouseOver=True
		self.IsMouseHover=True
		return super(MyApp,self).enterEvent(e)

	def setMouseHoverFalse(self):
		# if(not self.MouseOver):
		self.IsMouseHover=self.MouseOver


	def leaveEvent(self,e):
		QTimer.singleShot(800,self.setMouseHoverFalse)
		self.MouseOver=False
		return super(MyApp,self).leaveEvent(e)

	def resizeEvent(self,e):
		GLOBAL.WINDOWWIDTH=self.width()
		GLOBAL.WINDOWHEIGHT=self.height()
		# self.screenBuffer=QBitmap(GLOBAL.WINDOWWIDTH,GLOBAL.WINDOWHEIGHT)
		# self.bufferPainter=QPainter(self.screenBuffer)
		# print('resized')
		# self.repaint()
		e.accept()

	def paintEvent(self,e):
		# Get the Painter
		painter=QPainter(self)
		font=QFont('黑体',GLOBAL.BULLETFONTSIZE,QFont.Bold)
		painter.setFont(font)
		#Draw a semi-Transparent rect whose size is the same with this window
		if(self.IsMouseHover and (not self.Locked)):
			painter.fillRect(0,0,GLOBAL.WINDOWWIDTH,GLOBAL.WINDOWHEIGHT\
				,self.bgColor)
		# painter.setBackground(QBrush(QColor(123,222,123,122)))
		#画所有bullet
		for b in self.bullets:
			b.draw(painter)
		for b in self.bullets:
			if(b.IsExpired):
				self.bullets.remove(b)
		# painter.drawPicture(0,0,self.picture)
		# painter.drawText(30,100,"Hello this is a PyQt5 App我也会说中文")
		return super(MyApp,self).paintEvent(e)

	def exitApp(self):
		self.trayIcon.hide()
		sys.exit()
Beispiel #46
0
class SystemTrayIcon(QMainWindow):
    def __init__(self, parent=None):
        super(SystemTrayIcon, self).__init__(parent)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.settings = QSettings()
        self.language = self.settings.value('Language') or ''
        self.temp_decimal_bool = self.settings.value('Decimal') or False
        # initialize the tray icon type in case of first run: issue#42
        self.tray_type = self.settings.value('TrayType') or 'icon&temp'
        cond = conditions.WeatherConditions()
        self.temporary_city_status = False
        self.conditions = cond.trans
        self.clouds = cond.clouds
        self.wind = cond.wind
        self.wind_dir = cond.wind_direction
        self.wind_codes = cond.wind_codes
        self.inerror = False
        self.tentatives = 0
        self.baseurl = 'http://api.openweathermap.org/data/2.5/weather?id='
        self.accurate_url = 'http://api.openweathermap.org/data/2.5/find?q='
        self.forecast_url = ('http://api.openweathermap.org/data/2.5/forecast/'
                             'daily?id=')
        self.day_forecast_url = ('http://api.openweathermap.org/data/2.5/'
                                 'forecast?id=')
        self.wIconUrl = 'http://openweathermap.org/img/w/'
        apikey = self.settings.value('APPID') or ''
        self.appid = '&APPID=' + apikey
        self.forecast_icon_url = self.wIconUrl
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.refresh)
        self.menu = QMenu()
        self.citiesMenu = QMenu(self.tr('Cities'))
        self.tempCityAction = QAction(self.tr('&Temporary city'), self)
        self.refreshAction = QAction(self.tr('&Update'), self)
        self.settingsAction = QAction(self.tr('&Settings'), self)
        self.aboutAction = QAction(self.tr('&About'), self)
        self.exitAction = QAction(self.tr('Exit'), self)
        self.exitAction.setIcon(QIcon(':/exit'))
        self.aboutAction.setIcon(QIcon(':/info'))
        self.refreshAction.setIcon(QIcon(':/refresh'))
        self.settingsAction.setIcon(QIcon(':/configure'))
        self.tempCityAction.setIcon(QIcon(':/tempcity'))
        self.citiesMenu.setIcon(QIcon(':/bookmarks'))
        self.menu.addAction(self.settingsAction)
        self.menu.addAction(self.refreshAction)
        self.menu.addMenu(self.citiesMenu)
        self.menu.addAction(self.tempCityAction)
        self.menu.addAction(self.aboutAction)
        self.menu.addAction(self.exitAction)
        self.settingsAction.triggered.connect(self.config)
        self.exitAction.triggered.connect(qApp.quit)
        self.refreshAction.triggered.connect(self.manual_refresh)
        self.aboutAction.triggered.connect(self.about)
        self.tempCityAction.triggered.connect(self.tempcity)
        self.systray = QSystemTrayIcon()
        self.systray.setContextMenu(self.menu)
        self.systray.activated.connect(self.activate)
        self.systray.setIcon(QIcon(':/noicon'))
        self.systray.setToolTip(self.tr('Searching weather data...'))
        self.notification = ''
        self.notification_temp = 0
        self.notifications_id = ''
        self.systray.show()
        # The dictionnary has to be intialized here. If there is an error
        # the program couldn't become functionnal if the dictionnary is
        # reinitialized in the weatherdata method
        self.weatherDataDico = {}
        # The traycolor has to be initialized here for the case when we cannot
        # reach the tray method (case: set the color at first time usage)
        self.traycolor = ''
        self.refresh()

    def icon_loading(self):
        self.gif_loading = QMovie(":/loading")
        self.gif_loading.frameChanged.connect(self.update_gif)
        self.gif_loading.start()

    def update_gif(self):
        gif_frame = self.gif_loading.currentPixmap()
        self.systray.setIcon(QIcon(gif_frame))

    def manual_refresh(self):
        self.tentatives = 0
        self.refresh()

    def cities_menu(self):
        # Don't add the temporary city in the list
        if self.temporary_city_status:
            return
        self.citiesMenu.clear()
        cities = self.settings.value('CityList') or []
        if type(cities) is str:
            cities = eval(cities)
        try:
            current_city = (self.settings.value('City') + '_' +
                        self.settings.value('Country') + '_' +
                        self.settings.value('ID'))
        except:
            # firsttime run,if clic cancel in setings without any city configured
            pass
        # Prevent duplicate entries
        try:
            city_toadd = cities.pop(cities.index(current_city))
        except:
            city_toadd = current_city
        finally:
            cities.insert(0, city_toadd)
        # If we delete all cities it results to a '__'
        if (cities is not None and cities != '' and cities != '[]' and
                cities != ['__']):
            if type(cities) is not list:
                # FIXME sometimes the list of cities is read as a string (?)
                # eval to a list
                cities = eval(cities)
            # Create the cities list menu
            for city in cities:
                action = QAction(city, self)
                action.triggered.connect(partial(self.changecity, city))
                self.citiesMenu.addAction(action)
        else:
            self.empty_cities_list()

    @pyqtSlot(str)
    def changecity(self, city):
        cities_list = self.settings.value('CityList')
        logging.debug('Cities' + str(cities_list))
        if cities_list is None:
            self.empty_cities_list()
        if type(cities_list) is not list:
            # FIXME some times is read as string (?)
            cities_list = eval(cities_list)
        prev_city = (self.settings.value('City') + '_' +
                     self.settings.value('Country') + '_' +
                     self.settings.value('ID'))
        citytoset = ''
        # Set the chosen city as the default
        for town in cities_list:
            if town == city:
                ind = cities_list.index(town)
                citytoset = cities_list[ind]
                citytosetlist = citytoset.split('_')
                self.settings.setValue('City', citytosetlist[0])
                self.settings.setValue('Country', citytosetlist[1])
                self.settings.setValue('ID', citytosetlist[2])
                if prev_city not in cities_list:
                    cities_list.append(prev_city)
                self.settings.setValue('CityList', cities_list)
                logging.debug(cities_list)
        self.refresh()

    def empty_cities_list(self):
        self.citiesMenu.addAction(self.tr('Empty list'))

    def refresh(self):
        self.inerror = False
        self.window_visible = False
        self.systray.setIcon(QIcon(':/noicon'))
        if hasattr(self, 'overviewcity'):
            # if visible, it has to ...remain visible
            # (try reason) Prevent C++ wrapper error
            try:
                if not self.overviewcity.isVisible():
                    # kills the reference to overviewcity
                    # in order to be refreshed
                    self.overviewcity.close()
                    del self.overviewcity
                else:
                    self.overviewcity.close()
                    self.window_visible = True
            except:
                pass
        self.systray.setToolTip(self.tr('Fetching weather data ...'))
        self.city = self.settings.value('City') or ''
        self.id_ = self.settings.value('ID') or None
        if self.id_ is None:
            # Clear the menu, no cities configured
            self.citiesMenu.clear()
            self.empty_cities_list()
            # Sometimes self.overviewcity is in namespace but deleted
            try:
                self.overviewcity.close()
            except:
                e = sys.exc_info()[0]
                logging.error('Error closing overviewcity: ' + str(e))
                pass
            self.timer.singleShot(2000, self.firsttime)
            self.id_ = ''
            self.systray.setToolTip(self.tr('No city configured'))
            return
        # A city has been found, create the cities menu now
        self.cities_menu()
        self.country = self.settings.value('Country') or ''
        self.unit = self.settings.value('Unit') or 'metric'
        self.suffix = ('&mode=xml&units=' + self.unit + self.appid)
        self.interval = int(self.settings.value('Interval') or 30)*60*1000
        self.timer.start(self.interval)
        self.update()

    def firsttime(self):
        self.temp = ''
        self.wIcon = QPixmap(':/noicon')
        self.systray.showMessage(
            'meteo-qt:\n', self.tr('No city has been configured yet.') +
            '\n' + self.tr('Right click on the icon and click on Settings.'))

    def update(self):
        if hasattr(self, 'downloadThread'):
            if self.downloadThread.isRunning():
                logging.debug('remaining thread...')
                return
        logging.debug('Update...')
        self.icon_loading()
        self.wIcon = QPixmap(':/noicon')
        self.downloadThread = Download(
            self.wIconUrl, self.baseurl, self.forecast_url,
            self.day_forecast_url, self.id_, self.suffix)
        self.downloadThread.wimage['PyQt_PyObject'].connect(self.makeicon)
        self.downloadThread.finished.connect(self.tray)
        self.downloadThread.xmlpage['PyQt_PyObject'].connect(self.weatherdata)
        self.downloadThread.forecast_rawpage.connect(self.forecast)
        self.downloadThread.day_forecast_rawpage.connect(self.dayforecast)
        self.downloadThread.uv_signal.connect(self.uv)
        self.downloadThread.error.connect(self.error)
        self.downloadThread.done.connect(self.done, Qt.QueuedConnection)
        self.downloadThread.start()

    def uv(self, value):
        self.uv_coord = value

    def forecast(self, data):
        self.forecast_data = data

    def dayforecast(self, data):
        self.dayforecast_data = data

    def instance_overviewcity(self):
        try:
            self.inerror = False
            if hasattr(self, 'overviewcity'):
                logging.debug('Deleting overviewcity instance...')
                del self.overviewcity
            self.overviewcity = overview.OverviewCity(
                self.weatherDataDico, self.wIcon, self.forecast_data,
                self.dayforecast_data, self.unit, self.forecast_icon_url,
                self.uv_coord, self)
            self.overviewcity.closed_status_dialogue.connect(self.remove_object)
        except:
            self.inerror = True
            e = sys.exc_info()[0]
            logging.error('Error: ' + str(e))
            logging.debug('Try to create the city overview...\nAttempts: ' +
                          str(self.tentatives))
            return 'error'

    def remove_object(self):
        del self.overviewcity

    def done(self, done):
        if done == 0:
            self.inerror = False
        elif done == 1:
            self.inerror = True
            logging.debug('Trying to retrieve data ...')
            self.timer.singleShot(10000, self.try_again)
            return
        if hasattr(self, 'updateicon'):
            # Keep a reference of the image to update the icon in overview
            self.wIcon = self.updateicon
        if hasattr(self, 'forecast_data'):
            if hasattr(self, 'overviewcity'):
                # Update also the overview dialog if open
                if self.overviewcity.isVisible():
                    # delete dialog to prevent memory leak
                    self.overviewcity.close()
                    self.instance_overviewcity()
                    self.overview()
            elif self.window_visible is True:
                self.instance_overviewcity()
                self.overview()
            else:
                self.inerror = True
                self.try_create_overview()
        else:
            self.try_again()

    def try_create_overview(self):
        logging.debug('Tries to create overview :' + str(self.tentatives))
        instance = self.instance_overviewcity()
        if instance == 'error':
            self.inerror = True
            self.refresh()
        else:
            self.tentatives = 0
            self.inerror = False
            self.tooltip_weather()

    def try_again(self):
        self.nodata_message()
        logging.debug('Attempts: ' + str(self.tentatives))
        self.tentatives += 1
        self.timer.singleShot(5000, self.refresh)

    def nodata_message(self):
        nodata = QCoreApplication.translate(
            "Tray icon", "Searching for weather data...",
            "Tooltip (when mouse over the icon")
        self.systray.setToolTip(nodata)
        self.notification = nodata

    def error(self, error):
        logging.error('Error:\n' + str(error))
        self.nodata_message()
        self.timer.start(self.interval)
        self.inerror = True

    def makeicon(self, data):
        image = QImage()
        image.loadFromData(data)
        self.wIcon = QPixmap(image)
        # Keep a reference of the image to update the icon in overview
        self.updateicon = self.wIcon

    def weatherdata(self, tree):
        if self.inerror:
            return
        self.tempFloat = tree[1].get('value')
        self.temp = ' ' + str(round(float(self.tempFloat))) + '°'
        self.temp_decimal = '{0:.1f}'.format(float(self.tempFloat)) + '°'
        self.meteo = tree[8].get('value')
        meteo_condition = tree[8].get('number')
        try:
            self.meteo = self.conditions[meteo_condition]
        except:
            logging.debug('Cannot find localisation string for'
                          'meteo_condition:' + str(meteo_condition))
            pass
        clouds = tree[5].get('name')
        clouds_percent = tree[5].get('value') + '%'
        try:
            clouds = self.clouds[clouds]
            clouds = self.conditions[clouds]
        except:
            logging.debug('Cannot find localisation string for clouds:' +
                          str(clouds))
            pass
        wind = tree[4][0].get('name').lower()
        try:
            wind = self.wind[wind]
            wind = self.conditions[wind]
        except:
            logging.debug('Cannot find localisation string for wind:' +
                          str(wind))
            pass
        wind_codes = tree[4][2].get('code')
        try:
            wind_codes = self.wind_codes[wind_codes]
        except:
            logging.debug('Cannot find localisation string for wind_codes:' +
                          str(wind_codes))
            pass
        wind_dir = tree[4][2].get('name')
        try:
            wind_dir = self.wind_dir[tree[4][2].get('code')]
        except:
            logging.debug('Cannot find localisation string for wind_dir:' +
                          str(wind_dir))
            pass
        self.city_weather_info = (self.city + ' ' + self.country + ' ' +
                                  self.temp_decimal + ' ' + self.meteo)
        self.tooltip_weather()
        self.notification = self.city_weather_info
        self.weatherDataDico['City'] = self.city
        self.weatherDataDico['Country'] = self.country
        self.weatherDataDico['Temp'] = self.tempFloat + '°'
        self.weatherDataDico['Meteo'] = self.meteo
        self.weatherDataDico['Humidity'] = (tree[2].get('value'),
                                            tree[2].get('unit'))
        self.weatherDataDico['Wind'] = (
            tree[4][0].get('value'), wind, str(int(float(tree[4][2].get('value')))),
            wind_codes, wind_dir)
        self.weatherDataDico['Clouds'] = (clouds_percent + ' ' + clouds)
        self.weatherDataDico['Pressure'] = (tree[3].get('value'),
                                            tree[3].get('unit'))
        self.weatherDataDico['Humidity'] = (tree[2].get('value'),
                                            tree[2].get('unit'))
        self.weatherDataDico['Sunrise'] = tree[0][2].get('rise')
        self.weatherDataDico['Sunset'] = tree[0][2].get('set')
        rain_value = tree[7].get('value')
        if rain_value == None:
            rain_value = ''
        self.weatherDataDico['Precipitation'] = (tree[7].get('mode'), rain_value)

    def tooltip_weather(self):
        self.systray.setToolTip(self.city_weather_info)

    def tray(self):
        temp_decimal = eval(self.settings.value('Decimal') or 'False')
        try:
            if temp_decimal:
                temp_tray = self.temp_decimal
            else:
                temp_tray = self.temp
        except:
            # First time launch
            return
        if self.inerror or not hasattr(self, 'temp'):
            logging.critical('Cannot paint icon!')
            if hasattr(self, 'overviewcity'):
                try:
                    # delete dialog to prevent memory leak
                    self.overviewcity.close()
                except:
                    pass
            return
        try:
            self.gif_loading.stop()
        except:
            # In first time run the gif is not animated
            pass
        logging.debug('Paint tray icon...')
        # Place empty.png here to initialize the icon
        # don't paint the T° over the old value
        icon = QPixmap(':/empty')
        self.traycolor = self.settings.value('TrayColor') or ''
        self.fontsize = self.settings.value('FontSize') or '18'
        self.tray_type = self.settings.value('TrayType') or 'icon&temp'
        pt = QPainter()
        pt.begin(icon)
        if self.tray_type != 'temp':
            pt.drawPixmap(0, -12, 64, 64, self.wIcon)
        pt.setFont(QFont('sans-sertif', int(self.fontsize)))
        pt.setPen(QColor(self.traycolor))
        if self.tray_type == 'icon&temp':
            pt.drawText(icon.rect(), Qt.AlignBottom | Qt.AlignCenter,
                        str(temp_tray))
        if self.tray_type == 'temp':
            pt.drawText(icon.rect(), Qt.AlignCenter, str(temp_tray))
        pt.end()
        if self.tray_type == 'icon':
            self.systray.setIcon(QIcon(self.wIcon))
        else:
            self.systray.setIcon(QIcon(icon))
        try:
            if not self.overviewcity.isVisible():
                notifier = self.settings.value('Notifications') or 'True'
                notifier = eval(notifier)
                if notifier:
                    temp = int(re.search('\d+', self.temp_decimal).group())
                    if temp != self.notification_temp or self.id_ != self.notifications_id:
                        self.notifications_id = self.id_
                        self.notification_temp = temp
                        self.systray.showMessage('meteo-qt', self.notification)
        except:
            logging.debug('OverviewCity has been deleted' +
                          'Download weather information again...')
            self.try_again()
            return
        self.restore_city()
        self.tentatives = 0
        self.tooltip_weather()
        logging.info('Actual weather status for: ' + self.notification)

    def restore_city(self):
        if self.temporary_city_status:
            logging.debug('Restore the default settings (city)' +
                          'Forget the temporary city...')
            for e in ('ID', self.id_2), ('City', self.city2), ('Country', self.country2):
                self.citydata(e)
            self.temporary_city_status = False

    def activate(self, reason):
        if reason == 3:
            if self.inerror or self.id_ is None or self.id_ == '':
                return
            try:
                if hasattr(self, 'overviewcity') and self.overviewcity.isVisible():
                    self.overviewcity.hide()
                else:
                    self.overviewcity.hide()
                    # If dialog closed by the "X"
                    self.done(0)
                    self.overview()
            except:
                self.done(0)
                self.overview()
        elif reason == 1:
            self.menu.popup(QCursor.pos())

    def overview(self):
        if self.inerror or len(self.weatherDataDico) == 0:
            return
        self.overviewcity.show()

    def config_save(self):
        logging.debug('Config saving...')
        city = self.settings.value('City'),
        id_ = self.settings.value('ID')
        country = self.settings.value('Country')
        unit = self.settings.value('Unit')
        traycolor = self.settings.value('TrayColor')
        tray_type = self.settings.value('TrayType')
        fontsize = self.settings.value('FontSize')
        language = self.settings.value('Language')
        decimal = self.settings.value('Decimal')
        self.appid = '&APPID=' + self.settings.value('APPID') or ''
        if language != self.language and language is not None:
            self.systray.showMessage('meteo-qt:',QCoreApplication.translate(
                    "System tray notification",
                    "The application has to be restarted to apply the language setting", ''))
            self.language = language
        # Check if update is needed
        if traycolor is None:
            traycolor = ''
        if (self.traycolor != traycolor or self.tray_type != tray_type or
                self.fontsize != fontsize or decimal != self.temp_decimal):
            self.tray()
        if (city[0] == self.city and
           id_ == self.id_ and
           country == self.country and
           unit == self.unit):
            return
        else:
            logging.debug('Apply changes from settings...')
            self.refresh()

    def config(self):
        dialog = settings.MeteoSettings(self.accurate_url, self.appid, self)
        dialog.applied_signal.connect(self.config_save)
        if dialog.exec_() == 1:
            self.config_save()
            logging.debug('Update Cities menu...')
            self.cities_menu()

    def tempcity(self):
        # Prevent to register a temporary city
        # This happen when a temporary city is still loading
        self.restore_city()
        dialog = searchcity.SearchCity(self.accurate_url, self.appid, self)
        self.id_2, self.city2, self.country2 = (self.settings.value('ID'),
                                                self.settings.value('City'),
                                                self.settings.value('Country'))
        dialog.id_signal[tuple].connect(self.citydata)
        dialog.city_signal[tuple].connect(self.citydata)
        dialog.country_signal[tuple].connect(self.citydata)
        if dialog.exec_():
            self.temporary_city_status = True
            self.systray.setToolTip(self.tr('Fetching weather data...'))
            self.refresh()

    def citydata(self, what):
        self.settings.setValue(what[0], what[1])
        logging.debug('write ' + str(what[0]) + ' ' + str(what[1]))

    def about(self):
        title = self.tr("""<b>meteo-qt</b> v{0}
            <br/>License: GPLv3
            <br/>Python {1} - Qt {2} - PyQt {3} on {4}""").format(
                __version__, platform.python_version(),
                QT_VERSION_STR, PYQT_VERSION_STR, platform.system())
        image = ':/logo'
        text = self.tr("""<p>Author: Dimitrios Glentadakis <a href="mailto:[email protected]">[email protected]</a>
                        <p>A simple application showing the weather status
                        information on the system tray.
                        <p>Website: <a href="https://github.com/dglent/meteo-qt">
                        https://github.com/dglent/meteo-qt</a>
                        <br/>Data source: <a href="http://openweathermap.org/">
                        OpenWeatherMap</a>.
                        <br/>This software uses icons from the
                        <a href="http://www.kde.org/">Oxygen Project</a>.
                        <p>To translate meteo-qt in your language or contribute to
                        current translations, you can use the
                        <a href="https://www.transifex.com/projects/p/meteo-qt/">
                        Transifex</a> platform.
                        <p>If you want to report a dysfunction or a suggestion,
                        feel free to open an issue in <a href="https://github.com/dglent/meteo-qt/issues">
                        github</a>.""")

        contributors = QCoreApplication.translate("About dialog", """
            Pavel Fric<br/>
            [cs] Czech translation
            <p>Jürgen <a href="mailto:[email protected]">[email protected]</a><br/>
            [de] German translation
            <p>Peter Mattern <a href="mailto:[email protected]">[email protected]</a><br/>
            [de] German translation, Project
            <p>Dimitrios Glentadakis <a href="mailto:[email protected]">[email protected]</a><br/>
            [el] Greek translation
            <p>Ozkar L. Garcell <a href="mailto:[email protected]">[email protected]</a><br/>
            [es] Spanish translation
            <p>Laurene Albrand <a href="mailto:[email protected]">[email protected]</a><br/>
            [fr] French translation
            <p>Rémi Verschelde <a href="mailto:[email protected]">[email protected]</a><br/>
            [fr] French translation, Project
            <p>Daniel Napora <a href="mailto:[email protected]">[email protected]</a><br/>
            Tomasz Przybył <a href="mailto:[email protected]">[email protected]</a><br/>
            [pl] Polish translation
            <p>Artem Vorotnikov <a href="mailto:[email protected]">[email protected]</a><br/>
            [ru] Russian translation
            <p>Atilla Öntaş <a href="mailto:[email protected]">[email protected]</a><br/>
            [tr] Turkish translation
            <p>Yuri Chornoivan <a href="mailto:[email protected]">[email protected]</a><br/>
            [uk] Ukrainian translation
            <p>You-Cheng Hsieh <a href="mailto:[email protected]">[email protected]</a><br/>
            [zh_TW] Chinese (Taiwan) translation
            <p>pmav99<br/>
            Project""", "List of contributors")

        dialog = about_dlg.AboutDialog(title, text, image, contributors, self)
        dialog.exec_()
Beispiel #47
0
class SystemTrayIcon(QMainWindow):
    def __init__(self, parent=None):
        super(SystemTrayIcon, self).__init__(parent)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.settings = QSettings()
        self.language = self.settings.value('Language') or ''
        self.temp_decimal_bool = self.settings.value('Decimal') or False
        # initialize the tray icon type in case of first run: issue#42
        self.tray_type = self.settings.value('TrayType') or 'icon&temp'
        cond = conditions.WeatherConditions()
        self.temporary_city_status = False
        self.conditions = cond.trans
        self.clouds = cond.clouds
        self.wind = cond.wind
        self.wind_dir = cond.wind_direction
        self.wind_codes = cond.wind_codes
        self.inerror = False
        self.tentatives = 0
        self.baseurl = 'http://api.openweathermap.org/data/2.5/weather?id='
        self.accurate_url = 'http://api.openweathermap.org/data/2.5/find?q='
        self.forecast_url = ('http://api.openweathermap.org/data/2.5/forecast/'
                             'daily?id=')
        self.day_forecast_url = ('http://api.openweathermap.org/data/2.5/'
                                 'forecast?id=')
        self.wIconUrl = 'http://openweathermap.org/img/w/'
        apikey = self.settings.value('APPID') or ''
        self.appid = '&APPID=' + apikey
        self.forecast_icon_url = self.wIconUrl
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.refresh)
        self.menu = QMenu()
        self.citiesMenu = QMenu(self.tr('Cities'))
        self.panelAction = QAction(
            QCoreApplication.translate("Tray context menu", "Toggle Panel",
                                       "Menu entry"), self)
        self.tempCityAction = QAction(self.tr('&Temporary city'), self)
        self.refreshAction = QAction(self.tr('&Update'), self)
        self.settingsAction = QAction(self.tr('&Settings'), self)
        self.aboutAction = QAction(self.tr('&About'), self)
        self.exitAction = QAction(self.tr('Exit'), self)
        self.panelAction.setIcon(QIcon(':/panel'))
        self.exitAction.setIcon(QIcon(':/exit'))
        self.aboutAction.setIcon(QIcon(':/info'))
        self.refreshAction.setIcon(QIcon(':/refresh'))
        self.settingsAction.setIcon(QIcon(':/configure'))
        self.tempCityAction.setIcon(QIcon(':/tempcity'))
        self.citiesMenu.setIcon(QIcon(':/bookmarks'))
        self.menu.addAction(self.panelAction)
        self.menu.addAction(self.settingsAction)
        self.menu.addAction(self.refreshAction)
        self.menu.addMenu(self.citiesMenu)
        self.menu.addAction(self.tempCityAction)
        self.menu.addAction(self.aboutAction)
        self.menu.addAction(self.exitAction)
        self.panelAction.triggered.connect(self.showpanel)
        self.settingsAction.triggered.connect(self.config)
        self.exitAction.triggered.connect(qApp.quit)
        self.refreshAction.triggered.connect(self.manual_refresh)
        self.aboutAction.triggered.connect(self.about)
        self.tempCityAction.triggered.connect(self.tempcity)
        self.systray = QSystemTrayIcon()
        self.systray.setContextMenu(self.menu)
        self.systray.activated.connect(self.activate)
        self.systray.setIcon(QIcon(':/noicon'))
        self.systray.setToolTip(self.tr('Searching weather data...'))
        self.notification = ''
        self.notification_temp = 0
        self.notifications_id = ''
        self.systray.show()
        # The dictionnary has to be intialized here. If there is an error
        # the program couldn't become functionnal if the dictionnary is
        # reinitialized in the weatherdata method
        self.weatherDataDico = {}
        # The traycolor has to be initialized here for the case when we cannot
        # reach the tray method (case: set the color at first time usage)
        self.traycolor = ''
        self.refresh()

    def icon_loading(self):
        self.gif_loading = QMovie(":/loading")
        self.gif_loading.frameChanged.connect(self.update_gif)
        self.gif_loading.start()

    def update_gif(self):
        gif_frame = self.gif_loading.currentPixmap()
        self.systray.setIcon(QIcon(gif_frame))

    def manual_refresh(self):
        self.tentatives = 0
        self.refresh()

    def cities_menu(self):
        # Don't add the temporary city in the list
        if self.temporary_city_status:
            return
        self.citiesMenu.clear()
        cities = self.settings.value('CityList') or []
        if type(cities) is str:
            cities = eval(cities)
        try:
            current_city = (self.settings.value('City') + '_' +
                            self.settings.value('Country') + '_' +
                            self.settings.value('ID'))
        except:
            # firsttime run,if clic cancel in setings without any city configured
            pass
        # Prevent duplicate entries
        try:
            city_toadd = cities.pop(cities.index(current_city))
        except:
            city_toadd = current_city
        finally:
            cities.insert(0, city_toadd)
        # If we delete all cities it results to a '__'
        if (cities is not None and cities != '' and cities != '[]'
                and cities != ['__']):
            if type(cities) is not list:
                # FIXME sometimes the list of cities is read as a string (?)
                # eval to a list
                cities = eval(cities)
            # Create the cities list menu
            for city in cities:
                action = QAction(city, self)
                action.triggered.connect(partial(self.changecity, city))
                self.citiesMenu.addAction(action)
        else:
            self.empty_cities_list()

    @pyqtSlot(str)
    def changecity(self, city):
        cities_list = self.settings.value('CityList')
        logging.debug('Cities' + str(cities_list))
        if cities_list is None:
            self.empty_cities_list()
        if type(cities_list) is not list:
            # FIXME some times is read as string (?)
            cities_list = eval(cities_list)
        prev_city = (self.settings.value('City') + '_' +
                     self.settings.value('Country') + '_' +
                     self.settings.value('ID'))
        citytoset = ''
        # Set the chosen city as the default
        for town in cities_list:
            if town == city:
                ind = cities_list.index(town)
                citytoset = cities_list[ind]
                citytosetlist = citytoset.split('_')
                self.settings.setValue('City', citytosetlist[0])
                self.settings.setValue('Country', citytosetlist[1])
                self.settings.setValue('ID', citytosetlist[2])
                if prev_city not in cities_list:
                    cities_list.append(prev_city)
                self.settings.setValue('CityList', cities_list)
                logging.debug(cities_list)
        self.refresh()

    def empty_cities_list(self):
        self.citiesMenu.addAction(self.tr('Empty list'))

    def refresh(self):
        self.inerror = False
        self.window_visible = False
        self.systray.setIcon(QIcon(':/noicon'))
        if hasattr(self, 'overviewcity'):
            # if visible, it has to ...remain visible
            # (try reason) Prevent C++ wrapper error
            try:
                if not self.overviewcity.isVisible():
                    # kills the reference to overviewcity
                    # in order to be refreshed
                    self.overviewcity.close()
                    del self.overviewcity
                else:
                    self.overviewcity.close()
                    self.window_visible = True
            except:
                pass
        self.systray.setToolTip(self.tr('Fetching weather data ...'))
        self.city = self.settings.value('City') or ''
        self.id_ = self.settings.value('ID') or None
        if self.id_ is None:
            # Clear the menu, no cities configured
            self.citiesMenu.clear()
            self.empty_cities_list()
            # Sometimes self.overviewcity is in namespace but deleted
            try:
                self.overviewcity.close()
            except:
                e = sys.exc_info()[0]
                logging.error('Error closing overviewcity: ' + str(e))
                pass
            self.timer.singleShot(2000, self.firsttime)
            self.id_ = ''
            self.systray.setToolTip(self.tr('No city configured'))
            return
        # A city has been found, create the cities menu now
        self.cities_menu()
        self.country = self.settings.value('Country') or ''
        self.unit = self.settings.value('Unit') or 'metric'
        self.suffix = ('&mode=xml&units=' + self.unit + self.appid)
        self.interval = int(self.settings.value('Interval') or 30) * 60 * 1000
        self.timer.start(self.interval)
        self.update()

    def firsttime(self):
        self.temp = ''
        self.wIcon = QPixmap(':/noicon')
        self.systray.showMessage(
            'meteo-qt:\n',
            self.tr('No city has been configured yet.') + '\n' +
            self.tr('Right click on the icon and click on Settings.'))

    def update(self):
        if hasattr(self, 'downloadThread'):
            if self.downloadThread.isRunning():
                logging.debug('remaining thread...')
                return
        logging.debug('Update...')
        self.icon_loading()
        self.wIcon = QPixmap(':/noicon')
        self.downloadThread = Download(self.wIconUrl, self.baseurl,
                                       self.forecast_url,
                                       self.day_forecast_url, self.id_,
                                       self.suffix)
        self.downloadThread.wimage['PyQt_PyObject'].connect(self.makeicon)
        self.downloadThread.finished.connect(self.tray)
        self.downloadThread.xmlpage['PyQt_PyObject'].connect(self.weatherdata)
        self.downloadThread.forecast_rawpage.connect(self.forecast)
        self.downloadThread.day_forecast_rawpage.connect(self.dayforecast)
        self.downloadThread.uv_signal.connect(self.uv)
        self.downloadThread.error.connect(self.error)
        self.downloadThread.done.connect(self.done, Qt.QueuedConnection)
        self.downloadThread.start()

    def uv(self, value):
        self.uv_coord = value

    def forecast(self, data):
        self.forecast_data = data

    def dayforecast(self, data):
        if type(data) == dict:
            self.json_data_bool = True
        else:
            self.json_data_bool = False
        self.dayforecast_data = data

    def instance_overviewcity(self):
        try:
            self.inerror = False
            if hasattr(self, 'overviewcity'):
                logging.debug('Deleting overviewcity instance...')
                del self.overviewcity
            self.overviewcity = overview.OverviewCity(
                self.weatherDataDico, self.wIcon, self.forecast_data,
                self.dayforecast_data, self.json_data_bool, self.unit,
                self.forecast_icon_url, self.uv_coord, self)
            self.overviewcity.closed_status_dialogue.connect(
                self.remove_object)
        except:
            self.inerror = True
            e = sys.exc_info()[0]
            logging.error('Error: ' + str(e))
            logging.debug('Try to create the city overview...\nAttempts: ' +
                          str(self.tentatives))
            return 'error'

    def remove_object(self):
        del self.overviewcity

    def done(self, done):
        if done == 0:
            self.inerror = False
        elif done == 1:
            self.inerror = True
            logging.debug('Trying to retrieve data ...')
            self.timer.singleShot(10000, self.try_again)
            return
        if hasattr(self, 'updateicon'):
            # Keep a reference of the image to update the icon in overview
            self.wIcon = self.updateicon
        if hasattr(self, 'forecast_data'):
            if hasattr(self, 'overviewcity'):
                try:
                    # Update also the overview dialog if open
                    if self.overviewcity.isVisible():
                        # delete dialog to prevent memory leak
                        self.overviewcity.close()
                        self.instance_overviewcity()
                        self.overview()
                except:
                    # if the dialogue has been closed by the 'X' button
                    # remove the delelted window object from memory
                    # RuntimeError: wrapped C/C++ object of type OverviewCity has been deleted
                    self.remove_object()
                    self.instance_overviewcity()
            elif self.window_visible is True:
                self.instance_overviewcity()
                self.overview()
            else:
                self.inerror = True
                self.try_create_overview()
        else:
            self.try_again()

    def try_create_overview(self):
        logging.debug('Tries to create overview :' + str(self.tentatives))
        instance = self.instance_overviewcity()
        if instance == 'error':
            self.inerror = True
            self.refresh()
        else:
            self.tentatives = 0
            self.inerror = False
            self.tooltip_weather()

    def try_again(self):
        self.nodata_message()
        logging.debug('Attempts: ' + str(self.tentatives))
        self.tentatives += 1
        self.timer.singleShot(5000, self.refresh)

    def nodata_message(self):
        nodata = QCoreApplication.translate(
            "Tray icon", "Searching for weather data...",
            "Tooltip (when mouse over the icon")
        self.systray.setToolTip(nodata)
        self.notification = nodata

    def error(self, error):
        logging.error('Error:\n' + str(error))
        self.nodata_message()
        self.timer.start(self.interval)
        self.inerror = True

    def makeicon(self, data):
        image = QImage()
        image.loadFromData(data)
        self.wIcon = QPixmap(image)
        # Keep a reference of the image to update the icon in overview
        self.updateicon = self.wIcon

    def weatherdata(self, tree):
        if self.inerror:
            return
        self.tempFloat = tree[1].get('value')
        self.temp = ' ' + str(round(float(self.tempFloat))) + '°'
        self.temp_decimal = '{0:.1f}'.format(float(self.tempFloat)) + '°'
        self.meteo = tree[8].get('value')
        meteo_condition = tree[8].get('number')
        try:
            self.meteo = self.conditions[meteo_condition]
        except:
            logging.debug('Cannot find localisation string for'
                          'meteo_condition:' + str(meteo_condition))
            pass
        clouds = tree[5].get('name')
        clouds_percent = tree[5].get('value') + '%'
        try:
            clouds = self.clouds[clouds]
            clouds = self.conditions[clouds]
        except:
            logging.debug('Cannot find localisation string for clouds:' +
                          str(clouds))
            pass
        wind = tree[4][0].get('name').lower()
        try:
            wind = self.wind[wind]
            wind = self.conditions[wind]
        except:
            logging.debug('Cannot find localisation string for wind:' +
                          str(wind))
            pass
        try:
            wind_codes = tree[4][2].get('code')
            wind_dir_value = tree[4][2].get('value')
            wind_dir = tree[4][2].get('name')
        except:
            wind_codes = tree[4][1].get('code')
            wind_dir_value = tree[4][1].get('value')
            wind_dir = tree[4][1].get('name')
        try:
            wind_codes = self.wind_codes[wind_codes]
        except:
            logging.debug('Cannot find localisation string for wind_codes:' +
                          str(wind_codes))
            pass
        try:
            wind_dir = self.wind_dir[tree[4][2].get('code')]
        except:
            logging.debug('Cannot find localisation string for wind_dir:' +
                          str(wind_dir))
            pass
        self.city_weather_info = (self.city + ' ' + self.country + ' ' +
                                  self.temp_decimal + ' ' + self.meteo)
        self.tooltip_weather()
        self.notification = self.city_weather_info
        self.weatherDataDico['City'] = self.city
        self.weatherDataDico['Country'] = self.country
        self.weatherDataDico['Temp'] = self.tempFloat + '°'
        self.weatherDataDico['Meteo'] = self.meteo
        self.weatherDataDico['Humidity'] = (tree[2].get('value'),
                                            tree[2].get('unit'))
        self.weatherDataDico['Wind'] = (tree[4][0].get('value'), wind,
                                        str(int(float(wind_dir_value))),
                                        wind_codes, wind_dir)
        self.weatherDataDico['Clouds'] = (clouds_percent + ' ' + clouds)
        self.weatherDataDico['Pressure'] = (tree[3].get('value'),
                                            tree[3].get('unit'))
        self.weatherDataDico['Humidity'] = (tree[2].get('value'),
                                            tree[2].get('unit'))
        self.weatherDataDico['Sunrise'] = tree[0][2].get('rise')
        self.weatherDataDico['Sunset'] = tree[0][2].get('set')
        rain_value = tree[7].get('value')
        if rain_value == None:
            rain_value = ''
        self.weatherDataDico['Precipitation'] = (tree[7].get('mode'),
                                                 rain_value)

    def tooltip_weather(self):
        self.systray.setToolTip(self.city_weather_info)

    def tray(self):
        temp_decimal = eval(self.settings.value('Decimal') or 'False')
        try:
            if temp_decimal:
                temp_tray = self.temp_decimal
            else:
                temp_tray = self.temp
        except:
            # First time launch
            return
        if self.inerror or not hasattr(self, 'temp'):
            logging.critical('Cannot paint icon!')
            if hasattr(self, 'overviewcity'):
                try:
                    # delete dialog to prevent memory leak
                    self.overviewcity.close()
                except:
                    pass
            return
        try:
            self.gif_loading.stop()
        except:
            # In first time run the gif is not animated
            pass
        logging.debug('Paint tray icon...')
        # Place empty.png here to initialize the icon
        # don't paint the T° over the old value
        icon = QPixmap(':/empty')
        self.traycolor = self.settings.value('TrayColor') or ''
        self.fontsize = self.settings.value('FontSize') or '18'
        self.tray_type = self.settings.value('TrayType') or 'icon&temp'
        pt = QPainter()
        pt.begin(icon)
        if self.tray_type != 'temp':
            pt.drawPixmap(0, -12, 64, 64, self.wIcon)
        pt.setFont(QFont('sans-sertif', int(self.fontsize)))
        pt.setPen(QColor(self.traycolor))
        if self.tray_type == 'icon&temp':
            pt.drawText(icon.rect(), Qt.AlignBottom | Qt.AlignCenter,
                        str(temp_tray))
        if self.tray_type == 'temp':
            pt.drawText(icon.rect(), Qt.AlignCenter, str(temp_tray))
        pt.end()
        if self.tray_type == 'icon':
            self.systray.setIcon(QIcon(self.wIcon))
        else:
            self.systray.setIcon(QIcon(icon))
        try:
            if not self.overviewcity.isVisible():
                notifier = self.settings.value('Notifications') or 'True'
                notifier = eval(notifier)
                if notifier:
                    temp = int(re.search('\d+', self.temp_decimal).group())
                    if temp != self.notification_temp or self.id_ != self.notifications_id:
                        self.notifications_id = self.id_
                        self.notification_temp = temp
                        self.systray.showMessage('meteo-qt', self.notification)
        except:
            logging.debug('OverviewCity has been deleted' +
                          'Download weather information again...')
            self.try_again()
            return
        self.restore_city()
        self.tentatives = 0
        self.tooltip_weather()
        logging.info('Actual weather status for: ' + self.notification)

    def restore_city(self):
        if self.temporary_city_status:
            logging.debug('Restore the default settings (city)' +
                          'Forget the temporary city...')
            for e in ('ID', self.id_2), ('City', self.city2), ('Country',
                                                               self.country2):
                self.citydata(e)
            self.temporary_city_status = False

    def showpanel(self):
        self.activate(3)

    def activate(self, reason):
        if reason == 3:
            if self.inerror or self.id_ is None or self.id_ == '':
                return
            try:
                if hasattr(self,
                           'overviewcity') and self.overviewcity.isVisible():
                    self.overviewcity.hide()
                else:
                    self.overviewcity.hide()
                    # If dialog closed by the "X"
                    self.done(0)
                    self.overview()
            except:
                self.done(0)
                self.overview()
        elif reason == 1:
            self.menu.popup(QCursor.pos())

    def overview(self):
        if self.inerror or len(self.weatherDataDico) == 0:
            return
        self.overviewcity.show()

    def config_save(self):
        logging.debug('Config saving...')
        city = self.settings.value('City'),
        id_ = self.settings.value('ID')
        country = self.settings.value('Country')
        unit = self.settings.value('Unit')
        traycolor = self.settings.value('TrayColor')
        tray_type = self.settings.value('TrayType')
        fontsize = self.settings.value('FontSize')
        language = self.settings.value('Language')
        decimal = self.settings.value('Decimal')
        self.appid = '&APPID=' + self.settings.value('APPID') or ''
        if language != self.language and language is not None:
            self.systray.showMessage(
                'meteo-qt:',
                QCoreApplication.translate(
                    "System tray notification",
                    "The application has to be restarted to apply the language setting",
                    ''))
            self.language = language
        # Check if update is needed
        if traycolor is None:
            traycolor = ''
        if (self.traycolor != traycolor or self.tray_type != tray_type
                or self.fontsize != fontsize or decimal != self.temp_decimal):
            self.tray()
        if (city[0] == self.city and id_ == self.id_
                and country == self.country and unit == self.unit):
            return
        else:
            logging.debug('Apply changes from settings...')
            self.refresh()

    def config(self):
        dialog = settings.MeteoSettings(self.accurate_url, self.appid, self)
        dialog.applied_signal.connect(self.config_save)
        if dialog.exec_() == 1:
            self.config_save()
            logging.debug('Update Cities menu...')
            self.cities_menu()

    def tempcity(self):
        # Prevent to register a temporary city
        # This happen when a temporary city is still loading
        self.restore_city()
        dialog = searchcity.SearchCity(self.accurate_url, self.appid, self)
        self.id_2, self.city2, self.country2 = (self.settings.value('ID'),
                                                self.settings.value('City'),
                                                self.settings.value('Country'))
        dialog.id_signal[tuple].connect(self.citydata)
        dialog.city_signal[tuple].connect(self.citydata)
        dialog.country_signal[tuple].connect(self.citydata)
        if dialog.exec_():
            self.temporary_city_status = True
            self.systray.setToolTip(self.tr('Fetching weather data...'))
            self.refresh()

    def citydata(self, what):
        self.settings.setValue(what[0], what[1])
        logging.debug('write ' + str(what[0]) + ' ' + str(what[1]))

    def about(self):
        title = self.tr("""<b>meteo-qt</b> v{0}
            <br/>License: GPLv3
            <br/>Python {1} - Qt {2} - PyQt {3} on {4}""").format(
            __version__, platform.python_version(), QT_VERSION_STR,
            PYQT_VERSION_STR, platform.system())
        image = ':/logo'
        text = self.tr(
            """<p>Author: Dimitrios Glentadakis <a href="mailto:[email protected]">[email protected]</a>
                        <p>A simple application showing the weather status
                        information on the system tray.
                        <p>Website: <a href="https://github.com/dglent/meteo-qt">
                        https://github.com/dglent/meteo-qt</a>
                        <br/>Data source: <a href="http://openweathermap.org/">
                        OpenWeatherMap</a>.
                        <br/>This software uses icons from the
                        <a href="http://www.kde.org/">Oxygen Project</a>.
                        <p>To translate meteo-qt in your language or contribute to
                        current translations, you can use the
                        <a href="https://www.transifex.com/projects/p/meteo-qt/">
                        Transifex</a> platform.
                        <p>If you want to report a dysfunction or a suggestion,
                        feel free to open an issue in <a href="https://github.com/dglent/meteo-qt/issues">
                        github</a>.""")

        contributors = QCoreApplication.translate(
            "About dialog", """
            Pavel Fric<br/>
            [cs] Czech translation
            <p>Jürgen <a href="mailto:[email protected]">[email protected]</a><br/>
            [de] German translation
            <p>Peter Mattern <a href="mailto:[email protected]">[email protected]</a><br/>
            [de] German translation, Project
            <p>Dimitrios Glentadakis <a href="mailto:[email protected]">[email protected]</a><br/>
            [el] Greek translation
            <p> juancarlospaco <a href="mailto:[email protected]">[email protected]</a><br/>
            [es] Spanish translation, Project
            <p>Ozkar L. Garcell <a href="mailto:[email protected]">[email protected]</a><br/>
            [es] Spanish translation
            <p>Laurene Albrand <a href="mailto:[email protected]">[email protected]</a><br/>
            [fr] French translation
            <p>Rémi Verschelde <a href="mailto:[email protected]">[email protected]</a><br/>
            [fr] French translation, Project
            <p>Daniel Napora <a href="mailto:[email protected]">[email protected]</a><br/>
            Tomasz Przybył <a href="mailto:[email protected]">[email protected]</a><br/>
            [pl] Polish translation
            <p>Artem Vorotnikov <a href="mailto:[email protected]">[email protected]</a><br/>
            [ru] Russian translation
            <p>Atilla Öntaş <a href="mailto:[email protected]">[email protected]</a><br/>
            [tr] Turkish translation
            <p>Yuri Chornoivan <a href="mailto:[email protected]">[email protected]</a><br/>
            [uk] Ukrainian translation
            <p>You-Cheng Hsieh <a href="mailto:[email protected]">[email protected]</a><br/>
            [zh_TW] Chinese (Taiwan) translation
            <p>pmav99<br/>
            Project""", "List of contributors")

        dialog = about_dlg.AboutDialog(title, text, image, contributors, self)
        dialog.exec_()
Beispiel #48
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

    def createUI(self):
        self.home = os.getenv("HOME")
        # vlc player init
        self.instance = vlc.Instance('-q')
        #		self.instance = vlc.Instance('-q --network-caching=1000')
        self.mediaplayer = self.instance.media_player_new()
        #		self.mediaplayer.set_xwindow(self.winId())
        # Main window settings
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.videoFrame = QFrame(self)
        self.videoFrame.setObjectName("videoFrame")
        self.gridLayout.addWidget(self.videoFrame, 0, 0, 1, 1)

        self.mediaplayer.set_xwindow(self.winId())
        #		self.mediaplayer.set_xwindow(self.videoFrame.winId())

        self.setWindowIcon(
            QIcon(scriptDir + os.path.sep + 'pics' + os.path.sep + 'logo.png'))
        self.resize(cfg.value('Width', 456, type=int),
                    cfg.value('Height', 256, type=int))
        self.setGeometry(
            cfg.value('Left', 456, type=int) + WINDOW_DECORATION_WIDTH_BORDER,
            cfg.value('Top', 256, type=int) + WINDOW_DECORATION_HEIGHT_TITLE,
            cfg.value('Width', 456, type=int),
            cfg.value('Height', 256, type=int))
        pal = self.palette()
        pal.setColor(self.backgroundRole(), Qt.blue)
        self.setPalette(pal)
        #    self.setAutoFillBackground(True)
        self.currentCursor = self.cursor()
        # Save status audio mute
        self.AudioMuteOnStart = self.mediaplayer.audio_get_mute()
        self.AudioVolumeOnStart = self.mediaplayer.audio_get_volume()
        self.Volume = cfg.value('Volume', 80, type=int)

        # Registered DBUS service
        DBUSName = 'tv.ok'
        DBUSConn = QDBusConnection.connectToBus(QDBusConnection.SessionBus,
                                                DBUSName)
        DBUSConn.registerService(DBUSName)
        DBUSConn.registerObject("/", self, QDBusConnection.ExportAllContents)
        # Timer 1 second init. Once second call function t1secEvent
        self.t1sec = QTimer(self)
        self.t1sec.timeout.connect(self.t1secEvent)
        self.t1sec.start(1000)
        # Select channel saved previous run
        self.chNum = cfg.value('Channel', 1, type=int)
        self.chPrev = self.chNum + 1
        self.chChange()

        self.trayIcon = QSystemTrayIcon()
        self.trayIcon.setToolTip('TVOK Python')
        self.trayIcon.activated.connect(self.ToggleMute)
        self.swapIcon()

        self.selectChannel = ''
        self.tChSelect = QTimer(self)
        self.tChSelect.timeout.connect(self.tChSelectTimeout)

#		self.label = QLabel(self)
#		self.label.setText("<font color='yellow'>TEST</font>")

    def osdView(self, mess):
        # Send OSD
        # If DBUS daemon org.kochkin.okindd is running
        dbus_interface = QDBusInterface("org.kochkin.okindd", "/Text")
        if dbus_interface.isValid():
            dbus_interface.call('printText', 'Tvok', mess, 5000)

    @pyqtSlot(int)
    def channelNum(self, digit):
        if (digit >= 0) and (digit <= 9):
            self.selectChannel = self.selectChannel + str(digit)
            if int(self.selectChannel) > len(pl):
                self.selectChannel = self.selectChannel[:-1]
            if int(self.selectChannel) < 1:
                self.selectChannel = self.selectChannel[:-1]
            self.osdView(self.selectChannel + ': ' +
                         pl[int(self.selectChannel) - 1][0])
            self.tChSelect.start(2000)

    @pyqtSlot()
    def tChSelectTimeout(self):
        self.tChSelect.stop()
        self.chNum = int(self.selectChannel)
        self.selectChannel = ''
        self.chChange()

    def swapIcon(self):
        picture = scriptDir + os.path.sep + 'pics' + os.path.sep + 'din-on.png'
        if not self.mute():
            picture = scriptDir + os.path.sep + 'pics' + os.path.sep + 'din-off.png'
        self.trayIcon.setIcon(QIcon(picture))
        self.trayIcon.show()

    @pyqtSlot(result=bool)
    def mute(self):
        return self.mediaplayer.audio_get_mute()

    @pyqtSlot(result=int)
    def GetChannelNum(self):
        return self.chNum

    @pyqtSlot(result=str)
    def GetChannel(self):
        return pl[self.chNum - 1][0]

    @pyqtSlot(result=int)
    def GetVolume(self):
        return self.mediaplayer.audio_get_volume()

    @pyqtSlot()
    def VolumeIncrease(self):
        self.mediaplayer.audio_set_volume(self.mediaplayer.audio_get_volume() +
                                          VOLUME_CHANGE)
        cfg.setValue('Volume', self.mediaplayer.audio_get_volume())

    @pyqtSlot()
    def VolumeDecrease(self):
        self.mediaplayer.audio_set_volume(self.mediaplayer.audio_get_volume() -
                                          VOLUME_CHANGE)
        cfg.setValue('Volume', self.mediaplayer.audio_get_volume())

# Once second

    def t1secEvent(self):
        if self.isFullScreen(): self.setCursor(Qt.BlankCursor)

    @pyqtSlot()
    def ToggleMute(self):
        self.mediaplayer.audio_set_mute(not self.mediaplayer.audio_get_mute())
        self.swapIcon()

    @pyqtSlot()
    def ChannelNext(self):
        self.chNum += 1
        self.chChange()

    @pyqtSlot()
    def ChannelPrev(self):
        self.chNum -= 1
        self.chChange()

# On mouse wheel change

    def wheelEvent(self, event):
        if event.angleDelta().y() > 0: self.ChannelNext()
        if event.angleDelta().y() < 0: self.ChannelPrev()

    @pyqtSlot()
    def ChannelRestart(self):
        self.chChange()

    # Stop current channel and start chNum channel
    def chChange(self):
        if self.chNum != self.chPrev:
            if self.chNum > len(pl): self.chNum = 1
            if self.chNum < 1: self.chNum = len(pl)
            self.setWindowTitle(str(self.chNum) + '. ' + pl[self.chNum - 1][0])
            self.osdView(str(self.chNum) + ': ' + pl[self.chNum - 1][0])
            self.mediaplayer.stop()

            self.media = self.instance.media_new(pl[self.chNum - 1][1])
            self.mediaplayer.set_media(self.media)
            playerError = self.mediaplayer.play()
            if playerError != 0: sys.exit()
            cfg.setValue('Channel', self.chNum)
            cfg.setValue('Volume', self.mediaplayer.audio_get_volume())
            self.chPrev = self.chNum

# If double click mouse - toggle full screen

    def mouseDoubleClickEvent(self, event):
        self.ToggleFullScreen()

    @pyqtSlot()
    def ToggleFullScreen(self):
        if self.isFullScreen():
            self.showNormal()
            self.setCursor(self.currentCursor)
        else:
            self.showFullScreen()
            self.setCursor(Qt.BlankCursor)


# Mouse pressed for context menu

    def contextMenuEvent(self, event):
        menu = QMenu(self)
        # Fill channels
        index = 0
        for chs in pl:
            action = menu.addAction(chs[0])
            if index == self.chNum - 1:
                menu.setActiveAction(action)
                print(index)
            index += 1

        menu.addSeparator()
        quitAction = menu.addAction(self.tr("Quit"))
        action = menu.exec_(self.mapToGlobal(event.pos()))
        if action:
            value_index = 1
            for value in pl:
                if value[0] == action.iconText():
                    if self.chNum != value_index:
                        self.chNum = value_index
                        self.chChange()
                    break
                else:
                    value_index += 1
            if action == quitAction:
                self.close()

    def closeEvent(self, event):
        self.mediaplayer.stop()
        if not self.isFullScreen():
            cfg.setValue('Left', self.x())
            cfg.setValue('Top', self.y())
            cfg.setValue('Width', self.width())
            cfg.setValue('Height', self.height())
        cfg.setValue('Volume', self.mediaplayer.audio_get_volume())
        cfg.sync()
        self.mediaplayer.audio_set_mute(self.AudioMuteOnStart)
        self.mediaplayer.audio_set_volume(self.AudioVolumeOnStart)
        #		self.trayIcon.close()
        exit()
Beispiel #49
0
class MainApp(QMainWindow):
    tray_icon = None
    active = False

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

        # Init notification
        notify2.init('webcam-manager')
        
        # Init tray icon
        self.tray_icon = QSystemTrayIcon(QIcon.fromTheme('camera-web'), parent)        
        self.initTrayIcon()
        self.tray_icon.show()        
        
    def initTrayIcon(self):
        # Menu actions
        toogle_action = QAction(self.tr('&Toogle'), self)
        toogle_action.triggered.connect(self.onToogle)
        about_action = QAction(self.tr('&About'), self)
        about_action.setIcon(QIcon.fromTheme("help-about"))
        about_action.triggered.connect(self.onAbout)
        quit_action = QAction(self.tr('&Exit'), self)
        quit_action.setIcon(QIcon.fromTheme("application-exit"))
        quit_action.triggered.connect(self.onQuit)

        tray_menu = QMenu()
        tray_menu.addAction(toogle_action)
        tray_menu.addSeparator()
        tray_menu.addAction(about_action)
        tray_menu.addAction(quit_action)
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.activated.connect(self.onToogle)   
        output = int(self.execCommand('lsmod | grep uvcvideo | wc -l').split()[0])
        if output > 0:
            self.updateTrayIcon(True)
        else:
            self.updateTrayIcon(False)        

    def onToogle(self, widget):
        if self.active:
            self.disable()
        else:
            self.enable()

    def onQuit(self, widget):
        qApp.quit()

    def onAbout(self, widget):
        dialog = QDialog(self)
        aboutText = self.tr("""<p>A simple applet for enable/disable webcams.</p>
            <p>Website: <a href="https://github.com/abbarrasa/openbox">
            https://github.com/abbarrasa/openbox</a></p>
            <p>Based in <a href="https://extensions.gnome.org/extension/1477/webcam-manager/">Webcam Manager</a>.</p>
            <p>If you want to report a dysfunction or a suggestion,
            feel free to open an issue in <a href="https://github.com/abbarrasa/openbox/issues">
            github</a>.""")
        creditsText = self.tr("""(c) 2018 Alberto Buitrago <%s>""") % base64.b64decode('YWJiYXJyYXNhQGdtYWlsLmNvbQ==').decode('utf-8')
        licenseText = self.tr("""<p>This program is free software: you
            can redistribute it and/or modify it under the terms of the
            GNU General Public License as published by the Free Software
            Foundation, either version 3 of the License, or (at your
            option) any later version.</p>
            <p>This program is distributed in the hope that it will be
            useful, but WITHOUT ANY WARRANTY; without even the implied
            warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
            PURPOSE. See the GNU General Public License for more
            details.</p>
            <p>You should have received a copy of the GNU General Public
            License along with this program. If not, see
            <a href="https://www.gnu.org/licenses/gpl-3.0.html">
            GNU General Public License version 3</a>.</p>""")
        layout = QVBoxLayout()
        titleLayout = QHBoxLayout()
        titleLabel = QLabel('<font size="4"><b>{0} {1}</b></font>'.format('Webcam Manager', VERSION))

        contentsLayout = QHBoxLayout()
        aboutBrowser = QTextBrowser()
        aboutBrowser.append(aboutText)
        aboutBrowser.setOpenExternalLinks(True)

        creditsBrowser = QTextBrowser()
        creditsBrowser.append(creditsText)
        creditsBrowser.setOpenExternalLinks(True)

        licenseBrowser = QTextBrowser()
        licenseBrowser.append(licenseText)
        licenseBrowser.setOpenExternalLinks(True)

        TabWidget = QTabWidget()
        TabWidget.addTab(aboutBrowser, self.tr('About'))
        TabWidget.addTab(creditsBrowser, self.tr('Contributors'))
        TabWidget.addTab(licenseBrowser, self.tr('License'))

        aboutBrowser.moveCursor(QTextCursor.Start)
        creditsBrowser.moveCursor(QTextCursor.Start)
        licenseBrowser.moveCursor(QTextCursor.Start)

        icon = QIcon.fromTheme('camera-web')
        pixmap = icon.pixmap(QSize(64, 64))
        imageLabel = QLabel()
        imageLabel.setPixmap(pixmap)
        titleLayout.addWidget(imageLabel)
        titleLayout.addWidget(titleLabel)
        titleLayout.addStretch()
        contentsLayout.addWidget(TabWidget)
        buttonLayout = QHBoxLayout()
        buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
        buttonLayout.addWidget(buttonBox)
        layout.addLayout(titleLayout)
        layout.addLayout(contentsLayout)
        layout.addLayout(buttonLayout)
        buttonBox.clicked.connect(dialog.accept)

        dialog.setLayout(layout)
        dialog.setMinimumSize(QSize(480, 400))
        dialog.setWindowTitle(self.tr('About Webcam Manager'))
        dialog.setWindowIcon(QIcon.fromTheme('help-about'))

        dialog.show()
        
    def enable(self):
        tool = self.getGUISudo()
        cmd = '%s modprobe -a uvcvideo' % tool
        self.execCommand(cmd)
        output = int(self.execCommand('lsmod | grep uvcvideo | wc -l').split()[0])
        if output > 0:
            self.updateTrayIcon(True)
            self.showNotification('Webcam enabled!', 'Webcam is turned on and ready to use')
        
    def disable(self):
        tool = self.getGUISudo()
        cmd = '%s modprobe -r uvcvideo' % tool
        self.execCommand(cmd)
        output = int(self.execCommand('lsmod | grep uvcvideo | wc -l').split()[0])
        if output == 0:
            self.updateTrayIcon(False)
            self.showNotification('Webcam disabled!', 'Webcam is turned off')
        
    def getGUISudo(self):
        tools = ['kdesu', 'lxqt-sudo', 'gksu', 'gksudo', 'pkexec', 'sudo']
        for tool in tools:
            if distutils.spawn.find_executable(tool) is not None:
                return tool
                
    def execCommand(self, cmd):
        try:
            output = subprocess.check_output(cmd, shell=True)
            return output
        except subprocess.CalledProcessError as e:
            self.showNotification('Error!', e.output)
            
    def updateTrayIcon(self, active):
        self.active = active
        if self.active:
            self.tray_icon.setIcon(QIcon.fromTheme('camera-on'))
            self.tray_icon.setToolTip('Webcam is enabled')
        else:
            self.tray_icon.setIcon(QIcon.fromTheme('camera-off'))
            self.tray_icon.setToolTip('Webcam is disabled')            
            
    def showNotification(self, title, message):
        n = notify2.Notification(title, message, 'camera-web')
        n.show()
Beispiel #50
0
class BlenderLauncher(QMainWindow, BaseWindow, Ui_MainWindow):
    def __init__(self, app):
        super().__init__()
        self.setupUi(self)

        # Server
        self.server = QLocalServer()
        self.server.listen("blender-launcher-server")
        self.server.newConnection.connect(self.new_connection)

        # Global scope
        self.app = app
        self.favorite = None
        self.status = "None"
        self.app_state = AppState.IDLE
        self.cashed_builds = []
        self.manager = PoolManager(200)
        self.timer = None

        # Setup window
        self.setWindowTitle("Blender Launcher")
        self.app.setWindowIcon(QIcon(":resources/icons/tray.ico"))

        # Setup font
        QFontDatabase.addApplicationFont(
            ":/resources/fonts/OpenSans-SemiBold.ttf")
        self.font = QFont("Open Sans SemiBold", 10)
        self.font.setHintingPreference(QFont.PreferNoHinting)
        self.app.setFont(self.font)

        # Setup style
        file = QFile(":/resources/styles/global.qss")
        file.open(QFile.ReadOnly | QFile.Text)
        self.style_sheet = QTextStream(file).readAll()
        self.app.setStyleSheet(self.style_sheet)

        # Check library folder
        if is_library_folder_valid() is False:
            self.dlg = DialogWindow(
                self, title="Information",
                text="First, choose where Blender\nbuilds will be stored",
                accept_text="Continue", cancel_text=None, icon=DialogIcon.INFO)
            self.dlg.accepted.connect(self.set_library_folder)
        else:
            self.draw()

    def set_library_folder(self):
        library_folder = Path.cwd().as_posix()
        new_library_folder = QFileDialog.getExistingDirectory(
            self, "Select Library Folder", library_folder)

        if new_library_folder:
            set_library_folder(new_library_folder)
            self.draw()

    def draw(self):
        self.HeaderLayout = QHBoxLayout()
        self.HeaderLayout.setContentsMargins(1, 1, 1, 0)
        self.HeaderLayout.setSpacing(0)
        self.CentralLayout.addLayout(self.HeaderLayout)

        self.SettingsButton = \
            QPushButton(QIcon(":resources/icons/settings.svg"), "")
        self.SettingsButton.setIconSize(QSize(20, 20))
        self.SettingsButton.setFixedSize(36, 32)
        self.WikiButton = \
            QPushButton(QIcon(":resources/icons/wiki.svg"), "")
        self.WikiButton.setIconSize(QSize(20, 20))
        self.WikiButton.setFixedSize(36, 32)
        self.MinimizeButton = \
            QPushButton(QIcon(":resources/icons/minimize.svg"), "")
        self.MinimizeButton.setIconSize(QSize(20, 20))
        self.MinimizeButton.setFixedSize(36, 32)
        self.CloseButton = \
            QPushButton(QIcon(":resources/icons/close.svg"), "")
        self.CloseButton.setIconSize(QSize(20, 20))
        self.CloseButton.setFixedSize(36, 32)
        self.HeaderLabel = QLabel("Blender Launcher")
        self.HeaderLabel.setAlignment(Qt.AlignCenter)

        self.HeaderLayout.addWidget(self.SettingsButton, 0, Qt.AlignLeft)
        self.HeaderLayout.addWidget(self.WikiButton, 0, Qt.AlignLeft)
        self.HeaderLayout.addWidget(self.HeaderLabel, 1)
        self.HeaderLayout.addWidget(self.MinimizeButton, 0, Qt.AlignRight)
        self.HeaderLayout.addWidget(self.CloseButton, 0, Qt.AlignRight)

        self.SettingsButton.setProperty("HeaderButton", True)
        self.WikiButton.setProperty("HeaderButton", True)
        self.MinimizeButton.setProperty("HeaderButton", True)
        self.CloseButton.setProperty("HeaderButton", True)
        self.CloseButton.setProperty("CloseButton", True)

        # Tab layout
        self.TabWidget = QTabWidget()
        self.CentralLayout.addWidget(self.TabWidget)

        self.LibraryTab = QWidget()
        self.LibraryTabLayout = QVBoxLayout()
        self.LibraryTabLayout.setContentsMargins(0, 0, 0, 0)
        self.LibraryTab.setLayout(self.LibraryTabLayout)
        self.TabWidget.addTab(self.LibraryTab, "Library")

        self.DownloadsTab = QWidget()
        self.DownloadsTabLayout = QVBoxLayout()
        self.DownloadsTabLayout.setContentsMargins(0, 0, 0, 0)
        self.DownloadsTab.setLayout(self.DownloadsTabLayout)
        self.TabWidget.addTab(self.DownloadsTab, "Downloads")

        self.LibraryToolBox = BaseToolBoxWidget(self)

        self.LibraryStableListWidget = \
            self.LibraryToolBox.add_list_widget("Stable Releases")
        self.LibraryDailyListWidget = \
            self.LibraryToolBox.add_list_widget("Daily Builds")
        self.LibraryExperimentalListWidget = \
            self.LibraryToolBox.add_list_widget("Experimental Branches")
        self.LibraryCustomListWidget = \
            self.LibraryToolBox.add_list_widget("Custom Builds")
        self.LibraryTab.layout().addWidget(self.LibraryToolBox)

        self.DownloadsToolBox = BaseToolBoxWidget(self)

        self.DownloadsStableListWidget = \
            self.DownloadsToolBox.add_list_widget("Stable Releases")
        self.DownloadsDailyListWidget = \
            self.DownloadsToolBox.add_list_widget("Daily Builds")
        self.DownloadsExperimentalListWidget = \
            self.DownloadsToolBox.add_list_widget("Experimental Branches")
        self.DownloadsTab.layout().addWidget(self.DownloadsToolBox)

        self.LibraryToolBox.setCurrentIndex(get_default_library_page())

        # Connect buttons
        self.SettingsButton.clicked.connect(self.show_settings_window)
        self.WikiButton.clicked.connect(lambda: webbrowser.open(
            "https://github.com/DotBow/Blender-Launcher/wiki"))
        self.MinimizeButton.clicked.connect(self.showMinimized)
        self.CloseButton.clicked.connect(self.close)

        self.StatusBar.setFont(self.font)
        self.statusbarLabel = QLabel()
        self.statusbarVersion = QLabel(self.app.applicationVersion())
        self.StatusBar.addPermanentWidget(self.statusbarLabel, 1)
        self.StatusBar.addPermanentWidget(self.statusbarVersion)

        # Draw library
        self.draw_library()

        # Setup tray icon context Menu
        quit_action = QAction("Quit", self)
        quit_action.triggered.connect(self.quit)
        hide_action = QAction("Hide", self)
        hide_action.triggered.connect(self.hide)
        show_action = QAction("Show", self)
        show_action.triggered.connect(self._show)
        launch_favorite_action = QAction(
            QIcon(":resources/icons/favorite.svg"), "Blender", self)
        launch_favorite_action.triggered.connect(self.launch_favorite)

        tray_menu = QMenu()
        tray_menu.setFont(self.font)
        tray_menu.addAction(launch_favorite_action)
        tray_menu.addAction(show_action)
        tray_menu.addAction(hide_action)
        tray_menu.addAction(quit_action)

        # Setup tray icon
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(QIcon(":resources/icons/tray.ico"))
        self.tray_icon.setToolTip("Blender Launcher")
        self.tray_icon.activated.connect(self.tray_icon_activated)
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

        # Forse style update
        self.style().unpolish(self.app)
        self.style().polish(self.app)

        # Show window
        if get_launch_minimized_to_tray() is False:
            self._show()

    def _show(self):
        self.activateWindow()
        self.show()
        self.set_status()

    def launch_favorite(self):
        try:
            self.favorite.launch()
        except Exception:
            self.dlg = DialogWindow(
                self, text="Favorite build not found!",
                accept_text="OK", cancel_text=None)

    def tray_icon_activated(self, reason):
        if reason == QSystemTrayIcon.Trigger:
            self._show()
        elif reason == QSystemTrayIcon.MiddleClick:
            self.launch_favorite()

    def quit(self):
        download_widgets = []

        download_widgets.extend(self.DownloadsStableListWidget.items())
        download_widgets.extend(self.DownloadsDailyListWidget.items())
        download_widgets.extend(self.DownloadsExperimentalListWidget.items())

        for widget in download_widgets:
            if widget.state == DownloadState.DOWNLOADING:
                self.dlg = DialogWindow(
                    self, title="Warning", text="Download task in progress!<br>\
                    Are you sure you want to quit?",
                    accept_text="Yes", cancel_text="No",
                    icon=DialogIcon.WARNING)

                self.dlg.accepted.connect(self.quit2)
                return

        self.quit2()

    def quit2(self):
        if self.timer is not None:
            self.timer.cancel()

        self.tray_icon.hide()
        self.app.quit()

    def draw_library(self, clear=False):
        self.set_status("Reading local builds")

        if clear:
            self.timer.cancel()
            self.scraper.quit()
            self.DownloadsStableListWidget.clear()
            self.DownloadsDailyListWidget.clear()
            self.DownloadsExperimentalListWidget.clear()

        self.favorite = None

        self.LibraryStableListWidget.clear()
        self.LibraryDailyListWidget.clear()
        self.LibraryExperimentalListWidget.clear()

        self.library_drawer = LibraryDrawer(self)
        self.library_drawer.build_found.connect(self.draw_to_library)
        self.library_drawer.finished.connect(self.draw_downloads)
        self.library_drawer.start()

    def draw_downloads(self):
        self.app_state = AppState.CHECKINGBUILDS
        self.set_status("Checking for new builds")
        self.scraper = Scraper(self, self.manager)
        self.scraper.links.connect(self.draw_new_builds)
        self.scraper.error.connect(self.connection_error)
        self.scraper.start()

    def connection_error(self):
        set_locale()
        utcnow = strftime(('%H:%M'), localtime())
        self.set_status("Connection Error at " + utcnow)
        self.app_state = AppState.IDLE

        self.timer = threading.Timer(600.0, self.draw_downloads)
        self.timer.start()

    def draw_new_builds(self, builds):
        self.cashed_builds.clear()
        self.cashed_builds.extend(builds)

        library_widgets = []
        download_widgets = []

        library_widgets.extend(self.LibraryStableListWidget.items())
        library_widgets.extend(self.LibraryDailyListWidget.items())
        library_widgets.extend(self.LibraryExperimentalListWidget.items())

        download_widgets.extend(self.DownloadsStableListWidget.items())
        download_widgets.extend(self.DownloadsDailyListWidget.items())
        download_widgets.extend(self.DownloadsExperimentalListWidget.items())

        for widget in download_widgets:
            if widget.build_info in builds:
                builds.remove(widget.build_info)
            elif widget.state != DownloadState.DOWNLOADING:
                widget.destroy()

        for widget in library_widgets:
            if widget.build_info in builds:
                builds.remove(widget.build_info)

        for build_info in builds:
            self.draw_to_downloads(build_info)

        set_locale()
        utcnow = strftime(('%H:%M'), localtime())
        self.set_status("Last check at " + utcnow)
        self.app_state = AppState.IDLE

        self.timer = threading.Timer(600.0, self.draw_downloads)
        self.timer.start()

    def draw_from_cashed(self, build_info):
        if self.app_state == AppState.IDLE:
            if build_info in self.cashed_builds:
                i = self.cashed_builds.index(build_info)
                self.draw_to_downloads(self.cashed_builds[i])

    def draw_to_downloads(self, build_info):
        branch = build_info.branch

        if branch == 'stable':
            list_widget = self.DownloadsStableListWidget
        elif branch == 'daily':
            list_widget = self.DownloadsDailyListWidget
        else:
            list_widget = self.DownloadsExperimentalListWidget

        item = BaseListWidgetItem(build_info.commit_time)
        widget = DownloadWidget(self, list_widget, item, build_info)
        item.setSizeHint(widget.sizeHint())
        list_widget.addItem(item)
        list_widget.setItemWidget(item, widget)

    def draw_to_library(self, path):
        category = Path(path).parent.name

        if category == 'stable':
            list_widget = self.LibraryStableListWidget
        elif category == 'daily':
            list_widget = self.LibraryDailyListWidget
        elif category == 'experimental':
            list_widget = self.LibraryExperimentalListWidget
        elif category == 'custom':
            list_widget = self.LibraryCustomListWidget
        else:
            return

        item = BaseListWidgetItem()
        widget = LibraryWidget(self, item, path, list_widget)
        list_widget.insertItem(0, item)
        list_widget.setItemWidget(item, widget)

    def set_status(self, status=None):
        if status is not None:
            self.status = status

        self.statusbarLabel.setText("Status: {0}".format(self.status))

    def show_settings_window(self):
        self.settings_window = SettingsWindow(self)

    def clear_temp(self):
        temp_folder = Path(get_library_folder()) / ".temp"
        self.remover = Remover(temp_folder)
        self.remover.start()

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

    def new_connection(self):
        self._show()
Beispiel #51
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)