-
-
Notifications
You must be signed in to change notification settings - Fork 508
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
331 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import datetime as dt\n", | ||
"import panel as pn\n", | ||
"\n", | ||
"pn.extension()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The ``TimePicker`` widget allows entering a time value as text or `datetime.time`. \n", | ||
"\n", | ||
"Discover more on using widgets to add interactivity to your applications in the [how-to guides on interactivity](../how_to/interactivity/index.md). Alternatively, learn [how to set up callbacks and (JS-)links between parameters](../../how_to/links/index.md) or [how to use them as part of declarative UIs with Param](../../how_to/param/index.html).\n", | ||
"\n", | ||
"#### Parameters:\n", | ||
"\n", | ||
"For details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n", | ||
"\n", | ||
"##### Core\n", | ||
"\n", | ||
"* **``value``** (str | datetime.time): The current value\n", | ||
"* **``start``** (str | datetime.time): Inclusive lower bound of the allowed time selection\n", | ||
"* **``end``** (str | datetime.time): Inclusive upper bound of the allowed time selection\n", | ||
"\n", | ||
" ```\n", | ||
" +---+------------------------------------+------------+\n", | ||
" | H | Hours (24 hours) | 00 to 23 |\n", | ||
" | h | Hours | 1 to 12 |\n", | ||
" | G | Hours, 2 digits with leading zeros | 1 to 12 |\n", | ||
" | i | Minutes | 00 to 59 |\n", | ||
" | S | Seconds, 2 digits | 00 to 59 |\n", | ||
" | s | Seconds | 0, 1 to 59 |\n", | ||
" | K | AM/PM | AM or PM |\n", | ||
" +---+------------------------------------+------------+\n", | ||
" ```\n", | ||
" See also https://flatpickr.js.org/formatting/#date-formatting-tokens.\n", | ||
"\n", | ||
"\n", | ||
"\n", | ||
"##### Display\n", | ||
"\n", | ||
"* **``disabled``** (boolean): Whether the widget is editable\n", | ||
"* **``name``** (str): The title of the widget\n", | ||
"* **``format``** (str): Formatting specification for the display of the picked date.\n", | ||
"* **``hour_increment``** (int): Defines the granularity of hour value increments in the UI. Default is 1.\n", | ||
"* **``minute_increment``** (int): Defines the granularity of minute value increments in the UI. Default is 1.\n", | ||
"* **``second_increment``** (int): Defines the granularity of second value increments in the UI. Default is 1.\n", | ||
"* **``seconds``** (bool): Allows to select seconds. By default, only hours and minutes are selectable, and AM/PM depending on the `clock` option. Default is False.\n", | ||
"* **``clock``** (bool): Whether to use 12 hour or 24 hour clock.\n", | ||
"___" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The `TimePicker` widget allows selecting a time of day." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"time_picker = pn.widgets.TimePicker(name='Time Picker', value=dt.datetime.now().time(), format='H:i K')\n", | ||
"time_picker" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Either `datetime.time` or `str` can be used as input and `TimePicker` can be bounded by a start and end time." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"time_picker = pn.widgets.TimePicker(name='Time Picker', value=\"08:28\", start='00:00', end='12:00')\n", | ||
"time_picker" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"language_info": { | ||
"name": "python", | ||
"pygments_lexer": "ipython3" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 4 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from bokeh.models import TimePicker as BkTimePicker | ||
|
||
|
||
class TimePicker(BkTimePicker): | ||
""" | ||
A custom Panel version of the Bokeh TimePicker model which fixes timezones. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import {TimePicker as BkTimePicker, TimePickerView as BkTimePickerView} from "@bokehjs/models/widgets/time_picker" | ||
import type * as p from "@bokehjs/core/properties" | ||
import type flatpickr from "flatpickr" | ||
|
||
export class TimePickerView extends BkTimePickerView { | ||
declare model: TimePicker | ||
|
||
private _offset_time(value: string | number): number { | ||
const baseDate = new Date(value) | ||
const timeZoneOffset = baseDate.getTimezoneOffset() * 60 * 1000 | ||
return baseDate.getTime() + timeZoneOffset | ||
} | ||
|
||
private _setDate(date: string | number): void { | ||
date = this._offset_time(date) | ||
this.picker.setDate(date) | ||
} | ||
|
||
protected override get flatpickr_options(): flatpickr.Options.Options { | ||
// on init | ||
const options = super.flatpickr_options | ||
if (options.defaultDate != null) { options.defaultDate = this._offset_time(options.defaultDate as string) } | ||
return options | ||
} | ||
|
||
override connect_signals(): void { | ||
super.connect_signals() | ||
|
||
const {value} = this.model.properties | ||
this.connect(value.change, () => { | ||
const {value} = this.model | ||
if (value != null && typeof value === "number") { | ||
// we need to handle it when programmatically changed thru Python, e.g. | ||
// time_picker.value = "4:08" or time_picker.value = "datetime.time(4, 8)" | ||
// else, when changed in the UI, e.g. by typing in the input field | ||
// no special handling is needed | ||
this._setDate(value) | ||
} | ||
}) | ||
} | ||
|
||
} | ||
|
||
export namespace TimePicker { | ||
export type Attrs = p.AttrsOf<Props> | ||
export type Props = BkTimePicker.Props & { | ||
} | ||
} | ||
|
||
export interface TimePicker extends TimePicker.Attrs { } | ||
|
||
export class TimePicker extends BkTimePicker { | ||
declare properties: TimePicker.Props | ||
|
||
constructor(attrs?: Partial<TimePicker.Attrs>) { | ||
super(attrs) | ||
} | ||
|
||
static override __module__ = "panel.models.time_picker" | ||
|
||
static { | ||
this.prototype.default_view = TimePickerView | ||
|
||
this.define<TimePicker.Props>(({ }) => ({ | ||
})) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import datetime | ||
|
||
import pytest | ||
|
||
from panel.tests.util import serve_component, wait_until | ||
from panel.widgets import TimePicker | ||
|
||
pytestmark = pytest.mark.ui | ||
|
||
|
||
def test_time_picker(page): | ||
|
||
time_picker = TimePicker(value="18:08", format="H:i") | ||
|
||
serve_component(page, time_picker) | ||
|
||
# test init corrected timezone | ||
locator = page.locator("#input") | ||
assert locator.get_attribute("value") == "18:08:00" | ||
|
||
# test UI change | ||
locator = page.locator("input.bk-input.form-control.input") | ||
locator.click() | ||
wait_until(lambda: page.locator("input.numInput.flatpickr-hour").is_visible()) | ||
locator = page.locator("input.numInput.flatpickr-hour") | ||
locator.press("ArrowDown") | ||
locator.press("Enter") | ||
wait_until(lambda: time_picker.value == datetime.time(17, 8)) | ||
|
||
# test str value change | ||
time_picker.value = "04:08" | ||
wait_until(lambda: time_picker.value == "04:08") | ||
locator = page.locator("#input") | ||
assert locator.get_attribute("value") == "04:08:00" | ||
|
||
# test datetime.time value change | ||
time_picker.value = datetime.time(18, 8) | ||
wait_until(lambda: time_picker.value == datetime.time(18, 8)) | ||
locator = page.locator("#input") | ||
assert locator.get_attribute("value") == "18:08:00" | ||
|
||
|
||
@pytest.mark.parametrize("timezone_id", [ | ||
"America/New_York", | ||
"Europe/Berlin", | ||
"UTC", | ||
]) | ||
def test_time_picker_timezone_different(page, timezone_id): | ||
context = page.context.browser.new_context( | ||
timezone_id=timezone_id, | ||
) | ||
page = context.new_page() | ||
|
||
time_picker = TimePicker(value="18:08", format="H:i") | ||
serve_component(page, time_picker) | ||
|
||
locator = page.locator("#input") | ||
assert locator.get_attribute("value") == "18:08:00" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.