def show_notification(self):
     """Show a notification that the Course downloader has completed all downloads
     """
     system_tray_icon = QSystemTrayIcon(self)
     system_tray_icon.show()
     text = 'Downloads are completed!'
     system_tray_icon.showMessage('Course Downloader', text)
Beispiel #2
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 #3
0
class MainWindow(QMainWindow):
    def __init__(self, app):
        QMainWindow.__init__(self)

        # Config app icon
        self.app_icon = QtGui.QIcon("assets/icon.png")
        self.setWindowIcon(self.app_icon)

        self.tray_icon = QSystemTrayIcon(self.app_icon, self)
        self.tray_icon.activated.connect(self.tray_icon_event)
        menu = QMenu(self)
        quit_action = menu.addAction("Fechar aplicação")
        quit_action.triggered.connect(app.quit)
        tray_menu = QMenu()
        tray_menu.addAction(quit_action)
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

    def tray_icon_event(self, reason):
        if reason == QSystemTrayIcon.DoubleClick:
            if not self.isVisible():
                self.show()

    def hide_window_and_tray(self):
        if self.tray_icon.isVisible():
            self.tray_icon.hide()
        if self.isVisible():
            self.hide()

    # When user clicks on window quit button
    def closeEvent(self, event):
        event.ignore()
        self.hide()
        self.tray_icon.showMessage("File upload", "Aplicação foi minimizada", QSystemTrayIcon.Information, 1000)
Beispiel #4
0
class SystemTray(QWidget):
    """只有托盘"""
    def __init__(self, config_list):
        super().__init__()
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(QIcon('icons/app.ico'))
        self.add_menu(config_list)

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

    def display(self):
        """icon的值: 0-没有图标  1-是提示  2-是警告  3-是错误"""
        self.tray.show()
        self.tray.showMessage(u"启动成功", '请通过右键操作')
Beispiel #5
0
class BrowserScreen(QWebEngineView):
    def __init__(self):
        QWebEngineView.__init__(self)

        self.resize(800, 600)
        self.show()
        #增加了<script src="qrc:///qtwebchannel/test.js"></script>
        self.setHtml("""
            <script src="qrc:///qtwebchannel/test.js"></script>
           <script>function message() { return "Clicked!"; }</script>
           <h1>QtWebKit + Python sample program</h1>
           <input type="button" value="Click JavaScript!"
                  onClick="alert('[javascript] ' + message())"/>
           <input type="button" value="Click Python!"
                  onClick="python.alert('[python] ' +
                                        python.message())"/>
           <br />

           <iframe src="http://www.so.com/"
                   width="750" height="500"
                   scrolling="no"
                   frameborder="0"
                   align="center"></iframe>
        """)
        self.createTrayIcon()
        self.trayIcon.show()

    def createTrayIcon(self):
        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setIcon(QIcon("images/trash.png"))

    def showMessage(self, msg):
        self.trayIcon.showMessage("This is Python", msg,
                                  QSystemTrayIcon.MessageIcon(0), 15 * 1000)
Beispiel #6
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 #7
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 #8
0
class SystemTray(object):
    # 程序托盘类
    def __init__(self, w):
        self.app = app
        self.w = w
        QApplication.setQuitOnLastWindowClosed(
            False)  # 禁止默认的closed方法,只能使用qapp.quit()的方法退出程序
        self.w.show()  # 不设置显示则为启动最小化到托盘
        self.tp = QSystemTrayIcon(self.w)
        self.initUI()
        self.run()

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

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

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

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

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

    def run(self):

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

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

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

    dx_SystemTray_Signal = pyqtSignal(str)

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

        self.tray = QSystemTrayIcon()

        # 创建QIcon对象,用于设置图标(图片过大会出错) -- 运行python命令的目录必须在文件目录,不然会报错
        # self.trayIconPix = QPixmap(16,16)
        # self.trayIconPix.fill(QColor(100,100,100))
        self.trayIconPix = QPixmap("./images/me.png")
        self.Icon = QIcon(self.trayIconPix)

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

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

        self.tray.show()

    # 显示程序主页面
    def showApp(self):
        self.dx_SystemTray_Signal.emit("show")

    # 程序退出
    def closeApp(self):
        self.dx_SystemTray_Signal.emit("close")

    # 显示消息
    def showMsg(self, Msg_type, Msg_str):

        if (Msg_type == 1):
            showMsgType = QSystemTrayIcon.Information
        elif (Msg_type == 2):
            showMsgType = QSystemTrayIcon.Warning
        elif (Msg_type == 3):
            showMsgType = QSystemTrayIcon.Critical

        self.tray.showMessage(
            "霄哥的神秘工具",
            # "程序缩小至系统托盘!",
            Msg_str,
            Msg_type,
            2000)
Beispiel #10
0
 def show_notification(self) -> None:
     """Show a notification that the Pomodoro has completed one run, and to take a break.
     """
     system_tray_icon = QSystemTrayIcon(QIcon('src/main/icons/Icon.ico'),
                                        self)
     system_tray_icon.show()
     text = str(self.time_limit) + \
         ' minutes have passed. ' if self.time_limit > 1 else '1 minutes has passed. '
     system_tray_icon.showMessage('Time Up!', text + 'Take a break!')
Beispiel #11
0
class MainWindow(QMainWindow):
    """
         Сheckbox and system tray icons.
         Will initialize in the constructor.
    """
    check_box = None
    tray_icon = None

    # Override the class constructor
    def __init__(self):
        # Be sure to call the super class method
        QMainWindow.__init__(self)

        self.setMinimumSize(QSize(480, 80))  # Set sizes
        self.setWindowTitle("System Tray Application")  # Set a title
        central_widget = QWidget(self)  # Create a central widget
        self.setCentralWidget(central_widget)  # Set the central widget

        grid_layout = QGridLayout(self)  # Create a QGridLayout
        central_widget.setLayout(grid_layout)  # Set the layout into the central widget
        grid_layout.addWidget(QLabel("Application, which can minimize to Tray", self), 0, 0)

        # Add a checkbox, which will depend on the behavior of the c_stock_program when the window is closed
        self.check_box = QCheckBox('Minimize to Tray')
        grid_layout.addWidget(self.check_box, 1, 0)
        grid_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding), 2, 0)

        # Init QSystemTrayIcon
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))

        '''
            Define and add steps to work with the system tray icon
            show - show window
            hide - hide window
            exit - exit from application
        '''
        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(qApp.quit)
        tray_menu = QMenu()
        tray_menu.addAction(show_action)
        tray_menu.addAction(hide_action)
        tray_menu.addAction(quit_action)
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

    # Override closeEvent, to intercept the window closing event
    # The window will be closed only if there is no check mark in the check box
    def closeEvent(self, event):
        event.ignore()
        self.hide()
        self.tray_icon.showMessage("Tray Program","Application was minimized to Tray",QSystemTrayIcon.Information,2000)
Beispiel #12
0
    def showMessage(self,
                    title: str,
                    body: str,
                    icon: int = 1,
                    limit: int = 5):
        tray = QSystemTrayIcon(_pkg.icon_path("logo.png", True))
        icon_ = tray.MessageIcon(icon)

        tray.showMessage(title, body, icon_, limit * 2000)
        tray.show()
Beispiel #13
0
def show_message(title, message, level=0):
    #QMessageBox.standardIcon(
    #            QStyle.SP_MessageBoxInformation), "Information",
    #            QSystemTrayIcon.Information)
    print("Running show message")
    tray_icon = QSystemTrayIcon()
    tray_icon.setIcon(QIcon(":/images/icons/heart.png"))
    # icon = tray_icon.Information
    tray_icon.show()
    tray_icon.showMessage(title, message)
Beispiel #14
0
def toast(title: str = 'Notification',
          msg: str = 'Here Comes The Message!',
          delay: int = 2,
          icon: str = '',
          msg_icon: str = ''):
    App = QApplication([])
    Tray = QSystemTrayIcon(QIcon(icon))
    Tray.show()
    QTimer.singleShot(delay * 1000, App.exit)
    Tray.showMessage(title, msg, QIcon(msg_icon), delay * 1000)
    App.exec_()
Beispiel #15
0
class StayAwakeApp(QtWidgets.QMainWindow):
    def __init__(self):
        super(StayAwakeApp, self).__init__()
        self.ui = StayAwake.Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui.label_2.setPixmap(QtGui.QPixmap(":icons/32x32.png"))    # set logo in app window (workaround for pyinstaller
        self.ui.toggle.clicked.connect(self.togglePressed)  # listener for when toggle button is pressed

        self.running = False  # flag to check if program is on or off
        self.preventSleep = None  # initializing to hold PreventSleep instance
        self.trayIcon = None  # initializing to hold tray icon instance

        self.setupTray()  # setup tray icon and menu


    # setup tray icon and menu
    def setupTray(self):
        self.trayIcon = QSystemTrayIcon(self)  # instance of QSystemTrayIcon
        self.trayIcon.setIcon(QIcon(":icons/StayAwakeOpaque.ico"))  # set tray icon image

        openAction = QAction("Open", self)  # "open" right click option in tray
        quitAction = QAction("Quit", self)  # "quit" right click option in tray
        openAction.triggered.connect(self.show)  # open app trigger
        quitAction.triggered.connect(qApp.quit)  # quit app trigger
        trayMenu = QMenu()  # QMenu class instance
        trayMenu.addAction(openAction)  # add open action
        trayMenu.addAction(quitAction)  # add quit action
        self.trayIcon.setContextMenu(trayMenu)  # sets menu actions
        self.trayIcon.show()  # makes tray icon visible

    # handle toggle button states and calling prevent sleep
    def togglePressed(self):
        if self.running:
            self.ui.toggle.setText("Off")  # set toggle button text to "Off"
            self.running = False  # update state
            self.preventSleep.terminate()  # terminate prevent sleep thread
        else:
            self.ui.toggle.setText("On")  # set toggle button text to "On"
            self.running = True  # update state
            self.preventSleep = PreventSleep()  # create instance of PreventSleep class
            self.preventSleep.start()  # start prevent sleep thread

    # making "minimize" button minimizes to tray instead of taskbar
    def changeEvent(self, event):
        if event.type() == QtCore.QEvent.WindowStateChange:
            if self.windowState() & QtCore.Qt.WindowMinimized:  # if minimize button is pressed
                event.ignore()  # ignore "minimize to taskbar" event
                self.hide()  # hide the window
                self.trayIcon.show()  # show tray icon
                self.trayIcon.showMessage("Stay Awake", "Stay Awake was minimized to tray.",
                                          QSystemTrayIcon.Information)  # show tray message popup
Beispiel #16
0
class AppWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(self.style().standardIcon(
            QStyle.SP_ComputerIcon))
        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)
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()
        self.tray_icon.activated.connect(self.hand_tray_activated)
        self.ui = Control_Panel()
        self.ui.setupUi(self)
        self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint
                            | QtCore.Qt.WindowMinimizeButtonHint)
        self.show()
        self.ui.th.hide_signal.connect(self.minimize_to_tray)

    def hand_tray_activated(self, reason):
        # On double clicking the tray icon the windows is shown again
        if reason == QSystemTrayIcon.DoubleClick:
            self.show()

    def minimize_to_tray(self):
        # when go button is clicked the app is minimized to tray
        self.hide()
        self.tray_icon.showMessage("Hand of The King",
                                   "Application was minimized to tray",
                                   QSystemTrayIcon.Information, 2000)

    def closeEvent(self, event):
        # overriding the closeEvent function of the window to cleanup all open windows and release the camera
        self.tray_icon.hide()
        self.ui.application_on = False
        self.ui.gesture_detector.camera.release()
        self.ui.hand_window.close()
        event.accept()
Beispiel #17
0
class SystemTray(QWidget):
    def __init__(self, parent=None):
        super(SystemTray, self).__init__(parent)
        self.tray_icon_menu = QMenu(self)
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setContextMenu(self.tray_icon_menu)
        self.icon_management = IconManagement(self.tray_icon)
        self.connection_handler = ConnectionHandler(FREQUENCY_CHECK_MS,
                                                    TIME_OUT_CALL_S, self)
        self.connection_handler.value_changed.connect(self.internet_connection)
        self.connection_handler.start()

    def add_action(self, name, triggered_action):
        action = QAction(QCoreApplication.translate(trad_context, name),
                         self,
                         triggered=triggered_action)
        self.tray_icon_menu.addAction(action)

    def add_separator(self):
        self.tray_icon_menu.addSeparator()

    def show(self):
        super(SystemTray, self).show()
        self.tray_icon.show()

    @pyqtSlot()
    def event_started(self):
        self.icon_management.start()

    @pyqtSlot()
    def event_finished(self):
        self.icon_management.stop()

    @pyqtSlot(Exception)
    def conductor_problem(self, e):
        self.notify("Demerio", "There was a problem : %s" % (e, ))
        self.icon_management.conductor_problem()

    @pyqtSlot(bool)
    def internet_connection(self, internet_is_ok):
        if not internet_is_ok:
            self.notify("Demerio", "Internet connection is lost")
        self.icon_management.internet_is_ok(internet_is_ok)

    def notify(self, title, message):
        self.tray_icon.showMessage(title, message, BAR_NOTIFICATION_TIME)
Beispiel #18
0
class SystemTray(QWidget):

    def __init__(self, parent=None):
        super(SystemTray, self).__init__(parent)
        self.tray_icon_menu = QMenu(self)
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setContextMenu(self.tray_icon_menu)
        self.icon_management = IconManagement(self.tray_icon)
        self.connection_handler = ConnectionHandler(FREQUENCY_CHECK_MS, TIME_OUT_CALL_S, self)
        self.connection_handler.value_changed.connect(self.internet_connection)
        self.connection_handler.start()

    def add_action(self, name, triggered_action):
        action = QAction(QCoreApplication.translate(trad_context, name), self, triggered = triggered_action)
        self.tray_icon_menu.addAction(action)

    def add_separator(self):
        self.tray_icon_menu.addSeparator()

    def show(self):
        super(SystemTray, self).show()
        self.tray_icon.show()

    @pyqtSlot()
    def event_started(self):
        self.icon_management.start()

    @pyqtSlot()
    def event_finished(self):
        self.icon_management.stop()

    @pyqtSlot(Exception)
    def conductor_problem(self, e):
        self.notify("Demerio", "There was a problem : %s" % (e,))
        self.icon_management.conductor_problem()

    @pyqtSlot(bool)
    def internet_connection(self, internet_is_ok):
        if not internet_is_ok:
            self.notify("Demerio", "Internet connection is lost")
        self.icon_management.internet_is_ok(internet_is_ok)

    def notify(self, title, message):
        self.tray_icon.showMessage(title, message, BAR_NOTIFICATION_TIME)
class MainWindow(QMainWindow):
    """
         Сheckbox and system tray icons.
         Will initialize in the constructor.
    """
    check_box = None
    tray_icon = None

    # Override the class constructor
    def __init__(self):
        # Be sure to call the super class method
        QMainWindow.__init__(self)

        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(self.style().standardIcon(
            QStyle.SP_ComputerIcon))
        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.quit)
        tray_menu = QMenu()
        tray_menu.addAction(show_action)
        tray_menu.addAction(hide_action)
        tray_menu.addAction(quit_action)
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

    def quit(self):
        self.tray_icon.hide()
        qApp.quit()

    # Override closeEvent, to intercept the window closing event
    # The window will be closed only if there is no check mark in the check box
    def closeEvent(self, event):
        event.ignore()
        self.hide()

        self.tray_icon.showMessage("Tray Program",
                                   "Application was minimized to Tray",
                                   QSystemTrayIcon.Information, 2000)
Beispiel #20
0
class BaseTray(object):
    def __init__(self, icon_path):
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(QIcon(icon_path))
        self.tray_menu = QMenu()
        self._set_menu()

    def _set_menu(self):
        pass

    def show_message(self, text):
        icon = self.tray.MessageIcon()
        self.tray.showMessage('message', text, icon, 1000)

    def show(self):
        self.tray.show()

    def quit(self):
        self.tray.hide()
        sys.exit()
Beispiel #21
0
def main_gui():
    """
    A simple PyQt5 GUI application that loads a main window from a designer file and creates
    a system tray icon with a right-click menu and can generate notifications.

    :return:
    """
    import sys
    import pkg_resources
    import os
    from PyQt5.QtWidgets import QApplication
    from PyQt5 import uic

    app = QApplication(sys.argv)

    # Or load a UI designer file
    designer_filepath = pkg_resources.resource_filename(
        __name__, os.path.join("resources", "main_window.ui"))
    win2 = uic.loadUi(designer_filepath)
    win2.show()
    win2.myLabel.setText('Hello world!')

    # System tray icon
    from PyQt5.QtWidgets import QSystemTrayIcon
    from PyQt5.uic.properties import QtGui
    from PyQt5.QtWidgets import QAction, QMenu
    tray_icon = QSystemTrayIcon()
    icon_path = pkg_resources.resource_filename(
        __name__, os.path.join("resources", "devdungeon500x500.jpg"))
    tray_icon.setIcon(QtGui.QIcon(icon_path))
    tray_menu = QMenu()
    exit_action = QAction("Exit")
    exit_action.triggered.connect(sys.exit)
    tray_menu.addAction(exit_action)
    tray_icon.setContextMenu(tray_menu)  # Set right-click menu
    tray_icon.show()
    # Notifications
    tray_icon.showMessage("Hello!", "App loaded", QSystemTrayIcon.Information,
                          3000)

    sys.exit(app.exec_())
Beispiel #22
0
class Applejack(QApplication):
    def __init__(self):
        QApplication.__init__(self, sys.argv)

        self.logWin = QMainWindow()
        self.logWin.setMinimumSize(100, 100)
        self.logWin.setWindowTitle('Sign in IPM')
        self.logWin.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))

        self.cw = QWidget(self.logWin)
        self.logWin.setCentralWidget(self.cw)

        self.gl = QGridLayout(self.logWin)
        self.cw.setLayout(self.gl)

        self.gl.addWidget(QLabel('Login:'******'Password:'******'Log in', self.logWin)
        self.gl.addWidget(self.btn, 2, 0)
        self.btn.clicked.connect(self.on_click_login)

        self.tray = QSystemTrayIcon(self.logWin)
        self.tray.setIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))

        self.logWin.show()
        self.tray.show()

    def output(self, cond, str):
        self.tray.showMessage(cond, str, msecs=5000)

    def on_click_login(self):
        if self.le.text() != '' and self.pe.text() != '':
            self.logWin.hide()
            Applet(login=self.le.text(), password=self.pe.text(), fOUT=self.output).start()
Beispiel #23
0
class IconWidget(QWidget):
    tray_icon = None

    def __init__(self):
        QWidget.__init__(self)

        quit_action = QAction("Exit", self)
        quit_action.triggered.connect(qApp.quit)

        tray_menu = QMenu()
        tray_menu.addAction(quit_action)

        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(self.style().standardIcon(
            QStyle.SP_ComputerIcon))
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

        import os
        self.tray_icon.showMessage("Tray Program",
                                   os.path.dirname(os.path.abspath(__file__)),
                                   QSystemTrayIcon.Information, 2000)
Beispiel #24
0
class IconWidget(QWidget):
    tray_icon = None
    secs_passed = 0

    def __init__(self):
        QWidget.__init__(self)

        quit_action = QAction("Exit", self)
        quit_action.triggered.connect(qApp.quit)
        stat_action = QAction("Stat", self)
        stat_action.triggered.connect(self.show_stat)

        tray_menu = QMenu()
        tray_menu.addAction(stat_action)
        tray_menu.addAction(quit_action)

        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(self.style().standardIcon(
            QStyle.SP_DriveHDIcon))
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

        self.tray_icon.showMessage(APP_NAME, '%s running!' % APP_NAME,
                                   QSystemTrayIcon.Information, 1000)
        start_new_thread(self.write_txt, ())

    def show_stat(self):
        self.tray_icon.showMessage(
            APP_NAME, 'Written %s for %d secs.' % (TXT_PATH, self.secs_passed),
            QSystemTrayIcon.Information, 2000)

    def write_txt(self):
        while True:
            file = open(TXT_PATH, 'w')
            file.write('[%s] Written %s for %d secs.' %
                       (time.asctime(), TXT_PATH, self.secs_passed))
            file.close()
            time.sleep(1)
            self.secs_passed += 1
Beispiel #25
0
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.tray_icon = QSystemTrayIcon(self)

        # Set icon to a standard or custom icon
        self.tray_icon.setIcon(self.style().standardIcon(
            QStyle.SP_ComputerIcon))
        # self.tray_icon.setIcon(QtGui.QIcon("icons/devdungeon32x32.png"))

        exit_action = QAction("Exit", self)
        exit_action.triggered.connect(self.exit_app)

        tray_menu = QMenu()
        tray_menu.addAction(exit_action)
        self.tray_icon.setContextMenu(tray_menu)  # Set right-click menu
        self.tray_icon.show()

    def notify(self, message):
        """Generate a desktop notification"""
        self.tray_icon.showMessage("Pssst!", message,
                                   QSystemTrayIcon.Information, 3000)

    def exit_app(self):
        self.tray_icon.hide(
        )  # Do this or icon will linger until you hover after exit
        qApp.quit()

    def closeEvent(self, event):
        """
        By overriding closeEvent, we can ignore the event and instead
        hide the window, effectively performing a "close-to-system-tray"
        action. To exit, the right-click->Exit option from the system
        tray must be used.
        """
        event.ignore()
        self.hide()
        self.notify("App minimize to system tray.")
Beispiel #26
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 #27
0
class LidControlMenu(object):
    def __init__(self, app, controller):
        """
        """
        self._controller = controller

        # system tray
        self._trayIcon = QSystemTrayIcon(
            QIcon(config.iconPath + "icon-sq.png"), app)
        menu = QMenu("Lid Control")

        activateAction = menu.addAction("Lid Control")
        activateAction.triggered.connect(self.MenuShowActivated)

        menu.addSeparator()

        # exit menu entry
        exitAction = menu.addAction(QIcon(config.iconPath + "icon-exit.svg"),
                                    "Exit")
        exitAction.triggered.connect(app.quit)
        self._trayIcon.setContextMenu(menu)

        # handle activation
        self._trayIcon.activated.connect(self.IconActivated)

    # endif

    def IconActivated(self):
        controller.OnActivateToggle()

    def MenuShowActivated(self):
        controller.OnActivate()

    def Show(self):
        self._trayIcon.show()

    def ShowMessage(self, title, message):
        self._trayIcon.showMessage(title, message)
Beispiel #28
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 #29
0
class Parse99(QApplication):

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

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

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

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

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

        # Thread
        self._thread = None

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

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

        # Start
        self.toggle('on')

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

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

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

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

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

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

    def edit_settings(self, **kwargs):
        try:
            if not self.settings.gui.isVisible():
                self.toggle('off')
                self.settings.gui.set_show_tab(kwargs.get('tab', None))
                self.settings.gui.exec()
                self.toggle('on')
        except Exception as e:
            print("parse99.edit_settings():", e)
Beispiel #30
0
class 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 #31
0
class NetDotTsinghuaApplication(QApplication):
    """NetDotTsinghuaApplication"""
    start_worker = pyqtSignal()
    username_changed = pyqtSignal(str)
    update_all = pyqtSignal()

    def __init__(self, argv):
        super().__init__(argv)
        icon = QIcon(":/icon.png")

        self.setQuitOnLastWindowClosed(False)  # Run without windows.
        self.setWindowIcon(icon)
        self.account_setting_dialog = None

        self.worker = Worker()
        self.worker_thread = QThread()
        self.worker.moveToThread(self.worker_thread)

        # For convenience.
        worker = self.worker
        config = worker.config
        acc = worker.account

        # Set up tray menu.
        self.tray = QSystemTrayIcon(icon, self)
        self.tray_menu = QMenu()

        # Status section.
        self.status_action = self.add_unabled_action()
        self.status_changed(worker.account.status)
        self.status = acc.status
        self.last_session = None

        # Account info section.
        self.tray_menu.addSeparator()
        self.username_action = self.add_unabled_action()
        self.usage_action = self.add_unabled_action()
        self.balance_action = self.add_unabled_action()
        self.refresh_username(config['username'])
        self.refresh_account_info(None, None)

        # Sessions section.
        self.sessions = []
        self.session_menus = []
        self.last_check = None

        self.tray_menu.addSeparator()
        self.sessions_title_action = self.add_unabled_action()
        self.last_check_action = self.add_unabled_action()

        self.refresh_sessions([])

        # Actions.
        self.tray_menu.addSeparator()
        self.tray_menu.addAction('上线').triggered.connect(acc.login)
        self.tray_menu.addAction('下线').triggered.connect(acc.logout)
        self.tray_menu.addAction('现在刷新').triggered.connect(acc.update_all)

        # Config section.
        self.tray_menu.addSeparator()
        self.auto_manage_action = self.tray_menu.addAction('自动管理')
        self.auto_manage_action.setCheckable(True)
        self.auto_manage_action.setChecked(config['auto_manage'])
        self.auto_manage_action.toggled.connect(worker.auto_manage_changed)

        self.account_setting_action = self.tray_menu.addAction('账号设置...')
        self.account_setting_action.triggered.connect(self.account_setting)

        # About.
        self.tray_menu.addSeparator()
        self.tray_menu.addAction('关于').triggered.connect(self.show_about)

        # Quit.
        self.tray_menu.addSeparator()
        self.tray_menu.addAction('退出').triggered.connect(self.quit)

        self.tray.setContextMenu(self.tray_menu)
        self.tray.show()

        # Connect signals.
        self.start_worker.connect(worker.setup)
        self.username_changed.connect(self.refresh_username)
        self.username_changed.connect(worker.username_changed)
        self.update_all.connect(acc.update_all)

        acc.status_changed.connect(self.status_changed)
        acc.info_updated.connect(self.refresh_account_info)
        acc.last_session_updated.connect(self.last_session_changed)
        acc.sessions_updated.connect(self.refresh_sessions)

        # About to show.
        self.tray_menu.aboutToShow.connect(self.update_time)
        self.tray_menu.aboutToShow.connect(self.refresh_status)

    def add_unabled_action(self, text=''):
        action = self.tray_menu.addAction(text)
        action.setEnabled(False)
        return action

    def exec(self):
        self.worker_thread.start()
        self.start_worker.emit()  # Start timers & check status.

        logging.debug('GUI thread enters event loop')
        return super().exec()

    def refresh_status(self):
        logging.debug('Refreshing status in the menu')

        s = STATUS_STR[self.status]
        # Show session usage if possible.
        if self.last_session and self.status in ('ONLINE',
                                                 'OTHERS_ACCOUNT_ONLINE'):
            s = s + ' - ' + _usage_str(self.last_session.byte)

        self.status_action.setText(s)

    def refresh_username(self, username):
        logging.debug('Refreshing username in the menu')
        if not username:
            self.username_action.setText('未设置账号')
            self.usage_action.setVisible(False)
            self.balance_action.setVisible(False)
        else:
            self.username_action.setText(username)
            self.usage_action.setVisible(True)
            self.balance_action.setVisible(True)

    def refresh_account_info(self, balance, byte):
        logging.debug('Refreshing account info section in the menu')

        self.usage_action.setText('本月流量:{}'.format(_usage_str(byte)))
        self.balance_action.setText('当前余额:{}'.format(_balance_str(balance)))

    def status_changed(self, status):
        # Show tray message.
        if status == 'ONLINE':
            self.tray.showMessage('当前在线', '本人账号在线')
        elif status == 'OTHERS_ACCOUNT_ONLINE':
            self.tray.showMessage('当前在线', '他人账号在线')
        elif status == 'OFFLINE':
            self.tray.showMessage('当前离线', '可以登录校园网')

        self.status = status

    def last_session_changed(self, session):
        self.last_session = session

    def refresh_sessions(self, sessions):
        logging.debug('Refreshing sessions section in the menu')

        self.sessions = sessions
        self.last_check = datetime.now()

        if len(sessions):
            self.sessions_title_action.setText('当前在线')
        else:
            self.sessions_title_action.setText('无设备在线')

        # Remove old actions
        for menu in self.session_menus:
            self.tray_menu.removeAction(menu.menuAction())
        self.session_menus.clear()

        # Add new actions.
        for session in sessions:
            menu = SessionMenu(self.worker.account, session)
            self.tray_menu.insertMenu(self.last_check_action,
                                      menu).setText(session.device_name)
            self.session_menus.append(menu)

    def update_time(self):
        self.last_check_action.setText(
            '上次更新:{}'.format(_time_passed_str(self.last_check)))

    @pyqtSlot()
    def account_setting(self):
        if self.account_setting_dialog is None:
            self.account_setting_dialog = AccountSettingDialog()
            existed = False
        else:
            existed = True

        dialog = self.account_setting_dialog
        dialog.show()
        dialog.raise_()
        dialog.activateWindow()

        if existed:  # Avoid multiple dialogs.
            return

        if dialog.exec():
            username = dialog.username.text()
            # Set password if needed.
            if username:
                acc = Account(username)
                acc.password = dialog.password.text()

            if username != self.worker.account.username:
                # If username changed, emit signal and clear current account info.
                self.username_changed.emit(username)
                self.refresh_account_info(None, None)
            else:
                # Update all because password might has changed.
                self.update_all.emit()

        self.account_setting_dialog = None

    @pyqtSlot()
    def show_about(self):
        msg = """<p><b>net.tsinghua {}</b><br>
<small>Copyright Ⓒ 2015 Thomas Lee</small></p>

<p><a href="https://github.com/ThomasLee969/net.tsinghua">Github 主页</a>
</p>""".format(__VERSION__)

        QMessageBox.about(None, '关于 net.tsinghua', msg)
class MainWindow(QMainWindow):

    restartOdooMenuItem = QtCore.pyqtSignal()
    stopOdooMenuItem = QtCore.pyqtSignal()
    restartPostgreMenuItem = QtCore.pyqtSignal()
    stopPostgreMenuItem = QtCore.pyqtSignal()

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

        if OdooInstallationFound == False:

            if not os.path.isdir(appfolder + '\\Temp'):
                os.makedirs(appfolder + '\\Temp')

            unzipToPath = appfolder + '\\Temp\\Unzip'
            destinationPath = appfolder + "\\Runtime\\Odoo"

            self.downloadFileWorker(mainSettings['github']['downloadPath'])

            if os.path.isfile(appfolder + '\\Temp\\GitHub-Odoo.zip'):
                if not os.path.isdir(appfolder + '\\Temp\\unzip'):
                    os.makedirs(appfolder + '\\Temp\\unzip')

                self.zipFileWorker(appfolder + '\\Temp\\GitHub-Odoo.zip', unzipToPath)
                self.zipWindow.close()

                #Check if the file is etxracted to subfolder - Files on github includes branch name -> Correct this
                countFolders = 0
                extractFolder = None
                for name  in os.listdir(unzipToPath):
                    extractFolder = name
                    countFolders += 1
                if countFolders == 1:
                    shutil.move(unzipToPath + "\\" + extractFolder + "\\", destinationPath)
                self.startCybeSystemsApplication()
        else:
            self.startCybeSystemsApplication()

        if os.path.isdir(appfolder + '\\Temp'):
            shutil.rmtree(appfolder + '\\Temp',ignore_errors=True)

    def zipFileWorker(self,file, destination_folder):
        self.zipWindow = ZipWindow(file, destination_folder)
        self.zipWindow.show()

    def downloadFileWorker(self,url):
        self.httpWin = HttpWindow(url)
        self.httpWin.exec()

    def startCybeSystemsApplication(self):

        #Set Loading TrayIcon
        self.setWindowIcon(QtGui.QIcon(appfolder + '/ressource/icons/icon.png'))

        img = QtGui.QImage()
        img.load(appfolder + '/ressource/icons/icon_loading.png')
        self.pixmap = QtGui.QPixmap.fromImage(img)
        self.icon = QtGui.QIcon()
        self.icon.addPixmap(self.pixmap)
        self.tray = QSystemTrayIcon(self.icon, self)
        self.tray.show()
        traymenu = QMenu()

        #Set Real Icon
        self.tray.hide()
        img = QtGui.QImage()
        img.load(appfolder + '/ressource/icons/icon.png')
        self.pixmap = QtGui.QPixmap.fromImage(img)
        self.icon = QtGui.QIcon()
        self.icon.addPixmap(self.pixmap)
        self.tray = QSystemTrayIcon(self.icon, self)
        self.tray.activated.connect(self.onTrayIconActivated)
        self.tray.setContextMenu(traymenu)
        self.tray.show()

        #Load Stylesheet
        if mainSettings['other']['theme'].lower() != 'default':
            if mainSettings['other']['theme'].lower() == 'steamlike':
                stylesheetFile = open(appfolder + '/ressource/ui/steamlike.stylesheet', "r")
            elif mainSettings['other']['theme'].lower() == 'darkorange':
                stylesheetFile = open(appfolder + '/ressource/ui/darkorange.stylesheet', "r")
            elif mainSettings['other']['theme'].lower() == 'maya':
                stylesheetFile = open(appfolder + '/ressource/ui/maya.stylesheet', "r")
            stylesheet = stylesheetFile.read()
            traymenu.setStyleSheet(stylesheet)
            stylesheetFile.close()

        trayoption_openBrowser_entry = QAction(QtGui.QIcon(self.icon), "Open Odoo", self)
        trayoption_openBrowser_entry.triggered.connect(lambda: webbrowser.open(mainSettings['odoo']['startpage']))
        traymenu.addAction(trayoption_openBrowser_entry)
        trayoption_openBrowser_entry.setIcon(QtGui.QIcon(appfolder + '/ressource/icons/world.png'))

        traymenu.addSeparator()

        tools = traymenu.addMenu('&Odoo')
        tools.setIcon(QtGui.QIcon(appfolder + '/ressource/icons/icon.png'))

        tools_odoo_restart = QAction(QtGui.QIcon(self.icon), "Restart Server", self)
        tools_odoo_restart.triggered.connect(lambda: (self.restartOdooMenuItem.emit()))
        tools.addAction(tools_odoo_restart)
        tools_odoo_restart.setIcon(QtGui.QIcon(appfolder + '/ressource/icons/start_server.png'))

        tools_odoo_stop = QAction(QtGui.QIcon(self.icon), "Stop Server", self)
        tools_odoo_stop.triggered.connect(lambda: (self.stopOdooMenuItem.emit()))
        tools.addAction(tools_odoo_stop)
        tools_odoo_stop.setIcon(QtGui.QIcon(appfolder + '/ressource/icons/stop_server.png'))

        #traymenu.addSeparator()

        tools = traymenu.addMenu('&PostgreSQL')
        tools.setIcon(QtGui.QIcon(appfolder + '/ressource/icons/postgresql.png'))

        tools_postgre_restart = QAction(QtGui.QIcon(self.icon), "Restart Server", self)
        tools_postgre_restart.triggered.connect(lambda: (self.restartPostgreMenuItem.emit()))
        tools.addAction(tools_postgre_restart)
        tools_postgre_restart.setIcon(QtGui.QIcon(appfolder + '/ressource/icons/start_server.png'))

        tools_postgre_stop = QAction(QtGui.QIcon(self.icon), "Stop Server", self)
        tools_postgre_stop.triggered.connect(lambda: (self.stopPostgreMenuItem.emit()))
        tools.addAction(tools_postgre_stop)
        tools_postgre_stop.setIcon(QtGui.QIcon(appfolder + '/ressource/icons/stop_server.png'))

        tools.addSeparator()

        tools_pgadmin = QAction(QtGui.QIcon(self.icon), "pgAdmin III", self)
        tools_pgadmin.triggered.connect(lambda: self.startpgadmin())
        tools.addAction(tools_pgadmin)
        tools_pgadmin.setIcon(QtGui.QIcon(appfolder + '/ressource/icons/cog.png'))

        traymenu.addSeparator()

        trayoption_quickconfig = QAction(QtGui.QIcon(self.icon), "Show Output/Config", self)
        trayoption_quickconfig.triggered.connect(lambda: self.showCommandLineWindowTryOption())
        traymenu.addAction(trayoption_quickconfig)
        trayoption_quickconfig.setIcon(QtGui.QIcon(appfolder + '/ressource/icons/application_osx_terminal.png'))

        traymenu.addSeparator()

        trayoption_exit_entry = QAction(QtGui.QIcon(self.icon), "Exit", self)
        trayoption_exit_entry.triggered.connect(lambda: self.trayOptionExit())
        traymenu.addAction(trayoption_exit_entry)
        trayoption_exit_entry.setIcon(QtGui.QIcon(appfolder + '/ressource/icons/cancel.png'))

        self.tray.showMessage('Odoo is Loading - Please wait','\nLeft click to open CommandWindow\nRight click to open Traymenu')

        self.showCommandLineWindow()

    def startpgadmin(self):
        os.startfile(appfolder + '/Runtime/PostgreSQL/bin/pgAdmin3.exe')

    def showCommandLineWindow(self):
        self.ShowCommandLineWindow=CommandLineWindow(self)
        self.ShowCommandLineWindow.setWindowIcon(QtGui.QIcon(appfolder + '/ressource/icons/icon.png'))
        self.ShowCommandLineWindow.show()
        if mainSettings['other']['minimizeToTray']:
            self.ShowCommandLineWindow.setVisible(False)
        else:
            self.ShowCommandLineWindow.setVisible(True)

    def toggleCommandLineWindow(self):
        if self.ShowCommandLineWindow.isMinimized():
            self.ShowCommandLineWindow.setVisible(True)
            self.ShowCommandLineWindow.showNormal()
            self.ShowCommandLineWindow.activateWindow()
        elif self.ShowCommandLineWindow.isVisible ():
            self.ShowCommandLineWindow.showNormal()
            self.ShowCommandLineWindow.setVisible(False)
        else:
            self.ShowCommandLineWindow.setVisible(True)
            self.ShowCommandLineWindow.showNormal()
            self.ShowCommandLineWindow.activateWindow()

    def showCommandLineWindowTryOption(self):
        self.ShowCommandLineWindow.setVisible(True)
        self.ShowCommandLineWindow.showNormal()
        self.ShowCommandLineWindow.activateWindow()

    def onTrayIconActivated(self,reason):
        if reason == QSystemTrayIcon.DoubleClick:
            pass

        if reason == QSystemTrayIcon.Trigger:
            self.toggleCommandLineWindow()

        if reason == QSystemTrayIcon.Context:
            pass

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

    def trayOptionExit(self,msgbox=True):
        if msgbox:
            quit_msg = "Stop all running Server ?"
            reply = QMessageBox.question(self.center(), 'Exit',
                quit_msg, QMessageBox.Yes, QMessageBox.No)
            if reply == QMessageBox.Yes:
                confirmed = True
            else:
                confirmed = False
        else:
            confirmed = True

        if confirmed:
            runtimeSettings["closeMainWindow"] = True
            self.ShowCommandLineWindow.setVisible(True)
            app = QApplication.instance()
            app.closeAllWindows()
            self.tray.hide()
            os._exit(1)
Beispiel #33
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 #34
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)
Beispiel #35
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 #36
0
class MainWindow(QMainWindow):
    colorWidget = None
    mode_steady = None
    mode_blink = None
    mode_pulse = None
    speed = None
    notifier = None
    mode = Command.Mode.Steady
    prev_mode = Command.Mode.Blink
    blink_min = 0
    blink_max = 3000
    pulse_min = 0
    pulse_max = 3000

    def __init__(self, notifier, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.setWindowTitle(_("Status Light Controller"))
        self.setWindowIcon(QIcon('horizontal-traffic-light.ico'))
        self.notifier = notifier

        layout = QGridLayout()

        # Color Picker section
        self.colorWidget = Color('black')
        self.colorWidget.setMouseReleased(self.change_color)
        layout.addWidget(self.colorWidget, 0, 0, 1, 2)

        # status_set = QPushButton(_("Set Status"))
        # status_set.clicked.connect(self.set_status)
        # layout.addWidget(status_set, 0, 1, 1, 2)

        # Mode section
        mode_layout = QVBoxLayout()

        self.mode_steady = QRadioButton(_("Steady"))
        self.mode_steady.setChecked(True)
        self.mode_steady.toggled.connect(
            lambda: self.change_mode(Command.Mode.Steady))
        mode_layout.addWidget(self.mode_steady)

        self.mode_blink = QRadioButton(_("Blink"))
        self.mode_blink.toggled.connect(
            lambda: self.change_mode(Command.Mode.Blink))
        mode_layout.addWidget(self.mode_blink)

        self.mode_pulse = QRadioButton(_("Pulse"))
        self.mode_pulse.toggled.connect(
            lambda: self.change_mode(Command.Mode.Pulse))
        mode_layout.addWidget(self.mode_pulse)

        layout.addLayout(mode_layout, 0, 2, 1, 2)

        # Speed section
        speed_label = QLabel(_("Speed:"))
        layout.addWidget(speed_label, 1, 0, 1, 1)

        self.speed = QSlider(Qt.Horizontal)
        self.speed.setTickPosition(QSlider.TicksBothSides)
        self.speed.setTickInterval(100)
        self.speed.setSingleStep(10)
        self.speed.setMinimum(self.blink_min)
        self.speed.setMaximum(self.blink_max)
        self.speed.setValue((self.blink_max - self.blink_min) / 2)
        self.speed.setEnabled(False)
        self.speed.valueChanged[int].connect(self.change_speed)
        layout.addWidget(self.speed, 1, 1, 1, 3)

        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        # System Tray Icon
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(QIcon('horizontal-traffic-light.ico'))

        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_app)
        tray_menu = QMenu()
        tray_menu.addAction(show_action)
        tray_menu.addAction(hide_action)
        tray_menu.addAction(quit_action)
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

    def change_color(self):
        color = QColorDialog.getColor()
        self.colorWidget.setColor(color)
        self.notifier.set_rgb("{:02x}".format(color.red()),
                              "{:02x}".format(color.green()),
                              "{:02x}".format(color.blue()))

    def change_mode(self, mode):
        self.notifier.set_mode(mode)
        if mode == Command.Mode.Steady:
            self.speed.setEnabled(False)
        else:
            if self.prev_mode != mode:
                self.convert_range(self.prev_mode, mode)
                self.prev_mode = mode
            self.speed.setEnabled(True)
            self.change_speed()
        self.mode = mode

    def convert_range(self, old_mode, new_mode):
        if old_mode == Command.Mode.Blink:
            old_min = self.blink_min
            old_max = self.blink_max
        elif old_mode == Command.Mode.Pulse:
            old_min = self.pulse_min
            old_max = self.pulse_max

        if new_mode == Command.Mode.Blink:
            new_min = self.blink_min
            new_max = self.blink_max
        elif new_mode == Command.Mode.Pulse:
            new_min = self.pulse_min
            new_max = self.pulse_max

        old_val = self.speed.value()
        old_range = old_max - old_min
        new_range = new_max - new_min
        new_val = (((old_val - old_min) * new_range) / old_range) + new_min
        self.speed.setMinimum(new_min)
        self.speed.setMaximum(new_max)
        self.speed.setValue(new_val)

    def change_speed(self):
        val = self.speed.value()
        speed = (self.speed.maximum() + 1) - val
        self.notifier.set_speed(speed)

    def closeEvent(self, event):
        event.ignore()
        self.hide()
        self.tray_icon.showMessage('Status Light Controller',
                                   'Application was minimized to tray',
                                   QSystemTrayIcon.Information, 2000)

    def close_app(self, event):
        self.notifier.set_rgb('00', '00', '00')
        qApp.quit()
Beispiel #37
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
Beispiel #38
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()))
Beispiel #39
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.connect_handlers()

        # Sub Views
        self.server_form = ServerForm()
        self.server_form.set_main_window(self)
        self.server_form.hide()

        # Initiate empty list
        self.data = []
        self.notifications = []

        # Refresh list
        self.refresh_list()

        # Set icons
        icon = QtGui.QIcon(os.path.join(os.path.dirname(__file__), "icon.png"))
        self.setWindowIcon(icon)

        self.msgIcon = QSystemTrayIcon.MessageIcon(QSystemTrayIcon.Information)
        self.systray_icon = QSystemTrayIcon(icon)
        self.systray_icon.activated.connect(self.toggle_window)
        self.systray_icon.show()

    def toggle_window(self):
        if self.isVisible():
            self.hide()
        else:
            self.show()

    def update_list(self):
        save_data(self.data, JSON_PATH)

    def refresh_list(self):
        # List Data
        self.data = read_data(JSON_PATH)

        self.ui.serverList.clear()
        for x in self.data:
            # print x
            self.ui.serverList.addItem(x['name'])

    def connect_handlers(self):
        user_interface = self.ui

        # Bind Add Events
        user_interface.addButton.clicked.connect(self.handle_add_events)
        user_interface.actionAdd_Server.triggered.connect(self.handle_add_events)

        # Bind Edit Events
        user_interface.editButton.clicked.connect(self.handle_edit_events)

        # Bind Del Events
        user_interface.delButton.clicked.connect(self.handle_del_events)

        # Bind Toggle Events
        user_interface.toggleButton.clicked.connect(self.handle_toggle_events)
        user_interface.actionStart_Monitoring.triggered.connect(self.handle_toggle_events)
        user_interface.actionStop_Monitoring.triggered.connect(self.handle_toggle_events)

        # Bind Quit Events
        user_interface.actionQuit.triggered.connect(self.handle_quit_events)

        # Bind Signals
        NOTIFIER.notify.connect(self.push_notifications)

    def handle_add_events(self):
        self.server_form.show()
        self.hide()

    def handle_edit_events(self):
        try:
            item = self.ui.serverList.selectedIndexes()[0]
            index = item.row()
        except:
            index = None

        if index is not None:
            self.server_form.set_entry_id(index)
        else:
            self.server_form.set_default_texts()

        self.server_form.show()
        self.hide()

    def handle_del_events(self):
        try:
            item = self.ui.serverList.selectedIndexes()[0]
            index = item.row()
            del self.data[index]
        except:
            pass

        self.update_list()
        self.refresh_list()

    def handle_toggle_events(self):
        try:
            timer = self.timer
            self.timer.cancel()
            del self.timer
            self.ui.toggleButton.setText("START")
            self.ui.actionStop_Monitoring.setDisabled(True)
            self.ui.actionStart_Monitoring.setEnabled(True)

            self.systray_icon.showMessage('STOP', "Server monitoring was stopped!", self.msgIcon)
        except:
            self.ui.actionStop_Monitoring.setEnabled(True)
            self.ui.actionStart_Monitoring.setDisabled(True)
            self.ui.toggleButton.setText("STOP")
            self.handle_timed_loop()

            self.systray_icon.showMessage('START', "Server monitoring has started!", self.msgIcon)

    def handle_quit_events(self):
        self.timer.cancel()
        self.close()

    def handle_timed_loop(self):
        self.notifications = monitor_servers(self.data)
        self.timer = threading.Timer(10, self.handle_timed_loop)
        self.timer.start()
        NOTIFIER.notify.emit()

    def push_notifications(self):
        # print "ok- called"
        for x in self.notifications:
            self.systray_icon.showMessage('ALERT', x, self.msgIcon)
            # pass

    def closeEvent(self, event):
        try:
            timer = self.timer
            self.timer.cancel()
            del self.timer
        except:
            pass

        event.accept()
Beispiel #40
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 #41
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 #42
0
class QtApplication(QApplication, Application):
    pluginsLoaded = Signal()
    applicationRunning = Signal()
    
    def __init__(self, tray_icon_name: str = None, **kwargs) -> None:
        plugin_path = ""
        if sys.platform == "win32":
            if hasattr(sys, "frozen"):
                plugin_path = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "PyQt5", "plugins")
                Logger.log("i", "Adding QT5 plugin path: %s", plugin_path)
                QCoreApplication.addLibraryPath(plugin_path)
            else:
                import site
                for sitepackage_dir in site.getsitepackages():
                    QCoreApplication.addLibraryPath(os.path.join(sitepackage_dir, "PyQt5", "plugins"))
        elif sys.platform == "darwin":
            plugin_path = os.path.join(self.getInstallPrefix(), "Resources", "plugins")

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

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

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

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

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

        self._is_shutting_down = False #type: bool

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

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

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

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

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

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

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

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

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

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

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

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

        self._package_manager.initialize()

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

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

        i18n_catalog = i18nCatalog("uranium")

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

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

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

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

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

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

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

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

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

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

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

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

        self.registerObjects(self._qml_engine)

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

    recentFilesChanged = pyqtSignal()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    engineCreatedSignal = Signal()

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

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

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

        return cast(QtRenderer, self._renderer)

    mainWindowChanged = Signal()

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

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

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

            self.mainWindowChanged.emit()

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

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

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

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

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

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

        return super().event(event)

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

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

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

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

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

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

        self.quit()

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

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

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

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

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

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

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

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

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

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

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

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

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

            op.push()
            Selection.clear()

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

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

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

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

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

    def _screenScaleFactor(self) -> float:
        # OSX handles sizes of dialogs behind our backs, but other platforms need
        # to know about the device pixel ratio
        if sys.platform == "darwin":
            return 1.0
        else:
            # determine a device pixel ratio from font metrics, using the same logic as UM.Theme
            fontPixelRatio = QFontMetrics(QCoreApplication.instance().font()).ascent() / 11
            # round the font pixel ratio to quarters
            fontPixelRatio = int(fontPixelRatio * 4) / 4
            return fontPixelRatio
Beispiel #43
0
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.running = False
        self.setWindowTitle('PySwicher v{}'.format(VERSION))

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

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

        self.main_window_ui()
        self.starter()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def click_timeout(self):
        self.starter()

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

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

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

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

	def __init__(self):
		super(Onkyo, self).__init__()
		self.initUI()
		self.settings = QSettings('onkyoqt', 'settings')
		self.host = self.settings.value('host', type=str)
		self.receiver = eiscp.eISCP(self.host)

	def btn_inputpc_click(self):
		try:
			self.receiver.command('input-selector=pc')
		except Exception as e:
			self.createTrayError(e)

	def btn_inputusb_click(self):
		try:
			self.receiver.command('input-selector=usb')
		except Exception as e:
			self.createTrayError(e)

	def btn_volumeup_click(self):
		try:
			volume_now = self.receiver.command('volume=level-up')
			self.sendTextToStatusBar(volume_now)
		except Exception as e:
			self.createTrayError(e)

	def btn_volumedown_click(self):
		try:
			volume_now = self.receiver.command('volume=level-down')
			self.sendTextToStatusBar(volume_now)
		except Exception as e:
			self.createTrayError(e)

	def btn_usbstop_click(self):
		try:
			self.receiver.command('network-usb=stop')
		except:
			pass

	def btn_usbplay_click(self):
		try:
			self.receiver.command('network-usb=play')
		except:
			pass

	def btn_usbpause_click(self):
		try:
			self.receiver.command('network-usb=pause')
		except:
			pass

	def btn_usbprew_click(self):
		try:
			self.receiver.command('network-usb=trdn')
		except:
			pass

	def btn_usbnext_click(self):
		try:
			self.receiver.command('network-usb=trup')
		except:
			pass

	def btn_poweroff_click(self):
		try:
			self.receiver.command('system-power=standby')
		except Exception as e:
			pass

	def btn_mute_click(self):
		try:
			volume_now = self.receiver.command('volume=0')
			self.sendTextToStatusBar(volume_now)
		except Exception as e:
			self.createTrayError(e)

	def initUI(self):

		self.settings = QSettings('onkyoqt', 'settings')
		self.hide_window = self.settings.value('hide') or 'False'
		self.hide_window = eval(self.hide_window)

		self.btn_inputpc = QPushButton('PC', self)
		self.btn_inputusb = QPushButton('USB', self)
		self.btn_volumeup = QPushButton('Vol +', self)
		self.btn_volumedown = QPushButton('Vol -', self)
		self.btn_usbstop = QPushButton('Stop', self)
		self.btn_usbplay = QPushButton('Play', self)
		self.btn_usbpause = QPushButton('Pause', self)
		self.btn_usbprew = QPushButton('Prew', self)
		self.btn_usbnext = QPushButton('Next', self)
		self.btn_poweroff = QPushButton('Power OFF', self)
		self.btn_mute = QPushButton('Mute', self)
		self.btn_quit = QPushButton('Quit', self)

		self.btn_inputpc.clicked.connect(self.btn_inputpc_click)
		self.btn_inputusb.clicked.connect(self.btn_inputusb_click)
		self.btn_volumeup.clicked.connect(self.btn_volumeup_click)
		self.btn_volumedown.clicked.connect(self.btn_volumedown_click)
		self.btn_usbstop.clicked.connect(self.btn_usbstop_click)
		self.btn_usbplay.clicked.connect(self.btn_usbplay_click)
		self.btn_usbpause.clicked.connect(self.btn_usbpause_click)
		self.btn_usbprew.clicked.connect(self.btn_usbprew_click)
		self.btn_usbnext.clicked.connect(self.btn_usbnext_click)
		self.btn_poweroff.clicked.connect(self.btn_poweroff_click)
		self.btn_quit.clicked.connect(QApplication.instance().quit)
		self.btn_mute.clicked.connect(self.btn_mute_click)

		self.mainWidget = QWidget(self)
		self.mainLayout = QGridLayout(self.mainWidget)
		self.mainLayout.addWidget(self.btn_inputpc, 0, 0, 1, 2)
		self.mainLayout.addWidget(self.btn_volumeup, 0, 3, 1, 2)
		self.mainLayout.addWidget(self.btn_inputusb, 1, 0, 1, 2)
		self.mainLayout.addWidget(self.btn_volumedown, 1, 3, 1, 2)
		self.mainLayout.addWidget(self.btn_usbstop, 2, 0)
		self.mainLayout.addWidget(self.btn_usbplay, 2, 1)
		self.mainLayout.addWidget(self.btn_usbpause, 2, 2)
		self.mainLayout.addWidget(self.btn_usbprew, 2, 3)
		self.mainLayout.addWidget(self.btn_usbnext, 2, 4)
		self.mainLayout.addWidget(self.btn_poweroff, 4, 0, 1, 2)
		self.mainLayout.addWidget(self.btn_mute, 3, 3, 1, 2)
		self.mainLayout.addWidget(self.btn_quit, 4, 3, 1, 2)

		self.mainWidget.setLayout(self.mainLayout)

		self.setCentralWidget(self.mainWidget)
		self.setWindowIcon(QIcon('/usr/share/icons/onkyo.png'))
		self.setFixedSize(280, 200)
		self.setWindowTitle('Onkyo Control')

		if self.hide_window:
			self.hide()
		else:
			self.show()

		self.createActions()
		self.createTrayIcon()
		self.createShortcuts()
		self.trayIcon.show()
		self.statusBar()
		self.createMenu()

	def createShortcuts(self):
		self.shortcut = QShortcut(QKeySequence("Ctrl+Q"), self)
		self.shortcut.activated.connect(QApplication.instance().quit)
		self.shortcut = QShortcut(QKeySequence("Ctrl+M"), self)
		self.shortcut.activated.connect(self.btn_mute_click)

	def createActions(self):
		self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide)
		self.maximizeAction = QAction("Ma&ximize", self, triggered=self.showMaximized)
		self.restoreAction = QAction(QIcon('/usr/share/icons/onkyo_restore.png'), "&Restore", self, triggered=self.showNormal)
		self.quitAction = QAction(QIcon('/usr/share/icons/onkyo_exit.png'), "&Quit", self, triggered=QApplication.instance().quit)
		self.PoweroffAction = QAction(QIcon('/usr/share/icons/onkyo_poweroff.png'), "&Power OFF", self, triggered=self.btn_poweroff_click)
		self.MuteAction = QAction(QIcon('/usr/share/icons/onkyo_mute.png'), "&Mute", self, triggered=self.btn_mute_click)
		self.VolumeupAction = QAction(QIcon('/usr/share/icons/onkyo_volumeup.png'), "&Volume UP", self, triggered=self.btn_volumeup_click)
		self.VolumedownAction = QAction(QIcon('/usr/share/icons/onkyo_volumedown.png'), "&Volume Down", self, triggered=self.btn_volumedown_click)
		self.aboutAction = QAction(QIcon('/usr/share/icons/onkyo_about.png'),"&About", self, triggered=self.about)
		self.settingsAction = QAction(QIcon('/usr/share/icons/onkyo_settings.png'), "&Settings", self, triggered=self.config)

	def createTrayIcon(self):
		self.trayIconMenu = QMenu(self)
		self.trayIconMenu.addAction(self.restoreAction)
		self.trayIconMenu.addSeparator()
		self.trayIconMenu.addAction(self.VolumeupAction)
		self.trayIconMenu.addAction(self.VolumedownAction)
		self.trayIconMenu.addAction(self.MuteAction)
		self.trayIconMenu.addSeparator()
		self.trayIconMenu.addAction(self.PoweroffAction)
		self.trayIconMenu.addSeparator()
		self.trayIconMenu.addAction(self.aboutAction)
		self.trayIconMenu.addAction(self.quitAction)
		self.trayIcon = QSystemTrayIcon(QIcon("/usr/share/icons/onkyo.png"), self)
		self.trayIcon.setContextMenu(self.trayIconMenu)

	def createMenu(self):
		menubar = self.menuBar()
		fileMenu = menubar.addMenu('&File')
		fileMenu.addAction(self.settingsAction)
		fileMenu.addAction(self.quitAction)
		helpMenu = menubar.addMenu('&Help')
		helpMenu.addAction(self.aboutAction)

	def sendTextToStatusBar(self, text):
		self.statusBar().showMessage(str(text))

	def createTrayError(self, e):
		return self.trayIcon.showMessage('Error', 'Send command to receiver failed:\n' + str(e), QSystemTrayIcon.Critical, 5 * 1000)

	def about(self):
		title = self.tr("""<b>Onkyo QT</b>
			<br/>License: GPLv3
			<br/>Python {0} - on {1}""").format(platform.python_version(), platform.system())
		image = ':/logo'
		text = self.tr("""<p>Author: Andry Kondratiev <a href="mailto:[email protected]">[email protected]</a>
						<p>Website: <a href="https://github.com/massdest/onkyoqtpy">
						https://github.com/massdest/onkyoqtpy</a>
						""")
		contributors = QCoreApplication.translate("About dialog", """
			Author: Andry Kondratiev<br/>
			""", "List of contributors")

		dialog = about_dlg.AboutDialog(title, text, image, contributors, self)
		dialog.exec_()

	def config(self):
		dialog = settings.OnkyoSettings(self)
		dialog.applied_signal.connect(self.config_save)
		if dialog.exec_() == 1:
			self.config_save()

	def config_save(self):
		logging.debug('Config saving...')
		self.host = self.settings.value('host')
Beispiel #45
0
class MainApp(QMainWindow, Ui_MainWindow):

    _translate = QCoreApplication.translate
    tab_list = []
    # TODO - add dutch translation files

    def __init__(self, isolated, *args):
        super(MainApp, self).__init__(*args)
        Lumberjack.info('spawning the <<< MainApp >>> hey says: I am the Main man here see!')
        self.load_settings()
        self.setup_tray(isolated)
        self.dbhelper = DbHelper()

        self.setupUi(self)
        self.iconize_controls()
        self.load_styling()

        self.tabWidget = QTabWidget(self.centralwidget)
        self.tabWidget.setTabsClosable(True)
        self.tabWidget.setMovable(True)
        self.tabWidget.setTabBarAutoHide(True)
        self.tabWidget.setObjectName("tabWidget")
        self.verticalLayout.addWidget(self.tabWidget)

        builderLabel = QLabel('made by: MazeFX Solutions')
        self.statusbar.addPermanentWidget(builderLabel)

        self.menuPAT.triggered.connect(self.handle_menu_event)
        self.menuLists.triggered.connect(self.handle_menu_event)
        self.menuHelp.triggered.connect(self.handle_menu_event)
        self.tabWidget.tabCloseRequested.connect(self.close_tab)
        self.actionHome.trigger()

        self._retranslateUi(self)

    def iconize_controls(self):
        Lumberjack.info('< MainApp > - -> (iconize_controls)')

        homeIcon = qta.icon('fa.home', color='white')
        self.actionHome.setIcon(homeIcon)
        wrenchIcon = qta.icon('fa.wrench', color='white')
        self.actionSettings.setIcon(wrenchIcon)

        bankIcon = qta.icon('fa.bank', color='white')
        self.actionListBankAccounts.setIcon(bankIcon)

        contractIcon = QIcon(':/app_icons/rc/handshake_icon.svg')
        self.actionListContracts.setIcon(contractIcon)

        atIcon = qta.icon('fa.at', color='white')
        self.actionListEmailAddresses.setIcon(atIcon)

        envelopeIcon = qta.icon('fa.envelope', color='white')
        self.actionListLetters.setIcon(envelopeIcon)

        relationIcon = qta.icon('fa.group', color='white')
        self.actionListRelations.setIcon(relationIcon)

        transactionIcon = qta.icon('fa.money', color='white')
        self.actionListTransactions.setIcon(transactionIcon)

        userIcon = qta.icon('fa.user', color='white')
        self.actionListUsers.setIcon(userIcon)

        helpIcon = qta.icon('fa.question', color='white')
        self.actionHelp.setIcon(helpIcon)

        aboutIcon = qta.icon('fa.info', color='white')
        self.actionAbout.setIcon(aboutIcon)

    def setup_tray(self, isolated):
        Lumberjack.info('< MainApp > - -> (setup_tray)')
        self.trayIcon = QSystemTrayIcon(QIcon(':/app_icons/rc/PAT_icon.png'), self)
        self.trayMenu = QMenu(self)
        showAction = self.trayMenu.addAction("Open PAT")
        self.trayMenu.addSeparator()
        exitAction = self.trayMenu.addAction("Exit")
        self.trayIcon.setContextMenu(self.trayMenu)
        self.trayMenu.triggered.connect(self.handle_tray_event)
        self.trayIcon.activated.connect(self.handle_tray_event)
        self.trayIcon.show()
        if isolated:
            self.trayIcon.showMessage('PAT Service', 'PAT service is now running..')

    def handle_tray_event(self, *args):
        Lumberjack.info('< MainApp > - -> (handle_tray_event)')
        print(Fore.MAGENTA + '$! Received a tray action with args: ', args)
        if args[0] == 3:
            self.show()
            return
        elif hasattr(args[0], 'text'):
            print(Fore.MAGENTA + '$! Tray event has text!!')
            if args[0].text() == 'Open PAT':
                self.show()
            elif args[0].text() == 'Exit':
                self.close()

    def _retranslateUi(self, MainWindow):
        pass

    def handle_menu_event(self, *args):
        Lumberjack.info('< MainApp > - -> (handle_menu_event)')
        Lumberjack.debug('(handle_menu_event) - args = {}'.format(args))

        action_text = args[0].text()
        icon = args[0].icon()

        Lumberjack.debug('(handle_menu_event) - Action text selector = {}'.format(action_text))
        print(Fore.MAGENTA + '$! Action text received: ', action_text)

        if action_text == 'Home':
            Lumberjack.info('(handle_menu_event) >User action> :  Adding Home tab to self')
            self.add_tab(HomeTab, 'Home', icon)

        if action_text == 'Settings':
            Lumberjack.info('(handle_menu_event) >User action> :  Showing settings dialog')
            self.show_settings()

        elif action_text == 'Bank accounts':
            Lumberjack.info('(handle_menu_event) >User action> : Adding Bank account List tab to self')
            self.add_tab(BankAccountListTab, 'Bank accounts', icon)

        elif action_text == 'Contracts':
            Lumberjack.info('(handle_menu_event) >User action> : Adding Contracts List tab to self')
            self.add_tab(ContractListTab, 'Contracts', icon)

        elif action_text == 'Email addresses':
            Lumberjack.info('(handle_menu_event) >User action> : Adding EmailAddress List tab to self')
            self.add_tab(EmailAddressListTab, 'Email addresses', icon)

        elif action_text == 'Letters':
            Lumberjack.info('(handle_menu_event) >User action> :  Adding Letter List tab to self')
            self.add_tab(LetterListTab, 'Letters', icon)

        elif action_text == 'Users':
            Lumberjack.info('(handle_menu_event) >User action> :  Adding User List tab to self')
            self.add_tab(UserListTab, 'Users', icon)

        elif action_text == 'Relations':
            Lumberjack.info('(handle_menu_event) >User action> :  Adding Relation List tab to self')
            self.add_tab(RelationListTab, 'Relations', icon)

        elif action_text == 'Transactions':
            Lumberjack.info('(handle_menu_event) >User action> :  Adding Transaction List tab to self')
            self.add_tab(TransactionListTab, 'Transactions', icon)

        elif action_text == 'Help':
            Lumberjack.info('(handle_menu_event) >User action> :  Showing help dialog')
            # TODO - build help dialog and help files

        elif action_text == 'About':
            Lumberjack.info('(handle_menu_event) >User action> :  Showing about dialog')
            # TODO build About dialog.

    def show_settings(self):
        Lumberjack.info('< MainApp > - -> (show_settings)')
        settings_dialog = SettingsDialog()
        settings_dialog.exec_()

    def add_tab(self, tab_cls, tab_name, icon):
        Lumberjack.info('< MainApp > - -> (add_tab)')
        new_tab = tab_cls(self.dbhelper)
        print(Fore.MAGENTA + 'Adding a tab with class: ', str(tab_cls))
        new_tab.setObjectName(str(tab_cls))
        self.tabWidget.addTab(new_tab, icon, self._translate("MainWindow", tab_name))

        print(Fore.MAGENTA + 'New tab added to tab list.')
        self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(new_tab))
        self.tab_list.append(new_tab)

    def close_tab(self, index):
        # TODO - Check if index stays correct when moving tabs around
        requesting_tab = self.tab_list[index]
        print(Fore.MAGENTA + 'requesting tab is: ', requesting_tab)
        if hasattr(requesting_tab, 'form'):
            if requesting_tab.form.edit_mode:
                print(Fore.MAGENTA + 'Tab is in edit mode.')
                requesting_tab.form.toggle_edit_mode(False, None, None)
            if requesting_tab.form.edit_mode is None:
                print(Fore.MAGENTA + 'Tab is now in equil.')
                self.tabWidget.removeTab(index)
                del self.tab_list[index]
        else:
            self.tabWidget.removeTab(index)
            del self.tab_list[index]

    def load_settings(self):
        self.settings = QSettings()

        db_path = self.settings.value('db_base_path')
        db_name = self.settings.value('db_name')

        if db_path is not None and db_name is not None:
            db_file = os.path.join(db_path, db_name)
            Lumberjack.debug('__init__ - db_file = {}'.format(db_file))
            if os.path.exists(db_file):
                return
        Lumberjack.warning('(load_settings) - database not found')
        settings_dialog = SettingsDialog()
        settings_dialog.exec_()
        int_value = self.settings.value('db_type', type=int)
        print(Fore.MAGENTA + "load choosen database setting: %s" % repr(int_value))

    def load_styling(self):
        style.set_window_style(self)

    def closeEvent(self, event):
        print(Fore.MAGENTA + "User has clicked the red x on the main window")
        for tab in self.tab_list:
            if hasattr(tab, 'form'):
                if tab.form.edit_mode:
                    print(Fore.MAGENTA + 'Tab is in edit mode.')
                    tab.form.toggle_edit_mode(False, None, None)

        close_dialog = CloseDialog()
        result = close_dialog.exec_()
        if result == close_dialog.Minimize:
            self.hide()
            event.ignore()
        elif result == close_dialog.Rejected:
            event.ignore()
        elif result == close_dialog.Exit:
            print(Fore.MAGENTA + "Exiting via save dialog, result = ", result)
            self.trayIcon.hide()
            event.accept()
Beispiel #46
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 #47
0
class Example(QMainWindow):

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

        self.initUI()
        self.trayIcon = None
        self.kill = False
        self.setupTrayIcon()
        try:
            cherrypy_server.start_server()
        except Exception as e:
            logging.getLogger(__name__).warn("Problem starting print server! {}".format(e))
            traceback.print_exc()

    def reallyClose(self):
        self.kill = True
        self.close()

    def openConsole(self):
        from web.server import cfg
        url = r'https://localhost:{}'.format(cfg.port)
        webbrowser.open(url)


    def initUI(self):

        openAction = QAction("&Open Console", self)
        openAction.setShortcut('Ctrl+O')
        openAction.setStatusTip('Open Console')
        openAction.triggered.connect(self.openConsole)

        exitAction = QAction(QIcon(os.path.join('web', 'static', 'img', 'exit-icon-3.png')), '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(self.reallyClose)

        self.statusBar()

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(openAction)
        fileMenu.addSeparator()
        fileMenu.addAction(exitAction)

        wrapper = QWidget()
        txt = QTextEdit()
        txt.setReadOnly(True)
        txt.setLineWrapMode(QTextEdit.NoWrap)
        txt.setUndoRedoEnabled(False)
        txt.setAcceptDrops(False)
        txt.setAcceptRichText(False)

        font = txt.font()
        font.setFamily("Courier")
        font.setPointSize(9)
        txt.setFont(font)

        policy = txt.sizePolicy()
        policy.setVerticalStretch(1)
        txt.setSizePolicy(policy)

        layout = QGridLayout()
        layout.addWidget(txt)
        wrapper.setLayout(layout)

        self.setCentralWidget(wrapper)

        def _write(s):
            txt.moveCursor(QTextCursor.End)
            txt.insertPlainText(str(s))
            txt.moveCursor(QTextCursor.End)

        setup_logging(_write)

        self.setGeometry(300, 300, 800, 600)
        self.setWindowTitle('Antix Print Server')

    def setupTrayIcon(self):
        _icon = QIcon(os.path.join('web', 'static', 'img', 'Logo.144x144.png'))
        self.trayIcon = QSystemTrayIcon(_icon, self)
        menu = QMenu(self)
        menu.addAction("Show", self.show)
        menu.addAction("Hide", self.hide)
        menu.addAction("Exit", self.reallyClose)
        self.trayIcon.setContextMenu(menu)
        self.trayIcon.show()
        self.trayIcon.showMessage("Antix Printer Server", "Is running")

    def closeEvent(self, evnt):
        if self.kill:
            self.trayIcon.hide()
            try:
                cherrypy_server.stop_server()
            except Exception as e:
                logging.getLogger(__name__).warn("Problem stopping server! {}".format(e))
            qApp.exit(0)
        else:
            evnt.ignore()
            self.hide()
Beispiel #48
0
class MainWindow(QtWidgets.QMainWindow, MainF):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        global f1, f2, f3, f4
        f1 = False
        f2 = True
        f3 = False
        f4 = True

        def setDirectory(event):
            dirlist = QFileDialog.getExistingDirectory(self, "Выбрать папку",
                                                       ".")
            self.dirLineEdit.setText(dirlist)
            # self.plainTextEdit.appendHtml("<br>Выбрали папку: <b>{}</b>".format(dirlist))

        self.dirLineEdit.mouseDoubleClickEvent = setDirectory

        self.SearchButton.clicked.connect(lambda: self.Search())
        # self.pushButton.clicked.connect(lambda: self.pr())

        self.action_help.triggered.connect(lambda: self.help())
        self.action_settings.triggered.connect(lambda: self.settings())

        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(self.style().standardIcon(
            QStyle.SP_ComputerIcon))

        @pyqtSlot()
        def action(signal):
            if signal == 2:
                self.showNormal()

        self.tray_icon.activated.connect(action)
        quit_action = QAction("Exit", self)
        quit_action.triggered.connect(app.quit)
        tray_menu = QMenu()
        tray_menu.addAction(quit_action)
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

    def closeEvent(self, event):
        if f1:
            event.ignore()
            self.hide()
            self.tray_icon.showMessage("Tray Program",
                                       "Application was minimized to Tray",
                                       QSystemTrayIcon.Information, 2000)

    def help(self):
        self.h = Help()

        self.h.show()

    def settings(self):
        self.s = Settings()
        self.s.show()

    def Search(self):
        global filelist, dir
        dir = self.dirLineEdit.text()
        word = self.wordLineEdit.text()
        txt = ""
        if dir == "":
            QMessageBox.critical(self, "Ошибка ", "Введите директроию",
                                 QMessageBox.Ok)
        else:
            try:
                filelist = []
                log = open("log.txt", 'w')
                log.write("Remover log:\n")
                log.write("Dir: " + dir + "\nWord: " + word + "\n\n")
                print(os.listdir(dir))

                for f in os.listdir(dir):
                    log.write(f)

                    if f.find(word) != -1:
                        print(f)
                        filelist.append(f)

                        log.write(" " * (20 - len(f)) +
                                  "Содержит в названии " + word + "\n")

                    else:
                        if f.endswith(".txt"):
                            # print(f)
                            file = open(dir + "/" + f, 'r')
                            if file.read().find(word) != -1:
                                filelist.append(f)
                                log.write(" " * (20 - len(f)) +
                                          "В файле найдено " + word + "\n")
                            else:
                                log.write("\n")
                                # pass
                        else:
                            log.write("\n")
                            # pass

                # log.close()
                self.listw = ListWindow(self)
                self.listw.show()
            except:
                QMessageBox.critical(self, "Ошибка ", "Такой дириктории нет",
                                     QMessageBox.Ok)
Beispiel #49
0
class Window(QMainWindow):
    """
    Main GUI class for application
    """

    def __init__(self):
        QWidget.__init__(self)

        # loaind ui from xml
        uic.loadUi(os.path.join(DIRPATH, 'app.ui'), self)

        # self.show_msgbox("Info", "Lan Messenger")

        self.users = {}

        self.host = socket.gethostname()
        self.ip = get_ip_address()

        # button event handlers
        self.btnRefreshBuddies.clicked.connect(self.refreshBuddies)
        self.btnSend.clicked.connect(self.sendMsg)

        self.lstBuddies.currentItemChanged.connect(
                self.on_buddy_selection_changed)

        self.msg_manager = MessageManager()
        self.msg_sender = MessageSender(self.host, self.ip)

        self.message_listener = MessageListener()
        self.message_listener.message_received.connect(self.handle_messages)

        self.send_IAI()

        self.setup_tray_menu()

        # setting up handlers for menubar actions
        self.actionAbout.triggered.connect(self.about)
        self.actionExit.triggered.connect(qApp.quit)
        self.actionPreferences.triggered.connect(self.show_preferences)

    def about(self):
        print("about")
        ad = AboutDialog()
        ad.display()

    def show_preferences(self):
        print("preferences")
        pd = PrefsDialog()
        pd.display()

    def setup_tray_menu(self):

        # setting up QSystemTrayIcon
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(
                self.style().standardIcon(QStyle.SP_ComputerIcon))

        # tray actions
        show_action = QAction("Show", self)
        quit_action = QAction("Exit", self)
        hide_action = QAction("Hide", self)

        # action handlers
        show_action.triggered.connect(self.show)
        hide_action.triggered.connect(self.hide)
        quit_action.triggered.connect(qApp.quit)

        # tray menu
        tray_menu = QMenu()
        tray_menu.addAction(show_action)
        tray_menu.addAction(hide_action)
        tray_menu.addAction(quit_action)
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

    def closeEvent(self, event):
        event.ignore()
        self.hide()
        self.tray_icon.showMessage(
            "PyLanMessenger",
            "PyLanMessenger was minimized to Tray",
            QSystemTrayIcon.Information,
            2000
        )

    def handle_messages(self, data):
        log.debug("UI handling message: %s" % data)
        pkt = Packet()
        pkt.json_to_obj(data)
        if pkt.op == "IAI":
            self.handle_IAI(pkt.ip, pkt.host)
        if pkt.op == "MTI":
            self.handle_MTI(pkt.ip, pkt.host)
        if pkt.op == "TCM":
            self.handle_TCM(pkt.ip, pkt.host, pkt.msg)

    def send_IAI(self):
        # broadcast a message that IAI - "I Am In" the n/w
        pkt = Packet(op="IAI", ip=self.ip, host=self.host).to_json()
        self.msg_sender.send_broadcast_message(pkt)

    def send_MTI(self):
        # broadcast a message that MTI - "Me Too In" the n/w
        pkt = Packet(op="MTI", ip=self.ip, host=self.host).to_json()
        self.msg_sender.send_broadcast_message(pkt)

    def handle_IAI(self, ip, host):
        """
        handle "I am In" packet

        reply with MTI for IAI
        me too in when other says "i am in"
        """
        self.send_MTI()

        if host not in self.users:
            print("adding host", host)
            self.users[host] = ip
            self.lstBuddies.addItem(str(host))

    def handle_MTI(self, ip, host):
        """
        handle Me Too In packet
        """

        if host not in self.users:
            self.users[host] = ip
            self.lstBuddies.addItem(str(host))

    def handle_TCM(self, ip, host, msg):
        self.add_chat_msg(ip, host, "%s: %s" % (host, msg))

    def refreshBuddies(self):
        self.lstBuddies.clear()
        self.users = {}
        self.send_IAI()

    def sendMsg(self):
        try:
            receiver_host = self.lstBuddies.currentItem().text()
        except:
            log.warning("no host found from selection")
            return

        msg = self.teMsg.toPlainText()

        receiver_ip = self.users[receiver_host]

        # sending msg to receiver
        self.msg_sender.send_to_ip(receiver_ip, receiver_host, msg.strip())

        # adding my message in chat area in UI
        self.add_chat_msg(
                receiver_ip, receiver_host, "%s: %s" % (self.host, msg))

        # cleaning up textbox for typed message in UI
        self.teMsg.setText("")

    def add_chat_msg(self, ip, other_host, msg):

        self.msg_manager.add_chat_msg(ip, other_host, msg)

        # showing msg in UI
        self.teMsgsList.append(msg)

    def on_buddy_selection_changed(self):
        if self.lstBuddies.count() == 0:
            return

        # no buddy selected
        if not self.lstBuddies.currentItem():
            return

        sel_user = self.lstBuddies.currentItem().text()
        log.debug("You selected buddy is: \"%s\"" % sel_user)

        self.teMsgsList.clear()

        msgs = self.msg_manager.get_message_for_user(sel_user)
        for m in msgs:
            self.teMsgsList.append(m)

    def show_msgbox(self, title, text):
        """
        Function for showing error/info message box
        """
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)
        msg.setText(text)
        msg.setWindowTitle(title)
        msg.setStandardButtons(QMessageBox.Ok)
        retval = msg.exec_()