Clearer error handling, rounding values to nearest stop
This commit is contained in:
parent
b1f31c08cf
commit
e4a7e4fcef
@ -18,15 +18,84 @@ K = 12.5
|
||||
|
||||
class EVCalculator:
|
||||
|
||||
DEFAULT_ISO_SPEED = 100
|
||||
|
||||
APERTURE_VALUES = {
|
||||
1/32: "1/32",
|
||||
1/22: "1/22",
|
||||
1/16: "1/16",
|
||||
1/11: "1/11",
|
||||
1/8: "1/8",
|
||||
1/5.6: "1/5.6",
|
||||
1/4: "1/4",
|
||||
1/2.8: "1/2.8",
|
||||
1/2: "1/2",
|
||||
1/1.4: "1/1.4"
|
||||
}
|
||||
|
||||
SHUTTER_SPEED_VALUES = {
|
||||
1/4000: "1/4000",
|
||||
1/2000: "1/2000",
|
||||
1/1000: "1/1000",
|
||||
1/500: "1/500",
|
||||
1/250: "1/250",
|
||||
1/125: "1/125",
|
||||
1/60: "1/60",
|
||||
1/30: "1/30",
|
||||
1/15: "1/15",
|
||||
1/8: "1/8",
|
||||
1/4: "1/4",
|
||||
1/2: "1/2",
|
||||
1: "1",
|
||||
2: "2",
|
||||
4: "4",
|
||||
8: "8",
|
||||
15: "15",
|
||||
30: "30"
|
||||
}
|
||||
|
||||
def luxToEV(lux: float) -> float:
|
||||
# Wikipedia (https://en.wikipedia.org/wiki/Light_meter#Exposure_equations)
|
||||
# says E = 2.5 * (2^EV), so the inverse is EV = ln(2*E/5)/ln(2)
|
||||
return log(2*lux/5, 2)
|
||||
|
||||
def calcShutterSpeed(isoSpeed: int, lux: float, aperture: float):
|
||||
return (aperture*aperture*K)/(lux*isoSpeed)
|
||||
result = (aperture*aperture*K)/(lux*isoSpeed)
|
||||
print("aperture: {}, K: {}, lux: {}, isoSpeed: {}, result: {}".format(aperture, K, lux, isoSpeed, result))
|
||||
return result
|
||||
|
||||
def calcAperture(isoSpeed: int, lux: float, shutterSpeed: float):
|
||||
# shutterSpeed is in seconds
|
||||
return sqrt(lux*isoSpeed*shutterSpeed/K)
|
||||
|
||||
|
||||
def toNearestStr(valueToRound: float, roundingValues: dict[float,str]) -> [str, bool]:
|
||||
# Rounds to the nearest value in the provided ORDERED dict.
|
||||
# The dict is expected to have a float value as key and the corresponding human readable string value as value
|
||||
# Returns the string value and a boolean set to true if the value was in the list range.
|
||||
|
||||
roundingNumericValues = list(roundingValues.keys())
|
||||
roundingStringValues = list(roundingValues.values())
|
||||
|
||||
first = roundingNumericValues[0]
|
||||
last = roundingNumericValues[-1]
|
||||
|
||||
if valueToRound < first / 1.5:
|
||||
# More than half stop below minimum value!
|
||||
return [roundingStringValues[0], False]
|
||||
if valueToRound > last * 1.5:
|
||||
# More than half stop above maximum value!
|
||||
return [roundingStringValues[-1], False]
|
||||
|
||||
# Note: range(roundingValues - 1) to exclude last element
|
||||
for i in range(len(roundingNumericValues) - 1):
|
||||
value = float(roundingNumericValues[i])
|
||||
nextValue = float(roundingNumericValues[i + 1])
|
||||
limit = (value + nextValue) / 2
|
||||
if valueToRound <= limit:
|
||||
return [roundingStringValues[i], True]
|
||||
return [roundingStringValues[-1], True]
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
from gi.repository import Gio, GLib
|
||||
from threading import Timer
|
||||
from enum import Enum
|
||||
import traceback
|
||||
|
||||
# window.py
|
||||
#
|
||||
@ -46,6 +47,7 @@ class SensorsPollingTimer(Timer):
|
||||
self.function(value, unit) # Invoke callback
|
||||
|
||||
except Exception as e:
|
||||
print(traceback.format_exc(e))
|
||||
if not self.onError:
|
||||
print("SensorsPollingTimer: error occurred, but no onError callback defined")
|
||||
return
|
||||
|
@ -1,5 +1,5 @@
|
||||
label.lumos-big-result {
|
||||
font-size: 80px;
|
||||
font-size: 60px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
|
@ -26,25 +26,15 @@ class AperturePriorityPage(Gtk.Box):
|
||||
__gtype_name__ = 'AperturePriorityPage'
|
||||
|
||||
# Values of aperture dropdown entries defined in the .ui file
|
||||
__aperture_priority_speed_dropdown_values = [
|
||||
1/32,
|
||||
1/22,
|
||||
1/16,
|
||||
1/11,
|
||||
1/8,
|
||||
1/5.6,
|
||||
1/4,
|
||||
1/2.8,
|
||||
1/2,
|
||||
1/1.4
|
||||
]
|
||||
# TODO: Load dropdown strings from APERTURE_VALUES.values()
|
||||
__aperture_priority_speed_dropdown_values = list(EVCalculator.APERTURE_VALUES.keys())
|
||||
|
||||
# Widgets
|
||||
aperture_priority_aperture_dropdown = Gtk.Template.Child()
|
||||
aperture_priority_time_label = Gtk.Template.Child()
|
||||
|
||||
__sensorValue = None
|
||||
__isoSpeed = 100
|
||||
__isoSpeed = EVCalculator.DEFAULT_ISO_SPEED
|
||||
|
||||
def onValuesChanged(self, isoSpeed: int, sensorValue: float, sensorUnit: str):
|
||||
# Check the unit is absolute ("lux")
|
||||
@ -64,9 +54,16 @@ class AperturePriorityPage(Gtk.Box):
|
||||
def updateView(self):
|
||||
apertureValue = self.__aperture_priority_speed_dropdown_values[self.aperture_priority_aperture_dropdown.get_selected()]
|
||||
shutterSpeed = EVCalculator.calcShutterSpeed(self.__isoSpeed, self.__sensorValue, apertureValue)
|
||||
# TODO: Round shutter speed value to nearest existing value and set label color to red if outside 1 stop range
|
||||
self.aperture_priority_time_label.set_label("1/ {:.5f}".format(shutterSpeed))
|
||||
|
||||
# Round shutter speed value to nearest existing value and set label color to red if outside 1 stop range
|
||||
[nearestValue, isInsideRange] = EVCalculator.toNearestStr(shutterSpeed, EVCalculator.SHUTTER_SPEED_VALUES)
|
||||
|
||||
if isInsideRange:
|
||||
self.aperture_priority_time_label.set_label(nearestValue)
|
||||
else:
|
||||
self.aperture_priority_time_label.set_label("<span foreground=\"red\">{}</span>".format(nearestValue))
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def onApertureChanged(self, dropDown: Gtk.DropDown, _: any):
|
||||
self.updateView()
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
<child>
|
||||
<object class="GtkLabel" id="aperture_priority_time_label">
|
||||
<property name="label" translatable="no">--s</property>
|
||||
<property name="use-markup">true</property>
|
||||
<style>
|
||||
<class name="lumos-big-result"/>
|
||||
</style>
|
||||
|
@ -52,7 +52,7 @@ class TimePriorityPage(Gtk.Box):
|
||||
time_priority_aperture_label = Gtk.Template.Child()
|
||||
|
||||
__sensorValue = None
|
||||
__isoSpeed = 100
|
||||
__isoSpeed = EVCalculator.DEFAULT_ISO_SPEED
|
||||
|
||||
def onValuesChanged(self, isoSpeed: int, sensorValue: float, sensorUnit: str):
|
||||
# Check the unit is absolute ("lux")
|
||||
@ -71,8 +71,13 @@ class TimePriorityPage(Gtk.Box):
|
||||
def updateView(self):
|
||||
shutterSpeed = self.__time_priority_speed_dropdown_values[self.time_priority_speed_dropdown.get_selected()]
|
||||
apertureValue = EVCalculator.calcAperture(self.__isoSpeed, self.__sensorValue, shutterSpeed)
|
||||
# TODO: Round aperture value to nearest existing value and set label color to red if outside 1 stop range
|
||||
self.time_priority_aperture_label.set_label("f/ {:.2f}".format(apertureValue))
|
||||
|
||||
# Round aperture value to nearest existing value and set label color to red if outside range
|
||||
[nearestValue, isInsideRange] = EVCalculator.toNearestStr(shutterSpeed, EVCalculator.APERTURE_VALUES)
|
||||
if isInsideRange:
|
||||
self.time_priority_aperture_label.set_label(nearestValue)
|
||||
else:
|
||||
self.time_priority_aperture_label.set_label("<span foreground=\"red\">{}</span>".format(nearestValue))
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def onShutterSpeedChanged(self, dropDown: Gtk.DropDown, _: any):
|
||||
|
@ -30,6 +30,7 @@
|
||||
<child>
|
||||
<object class="GtkLabel" id="time_priority_aperture_label">
|
||||
<property name="label" translatable="no">f/--</property>
|
||||
<property name="use-markup">true</property>
|
||||
<style>
|
||||
<class name="lumos-big-result"/>
|
||||
</style>
|
||||
|
@ -69,7 +69,6 @@ class LumosWindow(Adw.ApplicationWindow):
|
||||
self.sensor_unit_error_banner.set_revealed(True)
|
||||
|
||||
def onError(self, e: Exception):
|
||||
print(e)
|
||||
self.lastError = e
|
||||
self.error_banner.set_revealed(True)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user