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:
|
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:
|
def luxToEV(lux: float) -> float:
|
||||||
# Wikipedia (https://en.wikipedia.org/wiki/Light_meter#Exposure_equations)
|
# 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)
|
# says E = 2.5 * (2^EV), so the inverse is EV = ln(2*E/5)/ln(2)
|
||||||
return log(2*lux/5, 2)
|
return log(2*lux/5, 2)
|
||||||
|
|
||||||
def calcShutterSpeed(isoSpeed: int, lux: float, aperture: float):
|
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):
|
def calcAperture(isoSpeed: int, lux: float, shutterSpeed: float):
|
||||||
# shutterSpeed is in seconds
|
# shutterSpeed is in seconds
|
||||||
return sqrt(lux*isoSpeed*shutterSpeed/K)
|
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 gi.repository import Gio, GLib
|
||||||
from threading import Timer
|
from threading import Timer
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
import traceback
|
||||||
|
|
||||||
# window.py
|
# window.py
|
||||||
#
|
#
|
||||||
@ -46,6 +47,7 @@ class SensorsPollingTimer(Timer):
|
|||||||
self.function(value, unit) # Invoke callback
|
self.function(value, unit) # Invoke callback
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(traceback.format_exc(e))
|
||||||
if not self.onError:
|
if not self.onError:
|
||||||
print("SensorsPollingTimer: error occurred, but no onError callback defined")
|
print("SensorsPollingTimer: error occurred, but no onError callback defined")
|
||||||
return
|
return
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
label.lumos-big-result {
|
label.lumos-big-result {
|
||||||
font-size: 80px;
|
font-size: 60px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,25 +26,15 @@ class AperturePriorityPage(Gtk.Box):
|
|||||||
__gtype_name__ = 'AperturePriorityPage'
|
__gtype_name__ = 'AperturePriorityPage'
|
||||||
|
|
||||||
# Values of aperture dropdown entries defined in the .ui file
|
# Values of aperture dropdown entries defined in the .ui file
|
||||||
__aperture_priority_speed_dropdown_values = [
|
# TODO: Load dropdown strings from APERTURE_VALUES.values()
|
||||||
1/32,
|
__aperture_priority_speed_dropdown_values = list(EVCalculator.APERTURE_VALUES.keys())
|
||||||
1/22,
|
|
||||||
1/16,
|
|
||||||
1/11,
|
|
||||||
1/8,
|
|
||||||
1/5.6,
|
|
||||||
1/4,
|
|
||||||
1/2.8,
|
|
||||||
1/2,
|
|
||||||
1/1.4
|
|
||||||
]
|
|
||||||
|
|
||||||
# Widgets
|
# Widgets
|
||||||
aperture_priority_aperture_dropdown = Gtk.Template.Child()
|
aperture_priority_aperture_dropdown = Gtk.Template.Child()
|
||||||
aperture_priority_time_label = Gtk.Template.Child()
|
aperture_priority_time_label = Gtk.Template.Child()
|
||||||
|
|
||||||
__sensorValue = None
|
__sensorValue = None
|
||||||
__isoSpeed = 100
|
__isoSpeed = EVCalculator.DEFAULT_ISO_SPEED
|
||||||
|
|
||||||
def onValuesChanged(self, isoSpeed: int, sensorValue: float, sensorUnit: str):
|
def onValuesChanged(self, isoSpeed: int, sensorValue: float, sensorUnit: str):
|
||||||
# Check the unit is absolute ("lux")
|
# Check the unit is absolute ("lux")
|
||||||
@ -64,9 +54,16 @@ class AperturePriorityPage(Gtk.Box):
|
|||||||
def updateView(self):
|
def updateView(self):
|
||||||
apertureValue = self.__aperture_priority_speed_dropdown_values[self.aperture_priority_aperture_dropdown.get_selected()]
|
apertureValue = self.__aperture_priority_speed_dropdown_values[self.aperture_priority_aperture_dropdown.get_selected()]
|
||||||
shutterSpeed = EVCalculator.calcShutterSpeed(self.__isoSpeed, self.__sensorValue, apertureValue)
|
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()
|
@Gtk.Template.Callback()
|
||||||
def onApertureChanged(self, dropDown: Gtk.DropDown, _: any):
|
def onApertureChanged(self, dropDown: Gtk.DropDown, _: any):
|
||||||
self.updateView()
|
self.updateView()
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="aperture_priority_time_label">
|
<object class="GtkLabel" id="aperture_priority_time_label">
|
||||||
<property name="label" translatable="no">--s</property>
|
<property name="label" translatable="no">--s</property>
|
||||||
|
<property name="use-markup">true</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="lumos-big-result"/>
|
<class name="lumos-big-result"/>
|
||||||
</style>
|
</style>
|
||||||
|
@ -52,7 +52,7 @@ class TimePriorityPage(Gtk.Box):
|
|||||||
time_priority_aperture_label = Gtk.Template.Child()
|
time_priority_aperture_label = Gtk.Template.Child()
|
||||||
|
|
||||||
__sensorValue = None
|
__sensorValue = None
|
||||||
__isoSpeed = 100
|
__isoSpeed = EVCalculator.DEFAULT_ISO_SPEED
|
||||||
|
|
||||||
def onValuesChanged(self, isoSpeed: int, sensorValue: float, sensorUnit: str):
|
def onValuesChanged(self, isoSpeed: int, sensorValue: float, sensorUnit: str):
|
||||||
# Check the unit is absolute ("lux")
|
# Check the unit is absolute ("lux")
|
||||||
@ -71,8 +71,13 @@ class TimePriorityPage(Gtk.Box):
|
|||||||
def updateView(self):
|
def updateView(self):
|
||||||
shutterSpeed = self.__time_priority_speed_dropdown_values[self.time_priority_speed_dropdown.get_selected()]
|
shutterSpeed = self.__time_priority_speed_dropdown_values[self.time_priority_speed_dropdown.get_selected()]
|
||||||
apertureValue = EVCalculator.calcAperture(self.__isoSpeed, self.__sensorValue, shutterSpeed)
|
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()
|
@Gtk.Template.Callback()
|
||||||
def onShutterSpeedChanged(self, dropDown: Gtk.DropDown, _: any):
|
def onShutterSpeedChanged(self, dropDown: Gtk.DropDown, _: any):
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="time_priority_aperture_label">
|
<object class="GtkLabel" id="time_priority_aperture_label">
|
||||||
<property name="label" translatable="no">f/--</property>
|
<property name="label" translatable="no">f/--</property>
|
||||||
|
<property name="use-markup">true</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="lumos-big-result"/>
|
<class name="lumos-big-result"/>
|
||||||
</style>
|
</style>
|
||||||
|
@ -69,7 +69,6 @@ class LumosWindow(Adw.ApplicationWindow):
|
|||||||
self.sensor_unit_error_banner.set_revealed(True)
|
self.sensor_unit_error_banner.set_revealed(True)
|
||||||
|
|
||||||
def onError(self, e: Exception):
|
def onError(self, e: Exception):
|
||||||
print(e)
|
|
||||||
self.lastError = e
|
self.lastError = e
|
||||||
self.error_banner.set_revealed(True)
|
self.error_banner.set_revealed(True)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user