Skip to content

Commit

Permalink
Show interval value on click (#7064)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahuang11 authored Aug 14, 2024
1 parent 1ce03a2 commit a3959ec
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 2 deletions.
8 changes: 8 additions & 0 deletions panel/dist/css/player.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
.faster {
font-size: 11px;
}

.slower {
font-size: 11px;
}

.pn-player-value {
font-weight: bold;
}
29 changes: 27 additions & 2 deletions panel/models/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ export class PlayerView extends WidgetView {
protected _toogle_pause: CallableFunction
protected _toggle_play: CallableFunction
protected _changing: boolean = false
protected slowerButton: HTMLButtonElement
protected fasterButton: HTMLButtonElement

override connect_signals(): void {
super.connect_signals()
Expand Down Expand Up @@ -160,61 +162,72 @@ export class PlayerView extends WidgetView {
this.buttonEl = button_div
button_div.style.cssText = "margin: 0 auto; display: flex; padding: 5px; align-items: stretch; width: 100%;"

const button_style_small = "text-align: center; min-width: 20px; flex-grow: 1; margin: 2px"
const button_style = "text-align: center; min-width: 40px; flex-grow: 2; margin: 2px"
const button_style_small = "text-align: center; min-width: 50px; flex-grow: 1; margin: 2px"
const button_style = "text-align: center; min-width: 50px; flex-grow: 2; margin: 2px"

const slower = document.createElement("button")
slower.classList.add("slower")
slower.style.cssText = button_style_small
slower.innerHTML = SVG_STRINGS.slower
slower.onclick = () => this.slower()
this.slowerButton = slower
button_div.appendChild(slower)

const first = document.createElement("button")
first.classList.add("first")
first.style.cssText = button_style
first.innerHTML = SVG_STRINGS.first
first.onclick = () => this.first_frame()
button_div.appendChild(first)

const previous = document.createElement("button")
previous.classList.add("previous")
previous.style.cssText = button_style
previous.innerHTML = SVG_STRINGS.previous
previous.onclick = () => this.previous_frame()
button_div.appendChild(previous)

const reverse = document.createElement("button")
reverse.classList.add("reverse")
reverse.style.cssText = button_style
reverse.innerHTML = SVG_STRINGS.reverse
reverse.onclick = () => this.reverse_animation()
button_div.appendChild(reverse)

const pause = document.createElement("button")
pause.classList.add("pause")
pause.style.cssText = button_style
pause.innerHTML = SVG_STRINGS.pause
pause.onclick = () => this.pause_animation()
button_div.appendChild(pause)

const play = document.createElement("button")
play.classList.add("play")
play.style.cssText = button_style
play.innerHTML = SVG_STRINGS.play
play.onclick = () => this.play_animation()
button_div.appendChild(play)

const next = document.createElement("button")
next.classList.add("next")
next.style.cssText = button_style
next.innerHTML = SVG_STRINGS.next
next.onclick = () => this.next_frame()
button_div.appendChild(next)

const last = document.createElement("button")
last.classList.add("last")
last.style.cssText = button_style
last.innerHTML = SVG_STRINGS.last
last.onclick = () => this.last_frame()
button_div.appendChild(last)

const faster = document.createElement("button")
faster.classList.add("faster")
faster.style.cssText = button_style_small
faster.innerHTML = SVG_STRINGS.faster
faster.onclick = () => this.faster()
this.fasterButton = faster
button_div.appendChild(faster)

// toggle
Expand Down Expand Up @@ -384,8 +397,17 @@ export class PlayerView extends WidgetView {
this.set_frame(this.model.end)
}

updateSpeedButton(button: HTMLButtonElement, interval: number, originalSVG: string): void {
const fps = 1000 / interval
button.innerHTML = `${fps.toFixed(1)}<br>fps`
setTimeout(() => {
button.innerHTML = originalSVG
}, this.model.preview_duration) // Show for 1.5 seconds
}

slower(): void {
this.model.interval = Math.round(this.model.interval / 0.7)
this.updateSpeedButton(this.slowerButton, this.model.interval, SVG_STRINGS.slower)
if (this.model.direction > 0) {
this.play_animation()
} else if (this.model.direction < 0) {
Expand All @@ -395,6 +417,7 @@ export class PlayerView extends WidgetView {

faster(): void {
this.model.interval = Math.round(this.model.interval * 0.7)
this.updateSpeedButton(this.fasterButton, this.model.interval, SVG_STRINGS.faster)
if (this.model.direction > 0) {
this.play_animation()
} else if (this.model.direction < 0) {
Expand Down Expand Up @@ -497,6 +520,7 @@ export namespace Player {
value: p.Property<any>
value_align: p.Property<string>
value_throttled: p.Property<any>
preview_duration: p.Property<number>
show_loop_controls: p.Property<boolean>
show_value: p.Property<boolean>
}
Expand Down Expand Up @@ -528,6 +552,7 @@ export class Player extends Widget {
value: [Int, 0],
value_align: [Str, "start"],
value_throttled: [Int, 0],
preview_duration: [Int, 1500],
show_loop_controls: [Bool, true],
show_value: [Bool, true],
}))
Expand Down
4 changes: 4 additions & 0 deletions panel/models/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class Player(Widget):
show_loop_controls = Bool(True, help="""Whether the loop controls
radio buttons are shown""")

preview_duration = Int(1500, help="""
Duration (in milliseconds) for showing the current FPS when clicking
the slower/faster buttons, before reverting to the icon.""")

show_value = Bool(True, help="""
Whether to show the widget value""")

Expand Down
27 changes: 27 additions & 0 deletions panel/tests/ui/widgets/test_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,40 @@
pytestmark = pytest.mark.ui


def test_player_faster_click_shows_ms(page):
player = Player()
serve_component(page, player)

faster_element = page.locator(".faster")
faster_element.click()

wait_until(lambda: player.interval == 350)
assert faster_element.inner_text() == "2.9\nfps"

wait_until(lambda: faster_element.inner_text() == "")


def test_player_slower_click_shows_ms(page):
player = Player()
serve_component(page, player)

slower_element = page.locator(".slower")
slower_element.click()

wait_until(lambda: player.interval == 714)
assert slower_element.inner_text() == "1.4\nfps"

wait_until(lambda: slower_element.inner_text() == "")


def test_init(page):
player = Player()
serve_component(page, player)

assert not page.is_visible('pn-player-value')
assert page.query_selector('.pn-player-value') is None


def test_show_value(page):
player = Player(show_value=True)
serve_component(page, player)
Expand Down
4 changes: 4 additions & 0 deletions panel/widgets/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class PlayerBase(Widget):
default='once', objects=['once', 'loop', 'reflect'], doc="""
Policy used when player hits last frame""")

preview_duration = param.Integer(default=1500, bounds=(0, None), doc="""
Duration (in milliseconds) for showing the current FPS when clicking
the slower/faster buttons, before reverting to the icon.""")

show_loop_controls = param.Boolean(default=True, doc="""
Whether the loop controls radio buttons are shown""")

Expand Down

0 comments on commit a3959ec

Please sign in to comment.