Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a graphical user interface for the vision framework #10082

Closed
wants to merge 117 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
3a83cf8
Add a graphical user interface for the vision framework
Dec 13, 2018
f76da24
Layout adjustments to vision framework
feerrenrut Aug 22, 2019
641ccdc
Update user guide
Aug 22, 2019
1816b28
Many gui and framework updates and improvements, including a load war…
Aug 23, 2019
5a8de04
Fix last (linting) issues
Sep 6, 2019
99ea075
Apply user guide suggestions from code review
LeonarddeR Sep 9, 2019
985dc51
Revert accidental change to liblouis submodule
Sep 9, 2019
a419254
Fix translator comments
Sep 9, 2019
3fdc434
Apply additional review action for the highlighter user guide descrip…
Sep 9, 2019
f76ee19
Tidy ctypes declarations for screen curtain
feerrenrut Sep 21, 2019
8a6fac2
List each vision provider in it's own group.
feerrenrut Sep 21, 2019
a39fca9
Use a custom GUI for highlighter
feerrenrut Oct 4, 2019
5a226b1
Highligher custom gui works
feerrenrut Oct 4, 2019
2b8701c
Highlighter works without custom gui
feerrenrut Oct 4, 2019
82ea428
dynamic addition of settings at runtime is possible with auto (not cu…
feerrenrut Oct 7, 2019
e40f864
move driver settings and utils
feerrenrut Oct 17, 2019
920ca6d
Move percent/param conversion helpers
feerrenrut Oct 17, 2019
5f7df99
Move settings related content to AutoSettings class
feerrenrut Oct 17, 2019
e78d376
Tidy up config save reg / unreg
feerrenrut Oct 17, 2019
bdc2c72
Adapt identifier, section, and productName to AutoSettings class
feerrenrut Oct 17, 2019
a57b6b2
Add type hinting and comments
feerrenrut Oct 19, 2019
8c1043d
Fix: don't hide 'id'
feerrenrut Oct 19, 2019
5526dd1
Fix non-existent id attribute on DriverSetting instance.
feerrenrut Oct 19, 2019
b4df54b
Update DriverSettingsMixin and derived
feerrenrut Oct 26, 2019
e2803ab
Code cleanup
feerrenrut Oct 28, 2019
42aede5
Fix error on exit
feerrenrut Oct 28, 2019
ed6f3f7
Better docs in autoSettings class
feerrenrut Oct 30, 2019
5749543
Rename VisionEnhancementProviderStaticSettings
feerrenrut Oct 30, 2019
9cc72ce
Remove saveSettings param from terminate method on visionEnhancementP…
feerrenrut Oct 30, 2019
4730e47
Clarify naming, consolidate classes
feerrenrut Oct 30, 2019
15a285c
Make screen curtain work
feerrenrut Oct 30, 2019
94ed26e
Use preInitSettings for NVDAHighlighter
feerrenrut Oct 30, 2019
42ee9d0
Clarify abstract methods
feerrenrut Oct 30, 2019
3338ef9
Prettify screen curtain and highlighter settings
feerrenrut Oct 30, 2019
3a32607
Fix up toggle screen curtain command
feerrenrut Oct 31, 2019
19ab265
Add Example auto gui provider
feerrenrut Nov 1, 2019
4bb5cbf
Add visionEnhancementProvider Docs
feerrenrut Nov 1, 2019
76e93ce
Fix highlighter checkboxes being overwritten
feerrenrut Nov 3, 2019
8a85637
Fix auto gui example
feerrenrut Nov 3, 2019
66a0ca6
Rename autoGui.py
feerrenrut Nov 3, 2019
6fe7ba6
Match name with filename
feerrenrut Nov 3, 2019
357b6fb
Demo different sources for GUI values
feerrenrut Nov 3, 2019
574887b
Add copyright header and module doc.
feerrenrut Nov 3, 2019
b2662db
Use dataclass for "provider info"
feerrenrut Nov 3, 2019
365f7ec
Use providerInfo rather than providerId: str to control providers
feerrenrut Nov 4, 2019
dcef2db
Use a class to control start/stop of providers
feerrenrut Nov 4, 2019
59f8276
Improve docs
feerrenrut Nov 4, 2019
edd78ce
Provider ID no longer has to match module name
feerrenrut Nov 4, 2019
46ef9c4
Fix linting errors
feerrenrut Nov 4, 2019
8d812df
Remove unused role concept
feerrenrut Nov 4, 2019
f5ef622
Fix instance/class used in getSettingsPanelClass docs
feerrenrut Nov 4, 2019
06b296d
Clarify return types of starting/stopping a provider
feerrenrut Nov 4, 2019
ae5d686
Give NVDA highlighter enable checkbox a better name and accelerator
feerrenrut Nov 4, 2019
e92319a
Merge remote-tracking branch 'origin/master' into visionGui, fixing m…
Nov 4, 2019
122784f
Fix bad merge
Nov 4, 2019
cd4b563
Fix lint error not caught by previous version of flake8tabs
Nov 4, 2019
8761bdd
fixup merge
feerrenrut Nov 4, 2019
3634252
Fix case error in name of internal function
feerrenrut Nov 4, 2019
4007756
Add copyright headers, and fix docs
feerrenrut Nov 4, 2019
6bbea60
Use single definition for SupportedSettingType
feerrenrut Nov 4, 2019
24e648e
Remove copied base __init__ implementation in Driver class
feerrenrut Nov 4, 2019
520b9e4
Comments for dialog announcement in toggle Screen curtain gesture
feerrenrut Nov 4, 2019
5ddfbc6
Fix lint error
feerrenrut Nov 4, 2019
b5e0ba4
Use type hinting rather than docstring
feerrenrut Nov 5, 2019
e682631
Rename DriverSettingsMixin to AutoSettingsMixin
feerrenrut Nov 5, 2019
0ed5b0d
Add requirements doc string for classes using the AutoSettingsMixin
feerrenrut Nov 5, 2019
34552c4
Remove debug logging from AutoSettingsMixin
feerrenrut Nov 5, 2019
ddd371e
Fix some issues in docs
feerrenrut Nov 5, 2019
ef1932b
Fix multiple provider error handling.
feerrenrut Nov 6, 2019
94b28c9
Ensure destruction of warning dialog for screencurtain
feerrenrut Nov 6, 2019
3b886f9
Improve readability of make settings method.
feerrenrut Nov 6, 2019
07a5637
Fix ScreenCurtain enable checkbox state after initialisation error
feerrenrut Nov 7, 2019
0697bad
Fix initial state of screenCurtain enabled checkbox
feerrenrut Nov 7, 2019
edb662f
Refactor updateDriverSettings in in AutoSettingsMixin
feerrenrut Nov 7, 2019
9a3dd87
Remove noqa comment for bare Except
feerrenrut Nov 7, 2019
a35cdc2
Change error message for a provider GUI that can not be created
feerrenrut Nov 7, 2019
a8d5165
Simplify _enableToggle logic
feerrenrut Nov 7, 2019
e3f3998
Simplify and reorder imports
feerrenrut Nov 7, 2019
5b6e86d
Remove package level getProviderList method
feerrenrut Nov 7, 2019
5c1c93a
Fix typo in comment
feerrenrut Nov 7, 2019
25ad61a
Clarify comment
feerrenrut Nov 7, 2019
5ca437d
Use US spelling for initialize
feerrenrut Nov 7, 2019
2ba8a2c
Clarify comments on getSettingsPanelClass(cls)
feerrenrut Nov 7, 2019
b1362e9
Fix spelling of NVDAHighlighter class
feerrenrut Nov 7, 2019
ecfb700
Fix unused imports
feerrenrut Nov 7, 2019
c5116af
Remove duplicated methods
feerrenrut Nov 7, 2019
ee52b65
Fix missing call to super from terminate
feerrenrut Nov 7, 2019
4a02c38
Change getSettings to abstract
feerrenrut Nov 8, 2019
64794f0
Catch any error on re-init
feerrenrut Nov 8, 2019
fe4bc91
Fix lint
feerrenrut Nov 8, 2019
20813a2
Fix translation test
feerrenrut Nov 8, 2019
d587e5a
Rename example provider
feerrenrut Nov 8, 2019
3f3f7c0
Add instructions to test the example provider
feerrenrut Nov 8, 2019
62820ca
Clean up param/percent conv functions
feerrenrut Nov 13, 2019
e52d2b6
Add missing translator comments
feerrenrut Nov 13, 2019
223d4dd
remove unused properties
feerrenrut Nov 13, 2019
6b4e7dd
Fix incorrect type hint
feerrenrut Nov 13, 2019
9c25e25
Fix doc-string
feerrenrut Nov 13, 2019
6436b1d
Use ABC from base class
feerrenrut Nov 13, 2019
f8da0c5
Refactor provider list generation
feerrenrut Nov 13, 2019
0753c54
Make getProviderClass private
feerrenrut Nov 13, 2019
226d436
Move getProviderList and getProviderInfo functions
feerrenrut Nov 13, 2019
71d30da
Fix no contextmanager
feerrenrut Nov 13, 2019
eb4498f
Fix documentation for runtime settings
feerrenrut Nov 13, 2019
5be590a
Fix error on termination handling for autoGui providers
feerrenrut Nov 13, 2019
2701d41
Fix parent windows binding to AutoSettingsMixin controls
feerrenrut Nov 13, 2019
b4962c5
AutoSettings.percentToParam should be private, with an underscore
Nov 14, 2019
16717ff
Make VisionHandler.providers private
feerrenrut Nov 14, 2019
7ebc511
Update user guide
Nov 14, 2019
ef9b4fa
Fix square brackets
Nov 14, 2019
a9f179a
User docs: visual aide> visual aid
Nov 14, 2019
01dfbab
Fix spelling: aides vs aids
feerrenrut Nov 14, 2019
0a09c26
Handle providers with no options with auto gui
feerrenrut Nov 14, 2019
77ac29a
Remove saveSettings param from Driver.terminate
feerrenrut Nov 18, 2019
c4b8492
Remove preInitSettings property
feerrenrut Nov 18, 2019
e31e3a2
Rename getTranalatedName -> getDisplayName
feerrenrut Nov 18, 2019
4daa3de
Fix consistency of imports.
feerrenrut Nov 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 97 additions & 49 deletions source/driverHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import config
from copy import deepcopy
from logHandler import log
from typing import List, Tuple, Dict, Union


class Driver(AutoPropertyObject):
"""
Expand Down Expand Up @@ -44,41 +46,59 @@ def __init__(self):
super(Driver, self).__init__()
config.pre_configSave.register(self.saveSettings)

def initSettings(self):
"""
Initializes the configuration for this driver.
This method is called when initializing the driver.
"""
firstLoad = not config.conf[self._configSection].isSet(self.name)
@classmethod
def _initSpecificSettings(cls, clsOrInst, settings: List):
firstLoad = not config.conf[cls._configSection].isSet(cls.name)
if firstLoad:
# Create the new section.
config.conf[self._configSection][self.name] = {}
config.conf[cls._configSection][cls.name] = {}
# Make sure the config spec is up to date, so the config validator does its work.
config.conf[self._configSection][self.name].spec.update(self.getConfigSpec())
# Make sure the instance has attributes for every setting
for setting in self.supportedSettings:
if not hasattr(self, setting.id):
setattr(self, setting.id, setting.defaultVal)
config.conf[cls._configSection][cls.name].spec.update(
cls._getConfigSPecForSettings(settings)
)
# Make sure the clsOrInst has attributes for every setting
for setting in settings:
if not hasattr(clsOrInst, setting.id):
setattr(clsOrInst, setting.id, setting.defaultVal)
if firstLoad:
self.saveSettings() #save defaults
cls._saveSpecificSettings(clsOrInst, settings) # save defaults
else:
self.loadSettings()
cls._loadSpecificSettings(clsOrInst, settings)

def initSettings(self):
"""
Initializes the configuration for this driver.
This method is called when initializing the driver.
"""
self._initSpecificSettings(self, self.supportedSettings)

def terminate(self):
def terminate(self, saveSettings: bool = True):
"""Terminate this driver.
This should be used for any required clean up.
@param saveSettings: Whether settings should be saved on termination.
@precondition: L{initialize} has been called.
@postcondition: This driver can no longer be used.
"""
self.saveSettings()
if saveSettings:
self.saveSettings()
config.pre_configSave.unregister(self.saveSettings)

@classmethod
def _get_preInitSettings(self) -> Union[List, Tuple]:
"""The settings supported by the driver at pre initialisation time.
@rtype: list or tuple of L{DriverSetting}
"""
return ()

_abstract_supportedSettings = True
def _get_supportedSettings(self):

def _get_supportedSettings(self) -> Union[List, Tuple]:
"""The settings supported by the driver.
When overriding this property, subclasses are encouraged to extend the getter method
to ensure that L{preInitSettings} is part of the list of supported settings.
@rtype: list or tuple of L{DriverSetting}
"""
return ()
return self.preInitSettings

@classmethod
def check(cls):
Expand All @@ -98,60 +118,88 @@ def isSupported(self,settingID):
if s.id == settingID: return True
return False

def getConfigSpec(self):
spec=deepcopy(config.confspec[self._configSection]["__many__"])
for setting in self.supportedSettings:
@classmethod
def _getConfigSPecForSettings(
cls,
settings: Union[List, Tuple]
) -> Dict:
spec = deepcopy(config.confspec[cls._configSection]["__many__"])
for setting in settings:
if not setting.useConfig:
continue
spec[setting.id]=setting.configSpec
return spec

def getConfigSpec(self):
return self._getConfigSPecForSettings(self.supportedSettings)

@classmethod
def _saveSpecificSettings(
cls,
clsOrInst,
settings: Union[List, Tuple]
):
conf = config.conf[cls._configSection][cls.name]
for setting in settings:
if not setting.useConfig:
continue
try:
conf[setting.id] = getattr(clsOrInst, setting.id)
except UnsupportedConfigParameterError:
log.debugWarning(
f"Unsupported setting {setting.id!r}; ignoring",
exc_info=True
)
continue
if settings:
log.debug(f"Saved settings for {cls.__qualname__}")

def saveSettings(self):
"""
Saves the current settings for the driver to the configuration.
This method is also executed when the driver is loaded for the first time,
in order to populate the configuration with the initial settings..
"""
conf=config.conf[self._configSection][self.name]
for setting in self.supportedSettings:
if not setting.useConfig:
self._saveSpecificSettings(self, self.supportedSettings)

@classmethod
def _loadSpecificSettings(
cls,
clsOrInst,
settings: Union[List, Tuple],
onlyChanged: bool = False
):
conf = config.conf[cls._configSection][cls.name]
for setting in settings:
if not setting.useConfig or conf.get(setting.id) is None:
continue
val = conf[setting.id]
if onlyChanged and getattr(clsOrInst, setting.id) == val:
continue
try:
conf[setting.id] = getattr(self,setting.id)
setattr(clsOrInst, setting.id, val)
except UnsupportedConfigParameterError:
log.debugWarning("Unsupported setting %s; ignoring"%s.id, exc_info=True)
log.debugWarning(
f"Unsupported setting {setting.name!r}; ignoring",
exc_info=True
)
continue
if self.supportedSettings:
log.debug("Saved settings for {} {}".format(self.__class__.__name__, self.name))
if settings:
log.debug(
f"Loaded changed settings for {cls.__qualname__}"
if onlyChanged else
f"Loaded settings for {cls.__qualname__}"
)

def loadSettings(self, onlyChanged=False):
def loadSettings(self, onlyChanged: bool = False):
"""
Loads settings for this driver from the configuration.
This method assumes that the instance has attributes o/properties
corresponding with the name of every setting in L{supportedSettings}.
@param onlyChanged: When loading settings, only apply those for which
the value in the configuration differs from the current value.
@type onlyChanged: bool
"""
conf=config.conf[self._configSection][self.name]
for setting in self.supportedSettings:
if not setting.useConfig or conf.get(setting.id) is None:
continue
val=conf[setting.id]
if onlyChanged and getattr(self,setting.id) == val:
continue
try:
setattr(self,setting.id, val)
except UnsupportedConfigParameterError:
log.debugWarning("Unsupported setting %s; ignoring"%setting.name, exc_info=True)
continue
if self.supportedSettings:
log.debug(
(
"Loaded changed settings for {} {}"
if onlyChanged else
"Loaded settings for {} {}"
).format(self.__class__.__name__, self.name))
self._loadSpecificSettings(self, self.supportedSettings, onlyChanged)

@classmethod
def _paramToPercent(cls, current, min, max):
feerrenrut marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
20 changes: 15 additions & 5 deletions source/globalCommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2302,15 +2302,25 @@ def script_toggleScreenCurtain(self, gesture):
return
scriptCount = scriptHandler.getLastScriptRepeatCount()
if scriptCount == 0 and screenCurtainName in vision.handler.providers:
vision.handler.terminateProvider(screenCurtainName)
try:
vision.handler.terminateProvider(screenCurtainName)
except Exception:
# If the screen curtain was enabled, we do not expect exceptions.
log.error("Screen curtain termination error", exc_info=True)
# Translators: Reported when the screen curtain could not be enabled.
message = _("Could not disable screen curtain")
return
# Translators: Reported when the screen curtain is disabled.
message = _("Screen curtain disabled")
elif scriptCount in (0, 2):
temporary = scriptCount == 0
if not vision.handler.initializeProvider(
screenCurtainName,
temporary=temporary,
):
try:
vision.handler.initializeProvider(
screenCurtainName,
temporary=temporary,
)
except Exception:
log.error("Screen curtain initialization error", exc_info=True)
# Translators: Reported when the screen curtain could not be enabled.
message = _("Could not enable screen curtain")
return
Expand Down
14 changes: 10 additions & 4 deletions source/gui/nvdaControls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
#Copyright (C) 2016-2018 NV Access Limited, Derek Riemer
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.

from ctypes.wintypes import BOOL
from typing import Any, Tuple, Optional

import wx
from comtypes import GUID
from wx.lib.mixins import listctrl as listmix
from gui import accPropServer
from gui.dpiScalingHelper import DpiScalingHelperMixin
from . import accPropServer
from .dpiScalingHelper import DpiScalingHelperMixin
from . import guiHelper
import oleacc
import winUser
import winsound
Expand Down Expand Up @@ -305,6 +306,11 @@ def _addButtons(self, buttonHelper):
)
cancel.Bind(wx.EVT_BUTTON, lambda evt: self.EndModal(wx.CANCEL))

def _addContents(self, contentsSizer: guiHelper.BoxSizerHelper):
"""Adds additional contents to the dialog, before the buttons.
Subclasses may implement this method.
"""

def _setIcon(self, type):
try:
iconID = self._DIALOG_TYPE_ICON_ID_MAP[type]
Expand Down Expand Up @@ -333,12 +339,12 @@ def __init__(self, parent, title, message, dialogType=DIALOG_TYPE_STANDARD):
self.Bind(wx.EVT_SHOW, self._onShowEvt, source=self)

mainSizer = wx.BoxSizer(wx.VERTICAL)
from . import guiHelper
contentsSizer = guiHelper.BoxSizerHelper(parent=self, orientation=wx.VERTICAL)

text = wx.StaticText(self, label=message)
text.Wrap(self.scaleSize(self.GetSize().Width))
contentsSizer.addItem(text)
self._addContents(contentsSizer)

buttonHelper = guiHelper.ButtonHelper(wx.HORIZONTAL)
self._addButtons(buttonHelper)
Expand Down
Loading