Skip to content

Commit dd5eda9

Browse files
committed
New percentage helper function.
Since we were converting brightness values (int, float, str and relative) to a percentage in multiple places, it made sense to centralize it
1 parent 43777a7 commit dd5eda9

File tree

4 files changed

+105
-36
lines changed

4 files changed

+105
-36
lines changed

screen_brightness_control/__init__.py

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from ._debug import info as debug_info # noqa: F401
99
from ._version import __author__, __version__ # noqa: F401
1010
from .exceptions import NoValidDisplayError, format_exc
11-
from .helpers import MONITOR_MANUFACTURER_CODES # noqa: F401
11+
from .helpers import MONITOR_MANUFACTURER_CODES, percentage # noqa: F401
1212
from .helpers import BrightnessMethod, ScreenBrightnessError, logarithmic_range
1313

1414
logger = logging.getLogger(__name__)
@@ -99,34 +99,32 @@ def set_brightness(
9999
```
100100
'''
101101
if isinstance(value, str) and ('+' in value or '-' in value):
102-
value = int(float(value))
103-
monitors = filter_monitors(display=display, method=method)
104-
if len(monitors) > 1:
105-
output = []
106-
for monitor in monitors:
107-
identifier = Monitor.get_identifier(monitor)[1]
108-
tmp = set_brightness(
109-
get_brightness(display=identifier)[0] + value,
110-
display=identifier,
111-
force=force,
112-
verbose_error=verbose_error,
113-
no_return=no_return
114-
)
115-
if isinstance(tmp, list):
116-
output += tmp
117-
else: # otherwise the output should be None
118-
output.append(tmp)
119-
return output
120-
121-
value += get_brightness(display=Monitor.get_identifier(monitors[0])[1])[0]
122-
else:
123-
value = int(float(str(value)))
102+
output = []
103+
for monitor in filter_monitors(display=display, method=method):
104+
identifier = Monitor.get_identifier(monitor)[1]
105+
current_value = get_brightness(display=identifier)[0]
106+
result = set_brightness(
107+
# don't need to calculate lower bound here because it will be
108+
# done by the other path in `set_brightness`
109+
percentage(value, current=current_value),
110+
display=identifier,
111+
force=force,
112+
verbose_error=verbose_error,
113+
no_return=no_return
114+
)
115+
if result is None:
116+
output.append(result)
117+
else:
118+
output += result
124119

125-
# make sure value is within bounds
126-
value = max(min(100, value), 0)
120+
return output
127121

128122
if platform.system() == 'Linux' and not force:
129-
value = max(1, value)
123+
lower_bound = 1
124+
else:
125+
lower_bound = 0
126+
127+
value = percentage(value, lower_bound=lower_bound)
130128

131129
return __brightness(
132130
value, display=display, method=method,
@@ -498,19 +496,18 @@ def set_brightness(self, value: Union[int, str], no_return: bool = True, force:
498496
# refresh display info, in case another display has been unplugged or something
499497
# which would change the index of this display
500498
self.get_info()
501-
# min brightness value
499+
500+
# convert brightness value to percentage
502501
if platform.system() == 'Linux' and not force:
503502
lower_bound = 1
504503
else:
505504
lower_bound = 0
506505

507-
# changing string value to int
508-
if isinstance(value, str) and ('+' in value or '-' in value):
509-
value = int(float(value))
510-
value += self.method.get_brightness(display=self.index)[0]
511-
else:
512-
value = int(float(str(value)))
513-
value = max(lower_bound, min(value, 100))
506+
value = percentage(
507+
value,
508+
current=lambda: self.method.get_brightness(display=self.index)[0],
509+
lower_bound=lower_bound
510+
)
514511
self.method.set_brightness(value, display=self.index)
515512
if no_return:
516513
return None

screen_brightness_control/helpers.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import time
99
from abc import ABC, abstractclassmethod
1010
from functools import lru_cache
11-
from typing import List, Optional, Tuple, Union
11+
from typing import Callable, List, Optional, Tuple, Union
1212

1313
from .exceptions import (EDIDParseError, MaxRetriesExceededError, # noqa:F401
1414
ScreenBrightnessError)
@@ -448,3 +448,30 @@ def _monitor_brand_lookup(search: str) -> Union[Tuple[str, str], None]:
448448
else:
449449
return None
450450
return keys[index], values[index]
451+
452+
453+
def percentage(value: Union[int, str], current: Union[int, Callable[[], int]] = None, lower_bound: int = 0) -> int:
454+
'''
455+
Convenience function to convert a brightness value into a percentage. Can handle
456+
integers, floats and strings. Also can handle relative strings (eg: `'+10'` or `'-10'`)
457+
458+
Args:
459+
value (int or str or float): the brightness value to convert
460+
current (int or callable): the current brightness value or a function that returns the current brightness
461+
value. Used when dealing with relative brightness values
462+
lower_bound (int): the minimum value the brightness can be set to
463+
464+
Returns:
465+
int: the new brightness percentage, between `lower_bound` and 100
466+
'''
467+
if isinstance(value, str) and ('+' in value or '-' in value):
468+
if callable(current):
469+
current = current()
470+
value = int(float(value)) + int(float(str(current)))
471+
else:
472+
value = int(float(str(value)))
473+
474+
return min(
475+
100,
476+
max(lower_bound, value)
477+
)

screen_brightness_control/windows.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ def get_display_info() -> List[dict]:
8888
i.InstanceName
8989
for i in wmi.WmiMonitorBrightness()
9090
]
91-
except wmi.x_wmi as e:
91+
except Exception as e:
92+
# don't do specific exception classes here because WMI does not play ball with it
9293
logger.warning(f'get_display_info: failed to gather list of laptop displays - {format_exc(e)}')
9394
laptop_displays = []
9495

tests/test_helpers.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,50 @@ def test_skip_intervals(self):
9696
self.assertGreaterEqual(l_range[0] - l_range[1], l_range[-2] - l_range[-1])
9797

9898

99+
class TestPercentage(unittest.TestCase):
100+
def test_normal(self):
101+
percentage = sbc.helpers.percentage
102+
103+
# int
104+
self.assertEqual(percentage(100), 100)
105+
self.assertEqual(percentage(50), 50)
106+
self.assertEqual(percentage(0), 0)
107+
# float
108+
self.assertEqual(percentage(59.3), 59)
109+
self.assertEqual(percentage(24.999), 24)
110+
# str
111+
self.assertEqual(percentage('99'), 99)
112+
self.assertEqual(percentage('12.125'), 12)
113+
114+
def test_relative(self):
115+
percentage = sbc.helpers.percentage
116+
117+
self.assertEqual(percentage('+10', current=10), 20)
118+
self.assertEqual(percentage('-5', current=30), 25)
119+
self.assertEqual(percentage('-21', current=lambda: 99), 78)
120+
self.assertEqual(percentage('+50', current=lambda: 50), 100)
121+
self.assertEqual(percentage('-10.5', current=100, lower_bound=10), 90)
122+
123+
def test_bounds(self):
124+
percentage = sbc.helpers.percentage
125+
126+
self.assertEqual(percentage(101), 100)
127+
self.assertEqual(percentage(1000), 100)
128+
self.assertEqual(percentage(-1), 0)
129+
self.assertEqual(percentage(-19999), 0)
130+
self.assertEqual(percentage('-100', current=0), 0)
131+
self.assertEqual(percentage('+1000000', current=0), 100)
132+
133+
self.assertEqual(percentage(0, lower_bound=1), 1)
134+
self.assertEqual(percentage('-10', current=10, lower_bound=1), 1)
135+
136+
def test_abnormal(self):
137+
percentage = sbc.helpers.percentage
138+
139+
self.assertRaises(ValueError, percentage, [123])
140+
self.assertRaises(ValueError, percentage, '1{2!3')
141+
142+
99143
if __name__ == '__main__':
100144
if '--synthetic' in sys.argv:
101145
sys.argv.remove('--synthetic')

0 commit comments

Comments
 (0)