Trying to add some advanced interaction to my input field by making mouse motion scrub the value. but having difficulty converting js to python within events.
any guidance ?
I have this:
@when("pointerup", selector=".scrubbable-num")
def nodrag(evt):
evt.target.dragging = None
@when("pointerdown", selector=".scrubbable-num")
def pointer_capture(evt):
print("ptrdown",evt)
if not(evt.button != 0 or evt.ctrlKey):
evt.target.dragging = {dx: evt.target.pos.x - evt.clientX}
evt.target.setPointerCapture(evt.pointerId)
@when("pointermove", selector=".scrubbable-num")
def getpos(evt):
if evt.target.dragging:
x = evt.clientX
evt.target.pos.x = clamp(x + evt.target.dragging.dx, 0, 1000)
evt.target.el.value = round(evt.target.pos.x)
@when("pointercancel", selector=".scrubbable-num")
def pointer_up(evt):
evt.target.onpointerup(evt)
@when("touchstart",selector=".scrubbable-num")
@when("dragstart",selector=".scrubbable-num")
def prevent_default(evt):
evt.target.preventDefault()
In evt.target.dragging = {dx: evt.target.pos.x - evt.clientX}
this is obviously js and fails.
How do I do something similar?
The simplest way to create a JavaScript object from within Python is to use Pyodide's to_js function, especially when passing Object.fromEntries
as the conversion method.
from pyodide.ffi import to_js
from js import Object
evt.target.dragging = to_js(
{'dx': 1234},
dict_converter = Object.fromEntries
)
# Creates a JS Object: {dx: 1234}
Note that the keys in the python dict are strings, which get converted to attributes of the JavaScript object.
Edit: here's a working version of your code, at least from what I understand the intent to be. Only a couple of other minor changes were needed, and these may be unnecessary based on any surrounding code not shown in your example, so feel free to take or leave them:
clamp
is a JavaScript function, re-implemented here in Pythonpos
is not a default attribute of an HTML element and wasn't initialized, so I've condensed the two lines where it was used into one.from pyscript import when
from pyodide.ffi import to_js
from js import Object, console
def clamp(value, minimum, maximum):
return max(minimum, min(value, maximum))
@when("pointerup", selector=".scrubbable-num")
def nodrag(evt):
evt.target.dragging = None
@when("pointerdown", selector=".scrubbable-num")
def pointer_capture(evt):
print("ptrdown",evt)
targetPosition = evt.target.getBoundingClientRect()
if not(evt.button != 0 or evt.ctrlKey):
evt.target.dragging = to_js({'dx': targetPosition.x - evt.clientX}, dict_converter=Object.fromEntries)
# make changes relative to initial value, or 0 if none is set
evt.target.initialValue = int(evt.target.value) if evt.target.value else 0
evt.target.setPointerCapture(evt.pointerId)
@when("pointermove", selector=".scrubbable-num")
def getpos(evt):
if hasattr(evt.target, "dragging"):
x = evt.clientX
evt.target.value = evt.target.initialValue + round(clamp(x + evt.target.dragging.dx, -1000, 1000))
@when("pointercancel", selector=".scrubbable-num")
def pointer_up(evt):
evt.target.onpointerup(evt)
@when("touchstart",selector=".scrubbable-num")
@when("dragstart",selector=".scrubbable-num")
def prevent_default(evt):
evt.target.preventDefault()