Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a TimeField class to Pyface #507

Merged
merged 13 commits into from
Sep 28, 2021
13 changes: 10 additions & 3 deletions docs/source/fields.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,19 @@ ComboField
==========

The :py:class:`~pyface.fields.i_combo_field.IComboField` interface has an arbitrary
:py:attr:`~pyface.fields.i_spin_field.IComboField.value` that must come from a list
of valid :py:attr:`~pyface.fields.i_spin_field.IComboField.values`. For non-text
values, a :py:attr:`~pyface.fields.i_spin_field.IComboField.formatter` function
:py:attr:`~pyface.fields.i_combo_field.IComboField.value` that must come from a list
of valid :py:attr:`~pyface.fields.i_combo_field.IComboField.values`. For non-text
values, a :py:attr:`~pyface.fields.i_combo_field.IComboField.formatter` function
should be provided - this defaults to either :py:func:`str` (Python 3+) or
:py:func:`unicode` (Python 2).

TimeField
==========

The :py:class:`~pyface.fields.i_time_field.ITimeField` interface has a
:py:class:`datetime.time` :py:attr:`~pyface.fields.i_time_field.ITimeField.value`.
This value defaults to the current time.

ToggleField and Subclasses
==========================

Expand Down
4 changes: 4 additions & 0 deletions pyface/fields/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- :class:`~.RadioButtonField`
- :class:`~.SpinField`
- :class:`~.TextField`
- :class:`~.TimeField`
- :class:`~.ToggleButtonField`

Interfaces
Expand All @@ -25,6 +26,7 @@
- :class:`~.IField`
- :class:`~.ISpinField`
- :class:`~.ITextField`
- :class:`~.ITimeField`
- :class:`~.IToggleField`

"""
Expand All @@ -33,11 +35,13 @@
from .i_field import IField
from .i_spin_field import ISpinField
from .i_text_field import ITextField
from .i_time_field import ITimeField
from .i_toggle_field import IToggleField

from .combo_field import ComboField
from .spin_field import SpinField
from .text_field import TextField
from .time_field import TimeField
from .toggle_field import (
CheckBoxField, RadioButtonField, ToggleButtonField,
)
59 changes: 59 additions & 0 deletions pyface/fields/i_time_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" The time field interface. """

from datetime import time

from traits.api import HasTraits, Time

from pyface.fields.i_field import IField


class ITimeField(IField):
""" The time field interface.

This is for a field that edits a datetime.time value.
"""

#: The current value of the time field
value = Time()


class MTimeField(HasTraits):
""" Mixin class for TimeField implementations """

#: The current value of the time field
value = Time()

# ------------------------------------------------------------------------
# Private interface
# ------------------------------------------------------------------------

def _initialize_control(self):
super(MTimeField, self)._initialize_control()
self._set_control_value(self.value)

def _add_event_listeners(self):
""" Set up toolkit-specific bindings for events """
super(MTimeField, self)._add_event_listeners()
if self.control is not None:
self._observe_control_value()

def _remove_event_listeners(self):
""" Remove toolkit-specific bindings for events """
if self.control is not None:
self._observe_control_value(remove=True)
super(MTimeField, self)._remove_event_listeners()

# Trait defaults --------------------------------------------------------

def _value_default(self):
return time.now()
45 changes: 45 additions & 0 deletions pyface/fields/tests/test_time_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!


from datetime import time
import unittest

from ..time_field import TimeField
from .field_mixin import FieldMixin


class TestTimeField(FieldMixin, unittest.TestCase):

def _create_widget(self):
return TimeField(
parent=self.parent.control,
value=time(12, 0, 0),
tooltip="Dummy",
)

# Tests ------------------------------------------------------------------

def test_time_field(self):
self._create_widget_control()

self.widget.value = time(13, 1, 1)
self.gui.process_events()

self.assertEqual(self.widget._get_control_value(), time(13, 1, 1))

def test_time_field_set(self):
self._create_widget_control()

with self.assertTraitChanges(self.widget, "value", count=1):
self.widget._set_control_value(time(13, 1, 1))
self.gui.process_events()

self.assertEqual(self.widget.value, time(13, 1, 1))
16 changes: 16 additions & 0 deletions pyface/fields/time_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" The time field widget. """

# Import the toolkit specific version.
from pyface.toolkit import toolkit_object

TimeField = toolkit_object("fields.time_field:TimeField")
2 changes: 2 additions & 0 deletions pyface/qt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,5 @@
# useful constants
is_qt4 = qt_api in {"pyqt"}
is_qt5 = qt_api in {"pyqt5", "pyside2"}
is_pyqt = qt_api in {"pyqt", "pyqt5"}
is_pyside = qt_api in {"pyside", "pyside2"}
54 changes: 54 additions & 0 deletions pyface/ui/qt4/fields/time_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" The Qt-specific implementation of the time field class """


from traits.api import provides

from pyface.qt.QtGui import QTimeEdit

from pyface.fields.i_time_field import ITimeField, MTimeField
from pyface.ui.qt4.util.datetime import pytime_to_qtime, qtime_to_pytime
from .field import Field


@provides(ITimeField)
class TimeField(MTimeField, Field):
""" The Qt-specific implementation of the time field class """

# ------------------------------------------------------------------------
# IWidget interface
# ------------------------------------------------------------------------

def _create_control(self, parent):
""" Create the toolkit-specific control that represents the widget. """

control = QTimeEdit(parent)
return control

# ------------------------------------------------------------------------
# Private interface
# ------------------------------------------------------------------------

def _get_control_value(self):
""" Toolkit specific method to get the control's value. """
return qtime_to_pytime(self.control.time())

def _set_control_value(self, value):
""" Toolkit specific method to set the control's value. """
self.control.setTime(pytime_to_qtime(value))

def _observe_control_value(self, remove=False):
""" Toolkit specific method to change the control value observer. """
if remove:
self.control.timeChanged.disconnect(self._update_value)
else:
self.control.timeChanged.connect(self._update_value)
59 changes: 59 additions & 0 deletions pyface/ui/qt4/util/datetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" Utility functions for handling Qt dates and times. """

from pyface.qt import is_pyqt
from pyface.qt.QtCore import QTime


def qtime_to_pytime(qtime):
""" Convert a QTime to a Python datetime.time

This is needed to paper over the differences between PyQt and PySide.

Parameters
----------
qtime : QTime
The Qt QTime to convert

Returns
-------
pytime : datetime.time
The corresponding datetime.time.
"""
if is_pyqt:
return qtime.toPyTime()
else:
return qtime.toPython()


def pytime_to_qtime(pytime):
""" Convert a Python datetime.time to a QTime

This is a convenience function to construct a qtime from a Python time.
This loses any :attr:`fold` information in the Python time.

Parameters
----------
pytime : datetime.time
The datetime.time to convert

Returns
-------
qtime : QTime
The corresponding Qt QTime.
"""
return QTime(
pytime.hour,
pytime.minute,
pytime.second,
pytime.microsecond // 1000
)
41 changes: 41 additions & 0 deletions pyface/ui/qt4/util/tests/test_datetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" Utility functions for handling Qt dates and times. """

import datetime
import unittest

from pyface.qt.QtCore import QTime

from ..datetime import pytime_to_qtime, qtime_to_pytime


class TestTimeConversion(unittest.TestCase):

def test_pytime_to_qtime(self):
pytime = datetime.time(9, 8, 7, 123456)

qtime = pytime_to_qtime(pytime)

self.assertEqual(qtime.hour(), 9)
self.assertEqual(qtime.minute(), 8)
self.assertEqual(qtime.second(), 7)
self.assertEqual(qtime.msec(), 123)

def test_qtime_to_pytime(self):
qtime = QTime(9, 8, 7, 123)

pytime = qtime_to_pytime(qtime)

self.assertEqual(pytime.hour, 9)
self.assertEqual(pytime.minute, 8)
self.assertEqual(pytime.second, 7)
self.assertEqual(pytime.microsecond, 123000)
Loading