Skip to content

Commit

Permalink
Adding numeric input fields inplace of QSpinBox to allow empty text (#…
Browse files Browse the repository at this point in the history
…168)

* Adding numeric input fields inplace of QSpinBox to allow empty text

* Fixing unit tests

* Code cleanup
  • Loading branch information
DasAmpharos authored Aug 8, 2024
1 parent c93d6e5 commit 2016a31
Show file tree
Hide file tree
Showing 23 changed files with 431 additions and 202 deletions.
Binary file modified eon_timer/resources/themes/default.zip
Binary file not shown.
Binary file modified eon_timer/resources/themes/pokeball.zip
Binary file not shown.
15 changes: 8 additions & 7 deletions eon_timer/settings/action/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from PySide6.QtCore import Qt
from PySide6.QtGui import QIcon, QPixmap
from PySide6.QtWidgets import QColorDialog, QPushButton, QSpinBox
from PySide6.QtWidgets import QColorDialog, QPushButton

from eon_timer.util import const
from eon_timer.util.injector import component
Expand All @@ -15,6 +15,7 @@
from eon_timer.util.pyside.file_selector_widget import FileSelectorWidget
from eon_timer.util.pyside.form import FormWidget
from eon_timer.util.pyside.name_service import NameService
from eon_timer.util.pyside.numeric_input_field import IntInputField
from .model import ActionMode, ActionSettingsModel, ActionSound


Expand Down Expand Up @@ -69,15 +70,15 @@ def __init_components(self) -> None:
self.add_field(self.Field.COLOR, field, name='actionSettingsColor')
self.__set_icon_color(field)
# ----- interval -----
field = QSpinBox()
field = IntInputField()
field.set_range(0, const.INT_MAX)
bindings.bind(field.value, self.interval)
self.add_field(self.Field.INTERVAL, field, name='actionSettingsInterval')
field.setRange(0, const.INT_MAX)
bindings.bind_spinbox(field, self.interval)
# ----- count -----
field = QSpinBox()
field = IntInputField()
field.set_range(0, const.INT_MAX)
bindings.bind(field.value, self.count)
self.add_field(self.Field.COUNT, field, name='actionSettingsCount')
field.setRange(0, const.INT_MAX)
bindings.bind_spinbox(field, self.count)

def __on_sound_changed(self, event: PropertyChangeEvent[ActionSound]) -> None:
self.set_visible(self.Field.CUSTOM_SOUND, event.new_value == ActionSound.CUSTOM)
Expand Down
17 changes: 9 additions & 8 deletions eon_timer/settings/timer/widget.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
from typing import Final

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QCheckBox, QDoubleSpinBox, QSpinBox
from PySide6.QtWidgets import QCheckBox

from eon_timer.util.const import INT_MAX
from eon_timer.util.injector import component
from eon_timer.util.loggers import log_method_calls
from eon_timer.util.properties import bindings
from eon_timer.util.properties.property import FloatProperty, Property, BoolProperty
from eon_timer.util.properties.property import BoolProperty, FloatProperty, Property
from eon_timer.util.properties.property_change import PropertyChangeEvent
from eon_timer.util.pyside import EnumComboBox
from eon_timer.util.pyside.form import FormWidget
from eon_timer.util.pyside.name_service import NameService
from eon_timer.util.pyside.numeric_input_field import FloatInputField, IntInputField
from .model import Console, TimerSettingsModel


Expand Down Expand Up @@ -46,19 +47,19 @@ def __init_components(self) -> None:
name='timerSettingsConsole')
bindings.bind_enum_combobox(field, self.console)
# ----- custom framerate -----
field = QDoubleSpinBox()
field = FloatInputField()
field.set_range(0, INT_MAX)
bindings.bind(field.value, self.custom_framerate)
self.add_field(self.Field.CUSTOM_FRAMERATE, field,
visible=self.console.get() == Console.CUSTOM,
name='timerSettingsCustomFramerate')
field.setRange(0, INT_MAX)
bindings.bind_float_spinbox(field, self.custom_framerate)
self.console.on_change(self.__on_console_changed)
# ----- refresh interval -----
field = QSpinBox()
field = IntInputField()
field.set_range(1, INT_MAX)
bindings.bind(field.value, self.refresh_interval)
self.add_field(self.Field.REFRESH_INTERVAL, field,
name='timerSettingsRefreshInterval')
field.setRange(1, INT_MAX)
bindings.bind_spinbox(field, self.refresh_interval)
# ----- precision calibration -----
field = QCheckBox()
self.add_field(self.Field.PRECISION_CALIBRATION, field,
Expand Down
2 changes: 1 addition & 1 deletion eon_timer/timers/custom/custom_phase.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def __init__(self,
self.unit: Final = EnumProperty(unit)
self.target: Final = IntProperty(value)
self.calibration: Final = FloatProperty(calibration)
self.hit: Final = IntProperty(0, transient=True)
self.hit: Final = IntProperty(None, transient=True)

@classmethod
def read(cls, settings: QSettings) -> Self:
Expand Down
47 changes: 22 additions & 25 deletions eon_timer/timers/custom/custom_phase_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
from typing import Final

from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import QDoubleSpinBox, QGroupBox, QHBoxLayout, QLabel, QPushButton, QSizePolicy, QSpinBox, \
QWidget
from PySide6.QtWidgets import QGroupBox, QHBoxLayout, QLabel, QPushButton, QSizePolicy, QWidget

from eon_timer.timers import Calibrator
from eon_timer.util import loggers, pyside
from eon_timer.util import loggers, pyside, strings
from eon_timer.util.const import INT_MAX, INT_MIN
from eon_timer.util.loggers import log_method_calls
from eon_timer.util.properties import bindings
from eon_timer.util.properties.property import IntProperty
from eon_timer.util.properties.property_change import PropertyChangeEvent
from eon_timer.util.pyside import EnumComboBox
from eon_timer.util.pyside.form import FormWidget
from eon_timer.util.pyside.numeric_input_field import BlankBehavior, FloatInputField, IntInputField, Radix
from .custom_phase import CustomPhase


Expand All @@ -39,9 +39,9 @@ def __init__(self,
self.calibrator: Final = calibrator
self.index: Final = IntProperty(index)

self.__target_field: Final = QSpinBox()
self.__calibration_field: Final = QDoubleSpinBox()
self.__hit_field: Final = QSpinBox()
self.__target_field: Final = IntInputField()
self.__calibration_field: Final = FloatInputField()
self.__hit_field: Final = IntInputField()
self.__init_components()

@log_method_calls()
Expand Down Expand Up @@ -77,19 +77,21 @@ def __init_components(self):
form.add_field(self.Field.UNIT, field)
self.__on_unit_changed()
# ----- target -----
self.__target_field.setRange(0, INT_MAX)
bindings.bind_spinbox(self.__target_field, self.model.target)
self.__target_field.valueChanged.connect(self.__on_value_changed)
self.__target_field.set_range(0, INT_MAX)
bindings.bind(self.__target_field.value, self.model.target)
self.__target_field.value.on_change(self.__on_value_changed)
form.add_field(self.Field.TARGET, self.__target_field)
# ----- calibration -----
self.__calibration_field.setRange(INT_MIN, INT_MAX)
bindings.bind_float_spinbox(self.__calibration_field, self.model.calibration)
self.__calibration_field.valueChanged.connect(self.__on_value_changed)
self.__calibration_field.set_range(INT_MIN, INT_MAX)
bindings.bind(self.__calibration_field.value, self.model.calibration)
self.__calibration_field.value.on_change(self.__on_value_changed)
form.add_field(self.Field.CALIBRATION, self.__calibration_field)
# ----- hit_field -----
self.__hit_field.setRange(0, INT_MAX)
bindings.bind_spinbox(self.__hit_field, self.model.hit)
self.__hit_field.set_range(0, INT_MAX)
self.__hit_field.blank_behavior = BlankBehavior.BLANK
bindings.bind(self.__hit_field.value, self.model.hit)
form.add_field(self.Field.HIT, self.__hit_field)
self.__hit_field.setText('')
# ----- remove_btn -----
button = QPushButton(chr(0xf057))
button.setFont('Font Awesome 5 Free')
Expand All @@ -99,13 +101,13 @@ def __init_components(self):
pyside.set_class(button, ['danger'])

def calibrate(self):
if self.hit > 0:
if strings.strip_to_none(self.__hit_field.text()) is not None:
if self.unit != CustomPhase.Unit.MILLISECONDS:
calibration = self.calibrator.to_milliseconds(self.target - self.hit)
else:
calibration = self.target - self.hit
self.model.calibration.add(calibration)
self.model.hit.set(0)
self.model.hit.set(None)

def __update_index(self,
label: QLabel,
Expand All @@ -115,22 +117,17 @@ def __update_index(self,
index = event.new_value
label.setText(f'{index + 1}.')

def __on_value_changed(self):
def __on_value_changed(self, event: PropertyChangeEvent[int | float]):
self.changed.emit()

def __on_unit_changed(self, event: PropertyChangeEvent[CustomPhase.Unit] | None = None):
unit = self.unit
if event is not None:
unit = event.new_value

prefix = '0x' if unit == CustomPhase.Unit.HEX else ''
integer_base = 16 if unit == CustomPhase.Unit.HEX else 10
# ----- target_field -----
self.__target_field.setPrefix(prefix)
self.__target_field.setDisplayIntegerBase(integer_base)
# ----- hit_field -----
self.__hit_field.setPrefix(prefix)
self.__hit_field.setDisplayIntegerBase(integer_base)
radix = Radix.HEXADECIMAL if unit == CustomPhase.Unit.HEX else Radix.DECIMAL
self.__target_field.set_radix(radix)
self.__hit_field.set_radix(radix)

self.changed.emit()

Expand Down
4 changes: 3 additions & 1 deletion eon_timer/timers/custom/timer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Final
from typing import Final, override

from eon_timer.timers.calibrator import Calibrator
from eon_timer.timers.timer import Timer
Expand All @@ -12,6 +12,7 @@ class CustomTimer(Timer[CustomTimerModel]):
def __init__(self, calibrator: Calibrator):
self.calibrator: Final = calibrator

@override
def create(self, model: CustomTimerModel) -> list[float]:
phases = []
for phase in model.phases:
Expand All @@ -23,5 +24,6 @@ def create(self, model: CustomTimerModel) -> list[float]:
phases.append(value)
return phases

@override
def calibrate(self, model: CustomTimerModel):
pass
2 changes: 1 addition & 1 deletion eon_timer/timers/gen3/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Gen3Model(Settings):
pre_timer = IntProperty(5000)
target_frame = IntProperty(1000)
calibration = FloatProperty(0.0)
frame_hit = IntProperty(0, transient=True)
frame_hit = IntProperty(None, transient=True)

@property
@override
Expand Down
13 changes: 6 additions & 7 deletions eon_timer/timers/gen3/timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ def create(self, model: Gen3Model) -> list[float]:

@override
def calibrate(self, model: Gen3Model):
if model.frame_hit.get() > 0:
offset = self.frame_timer.calibrate(
model.target_frame.get(),
model.frame_hit.get()
)
model.calibration.add(offset)
model.frame_hit.set(0)
offset = self.frame_timer.calibrate(
model.target_frame.get(),
model.frame_hit.get()
)
model.calibration.add(offset)
model.frame_hit.set(None)
65 changes: 38 additions & 27 deletions eon_timer/timers/gen3/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@
from typing import Final, override

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QDoubleSpinBox, QGroupBox, QPushButton, QSizePolicy, QSpinBox
from PySide6.QtWidgets import QGroupBox, QPushButton, QSizePolicy

from eon_timer.app_state import AppState
from eon_timer.timers.timer_widget import TimerWidget
from eon_timer.util import const, pyside
from eon_timer.util import const, pyside, strings
from eon_timer.util.injector import component
from eon_timer.util.loggers import log_method_calls
from eon_timer.util.properties import bindings
from eon_timer.util.properties.property_change import PropertyChangeEvent
from eon_timer.util.pyside import EnumComboBox
from eon_timer.util.pyside.form import FormLayout, FormWidget
from eon_timer.util.pyside.name_service import NameService
from eon_timer.util.pyside.numeric_input_field import BlankBehavior, FloatInputField, IntInputField
from .model import Gen3Mode, Gen3Model
from .timer import Gen3Timer

Expand All @@ -35,6 +36,7 @@ def __init__(self,
timer: Gen3Timer,
name_service: NameService) -> None:
self.state: Final = state
self.frame_hit_field: Final = IntInputField()
FormWidget.__init__(self, name_service)
TimerWidget.__init__(self, model, timer)

Expand All @@ -47,9 +49,9 @@ def _init_components(self) -> None:
self._layout.set_content_margins(10, 10, 10, 10)
# ----- mode -----
field = EnumComboBox(Gen3Mode)
self.add_field(self.Field.MODE, field, name='gen3Mode')
bindings.bind_enum_combobox(field, self.model.mode)
self.model.mode.on_change(self.__on_mode_changed)
self.add_field(self.Field.MODE, field, name='gen3Mode')
# ----- form_group -----
form_group = QGroupBox()
self.name_service.set_name(form_group, 'gen3FormGroup')
Expand All @@ -59,20 +61,20 @@ def _init_components(self) -> None:
pyside.set_class(form_group, ['themeable-panel', 'themeable-border'])
form_group.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# ----- pre_timer -----
field = QSpinBox()
field = IntInputField()
field.set_range(0, const.INT_MAX)
bindings.bind(field.value, self.model.pre_timer)
self.add_field(self.Field.PRE_TIMER, field, layout=form_layout, name='gen3PreTimer')
field.setRange(0, const.INT_MAX)
bindings.bind_spinbox(field, self.model.pre_timer)
# ----- target_frame -----
field = QSpinBox()
field = IntInputField()
field.set_range(0, const.INT_MAX)
bindings.bind(field.value, self.model.target_frame)
self.add_field(self.Field.TARGET_FRAME, field, layout=form_layout, name='gen3TargetFrame')
field.setRange(0, const.INT_MAX)
bindings.bind_spinbox(field, self.model.target_frame)
# ----- calibration -----
field = QDoubleSpinBox()
field = FloatInputField()
field.set_range(const.INT_MIN, const.INT_MAX)
bindings.bind(field.value, self.model.calibration)
self.add_field(self.Field.CALIBRATION, field, layout=form_layout, name='gen3Calibration')
field.setRange(const.INT_MIN, const.INT_MAX)
bindings.bind_float_spinbox(field, self.model.calibration)
# ----- set_target_frame_btn -----
field = QPushButton(self.Field.SET_TARGET_FRAME.value)
self.name_service.set_name(field, 'gen3SetTargetFrameButton')
Expand All @@ -82,10 +84,11 @@ def _init_components(self) -> None:
field.pressed.connect(self.__on_set_target_frame)
field_set.enabled = False
# ----- frame_hit -----
field = QSpinBox()
self.add_field(self.Field.FRAME_HIT, field, name='gen3FrameHit')
field.setRange(0, const.INT_MAX)
bindings.bind_spinbox(field, self.model.frame_hit)
self.frame_hit_field.set_range(0, const.INT_MAX)
self.frame_hit_field.blank_behavior = BlankBehavior.BLANK
bindings.bind(self.frame_hit_field.value, self.model.frame_hit)
self.add_field(self.Field.FRAME_HIT, self.frame_hit_field, name='gen3FrameHit')
self.frame_hit_field.setText('')
# update field visibility
self.__on_mode_changed()

Expand All @@ -102,26 +105,34 @@ def field_changed(field: Gen3TimerWidget.Field,
self.model.target_frame.on_change(functools.partial(field_changed, self.Field.TARGET_FRAME))
self.model.calibration.on_change(functools.partial(field_changed, self.Field.CALIBRATION))

@override
def calibrate(self):
frame_hit = strings.strip_to_none(self.frame_hit_field.text())
if frame_hit is not None:
super().calibrate()
self.frame_hit_field.setText('')

@override
def setDisabled(self, disabled: bool):
self.set_disabled(self.Field.MODE, disabled)
self.set_disabled(self.Field.PRE_TIMER, disabled)
self.set_disabled(self.Field.CALIBRATION, disabled)
self.set_disabled(self.Field.FRAME_HIT, disabled)

mode = self.model.mode.get()
self.set_disabled(self.Field.TARGET_FRAME, self.state.running and mode == Gen3Mode.STANDARD)
self.set_enabled(self.Field.SET_TARGET_FRAME, self.state.running and mode == Gen3Mode.VARIABLE_TARGET)

def __on_mode_changed(self, event: PropertyChangeEvent[Gen3Mode] | None = None) -> None:
mode = event.new_value if event else self.model.mode.get()
self.set_visible(self.Field.SET_TARGET_FRAME,
mode == Gen3Mode.VARIABLE_TARGET)

def __on_set_target_frame(self) -> None:
phase = self.frame_timer.create_phase(
phase = self.timer.frame_timer.create_phase(
self.model.target_frame.get(),
self.model.calibration.get()
)
self.state.set_phase(1, phase)
self.set_disabled(self.Field.TARGET_FRAME, True)
self.set_disabled(self.Field.SET_TARGET_FRAME, True)

def setDisabled(self, disabled: bool):
self.set_disabled(self.Field.MODE, disabled)
self.set_disabled(self.Field.PRE_TIMER, disabled)
self.set_disabled(self.Field.CALIBRATION, disabled)
self.set_disabled(self.Field.FRAME_HIT, disabled)

mode = self.model.mode.get()
self.set_disabled(self.Field.TARGET_FRAME, self.state.running and mode == Gen3Mode.STANDARD)
self.set_enabled(self.Field.SET_TARGET_FRAME, self.state.running and mode == Gen3Mode.VARIABLE_TARGET)
2 changes: 1 addition & 1 deletion eon_timer/timers/gen4/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Gen4Model(Settings):
target_second = IntProperty(50)
calibrated_delay = IntProperty(500)
calibrated_second = IntProperty(14)
delay_hit = IntProperty(0, transient=True)
delay_hit = IntProperty(None, transient=True)

@property
@override
Expand Down
Loading

0 comments on commit 2016a31

Please sign in to comment.