1 new commit in windows7magnifier: https://bitbucket.org/nvdaaddonteam/windows7magnifier/commits/829697f2bb2e/ Changeset: 829697f2bb2e Branch: refactor User: norrumar Date: 2014-08-12 17:30:15 Summary: Refactor: Menu, terminate function and gestures. Affected #: 1 file diff --git a/addon/globalPlugins/Windows7Magnifier/__init__.py b/addon/globalPlugins/Windows7Magnifier/__init__.py index 1158278..d62426d 100644 --- a/addon/globalPlugins/Windows7Magnifier/__init__.py +++ b/addon/globalPlugins/Windows7Magnifier/__init__.py @@ -8,13 +8,6 @@ # This addon integrates configuration options into the standard NVDA GUI # To access, select "Preferences", "Magnifier Options" from the NVDA GUI # -# Shortcuts: -# NVDA+SHIFT+g: Toggle the screen magnifier -# NVDA+i: Toggle color inversion -# NVDA+plus: Zoom in -# NVDA+minus: Zoom out -# -######################################################################## # Standard Python Imports import os @@ -84,36 +77,25 @@ TBM_GETPOS = 0x400 TBM_SETPOSNOTIFY = 0x422 class GlobalPlugin(globalPluginHandler.GlobalPlugin): - """ Please see description at the top of this file. - """ scriptCategory = unicode(_addonSummary) - _instance = None - def __init__(self): - """ Class is instantiated during NVDA startup - """ - # Allow parent class to process super(GlobalPlugin, self).__init__() - # Singleton - so other classes can easily access w/out needing a # reference GlobalPlugin._instance = self - # Keep track of primary thread, so long-running functions can # detect it and be pushed to background threads self.mainThread = threading.currentThread() - # Add magnifier options to the NVDA preferences menu - prefsMenu = gui.mainFrame.sysTrayIcon.menu.FindItemByPosition(0).SubMenu - item = prefsMenu.FindItem(_("M&agnifier settings...")) - if item == -1: - item = prefsMenu.Append(wx.ID_ANY, _("M&agnifier settings..."), _("Magnifier settings")) - else: - item = prefsMenu.FindItemById(item) - - gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onMagnifierSettingsCommand, item) + self.menu = gui.mainFrame.sysTrayIcon.preferencesMenu + self.mainItem = self.menu.Append(wx.ID_ANY, + # Translators: the name of a menu item. + _("M&agnifier settings..."), + # Translators: the tooltip of a menu item. + _("Magnifier settings")) + gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onMagnifierSettingsCommand, self.mainItem) # The ID's of the controls in the (real) magnifier options # dialog. We need to know these so we can check/uncheck them to @@ -128,41 +110,40 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): "lensSizeVertical": 323, "okButton": 1 } - + # Launch the magnifier if it's configured to start w/ NVDA if Windows7MagnifierConfig.conf["magnifier"]["startWithNVDA"]: self.configuring = True self.startMagnifier() self.configuring = False ui.message(_("Magnifier launched")) - self.configuring = False - - def terminate(self): - """ Called when NVDA is done with the plugin - """ - # Close the magnifier if it's configured to close w/ NVDA + + def terminate(self): if Windows7MagnifierConfig.conf["magnifier"]["closeWithNVDA"]: self.closeMagnifier() + try: + self.menu.RemoveItem(self.mainItem) + except wx.PyDeadObjectError: + pass - super(GlobalPlugin, self).terminate() - def onMagnifierSettingsCommand(self, evt): """ Called when the user selects Magnifier Settings from the NVDA menu @param evt: the event which caused this action """ - # Launch the settings dialog just like other settings dialogs gui.mainFrame._popupSettingsDialog(MagnifierSettingsDialog) def script_toggleMagnifier(self, gesture): if self.isMagnifierRunning(): + # Translators: message presented when the magnifier is been closed. ui.message(_("Closing magnifier")) # Pause so the speech can complete uninterrupted time.sleep(1) self.closeMagnifier() else: self.startMagnifier() + # Translators: message presented in input help mode. script_toggleMagnifier.__doc__="Toggles magnifier on and off." def script_zoomIn(self, gesture): @@ -172,10 +153,10 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): tones.beep(800, 50) except: pass - # Windows will automatically launch the magnifier on zoom adjust # If this happens, the windows need to be hidden (if configured) self.hideWindows() + # Translators: message presented in input help mode. script_zoomIn.__doc__="Increase the zoom level." def script_zoomOut(self, gesture): @@ -185,10 +166,10 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): tones.beep(400, 50) except: pass - # Windows will automatically launch the magnifier on zoom adjust # If this happens, the windows need to be hidden (if configured) self.hideWindows() + # Translators: message presented in input help mode. script_zoomOut.__doc__="Decrease the zoom level." def script_invert(self, gesture): @@ -196,7 +177,6 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): # inversion, so we need to start it if not self.isMagnifierRunning(): self.startMagnifier() - # Simulate the Windows (built-in) hotkey for color inversion self._pressKey([winUser.VK_CONTROL, winUser.VK_MENU, 'i']) # Toggle in the config @@ -205,6 +185,7 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): tones.beep(1000, 50) except: pass + # Translators: message presented in input help mode. script_invert.__doc__="Invert the screen colors." def isMagnifierRunning(self): @@ -221,6 +202,7 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): """ # don't launch if already running if not self.isMagnifierRunning(): + # Translators: message presented when running magnifier. ui.message(_("Launching magnifier")) winDir = os.path.expandvars("%WINDIR%") @@ -232,11 +214,9 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): # Fallback exe = winDir + u"\\System32\\Magnify.exe" shellapi.ShellExecute(None, None, exe, subprocess.list2cmdline([exe]), None, 0) - if block or applyConfig: self._waitForMagnifierWindow() time.sleep(1) - if applyConfig: self.applyConfig() @@ -250,12 +230,11 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): "Lens": ("Screen Magnifier Lens Window", None), "Docked": ("Screen Magnifier Window", None) } - for mode,args in modes.items(): + for mode, args in modes.items(): if winUser.user32.FindWindowA(args[0], args[1]) != 0: return mode - return None - + def closeMagnifier(self): """ Close the magnifier """ @@ -281,24 +260,19 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): """ self.configuring = True self.startMagnifier(block=True, applyConfig=False) - if mode != None and self.detectCurrentMode() != mode: hwnd = self._waitForMagnifierWindow() - if mode == "Fullscreen": self._pressKey([winUser.VK_CONTROL, winUser.VK_MENU, 'f']) if mode == "Docked": self._pressKey([winUser.VK_CONTROL, winUser.VK_MENU, 'd']) elif mode == "Lens": self._pressKey([winUser.VK_CONTROL, winUser.VK_MENU, 'l']) - log.debug("Magnifier - Mode changed to %s" % mode) time.sleep(1) - for i in range(250): if self.detectCurrentMode() == mode: break time.sleep(0.02) - # When switching between modes, the window likes to be # shuffled. Without this, other settings do not get applied # properly @@ -306,7 +280,6 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): self._hideWindow(mainWindow) self._showWindow(mainWindow) self._hideWindow(mainWindow) - # If tracking options are specified, at least one must be enabled inputOK = False for trackingOption in [followMouse, followKeyboard, followTextInsertion]: @@ -321,14 +294,12 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): ("followKeyboard", followKeyboard), ("followTextInsertion", followTextInsertion) ] - # Magnifier will complain if you uncheck all tracking options. # So we must mark all the CHECKED boxes first checkboxes = sorted(checkboxes, key=lambda boxArgs: not boxArgs[1]) # Open the settings dialog and grab controls [dialog, controls] = self.openSettings() - # Loop through each setting for boxArgs in checkboxes: name = boxArgs[0] @@ -339,20 +310,17 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): if boxArgs[1] != None and name in controls: log.debug("Magnifier: Setting %s %s" % (name, boxArgs[1])) controls[name].setChecked(boxArgs[1]) - # Set the lens size if lensSizeHorizontal != None: controls["lensSizeHorizontal"].setTrackbarValue(lensSizeHorizontal - 10) if lensSizeVertical != None: controls["lensSizeVertical"].setTrackbarValue(100 - lensSizeVertical) - # Close the dialog time.sleep(0.25) keyboardHandler.KeyboardInputGesture.fromName("enter").send() # Pause for a moment - helps with windows behaving time.sleep(0.25) - self.configuring = False self.hideWindows() - + def openSettings(self): """ Opens the (real) settings window @returns The hwnd to the settings window and a list of each @@ -360,37 +328,30 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): """ # make sure the magnifier is running self.startMagnifier(block=True, applyConfig=False) - # So... find the window mainWindow = self._waitForMagnifierWindow() self._showWindow(mainWindow, True) - controls = winUser.getWindow(mainWindow, GW_CHILD) # short pause to help make sure the window has focus time.sleep(0.1) # click on the settings button self._click(160, 15, controls) - # Wait for options window to be visible (but don't wait forever) optionsWindow = self._waitForWindow(windowName=_("Magnifier Options")) - - # Grab the contros so we can return them to the caller + # Grab the controls so we can return them to the caller controls = {} for name,controlID in self.controlIDs.items(): controls[name] = Win32Control(optionsWindow, controlID) - return optionsWindow, controls def hideWindows(self, numberOfChecks=2, delayBetweenChecks=0.1, minimizeForce=False): """ Hide the (real) magnifier's control windows. This includes the standard window, the magnifier icon, and the settings dialog. - This function will push itself to a daemon thread for background processing, so it doesn't lock up NVDA. This is because the function may take a while to complete, as it can check repeatedly for windows to re-appear. - @param numberOfChecks: number of times to check for each window @param delayBetweenChecks: how long to wait before checking @@ -398,7 +359,6 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): """ # Exit if the user has configured windows to stay open if self.configuring or not Windows7MagnifierConfig.conf["magnifier"]["hideMagnifierControls"]: return - # Detect if the calling thread is main NVDA thread if threading.currentThread() == self.mainThread: # If it is, call this function inside a new thread and exit @@ -406,13 +366,11 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): t.daemon = True t.start() return - time.sleep(1) # Window classes and names to search for windowArgs = [ ("MagUIClass", None), ] - # Check for windows repeatedly, hide them if they are found for i in range(numberOfChecks): for args in windowArgs: @@ -421,7 +379,7 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): self._hideWindow(hwnd) # don't be a resource-hog, pause a bit between checks time.sleep(delayBetweenChecks) - + def _type(self, string): for c in string: self._pressKey(self._virtualizeKeys([c])) @@ -433,23 +391,20 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): will be pressed simultaneously. """ keyCodes = self._virtualizeKeys(keyCodes) - # Simulate each key being pressed down (in order) for key in keyCodes: winUser.user32.keybd_event(key, key, 0, 0) - # Simulate each key being released (in reverse order) keyCodes.reverse() for key in keyCodes: winUser.user32.keybd_event(key, key, KEYEVENTF_KEYUP, 0) - + def _releaseKeys(self, keyCodes, allModifiers=False): """ Ensure keyboard buttons are released. Some actions simulate key presses, and these don't behave properly if the user still has keys depressed. """ keyCodes = self._virtualizeKeys(keyCodes) - if allModifiers: keyCodes.append(winUser.VK_LWIN) keyCodes.append(winUser.VK_RWIN) @@ -458,17 +413,16 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): keyCodes.append(winUser.VK_SHIFT) keyCodes.append(winUser.VK_CAPITAL) keyCodes.append(winUser.VK_NUMLOCK) - for key in keyCodes: winUser.user32.keybd_event(key, key, KEYEVENTF_KEYUP, 0) - + def _hideWindow(self, hwnd): """ Internal convenience function to hide a window @param hwnd: A handle to the window to be hidden """ if self.configuring == True: return winUser.user32.ShowWindow(hwnd, SW_SHOWMINNOACTIVE) - + def _showWindow(self, hwnd, makeForeground=True): """ Internal convenience function to make a window visible @param hwnd: A handle to the window to be shown @@ -478,7 +432,7 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): winUser.user32.ShowWindow(hwnd, win32con.SW_RESTORE) if makeForeground: winUser.setForegroundWindow(hwnd) - + def _waitForMagnifierWindow(self, maxChecks=100, delayBetweenChecks=0.1): """ Block until the main magnifier window is available @param maxChecks: the maximum number of times to check for a @@ -487,7 +441,7 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): """ mainWindow = self._waitForWindow(windowClass="MagUIClass", windowName=None, maxChecks=maxChecks, delayBetweenChecks=delayBetweenChecks) return mainWindow - + def _waitForWindow(self, windowClass=None, windowName=None, maxChecks=100, delayBetweenChecks=0.1): """ Block until a given window is available @param windowClass: the class to search for @@ -502,17 +456,15 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): windowName = windowName.encode("ascii", "ignore") log.debug("Waiting for window '%s', '%s'" % (windowClass, windowName)) hwnd = 0 - for i in range(maxChecks): # For best results, change to xrange please. + for i in xrange(maxChecks): # For best results, change to xrange please. # Play progress tones while magnifier is loading. if i%10 == 0: tones.beep(440, 100) if hwnd == 0: hwnd = winUser.user32.FindWindowA(windowClass, windowName) - if hwnd != 0: break else: time.sleep(delayBetweenChecks) - return hwnd def _virtualizeKeys(self, keyCodes): @@ -523,9 +475,8 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): # If it's not a string, assume it's already a VK if isinstance(keyCodes[i], basestring): keyCodes[i] = winUser.VkKeyScanEx(keyCodes[i], winUser.user32.GetKeyboardLayout(0))[1] - return keyCodes - + @staticmethod def applyConfig(): """ Apply the configured magnifier options set from the NVDA @@ -549,9 +500,8 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): followKeyboard = Windows7MagnifierConfig.conf["magnifier"]["followKeyboard"], followTextInsertion = Windows7MagnifierConfig.conf["magnifier"]["followTextInsertion"] ) - + # Translators: message presented when settings have been applied. ui.message(_("Settings applied")) - # beep to indicate readiness for i in range(3): try: @@ -569,30 +519,27 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): """ # Grab the current position so we can move the mouse back when lastPos = win32api.GetCursorPos() - if hwnd != 0: # make the coordinates relative to the specified window offset = winUser.ScreenToClient(hwnd, 0, 0) x -= offset[0] y -= offset[1] winUser.setForegroundWindow(hwnd) - # move the mouse and click win32api.SetCursorPos((x,y)) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,x,y,0,0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,x,y,0,0) - # restore the previous mouse position win32api.SetCursorPos(lastPos) - + __gestures={ - "kb:NVDA+shift+g": "toggleMagnifier", - "kb:NVDA+plus": "zoomIn", - "kb:NVDA+=": "zoomIn", - "kb:NVDA+-": "zoomOut", + "kb:control+shift+g": "toggleMagnifier", + "kb:control+shift+plus": "zoomIn", + "kb:control+shift+0": "zoomIn", + "kb:control+shift+-": "zoomOut", "kb:NVDA+numpadPlus": "zoomIn", "kb:NVDA+numpadMinus": "zoomOut", - "kb:NVDA+i": "invert", + "kb:control+shift+i": "invert", } class Win32Control: @@ -600,7 +547,7 @@ class Win32Control: to the generic NVDAObject class... more likely: I don't really know what I'm doing. To work around either case, a wrapper class """ - + def __init__(self, parentHWND, controlID): """ @param parentHWND: A handle to the parent window @param controlID: The ID of the control. These (apparently) @@ -608,7 +555,6 @@ class Win32Control: """ self.parentHWND = parentHWND self.controlID = controlID - # use a Win32 function to convert the controlID to a handle self.hwnd = winUser.user32.GetDlgItem(self.parentHWND, self.controlID) @@ -626,19 +572,19 @@ class Win32Control: # if desired state doesn't match current state, simulate a click if checked != self.isChecked(): self.click() - + def isChecked(self): """ Determine the state of a checkbox @returns True if checked, False if not """ # The win32 function for checking returns a 1 if checked return 1 == winUser.sendMessage(self.hwnd, BM_GETCHECK, 0, 0) - + def toggleCheck(self): """ Toggle the state of a checkbox """ self.click() - + def click(self): """ Simulate a mouse click on the control """ @@ -649,6 +595,7 @@ class MagnifierSettingsDialog(gui.SettingsDialog): settings dialogs while being a friendlier replacement for the Windows 7 magnifier options dialog """ + # Translators: The title of a settings dialog. title = _("NVDA Magnifier Addon Options") def makeSettings(self, settingsSizer): Repository URL: https://bitbucket.org/nvdaaddonteam/windows7magnifier/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.