Skip to content

Commit

Permalink
Merge pull request #37 from Carifio24/datetime
Browse files Browse the repository at this point in the history
Add Qt connection handler for date/time widgets
  • Loading branch information
astrofrog authored May 13, 2024
2 parents 9747254 + c6c7caa commit e2f717d
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 4 deletions.
8 changes: 7 additions & 1 deletion echo/qt/autoconnect.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
connect_text,
connect_button,
connect_combo_selection,
connect_list_selection)
connect_list_selection,
connect_datetime)

__all__ = ['autoconnect_callbacks_to_qt']

Expand All @@ -20,6 +21,7 @@
HANDLERS['button'] = connect_button
HANDLERS['combosel'] = connect_combo_selection
HANDLERS['listsel'] = connect_list_selection
HANDLERS['datetime'] = connect_datetime


def autoconnect_callbacks_to_qt(instance, widget, connect_kwargs={}):
Expand Down Expand Up @@ -64,6 +66,10 @@ def autoconnect_callbacks_to_qt(instance, widget, connect_kwargs={}):
* ``combotext``: the callback property is linked to a QComboBox based on
the label of the entries in the combo box.
* ``datetime``: the callback property is linked to a QDateTimeEdit.
Note that this connection will also work for the more specific QDateEdit
and QTimeEdit widgets, as they are subclasses of QDateTimeEdit.
Applications can also define additional mappings between type and
auto-linking. To do this, simply add a new entry to the ``HANDLERS`` object::
Expand Down
53 changes: 51 additions & 2 deletions echo/qt/connect.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# The functions in this module are used to connect callback properties to Qt
# widgets.

from datetime import datetime
import math

from qtpy import QtGui, QtWidgets
from qtpy.QtCore import Qt
from qtpy.QtCore import Qt, QDateTime

import numpy as np

Expand All @@ -13,7 +14,7 @@

__all__ = ['connect_checkable_button', 'connect_text', 'connect_combo_data',
'connect_combo_text', 'connect_float_text', 'connect_value',
'connect_combo_selection', 'connect_list_selection',
'connect_combo_selection', 'connect_list_selection', 'connect_datetime',
'BaseConnection']


Expand Down Expand Up @@ -580,3 +581,51 @@ def connect(self):
def disconnect(self):
remove_callback(self._instance, self._prop, self.update_widget)
self._widget.itemSelectionChanged.disconnect(self.update_prop)


class connect_datetime(BaseConnection):
"""
Connect a CallbackProperty to a QDateTimeEdit.
Since QDateEdit and QTimeEdit are subclasses of QDateTimeEdit, this connection
will work for those more specific widgets as well.
"""

def __init__(self, instance, prop, widget):
super(connect_datetime, self).__init__(instance, prop, widget)
self.connect()

def update_prop(self):
qdatetime = self._widget.dateTime().toUTC()
value = np.datetime64(qdatetime.toPython())
setattr(self._instance, self._prop, value)

def update_widget(self, value):
if value is None:
value = np.datetime64('now')
dt = value.item()

# datetime64::item can return a date
# If this happens, we use midnight as our time
# (datetime is a subclass of date so we need to check this way)
if not isinstance(dt, datetime):
date = dt
time = datetime.min.time()
else:
date = dt.date()
time = dt.time()

qdatetime = QDateTime(date, time, Qt.TimeSpec.UTC).toTimeSpec(self._widget.timeSpec())
self._widget.setDateTime(qdatetime)

def connect(self):
add_callback(self._instance, self._prop, self.update_widget)
self._widget.dateTimeChanged.connect(self.update_prop)
self._widget.dateChanged.connect(self.update_prop)
self._widget.timeChanged.connect(self.update_prop)
self.update_widget(getattr(self._instance, self._prop))

def disconnect(self):
remove_callback(self._instance, self._prop, self.update_widget)
self._widget.dateTimeChanged.disconnect(self.update_prop)
self._widget.dateChanged.disconnect(self.update_prop)
self._widget.timeChanged.disconnect(self.update_prop)
19 changes: 19 additions & 0 deletions echo/qt/tests/test_autoconnect.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from datetime import datetime

from numpy import datetime64
from qtpy import QtWidgets, QtGui
from qtpy.QtCore import QDateTime, Qt

from echo.qt.autoconnect import autoconnect_callbacks_to_qt
from echo import CallbackProperty
Expand Down Expand Up @@ -47,13 +51,18 @@ def __init__(self, parent=None):
self.bool_log.setCheckable(True)
self.layout.addWidget(self.bool_log)

self.datetime_dob = QtWidgets.QDateTimeEdit(objectName="datetime_dob")
self.datetime_dob.setTimeSpec(Qt.TimeSpec.UTC)
self.layout.addWidget(self.datetime_dob)

class Person(object):
planet = CallbackProperty()
dataset = CallbackProperty()
name = CallbackProperty()
age = CallbackProperty()
height = CallbackProperty()
log = CallbackProperty()
dob = CallbackProperty()

widget = CustomWidget()

Expand Down Expand Up @@ -86,6 +95,11 @@ class Person(object):
widget.bool_log.setChecked(True)
assert person.log

dob = datetime(2000, 1, 1, 11, 52, 6)
qdob = QDateTime(dob.date(), dob.time(), Qt.TimeSpec.UTC)
widget.datetime_dob.setDateTime(qdob)
assert person.dob.item() == dob

# Check that modifying the callback properties updates the Qt widget

person.planet = 'mars'
Expand All @@ -106,6 +120,11 @@ class Person(object):
person.log = False
assert not widget.bool_log.isChecked()

dob = datetime(2010, 2, 3, 16, 22, 11)
person.dob = datetime64(dob)
qdatetime = widget.datetime_dob.dateTime().toUTC()
assert qdatetime.toPython() == person.dob.item()


def test_autoconnect_with_empty_qt_item():

Expand Down
29 changes: 28 additions & 1 deletion echo/qt/tests/test_connect.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from datetime import datetime

import pytest
from numpy import datetime64
from unittest.mock import MagicMock

from qtpy import QtWidgets
from qtpy.QtCore import QDateTime, Qt

from echo import CallbackProperty
from echo.qt.connect import (connect_checkable_button, connect_text,
from echo.qt.connect import (connect_checkable_button, connect_datetime, connect_text,
connect_combo_data, connect_combo_text,
connect_float_text, connect_value, connect_button,
UserDataWrapper)
Expand Down Expand Up @@ -211,3 +215,26 @@ class Example(object):
assert e.a.call_count == 0
button.clicked.emit()
assert e.a.call_count == 1


def test_connect_datetime():

class Example(object):
t = CallbackProperty()

e = Example()

widget = QtWidgets.QDateTimeEdit()
widget.setTimeSpec(Qt.TimeSpec.UTC)

conn = connect_datetime(e, 't', widget) # noqa

dt = datetime(2010, 5, 7, 13, 15, 3)
qdt = QDateTime(dt.date(), dt.time(), Qt.TimeSpec.UTC)
widget.setDateTime(qdt)
widget.dateTimeChanged.emit(qdt)
assert dt == e.t.item()

dt = datetime(2020, 3, 4, 7, 48, 16)
e.t = datetime64(dt)
assert widget.dateTime().toUTC().toPython() == dt

0 comments on commit e2f717d

Please sign in to comment.