touch-eventpyscript

pyscript @when decorator - js conversion


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?


Solution

  • 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:

    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()