class GlobalHotkey(QObject): pressed = Signal() def __init__(self, parent: Optional[QObject]): super().__init__(parent) self._hk = SystemHotkey() def is_registered(self, key): key = set(key) return any(set(k) == key for k in self._hk.keybinds) def register(self, key_sequence: Optional[QKeySequence]): key = self.convert_key_sequence(key_sequence) if key and self.is_registered(key): logger.debug("Already registered: %s", key) return self.unregister() if key_sequence and not key_sequence.isEmpty(): logger.info("Registering hotkey: %s", key) # If this fails, we get a system_hotkey.SystemRegisterError # exception, but in a different thread - so we can neither catch it # here nor is it logged. self._hk.register(key, callback=self._callback) @staticmethod def convert_key_sequence(key_sequence: QKeySequence): if not key_sequence or key_sequence.isEmpty(): return None # Restrict to the first key key_sequence = QKeySequence(key_sequence[0]) # Convert to format for SystemHotkey - simple approach, may not be # sufficient. key = key_sequence.toString(QKeySequence.PortableText).split("+") key = [part.lower() for part in key] if "meta" in key: key.remove("meta") key = ["super"] + key return key def unregister(self): for key in self._hk.keybinds: logger.info("Unregistering hotkey: %s", (key, )) self._hk.unregister(key) def _callback(self, *_, **__): self.pressed.emit()
def test_errors_raised_in_main(): hk = SystemHotkey() key = ('5',) cb = lambda e: print('hi') hk.register(key, callback=cb) try: hk.register(key, callback=cb) except SystemRegisterError: pass else: raise Exception('fail') hk.unregister(key) try: hk.unregister(key) except UnregisterError: pass else: raise Exception('fail') bad_key = ('badkey ..',) try: hk.register(bad_key, callback=cb) except InvalidKeyError: pass else: raise Exception('fail') try: hk.unregister(bad_key) except: pass else: raise Exception('fail')
def test_errors_raised_in_main(): hk = SystemHotkey() key = ('5', ) cb = lambda e: print('hi') hk.register(key, callback=cb) try: hk.register(key, callback=cb) except SystemRegisterError: pass else: raise Exception('fail') hk.unregister(key) try: hk.unregister(key) except UnregisterError: pass else: raise Exception('fail') bad_key = ('badkey ..', ) try: hk.register(bad_key, callback=cb) except InvalidKeyError: pass else: raise Exception('fail') try: hk.unregister(bad_key) except: pass else: raise Exception('fail')
class TaskBarIcon(wx.adv.TaskBarIcon): """Taskbar icon and menu class.""" def __init__(self, frame): self.g_settings = GeneralSettingsData() self.frame = frame super(TaskBarIcon, self).__init__() self.set_icon(TRAY_ICON) self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.on_left_down) # profile initialization self.job_lock = Lock() get_display_data() self.repeating_timer = None self.pause_item = None self.is_paused = False if sp_logging.DEBUG: sp_logging.G_LOGGER.info("START Listing profiles for menu.") self.list_of_profiles = list_profiles() if sp_logging.DEBUG: sp_logging.G_LOGGER.info("END Listing profiles for menu.") # Should now return an object if a previous profile was written or # None if no previous data was found self.active_profile = read_active_profile() self.start_prev_profile(self.active_profile) # if self.active_profile is None: # sp_logging.G_LOGGER.info("Starting up the first profile found.") # self.start_profile(wx.EVT_MENU, self.list_of_profiles[0]) # self.hk = None # self.hk2 = None if self.g_settings.use_hotkeys is True: try: # import keyboard # https://github.com/boppreh/keyboard # This import is here to have the module in the class scope from system_hotkey import SystemHotkey self.hk = SystemHotkey(check_queue_interval=0.05) self.hk2 = SystemHotkey(consumer=self.profile_consumer, check_queue_interval=0.05) self.seen_binding = set() self.register_hotkeys() except ImportError as excep: sp_logging.G_LOGGER.info( "WARNING: Could not import keyboard hotkey hook library, \ hotkeys will not work. Exception: %s", excep) if self.g_settings.show_help is True: config_frame = ConfigFrame(self) help_frame = HelpFrame() def register_hotkeys(self): """Registers system-wide hotkeys for profiles and application interaction.""" if self.g_settings.use_hotkeys is True: try: # import keyboard # https://github.com/boppreh/keyboard # This import allows access to the specific errors in this method. from system_hotkey import (SystemHotkey, SystemHotkeyError, SystemRegisterError, UnregisterError, InvalidKeyError) except ImportError as import_e: sp_logging.G_LOGGER.info( "WARNING: Could not import keyboard hotkey hook library, \ hotkeys will not work. Exception: %s", import_e) if "system_hotkey" in sys.modules: try: # Keyboard bindings: https://github.com/boppreh/keyboard # # Alternative KB bindings for X11 systems and Windows: # system_hotkey https://github.com/timeyyy/system_hotkey # seen_binding = set() # self.hk = SystemHotkey(check_queue_interval=0.05) # self.hk2 = SystemHotkey( # consumer=self.profile_consumer, # check_queue_interval=0.05) # Unregister previous hotkeys if self.seen_binding: for binding in self.seen_binding: try: self.hk.unregister(binding) if sp_logging.DEBUG: sp_logging.G_LOGGER.info( "Unreg hotkey %s", binding) except (SystemHotkeyError, UnregisterError, InvalidKeyError): try: self.hk2.unregister(binding) if sp_logging.DEBUG: sp_logging.G_LOGGER.info( "Unreg hotkey %s", binding) except (SystemHotkeyError, UnregisterError, InvalidKeyError): if sp_logging.DEBUG: sp_logging.G_LOGGER.info( "Could not unreg hotkey '%s'", binding) self.seen_binding = set() # register general bindings if self.g_settings.hk_binding_next not in self.seen_binding: try: self.hk.register(self.g_settings.hk_binding_next, callback=lambda x: self. next_wallpaper(wx.EVT_MENU), overwrite=False) self.seen_binding.add( self.g_settings.hk_binding_next) except (SystemHotkeyError, SystemRegisterError, InvalidKeyError): msg = "Error: could not register hotkey {}. \ Check that it is formatted properly and valid keys.".format( self.g_settings.hk_binding_next) sp_logging.G_LOGGER.info(msg) sp_logging.G_LOGGER.info(sys.exc_info()[0]) show_message_dialog(msg, "Error") if self.g_settings.hk_binding_pause not in self.seen_binding: try: self.hk.register(self.g_settings.hk_binding_pause, callback=lambda x: self. pause_timer(wx.EVT_MENU), overwrite=False) self.seen_binding.add( self.g_settings.hk_binding_pause) except (SystemHotkeyError, SystemRegisterError, InvalidKeyError): msg = "Error: could not register hotkey {}. \ Check that it is formatted properly and valid keys.".format( self.g_settings.hk_binding_pause) sp_logging.G_LOGGER.info(msg) sp_logging.G_LOGGER.info(sys.exc_info()[0]) show_message_dialog(msg, "Error") # try: # self.hk.register(('control', 'super', 'shift', 'q'), # callback=lambda x: self.on_exit(wx.EVT_MENU)) # except (SystemHotkeyError, SystemRegisterError, InvalidKeyError): # pass # register profile specific bindings self.list_of_profiles = list_profiles() for profile in self.list_of_profiles: if sp_logging.DEBUG: sp_logging.G_LOGGER.info( "Registering binding: \ %s for profile: %s", profile.hk_binding, profile.name) if (profile.hk_binding is not None and profile.hk_binding not in self.seen_binding): try: self.hk2.register(profile.hk_binding, profile, overwrite=False) self.seen_binding.add(profile.hk_binding) except (SystemHotkeyError, SystemRegisterError, InvalidKeyError): msg = "Error: could not register hotkey {}. \ Check that it is formatted properly and valid keys.".format(profile.hk_binding) sp_logging.G_LOGGER.info(msg) sp_logging.G_LOGGER.info(sys.exc_info()[0]) show_message_dialog(msg, "Error") elif profile.hk_binding in self.seen_binding: msg = "Could not register hotkey: '{}' for profile: '{}'.\n\ It is already registered for another action.".format(profile.hk_binding, profile.name) sp_logging.G_LOGGER.info(msg) show_message_dialog(msg, "Error") except (SystemHotkeyError, SystemRegisterError, UnregisterError, InvalidKeyError): if sp_logging.DEBUG: sp_logging.G_LOGGER.info( "Coulnd't register hotkeys, exception:") sp_logging.G_LOGGER.info(sys.exc_info()[0]) def profile_consumer(self, event, hotkey, profile): """Hotkey bindable method that starts up a profile.""" if sp_logging.DEBUG: sp_logging.G_LOGGER.info("Profile object is: %s", profile) self.start_profile(wx.EVT_MENU, profile[0][0]) def read_general_settings(self): """Refreshes general settings from file and applies hotkey bindings.""" self.g_settings = GeneralSettingsData() self.register_hotkeys() msg = "New settings are applied after an application restart. \ New hotkeys are registered." show_message_dialog(msg, "Info") def CreatePopupMenu(self): """Method called by WX library when user right clicks tray icon. Opens tray menu.""" menu = wx.Menu() create_menu_item(menu, "Open Config Folder", self.open_config) create_menu_item(menu, "Profile Configuration", self.configure_profiles) create_menu_item(menu, "Settings", self.configure_settings) create_menu_item(menu, "Reload Profiles", self.reload_profiles) menu.AppendSeparator() for item in self.list_of_profiles: create_menu_item(menu, item.name, self.start_profile, item) menu.AppendSeparator() create_menu_item(menu, "Next Wallpaper", self.next_wallpaper) self.pause_item = create_menu_item(menu, "Pause Timer", self.pause_timer, kind=wx.ITEM_CHECK) self.pause_item.Check(self.is_paused) menu.AppendSeparator() create_menu_item(menu, 'About', self.on_about) create_menu_item(menu, 'Exit', self.on_exit) return menu def set_icon(self, path): """Sets tray icon.""" icon = wx.Icon(path) self.SetIcon(icon, TRAY_TOOLTIP) def on_left_down(self, *event): """Allows binding left click event.""" sp_logging.G_LOGGER.info('Tray icon was left-clicked.') def open_config(self, event): """Opens Superpaper config folder, CONFIG_PATH.""" if platform.system() == "Windows": try: os.startfile(sp_paths.CONFIG_PATH) except BaseException: show_message_dialog( "There was an error trying to open the config folder.") elif platform.system() == "Darwin": try: subprocess.check_call(["open", sp_paths.CONFIG_PATH]) except subprocess.CalledProcessError: show_message_dialog( "There was an error trying to open the config folder.") else: try: subprocess.check_call(['xdg-open', sp_paths.CONFIG_PATH]) except subprocess.CalledProcessError: show_message_dialog( "There was an error trying to open the config folder.") def configure_profiles(self, event): """Opens profile configuration panel.""" config_frame = ConfigFrame(self) def configure_settings(self, event): """Opens general settings panel.""" setting_frame = SettingsFrame(self) def reload_profiles(self, event): """Reloads profiles from disk.""" self.list_of_profiles = list_profiles() def start_prev_profile(self, profile): """Checks if a previously running profile has been recorded and starts it.""" with self.job_lock: if profile is None: sp_logging.G_LOGGER.info("No previous profile was found.") else: self.repeating_timer = run_profile_job(profile) def start_profile(self, event, profile): """ Starts a profile job, i.e. runs a slideshow or a one time wallpaper change. If the input profile is the currently active profile, initiate a wallpaper change. """ if sp_logging.DEBUG: sp_logging.G_LOGGER.info("Start profile: %s", profile.name) if profile is None: sp_logging.G_LOGGER.info("start_profile: profile is None. \ Do you have any profiles in /profiles?") elif self.active_profile is not None: if sp_logging.DEBUG: sp_logging.G_LOGGER.info( "Check if the starting profile is already running: %s", profile.name) sp_logging.G_LOGGER.info("name check: %s, %s", profile.name, self.active_profile.name) if profile.name == self.active_profile.name: self.next_wallpaper(event) return 0 else: with self.job_lock: if (self.repeating_timer is not None and self.repeating_timer.is_running): self.repeating_timer.stop() if sp_logging.DEBUG: sp_logging.G_LOGGER.info( "Running quick profile job with profile: %s", profile.name) quick_profile_job(profile) if sp_logging.DEBUG: sp_logging.G_LOGGER.info( "Starting timed profile job with profile: %s", profile.name) self.repeating_timer = run_profile_job(profile) self.active_profile = profile write_active_profile(profile.name) if sp_logging.DEBUG: sp_logging.G_LOGGER.info("Wrote active profile: %s", profile.name) return 0 else: with self.job_lock: if (self.repeating_timer is not None and self.repeating_timer.is_running): self.repeating_timer.stop() if sp_logging.DEBUG: sp_logging.G_LOGGER.info( "Running quick profile job with profile: %s", profile.name) quick_profile_job(profile) if sp_logging.DEBUG: sp_logging.G_LOGGER.info( "Starting timed profile job with profile: %s", profile.name) self.repeating_timer = run_profile_job(profile) self.active_profile = profile write_active_profile(profile.name) if sp_logging.DEBUG: sp_logging.G_LOGGER.info("Wrote active profile: %s", profile.name) return 0 def next_wallpaper(self, event): """Calls the next wallpaper changer method of the running profile.""" with self.job_lock: if (self.repeating_timer is not None and self.repeating_timer.is_running): self.repeating_timer.stop() change_wallpaper_job(self.active_profile) self.repeating_timer.start() else: change_wallpaper_job(self.active_profile) def rt_stop(self): """Stops running slideshow timer if one is active.""" if (self.repeating_timer is not None and self.repeating_timer.is_running): self.repeating_timer.stop() def pause_timer(self, event): """Check if a slideshow timer is running and if it is, then try to stop/start.""" if (self.repeating_timer is not None and self.repeating_timer.is_running): self.repeating_timer.stop() self.is_paused = True if sp_logging.DEBUG: sp_logging.G_LOGGER.info("Paused timer") elif (self.repeating_timer is not None and not self.repeating_timer.is_running): self.repeating_timer.start() self.is_paused = False if sp_logging.DEBUG: sp_logging.G_LOGGER.info("Resumed timer") else: sp_logging.G_LOGGER.info("Current profile isn't using a timer.") def on_about(self, event): """Opens About dialog.""" # Credit for AboutDiaglog example to Jan Bodnar of # http://zetcode.com/wxpython/dialogs/ description = ( "Superpaper is an advanced multi monitor wallpaper\n" + "manager for Unix and Windows operating systems.\n" + "Features include setting a single or multiple image\n" + "wallpaper, pixel per inch and bezel corrections,\n" + "manual pixel offsets for tuning, slideshow with\n" + "configurable file order, multiple path support and more.") licence = ("Superpaper is free software; you can redistribute\n" + "it and/or modify it under the terms of the MIT" + " License.\n\n" + "Superpaper is distributed in the hope that it will" + " be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied" + " warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" + "See the MIT License for more details.") artists = "Icons kindly provided by Icons8 https://icons8.com" info = wx.adv.AboutDialogInfo() info.SetIcon(wx.Icon(TRAY_ICON, wx.BITMAP_TYPE_PNG)) info.SetName('Superpaper') info.SetVersion(__version__) info.SetDescription(description) info.SetCopyright('(C) 2019 Henri Hänninen') info.SetWebSite('https://github.com/hhannine/Superpaper/') info.SetLicence(licence) info.AddDeveloper('Henri Hänninen') info.AddArtist(artists) # info.AddDocWriter('Doc Writer') # info.AddTranslator('Tran Slator') wx.adv.AboutBox(info) def on_exit(self, event): """Exits Superpaper.""" self.rt_stop() wx.CallAfter(self.Destroy) self.frame.Close()
class ShkAdapterMixin(QObject): ''' Minimal interface adapter for global hotkeys ''' # This class may seem confusing at first. The complexity is due to Qt. # Qt's system doesn't like it when qt functions are preformed on anything # other than the main thread. This is a problem for system hotkey, which # always invokes callbacks on a different thread. So, this class sets up # a Qt signal and slot, and uses this to marshal the SHK callback onto # the main Qt thread, which then invokes the command bound to the hotkey. # Key points: _hotkeyPressed receives callback from SHK. It emits # _commandInvoked with the name of the command. _invokeCommand is # connected to this signal. It receives the command name, looks up # the *actual* command callback in the dictionary and invokes it. # Signal that indicates when a command-invoking hotkey was pressed _commandInvoked = pyqtSignal(str) def __init__(self): super().__init__() self.hk = SystemHotkey(consumer=self._hotkeyPressed) self._commandInvoked.connect(self.invokeCommand) #enddef def _hotkeyPressed(self, event, hotkey, args): ''' Adapts the global callback from system hotkey into a signal ''' self._commandInvoked.emit(args[0][0]) #enddef def load(self): super().load() self.applyHotkeys() #enddef def _bind(self, seq, cmd): ''' Binds a hotkey to a command name. seq can either be a QKeySequence or a string parseable by QKeySequence eg "ctrl+shift+k" ''' try: seq = QKeySequence(seq) except: pass t = seqToTuple(seq) log.debug('Binding hotkey "{hotkey}" => "{command}"', hotkey=seq.toString(), command=cmd) try: self.hk.register(t, cmd) except SystemRegisterError: log.exception('Failed to bind hotkey "{hotkey}"', hotkey=seq.toString()) else: return True #endtry return False #enddef def _unbind(self, seq, quiet=False): ''' Removes a hotkey binding. If Quiet is true, will not warn if the binding doesn't exist ''' try: seq = QKeySequence(seq) except: pass t = seqToTuple(seq) log.debug('Unbinding hotkey "{hotkey}"', hotkey=seq.toString()) try: self.hk.unregister(t) except UnregisterError: if not quiet: log.warn('Tried to unbind nonexistent hotkey "{hotkey}"', hotkey=seq.toString()) else: return True #endtry return False #enddef # TODO: These should directly use QKeySequence in the config def commandHasHotkey(self, command): ''' Checks if the given command has a hotkey registered for it ''' return not self.hotkeyForCommand(command).isEmpty() #enddef def hotkeyForCommand(self, command): for seq, cmd in config.default.get('hotkeys', {}).items(): if cmd == command: return QKeySequence(seq) #endfor return QKeySequence() #enddef def hasHotkey(self, seq): try: seq = QKeySequence(seq) except: pass return seq.toString() in config.default.get('hotkeys', {}) #enddef def add(self, seq, command): ''' Add a new hotkey with with a command name intended to be saved to the config ''' try: seq = QKeySequence(seq) except: pass seq = seq.toString( ) # TODO: Removed this (use kyseq directly). UPDATE LOGS when you do (toString) hks = config.default.get('hotkeys', {}) if seq in hks: log.warning('Reassigning existing sequence "{hotkey}"', hotkey=seq) #endif hks[seq] = command config.default.set('hotkeys', hks) config.default.save() # TODO: Move this to settings UI? log.debug('Attempting to bind "{sequence}"', sequence=seq) if command not in self.commands: log.warning( 'Saved hotkey "{hotkey}" to unknown command "{command}"', hotkey=seq, command=command) #endif return True #enddef def remove(self, seq): ''' Remove a hotkey from the config ''' hks = config.default.get('hotkeys', {}) try: del hks[seq] except KeyError: pass else: return self._unbind(seq) return False #endtry #enddef def applyHotkeys(self): ''' Apply all hotkeys defined in the config ''' log.info('Applying hotkeys') toRemove = [] for hotkey, command in config.default.get('hotkeys', {}).items(): if command in self.commands: self._bind(hotkey, command) else: toRemove.append(hotkey) log.error( 'Tried to bind hotkey "{hotkey}" to unknown command "{command}"', hotkey=hotkey, command=command) #endif #endfor for hotkey in toRemove: self.remove(hotkey) #enddef def removeHotkeys(self): ''' Remove all hotkeys defined in the config ''' log.info('Removing all hotkeys') for hotkey, command in config.default.get('hotkeys', {}).items(): self._unbind(hotkey)
class CMainApplication(Ui_MainWindow, QtWidgets.QMainWindow): def __init__(self): super(CMainApplication, self).__init__() self.setupUi(self) # Tray Menu self.tray = TrayIcon(self) self.tray.show() self.tray.switchTrigger.connect(self.__bSwitchTransactionOn) self.tray.quitTrigger.connect(self.__bQuit) # IS transaction on self.__switch = True # Hotkey listener self.__initHotkeys() # Attribute relates on transaction self.__descriptionSwitch = False self.__word = '' self.__transaction = '' # Transaction widget self.__transactionWidget = CTransaction() self.__transactionWidget.cancelSignal.connect(self.__bCancelTransaction) # Database widget showed? self.databaseShowed = True # Init the database self.__initDatabase() self.initUI() def __initHotkeys(self): # self.__globalHotKCListener = CGlobalHotKCListener() # self.__globalHotKCListener.start() # self.__globalHotKCListener.addTrigger.connect(self.__bStartDescription) # self.__globalHotKCListener.cancelTrigger.connect(self.__bCancelDescription) self.__GlobalHotkeyListener = SystemHotkey() self.__GlobalHotkeyListener.register(('control', 'd'), callback=self.__bStartDescription) self.__GlobalHotkeyListener.register(('control', 's'), callback=self.__bCancelDescription) def closeEvent(self, vEvent): vEvent.ignore() self.hide() def hideEvent(self, vEvent): self.tray.showMessage("title", "hide in tray icon") self.hide() vEvent.ignore() def initUI(self): # self.setGeometry(300, 300, 300, 220) self.setWindowTitle('Dict') # AX self.__transactionWidget.transactionAx.setControl(u"{8856F961-340A-11D0-A96B-00C04FD705A2}") # self.transactionAx.setFocusPolicy(Qt::StrongFocus);//设置控件接收键盘焦点的方式:鼠标单击、Tab键 self.__transactionWidget.transactionAx.setProperty("DisplayAlerts", False) # 不显示任何警告信息。 self.__transactionWidget.transactionAx.setProperty("DisplayScrollBars", True) # 显示滚动条 # self.setMouseTracking(True) self.addClipbordListener() self.show() # self.__transactionWidget.show() # self.hide() def addClipbordListener(self): self.clipboard = QApplication.clipboard() self.clipboard.dataChanged.connect(self.onClipboradChanged) def onClipboradChanged(self): # Add description of the word if self.__descriptionSwitch: try: self.__globalHotKCListener.quit() hld = win32gui.FindWindow("Qt5QWindowIcon", "Form") shell = win32com.client.Dispatch("WScript.Shell") shell.SendKeys('%') win32gui.SetForegroundWindow(hld) except Exception as MyException: print(MyException) reply = QMessageBox.information(self, "Tips", "Sure you want to add this words?", QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.No: return win32api.keybd_event(18, 0, 0, 0) # Alt win32api.keybd_event(27, 0, 0, 0) # F win32api.keybd_event(27, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放按键 win32api.keybd_event(18, 0, win32con.KEYEVENTF_KEYUP, 0) clipboard = QApplication.clipboard() description = clipboard.text() # Save the word and description self.__saveData(self.__word, self.__transaction, description) # Tip to window self.tray.showMessage("Tips", "Insert Successful", self.tray.icon) self.incrementLine.setText(str(int(self.incrementLine.text()) + 1)) self.todayLine.setText(str(int(self.todayLine.text()) + 1)) self.totalLine.setText(str(int(self.totalLine.text()) + 1)) self.__bCancelDescription(None) self.__initDatabase() else: try: # Get the text in clipboard clipboard = QApplication.clipboard() text = clipboard.text() if len(text) < 2: return # text = re.search(r' ?[a-zA-Z ]+ ?', text).group() text = text.strip() text = str.lower(text) # Find the transaction Transaction = self.__transact(text) self.__transaction = Transaction self.__word = text File = codecs.open('resources/index.html', 'w', 'utf-8') if Transaction[0] != '<': File.write(HTMLSTATIC1 + Transaction + HTMLSTATIC2) else: File.write(Transaction) File.close() # Show the transaction window # self.__transactionWidget.transactionBrowser.setText(self.__transaction) self.__transactionWidget.transactionAx.dynamicCall("Navigate(str)", BASEDIR + "/resources/index.html") self.__transactionWidget.statusLabel.setText("Search Mode") CursurPoint = QCursor.pos() desktopWidget = QApplication.desktop(); DesktopPoint = desktopWidget.availableGeometry() if CursurPoint.x() + 620 > DesktopPoint.width(): CursurPoint.setX(DesktopPoint.width() - 620) if CursurPoint.y() + 400 > DesktopPoint.height(): CursurPoint.setY(DesktopPoint.height() - 400) self.__transactionWidget.move(CursurPoint) # self.__transactionWidget.setWindowFlags(self.__transactionWidget.windowFlags() |QtCore.Qt.WindowStaysOnTopHint) # self.__transactionWidget.show() # self.__transactionWidget.setWindowFlags(QtCore.Qt.Widget) self.__transactionWidget.activateWindow() self.__transactionWidget.show() except Exception as e: print(e) def incrementButtonPushed(self): cn = sqlite3.connect(WORDRECORD) cu = cn.cursor() cu.execute("SELECT word,wordTransaction,description " "FROM record WHERE alreadyOut='false'") with open(EXPORTPATH + "increment.txt", "w+", encoding='utf-8') as f: for res in cu.fetchall(): f.write(res[0]) f.write(",") f.write(res[1]) f.write('\n') f.write(res[2]) f.write('@\n') f.close() cu.execute("UPDATE record SET alreadyOut='true' WHERE alreadyOut='false'") cn.commit() cn.close() self.statusbar.showMessage("Successful!") def todayButtonPushed(self): NowTime = time.time() Midnight = NowTime - NowTime % 86400 cn = sqlite3.connect(WORDRECORD) cu = cn.cursor() cu.execute("SELECT word,wordTransaction,description " "FROM record WHERE insertTime>?", (Midnight,)) with open(EXPORTPATH + "today.txt", "w+", encoding='utf-8') as f: for res in cu.fetchall(): f.write(res[0]) f.write(",") f.write(res[1]) f.write('\n') f.write(res[2]) f.write('@\n') f.close() cu.execute("UPDATE record SET alreadyOut='true' WHERE insertTime>?", (Midnight,)) cn.commit() cn.close() self.statusbar.showMessage("Successful!") def totalButtonPushed(self): cn = sqlite3.connect(WORDRECORD) cu = cn.cursor() cu.execute("SELECT word,wordTransaction,description " "FROM record") with open(EXPORTPATH + "total.txt", "w+", encoding='utf-8') as f: for res in cu.fetchall(): f.write(res[0]) f.write(",") f.write(res[1]) f.write('\n') f.write(res[2]) f.write('@\n') f.close() cu.execute("UPDATE record SET alreadyOut='true'") cn.commit() cn.close() self.statusbar.showMessage("Successful!") def displayButtonPushed(self): if self.databaseShowed: self.databaseWidget.hide() self.databaseShowed = not self.databaseShowed self.displayButton.setText(">>") self.resize(500, 650) else: self.databaseWidget.show() self.databaseShowed = not self.databaseShowed self.displayButton.setText("<<") self.resize(1200, 650) def removeButtonPushed(self): if self.databaseWidget.currentRow() == -1: pass RemoveLists = [] for SelectedRange in self.databaseWidget.selectedRanges(): for SelectIndex in range(SelectedRange.rowCount()): Word = self.databaseWidget.item(SelectedRange.topRow() + SelectIndex, 2).text() try: requests.post(HOST + "/remove", data={'word': Word}) except: self.statusbar.showMessage("No network!") # Remove cn = sqlite3.connect(WORDRECORD) cu = cn.cursor() cu.execute('delete from record where word=?', (Word,)) cn.commit() cn.close() RemoveLists.append(SelectedRange.topRow() + SelectIndex) RemoveLists.sort(reverse=True) for RowIndex in RemoveLists: self.databaseWidget.removeRow(RowIndex) self.__initCounts() def addWordsButtonPushed(self): FileName = QFileDialog.getOpenFileName(self, "select your words file", "", "Txt files(*.txt)") Results = self.__parseImportWords(FileName[0]) # Add words to database cn = sqlite3.connect(WORDRECORD) cu = cn.cursor() for Result in Results: Word = Result.get('word') Transaction = Result.get('transaction') Description = "" # Find if it is exist cu.execute('select proficiency from record where word=?', (Word,)) res = cu.fetchone() if res is None: cu.execute("INSERT INTO record (word, wordTransaction, description, insertTime) " "VALUES (?,?,?,?)", (Word, Transaction, Description, time.time())) else: ProficiencyIncreament = 100 if res[0] + 25 > 100 else 100 cu.execute("update record set proficiency=? where word = ?", (ProficiencyIncreament, Word)) cn.commit() cn.close() self.__initDatabase() def synchronizeButtonPushed(self): self.__initDatabase() def __parseImportWords(self, vFileName): File = open(vFileName, 'r', encoding='UTF-16 LE', errors='ignore') lines = File.readlines() tempList = [] temp = "" flag = 0 final = [] word = "" transaction = "" for line in lines: line.encode("utf8") if flag == 0: pattern0 = re.compile(r'\ufeff(.*\n)') match0 = pattern0.match(line) temp += match0.group(1) else: pattern = re.compile(r'\d,') match = pattern.match(line) if match: tempList.append(temp) temp = "" temp += line else: temp += line flag += 1 tempList.append(temp) for temp in tempList: transaction = "" pattern = re.compile(r'\d, (.*?) (.*)\n') match = pattern.match(temp) word = match.group(1) transction0 = match.group(2) transaction += transction0 + "\n" end = match.end() translationLine = temp[end:] translations = translationLine.split("\n\n") length = len(translations) for i in range(length): tmp = translations[i].partition(".") transaction += str(i + 1) + ". [" + tmp[0] + "]" + tmp[2] + "\n" element = {"word": word, "transaction": transaction} final.append(element) return final def __transact(self, vWord): try: # Dict Parser builder = IndexBuilder('MDXData/Oxford.mdx') ResultWord = builder.mdx_lookup(vWord) # Internet Based Transaction if len(ResultWord) == 0: TransactionRequest = requests.post(HOST + "/transaction" , data={'word': vWord}) return json.loads(TransactionRequest.text) # Using local dictionary else: parser = MyHTMLParser(ResultWord[0]) return parser.getData() except Exception as e: print(e) def __synchronize(self): try: response = requests.get(HOST + "/sychronize", timeout=10, data={"LocalTime": os.stat(WORDRECORD).st_mtime}) if response.status_code == 200: with open(WORDRECORD, 'wb') as TargetFile: TargetFile.write(response.content) self.statusbar.showMessage("Download Successful") elif response.status_code == 302: with open(WORDRECORD, "rb") as File: data = File.read() if requests.post(HOST + "/sychronize", data=data).status_code == 200: self.statusbar.showMessage("Upload successful") else: self.statusbar.showMessage("Upload Error") else: QMessageBox.information(self, "Warning", "Can't synchronize with remote database, using local mode", QMessageBox.Yes) except: QMessageBox.information(self, "Warning", "Can't synchronize with remote database, using local mode", QMessageBox.Yes) self.__initCounts() def __initCounts(self): NowTime = time.time() Midnight = NowTime - NowTime % 86400 cn = sqlite3.connect(WORDRECORD) cu = cn.cursor() cu.execute('SELECT * FROM record') res = cu.fetchall() TotalCount = len(res) cu.execute('SELECT * FROM record WHERE insertTime>?', (Midnight,)) res = cu.fetchall() TodayCount = len(res) cu.execute("SELECT * FROM record WHERE alreadyOut='false' ") res = cu.fetchall() IncrementCount = len(res) cn.close() self.incrementLine.setText(str(IncrementCount)) self.todayLine.setText(str(TodayCount)) self.totalLine.setText(str(TotalCount)) def __initDatabase(self): self.databaseWidget.clear() self.databaseWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.databaseWidget.setSelectionBehavior(QAbstractItemView.SelectRows) # Synchronize self.__synchronize() cn = sqlite3.connect(WORDRECORD) cu = cn.cursor() # Get the record cu.execute("select insertTime,proficiency,word,description,wordTransaction from record") reses = cu.fetchall() self.databaseWidget.setRowCount(len(reses)) self.databaseWidget.setColumnCount(5) self.databaseWidget.setHorizontalHeaderItem(0, QTableWidgetItem("Date")) self.databaseWidget.setHorizontalHeaderItem(1, QTableWidgetItem("P")) self.databaseWidget.setHorizontalHeaderItem(2, QTableWidgetItem("Word")) self.databaseWidget.setHorizontalHeaderItem(3, QTableWidgetItem("D")) self.databaseWidget.setHorizontalHeaderItem(4, QTableWidgetItem("Transaction")) Header = self.databaseWidget.horizontalHeader() Header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) Header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents) Header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) Header.setSectionResizeMode(4, QtWidgets.QHeaderView.ResizeToContents) index = 0 for res in reses: self.databaseWidget.setItem(index, 0, QTableWidgetItem( time.strftime("%m-%d", time.localtime(res[0])))) # Date self.databaseWidget.setItem(index, 1, QTableWidgetItem(str(res[1]))) # Proficiency self.databaseWidget.setItem(index, 2, QTableWidgetItem(res[2])) # Word self.databaseWidget.setItem(index, 3, QTableWidgetItem(res[3])) # wordTransaction self.databaseWidget.setItem(index, 4, QTableWidgetItem(res[4])) # description index += 1 cn.close() # Display details signals for x in range(self.databaseWidget.rowCount()): self.databaseWidget.item(x, 3).setToolTip(self.databaseWidget.item(x, 3).text()) self.databaseWidget.item(x, 4).setToolTip(self.databaseWidget.item(x, 4).text()) def __saveData(self, vWord, vTransaction, vDescription): try: cn = sqlite3.connect(WORDRECORD) cu = cn.cursor() # Find if it is exist cu.execute('select proficiency from record where word=?', (vWord,)) res = cu.fetchone() if res is None: cu.execute("INSERT INTO record (word, wordTransaction, description, insertTime) " "VALUES (?,?,?,?)", (vWord, vTransaction, vDescription, time.time())) else: ProficiencyIncreament = 100 if res[0] + 5 > 100 else 100 cu.execute("update record set proficiency=? where word = ?", (ProficiencyIncreament, vWord)) cn.commit() cn.close() return True except Exception as e: return False def __bCancelTransaction(self): self.__transactionWidget.close() def __bSwitchTransactionOn(self): if self.__switch: self.__descriptionSwitch = False # self.__globalHotKCListener.addTrigger.disconnect() # self.__globalHotKCListener.cancelTrigger.disconnect() # self.__globalHotKCListener.cancelHotKey() # self.__globalHotKCListener.quit() self.__GlobalHotkeyListener.unregister(('control', 'd')) self.__GlobalHotkeyListener.unregister(('control', 's')) self.__switch = not self.__switch self.clipboard.dataChanged.disconnect() else: self.__descriptionSwitch = False # self.__globalHotKCListener.start() # self.__globalHotKCListener.addTrigger.connect(self.__bStartDescription) # self.__globalHotKCListener.cancelTrigger.connect(self.__bCancelDescription) self.__GlobalHotkeyListener.register(('control', 'd'), callback=self.__bStartDescription) self.__GlobalHotkeyListener.register(('control', 's'), callback=self.__bCancelDescription) self.__switch = not self.__switch self.clipboard.dataChanged.connect(self.onClipboradChanged) def __bStartDescription(self, event): self.__descriptionSwitch = True self.__transactionWidget.statusLabel.setText("Insert Mode") return event def __bCancelDescription(self, event): self.__descriptionSwitch = False self.__transactionWidget.statusLabel.setText("Search Mode") return False def __bQuit(self): self.__transactionWidget.close() self.__initDatabase() # self.p.terminate() self.tray.setVisible(False) # self.tray.close() self.close() # qApp.__bQuit() sys.exit()
class Keybindings(object): def __init__(self, settings: ApplicationSettings, mappings: Dict[str, Dict[str, Tuple[str, Callable]]]): """ Creates keybindings for shortcuts stores in GSettings. The list of settings cannot be changed after created. Pass a map of (setting_id -> callback) """ super().__init__() self._mappings = mappings self._settings = settings self._active_shortcuts = dict() # see https://github.com/timeyyy/system_hotkey from system_hotkey import SystemHotkey self._keybinder = SystemHotkey() self.rebind_all() def rebind_all(self): for category, shortcuts in self._mappings.items(): if not shortcuts: continue for title, info in shortcuts.items(): shortcut_id, callback = info shortcut = self._settings.get_keybinding(shortcut_id) parsed = parse_keystroke(shortcut) if not callback: logging.warning( f"Empty callback for shortcut '{shortcut_id}': ignored" ) continue if not shortcut: logging.warning( f"Empty shortcut for settings '{shortcut_id}': ignored" ) continue logging.info( f"Binding '{shortcut_id}' -> '{callback.__name__}'") if shortcut and shortcut in self._active_shortcuts and self._active_shortcuts[ shortcut] != callback: logging.debug(f"Removing current binding '{shortcut}'") try: self._keybinder.unregister(parsed) del self._active_shortcuts[shortcut] except Exception as e: logging.error(f"Could not unbind '{shortcut}': {e}") continue if shortcut and shortcut not in self._active_shortcuts: logging.info( f"Binding '{shortcut}' ({parsed}) to '{callback.__name__}'" ) try: self._keybinder.register(parsed, callback=callback) self._active_shortcuts[shortcut] = callback except Exception as e: logging.error( f"Could not bind {shortcut} to {callback.__name__}: {e}" ) continue self._settings.connect(f"changed::{shortcut_id}", lambda k, s: self.rebind_all())
class myapp(tk.Tk): def __init__(self, ScriptPath=None): super().__init__() self.Cfg = Configuration() self.attributes('-type', 'dock') self.geometry("0x0+0+0") self.hk = SystemHotkey() self.hk.register(('control', 'shift', 'h'), callback=self.showhide) self.doingInput = False self.allDirs = None self.readAllDirsFromFile() dirFindThread = threading.Thread(target=self.updateAllDirs) dirFindThread.daemon = True dirFindThread.start() def exitProgram(self, *args): self.quit() def showhide(self, *args): if self.allDirs is None: return if not self.doingInput: self.update_idletasks() self.doingInput = True self.hk.unregister(('control', 'shift', 'h')) inputResult = inputDialog(self, self.allDirs).result self.hk.register(('control', 'shift', 'h'), callback=self.showhide) self.doingInput = False if not inputResult: return if inputResult == "###I###WANT###YOU###TO###GO###KILL###YOURSELF###": self.exitProgram() return #subprocess.run("/usr/bin/konsole &", shell=True, cwd=inputResult) subprocess.run('xdg-open "%s" &' % (inputResult), shell=True) def writeAllDirsToFile(self): if not os.path.isdir(self.Cfg.get('csvdir')): os.mkdir(self.Cfg.get('csvdir')) self.allDirs.to_csv( os.path.join(self.Cfg.get('csvdir'), self.Cfg.get('csvname')), columns=['Name'], ) def readAllDirsFromFile(self): csvfile = os.path.join(self.Cfg.get('csvdir'), self.Cfg.get('csvname')) if os.path.isfile(csvfile): self.allDirs = pd.read_csv(csvfile) def updateAllDirs(self): dirl = [] for root, dirs, files in os.walk(self.Cfg.get('topdir')): dirs[:] = [d for d in dirs if not d.startswith('.')] dirl.extend([os.path.join(root, dir) for dir in dirs]) #dirl = [x for x, _, _ in os.walk()] self.allDirs = pd.DataFrame(columns=['Name'], data=dirl) self.writeAllDirsToFile()
class MainWindow(QMainWindow): iconPath = "image/send.png" def __init__(self, parent=None): super().__init__(parent) self.w = Win32Window.from_qwindow(self) self.mwidget = QMainWindow(self) self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setWindowTitle("MTwit") # window size self.setFixedSize(480, 120) self.center() # TrayIcon self.createActions() self.createTrayIcon() self.trayIcon.show() self.trayIcon.activated.connect(self.iconActivated) # Textwindow self.textWindow = QPlainTextEdit('', self) self.textWindow.resize(400, 100) self.textWindow.move(10, 10) self.textWindow.setStyleSheet("background-color: rgba(0,0,0,50);" "border: 1px solid gray;" "font: 14pt 'Meiryo UI' ;" "color: #FFFFFF;") self.textWindow.setPlaceholderText("What's Happening?") # Quit Button self.qbtn = QuitButton(self) self.qbtn.setButtonPosition(self.size()) # Tweet Button self.tbtn = HoverButton(self) self.tbtn.resize(48, 48) self.tbtn.move(420, 62) self.tbtn.setObjectName('tButton') self.tbtn.setIcon(QIcon("image/send.png")) self.tbtn.setIconSize(QSize(32, 32)) self.tbtn.setStyleSheet("background-color: rgba(200, 200, 200, 0);") self.tbtn.clicked.connect(self.tweetEvent) # tweet Shortcut self.tShortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Return), self) self.tShortcut.activated.connect(self.tweetEvent) # window show Shortcut self.hk = SystemHotkey(consumer=self.ShowOrHide) self.hk.register(('alt', 'shift', 'f1')) # label """self.lbl = QLabel(self) self.lbl.setText("") self.lbl.setStyleSheet("background-color: rgb(0,0,0);" "border: 0px solid red;" "color: rgb(255,255,255);" "font: bold italic 20pt 'Times New Roman';") self.lbl.setGeometry(5,5,60,40) """ self.oldPos = self.pos() self.Firstshow() def Firstshow(self): # check user data and init api pass # windowMove -- def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def mousePressEvent(self, event): self.oldPos = event.globalPos() def mouseMoveEvent(self, event): delta = QPoint(event.globalPos() - self.oldPos) self.move(self.x() + delta.x(), self.y() + delta.y()) self.oldPos = event.globalPos() # windowMove End -- def tweetEvent(self): print("tweetActivated") self.hide() text = self.textWindow.document().toPlainText() self.textWindow.setPlainText("") try: print(self.api.update_status(text)) # print(self.api.mentions_timeline(count=200)) except tweepy.error.TweepError as e: tb = sys.exc_info()[2] print("message:{0}".format(e.with_traceback(tb))) pass def quitEvent(self): self.trayIcon.hide() self.hk.unregister(('alt', 'shift', 'f1')) QCoreApplication.instance().quit() pass def templateTweetEvent(self): pass def iconActivated(self, reason): if reason == 3: self.ShowOrHide() def ShowOrHide(self, *args): if self.isHidden(): self.showEvent_() else: self.hide() def createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.debugMakeWindowAction) self.trayIconMenu.addAction(self.debugMakeWindow2Action) self.trayIconMenu.addAction(self.debugMakeWindow3Action) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.minimizeAction) self.trayIconMenu.addAction(self.restoreAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu) self.trayIcon.setIcon(QIcon(self.iconPath)) def createActions(self): self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide) self.restoreAction = QAction("&Restore", self, triggered=self.showNormal) self.quitAction = QAction("&Quit", self, triggered=self.quitEvent) self.debugMakeWindowAction = QAction("&DebugMakeAuth", self, triggered=self.makeAuthWindow) self.debugMakeWindow2Action = QAction("&DebugMake2Auth", self, triggered=self.makeAuthWindow2) self.debugMakeWindow3Action = QAction("&DebugMake3(Notification)", self, triggered=self.makeDebugwindow) def showEvent_(self): self.textWindow.setPlainText("") self.show() self.w.focus() self.textWindow.setFocus() # Auth Window def makeAuthWindow(self): from mTwit.Authwindow_Ui import AuthWindow authWindow = AuthWindow(self) authWindow.show("TwitterPIN") # Debug def makeAuthWindow2(self): from mTwit.Authwindow_Ui import AuthWindow authWindow = AuthWindow(self) authWindow.show("Consumer") def makeNotificationWindow(self): pass def makeDebugwindow(self, *args): fav = NotificationWindow(self, message="Test Message.") fav.show(NotificationMode.ERROR) """ try: raise MTwitError except: pass """ def setParam(self, param): self.textWindow.setPlainText(param)
[sg.Text("Say manually")], [sg.Input(key="-MANUAL-"),sg.Button("Say")], [sg.Button('Ok'), sg.Button('Quit')] ] # Create the window window = sg.Window('sttttts', layout,icon="logos\\icon.ico") hk = SystemHotkey() hk.register(('control', 'q'), callback=lambda x:b.main(None,None,None)) hk.register(('alt', 'q'), callback=lambda x:b.repeat(None,None)) # Display and interact with the Window using an Event Loop while True: event, values = window.read() # See if user wants to quit or window was closed if event == sg.WINDOW_CLOSED or event == 'Quit': break devices = b.get_io_devices() wanted_input = devices[0].index(values["-INPUT-"]) wanted_output = devices[1].index(values["-OUTPUT-"]) + len(devices[0]) if values["-OUTPUT2-"] == "Disabled": wanted_output2 = None else: wanted_output2 = devices[1].index(values["-OUTPUT2-"]) + len(devices[0]) if event == 'Say': b.say(wanted_output,wanted_output2,values["-MANUAL-"]) hk.unregister(('control', 'q')) hk.unregister(('alt', 'q')) hk.register(('control', 'q'), callback=lambda x:b.main(wanted_input,wanted_output,wanted_output2)) hk.register(('alt', 'q'), callback=lambda x:b.repeat(wanted_output,wanted_output2)) # Finish up by removing from the screen window.close()
class Translation(QMainWindow): # 范围快捷键信号 range_hotkey_sign = pyqtSignal(bool) # 自动翻译模式信号 auto_open_sign = pyqtSignal(bool) # 隐藏范围框快捷键 hide_range_sign = pyqtSignal(bool) def __init__(self, object): super(Translation, self).__init__() self.translater_yaml_map = { "youdao": "youdao", "baidu": "baiduweb", "tencent": "tencentweb", "caiyun": "caiyun", "google": "google", "deepl": "deepl" } self.object = object self.logger = object.logger self.getInitConfig() self.ui() # 开启朗读模块 self.sound = translator.sound.Sound(self.object) utils.thread.createThread(self.sound.openWebdriver) # 开启翻译模块 self.createWebdriverThread() # 自动翻译信号 self.auto_open_sign.connect( lambda: utils.thread.createThread(self.startTranslater)) def ui(self): # 窗口尺寸 self.resize(int(800 * self.rate), int(130 * self.rate)) # 窗口无标题栏、窗口置顶、窗口透明 self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self.setMouseTracking(True) # 窗口图标 icon = QIcon() icon.addPixmap(QPixmap(LOGO_PATH), QIcon.Normal, QIcon.On) self.setWindowIcon(icon) # 鼠标样式 pixmap = QPixmap(PIXMAP_PATH) pixmap = pixmap.scaled(int(20 * self.rate), int(20 * self.rate), Qt.KeepAspectRatio, Qt.SmoothTransformation) cursor = QCursor(pixmap, 0, 0) self.setCursor(cursor) # 鼠标选中状态图标 select_pixmap = QPixmap(PIXMAP2_PATH) select_pixmap = select_pixmap.scaled(int(20 * self.rate), int(20 * self.rate), Qt.KeepAspectRatio, Qt.SmoothTransformation) select_pixmap = QCursor(select_pixmap, 0, 0) # 工具栏标签 label = QLabel(self) self.customSetGeometry(label, 0, 0, 800, 30) label.setStyleSheet("background-color: rgba(62, 62, 62, 0.01)") # 翻译框字体 self.font = QFont() self.font.setFamily(self.font_type) self.font.setPointSize(self.font_size) # 翻译框 self.translate_text = QTextBrowser(self) self.customSetGeometry(self.translate_text, 0, 30, 1500, 110) self.translate_text.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.translate_text.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.translate_text.setFont(self.font) self.translate_text.setStyleSheet("border-width: 0;\ border-style: outset;\ border-top: 0px solid #e8f3f9;\ color: white;\ font-weight: bold;\ background-color: rgba(62, 62, 62, %s)" % (self.horizontal / 100)) # 翻译框加入描边文字 self.format = QTextCharFormat() # 翻译界面显示通知信息 thread = utils.thread.createShowTranslateTextQThread(self.object) thread.signal.connect(self.showTranslateText) utils.thread.runQThread(thread) # 重叠提示消息框 self.temp_text = QTextBrowser(self) self.customSetGeometry(self.temp_text, 0, 30, 1500, 110) self.temp_text.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.temp_text.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.temp_text.setFont(self.font) self.temp_text.setStyleSheet("border-width: 0;\ border-style: outset;\ border-top: 0px solid #e8f3f9;\ color: white;\ font-weight: bold;\ background-color: rgba(62, 62, 62, %s)" % (self.horizontal / 100)) self.format.setTextOutline( QPen(QColor(self.font_color_1), 0.7, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self.temp_text.mergeCurrentCharFormat(self.format) self.temp_text.append("翻译框和范围区域重叠, 请挪开翻译框!!!") self.temp_text.hide() # 翻译框根据内容自适应大小 self.document = self.translate_text.document() self.document.contentsChanged.connect(self.textAreaChanged) # 此Label用于当鼠标进入界面时给出颜色反应 self.drag_label = QLabel(self) self.drag_label.setObjectName("drag_label") self.customSetGeometry(self.drag_label, 0, 0, 4000, 2000) self.drag_label.setMouseTracking(True) # 翻译按钮 self.start_button = QPushButton( qtawesome.icon("fa.play", color=self.icon_color), "", self) self.customSetIconSize(self.start_button, 20, 20) self.customSetGeometry(self.start_button, 173, 5, 20, 20) self.start_button.setToolTip("<b>翻译键 Translate</b><br>点击后翻译(手动模式)") self.start_button.setStyleSheet("background: transparent;") self.start_button.clicked.connect( lambda: utils.thread.createThread(self.startTranslater)) self.start_button.setCursor(select_pixmap) self.start_button.hide() # 设置按钮 self.settin_button = QPushButton( qtawesome.icon("fa.cog", color=self.icon_color), "", self) self.customSetIconSize(self.settin_button, 20, 20) self.customSetGeometry(self.settin_button, 213, 5, 20, 20) self.settin_button.setToolTip("<b>设置键 Settin</b><br>翻译器的详细设置") self.settin_button.setStyleSheet("background: transparent;") self.settin_button.setCursor(select_pixmap) self.settin_button.hide() # 范围按钮 self.range_button = QPushButton( qtawesome.icon("fa.crop", color=self.icon_color), "", self) self.customSetIconSize(self.range_button, 20, 20) self.customSetGeometry(self.range_button, 253, 5, 20, 20) self.range_button.setToolTip( "<b>范围 Range</b><br>框选要翻译的区域<br>需从左上到右下拖动") self.range_button.setStyleSheet("background: transparent;") self.range_button.setCursor(select_pixmap) self.range_button.clicked.connect(self.clickRange) self.range_button.hide() # 复制按钮 self.copy_button = QPushButton( qtawesome.icon("fa.copy", color=self.icon_color), "", self) self.customSetIconSize(self.copy_button, 20, 20) self.customSetGeometry(self.copy_button, 293, 5, 20, 20) self.copy_button.setToolTip("<b>复制 Copy</b><br>将当前识别到的文本<br>复制至剪贴板") self.copy_button.setStyleSheet("background: transparent;") self.copy_button.setCursor(select_pixmap) self.copy_button.clicked.connect(lambda: pyperclip.copy(self.original)) self.copy_button.hide() # 屏蔽词按钮 self.filter_word_button = QPushButton( qtawesome.icon("fa.ban", color=self.icon_color), "", self) self.customSetIconSize(self.filter_word_button, 20, 20) self.customSetGeometry(self.filter_word_button, 333, 5, 20, 20) self.filter_word_button.setToolTip( "<b>屏蔽字符 Filter</b><br>将特定翻译错误的词<br>屏蔽不显示") self.filter_word_button.setStyleSheet("background: transparent;") self.filter_word_button.setCursor(select_pixmap) self.filter_word_button.clicked.connect(self.clickFilter) self.filter_word_button.hide() # 翻译模式按钮 self.switch_button = ui.switch.SwitchButton(self, sign=self.translate_mode, startX=(50 - 20) * self.rate) self.customSetGeometry(self.switch_button, 373, 5, 50, 20) self.switch_button.setToolTip("<b>模式 Mode</b><br>手动翻译/自动翻译") self.switch_button.checkedChanged.connect(self.changeTranslateMode) self.switch_button.setCursor(select_pixmap) self.switch_button.hide() # 朗读原文按钮 self.play_voice_button = QPushButton( qtawesome.icon("fa.music", color=self.icon_color), "", self) self.customSetIconSize(self.play_voice_button, 20, 20) self.customSetGeometry(self.play_voice_button, 443, 5, 20, 20) self.play_voice_button.setToolTip("<b>朗读原文 Play Voice</b><br>朗读识别到的原文") self.play_voice_button.setStyleSheet("background: transparent;") self.play_voice_button.clicked.connect( lambda: utils.thread.createThread(self.sound.playSound, self. original)) self.play_voice_button.setCursor(select_pixmap) self.play_voice_button.hide() # 充电按钮 self.battery_button = QPushButton( qtawesome.icon("fa.battery-half", color=self.icon_color), "", self) self.customSetIconSize(self.battery_button, 24, 20) self.customSetGeometry(self.battery_button, 483, 5, 24, 20) self.battery_button.setToolTip( "<b>充电入口 Support author</b><br>我要给团子充电支持!") self.battery_button.setStyleSheet("background: transparent;") self.battery_button.setCursor(select_pixmap) self.battery_button.hide() # 锁按钮 self.lock_button = QPushButton( qtawesome.icon("fa.lock", color=self.icon_color), "", self) self.customSetIconSize(self.lock_button, 20, 20) self.customSetGeometry(self.lock_button, 527, 5, 20, 20) self.lock_button.setToolTip("<b>锁定翻译界面 Lock</b>") self.lock_button.setStyleSheet("background: transparent;") self.lock_button.setCursor(select_pixmap) self.lock_button.clicked.connect(self.lock) self.lock_button.hide() # 最小化按钮 self.minimize_button = QPushButton( qtawesome.icon("fa.minus", color=self.icon_color), "", self) self.customSetIconSize(self.minimize_button, 20, 20) self.customSetGeometry(self.minimize_button, 567, 5, 20, 20) self.minimize_button.setToolTip("<b>最小化 Minimize</b>") self.minimize_button.setStyleSheet("background: transparent;") self.minimize_button.setCursor(select_pixmap) self.minimize_button.clicked.connect(self.showMinimized) self.minimize_button.hide() # 退出按钮 self.quit_button = QPushButton( qtawesome.icon("fa.times", color=self.icon_color), "", self) self.customSetIconSize(self.quit_button, 20, 20) self.customSetGeometry(self.quit_button, 607, 5, 20, 20) self.quit_button.setToolTip("<b>退出程序 Quit</b>") self.quit_button.setStyleSheet("background: transparent;") self.quit_button.setCursor(select_pixmap) self.quit_button.clicked.connect(self.showAppquitMessageBox) self.quit_button.hide() # 右下角用于拉伸界面的控件 self.statusbar = QStatusBar(self) self.setStatusBar(self.statusbar) self.statusbar.setStyleSheet( "font: 10pt %s;" "color: %s;" "background-color: rgba(62, 62, 62, 0.1)" % (self.font_type, self.icon_color)) if not self.statusbar_sign: self.statusbar.hide() # 注册翻译快捷键 self.translate_hotkey = SystemHotkey() if self.object.config["showHotKey1"] == "True": self.translate_hotkey.register( (self.translate_hotkey_value1, self.translate_hotkey_value2), callback=lambda x: utils.thread.createThread(self. startTranslater)) # 注册范围快捷键 self.range_hotkey = SystemHotkey() if self.object.config["showHotKey2"] == "True": self.range_hotkey.register( (self.range_hotkey_value1, self.range_hotkey_value2), callback=lambda x: self.range_hotkey_sign.emit(True)) self.range_hotkey_sign.connect(self.clickRange) # 注册隐藏范围框快捷键 self.hide_range_hotkey = SystemHotkey() if self.object.config["showHotKey3"]: self.hide_range_hotkey.register( (self.hide_range_hotkey_value1, self.hide_range_hotkey_value2), callback=lambda x: self.hide_range_sign.emit(True)) # 窗口显示信号 def showEvent(self, e): # 如果处于自动模式下则开始 if self.translate_mode: self.stop_sign = False # 窗口隐藏信号 def hideEvent(self, e): # 如果处于自动模式下则暂停 if self.translate_mode and not self.object.config["drawImageUse"]: self.stop_sign = True # 初始化配置 def getInitConfig(self): # 界面字体 self.font_type = "华康方圆体W7" # 界面字体大小 self.font_size = 15 # 图标按键颜色 self.icon_color = "white" # 字体颜色蓝色 self.font_color_1 = "#1E90FF" self.font_color_2 = "#FF69B4" # 界面缩放比例 self.rate = self.object.yaml["screen_scale_rate"] # 界面透明度 self.horizontal = self.object.config["horizontal"] if self.horizontal == 0: self.horizontal = 1 # 当前登录的用户 self.user = self.object.yaml["user"] # 界面锁 self.lock_sign = False # 翻译模式 self.translate_mode = False # 自动翻译暂停标志 self.stop_sign = False # 原文 self.original = "" # 翻译线程1启动成功标志 self.webdriver_1_sign = False # 翻译线程2启动成功标志 self.webdriver_2_sign = False # 翻译线程1翻译类型 self.webdriver_type1 = "" # 翻译线程2翻译类型 self.webdriver_type2 = "" # 翻译线程3翻译类型 self.webdriver_type3 = "" # 状态栏是否隐藏标志 self.statusbar_sign = self.object.config["showStatusbarUse"] # 各翻译源线程状态标志 self.thread_state = 0 # 自动翻译线程存在标志 self.auto_trans_exist = False # 按键转换映射关系 hotkey_map = {"ctrl": "control", "win": "super"} # 翻译快捷键 self.translate_hotkey_value1 = hotkey_map.get( self.object.config["translateHotkeyValue1"], self.object.config["translateHotkeyValue1"]) self.translate_hotkey_value2 = hotkey_map.get( self.object.config["translateHotkeyValue2"], self.object.config["translateHotkeyValue2"]) # 范围快捷键 self.range_hotkey_value1 = hotkey_map.get( self.object.config["rangeHotkeyValue1"], self.object.config["rangeHotkeyValue1"]) self.range_hotkey_value2 = hotkey_map.get( self.object.config["rangeHotkeyValue2"], self.object.config["rangeHotkeyValue2"]) # 范围快捷键 self.hide_range_hotkey_value1 = hotkey_map.get( self.object.config["hideRangeHotkeyValue1"], self.object.config["hideRangeHotkeyValue1"]) self.hide_range_hotkey_value2 = hotkey_map.get( self.object.config["hideRangeHotkeyValue2"], self.object.config["hideRangeHotkeyValue2"]) # 竖排翻译贴字 self.object.ocr_result = None # 根据分辨率定义控件位置尺寸 def customSetGeometry(self, object, x, y, w, h): object.setGeometry( QRect(int(x * self.rate), int(y * self.rate), int(w * self.rate), int(h * self.rate))) # 根据分辨率定义图标位置尺寸 def customSetIconSize(self, object, w, h): object.setIconSize(QSize(int(w * self.rate), int(h * self.rate))) # 鼠标移动事件 def mouseMoveEvent(self, e: QMouseEvent): # 判断鼠标位置以适配特定位置可拉伸 if self.width() - e.x() < 15 * self.rate and self.height() - e.y( ) < 15 * self.rate: self.statusbar.show() elif not self.object.config["showStatusbarUse"]: self.statusbar.hide() if self.lock_sign == True: return try: self._endPos = e.pos() - self._startPos self.move(self.pos() + self._endPos) except Exception: pass # 判断是否和范围框碰撞 self.checkOverlap() # 鼠标按下事件 def mousePressEvent(self, e: QMouseEvent): if self.lock_sign == True: return try: if e.button() == Qt.LeftButton: self._isTracking = True self._startPos = QPoint(e.x(), e.y()) except Exception: pass # 鼠标松开事件 def mouseReleaseEvent(self, e: QMouseEvent): if self.lock_sign == True: return try: if e.button() == Qt.LeftButton: self._isTracking = False self._startPos = None self._endPos = None except Exception: pass # 鼠标进入控件事件 def enterEvent(self, QEvent): if self.lock_sign == True: self.lock_button.show() self.lock_button.setStyleSheet( "background-color:rgba(62, 62, 62, 0.1);") return # 显示所有顶部工具栏控件 self.switch_button.show() self.start_button.show() self.settin_button.show() self.range_button.show() self.copy_button.show() self.quit_button.show() self.minimize_button.show() self.battery_button.show() self.play_voice_button.show() self.lock_button.show() self.filter_word_button.show() self.setStyleSheet( "QLabel#drag_label {background-color:rgba(62, 62, 62, 0.1)}") if self.statusbar_sign: self.statusbar.show() # 鼠标离开控件事件 def leaveEvent(self, QEvent): if self.lock_sign == False and self.statusbar_sign: self.statusbar.show() width = round((self.width() - 454 * self.rate) / 2) height = self.height() - 30 * self.rate # 重置所有控件的位置和大小 self.start_button.setGeometry( QRect(width, 5 * self.rate, 20 * self.rate, 20 * self.rate)) self.settin_button.setGeometry( QRect(width + 40 * self.rate, 5 * self.rate, 20 * self.rate, 20 * self.rate)) self.range_button.setGeometry( QRect(width + 80 * self.rate, 5 * self.rate, 20 * self.rate, 20 * self.rate)) self.copy_button.setGeometry( QRect(width + 120 * self.rate, 5 * self.rate, 20 * self.rate, 20 * self.rate)) self.filter_word_button.setGeometry( QRect(width + 160 * self.rate, 5 * self.rate, 20 * self.rate, 20 * self.rate)) self.switch_button.setGeometry( QRect(width + 200 * self.rate, 5 * self.rate, 50 * self.rate, 20 * self.rate)) self.play_voice_button.setGeometry( QRect(width + 270 * self.rate, 5 * self.rate, 20 * self.rate, 20 * self.rate)) self.battery_button.setGeometry( QRect(width + 314 * self.rate, 5 * self.rate, 24 * self.rate, 20 * self.rate)) self.lock_button.setGeometry( QRect(width + 358 * self.rate, 5 * self.rate, 24 * self.rate, 20 * self.rate)) self.minimize_button.setGeometry( QRect(width + 398 * self.rate, 5 * self.rate, 20 * self.rate, 20 * self.rate)) self.quit_button.setGeometry( QRect(width + 438 * self.rate, 5 * self.rate, 20 * self.rate, 20 * self.rate)) self.translate_text.setGeometry(0, 30 * self.rate, self.width(), height * self.rate) # 隐藏所有顶部工具栏控件 self.switch_button.hide() self.start_button.hide() self.settin_button.hide() self.range_button.hide() self.copy_button.hide() self.quit_button.hide() self.minimize_button.hide() self.battery_button.hide() self.play_voice_button.hide() self.lock_button.hide() self.filter_word_button.hide() self.setStyleSheet("QLabel#drag_label {background-color:none}") self.textAreaChanged() # 翻译框初始消息 def showTranslateText(self, result): if result: for content in result.split(r"\n"): self.format.setTextOutline( QPen(QColor(self.font_color_1), 0.7, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self.translate_text.mergeCurrentCharFormat(self.format) self.translate_text.append(content) else: self.format.setTextOutline( QPen(QColor(self.font_color_1), 0.7, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self.translate_text.mergeCurrentCharFormat(self.format) self.translate_text.append("欢迎你 ~ %s 么么哒 ~" % self.user) self.format.setTextOutline( QPen(QColor(self.font_color_2), 0.7, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self.translate_text.mergeCurrentCharFormat(self.format) self.translate_text.append("b站关注[团子翻译器]查看动态可了解翻译器最新情况 ~") self.format.setTextOutline( QPen(QColor(self.font_color_1), 0.7, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self.translate_text.mergeCurrentCharFormat(self.format) self.translate_text.append("团子一个人开发不易,这个软件真的花了很大很大的精力 _(:з」∠)_") self.format.setTextOutline( QPen(QColor(self.font_color_2), 0.7, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self.translate_text.mergeCurrentCharFormat(self.format) self.translate_text.append("喜欢的话能不能在b站给团子一个关注,团子会很开心的~真心感谢你❤") # 当翻译内容改变时界面自适应窗口大小 def textAreaChanged(self): newHeight = self.document.size().height() if self.statusbar_sign: newHeight += self.statusbar.height() width = self.width() self.resize(width, newHeight + 30 * self.rate) self.translate_text.setGeometry(0, 30 * self.rate, width, newHeight) # 判断是否和范围框碰撞 try: self.checkOverlap() except Exception: pass # 锁定界面 def lock(self): # 上锁 if not self.lock_sign: self.lock_button.setIcon( qtawesome.icon("fa.unlock", color=self.icon_color)) self.drag_label.hide() self.lock_sign = True if self.horizontal == 1: self.horizontal = 0 # 解锁 else: self.lock_button.setIcon( qtawesome.icon("fa.lock", color=self.icon_color)) self.lock_button.setStyleSheet("background: transparent;") self.drag_label.show() self.lock_sign = False if self.horizontal == 0: self.horizontal = 1 self.translate_text.setStyleSheet("border-width:0;\ border-style:outset;\ border-top:0px solid #e8f3f9;\ color:white;\ font-weight: bold;\ background-color:rgba(62, 62, 62, %s)" % (self.horizontal / 100)) self.temp_text.setStyleSheet("border-width:0;\ border-style:outset;\ border-top:0px solid #e8f3f9;\ color:white;\ font-weight: bold;\ background-color:rgba(62, 62, 62, %s)" % (self.horizontal / 100)) # 改变翻译模式 def changeTranslateMode(self, checked): if checked: self.translate_mode = True self.auto_open_sign.emit(True) else: self.translate_mode = False # 按下翻译键 def startTranslater(self): # 如果已处在自动翻译模式下则直接退出 if self.auto_trans_exist: return thread = utils.translater.Translater(self.object) thread.clear_text_sign.connect(self.clearText) thread.hide_range_ui_sign.connect(self.object.range_ui.hideUI) thread.start() thread.wait() # 收到翻译信息清屏 def clearText(self): # 记录翻译开始时间 self.object.translation_ui.start_time = time.time() # 翻译界面清屏 self.translate_text.clear() # 设定翻译时的字体类型和大小 self.font.setFamily(self.object.config["fontType"]) self.font.setPointSize(self.object.config["fontSize"]) self.translate_text.setFont(self.font) # 注销快捷键 def unregisterHotKey(self): if self.object.config["showHotKey1"] == "True": self.translate_hotkey.unregister( (self.translate_hotkey_value1, self.translate_hotkey_value2)) if self.object.config["showHotKey2"] == "True": self.range_hotkey.unregister( (self.range_hotkey_value1, self.range_hotkey_value2)) if self.object.config["showHotKey3"]: self.hide_range_hotkey.unregister( (self.hide_range_hotkey_value1, self.hide_range_hotkey_value2)) # 将翻译结果打印 def display_text(self, result, trans_type): # 公共翻译一 if trans_type == "webdriver_1": color = self.object.config["fontColor"][self.translater_yaml_map[ self.webdriver1.web_type]] trans_type = self.webdriver1.web_type # 公共翻译二 elif trans_type == "webdriver_2": color = self.object.config["fontColor"][self.translater_yaml_map[ self.webdriver2.web_type]] trans_type = self.webdriver2.web_type # 公共翻译三 elif trans_type == "webdriver_3": color = self.object.config["fontColor"][self.translater_yaml_map[ self.webdriver3.web_type]] trans_type = self.webdriver3.web_type # 私人百度 elif trans_type == "baidu_private": color = self.object.config["fontColor"]["baidu"] # 私人腾讯 elif trans_type == "tencent_private": color = self.object.config["fontColor"]["tencent"] # 私人彩云 elif trans_type == "caiyun_private": color = self.object.config["fontColor"]["caiyunPrivate"] # 原文 elif trans_type == "original": color = self.object.config.get("fontColor", {}).get("original", self.font_color_1) else: return # 显示在文本框上 if self.object.config["showColorType"] == "False": self.format.setTextOutline( QPen(QColor(color), 0.7, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self.translate_text.mergeCurrentCharFormat(self.format) self.translate_text.append(result) else: result = result.replace("\n", "<br>") self.translate_text.append("<font color=%s>%s</font>" % (color, result)) QApplication.processEvents() # 保存译文 utils.config.saveTransHisTory(result, trans_type) # 线程结束,减少线程数 self.thread_state -= 1 if self.thread_state < 0: self.thread_state = 0 if self.thread_state == 0: try: self.statusbar.showMessage( "翻译结束, 耗时: {:.2f} s".format(time.time() - self.start_time + self.ocr_time)) except Exception: self.statusbar.showMessage( "翻译结束, 耗时: {:.2f} s".format(time.time() - self.start_time)) self.ocr_time = 0 # 检测范围区域和翻译区域是否有重叠 def checkOverlap(self): # 翻译框坐标 rect = self.geometry() X1 = rect.left() Y1 = rect.top() + (self.height() - self.translate_text.height()) X2 = rect.left() + rect.width() Y2 = rect.top() + rect.height() # 范围框坐标 rect = self.object.range_ui.geometry() x1 = rect.left() y1 = rect.top() x2 = rect.left() + rect.width() y2 = rect.top() + rect.height() rr1 = utils.range.Rectangular(X1, Y1, X2 - X1, Y2 - Y1) rr2 = utils.range.Rectangular(x1, y1, x2 - x1, y2 - y1) if rr2.collision(rr1): self.customSetGeometry(self.temp_text, 0, 30, self.translate_text.width(), self.translate_text.height()) self.translate_text.hide() self.temp_text.show() else: self.temp_text.hide() self.translate_text.show() # 加载翻译引擎1 def openWebdriver1(self): # 翻译模块1 self.webdriver1 = translator.all.Webdriver(self.object) # 连接消息提示框 self.webdriver1.message_sign.connect(self.showStatusbar) # 加载翻译引擎 self.webdriver1.openWebdriver() # 开启翻译页面 if self.webdriver_type1: utils.thread.createThread(self.webdriver1.openWeb, self.webdriver_type1) # 加载翻译引擎2 def openWebdriver2(self): # 翻译模块2 self.webdriver2 = translator.all.Webdriver(self.object) # 连接消息提示框 self.webdriver2.message_sign.connect(self.showStatusbar) # 加载翻译引擎 self.webdriver2.openWebdriver() # 开启翻译页面 if self.webdriver_type2: utils.thread.createThread(self.webdriver2.openWeb, self.webdriver_type2) # 加载翻译引擎3 def openWebdriver3(self): # 翻译模块3 self.webdriver3 = translator.all.Webdriver(self.object) # 连接消息提示框 self.webdriver3.message_sign.connect(self.showStatusbar) # 加载翻译引擎 self.webdriver3.openWebdriver() # 开启翻译页面 if self.webdriver_type3: utils.thread.createThread(self.webdriver3.openWeb, self.webdriver_type3) # 开启翻译模块 def createWebdriverThread(self): self.statusbar.showMessage("翻译模块启动中, 请等待完成后再操作...") # 筛选翻译源类型 translater_list = [ "youdaoUse", "baiduwebUse", "tencentwebUse", "deeplUse", "googleUse", "caiyunUse" ] for val in translater_list: if self.object.config[val] == "False": continue if not self.webdriver_type1: # 翻译模块一的翻译源类型 self.webdriver_type1 = val.replace("Use", "").replace("web", "") elif not self.webdriver_type2: # 翻译模块二的翻译源类型 self.webdriver_type2 = val.replace("Use", "").replace("web", "") else: self.webdriver_type3 = val.replace("Use", "").replace("web", "") utils.thread.createThread(self.openWebdriver1) utils.thread.createThread(self.openWebdriver2) utils.thread.createThread(self.openWebdriver3) # 状态栏显示消息信号槽 def showStatusbar(self, message): self.statusbar.showMessage(message) # 按下屏蔽词键后做的事情 def clickFilter(self): self.hide() self.object.filter_ui.show() # 按下范围框选键 def clickRange(self): # 如果处于自动模式下则暂停 if self.translate_mode: self.stop_sign = True self.object.screen_shot_ui = ui.range.WScreenShot(self.object) self.object.screen_shot_ui.show() self.show() # 关闭selenuim的driver引擎 def killDriVer(self): utils.thread.createThreadDaemonFalse( os.popen, "taskkill /im chromedriver.exe /F") utils.thread.createThreadDaemonFalse( os.popen, "taskkill /im geckodriver.exe /F") utils.thread.createThreadDaemonFalse( os.popen, "taskkill /im msedgedriver.exe /F") # 退出提示框 def showAppquitMessageBox(self): utils.message.quitAppMessageBox("退出程序", "真的要关闭团子吗?QAQ ", self.object) # 退出程序 def quit(self): # 界面关闭 self.hide() self.object.range_ui.close() # 注销快捷键 utils.thread.createThreadDaemonFalse(self.unregisterHotKey) # 关闭引擎模块 utils.thread.createThreadDaemonFalse(self.sound.close) utils.thread.createThreadDaemonFalse(self.webdriver1.close) utils.thread.createThreadDaemonFalse(self.webdriver2.close) utils.thread.createThreadDaemonFalse(self.webdriver3.close) # 关闭selenium的driver引擎 self.killDriVer() # 退出程序前保存设置 utils.thread.createThreadDaemonFalse(utils.config.postSaveSettin, self.object) self.close()
# mouse.click(button='left') # x,y = win32api.GetCursorPos() # win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,x,y,0,0) # win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,x,y,0,0) ptg.click() ptg.mouseDown() ptg.mouseUp() print("click") time.sleep(0.5) def hotkeyCallback(x): global flag flag = flag ^ 1 print("触发热键!a = ", flag) if flag: threading.Thread(target=click).start() try: hk = SystemHotkey() hk.register(('control', 'shift', 'h'), callback=hotkeyCallback) print('热键已注册!') input("按回车键结束程序!\n") finally: print('热键已注销!') hk.unregister(('control', 'shift', 'h'))
class playWindow(QWidget): sig_hot_key = pyqtSignal(str) def __init__(self, parent=None): super(playWindow, self).__init__(parent) # 创建自动演奏线程 self.playThread = PlayThread() # ---------设置全局快捷键---------- # 设置我们的自定义热键响应函数 self.sig_hot_key.connect(self.mkey_press_event) # 初始化热键 self.hk_stop = SystemHotkey() # 绑定快捷键和对应的信号发送函数 try: self.hk_stop.register( ('control', 'shift', 'g'), callback=lambda x: self.send_key_event("stop")) except InvalidKeyError as e: QMessageBox(QMessageBox.Warning, '警告', '热键设置失败').exec_() print(e) except SystemRegisterError as e: QMessageBox(QMessageBox.Warning, '警告', '热键设置冲突').exec_() print(e) # 5.设置pyqt5的快捷键,ESC退出工具 QShortcut(QKeySequence("Escape"), self, self.stop_tool) # 6.设置图形界面 self.setup_ui() def setup_ui(self): self.setWindowTitle("疯物之诗琴") self.setWindowIcon(QIcon('icon.ico')) self.setFixedSize(QSize(360, 400)) self.setWindowFlags(Qt.WindowStaysOnTopHint) # self.setAttribute(Qt.WA_TranslucentBackground) self.widgetLayout = QVBoxLayout() # 创建垂直布局 self.widgetLayout.setObjectName("widgetLayout") self.setLayout(self.widgetLayout) self.playList = QListWidget() self.playList.setGeometry(QRect(0, 50, 340, 60)) self.playList.setMinimumSize(QSize(340, 60)) self.playList.setBaseSize(QSize(340, 60)) try: self.fileList = os.listdir("midi/") self.playList.addItems(self.fileList) except FileNotFoundError as e: QMessageBox(QMessageBox.Warning, '警告', '没有找到midi文件').exec_() print(e) self.msgLabel = QLabel( '双击列表选项开始或停止演奏\nEsc退出程序,Ctrl+Shift+G停止演奏\n目前一共有%d条曲目' % (len(self.playList))) self.msgLabel.setGeometry(QRect(0, 0, 360, 50)) self.msgLabel.setMinimumSize(QSize(360, 50)) self.msgLabel.setBaseSize(QSize(360, 50)) self.msgLabel.setAlignment(Qt.AlignLeft) self.msgLabel.setObjectName("msgLabel") self.playStatus = QLabel('请选择一首音乐开始演奏') self.playStatus.setGeometry(QRect(0, 130, 360, 20)) self.playStatus.setMinimumSize(QSize(360, 20)) self.playStatus.setBaseSize(QSize(360, 20)) # 添加控件到布局中 self.widgetLayout.addWidget(self.msgLabel) self.widgetLayout.addWidget(self.playList) self.widgetLayout.addWidget(self.playStatus) # 绑定操作函数 self.playList.itemClicked.connect(self.play_item_clicked) self.playList.doubleClicked.connect(self.play_midi) self.playThread.playSignal.connect(self.show_stop_play) # 在界面显示选择的状态 def play_item_clicked(self, item): print('你选择了:' + item.text()) self.playStatus.setText('你选择了:' + item.text()) # 热键处理函数 def mkey_press_event(self, i_str): print("按下的按键是%s" % (i_str, )) self.stop_play_thread() # 按下全局快捷键终止演奏线程 # 热键信号发送函数(将外部信号,转化成qt信号) def send_key_event(self, i_str): self.sig_hot_key.emit(i_str) # 启动playThread进行自动演奏 def play_midi(self, index): self.stop_play_thread() print('开始演奏:' + self.fileList[index.row()]) # 显示演奏的状态 self.playStatus.setText('开始演奏:' + self.fileList[index.row()]) self.playThread.set_file_path("midi/" + self.fileList[index.row()]) self.playThread.start() pass def show_stop_play(self, msg): self.playStatus.setText(msg) # 终止演奏线程,停止自动演奏 def stop_play_thread(self): self.playStatus.setText('停止演奏') # 在工具界面显示状态 self.playThread.stop_play() time.sleep(0.1) if not self.playThread.isFinished(): self.playThread.terminate() self.playThread.wait() return # 工具退出函数,主要用来停止演奏线程和退出注销热键 def stop_tool(self): self.stop_play_thread() time.sleep(0.1) try: self.hk_stop.unregister(('control', 'shift', 'g')) except UnregisterError as e: QMessageBox(QMessageBox.Warning, '警告', '热键注销失败').exec_() print(e) QCoreApplication.instance().quit() print('退出了应用!!!')