class Systray(QObject): trayIconMenu = None def __init__(self, parent): super().__init__(parent) self.trayIconMenu = ContextMenu(None) icon = QIcon(":/image/thunder.ico") self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setIcon(icon) self.trayIcon.setContextMenu(self.trayIconMenu) self.trayIcon.setVisible(True) self.trayIcon.activated.connect(self.slotSystrayActivated) @pyqtSlot(QSystemTrayIcon.ActivationReason) def slotSystrayActivated(self, reason): if reason == QSystemTrayIcon.Context: # right pass elif reason == QSystemTrayIcon.MiddleClick: # middle pass elif reason == QSystemTrayIcon.DoubleClick: # double click pass elif reason == QSystemTrayIcon.Trigger: # left if app.mainWin.isHidden() or app.mainWin.isMinimized(): app.mainWin.restore() else: app.mainWin.minimize()
class Icon(QWidget): def __init__(self, app, newEntry, listWin): super().__init__() self.newEntry = newEntry self.listWin = listWin self.app = app self.initUI() def initUI(self): menu = QMenu() Ajouter = QAction(QIcon(''), '&Ajouter un tag', menu) Ajouter.triggered.connect(self.newEntry.show) menu.addAction(Ajouter) ouvrir = QAction(QIcon(''), '&Ouvrir', menu) ouvrir.triggered.connect(self.listWin.show) menu.addAction(ouvrir) Quitter = QAction(QIcon(''), '&Quitter', menu) Quitter.triggered.connect(self.app.exit) menu.addAction(Quitter) self.icon = QSystemTrayIcon() self.icon.setIcon(QIcon('./icone.png')) self.icon.setContextMenu(menu) self.icon.show()
def main(args=sys.argv): NAMESPACE = ' %(module)s.%(name)s.%(funcName)s' FORMAT = '%(asctime)s %(levelname)-8s ' + NAMESPACE + ': %(message)s' color_handler = create_color_handler(FORMAT) LOGFILENAME = os.path.join(os.getenv('HOME'), 'hue_gui.log') file_handler = logging.FileHandler(LOGFILENAME, encoding='utf-8') logging.basicConfig(level=logging.DEBUG, format=FORMAT, handlers=[color_handler, file_handler]) log = logging.getLogger(__name__) app = QApplication(args) trayIcon = QSystemTrayIcon(QIcon(':/icons8-light-on-96.png'), app) hueControl = hue.HueControl() systray = SystrayApp(hueControl) menu = QMenu() toggleAction = menu.addAction('on/off') toggleAction.triggered.connect(systray.toggleAction) colorAction = menu.addAction('color') colorAction.triggered.connect(systray.colorAction) blueAction = menu.addAction('blue') blueAction.triggered.connect(systray.blueAction) redAction = menu.addAction('red') redAction.triggered.connect(systray.redAction) yellowAction = menu.addAction('yellow') yellowAction.triggered.connect(systray.yellowAction) randomAction = menu.addAction('random') randomAction.triggered.connect(systray.randomAction) aboutAction = menu.addAction('about') aboutAction.triggered.connect(systray.aboutAction) exitAction = menu.addAction('Exit') exitAction.triggered.connect(systray.exitAction) trayIcon.setContextMenu(menu) trayIcon.show() return_code = app.exec_() logging.shutdown() return return_code
def __init__(self): QtWidgets.QWidget.__init__(self) self.initPet() # setting menu group settings = QMenu("Settings", self) settings.setIcon(QIcon("resources/ico/poro.ico")) # setting -> draggable self.drag_action = QAction("Draggable", self) self.drag_action.setCheckable(True) self.drag_action.setChecked(DEFAULT_DRAGGABLE) self.drag_action.triggered.connect(self.freezeOrNot) # setting -> opacity slider self.opacity_action = self.initOpacitySlider(QWidgetAction(self)) settings.addAction(self.drag_action) settings.addAction(self.opacity_action) # about and exit action about_action = QAction(QIcon("resources/ico/poro.ico"), "About", self) exit_action = QAction(QIcon("resources/ico/poro.ico"), '&Exit', self) about_action.triggered.connect(self.aboutInfo) exit_action.triggered.connect(qApp.quit) # init the tray_menu tray_menu = QMenu(self) tray_menu.addMenu(settings) tray_menu.addAction(about_action) tray_menu.addSeparator() tray_menu.addAction(exit_action) tray_icon = QSystemTrayIcon(self) tray_icon.setIcon(QIcon("resources/ico/poro.ico")) tray_icon.setContextMenu(tray_menu) tray_icon.show() ClientStatus.init() self.count_false = 0 self.has_client_connected = False # init thread using to monitor lol client self.lol_client_heart_beat_thread = ClientHeartBeat( LOL_CLIENT_HEART_BEAT_RATE) # client 的监听信号 会发给 self.getClientInfo 这个函数 self.lol_client_heart_beat_thread.keeper.connect(self.getClientInfo) self.thread = QThread() # QObject 转 Qthread self.lol_client_heart_beat_thread.moveToThread(self.thread) self.thread.started.connect(self.lol_client_heart_beat_thread.run) self.thread.start() # when the signal got new position, send to ImgCatcher self.client_info_sender.connect(statusChange)
def set_tray_icon(self): tray_icon = QSystemTrayIcon() menu = QMenu() open_action = menu.addAction('Open') exit_action = menu.addAction('Exit') open_action.triggered.connect(self._window.showNormal) exit_action.triggered.connect(sys.exit) tray_icon.setIcon(QIcon(':/resource/resources/icons/system_tray.ico')) tray_icon.setContextMenu(menu) tray_icon.show()
class MainWindow(QMainWindow): tray_icon = None def __init__(self): QMainWindow.__init__(self) self.tray_icon = QSystemTrayIcon(self) self.tray_icon.setIcon(QIcon('icon.png')) self.hide() central_widget = QWidget(self) self.setCentralWidget(central_widget) quit_action = QAction("quit", self) quit_action.triggered.connect(qApp.quit) tray_menu = QMenu() tray_menu.addAction(quit_action) self.tray_icon.setContextMenu(tray_menu) self.tray_icon.show() self.pouple = Pouple() self.history = History() self.bind_keys() def __exit__(self, exc_type, exc_val, exc_tb): self.tray_icon.hide() def command(self, cmd): def execute(): print(cmd.__name__) self.history.add(cmd) cmd() return execute def bind_keys(self): cfg = configparser.RawConfigParser() cfg.read('settings.cfg') hot_keys = { 'align_left': self.pouple.align_left, 'align_right': self.pouple.align_right, 'align_top': self.pouple.align_top, 'align_bottom': self.pouple.align_bottom, 'center': self.pouple.center, 'screen': self.pouple.screen, 'fullscreen': self.pouple.fullscreen, 'undo': self.history.undo, 'redo': self.history.redo, } for name in hot_keys: keyboard.add_hotkey(cfg.get('keys', name), self.command(hot_keys[name]))
def __init__(self): super().__init__() loadUi(get_file_realpath("kdDesktopAssistant.ui"), self) icon = QIcon(get_file_realpath('data/image/logo.png')) self.setWindowIcon(icon) self.setWindowFlag(Qt.FramelessWindowHint) # self.setStyleSheet("#MainWindow{border-image:url("+get_file_realpath("data/image/S60922-232113.jpg").replace("\\","/") +");}") self.gl_apps.setAlignment(Qt.AlignTop) # 右键菜单设置 self.pop_menu = QMenu() # TODO 待实现的右键功能:QAction("导出配置"),QAction("导入配置") self.pop_menu_item = [QAction("新增启动项"),QAction("新增桌面"),QAction("设置背景图片"),QAction("修改桌面"),QAction("删除桌面"),QAction("设置为主桌面"),QAction("小程序模式"),QAction("退出")] self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested[QPoint].connect(self.handle_pop_menu) # 系统托盘 sys_tray = QSystemTrayIcon(self) self.sys_tray = sys_tray sys_tray.setIcon(icon) sys_tray.activated.connect(self.sys_tray_handler) sys_tray_menu = QMenu() sys_tray_menu.menu_items =[ QAction("退出",self,triggered=sys.exit)] sys_tray.setContextMenu(sys_tray_menu) sys_tray.show() self.session_bt_size_policy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.session_btn_size = QSize(100, 25) # 初始化桌面 print(QDesktopWidget().screenGeometry().height()) print(self.gl_apps.geometry().height()) self.vetical_widget_number = math.floor((QDesktopWidget().screenGeometry().height() - 150) / get_option_int("item_size")) self.home_session = get_option_value("home_session") self.init_session() # 初始化对话框对象 self.dl_launch_item_detail = dl_launch_item_detail() self.session = session() self.wg_catelog = QFrame() self.wg_catelog.setWindowFlags(Qt.Popup) self.gl_catelog = QGridLayout() self.wg_catelog.setLayout(self.gl_catelog) # self.gl_catelog.setWindowOpacity(0.5) # self.wg_catelog.setAttribute(Qt.WA_TranslucentBackground,True ) self.wg_catelog.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool) self.wg_catelog.setAutoFillBackground(True) # self.wg_catelog.setStyleSheet("background-image:url("+get_file_realpath("data/image/bg_catelog.gif") + ");background-size:cover;}") p = QPalette() p.setBrush(QPalette.Background, QBrush(QPixmap(get_file_realpath("data/image/bg_catelog.jpg")))) self.wg_catelog.setPalette(p)
def _createTrayIcon(self): trayIcon = QSystemTrayIcon(self.mw) ankiLogo = QIcon() ankiLogo.addPixmap(QPixmap(":/icons/anki.png"), QIcon.Normal, QIcon.Off) trayIcon.setIcon(QIcon.fromTheme("anki", ankiLogo)) trayMenu = QMenu(self.mw) trayIcon.setContextMenu(trayMenu) showAction = trayMenu.addAction("Show all windows") showAction.triggered.connect(self.showAll) trayMenu.addAction(self.mw.form.actionExit) trayIcon.activated.connect(self.onActivated) return trayIcon
def init_tray(self): tray = QSystemTrayIcon() # 设置图标 tray.setIcon(QIcon("E:\pypro\chat\client\res\icon\setting.png")) # 托盘菜单 tray_menu = QMenu() # 增加动作 tray_menu.addAction(QAction("还原", self, triggered=self.showNormal)) tray_menu.addAction(QAction("退出", self, triggered=self.close)) # 应用菜单 tray.setContextMenu(tray_menu) # 显示托盘 tray.show()
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
def main(): d = getDateTime() # Global app app = QApplication(sys.argv) QApplication.setQuitOnLastWindowClosed(False) qIcon = QIcon('icons/shotty.png') app.setWindowIcon(qIcon) shotty = ShottyFullscreen() showNotification('Shotty', 'Running in the background') tray = QSystemTrayIcon() if tray.isSystemTrayAvailable(): tray.setIcon(QIcon('icons/shotty.png')) tray.setVisible(True) tray.show() # Add a menu trayMenu = QMenu() region_screenshot_action = QAction(QIcon("icons/screenshot.png"), 'Take region screenshot') full_screenshot_action = QAction(QIcon("icons/screenshot.png"), 'Take screenshot') settings_action = QAction(QIcon("icons/settings.png"), 'Settings') about_action = QAction(QIcon("icons/info.png"), 'About') exit_action = QAction(QIcon("icons/exit.png"), 'Exit Shoty') exit_action.triggered.connect(app.exit) about_action.triggered.connect(shotty.showShottyAboutWindow) region_screenshot_action.triggered.connect(shotty.initUI) # We need to pass checked because connect passes # a bool arg as first param full_screenshot_action.triggered.connect( lambda checked, date=getDateTime( ), x1=-1, y1=-1, x2=-1, y2=-1, im=screenshot( ): shotty.saveScreenShot(date, x1, y1, x2, y2, im=im[:, :, :3])) trayMenu.addAction(region_screenshot_action) trayMenu.addAction(full_screenshot_action) trayMenu.addAction(settings_action) trayMenu.addAction(about_action) trayMenu.addAction(exit_action) tray.setContextMenu(trayMenu) else: print("[ERROR] Can't instantiate tray icon") sys.exit(app.exec_())
class ProgramTray(QtCore.QThread): def __init__(self, icon): QtCore.QThread.__init__(self) self.menu = QMenu() self.icon = QSystemTrayIcon(QtGui.QIcon(icon)) self.flag_exit = True self.first = True def run(self): if chrome_cheker(): p1 = Popen( '"{}" --remote-debugging-port=9222 & exit'.format(chrome_path), shell=True, stdout=PIPE) time.sleep(2) p1.kill() try: bot.delete_webhook() bot.polling(none_stop=True) except: bot.send_message(white_list[0], 'Программа выключенна. Сбой') else: if self.first: self.first = False bot.send_message( white_list[0], 'Полностью закройте Chrome и запустите программу') print('Кажется я умираю') QApplication.quit() def stop(self): QApplication.quit() def setMenu(self, menu=None): if not menu: menu = [] collection = OrderedDict(menu) items = collection.keys() functions = collection.values() for i, item in enumerate(items): function = functions[i] if isinstance(function, types.MethodType) \ or isinstance(function, types.FunctionType): self.menu.addAction(QAction(item, self, triggered=function)) self.quitAction = QAction("Exit", self, triggered=self.stop) self.menu.addAction(self.quitAction) self.icon.setContextMenu(self.menu)
class TrayIcon(QApplication): """System tray icon, Qt version.""" def __init__(self, icon, fallback_icon_path, **kwargs): """Create a TrayIcon instance.""" QApplication.__init__(self, sys.argv) self._fallback_icon = QIcon(fallback_icon_path) self._icon = QIcon.fromTheme(icon, self._fallback_icon) self.tray_icon = QSystemTrayIcon() self.tray_icon.setIcon(self._icon) self.menu = SubMenu() self.tray_icon.setContextMenu(self.menu) self.tray_icon.show() def loop(self, tk_window): """Update Qt GUI inside tkinter mainloop.""" self.processEvents() tk_window.loop_id = tk_window.after(10, self.loop, tk_window) def change_icon(self, icon, desc=''): """Change icon.""" del self._icon self._icon = QIcon(icon) self.tray_icon.setIcon(self._icon) def bind_left_click(self, command): """Bind command to left click on the icon.""" def action(reason): """Execute command only on left click (not when the menu is displayed).""" if reason == QSystemTrayIcon.Trigger: command() self.tray_icon.activated.connect(action) def bind_middle_click(self, command): """Bind command to middle click on the icon.""" def action(reason): """Execute command only on middle click (not when the menu is displayed).""" if reason == QSystemTrayIcon.MiddleClick: command() self.tray_icon.activated.connect(action) def bind_double_click(self, command): """Bind command to double left click on the icon.""" def action(reason): """Execute command only on double click (not when the menu is displayed).""" if reason == QSystemTrayIcon.DoubleClick: command() self.tray_icon.activated.connect(action)
class App(QWidget): def __init__(self): super().__init__() self.move(180, 100) self.setFixedSize(400, 300) self.setWindowTitle('control') self.server = threading.Thread(target=server) self.toclose = False self.all_buttons = [] self.start_button = QPushButton('start/stop', parent=self) self.start_button.setGeometry(100, 100, 200, 100) self.start_button.action = 'start' self.all_buttons.append(self.start_button) for i in self.all_buttons: i.clicked.connect(self.onClick) show_action = QAction("Show", self) quit_action = QAction("Exit", self) show_action.triggered.connect(self.show) quit_action.triggered.connect(qApp.quit) tray_menu = QMenu() tray_menu.addAction(show_action) 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() def onClick(self): btn = self.sender().action print(btn, 'action released!') if btn == 'start': if not self.server.is_alive(): self.server.start() self.start_button.setEnabled(0) self.start_button.setText('server running') def closeEvent(self, evnt): if self.toclose: super(App, self).closeEvent(evnt) else: evnt.ignore() self.hide()
class TrayIcon(QApplication): def __init__(self, icon): QApplication.__init__(self, sys.argv) # Init QSystemTrayIcon self.icon = QIcon(icon) self.tray_icon = QSystemTrayIcon() self.tray_icon.setIcon(self.icon) self.menu = QMenu() self.menu_items = [] self.tray_icon.setContextMenu(self.menu) self.tray_icon.show() def add_menu_separator(self): self.menu.addSeparator() def add_menu_item(self, label="", command=None): action = QAction(label, self.tray_icon) action.triggered.connect(lambda *args: command()) self.menu.addAction(action) self.menu_items.append(action) def loop(self, tk_window): self.processEvents() tk_window.loop_id = tk_window.after(10, self.loop, tk_window) def change_icon(self, icon, desc): del self.icon self.icon = QIcon(icon) self.tray_icon.setIcon(self.icon) def get_item_label(self, item): return self.menu_items[item].text() def set_item_label(self, item, label): self.menu_items[item].setText(label) def disable_item(self, item): self.menu_items[item].setDisabled(True) def enable_item(self, item): self.menu_items[item].setDisabled(False) def bind_left_click(self, command): def action(reason): """Execute command only on click (not when the menu is displayed).""" if reason == QSystemTrayIcon.Trigger: command() self.tray_icon.activated.connect(action)
class Tray: # create tray icon menu def __init__(self, ): # create systemtray UI self.icon = QIcon(ico) self.tray = QSystemTrayIcon() self.tray.setIcon(self.icon) self.tray.setToolTip("Now Playing ▶") self.tray.setVisible(True) self.menu = QMenu() # create systemtray options and actions self.actTitle = QAction("Now Playing v1.4") self.menu.addAction(self.actTitle) self.actTitle.setEnabled(False) self.actConfig = QAction("Settings") self.actConfig.triggered.connect(win.show) self.menu.addAction(self.actConfig) self.menu.addSeparator() self.actPause = QAction() self.actPause.triggered.connect(self.pause) self.menu.addAction(self.actPause) self.actPause.setEnabled(False) self.actExit = QAction("Exit") self.actExit.triggered.connect(self.cleanquit) self.menu.addAction(self.actExit) # add menu to the systemtray UI self.tray.setContextMenu(self.menu) def unpause(self): # unpause polling global paused paused = 0 self.actPause.setText('Pause') self.actPause.triggered.connect(self.pause) def pause(self): # pause polling global paused paused = 1 self.actPause.setText('Resume') self.actPause.triggered.connect(self.unpause) def cleanquit(self): # quit app and cleanup self.tray.setVisible(False) file = ConfigFile(config, config_file).file if file: writetrack(file) sys.exit()
class TrayWindow(QDialog): def __init__(self, main_window, *args, **kwargs): super(TrayWindow, self).__init__(*args, **kwargs) self.createActions() self.createTrayIcon() self.trayIcon.show() self.settingsWindow = main_window def createActions(self): self.settingsAction = QAction("Settings", self, triggered=self.showSettings) self.quitAction = QAction("Quit", self, triggered=QApplication.instance().quit) def createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.settingsAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu) self.trayIcon.activated.connect(self.iconActivated) icon = QIcon(getResource("speechOn.png")) self.trayIcon.setIcon(icon) self.setWindowIcon(icon) self.trayIcon.setToolTip("MicroCommander") def iconActivated(self, reason): self.showNotification("Micro commander", "Status: running") def showSettings(self): self.settingsWindow.show() @staticmethod def showNotification(title, body): trayIcon = QSystemTrayIcon() trayIcon.setIcon(QIcon(getResource("speech.png"))) trayIcon.show() icon = QSystemTrayIcon.MessageIcon(QSystemTrayIcon.Information) trayIcon.showMessage(title, body, icon) @staticmethod def show_message(tray, title, body): icon = QSystemTrayIcon.MessageIcon(QSystemTrayIcon.Information) tray.showMessage(title, body, icon)
class tray(): def __init__(self): super().__init__() self.initUI() def turn_off(self,device): is_list = isinstance(device,list) if is_list == False: return device.turn_off() else: return [i.turn_off() for i in device] def turn_on(self,device): is_list = isinstance(device,list) if is_list == False: return device.turn_on() else: return[ i.turn_on() for i in device] def initUI(self): self.app = QApplication(sys.argv) api.init('LOGIN','PASSWORD',"44","tuya") self.device_ids = api.get_all_devices() self.devices = dict((i.name(),i) for i in self.device_ids if i.obj_type == 'light' or i.obj_type == 'switch') self.lights = dict((i.name(),i) for i in self.device_ids if i.obj_type == 'light') self.devices['All Lights'] = list(self.lights.values()) self.tray_icon = QSystemTrayIcon(QIcon('lightbulb.png'),parent=self.app) self.tray_icon.setToolTip('Tuya - Lights') self.menu = QMenu() self.menus = dict() self.buttons = dict() for j in self.devices.keys(): self.menus[f"{j}_Action"] = self.menu.addMenu(j) self.menus[f"{j}_Action"].addAction('On') self.menus[f"{j}_Action"].addAction('Off') self.buttons = self.menus[f"{j}_Action"].actions() for i in self.buttons: if i.iconText() == 'On': i.triggered.connect(partial(self.turn_on,self.devices[j])) elif i.iconText() == 'Off': i.triggered.connect(partial(self.turn_off,self.devices[j])) self.exitaction = self.menu.addAction('Exit') self.exitaction.triggered.connect(self.app.quit) self.tray_icon.setContextMenu(self.menu) self.tray_icon.show() self.app.exec_() sys.exit(self.app.exec_())
class Main(QDialog): def __init__(self): super().__init__() self.loadMenu() self.initUI() def loadMenu(self): menuItems = [] # 菜单列表 menuItems.append({ "text": "启动", "icon": "./icons/set.png", "event": self.show, "hot": "D" }) menuItems.append({ "text": "退出", "icon": "./icons/close.png", "event": self.close, "hot": "Q" }) self.trayIconMenu = QMenu(self) # 创建菜单 #遍历绑定 显示的文字、图标、热键和点击事件 #热键可能是无效的 我这里只是为了显示效果而已 for i in menuItems: tmp = QAction(QIcon(i["icon"]), i["text"], self, triggered=i["event"]) tmp.setShortcut(self.tr(i["hot"])) self.trayIconMenu.addAction(tmp) def initUI(self): self.trayIcon = QSystemTrayIcon(self) # <===创建通知栏托盘图标 self.trayIcon.setIcon(QIcon("./joyrun/request/pic.ico")) #<===设置托盘图标 self.trayIcon.setContextMenu(self.trayIconMenu) #<===创建右键连接菜单 self.trayIcon.show() #<====显示托盘 self.setWindowIcon(QIcon("./joyrun/request/pic.ico")) #<===设置窗体图标 self.setGeometry(300, 300, 180, 300) # <===设置窗体打开位置与宽高 self.setWindowTitle('窗体标题') self.show() #<====显示窗体 # self.hide()#<====隐藏窗体 # 默认不显示窗体 # 重写窗体关闭事件,让其点击关闭时隐藏 def closeEvent(self, event): if self.trayIcon.isVisible(): self.trayIcon.hide()
class systemTray(QObject): cs = check_service def start_tray(self, ns, ip): # Initialize QApplication self.app = QApplication([]) self.app.setQuitOnLastWindowClosed(False) # Check service status ipc, state = self.cs(ns, ip) # Set icon icon = QIcon(srcdir + ipc) self.tray = QSystemTrayIcon() self.tray.setIcon(icon) self.tray.setVisible(True) self.tray.setToolTip(ns + " is " + state) # Set menu self.menu = QMenu() self.quit = QAction("Exit") self.quit.triggered.connect(self.app.quit) self.menu.addAction(self.quit) # Add menu to tray self.tray.setContextMenu(self.menu) # Create thread self.thread = QThread() # Create object which will be moved from main thread to worker thread self.worker = worker(ns, ip) # Move object to worker thread self.worker.moveToThread(self.thread) # Connect object to signal self.worker.newIcon.connect(self.updateIcon) self.worker.newToolTip.connect(self.updateToolTip) # Connect started signal to run method of object in worker thread self.thread.started.connect(self.worker.run) # Start thread self.thread.start() # Run tray self.app.exec_() def updateIcon(self, icon): self.tray.setIcon(icon) def updateToolTip(self, tooltip): self.tray.setToolTip(tooltip)
class TrayIcon(QWidget): #child class name def __init__(self): #class constructor super().__init__(); #inheritance from parent attributes self.tray=QSystemTrayIcon(QtGui.QIcon('chidalu.jpg')); #create a systemtrayicon self.tray.setToolTip('Click Me'); #set tooltip of trayicon self.tray.show(); #show trayicon menu=QMenu(self); #create a menu object exitAction=QAction('Exit'); #create an exit action exitAction.triggered.connect(app.quit); #connect a signal to a slot menu.addAction(exitAction); #add action to menu self.tray.setContextMenu(menu); #set context menu of trayicon
def init_tray(self, app): actions = { self.open_action: self.show, self.hide_action: self.hide, self.close_action: self.quit_app } tray_menu = QMenu() for key, value in actions.items(): # noinspection PyUnresolvedReferences key.triggered.connect(value) tray_menu.addAction(key) tray_icon = QSystemTrayIcon(self.settings.app_icon(), app) tray_icon.setContextMenu(tray_menu) return tray_icon
def apiTray(): app = QtWidgets.QApplication(sys.argv) scriptDir = os.path.dirname(os.path.realpath(__file__)) trayIcon = QSystemTrayIcon(QtGui.QIcon(scriptDir + "/resources/logo.png")) trayIcon.setToolTip("AmazonWatch") trayIcon.show() menu = QMenu() openAction = menu.addAction("Open GUI") openAction.triggered.connect(startGuiProcess) menu.addSeparator() actionGithub = menu.addAction("Visit Github") actionGithub.triggered.connect(openBrowser) menu.addSeparator() exitAction = menu.addAction("Exit") exitAction.triggered.connect(app.quit) trayIcon.setContextMenu(menu) sys.exit(app.exec_())
def set_tray(self): tray_icon = QSystemTrayIcon(self) icon = QIcon("icon.png") tray_icon.setIcon(icon) show_action = QAction("Показать", self) quit_action = QAction("Выйти", self) hide_action = QAction("Спрятать", self) show_action.triggered.connect(self.show) hide_action.triggered.connect(self.hide) quit_action.triggered.connect(self.close) tray_menu = QMenu() tray_menu.addAction(show_action) tray_menu.addAction(hide_action) tray_menu.addAction(quit_action) tray_icon.setContextMenu(tray_menu) tray_icon.show() tray_icon.setToolTip('Address book')
def set_tray(self): tray_icon = QSystemTrayIcon(self) icon = QIcon("icon.png") tray_icon.setIcon(icon) show_action = QAction("Show", self) quit_action = QAction("Exit", self) hide_action = QAction("Hide", self) show_action.triggered.connect(self.show) hide_action.triggered.connect(self.hide) quit_action.triggered.connect(self.close) tray_menu = QMenu() tray_menu.addAction(show_action) tray_menu.addAction(hide_action) tray_menu.addAction(quit_action) tray_icon.setContextMenu(tray_menu) tray_icon.show() tray_icon.setToolTip('Solar combine')
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 main(UI): def __init__(self, parent=None): super(UI, self).__init__() self.setupUi(parent) self.parent = parent #print type(self).__name__ print self.lUsername #self.minimizeAction = QAction("Mi&nimize", parent, triggered=parent.hide) self.createActions() self.createTrayIcon() self.setIcon(QIcon(':/images/heart.png')) self.trayIcon.show() # setup gui events self.events() def createActions(self): #self.minimizeAction = QAction("Mi&nimize", self.parent, triggered=self.parent.hide) #self.maximizeAction = QAction("Ma&ximize", self.parent, triggered=self.parent.showMaximized) #self.restoreAction = QAction("&Restore", self.parent, triggered=self.parent.showNormal) self.quitAction = QAction("&Quit", self.parent, triggered=QApplication.instance().quit) def createTrayIcon(self): self.trayIconMenu = QMenu(self.parent) #self.trayIconMenu.addAction(self.minimizeAction) #self.trayIconMenu.addAction(self.maximizeAction) #self.trayIconMenu.addAction(self.restoreAction) #self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self.parent) self.trayIcon.setContextMenu(self.trayIconMenu) def setIcon(self, icon): self.trayIcon.setIcon(icon) self.parent.setWindowIcon(icon) def events(self): self.bCancelOk.button(self.bCancelOk.Ok).clicked.connect( QApplication.instance().quit) self.bCancelOk.button(self.bCancelOk.Cancel).clicked.connect( QApplication.instance().quit)
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()
class SystemTray(object): # 程序托盘类 def __init__(self, w): self.app = app self.w = w QApplication.setQuitOnLastWindowClosed(False) # 禁止默认的closed方法, self.w.show() # 不设置显示则为启动最小化到托盘 self.tp = QSystemTrayIcon(self.w) self.initUI() self.run() def initUI(self): # 设置托盘图标 self.tp.setIcon(QIcon('/etc/v2rayL/images/logo.ico')) def quitApp(self): # 退出程序 self.w.show() # w.hide() #设置退出时是否显示主窗口 re = QMessageBox.question(self.w, "提示", "确认退出?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if re == QMessageBox.Yes: self.tp.setVisible(False) # 隐藏托盘控件 qApp.quit() # 退出程序 self.w.v2rayL.disconnect() def act(self, reason): # 主界面显示方法 if reason == 2 or reason == 3: self.w.show() def run(self): a1 = QAction('恢复(Show)', triggered=self.w.show) a3 = QAction('退出(Exit)', triggered=self.quitApp) tpMenu = QMenu() tpMenu.addAction(a1) tpMenu.addAction(a3) self.tp.setContextMenu(tpMenu) self.tp.show() # 不调用show不会显示系统托盘消息,图标隐藏无法调用 # 绑定提醒信息点击事件 # self.tp.messageClicked.connect(self.message) # 绑定托盘菜单点击事件 self.tp.activated.connect(self.act) sys.exit(self.app.exec_()) # 持续对app的连接
def _create_systemtray(self): ''' Create & add systemtray icon with controls. ''' if self._systemtray: return systemtray = QSystemTrayIcon(self) systemtray.setToolTip(app_data.app_name()) systemtray.setIcon(QIcon(app_data.app_logo_path())) systemtray.setVisible(True) menu = QMenu(self) about_action = menu.addAction(QIcon(app_data.about_logo_path()), "About") about_action.triggered.connect(self._about_clicked) exclusions_action = menu.addAction(QIcon(app_data.exclusions_logo_path()), "Exclusions") exclusions_action.triggered.connect(self._exclusions_clicked) quit_action = menu.addAction(QIcon(app_data.quit_logo_path()), "Quit") quit_action.triggered.connect(self._quit_clicked) systemtray.setContextMenu(menu) self._systemtray = systemtray
class AppUi(QtCore.QObject): reload_data = QtCore.pyqtSignal() def __init__(self, parent, conf, build_icons): super(AppUi, self).__init__(parent) self.widget = QWidget() self.build_icons = build_icons self.tray = QSystemTrayIcon(self.build_icons.for_status(None), self.widget) self.tray.show() self.app_menu = AppMenu(self.widget, conf, self.build_icons) self.app_menu.reload_data.connect(self.reload_data) self.tray.setContextMenu(self.app_menu.menu) def update_projects(self, integration_status): count = len(integration_status.get_failing_builds()) self.tray.setIcon(self.build_icons.for_aggregate_status(integration_status.get_build_status(), count)) self.app_menu.update(integration_status.get_projects()) self.tray.setToolTip("Last checked: " + strftime("%Y-%m-%d %H:%M:%S"))
class main(QDialog): def __init__(self): super().__init__() self.trayIcon = QSystemTrayIcon(self) # <===创建通知栏托盘图标 self.trayIconMenu = QMenu(self) # 创建菜单 self.loadMenu() self.initUI() # self.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint) # self.tray.activated.connect(self.TuoPanEvent) #设置托盘点击事件处理函数 def loadMenu(self): menuItems = [{"text": "启动", "icon": "icons/set.png", "event": self.show, "hot": "D"}, {"text": "退出", "icon": "icons/switch.png", "event": self.close, "hot": "Q"}] # 菜单列表 # 遍历绑定 显示的文字、图标、热键和点击事件 # 热键可能是无效的 我这里只是为了显示效果而已 for i in menuItems: tmp = QAction(QIcon(i["icon"]), i["text"], self, triggered=i["event"]) tmp.setShortcut(self.tr(i["hot"])) self.trayIconMenu.addAction(tmp) def initUI(self): self.trayIcon.setIcon(QIcon("icons/alert.png")) # <===设置托盘图标 self.trayIcon.setContextMenu(self.trayIconMenu) # <===创建右键连接菜单 self.trayIcon.show() # <====显示托盘 # self.trayIcon.setToolTip('aa') self.setWindowIcon(QIcon("icons/alert.png")) # <===设置窗体图标 self.setGeometry(900, 300, 180, 300) # <===设置窗体打开位置与宽高 self.setWindowTitle('订单列表') # self.show() # <====显示窗体 # self.hide()#<====隐藏窗体 # 默认不显示窗体 def close(self): sys.exit(0) # 重写窗体关闭事件,让其点击关闭时隐藏 def closeEvent(self, event): if self.trayIcon.isVisible(): self.hide() event.ignore() else: event.accept()
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 Systray(QObject): app = None trayIconMenu = None def __init__(self, app): super().__init__() self.app = app self.mainWin = self.app.mainWin self.trayIconMenu = QMenu(None) icon = QIcon(":/image/thunder.ico") self.trayIcon = QSystemTrayIcon(None) self.trayIcon.setIcon(icon) self.trayIcon.setContextMenu(self.trayIconMenu) self.trayIcon.setVisible(True) self.trayIcon.activated.connect(self.slotSystrayActivated) self.app.lastWindowClosed.connect(self.slotTeardown) self.trayIconMenu.addAction(self.mainWin.action_exit) @pyqtSlot() def slotTeardown(self): print("teardown Systray") # On Ubuntu 13.10, systrayicon won't destroy itself gracefully, stops the whole program from exiting. del self.trayIcon @pyqtSlot(QSystemTrayIcon.ActivationReason) def slotSystrayActivated(self, reason): if reason == QSystemTrayIcon.Context: # right pass elif reason == QSystemTrayIcon.MiddleClick: # middle pass elif reason == QSystemTrayIcon.DoubleClick: # double click pass elif reason == QSystemTrayIcon.Trigger: # left if self.mainWin.isHidden() or self.mainWin.isMinimized(): self.mainWin.restore() else: self.mainWin.minimize()
class Window(QDialog): def __init__(self): super(Window, self).__init__() self.createIconGroupBox() self.createMessageGroupBox() self.iconLabel.setMinimumWidth(self.durationLabel.sizeHint().width()) self.createActions() self.createTrayIcon() self.showMessageButton.clicked.connect(self.showMessage) self.showIconCheckBox.toggled.connect(self.trayIcon.setVisible) self.iconComboBox.currentIndexChanged.connect(self.setIcon) self.trayIcon.messageClicked.connect(self.messageClicked) self.trayIcon.activated.connect(self.iconActivated) mainLayout = QVBoxLayout() mainLayout.addWidget(self.iconGroupBox) mainLayout.addWidget(self.messageGroupBox) self.setLayout(mainLayout) self.iconComboBox.setCurrentIndex(1) self.trayIcon.show() self.setWindowTitle("Systray") self.resize(400, 300) def setVisible(self, visible): self.minimizeAction.setEnabled(visible) self.maximizeAction.setEnabled(not self.isMaximized()) self.restoreAction.setEnabled(self.isMaximized() or not visible) super(Window, self).setVisible(visible) def closeEvent(self, event): if self.trayIcon.isVisible(): QMessageBox.information(self, "Systray", "The program will keep running in the system tray. To " "terminate the program, choose <b>Quit</b> in the " "context menu of the system tray entry.") self.hide() event.ignore() def setIcon(self, index): icon = self.iconComboBox.itemIcon(index) self.trayIcon.setIcon(icon) self.setWindowIcon(icon) self.trayIcon.setToolTip(self.iconComboBox.itemText(index)) def iconActivated(self, reason): if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick): self.iconComboBox.setCurrentIndex( (self.iconComboBox.currentIndex() + 1) % self.iconComboBox.count()) elif reason == QSystemTrayIcon.MiddleClick: self.showMessage() def showMessage(self): icon = QSystemTrayIcon.MessageIcon( self.typeComboBox.itemData(self.typeComboBox.currentIndex())) self.trayIcon.showMessage(self.titleEdit.text(), self.bodyEdit.toPlainText(), icon, self.durationSpinBox.value() * 1000) def messageClicked(self): QMessageBox.information(None, "Systray", "Sorry, I already gave what help I could.\nMaybe you should " "try asking a human?") def createIconGroupBox(self): self.iconGroupBox = QGroupBox("Tray Icon") self.iconLabel = QLabel("Icon:") self.iconComboBox = QComboBox() self.iconComboBox.addItem(QIcon(':/images/bad.png'), "Bad") self.iconComboBox.addItem(QIcon(':/images/heart.png'), "Heart") self.iconComboBox.addItem(QIcon(':/images/trash.png'), "Trash") self.showIconCheckBox = QCheckBox("Show icon") self.showIconCheckBox.setChecked(True) iconLayout = QHBoxLayout() iconLayout.addWidget(self.iconLabel) iconLayout.addWidget(self.iconComboBox) iconLayout.addStretch() iconLayout.addWidget(self.showIconCheckBox) self.iconGroupBox.setLayout(iconLayout) def createMessageGroupBox(self): self.messageGroupBox = QGroupBox("Balloon Message") typeLabel = QLabel("Type:") self.typeComboBox = QComboBox() self.typeComboBox.addItem("None", QSystemTrayIcon.NoIcon) self.typeComboBox.addItem(self.style().standardIcon( QStyle.SP_MessageBoxInformation), "Information", QSystemTrayIcon.Information) self.typeComboBox.addItem(self.style().standardIcon( QStyle.SP_MessageBoxWarning), "Warning", QSystemTrayIcon.Warning) self.typeComboBox.addItem(self.style().standardIcon( QStyle.SP_MessageBoxCritical), "Critical", QSystemTrayIcon.Critical) self.typeComboBox.setCurrentIndex(1) self.durationLabel = QLabel("Duration:") self.durationSpinBox = QSpinBox() self.durationSpinBox.setRange(5, 60) self.durationSpinBox.setSuffix(" s") self.durationSpinBox.setValue(15) durationWarningLabel = QLabel("(some systems might ignore this hint)") durationWarningLabel.setIndent(10) titleLabel = QLabel("Title:") self.titleEdit = QLineEdit("Cannot connect to network") bodyLabel = QLabel("Body:") self.bodyEdit = QTextEdit() self.bodyEdit.setPlainText("Don't believe me. Honestly, I don't have " "a clue.\nClick this balloon for details.") self.showMessageButton = QPushButton("Show Message") self.showMessageButton.setDefault(True) messageLayout = QGridLayout() messageLayout.addWidget(typeLabel, 0, 0) messageLayout.addWidget(self.typeComboBox, 0, 1, 1, 2) messageLayout.addWidget(self.durationLabel, 1, 0) messageLayout.addWidget(self.durationSpinBox, 1, 1) messageLayout.addWidget(durationWarningLabel, 1, 2, 1, 3) messageLayout.addWidget(titleLabel, 2, 0) messageLayout.addWidget(self.titleEdit, 2, 1, 1, 4) messageLayout.addWidget(bodyLabel, 3, 0) messageLayout.addWidget(self.bodyEdit, 3, 1, 2, 4) messageLayout.addWidget(self.showMessageButton, 5, 4) messageLayout.setColumnStretch(3, 1) messageLayout.setRowStretch(4, 1) self.messageGroupBox.setLayout(messageLayout) def createActions(self): self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide) self.maximizeAction = QAction("Ma&ximize", self, triggered=self.showMaximized) self.restoreAction = QAction("&Restore", self, triggered=self.showNormal) self.quitAction = QAction("&Quit", self, triggered=QApplication.instance().quit) def createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.minimizeAction) self.trayIconMenu.addAction(self.maximizeAction) self.trayIconMenu.addAction(self.restoreAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu)
class Coropata(QLabel): clicked = pyqtSignal() def __init__(self): # Really not meant to have a parent yet QLabel.__init__(self, None) self.quitAction = None self.trayIconMenu = None self.trayIcon = None self.createTrayIcon() self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.BypassWindowManagerHint) self.setAttribute(Qt.WA_TranslucentBackground) self.m_hoverbox = Hoverbox() self.m_hoverbox.show() self.m_animation_timer = QTimer(self) self.m_animation_timer.setInterval(100) self.m_physics_timer = QTimer(self) self.m_physics_timer.setInterval(30) self.m_physics_timer.timeout.connect(self.physicsTimerEvent) self.screen_width = QDesktopWidget().screenGeometry().width() self.screen_height = QDesktopWidget().screenGeometry().height() # Initialize properties self.m_velocity = QPoint(0, 0) self.m_pos = QPoint(self.screen_width / 2, (5.0 / 6.0) * self.screen_height) self.m_offset = QPoint(0, 0) self.m_size = QSize(0, 0) self._velocity = self.m_velocity self._pos = self.m_pos self._offset = self.m_offset self._size = self.m_size self.stateMachine = xmlToStateMachine(self, 'coropata.xml') self.stateMachine.start() self.m_animation_timer.start() self.m_physics_timer.start() # Properties ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @pyqtProperty(QSize) def _size(self): return self.m_size @_size.write def _size(self, value): self.m_size = value self.setFixedSize(value) @pyqtProperty(QPoint) def _offset(self): return self.m_offset @_offset.write def _offset(self, value): self.m_offset = value self.move(self.m_pos + self.m_offset) @pyqtProperty(QPoint) def _pos(self): return self.m_pos @_pos.write def _pos(self, value): self.m_pos = value self.move(self.m_pos + self.m_offset) self.m_hoverbox.move(self.m_pos - QPoint(60, 70)) @pyqtProperty(QPoint) def _velocity(self): return self.m_velocity @_velocity.write def _velocity(self, value): self.m_velocity = value # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def mousePressEvent(self, e): self.clicked.emit() def closeEvent(self, e): QApplication.instance().closeAllWindows() def keyPressEvent(self, e): if e.key() == Qt.Key_Q or e.key() == Qt.Key_Escape: QApplication.instance().closeAllWindows() def physicsTimerEvent(self): p = self.m_pos + self.m_velocity if p.x() < -self._size.width(): p.setX(self.screen_width) elif p.x() > self.screen_width + 10: p.setX(-self._size.width()) self._pos = p def createTrayIcon(self): self.quitAction = QAction('Quit', self) self.quitAction.triggered.connect(self.closeEvent) self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu) self.trayIcon.setIcon(QIcon(os.path.join('images', 'flower.png'))) self.trayIcon.show()
from PyQt5.QtGui import QIcon, QClipboard, QKeySequence import cpf def gerar_cpf(): clip = QApplication.clipboard() clip.setText(cpf.gerar_cpf(False)) def gerar_cpf_formatado(): clip = QApplication.clipboard() clip.setText(cpf.gerar_cpf(True)) def buscar_ip(): clip = QApplication.clipboard() clip.setText(ipgetter.myip()) if __name__ == '__main__': app = QApplication([]) icon = QSystemTrayIcon(QIcon("img/icon.png")) menu = QMenu() menu.addAction("Gerar cpf sem formatação", gerar_cpf) menu.addAction("Gerar cpf com formatação", gerar_cpf_formatado) menu.addSeparator() menu.addAction("Buscar id público", buscar_ip) menu.addSeparator() menu.addAction("Fechar", app.quit) icon.setContextMenu(menu) icon.show() app.exec_()
class Sansimera(QMainWindow): def __init__(self, parent=None): super(Sansimera, self).__init__(parent) self.settings = QSettings() self.timer = QTimer(self) self.timer_reminder = QTimer(self) self.timer_reminder.timeout.connect(self.reminder_tray) interval = self.settings.value('Interval') or '1' if interval != '0': self.timer_reminder.start(int(interval) * 60 * 60 * 1000) self.tentatives = 0 self.gui() self.lista = [] self.lista_pos = 0 self.eortazontes_shown = False self.eortazontes_names = '' def gui(self): self.systray = QSystemTrayIcon() self.icon = QIcon(':/sansimera.png') self.systray.setIcon(self.icon) self.systray.setToolTip('Σαν σήμερα...') self.menu = QMenu() self.exitAction = QAction('&Έξοδος', self) self.refreshAction = QAction('&Ανανέωση', self) self.aboutAction = QAction('&Σχετικά', self) self.notification_interval = QAction('Ει&δοποίηση εορταζόντων', self) self.menu.addAction(self.notification_interval) self.menu.addAction(self.refreshAction) self.menu.addAction(self.aboutAction) self.menu.addAction(self.exitAction) self.systray.setContextMenu(self.menu) self.notification_interval.triggered.connect(self.interval_namedays) self.exitAction.triggered.connect(exit) self.refreshAction.triggered.connect(self.refresh) self.aboutAction.triggered.connect(self.about) self.browser = QTextBrowser() self.browser.setOpenExternalLinks(True) self.setGeometry(600, 500, 400, 300) self.setWindowIcon(self.icon) self.setWindowTitle('Σαν σήμερα...') self.setCentralWidget(self.browser) self.systray.show() self.systray.activated.connect(self.activate) self.browser.append('Λήψη...') nicon = QIcon(':/next') picon = QIcon(':/previous') ricon = QIcon(':/refresh') iicon = QIcon(':/info') qicon = QIcon(':/exit') inicon = QIcon(':/notifications') self.nextAction = QAction('Επόμενο', self) self.nextAction.setIcon(nicon) self.previousAction = QAction('Προηγούμενο', self) self.refreshAction.triggered.connect(self.refresh) self.nextAction.triggered.connect(self.nextItem) self.previousAction.triggered.connect(self.previousItem) self.previousAction.setIcon(picon) self.refreshAction.setIcon(ricon) self.exitAction.setIcon(qicon) self.aboutAction.setIcon(iicon) self.notification_interval.setIcon(inicon) controls = QToolBar() self.addToolBar(Qt.BottomToolBarArea, controls) controls.setObjectName('Controls') controls.addAction(self.previousAction) controls.addAction(self.nextAction) controls.addAction(self.refreshAction) self.restoreState(self.settings.value("MainWindow/State", QByteArray())) self.refresh() def interval_namedays(self): dialog = sansimera_reminder.Reminder(self) dialog.applied_signal['QString'].connect(self.reminder) if dialog.exec_() == 1: print('Apply namedays reminder interval...') def reminder(self, time): self.settings.setValue('Interval', time) if time != '0': self.timer_reminder.start(int(time) * 60 * 60 * 1000) print('Reminder = ' + time + ' hour(s)') else: print('Reminder = None') def nextItem(self): if len(self.lista) >= 1: self.browser.clear() if self.lista_pos != len(self.lista)-1: self.lista_pos += 1 else: self.lista_pos = 0 self.browser.append(self.lista[self.lista_pos]) self.browser.moveCursor(QTextCursor.Start) else: return def previousItem(self): if len(self.lista) >= 1: self.browser.clear() if self.lista_pos == 0: self.lista_pos = len(self.lista)-1 else: self.lista_pos -= 1 self.browser.append(self.lista[self.lista_pos]) self.browser.moveCursor(QTextCursor.Start) else: return def refresh(self): try: if self.workThread.isRunning(): return except AttributeError: pass self.menu.hide() self.browser.clear() self.lista = [] self.systray.setToolTip('Σαν σήμερα...') self.browser.append('Λήψη...') self.tentatives = 0 self.eortazontes_shown = False self.download() def activate(self, reason): self.menu.hide() state = self.isVisible() if reason == 3: if state: self.hide() return else: self.show() return if reason == 1: self.menu.hide() self.menu.popup(QCursor.pos()) def download(self): self.workThread = WorkThread() self.workThread.online_signal[bool].connect(self.status) self.workThread.finished.connect(self.window) self.workThread.event['QString'].connect(self.addlist) self.workThread.names['QString'].connect(self.nameintooltip) self.workThread.start() def addlist(self, text): self.lista.append(text) def status(self, status): self.status_online = status def reminder_tray(self): text = self.eortazontes_names.replace('<br/>', '\n') urltexts = re.findall('(<a [\S]+php">)', text) urltexts.extend(['</a>', '<p>', '<div>']) show_notifier_text = text for i in urltexts: show_notifier_text = show_notifier_text.replace(i, '') show_notifier_text = show_notifier_text.replace('\n\n', '\n') show_notifier_text = show_notifier_text.replace('www.eortologio.gr)', 'www.eortologio.gr)\n') self.systray.showMessage('Εορτάζουν:\n', show_notifier_text) self.systray.setToolTip('Εορτάζουν:\n' + show_notifier_text) def nameintooltip(self, text): self.eortazontes_names = text for i in ['<br/>', '<div>']: text = text.replace(i, '') self.eortazontes_in_window = text if self.eortazontes_shown: return self.reminder_tray() self.eortazontes_shown = True def window(self): self.lista.append('<div class=""></div>' + self.eortazontes_in_window) if self.status_online: self.browser.clear() self.browser.append(self.lista[0]) self.lista_pos = 0 return else: if self.tentatives == 10: return self.timer.singleShot(5000, self.refresh) self.tentatives += 1 def closeEvent(self, event): self.settings.setValue("MainWindow/State", self.saveState()) def about(self): self.menu.hide() QMessageBox.about(self, "Εφαρμογή «Σαν σήμερα...»", """<b>sansimera-qt</b> v{0} <p>Δημήτριος Γλενταδάκης <a href="mailto:[email protected]">[email protected]</a> <br/>Ιστοσελίδα: <a href="https://github.com/dglent/sansimera-qt"> github sansimera-qt</a> <p>Εφαρμογή πλαισίου συστήματος για την προβολή <br/>των γεγονότων από την ιστοσελίδα <a href="http://www.sansimera.gr"> www.sansimera.gr</a><br/> Πηγή εορτολογίου: <a href="http://www.eortologio.gr"> www.eortologio.gr</a>, <a href="http://www.synaxari.gr"> www.synaxari.gr</a> <p>Άδεια χρήσης: GPLv3 <br/>Python {1} - Qt {2} - PyQt {3} σε {4}""".format( __version__, platform.python_version(), QT_VERSION_STR, PYQT_VERSION_STR, platform.system()))
class 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)
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()
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)
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)
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()
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)
class MyApp(QtWidgets.QMainWindow): mouseLeaveTimer=0 def __init__(self): # Ui_MainWindow.__init__(self) #自己有__init__函数时,不会默认调用基类的__init__函数 # 因为这里重写了__init__将基类的覆盖掉了,故需要主动调用之 # QtWidgets.QMainWindow.__init__(self) # super(MyApp,self).__init__() #上面两句的作用是相同的,下面这句是python3的新写法 super().__init__() # Get the Screen size self.screenWidth=QDesktopWidget().availableGeometry().width() self.screenHeight=QDesktopWidget().availableGeometry().height() #初始化字体 font=QFont('黑体') font.setPointSize(12) app.setFont(font) # ColorSetting self.bgColor=QColor(66,66,77,88) # # self.setupUi(self) self.initUI() #用来控制半透明的bg面板自动消失 self.timer=QTimer() self.timer.start(30) self.setGeometry(0,30,self.screenWidth,self.screenHeight//3) #Flagsq self.IsMouseHover=False self.MouseOver=False self.Locked=False self.Hidden=False self.isDrag=False self.isResize=False #变量初始化 GLOBAL.WINDOWWIDTH=self.width() GLOBAL.WINDOWHEIGHT=self.height() self.bullets=[] self.dragPos=QPoint(22,22) self.savedName='' # self.screenBuffer=QBitmap(GLOBAL.WINDOWWIDTH,GLOBAL.WINDOWHEIGHT) # self.bufferPainter=QPainter(self.screenBuffer) # self.picture=QPicture() # 建立connection和slot的回调连接 self.createConnections() # 连接到nodejs建立的服务器 self.connect2Server() def initUI(self): #构建托盘 self.trayIcon=QSystemTrayIcon(self) self.trayIcon.setIcon(QtGui.QIcon("tmpIcon.ico")) self.trayIcon.show() self.trayIcon.setToolTip('BulletGo') # 构建托盘菜单 action_quit=QAction('退出',self) action_quit.triggered.connect(self.exitApp) action_switchLock=QAction('锁定/解锁(F6)',self) action_switchLock.triggered.connect(self.switchLock) action_showHide=QAction('显示/隐藏(F7)',self) action_showHide.triggered.connect(lambda:self.switchVisible(self)) action_Settings=QAction('设置',self) action_Settings.triggered.connect(lambda:self.switchVisible(self.settingWindow)) trayIconMenu=QtWidgets.QMenu(self) trayIconMenu.addAction(action_switchLock) trayIconMenu.addAction(action_showHide) trayIconMenu.addSeparator() trayIconMenu.addAction(action_Settings) trayIconMenu.addAction(action_quit) #设定快捷键 QtWidgets.QShortcut(QtGui.QKeySequence(\ QtCore.Qt.Key_F7),self,\ (lambda:self.switchVisible(self.settingWindow))) QtWidgets.QShortcut(QtGui.QKeySequence(\ QtCore.Qt.Key_F6),self,\ (self.switchLock)) self.trayIcon.setContextMenu(trayIconMenu) # 保障不按下鼠标也追踪mouseMove事件 self.setMouseTracking(True) self.setMinimumSize(600,260) self.setWindowTitle("BulletGo") sizeGrip=QtWidgets.QSizeGrip(self) self.setWindowFlags(Qt.FramelessWindowHint\ |Qt.WindowStaysOnTopHint|Qt.Window|\ Qt.X11BypassWindowManagerHint) #Plan A self.setAttribute(Qt.WA_TranslucentBackground,True) #这一句是给Mac系统用的,防止它绘制(很黯淡的)背景 self.setAutoFillBackground(False) QSizeGrip(self).setVisible(True) sizeGrip.setVisible(True) #Plan B 失败 # palette=QPalette() # color=QColor(190, 230, 250) # color.setAlphaF(0.6) # palette.setBrush(self.backgroundRole(), color) # self.setPalette(palette) # self.setAutoFillBackground(True) # self.setBackgroundRole(QPalette.Window) #创建房间的Button和 输入框 self.roomName=QPlainTextEdit() self.roomName.setPlaceholderText('请输入房间名') # self.roomName.resize(50,20) # self.roomName.move(0,0) # self.roomName.setBackgroundVisible(False) self.createBtn=QPushButton("创建/进入") self.hideBtn=QPushButton('隐藏本设置窗口') self.hideBtn.clicked.connect(self.joinRoom) # lambda:self.switchVisible(self.settingWindow)) # self.createBtn.resize(50,20) # self.move(0,100) # self.d settingLayout=QVBoxLayout() hLayout=QHBoxLayout() settingLayout.addWidget(self.roomName) hLayout.addWidget(self.hideBtn) hLayout.addWidget(self.createBtn) self.settingWindow=QWidget() # self.hideBtn=setShortcut(QtGui.QKeySequence('Ctrl+B')) settingLayout.addLayout(hLayout) self.settingWindow.setLayout(settingLayout) # Qt.Tool的作用是 不在任务栏显示 self.settingWindow.setWindowFlags(Qt.FramelessWindowHint|Qt.Tool\ |Qt.X11BypassWindowManagerHint|Qt.Popup) self.roomName.show() self.createBtn.show() self.settingWindow.resize(160,26) self.settingWindow.show() # self.btnFire=QPushButton("Fire",self) # self.btnFire.resize(60,60) # self.btnFire.move(100,30) # self.btnFire.show() # self.btnLock=QPushButton("Lock",self) # self.btnLock.resize(40,40) # self.btnLock.move(self.screenWidth/2,30) # self.btnLock.setFlat(True) # self.btnLock.setIcon(QtGui.QIcon("tmpIcon.png")) # self.btnLock.show() # self.danmakuEditText=QPlainTextEdit(self) # self.danmakuEditText.resize(200,100) # self.danmakuEditText.move(100,100) # self.danmakuEditText.setBackgroundVisible(False) # self.danmakuEditText.show() def joinRoom(self): name=self.roomName.toPlainText() self.socketio.emit('join',name) def connect2Server(self): self.socketio=SocketIO('115.159.102.76/bulletgo_client',80,LoggingNamespace) self.registerEvents() # 开新线程监听服务器发来的消息,否则主线程被阻塞 _thread.start_new_thread(self.socketio.wait,()) def registerEvents(self): self.socketio.on('create_rsp',lambda rsp:self.handleIncomeBullet(rsp)) self.socketio.on('bullet',lambda msg:self.handleIncomeBullet(msg)) self.socketio.on('control_msg',lambda msg:print\ ('---control message--- : '+msg)) self.socketio.on('sys_notification',lambda msg:print\ ('---system notification--- : '+msg)) def handleIncomeBullet(self,bulletMsg): textsAndInfo=self.preProcessText(bulletMsg) if(len(textsAndInfo)>1): self.fireABullet(textsAndInfo[0],self.genQColorFromStr(textsAndInfo[1])) def createRoom_Nodejs(self,name): self.socketio.emit('create_room',name) def fireBtn(self): txt=self.danmakuEditText.toPlainText() tmpbullet=Bullet(txt,GLOBAL.ORANGE,random.randrange(9,16,2)) self.bullets.append(tmpbullet) tmpbullet.prepare() # print(len(self.bullets)) # testStr="line1\nline2\nline3" # textsAndInfo=self.preProcessText(testStr) # print(len(textsAndInfo)) # print def fireABullet(self,txt,color=GLOBAL.ORANGE): tmpbullet=Bullet(txt,color,random.randrange(12,22,2)) self.bullets.append(tmpbullet) tmpbullet.prepare() def createConnections(self): self.timer.timeout.connect(self.update) # self.btnFire.clicked.connect(self.fireBtn) self.createBtn.clicked.connect(\ lambda:self.createRoom_Nodejs\ (self.roomName.toPlainText())) # self.btnLock.clicked.connect(self.switchLock) # self.btnLock.clicked.connect(self.pullMsg) self.trayIcon.activated.connect(self.trayClick) def switchVisible(self,handle): if(handle.isHidden()): handle.activateWindow() handle.setHidden(not handle.isHidden()) def trayClick(self,reason): #单击事件还没设计好 # if(reason==QSystemTrayIcon.Trigger): # self.switchVisible(self) if(reason==QSystemTrayIcon.DoubleClick): self.switchVisible(self.settingWindow) def switchLock(self): self.Locked=not self.Locked '''这个神奇的用法, 在js中也可用''' '''博客搞好后, 这个要单独写个文章''' def genQColorFromStr(self,color): # print(color) return{ 'white':GLOBAL.WHITE, 'green':GLOBAL.GREEN, 'red':GLOBAL.RED, 'pink':GLOBAL.PINK, 'purple':GLOBAL.PURPLE, 'darkblue':GLOBAL.DARKBLUE, 'blue':GLOBAL.BLUE, 'yellow':GLOBAL.YELLOW, 'cyan':GLOBAL.CYAN, 'orange':GLOBAL.ORANGE, '':GLOBAL.ORANGE }[color] def preProcessText(self,string): return string.split('`<') '''---[deprecated]---''' def realPullMsg(self): url='http://danmaku.applinzi.com/message.php' r = requests.post(url,data=self.savedName) r.encoding='utf-8' #预处理收到的字符串 # print(r.text) # r.te textsAndInfo=self.preProcessText(r.text) i=0 # print(textsAndInfo) # print(r.text) if(len(textsAndInfo)>1): while(i<len(textsAndInfo)-1): # print(len(textsAndInfo)) # print('ddddd') # print(i) self.fireABullet(textsAndInfo[i],self.genQColorFromStr(textsAndInfo[i+1])) i+=2 '''---[deprecated]---''' def pullMsg(self): _thread.start_new_thread(self.realPullMsg,()) '''---[deprecated]---''' def createRoom(self): #编码问题实在天坑!!! self.savedName=self.roomName.toPlainText().encode('utf-8')#保存自己的房间号 postData=self.roomName.toPlainText().encode('utf-8') r = requests.post('http://danmaku.applinzi.com/createroom.php',data=postData) r.encoding='utf-8' self.fireABullet(r.text) # print(r.encoding) if(len(r.text)==7): # 开始自动获取服务器上的消息内容 self.pullTimer=QTimer() self.pullTimer.start(2000) self.pullTimer.timeout.connect(self.pullMsg) # print(r.content) # print(r.text) def closeEvent(self,e): e.accept() def mouseReleaseEvent(self,e): if(e.button()==Qt.LeftButton): self.isDrag=False self.isResize=False def mousePressEvent(self,e): if e.button()==Qt.LeftButton: self.LDown=True # self.dragPos=e.globalPos()-self.frameGeometry().topLeft() self.dragPos=e.pos()#效果同上,鼠标相对窗口左上角的位置 if(GLOBAL.WINDOWWIDTH-e.pos().x()<16\ and GLOBAL.WINDOWHEIGHT-e.pos().y()<16): self.topLeft=self.frameGeometry().topLeft() self.isResize=True else: if(not self.Locked): self.isDrag=True # else: # if e.button()==Qt.RightButton: # self.exitApp() e.accept() def mouseMoveEvent(self,e): if(GLOBAL.WINDOWWIDTH-e.pos().x()<16\ and GLOBAL.WINDOWHEIGHT-e.pos().y()<16): #更改鼠标样式 self.setCursor(Qt.SizeFDiagCursor) else: self.setCursor(Qt.ArrowCursor) #如果是Resize,改变窗口大小 if(self.isResize): tmp=e.globalPos()-self.topLeft self.move(self.topLeft) self.resize(tmp.x(),tmp.y()) if (self.isDrag): self.move(e.globalPos()-self.dragPos) e.accept(); def enterEvent(self,e): self.MouseOver=True self.IsMouseHover=True return super(MyApp,self).enterEvent(e) def setMouseHoverFalse(self): # if(not self.MouseOver): self.IsMouseHover=self.MouseOver def leaveEvent(self,e): QTimer.singleShot(800,self.setMouseHoverFalse) self.MouseOver=False return super(MyApp,self).leaveEvent(e) def resizeEvent(self,e): GLOBAL.WINDOWWIDTH=self.width() GLOBAL.WINDOWHEIGHT=self.height() # self.screenBuffer=QBitmap(GLOBAL.WINDOWWIDTH,GLOBAL.WINDOWHEIGHT) # self.bufferPainter=QPainter(self.screenBuffer) # print('resized') # self.repaint() e.accept() def paintEvent(self,e): # Get the Painter painter=QPainter(self) font=QFont('黑体',GLOBAL.BULLETFONTSIZE,QFont.Bold) painter.setFont(font) #Draw a semi-Transparent rect whose size is the same with this window if(self.IsMouseHover and (not self.Locked)): painter.fillRect(0,0,GLOBAL.WINDOWWIDTH,GLOBAL.WINDOWHEIGHT\ ,self.bgColor) # painter.setBackground(QBrush(QColor(123,222,123,122))) #画所有bullet for b in self.bullets: b.draw(painter) for b in self.bullets: if(b.IsExpired): self.bullets.remove(b) # painter.drawPicture(0,0,self.picture) # painter.drawText(30,100,"Hello this is a PyQt5 App我也会说中文") return super(MyApp,self).paintEvent(e) def exitApp(self): self.trayIcon.hide() sys.exit()
class Window(QWidget): w, h = 0, 0 layoutmode = False movetile = None ismin = False tiles = [] def __init__(self): super().__init__() if TRAY_ICON: self.inittray() self.setWindowTitle('Tilemenu') self.initgeometry() self.inittiles() self.mouseReleaseEvent = self.onClicked self.initstyle() self.show() def inittiles(self): for t in TILESET: newtile = t.tile(t.xy, parent=self, extra=t.extra) self.tiles.append(newtile) def testinittiles(self): for i in range(self.w): for j in range(self.h): self.tiles.append(RndColor((i, j), parent=self)) self.tiles[0] = Close((-1, 0), parent=self) self.tiles[1] = DesktopEntry((1, 1), (2, "gedit --new-window", "icons/gedit.png"), self) def initgeometry(self): self.w = (QApplication.desktop().width() - XPOS - XOFF) // (SUNIT) self.h = (QApplication.desktop().height() - YPOS - YOFF) // (SUNIT) if ALIGN == 'LB': self.dw = 0 self.dh = (QApplication.desktop().height() - YPOS - YOFF) % (SUNIT) elif ALIGN == 'RT': self.dw = (QApplication.desktop().width() - XPOS - XOFF) % (SUNIT) self.dh = 0 elif ALIGN == 'RB': self.dw = (QApplication.desktop().width() - XPOS - XOFF) % (SUNIT) self.dh = (QApplication.desktop().height() - YPOS - YOFF) % (SUNIT) else: self.dw = 0 self.dh = 0 self.usegeometry() def usegeometry(self): self.move(XPOS + self.dw, YPOS + self.dh) self.setFixedSize(self.w * SUNIT + SPC, self.h * SUNIT + SPC) def hideAll(self): for i in range(1, len(self.tiles)): self.tiles[i].hide() def showAll(self): for i in range(1, len(self.tiles)): self.tiles[i].show() def initstyle(self): self.setWindowIcon(QIcon('icon.gif')) self.setcolort(BACKGROUND_COLOR) if not TESTMODE: self.setWindowFlags(Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint | Qt.WindowStaysOnBottomHint) elif TESTMODE == 1: self.setWindowFlags(Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint) elif TESTMODE == 2: pass if BACKGROUND_TRANSPARENT: self.setAutoFillBackground(False) self.setAttribute(Qt.WA_TranslucentBackground) if FULSCREEN: self.showFullScreen() def setcolort(self, colort): self.colort = colort self.setStyleSheet("background-color:rgba" + self.colort + ";") def onClicked(self, event): if self.layoutmode and self.movetile != None: self.movetile.x = (self.cursor().pos().x() - self.x()) // SUNIT self.movetile.y = (self.cursor().pos().y() - self.y()) // SUNIT self.movetile.move(self.movetile.x * SUNIT + SPC, self.movetile.y * SUNIT + SPC) self.movetile.sethold(0, self.movetile.dragholdcolor) self.movetile.movemode = False self.movetile = None def keyPressEvent(self, event): if event.key() == Qt.Key_Delete and self.layoutmode: self.movetile.close() self.tiles.remove(self.movetile) self.movetile = None self.repaint() else: pass def genImports(self, filename): out = open(filename, 'w') out.write(IMPORTS_BEGIN) for tile in self.tiles: out.write(tile.getParamsStr()) def inittray(self): menu = QMenu() menu.addAction("Add tile", self.mAddTile) menu.addAction("Add app", self.mAddDE) self.lmaction = menu.addAction("[ ] Layout mode", self.mLayoutMode) menu.addAction("Close", self.mClose) self.tray = QSystemTrayIcon(QIcon('icons/maintile.png')) self.tray.show() self.tray.setContextMenu(menu) self.tray.activated.connect(self.trayclicked) self.tray.show() def mClose(self): self.genImports('imports.py') self.close() def mLayoutMode(self): self.layoutmode = not self.layoutmode # if self.layoutmode: # pass # #self.lmaction.setText('[*] Layout mode') # else: # pass # #self.lmaction.setText('[ ] Layout mode') def mAddTile(self): d = addTileDialog() if d.exec() == QDialog.Accepted: tile = d.output[0] xy = (d.output[1][0], d.output[1][1]) ext = d.output[1][2] newtile = tile(xy, parent=self, extra=ext) self.tiles.append(newtile) self.tiles[-1].show() self.repaint() else: pass def mAddDE(self): d = addDEDialog() if d.exec() == QDialog.Accepted: tile = DesktopEntry xy = (-2, -2) ext = d.output newtile = tile(xy, parent=self, extra=ext) self.tiles.append(newtile) self.tiles[-1].show() self.repaint() else: pass def trayclicked(self, reason): if reason == QSystemTrayIcon.Context: if self.layoutmode: self.lmaction.setText('[*] Layout mode') else: self.lmaction.setText('[ ] Layout mode') if reason == QSystemTrayIcon.Trigger: if self.ismin: self.ismin = False if FULSCREEN: self.showFullScreen() else: self.show() else: self.ismin = True self.hide()
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_()
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 TrayIcon(QObject): def __init__(self): super().__init__() self._supported = QSystemTrayIcon.isSystemTrayAvailable() self._context_menu = None self._enabled = False self._trayicon = None self._state_icons = {} for state in ( 'disconnected', 'disabled', 'enabled', ): icon = QIcon(':/state-%s.svg' % state) if hasattr(icon, 'setIsMask'): icon.setIsMask(True) self._state_icons[state] = icon self._machine = None self._machine_state = 'disconnected' self._is_running = False self._update_state() def set_menu(self, menu): self._context_menu = menu if self._enabled: self._trayicon.setContextMenu(menu) def log(self, level, message): if self._enabled: if level <= log.INFO: icon = QSystemTrayIcon.Information timeout = 10 elif level <= log.WARNING: icon = QSystemTrayIcon.Warning timeout = 15 else: icon = QSystemTrayIcon.Critical timeout = 25 self._trayicon.showMessage(__software_name__.capitalize(), message, icon, timeout * 1000) else: if level <= log.INFO: icon = QMessageBox.Information elif level <= log.WARNING: icon = QMessageBox.Warning else: icon = QMessageBox.Critical msgbox = QMessageBox() msgbox.setText(message) msgbox.setIcon(icon) msgbox.exec_() def is_supported(self): return self._supported def enable(self): if not self._supported: return self._trayicon = QSystemTrayIcon() # On OS X, the context menu is activated with either mouse buttons, # and activation messages are still sent, so ignore those... if not sys.platform.startswith('darwin'): self._trayicon.activated.connect(self._on_activated) if self._context_menu is not None: self._trayicon.setContextMenu(self._context_menu) self._enabled = True self._update_state() self._trayicon.show() def disable(self): if not self._enabled: return self._trayicon.hide() self._trayicon = None self._enabled = False def is_enabled(self): return self._enabled def update_machine_state(self, machine, state): self._machine = machine self._machine_state = state self._update_state() def update_output(self, enabled): self._is_running = enabled self._update_state() clicked = pyqtSignal() def _update_state(self): if self._machine_state not in ('initializing', 'connected'): state = 'disconnected' else: state = 'enabled' if self._is_running else 'disabled' icon = self._state_icons[state] if not self._enabled: return machine_state = _('{machine} is {state}').format( machine=_(self._machine), state=_(self._machine_state), ) if self._is_running: output_state = _('output is enabled') else: output_state = _('output is disabled') self._trayicon.setIcon(icon) self._trayicon.setToolTip( 'Plover:\n- %s\n- %s' % (output_state, machine_state) ) def _on_activated(self, reason): if reason == QSystemTrayIcon.Trigger: self.clicked.emit()
class MainWindow(QWidget): """ Main window of application. It is represented by icon in taskbar. Useful attributes ----------------- addWindow : AddWindow object addActive : boolean If user wants to add new task, 'addWindow' becomes the QWidget for it, and while it is active 'addActive' remains True. editWindow : EditWindow object editActive : boolean If user wants to edit tasks, 'editWindow' becomes the QWidget for it, and while it is active 'editActive' remains True. tray : QSystemTrayIcon object Tray icon and all its attributes like context menu and activated action. timer : QTimer object Timer that fires every second. If one reminder's time is up, shows message. backupTimer : QTimer object Timer that fires every 5 minutes. Saves current state of tasks file to backup file. """ def __init__(self): """ Init GUI and all required things. """ super().__init__() iconAdd = Icon(byte=icons.add).convertToIcon().getIcon() self.tray = QSystemTrayIcon(iconAdd, self) menu = QMenu() menu.addAction(conf.lang.ADD_ACTION, lambda: self.showWindow(QSystemTrayIcon.Trigger)) menu.addAction(conf.lang.EDIT_ACTION, lambda: self.showWindow('editAction')) menu.addSeparator() menu.addAction(conf.lang.OPT_ACTION, lambda: self.showWindow('optAction')) menu.addAction(conf.lang.RESTORE, self.restore) menu.addAction(conf.lang.QUIT, self.quit) self.tray.setContextMenu(menu) self.tray.activated.connect(self.showWindow) self.tray.show() self.addWindow = None self.addActive = False self.editWindow = None self.editActive = False self.optWindow = None self.optActive = False self.timer = QTimer() self.timer.timeout.connect(self.timerTick) self.timer.start(1000) self.backup() self.backupTimer = QTimer() self.backupTimer.timeout.connect(self.backup) self.backupTimer.start(conf.backup * 1000) def timerTick(self): """ Checks tasks entry's time if it is up. Shows message with entry's text if its time's up. """ global tasks global rewrite for entry in tasks: date = entry.getDateTime() today = dt.datetime.today() if (date - today).days < 0: tasks.remove(entry) class EvMessageBox(QMessageBox): """ QMessageBox with timer. Parameters ---------- text : string Text of message. title : string Title of message window. wicon : QIcon object Icon of message window. icon : QMessageBox.Icon int Icon of message body. timeout : int Time for message has being shown. Useful attributes ----------------- timer : QTimer object Timer attached to message. """ def __init__(self, text, title, wicon, icon, timeout): super().__init__() self.timeout = timeout self.setText(text) self.setWindowTitle(title) self.setWindowIcon(wicon) self.setIcon(icon) self.addButton(QPushButton(conf.lang.REPEAT.format( parseSeconds(conf.tdelta))), QMessageBox.YesRole) self.addButton(QPushButton(conf.lang.CLOSE), QMessageBox.NoRole) self.setWindowFlags(Qt.WindowStaysOnTopHint) # self.setTextFormat(Qt.RichText) self.timer = QTimer() self.timer.timeout.connect(self.timerTick) def showEvent(self, event): """ Start timer on message showEvent. """ self.currentTime = 0 self.timer.start(1000) def timerTick(self): """ Done message on timeout. """ self.currentTime += 1 if self.currentTime >= self.timeout: self.timer.stop() self.done(-1) msgBox = EvMessageBox( entry.text, '{} {}'.format(conf.lang.TASK, date), Icon(byte=icons.alert).convertToIcon().getIcon(), QMessageBox.Information, conf.mesout) reply = msgBox.exec_() msgBox.raise_() if reply != 1: td = conf.tdelta if reply == 0 else 300 - conf.mesout date = dateToStr(dt.datetime.now() + dt.timedelta(0, td)) date['date'] = date['date'].replace('/', '.') date['time'] = date['time'].replace('.', ':') tasks.append(Entry(date['date'], date['time'], entry.text)) rewrite() if self.editActive: self.editWindow.filterApply() def showWindow(self, event): """ Show child windows. If event is QSystemTrayIcon.Trigger then it checks if all windows are not open and show addWindow. If event is 'addAction' it means that user from editWindow want to edit reminder, so it opens addWindow. Then it checks if addAction or editAction is True, and alert appropriate window. Then if event is 'editAction' then it opens editWindow. """ if event == 'editAction': if self.editActive: QApplication.alert(self.editWindow) else: self.editWindow = EditWindow(self) self.editWindow.show() self.editWindow.setFocus(True) self.editWindow.activateWindow() return self.editWindow elif event == 'addAction': self.addWindow = AddWindow(self) self.addWindow.show() self.addWindow.setFocus(True) self.addWindow.activateWindow() return self.addWindow elif event == QSystemTrayIcon.Trigger: if self.addActive: QApplication.alert(self.addWindow) else: self.addWindow = AddWindow(self) self.addWindow.show() self.addWindow.setFocus(True) self.addWindow.activateWindow() return self.addWindow elif event == 'optAction': if self.addActive: self.addWindow.hide() if self.editActive: self.editWindow.hide() self.optWindow = OptionsWindow(self) self.optWindow.show() self.optWindow.setFocus(True) self.optWindow.activateWindow() def backup(self): """ Copies content of tasks file to backup file. """ with open(backup, 'w') as to_, open(filename, 'r') as from_: to_.write(from_.read()) def restore(self): """ Restores content of tasks file from backup file after user confirmation. """ global tasks shure = QMessageBox.question(self, conf.lang.RESTORE, conf.lang.RESTORE_TEXT, QMessageBox.No | QMessageBox.Yes, QMessageBox.No) if shure == QMessageBox.No: pass else: temp = open(backup).read() # don't forget to read backup self.backup() with open(filename, 'w') as to_: to_.write(temp) tasks = [] readTasks() if self.editActive: self.editWindow.filterApply() def quit(self, really=True): """ Quits application. Hides tray icon firstly. If really is not True, do not quits the application. It is used to re-launch the application. """ self.tray.hide() self.timer.stop() self.backupTimer.stop() if really: QCoreApplication.instance().quit()
class Qt4SysTrayIcon: def __init__(self): self.snapshots = snapshots.Snapshots() self.config = self.snapshots.config self.decode = None if len(sys.argv) > 1: if not self.config.setCurrentProfile(sys.argv[1]): logger.warning("Failed to change Profile_ID %s" %sys.argv[1], self) self.qapp = qt4tools.createQApplication(self.config.APP_NAME) translator = qt4tools.translator() self.qapp.installTranslator(translator) self.qapp.setQuitOnLastWindowClosed(False) import icon self.icon = icon self.qapp.setWindowIcon(icon.BIT_LOGO) self.status_icon = QSystemTrayIcon(icon.BIT_LOGO) #self.status_icon.actionCollection().clear() self.contextMenu = QMenu() self.menuProfileName = self.contextMenu.addAction(_('Profile: "%s"') % self.config.profileName()) qt4tools.setFontBold(self.menuProfileName) self.contextMenu.addSeparator() self.menuStatusMessage = self.contextMenu.addAction(_('Done')) self.menuProgress = self.contextMenu.addAction('') self.menuProgress.setVisible(False) self.contextMenu.addSeparator() self.btnPause = self.contextMenu.addAction(icon.PAUSE, _('Pause snapshot process')) action = lambda: os.kill(self.snapshots.pid(), signal.SIGSTOP) self.btnPause.triggered.connect(action) self.btnResume = self.contextMenu.addAction(icon.RESUME, _('Resume snapshot process')) action = lambda: os.kill(self.snapshots.pid(), signal.SIGCONT) self.btnResume.triggered.connect(action) self.btnResume.setVisible(False) self.btnStop = self.contextMenu.addAction(icon.STOP, _('Stop snapshot process')) self.btnStop.triggered.connect(self.onBtnStop) self.contextMenu.addSeparator() self.btnDecode = self.contextMenu.addAction(icon.VIEW_SNAPSHOT_LOG, _('decode paths')) self.btnDecode.setCheckable(True) self.btnDecode.setVisible(self.config.snapshotsMode() == 'ssh_encfs') self.btnDecode.toggled.connect(self.onBtnDecode) self.openLog = self.contextMenu.addAction(icon.VIEW_LAST_LOG, _('View Last Log')) self.openLog.triggered.connect(self.onOpenLog) self.startBIT = self.contextMenu.addAction(icon.BIT_LOGO, _('Start BackInTime')) self.startBIT.triggered.connect(self.onStartBIT) self.status_icon.setContextMenu(self.contextMenu) self.pixmap = icon.BIT_LOGO.pixmap(24) self.progressBar = QProgressBar() self.progressBar.setMinimum(0) self.progressBar.setMaximum(100) self.progressBar.setValue(0) self.progressBar.setTextVisible(False) self.progressBar.resize(24, 6) self.progressBar.render(self.pixmap, sourceRegion = QRegion(0, -14, 24, 6), flags = QWidget.RenderFlags(QWidget.DrawChildren)) self.first_error = self.config.notify() self.popup = None self.last_message = None self.timer = QTimer() self.timer.timeout.connect(self.updateInfo) self.ppid = os.getppid() def prepairExit(self): self.timer.stop() if not self.status_icon is None: self.status_icon.hide() self.status_icon = None if not self.popup is None: self.popup.deleteLater() self.popup = None self.qapp.processEvents() def run(self): self.status_icon.show() self.timer.start(500) logger.info("[qt4systrayicon] begin loop", self) self.qapp.exec_() logger.info("[qt4systrayicon] end loop", self) self.prepairExit() def updateInfo(self): if not tools.processAlive(self.ppid): self.prepairExit() self.qapp.exit(0) return paused = tools.processPaused(self.snapshots.pid()) self.btnPause.setVisible(not paused) self.btnResume.setVisible(paused) message = self.snapshots.takeSnapshotMessage() if message is None and self.last_message is None: message = (0, _('Working...')) if not message is None: if message != self.last_message: self.last_message = message if self.decode: message = (message[0], self.decode.log(message[1])) self.menuStatusMessage.setText('\n'.join(tools.wrapLine(message[1],\ size = 80,\ delimiters = '',\ new_line_indicator = '') \ )) self.status_icon.setToolTip(message[1]) pg = progress.ProgressFile(self.config) if pg.fileReadable(): pg.load() percent = pg.intValue('percent') if percent != self.progressBar.value(): self.progressBar.setValue(percent) self.progressBar.render(self.pixmap, sourceRegion = QRegion(0, -14, 24, 6), flags = QWidget.RenderFlags(QWidget.DrawChildren)) self.status_icon.setIcon(QIcon(self.pixmap)) self.menuProgress.setText(' | '.join(self.getMenuProgress(pg))) self.menuProgress.setVisible(True) else: self.status_icon.setIcon(self.icon.BIT_LOGO) self.menuProgress.setVisible(False) def getMenuProgress(self, pg): d = (('sent', _('Sent:')), \ ('speed', _('Speed:')),\ ('eta', _('ETA:'))) for key, txt in d: value = pg.strValue(key, '') if not value: continue yield txt + ' ' + value def onStartBIT(self): profileID = self.config.currentProfile() cmd = ['backintime-qt4',] if not profileID == '1': cmd += ['--profile-id', profileID] proc = subprocess.Popen(cmd) def onOpenLog(self): dlg = logviewdialog.LogViewDialog(self, systray = True) dlg.decode = self.decode dlg.cbDecode.setChecked(self.btnDecode.isChecked()) dlg.exec_() def onBtnDecode(self, checked): if checked: self.decode = encfstools.Decode(self.config) self.last_message = None self.updateInfo() else: self.decode = None def onBtnStop(self): os.kill(self.snapshots.pid(), signal.SIGKILL) self.btnStop.setEnabled(False) self.btnPause.setEnabled(False) self.btnResume.setEnabled(False) self.snapshots.setTakeSnapshotMessage(0, 'Snapshot terminated')
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()
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()
class Magneto(MagnetoCore): """ Magneto Updates Notification Applet class. """ def __init__(self): self._app = QApplication([sys.argv[0]]) from dbus.mainloop.pyqt5 import DBusQtMainLoop super(Magneto, self).__init__(main_loop_class = DBusQtMainLoop) self._window = QSystemTrayIcon(self._app) icon_name = self.icons.get("okay") self._window.setIcon(QIcon.fromTheme(icon_name)) self._window.activated.connect(self._applet_activated) self._menu = QMenu(_("Magneto Entropy Updates Applet")) self._window.setContextMenu(self._menu) self._menu_items = {} for item in self._menu_item_list: if item is None: self._menu.addSeparator() continue myid, _unused, mytxt, myslot_func = item name = self.get_menu_image(myid) action_icon = QIcon.fromTheme(name) w = QAction(action_icon, mytxt, self._window, triggered=myslot_func) self._menu_items[myid] = w self._menu.addAction(w) self._menu.hide() def _first_check(self): def _do_check(): self.send_check_updates_signal(startup_check = True) return False if self._dbus_service_available: const_debug_write("_first_check", "spawning check.") QTimer.singleShot(10000, _do_check) def startup(self): self._dbus_service_available = self.setup_dbus() if config.settings["APPLET_ENABLED"] and \ self._dbus_service_available: self.enable_applet(do_check = False) const_debug_write("startup", "applet enabled, dbus service available.") else: const_debug_write("startup", "applet disabled.") self.disable_applet() if not self._dbus_service_available: const_debug_write("startup", "dbus service not available.") QTimer.singleShot(30000, self.show_service_not_available) else: const_debug_write("startup", "spawning first check.") self._first_check() # Notice Window instance self._notice_window = AppletNoticeWindow(self) self._window.show() # Enter main loop self._app.exec_() def close_service(self): super(Magneto, self).close_service() self._app.quit() def change_icon(self, icon_name): name = self.icons.get(icon_name) self._window.setIcon(QIcon.fromTheme(name)) def disable_applet(self, *args): super(Magneto, self).disable_applet() self._menu_items["disable_applet"].setEnabled(False) self._menu_items["enable_applet"].setEnabled(True) def enable_applet(self, w = None, do_check = True): done = super(Magneto, self).enable_applet(do_check = do_check) if done: self._menu_items["disable_applet"].setEnabled(True) self._menu_items["enable_applet"].setEnabled(False) def show_alert(self, title, text, urgency = None, force = False, buttons = None): # NOTE: there is no support for buttons via QSystemTrayIcon. if ((title, text) == self.last_alert) and not force: return def _action_activate_cb(action_num): if not buttons: return try: action_info = buttons[action_num - 1] except IndexError: return _action_id, _button_name, button_callback = action_info button_callback() def do_show(): if not self._window.supportsMessages(): const_debug_write("show_alert", "messages not supported.") return icon_id = QSystemTrayIcon.Information if urgency == "critical": icon_id = QSystemTrayIcon.Critical self._window.showMessage(title, text, icon_id) self.last_alert = (title, text) QTimer.singleShot(0, do_show) def update_tooltip(self, tip): def do_update(): self._window.setToolTip(tip) QTimer.singleShot(0, do_update) def applet_context_menu(self): """No action for now.""" def _applet_activated(self, reason): const_debug_write("applet_activated", "Applet activated: %s" % reason) if reason == QSystemTrayIcon.DoubleClick: const_debug_write("applet_activated", "Double click event.") self.applet_doubleclick() def hide_notice_window(self): self.notice_window_shown = False self._notice_window.hide() def show_notice_window(self): if self.notice_window_shown: const_debug_write("show_notice_window", "Notice window already shown.") return if not self.package_updates: const_debug_write("show_notice_window", "No computed updates.") return entropy_ver = None packages = [] for atom in self.package_updates: key = entropy.dep.dep_getkey(atom) avail_rev = entropy.dep.dep_get_entropy_revision(atom) avail_tag = entropy.dep.dep_gettag(atom) my_pkg = entropy.dep.remove_entropy_revision(atom) my_pkg = entropy.dep.remove_tag(my_pkg) pkgcat, pkgname, pkgver, pkgrev = entropy.dep.catpkgsplit(my_pkg) ver = pkgver if pkgrev != "r0": ver += "-%s" % (pkgrev,) if avail_tag: ver += "#%s" % (avail_tag,) if avail_rev: ver += "~%s" % (avail_tag,) if key == "sys-apps/entropy": entropy_ver = ver packages.append("%s (%s)" % (key, ver,)) critical_msg = "" if entropy_ver is not None: critical_msg = "%s <b>sys-apps/entropy</b> %s, %s <b>%s</b>. %s." % ( _("Your system currently has an outdated version of"), _("installed"), _("the latest available version is"), entropy_ver, _("It is recommended that you upgrade to " "the latest before updating any other packages") ) self._notice_window.populate(packages, critical_msg) self._notice_window.show() self.notice_window_shown = True
class Window(QDialog): def __init__(self, gui_connection): super(Window, self).__init__() self.gui_connection = gui_connection self.gui_connection.changeState.connect(self.changeState) self.gui_connection.whatTime.connect(self.setTime) #init settings dialog self.settings_dialog = settings.SettingsDialog() self.createActions() self.createTrayIcon() self.setTrayIcon('work-full') self.trayIcon.show() self.setWindowTitle("KoffeeBreak") def createActions(self): self.openAction = QAction(QIcon().fromTheme('document-open'), "Open", self, triggered=self.showNormal) self.takeBreakAction = QAction(QIcon().fromTheme("koffeebreak-break-full"), "Take a break", self, triggered=self.start_break) self.pauseAction = QAction(QIcon().fromTheme('media-playback-pause'), "Pause program",self, triggered=self.pauseProgram) self.settingsAction = QAction(QIcon().fromTheme('configure'), "Settings", self, triggered=self.settings_dialog.show) self.quitAction = QAction(QIcon().fromTheme('application-exit'), "Quit", self, triggered=self.close_app) def createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.openAction) self.trayIconMenu.addAction(self.settingsAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.takeBreakAction) self.trayIconMenu.addAction(self.pauseAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu) def setTrayIcon(self, iconName): icon = QIcon().fromTheme('koffeebreak-' + iconName) self.trayIcon.setIcon(icon) def start_break(self): self.break_screen = break_screen.BreakWindow(self.gui_connection) def changeState(self, state): self.setTrayIcon(state) if state == "break-1-4": pass #self.gui_connection.whatTime.emit() #self.trayIcon.showMessage("Break-1-4", str(self.time)) elif state == "break-2-4": pass elif state == "break-3-4": pass elif state == "break-full": if (not self.break_screen.isVisible()): self.start_break() elif state == "work-1-8": pass elif state == "work-2-8": pass elif state == "work-3-8": pass elif state == "work-4-8": pass elif state == "work-5-8": pass elif state == "work-6-8": pass elif state == "work-7-8": pass elif state == "work-full": if (self.break_screen.isVisible()): self.break_screen.close() def setTime(self, time): self.time = time def pauseProgram(self): self.gui_connection.pauseTimer.emit() def close_app(self): self.gui_connection.closeApp.emit()
class Window(QDialog): def __init__(self): super(Window, self).__init__() # UI self.createActions() self.setTitle = programTitle self.createTrayIcon() # Draw system tray icon pixmap = QtGui.QPixmap(QtGui.QPixmap(":icons/mailbox_empty.png")) painter = QtGui.QPainter(pixmap) painter.setPen(QtGui.QColor(255, 0, 0)) painter.setFont(QtGui.QFont("Arial", QtGui.QFont.Bold)) painter.drawText(QtCore.QRectF(pixmap.rect()), QtCore.Qt.AlignCenter, "0") painter.end() self.trayIcon.setIcon(QtGui.QIcon(pixmap)) # End drawing system tray icon self.trayIcon.setToolTip("You have no unread letters") self.trayIcon.show() # setup settings self.ui = Ui_Settings() self.ui.setupUi(self) self.setWindowIcon(QIcon(os.path.dirname(os.path.realpath(__file__)) + "/icons/mailbox_empty.png")) self.SettingsRestore() self.ui.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.btnOK_clicked) self.ui.buttonBox.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.btnCancel_clicked) self.ui.btnTestConnection.clicked.connect(self.btnTestConnection_clicked) self.ui.comboAccounts.currentTextChanged.connect(self.comboAccounts_changed) self.ui.btnAddAccount.clicked.connect(self.btnAddAccount_clicked) self.ui.btnRenameAccount.clicked.connect(self.btnRenameAccount_clicked) self.ui.btnSaveAccount.clicked.connect(self.btnSaveAccount_clicked) self.ui.btnRemoveAccount.clicked.connect(self.btnRemoveAccount_clicked) # Main timer self.timer = QTimer(self) self.timer.timeout.connect(mail_check) self.lastCheckCount = ( 0 ) # variable for prevent annoying popup notification when mail count didn't change since last check # Menu actions def createActions(self): self.detailsShow = QAction(QIcon(":icons/details.png"), "&Details", self, triggered=self.detailsShow) self.aboutShow = QAction(QIcon(":icons/mailbox_empty.png"), "&About", self, triggered=self.aboutShow) self.checkNow = QAction(QIcon(":icons/check_now.png"), "&Check now", self, triggered=mail_check) self.restoreAction = QAction(QIcon(":icons/settings.png"), "&Settings", self, triggered=self.showNormal) self.quitAction = QAction(QIcon(":icons/menu_quit.png"), "&Quit", self, triggered=QApplication.instance().quit) # UI functions def createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.aboutShow) self.trayIconMenu.addAction(self.detailsShow) self.trayIconMenu.addAction(self.checkNow) self.trayIconMenu.addAction(self.restoreAction) self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu) self.trayIcon.activated.connect(self.trayIconActivated) def SettingsRestore(self): if GlobalSettingsExist() and AccountExist(): groups = settings.childGroups() for i in range(len(groups)): self.ui.comboAccounts.addItem(groups[i]) self.ui.comboAccounts.setCurrentText(groups[i]) settings.beginGroup(groups[i]) self.ui.txtboxMailServer.setText(settings.value("MailServer")) self.ui.txtboxPort.setText(settings.value("Port")) self.ui.txtboxLogin.setText(settings.value("Login")) self.ui.txtboxPassword.setText(settings.value("Password")) self.ui.boolifSSL.setChecked(bool(settings.value("SSL"))) settings.endGroup() if self.ui.comboAccounts.count() == 0: self.ui.comboAccounts.addItem("Default") self.ui.comboAccounts.setCurrentText("Default") self.ui.checkFreq.setValue(int(settings.value("CheckInterval"))) self.ui.boolifNotify.setChecked(bool(settings.value("Notify"))) def SettingsSave(self, account): settings.setValue("CheckInterval", self.ui.checkFreq.value()) settings.setValue("Notify", self.ui.boolifNotify.isChecked()) settings.beginGroup(account) settings.setValue("MailServer", self.ui.txtboxMailServer.text()) settings.setValue("Port", self.ui.txtboxPort.text()) settings.setValue("Login", self.ui.txtboxLogin.text()) settings.setValue("Password", self.ui.txtboxPassword.text()) settings.setValue("SSL", self.ui.boolifSSL.isChecked()) settings.endGroup() def SettingsRemove(self, group): settings.beginGroup(group) settings.remove("") settings.endGroup() def btnOK_clicked(self): self.SettingsSave(self.ui.comboAccounts.currentText()) if ( settings.value("MailServer") == "" or settings.value("Port") == "" or settings.value("Login") == "" or settings.value("Password") == "" ): QMessageBox.critical(self, "Warning", "You should fill all fields in IMAP settings!") self.show() mail_check() self.ui.lblTestOutput.setText("") self.stop() self.start() def btnCancel_clicked(self): self.SettingsRestore() self.ui.lblTestOutput.setText("") def btnTestConnection_clicked(self): try: if self.ui.boolifSSL.isChecked: self.imap = imaplib.IMAP4_SSL(self.ui.txtboxMailServer.text(), self.ui.txtboxPort.text()) else: self.imap = imaplib.IMAP4(self.ui.txtboxMailServer.text(), self.ui.txtboxPort.text()) self.imap.login(self.ui.txtboxLogin.text(), self.ui.txtboxPassword.text()) output = "Connection was established successfully" except: output = "Unable to establish connection to mailbox" finally: self.ui.lblTestOutput.setText(output) def btnAddAccount_clicked(self): GroupName = QInputDialog.getText(self, "Enter account name", "Enter account name", QLineEdit.Normal, "") if GroupName[0]: self.ui.comboAccounts.addItem(GroupName[0]) self.ui.comboAccounts.setCurrentText(GroupName[0]) def btnRenameAccount_clicked(self): Index = self.ui.comboAccounts.currentIndex() OldGroupName = self.ui.comboAccounts.currentText() GroupName = QInputDialog.getText( self, "Enter account name", "Enter account name", QLineEdit.Normal, self.ui.comboAccounts.currentText() ) if GroupName[0]: self.SettingsSave(GroupName[0]) self.ui.comboAccounts.setItemText(Index, GroupName[0]) self.ui.comboAccounts.setCurrentText(GroupName[0]) self.SettingsRemove(OldGroupName) def btnSaveAccount_clicked(self): self.SettingsSave(self.ui.comboAccounts.currentText()) self.ui.lblTestOutput.setText("Account saved") def btnRemoveAccount_clicked(self): reply = QMessageBox.warning( self, "Warning!", "Delete this account permanently?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if reply == QMessageBox.Yes: Index = self.ui.comboAccounts.currentIndex() GroupName = self.ui.comboAccounts.currentText() self.ui.comboAccounts.removeItem(Index) self.SettingsRemove(GroupName) def comboAccounts_changed(self): self.ui.lblTestOutput.setText("") settings.beginGroup(self.ui.comboAccounts.currentText()) self.ui.txtboxMailServer.setText(settings.value("MailServer")) self.ui.txtboxPort.setText(settings.value("Port")) self.ui.txtboxLogin.setText(settings.value("Login")) self.ui.txtboxPassword.setText(settings.value("Password")) self.ui.boolifSSL.setChecked(bool(settings.value("SSL"))) settings.endGroup() def aboutShow(self): if about.isMinimized: about.hide() about.show() about.activateWindow() def detailsShow(self): details.show() details.activateWindow() def trayIconActivated(self, reason): if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick): details.show() details.activateWindow() def start(self): if GlobalSettingsExist() and AccountExist(): CheckInterval = 1000 * 60 * int(settings.value("CheckInterval")) else: CheckInterval = 1000 * 60 * 5 self.timer.setInterval(CheckInterval) self.timer.start() def stop(self): self.timer.stop()
def initUI(self): # 获取电脑屏幕宽高 让主界面初始化后处于屏幕中间 wh = QApplication.desktop().screenGeometry() self.screen_w , self.screen_h = wh.width() ,wh.height() self.setGeometry(int((self.screen_w-300)/2),int((self.screen_h-600)/2),300,600) # self.setWindowOpacity(0.97); #当前播放歌曲的封面 songer_img = DragLabel(self) # songer_img.setwinflag.connect(self.setwinflag) songer_img.setParent(self) songer_img.resize(300,200) self.picture = QLabel(songer_img) self.picture.resize(300,200) self.picture.setStyleSheet("QLabel{ border-image:url("+conf['pifu']+")}") # syl = QLabel(songer_img) # syl.setGeometry(15,5,34,15) # syl.setStyleSheet("QLabel{ border-image:url(image/newimg/logo.png);}") # ================================ songinfo = QLabel(songer_img) songinfo.setGeometry(0,30,300,80) songinfo.setStyleSheet("QLabel{ background:transparent;}") songpic = QLabel(songinfo) songpic.setGeometry(10,0,80,80) songpic.setStyleSheet("QLabel{ border-image:url(image/newimg/user.jpg);border-radius:2px;}") self.songname = QLabel("老鼠爱大米 - 香香",songinfo) self.songname.setGeometry(105,0,210,25) self.songname.setStyleSheet("QLabel{ color:#EEE;font-size:15px;}") uploaduser = QLabel("By 张三的歌",songinfo) uploaduser.move(105,25) # uploaduser.setCursor(QCursor(Qt.PointingHandCursor)) uploaduser.setStyleSheet("QLabel{ color:yellow;font-size:15px;} QLabel:hover{color:red}") fenshu = QLabel("评分 - 7.6",songinfo) fenshu.setGeometry(105,50,210,25) # self.picture.setGraphicsEffect(QGraphicsBlurEffect()) fenshu.setStyleSheet("QLabel{ color:#EEE;font-size:15px;}") songtool = QLabel(songer_img) songtool.setGeometry(0,110,300,35) songtool.setStyleSheet("QLabel{ background:transparent;}") # 喜欢歌曲 lovesong = QLabel(songtool) lovesong.setGeometry(20,10,25,25) lovesong.setStyleSheet("QLabel{ border-image:url(image/newimg/kg_ic_player_liked.png);}") # 评论 pinglun = QLabel(songtool) pinglun.setGeometry(50,5,33,33) pinglun.setStyleSheet("QLabel{ border-image:url(image/newimg/pinglun.png);}") # 歌曲更多信息 songmore = QLabel("查看这首歌的更多资料",songtool) songmore.move(100,10) # songmore.setCursor(QCursor(Qt.PointingHandCursor)) songmore.setStyleSheet("QLabel{ color:#BBB} QLabel:hover{color:pink}") # ====================================== # 顶部工具栏 # 隐藏 btn = QPushButton("",self) btn.setGeometry(270,0,15,32) # btn.setCursor(QCursor(Qt.PointingHandCursor)) btn.setStyleSheet("QPushButton{ border:none;color:white;background:transparent;border-image:url(image/newimg/mini.png) } QPushButton:hover{ border-image:url(image/newimg/mini_2.png) } ") btn.clicked.connect(self.close) # 换皮肤 btn = QPushButton("",self) btn.setGeometry(230,10,20,20) # btn.setCursor(QCursor(Qt.PointingHandCursor)) btn.setStyleSheet("QPushButton{ border:none;color:white;background:transparent;border-image:url(image/newimg/fx_slide_menu_change_bg_2.png) } QPushButton:hover{ border-image:url(image/newimg/fx_slide_menu_change_bg.png) } ") btn.clicked.connect(self.huanfu) # 设置封面 # btn = QPushButton("",self) # btn.setGeometry(230,-10,41,48) # btn.setCursor(QCursor(Qt.PointingHandCursor)) # btn.setStyleSheet("QPushButton{ border:none;color:white;background:transparent;border-image:url(image/newimg/fengmian.png) } ") # btn.clicked.connect(self.setHeaderImg) # 开启/关闭歌词 # btn = QPushButton("",self) # btn.setGeometry(200,0,30,30) # btn.setCursor(QCursor(Qt.PointingHandCursor)) # btn.setStyleSheet("QPushButton{ border:none;color:white;background:transparent;border-image:url(image/newimg/geci.png) } ") # btn.clicked.connect(self.lrc) # 播放组件 ( 播放 前进 后退 播放时间 进度条 歌曲名 音量 ) # 播放/暂停 self.playBtn = QPushButton("",songer_img) self.playBtn.setGeometry(130,155,32,25) self.playBtn.setStyleSheet("QPushButton{ border-image:url(image/newimg/statusbar_btn_play.png);border:none } QPushButton:hover{ border-image:url(image/newimg/statusbar_btn_play_2.png)} ") # 下一首 self.nextBtn = QPushButton("",songer_img) self.nextBtn.setGeometry(186,159,20,20) self.nextBtn.setStyleSheet("QPushButton{ border-image:url(image/newimg/statusbar_btn_next.png);border:none } QPushButton:hover{ border-image:url(image/newimg/statusbar_btn_next_2.png)}") # 音量调节 self.songvolume = QPushButton("",songer_img) self.songvolume.setGeometry(236,159,20,20) self.songvolume.setStyleSheet("QPushButton{ border-image:url(image/newimg/ic_player_menu_volume.png);border:none } QPushButton:hover{ border-image:url(image/newimg/ic_player_menu_volume_2.png)}") self.songvolume.clicked.connect(self.setvolume) # 音量 self.volslider = QSlider(Qt.Horizontal,self) self.volslider.setCursor(QCursor(Qt.UpArrowCursor)) self.volslider.setGeometry(250,165,45,6) self.volslider.setValue(70) self.volslider.setRange(0,100) self.volslider.setStyleSheet(qss_vol) self.volslider.setVisible(False) # 上一首 self.prevBtn = QPushButton("",songer_img) self.prevBtn.setGeometry(85,159,20,20) self.prevBtn.setStyleSheet("QPushButton{ border-image:url(image/newimg/statusbar_btn_prev.png);border:none } QPushButton:hover{ border-image:url(image/newimg/statusbar_btn_prev_2.png)}") # 播放模式 self.playmodel = QPushButton("",songer_img) self.playmodel.setGeometry(35,156,25,25) self.playmodel.setStyleSheet("QPushButton{ border-image:url(image/newimg/allmodel.png);border:none } QPushButton:hover{ border-image:url(image/newimg/allmodel_2.png)}") self.playmodel.clicked.connect(self.moshi) # 当前播放时间 self.songTime = QLabel("",self) self.songTime.setGeometry(240,180,80,20) self.songTime.setStyleSheet("QLabel{ color:#AAA;font-size:12px;}") self.songTime.setAlignment(Qt.AlignHCenter) # 当前歌曲名 self.currentMusicName = QLabel("",songer_img) self.currentMusicName.setGeometry(0,180,200,20) self.currentMusicName.setStyleSheet("QLabel{ color:white ;font-weight:100;font-size:12px;margin-left:5px;}") # 歌曲进度条 self.processSlider = QSlider(Qt.Horizontal,self) self.processSlider.setGeometry(0,193,300,7) # self.processSlider.setRange(1,100) self.processSlider.setValue(0) self.processSlider.setStyleSheet(qss_process_slider) self.processSlider.setCursor(QCursor(Qt.UpArrowCursor)) # 歌曲列表 --------------------------- listWgt = QWidget(self) listWgt.setGeometry(0, 200, 300,380) listWgt.setStyleSheet(qss_scrollbar) #列表 self.songList = QListWidget(listWgt) self.songList.setGeometry(5,0,235,380) self.songList.setStyleSheet(qss_songlist) # 列表添加右键菜单 # self.songList.setContextMenuPolicy(Qt.CustomContextMenu) # self.songList.customContextMenuRequested.connect(self.rightMenuShow) #歌曲列表右边的功能列表 funcList = QListWidget(listWgt) funcList.setGeometry(240,0,55,380) funcList.setStyleSheet(qss_menu) btn = QPushButton("",funcList) btn.clicked.connect(self.newwindow) btn.setGeometry(15,10,30,30) btn.setStyleSheet("QPushButton{ border-image:url(image/home.png)} \ QPushButton:hover{ border-image:url(image/homehover.png) }") # btn.setCursor(QCursor(Qt.PointingHandCursor)) btn = QPushButton("",funcList) btn.setGeometry(15,60,30,30) btn.setStyleSheet("QPushButton{ border-image:url(image/tuijian.png) } \ QPushButton:hover{ border-image:url(image/tuijianhover.png) }") # btn.setCursor(QCursor(Qt.PointingHandCursor)) btn = QPushButton("",funcList) btn.setGeometry(15,100,30,30) btn.setStyleSheet("QPushButton{ border-image:url(image/shoucang.png) }\QPushButton:hover{ border-image:url(image/shoucanghover.png) }") # btn.setCursor(QCursor(Qt.PointingHandCursor)) btn = QPushButton("",funcList) btn.setGeometry(15,140,30,30) btn.setStyleSheet("QPushButton{ border-image:url(image/rizhi.png) }\ QPushButton:hover{ border-image:url(image/rizhihover.png) }") # btn.setCursor(QCursor(Qt.PointingHandCursor)) btn = QPushButton("",funcList) btn.setGeometry(17,180,30,30) btn.setStyleSheet("QPushButton{ border-image:url(image/mv.png) }\ QPushButton:hover{ border-image:url(image/mvhover.png) }") # btn.setCursor(QCursor(Qt.PointingHandCursor)) setbtn = QPushButton("",funcList) setbtn.setGeometry(15,225,33,33) setbtn.setStyleSheet("QPushButton{ border-image:url(image/settinghover.png) }\ QPushButton:hover{ border-image:url(image/setting.png) }") setbtn.clicked.connect(self.openseting) #底部状态栏 wg = QWidget(self) wg.setGeometry(0, 580, 300,20) wg.setStyleSheet("QWidget{ background:#2D2D2D; } ") # ql = QLabel(" <a style='color:#444;text-decoration:none;font-size:12px;' href ='https://github.com/codeAB/music-player' >S Y L </a>",wg) # ql.resize(300,20) # ql.setAlignment(Qt.AlignRight) # ql.linkActivated.connect(self.openurl) #设置托盘图标 tray = QSystemTrayIcon(self) tray.setIcon(QIcon('image/tray.png')) self.trayIconMenu = QMenu(self) self.trayIconMenu.setStyleSheet(qss_tray) showAction = QAction(QIcon('image/tray.png'),u"显示主面板", self,triggered=self.show) self.trayIconMenu.addAction(showAction) # self.trayIconMenu.addAction(preAction) # self.trayIconMenu.addAction(pauseAction) # self.trayIconMenu.addAction(nextAction) # self.trayIconMenu.addAction(quitAction) tray.setContextMenu(self.trayIconMenu) tray.show() tray.activated.connect(self.dbclick_tray)
class MainWindow(QMainWindow): """Voice Changer main window.""" def __init__(self, parent=None): super(MainWindow, self).__init__() self.statusBar().showMessage("Move Dial to Deform Microphone Voice !.") self.setWindowTitle(__doc__) self.setMinimumSize(240, 240) self.setMaximumSize(480, 480) self.resize(self.minimumSize()) self.setWindowIcon(QIcon.fromTheme("audio-input-microphone")) self.tray = QSystemTrayIcon(self) self.center() QShortcut("Ctrl+q", self, activated=lambda: self.close()) self.menuBar().addMenu("&File").addAction("Quit", lambda: exit()) self.menuBar().addMenu("Sound").addAction( "STOP !", lambda: call('killall rec', shell=True)) windowMenu = self.menuBar().addMenu("&Window") windowMenu.addAction("Hide", lambda: self.hide()) windowMenu.addAction("Minimize", lambda: self.showMinimized()) windowMenu.addAction("Maximize", lambda: self.showMaximized()) windowMenu.addAction("Restore", lambda: self.showNormal()) windowMenu.addAction("FullScreen", lambda: self.showFullScreen()) windowMenu.addAction("Center", lambda: self.center()) windowMenu.addAction("Top-Left", lambda: self.move(0, 0)) windowMenu.addAction("To Mouse", lambda: self.move_to_mouse_position()) # widgets group0 = QGroupBox("Voice Deformation") self.setCentralWidget(group0) self.process = QProcess(self) self.process.error.connect( lambda: self.statusBar().showMessage("Info: Process Killed", 5000)) self.control = QDial() self.control.setRange(-10, 20) self.control.setSingleStep(5) self.control.setValue(0) self.control.setCursor(QCursor(Qt.OpenHandCursor)) self.control.sliderPressed.connect( lambda: self.control.setCursor(QCursor(Qt.ClosedHandCursor))) self.control.sliderReleased.connect( lambda: self.control.setCursor(QCursor(Qt.OpenHandCursor))) self.control.valueChanged.connect( lambda: self.control.setToolTip(f"<b>{self.control.value()}")) self.control.valueChanged.connect( lambda: self.statusBar().showMessage( f"Voice deformation: {self.control.value()}", 5000)) self.control.valueChanged.connect(self.run) self.control.valueChanged.connect(lambda: self.process.kill()) # Graphic effect self.glow = QGraphicsDropShadowEffect(self) self.glow.setOffset(0) self.glow.setBlurRadius(99) self.glow.setColor(QColor(99, 255, 255)) self.control.setGraphicsEffect(self.glow) self.glow.setEnabled(False) # Timer to start self.slider_timer = QTimer(self) self.slider_timer.setSingleShot(True) self.slider_timer.timeout.connect(self.on_slider_timer_timeout) # an icon and set focus QLabel(self.control).setPixmap( QIcon.fromTheme("audio-input-microphone").pixmap(32)) self.control.setFocus() QVBoxLayout(group0).addWidget(self.control) self.menu = QMenu(__doc__) self.menu.addAction(__doc__).setDisabled(True) self.menu.setIcon(self.windowIcon()) self.menu.addSeparator() self.menu.addAction( "Show / Hide", lambda: self.hide() if self.isVisible() else self.showNormal()) self.menu.addAction("STOP !", lambda: call('killall rec', shell=True)) self.menu.addSeparator() self.menu.addAction("Quit", lambda: exit()) self.tray.setContextMenu(self.menu) self.make_trayicon() def run(self): """Run/Stop the QTimer.""" if self.slider_timer.isActive(): self.slider_timer.stop() self.glow.setEnabled(True) call('killall rec ; killall play', shell=True) self.slider_timer.start(3000) def on_slider_timer_timeout(self): """Run subprocess to deform voice.""" self.glow.setEnabled(False) value = int(self.control.value()) * 100 command = f'play -q -V0 "|rec -q -V0 -n -d -R riaa bend pitch {value} "' print(f"Voice Deformation Value: {value}") print(f"Voice Deformation Command: {command}") self.process.start(command) if self.isVisible(): self.statusBar().showMessage("Minimizing to System TrayIcon", 3000) print("Minimizing Main Window to System TrayIcon now...") sleep(3) self.hide() def center(self): """Center Window on the Current Screen,with Multi-Monitor support.""" window_geometry = self.frameGeometry() mousepointer_position = QApplication.desktop().cursor().pos() screen = QApplication.desktop().screenNumber(mousepointer_position) centerPoint = QApplication.desktop().screenGeometry(screen).center() window_geometry.moveCenter(centerPoint) self.move(window_geometry.topLeft()) def move_to_mouse_position(self): """Center the Window on the Current Mouse position.""" window_geometry = self.frameGeometry() window_geometry.moveCenter(QApplication.desktop().cursor().pos()) self.move(window_geometry.topLeft()) def make_trayicon(self): """Make a Tray Icon.""" if self.windowIcon() and __doc__: self.tray.setIcon(self.windowIcon()) self.tray.setToolTip(__doc__) self.tray.activated.connect( lambda: self.hide() if self.isVisible() else self.showNormal()) return self.tray.show()
class ElectrumGui(PrintError): @profiler def __init__(self, config, daemon, plugins): set_language(config.get('language', get_default_language())) # Uncomment this call to verify objects are being properly # GC-ed when windows are closed #network.add_jobs([DebugMem([Abstract_Wallet, SPV, Synchronizer, # ElectrumWindow], interval=5)]) QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) if hasattr(QtCore.Qt, "AA_ShareOpenGLContexts"): QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts) if hasattr(QGuiApplication, 'setDesktopFileName'): QGuiApplication.setDesktopFileName('electrum.desktop') self.gui_thread = threading.current_thread() self.config = config self.daemon = daemon self.plugins = plugins self.windows = [] self.efilter = OpenFileEventFilter(self.windows) self.app = QElectrumApplication(sys.argv) self.app.installEventFilter(self.efilter) self.app.setWindowIcon(read_QIcon("electrum.png")) # timer self.timer = QTimer(self.app) self.timer.setSingleShot(False) self.timer.setInterval(500) # msec self.nd = None self.network_updated_signal_obj = QNetworkUpdatedSignalObject() self._num_wizards_in_progress = 0 self._num_wizards_lock = threading.Lock() # init tray self.dark_icon = self.config.get("dark_icon", False) self.tray = QSystemTrayIcon(self.tray_icon(), None) self.tray.setToolTip('Electrum') self.tray.activated.connect(self.tray_activated) self.build_tray_menu() self.tray.show() self.app.new_window_signal.connect(self.start_new_window) self.set_dark_theme_if_needed() run_hook('init_qt', self) def set_dark_theme_if_needed(self): use_dark_theme = self.config.get('qt_gui_color_theme', 'default') == 'dark' if use_dark_theme: try: import qdarkstyle self.app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) except BaseException as e: use_dark_theme = False self.print_error('Error setting dark theme: {}'.format(repr(e))) # Even if we ourselves don't set the dark theme, # the OS/window manager/etc might set *a dark theme*. # Hence, try to choose colors accordingly: ColorScheme.update_from_widget(QWidget(), force_dark=use_dark_theme) def build_tray_menu(self): # Avoid immediate GC of old menu when window closed via its action if self.tray.contextMenu() is None: m = QMenu() self.tray.setContextMenu(m) else: m = self.tray.contextMenu() m.clear() for window in self.windows: submenu = m.addMenu(window.wallet.basename()) submenu.addAction(_("Show/Hide"), window.show_or_hide) submenu.addAction(_("Close"), window.close) m.addAction(_("Dark/Light"), self.toggle_tray_icon) m.addSeparator() m.addAction(_("Exit Electrum"), self.close) def tray_icon(self): if self.dark_icon: return read_QIcon('electrum_dark_icon.png') else: return read_QIcon('electrum_light_icon.png') def toggle_tray_icon(self): self.dark_icon = not self.dark_icon self.config.set_key("dark_icon", self.dark_icon, True) self.tray.setIcon(self.tray_icon()) def tray_activated(self, reason): if reason == QSystemTrayIcon.DoubleClick: if all([w.is_hidden() for w in self.windows]): for w in self.windows: w.bring_to_top() else: for w in self.windows: w.hide() def close(self): for window in self.windows: window.close() def new_window(self, path, uri=None): # Use a signal as can be called from daemon thread self.app.new_window_signal.emit(path, uri) def show_network_dialog(self, parent): if not self.daemon.network: parent.show_warning(_('You are using Electrum in offline mode; restart Electrum if you want to get connected'), title=_('Offline')) return if self.nd: self.nd.on_update() self.nd.show() self.nd.raise_() return self.nd = NetworkDialog(self.daemon.network, self.config, self.network_updated_signal_obj) self.nd.show() def _create_window_for_wallet(self, wallet): w = ElectrumWindow(self, wallet) self.windows.append(w) self.build_tray_menu() # FIXME: Remove in favour of the load_wallet hook run_hook('on_new_window', w) w.warn_if_watching_only() return w def count_wizards_in_progress(func): def wrapper(self: 'ElectrumGui', *args, **kwargs): with self._num_wizards_lock: self._num_wizards_in_progress += 1 try: return func(self, *args, **kwargs) finally: with self._num_wizards_lock: self._num_wizards_in_progress -= 1 return wrapper @count_wizards_in_progress def start_new_window(self, path, uri, *, app_is_starting=False): '''Raises the window for the wallet if it is open. Otherwise opens the wallet and creates a new window for it''' wallet = None try: wallet = self.daemon.load_wallet(path, None) except BaseException as e: traceback.print_exc(file=sys.stdout) QMessageBox.warning(None, _('Error'), _('Cannot load wallet') + ' (1):\n' + str(e)) # if app is starting, still let wizard to appear if not app_is_starting: return if not wallet: wallet = self._start_wizard_to_select_or_create_wallet(path) if not wallet: return # create or raise window try: for window in self.windows: if window.wallet.storage.path == wallet.storage.path: break else: window = self._create_window_for_wallet(wallet) except BaseException as e: traceback.print_exc(file=sys.stdout) QMessageBox.warning(None, _('Error'), _('Cannot create window for wallet') + ':\n' + str(e)) if app_is_starting: wallet_dir = os.path.dirname(path) path = os.path.join(wallet_dir, get_new_wallet_name(wallet_dir)) self.start_new_window(path, uri) return if uri: window.pay_to_URI(uri) window.bring_to_top() window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) window.activateWindow() return window def _start_wizard_to_select_or_create_wallet(self, path) -> Optional[Abstract_Wallet]: wizard = InstallWizard(self.config, self.app, self.plugins) try: path, storage = wizard.select_storage(path, self.daemon.get_wallet) # storage is None if file does not exist if storage is None: wizard.path = path # needed by trustedcoin plugin wizard.run('new') storage = wizard.create_storage(path) else: wizard.run_upgrades(storage) except (UserCancelled, GoBack): return except WalletAlreadyOpenInMemory as e: return e.wallet except (WalletFileException, BitcoinException) as e: traceback.print_exc(file=sys.stderr) QMessageBox.warning(None, _('Error'), _('Cannot load wallet') + ' (2):\n' + str(e)) return finally: wizard.terminate() # return if wallet creation is not complete if storage is None or storage.get_action(): return wallet = Wallet(storage) wallet.start_network(self.daemon.network) self.daemon.add_wallet(wallet) return wallet def close_window(self, window): if window in self.windows: self.windows.remove(window) self.build_tray_menu() # save wallet path of last open window if not self.windows: self.config.save_last_wallet(window.wallet) run_hook('on_close_window', window) self.daemon.stop_wallet(window.wallet.storage.path) def init_network(self): # Show network dialog if config does not exist if self.daemon.network: if self.config.get('auto_connect') is None: wizard = InstallWizard(self.config, self.app, self.plugins) wizard.init_network(self.daemon.network) wizard.terminate() def main(self): try: self.init_network() except UserCancelled: return except GoBack: return except BaseException as e: traceback.print_exc(file=sys.stdout) return self.timer.start() self.config.open_last_wallet() path = self.config.get_wallet_path() if not self.start_new_window(path, self.config.get('url'), app_is_starting=True): return signal.signal(signal.SIGINT, lambda *args: self.app.quit()) def quit_after_last_window(): # keep daemon running after close if self.config.get('daemon'): return # check if a wizard is in progress with self._num_wizards_lock: if self._num_wizards_in_progress > 0 or len(self.windows) > 0: return self.app.quit() self.app.setQuitOnLastWindowClosed(False) # so _we_ can decide whether to quit self.app.lastWindowClosed.connect(quit_after_last_window) def clean_up(): # Shut down the timer cleanly self.timer.stop() # clipboard persistence. see http://www.mail-archive.com/[email protected]/msg17328.html event = QtCore.QEvent(QtCore.QEvent.Clipboard) self.app.sendEvent(self.app.clipboard(), event) self.tray.hide() self.app.aboutToQuit.connect(clean_up) # main loop self.app.exec_() # on some platforms the exec_ call may not return, so use clean_up() def stop(self): self.print_error('closing GUI') self.app.quit()
class MainWindow(QMainWindow): """The main GUI application.""" def __init__(self, config): """Initializer for the GUI widgets. Pass in an instance of Config class, so that it may interact with the config.""" super().__init__() self.config = config self.setWindowTitle("Livestreamer GUI v{}".format(APPVERSION)) self.setup_systray() self.setup_menu() self.setup_geometry() self.livestreamer_thread = None self.thread_exit_grace_time = 10000 # How long a thread can take to exit in milliseconds self.timestamp_format = self.config.get_config_value("timestamp-format") self.setup_control_widgets() self.update_colors() # Load all streaming-related data self.selections = {"streamer": None, "channel": None} self.load_streamers() self.load_channels(self.streamer_input.currentText()) # Do the first configuration, if the application was run for the first time self.do_init_config() # Finally show the window and the system tray icon, if it should be shown self.show() self.close_override = False self.show_hide_systray() self.check_and_do_database_migration() def do_init_config(self): do_config = self.config.get_config_value("is-configured") if do_config == 0: self.menu_cmd_configure() self.config.set_config_value("is-configured", 1) self.insertText("Using config database version '{}'".format(self.config.get_config_value("db-version"))) def setup_systray(self): if not self.config.get_config_value("enable-systray-icon"): self.systray = None return self.systray = QSystemTrayIcon(self) self.systray.activated.connect(self.systray_activated) main_menu = QMenu(self) quit_action = QAction("&Quit", self) quit_action.triggered.connect(self.on_close_override) main_menu.addAction(quit_action) self.systray.setContextMenu(main_menu) def systray_activated(self, reason): if reason == QSystemTrayIcon.Trigger: if self.isVisible(): self.hide() else: self.showNormal() def check_and_do_database_migration(self): current_version = self.config.get_config_value("db-version") if self.config.is_migration_needed(): self.insertText("Detected pending config database upgrade to version '{}'. Awaiting user input...".format(DBVERSION)) message = "You are using an older version of the application config database.\n\nWould you like to upgrade the database now? Your existing config database will be backed up." upgrade_is_mandatory = current_version < MANDATORY_DBVERSION if upgrade_is_mandatory: message = message + "\n\nWARNING: Your config database is not compatible with this version of Livestreamer GUI. UPDATE IS MANDATORY! If you cancel the update, the application will exit." reply = QMessageBox.question(self, "Pending config database upgrade", message, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.Yes: self.insertText("Backing up config database...") backup = self.config.make_database_backup() self.insertText("Current config database backed up to '{}'".format(backup)) self.insertText("Config database update initialized...") self.update() self.config.execute_migration() new_version = self.config.get_config_value("db-version") self.insertText("Config database update from version '{}' to '{}' finished.".format(current_version, new_version)) elif reply == QMessageBox.No and upgrade_is_mandatory: QtCore.QTimer.singleShot(500, self.on_close_override) # self.on_close_override() # Calling this in an __init__()-called method doesn't seem to work... else: self.insertText("Config database update cancelled. No changes were made.") def setup_menu(self): config_action = QAction("&Configure...", self) config_action.triggered.connect(self.menu_cmd_configure) quit_action = QAction("&Quit", self) quit_action.setShortcut("Ctrl+Q") quit_action.triggered.connect(self.on_close_override) menu = self.menuBar() file_menu = menu.addMenu("&File") file_menu.addAction(config_action) file_menu.addSeparator() file_menu.addAction(quit_action) def setup_geometry(self): width = self.config.get_config_value("root-width") height = self.config.get_config_value("root-height") topleft = QApplication.desktop().availableGeometry().topLeft() if self.config.get_config_value("remember-window-position"): xoffset = self.config.get_config_value("root-xoffset") yoffset = self.config.get_config_value("root-yoffset") topleft.setX(self.config.get_config_value("root-xoffset")) topleft.setY(self.config.get_config_value("root-yoffset")) self.resize(width, height) self.setMinimumSize(500, 300) self.move(topleft) # Center the window # center_point = QApplication.desktop().availableGeometry().center() # frame_geometry = self.frameGeometry() # frame_geometry.moveCenter(center_point) # self.move(frame_geometry.topLeft()) def setup_control_widgets(self): self.cwidget = QWidget(self) self.setCentralWidget(self.cwidget) layout = QGridLayout(self.cwidget) self.cwidget.setLayout(layout) fg_fav = self.config.get_config_value("button-foreground-favorite") fg_edit = self.config.get_config_value("button-foreground-edit") fg_add = self.config.get_config_value("button-foreground-add") fg_delete = self.config.get_config_value("button-foreground-delete") control_button_width = 30 control_button_font_style = "QPushButton { font-family: Arial, sans-serif; font-size: 16px }" column = 0 label_streamer_input = QLabel("Streamer", self.cwidget) layout.addWidget(label_streamer_input, 0, column) label_channel_input = QLabel("Channel", self.cwidget) layout.addWidget(label_channel_input, 1, column) label_quality_input = QLabel("Stream quality", self.cwidget) layout.addWidget(label_quality_input, 2, column) column += 1 self.streamer_input = QComboBox(self.cwidget) self.streamer_input.setEnabled(False) self.streamer_input.currentIndexChanged.connect(self.on_streamer_select) layout.addWidget(self.streamer_input, 0, column) self.channel_input = QComboBox(self.cwidget) self.channel_input.setEnabled(False) self.channel_input.currentIndexChanged.connect(self.on_channel_select) layout.addWidget(self.channel_input, 1, column) self.quality_input = QComboBox(self.cwidget) self.quality_input.addItem("(auto-refresh is disabled; please refresh manually)") self.quality_input.setEnabled(False) layout.addWidget(self.quality_input, 2, column) layout.setColumnStretch(column, 5) column += 1 self.fav_streamer_button = QPushButton("\u2764", self.cwidget) self.fav_streamer_button.setMaximumWidth(control_button_width) self.fav_streamer_button.setStyleSheet(":enabled {{ color: {0} }} {1}".format(fg_fav, control_button_font_style)) self.fav_streamer_button.setEnabled(False) self.fav_streamer_button.setToolTip("Set the selected streamer as your most favorite streamer") self.fav_streamer_button.clicked.connect(self.cmd_set_favorite_streamer) layout.addWidget(self.fav_streamer_button, 0, column) self.fav_channel_button = QPushButton("\u2764", self.cwidget) self.fav_channel_button.setMaximumWidth(control_button_width) self.fav_channel_button.setStyleSheet(':enabled {{ color: {0} }} {1}'.format(fg_fav, control_button_font_style)) self.fav_channel_button.setEnabled(False) self.fav_channel_button.setToolTip("Set the selected channel as your most favorite channel") self.fav_channel_button.clicked.connect(self.cmd_set_favorite_channel) layout.addWidget(self.fav_channel_button, 1, column) self.clear_quality_cache_button = QPushButton("Refresh streams", self.cwidget) self.clear_quality_cache_button.setEnabled(False) self.clear_quality_cache_button.clicked.connect(self.cmd_refresh_quality_cache) layout.addWidget(self.clear_quality_cache_button, 2, column, 1, 4) column += 1 self.edit_streamer_button = QPushButton("\u270E", self.cwidget) self.edit_streamer_button.setMaximumWidth(control_button_width) self.edit_streamer_button.setStyleSheet(":enabled {{ color: {0} }} {1}".format(fg_edit, control_button_font_style)) self.edit_streamer_button.setEnabled(False) self.edit_streamer_button.setToolTip("Edit data about the selected streamer") self.edit_streamer_button.clicked.connect(self.cmd_edit_streamer) layout.addWidget(self.edit_streamer_button, 0, column) self.edit_channel_button = QPushButton("\u270E", self.cwidget) self.edit_channel_button.setMaximumWidth(control_button_width) self.edit_channel_button.setStyleSheet(":enabled {{ color: {0} }} {1}".format(fg_edit, control_button_font_style)) self.edit_channel_button.setToolTip("Edit data about the selected channel") self.edit_channel_button.clicked.connect(self.cmd_edit_channel) layout.addWidget(self.edit_channel_button, 1, column) column += 1 self.add_streamer_button = QPushButton("\u271A", self.cwidget) self.add_streamer_button.setMaximumWidth(control_button_width) self.add_streamer_button.setStyleSheet(":enabled {{ color: {0} }} {1}".format(fg_add, control_button_font_style)) self.add_streamer_button.setEnabled(False) self.add_streamer_button.setToolTip("Add a new streamer") self.add_streamer_button.clicked.connect(self.cmd_add_streamer) layout.addWidget(self.add_streamer_button, 0, column) self.add_channel_button = QPushButton("\u271A", self.cwidget) self.add_channel_button.setMaximumWidth(control_button_width) self.add_channel_button.setStyleSheet(":enabled {{ color: {0} }} {1}".format(fg_add, control_button_font_style)) self.add_channel_button.setToolTip("Add a new channel") self.add_channel_button.clicked.connect(self.cmd_add_channel) layout.addWidget(self.add_channel_button, 1, column) column += 1 self.delete_streamer_button = QPushButton("\u2716", self.cwidget) self.delete_streamer_button.setMaximumWidth(control_button_width) self.delete_streamer_button.setStyleSheet(":enabled {{ color: {0} }} {1}".format(fg_delete, control_button_font_style)) self.delete_streamer_button.setEnabled(False) self.delete_streamer_button.setToolTip("Remove the selected streamer permanently") self.delete_streamer_button.clicked.connect(self.cmd_delete_streamer) layout.addWidget(self.delete_streamer_button, 0, column) self.delete_channel_button = QPushButton("\u2716", self.cwidget) self.delete_channel_button.setMaximumWidth(control_button_width) self.delete_channel_button.setStyleSheet(":enabled {{ color: {0} }} {1}".format(fg_delete, control_button_font_style)) self.delete_channel_button.setToolTip("Remove the selected channel permanently") self.delete_channel_button.clicked.connect(self.cmd_delete_channel) layout.addWidget(self.delete_channel_button, 1, column) # Add button for running livestreamer at the fourth row self.run_livestreamer_button = QPushButton("Run Livestreamer", self.cwidget) self.run_livestreamer_button.setEnabled(False) self.run_livestreamer_button.clicked.connect(self.run_livestreamer) layout.addWidget(self.run_livestreamer_button, 3, 0) self.log_widget = QTextEdit(self.cwidget) layout.addWidget(self.log_widget, 4, 0, 1, column+1) self.log_widget.setAcceptRichText(False) self.log_widget.setReadOnly(True) self.log_widget.setTabChangesFocus(True) def set_window_icon(self): """Sets the root window's icon, which is also shown in the taskbar.""" streamer = self.config.get_streamer(self.streamer_input.currentText()) icon = QIcon(os.path.join(IMAGESROOT, streamer["icon"])) self.setWindowIcon(icon) if self.systray is not None: self.systray.setIcon(icon) def closeEvent(self, event): """When the QWidget is closed, QCloseEvent is triggered, and this method catches and handles it.""" if not self.close_override and self.put_to_systray("close"): event.ignore() return if self.livestreamer_thread is not None and self.livestreamer_thread.keep_running: reply = QMessageBox.question(self, "Really quit Livestreamer GUI?", "Livestreamer is still running. Quitting will close it and the opened player.\n\nQuit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: # Terminate the child process, else it'll keep running even after this application is closed if self.livestreamer_thread is not None: self.livestreamer_thread.term_process() self.livestreamer_thread.wait(self.thread_exit_grace_time) self.update() event.accept() else: event.ignore() # Explicitly hide the icon, if it remains visible after the application closes if self.systray is not None: self.systray.hide() # Remember the position of the window self.remember_window_position() event.accept() def changeEvent(self, event): if type(event) is not QWindowStateChangeEvent: return # It's one of the window state change events (normal, minimize, maximize, fullscreen, active) if self.isMinimized(): self.put_to_systray("minimize") def remember_window_position(self): if self.config.get_config_value("remember-window-position"): point = self.frameGeometry().topLeft() self.config.set_config_value("root-xoffset", point.x()) self.config.set_config_value("root-yoffset", point.y()) self.insertText("Window position saved.") def show_hide_systray(self): if self.systray is None: self.setup_systray() if self.systray is None: return if self.config.get_config_value("enable-systray-icon"): self.systray.show() else: self.systray.hide() def put_to_systray(self, event): if event == "minimize": config_value = "minimize-to-systray" elif event == "close": config_value = "close-to-systray" else: return False if self.systray is not None and self.config.get_config_value(config_value) and self.isVisible(): self.hide() return True return False def menu_cmd_configure(self): streamer = self.config.get_streamer(self.streamer_input.currentText()) dialog = AppConfigDialog(self, self.config, streamer_icon=os.path.join(IMAGESROOT, streamer["icon"])) dialog.exec() if dialog.result() == QDialog.Accepted: self.show_hide_systray() self.update_colors() dialog.close() dialog = None def cmd_set_favorite_streamer(self): raise NotImplementedException() # self.fav_streamer_button.setEnabled(False) # self.config.set_favorite_streamer(self.streamer_input.setCurrentText()) # self.insertText("Favorited streamer '{}'.".format(self.streamer_input.setCurrentText())) def cmd_edit_streamer(self): raise NotImplementedException() def cmd_add_streamer(self): raise NotImplementedException() def cmd_delete_streamer(self): raise NotImplementedException() def cmd_set_favorite_channel(self): self.fav_channel_button.setEnabled(False) self.config.set_favorite_channel(self.streamer_input.currentText(), self.channel_input.currentText()) self.insertText("Favorited channel '{}'.".format(self.channel_input.currentText())) def cmd_edit_channel(self): streamer = self.config.get_streamer(self.streamer_input.currentText()) streamer_icon = os.path.join(IMAGESROOT, streamer["icon"]) channel_data = self.config.get_streamer_channel(streamer["name"], self.channel_input.currentText()) dialog = AddEditChannelsDialog(self, self.config, title="Edit the channel", streamer_icon=streamer_icon, streamer=streamer, channel_data=channel_data) dialog.exec() result = dialog.result_data dialog.close() dialog = None if result is not None: self.insertText("Updated channel name '{old_name}' => '{new_name}, URL '{old_url}' => '{new_url}'".format(old_name=channel_data["name"], new_name=result["name"], old_url=channel_data["url"], new_url=result["url"])) self.load_channels(streamer["name"]) # Set the active channel to the previously selected (due to possible name change and sorting) self.channel_input.setCurrentIndex(self.channel_input.findText(result["name"])) def cmd_add_channel(self): streamer = self.config.get_streamer(self.streamer_input.currentText()) streamer_icon = os.path.join(IMAGESROOT, streamer["icon"]) dialog = AddEditChannelsDialog(self, self.config, title="Add a channel", streamer_icon=streamer_icon, streamer=streamer) dialog.exec() result = dialog.result_data dialog.close() dialog = None if result is not None: self.insertText("Added channel '{}' with URL '{}'".format(result["name"], result["url"])) self.load_channels(streamer["name"]) def cmd_delete_channel(self): channel = self.config.get_streamer_channel(self.streamer_input.currentText(), self.channel_input.currentText()) reply = QMessageBox.question(self, "Delete channel", "Are you sure you want to remove the channel?\nName: {}\nURL: {}".format(channel["name"], channel["url"]), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.config.delete_channel(self.streamer_input.currentText(), channel["name"]) self.insertText("Removed channel '{}' with URL '{}'".format(channel["name"], channel["url"])) self.load_channels(self.streamer_input.currentText()) def cmd_refresh_quality_cache(self): self.insertText("Refreshing cache for channel '{}'.".format(self.channel_input.currentText())) self.clear_quality_cache_button.setEnabled(False) self.clear_quality_cache_button.repaint() # Loading streams seems to block repainting of the GUI, so force a repaint here self.config.clean_quality_cache(self.streamer_input.currentText(), self.channel_input.currentText(), True) self.load_streams(True) self.clear_quality_cache_button.setEnabled(True) def on_close_override(self): self.close_override = True self.close() def on_streamer_select(self, event): # If the previously selected item is selected again, don't do anything if self.selections["streamer"] == self.streamer_input.currentText(): return self.selections["streamer"] = self.streamer_input.currentText() streamer = self.config.get_streamer(self.streamer_input.currentText()) self.set_window_icon() if streamer["favorite"]: self.fav_streamer_button.setEnabled(False) else: self.fav_streamer_button.setEnabled(True) def on_channel_select(self, event): # If the previously selected item is selected again, don't do anything if self.selections["channel"] == self.channel_input.currentText() or not self.channel_input.currentText(): return self.selections["channel"] = self.channel_input.currentText() channel = self.config.get_streamer_channel(self.streamer_input.currentText(), self.channel_input.currentText()) if channel and channel["favorite"]: self.fav_channel_button.setEnabled(False) else: self.fav_channel_button.setEnabled(True) self.load_streams() self.channel_input.setFocus(True) def load_streamers(self): streamers = self.config.get_streamers() favorite_streamer_index = 0 streamer_list = [] for index, streamer in enumerate(streamers): streamer_list.append(streamer["name"]) if streamer["favorite"]: favorite_streamer_index = index self.streamer_input.clear() self.streamer_input.addItems(streamer_list) if len(streamer_list) != 0: self.streamer_input.setCurrentIndex(favorite_streamer_index) self.selections["streamer"] = self.streamer_input.currentText() self.fav_streamer_button.setEnabled(False) def load_channels(self, streamer_name): channels = self.config.get_streamer_channels(streamer_name) self.channel_input.clear() favorite_channel = None channel_list = [] self.fav_channel_button.setEnabled(False) for index, channel in enumerate(channels): channel_list.append(channel["name"]) if channel["favorite"]: favorite_channel = channel["name"] self.channel_input.addItems(sorted(channel_list)) if len(channel_list) == 0: self.channel_input.addItem("(no channels exist for this streamer)") self.fav_channel_button.setEnabled(False) self.edit_channel_button.setEnabled(False) self.delete_channel_button.setEnabled(False) self.clear_quality_cache_button.setEnabled(False) self.channel_input.setEnabled(False) else: self.edit_channel_button.setEnabled(True) self.delete_channel_button.setEnabled(True) self.clear_quality_cache_button.setEnabled(True) self.channel_input.setEnabled(True) if favorite_channel is None: self.channel_input.setCurrentIndex(0) self.fav_channel_button.setEnabled(True) else: self.channel_input.setCurrentIndex(self.channel_input.findText(favorite_channel)) self.selections["channel"] = self.channel_input.currentText() def display_loaded_streams(self, streams, skip_caching=False): self.quality_input.clear() if len(streams) == 0: self.quality_input.addItem("(channel is currently not streaming)") else: self.run_livestreamer_button.setEnabled(True) self.clear_quality_cache_button.setEnabled(True) self.quality_input.addItems(sorted(streams)) self.quality_input.setCurrentIndex(0) self.quality_input.setEnabled(True) if not skip_caching: self.insertText("Cleaning any cached streams for channel '{}'...".format(self.channel_input.currentText())) self.config.clean_quality_cache(self.streamer_input.currentText(), self.channel_input.currentText()) self.insertText("Adding probed streams for channel '{}' to cache...".format(self.channel_input.currentText())) self.config.add_quality_to_cache(self.streamer_input.currentText(), self.channel_input.currentText(), streams) self.insertText("Done.") def load_streams(self, force_refresh=False): self.quality_input.clear() self.run_livestreamer_button.setEnabled(False) self.channel_input.setEnabled(False) self.quality_input.setEnabled(False) if self.channel_input.count() == 0: return streams = self.config.get_quality_from_cache(self.streamer_input.currentText(), self.channel_input.currentText()) if len(streams) > 0: self.display_loaded_streams(streams, True) self.insertText("Loaded streams for channel '{}' from cache.".format(self.channel_input.currentText())) else: self.insertText("No cached channel streams found for channel '{}'".format(self.channel_input.currentText())) if not force_refresh and self.config.get_config_value('auto-refresh-quality') == 0: self.quality_input.addItem("(auto-refresh is disabled; please refresh manually)") self.quality_input.setEnabled(False) else: stream_url = self.get_streamer_url() if stream_url is None: self.insertText("Failed to form a complete streamer URL (missing streamer/channel/stream)!") return self.probe_for_streams(stream_url) self.channel_input.setEnabled(True) def probe_for_streams(self, stream_url): self.insertText("Probing streamer's channel for live streams: {}".format(stream_url)) livestreamer = self.config.get_config_value("livestreamer-path") if livestreamer is None or livestreamer.strip() == "" or not os.path.isfile(livestreamer): self.insertText("Livestreamer path is not configured or file doesn't exist!") return command_format = self.config.get_config_value("probe-command-format") command = command_format.format(livestreamer=livestreamer, url=stream_url) self.livestreamer_thread = LivestreamerWorker(shlex.split(command)) self.livestreamer_thread.statusMessage.connect(self.parse_probed_streams, False) self.livestreamer_thread.start() self.livestreamer_thread.wait(self.thread_exit_grace_time) def parse_probed_streams(self, event): streams = [] message = event.message.lower() if "no streams found on this url" in message: self.insertText("No streams found. The channel is probably not streaming.") else: pos = message.find("available streams:") if pos == -1: return if "(best, worst)" in message: message = message.replace("(best, worst)", "(best and worst)") elif "(worst, best)" in message: message = message.replace("(worst, best)", "(worst and best)") qualities = message[pos+18:].split(",") for item in qualities: streams.append(item.strip()) left_parenthesis = item.find("(") if left_parenthesis == -1: continue if item.find("worst", left_parenthesis) >= left_parenthesis: streams.append("worst") if item.find("best", left_parenthesis) >= left_parenthesis: streams.append("best") streams.sort() self.insertText("Found {} stream(s): {}".format(len(streams), ", ".join(streams))) self.display_loaded_streams(streams) def get_streamer_url(self): streamer = self.config.get_streamer(self.streamer_input.currentText()) if streamer is None: self.insertText("No streamer selected!") return if streamer["url"] is None or streamer["url"].strip() == "": self.insertText("Invalid streamer URL!") return if self.channel_input.count() == 0: self.insertText("No channels exist!") return channel = self.config.get_streamer_channel(streamer["name"], self.channel_input.currentText()) return urljoin(streamer["url"], channel["url"]) def run_livestreamer(self): if self.livestreamer_thread is not None: if self.livestreamer_thread.isRunning(): self.insertText("Livestreamer should still be running!") return else: self.livestreamer_thread.wait(self.thread_exit_grace_time) self.livestreamer_thread = None self.update() if self.livestreamer_thread is None: livestreamer = self.config.get_config_value("livestreamer-path") if livestreamer is None or livestreamer.strip() == "" or not os.path.isfile(livestreamer): self.insertText("Livestreamer path is not configured or file doesn't exist!") return player = self.config.get_config_value("player-path") if player is None or player.strip() == "" or not os.path.isfile(player): self.insertText("Player path is not configured or file doesn't exist!") return stream_url = self.get_streamer_url() if stream_url is None: self.insertText("Failed to form a complete streamer URL (missing streamer/channel/stream)!") return command_format = self.config.get_config_value("command-format") quality = self.quality_input.currentText() if "(" in quality: quality = quality[:quality.find("(")].strip() command = command_format.format(livestreamer=livestreamer, player=player, url=stream_url, quality=quality) self.livestreamer_thread = LivestreamerWorker(shlex.split(command)) self.insertText("Starting Livestreamer thread.") self.livestreamer_thread.finished.connect(self.handle_livestreamer_thread_finished_signal) self.livestreamer_thread.statusMessage.connect(self.handle_livestreamer_thread_message_signal) self.livestreamer_thread.start() @QtCore.pyqtSlot(object) def handle_livestreamer_thread_message_signal(self, event): self.insertText(event.message, event.add_newline, event.add_timestamp) def handle_livestreamer_thread_finished_signal(self): self.livestreamer_thread = None def update_colors(self): foreground_color = self.config.get_config_value("foreground-color") background_color = self.config.get_config_value("background-color") self.cwidget.setStyleSheet("QWidget QLabel {{ color: {0} }} .QWidget {{ background-color: {1} }}".format(foreground_color, background_color)) self.cwidget.update() def insertText(self, msg, add_newline=True, timestamp=True): """Helper method for outputting text to the text box.""" text = "" if timestamp and self.timestamp_format is not None: timestamp = format(datetime.now().strftime(self.timestamp_format)) text = "{} ".format(timestamp) text += msg self.log_widget.moveCursor(QTextCursor.End) self.log_widget.insertPlainText(text) if add_newline: self.log_widget.insertPlainText("\n") self.log_widget.update()