英文:
pyscript @when decorator - js conversion
问题
在 evt.target.dragging = {dx: evt.target.pos.x - evt.clientX}
这部分显然是 JavaScript 代码,无法在 Python 中直接使用。您想要实现类似的功能,可以尝试以下 Python 代码:
@when("pointerdown", selector=".scrubbable-num")
def pointer_capture(evt):
print("ptrdown", evt)
if not (evt.button != 0 or evt.ctrlKey):
evt.target.dragging = evt.target.pos.x - evt.clientX
evt.target.setPointerCapture(evt.pointerId)
@when("pointermove", selector=".scrubbable-num")
def getpos(evt):
if evt.target.dragging is not None:
x = evt.clientX
evt.target.pos.x = max(min(x + evt.target.dragging, 1000), 0)
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()
这里的 Python 代码对应了您提供的 JavaScript 代码中的相同功能,但是做了必要的调整以适应 Python 语法和事件处理方式。希望这对您有所帮助。
英文:
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?
答案1
得分: 1
以下是您要求的代码部分的中文翻译:
from pyodide.ffi import to_js
from js import Object
evt.target.dragging = to_js(
{'dx': 1234},
dict_converter=Object.fromEntries
)
# 创建一个JS对象:{dx: 1234}
请注意,Python字典中的键是字符串,会被转换为JavaScript对象的属性。
编辑:这是您代码的可运行版本,至少是我理解意图的版本。只需要进行了一些其他小的更改,这些更改可能基于未在您的示例中显示的周围代码而不必要,因此可以根据需要接受或忽略它们:
clamp
是一个JavaScript函数,在此处重新在Python中实现pos
不是HTML元素的默认属性,并且未初始化,因此我将其用于在使用它的两行中进行了压缩。- 滚动,我猜想是相对于点击并拖动的起始位置
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)
# 以初始值为基础进行更改,如果没有设置初始值,则为0
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()
希望这能满足您的需求。如果有任何其他问题,请随时提出。
英文:
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.- The scrolling, I guessed, wanted to be relative to whatever it was at the start of the click-and-drag
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()
答案2
得分: 0
Jeff的答案展示了如何将JavaScript信息添加到JavaScript变量中,因此是最佳答案。
下面是一个可行的解决方案,它提供了更稳定的数值结果,并包含了(正如Jeff所添加的)设置初始值的部分。
这段代码不需要to_js()
的额外添加,因为事实证明还有另一种方法可以实现 - 将属性添加到事件类中。
(它还有更好命名的函数。)
我仍然面临的一个问题是,拖动事件不会传播到输入事件,因此当鼠标移动时,我无法获得连续的输入事件序列(与在数值输入/编辑值时获得的情况不同)。因此,也许我需要在input_drag()
中生成一个Event("input")
。
- 我曾认为
preventDefault
正在阻止冒泡,但是注释掉这些部分没有效果。(它们用于处理像Firefox这样的行为略有不同的浏览器。)
这是构建UI的pyscript代码中的输入定义。
(将其添加到内部HTML以创建输入字段):
'<input id="%s-scrubbable" class="scrub-num" pattern="[0-9]+" py-input="update_UI()">' %(id)
CSS的有用部分:
.scrub-num {
width: 3em;
text-align: center;
cursor: ew-resize; }
以及pyscript代码:
def clamp(value, minimum, maximum):
return max(minimum, min(value, maximum))
@when("pointerdown", selector=".scrubbable-num")
def input_initiate_drag(evt):
if not(evt.button != 0 ): # 允许手动输入数字来覆盖
# 相对于初始值进行更改,如果未设置初始值,则为0
evt.target.initialValue = int(evt.target.value) if evt.target.value else 0
# 记住我们的startX
evt.target.xoffset = evt.target.initialValue - evt.clientX
evt.target.setPointerCapture(evt.pointerId)
@when("pointermove", selector=".scrubbable-num")
def input_drag(evt):
if hasattr(evt.target, "xoffset") and evt.target.xoffset:
x = evt.clientX
evt.target.value = round(clamp(x + evt.target.xoffset, -270, 270))
# 我们需要触发一个input(也许)事件,以便在拖动时UI可以继续更新
@when("pointerup", selector=".scrubbable-num")
def input_stop_drag(evt):
evt.target.xoffset = None
# 如果浏览器接管
@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()
英文:
Jeff's answer shows how to add the javascript info to the js variable so is the best answer.
Below is a working solution which gives a more numerically stable result and incorporates (as Jeff added) the setting of an initial value.
This code does not need the to_js()
additions as it turns out there is another way to do it - add attributes to the event class.
(It also has better named functions.)
The one remaining problem I have is that dragging does not propagate up to the input event and so, as the mouse moves I do not get a continuous series of input events (as I do when numerically entering/editing a value). So perhaps I need to generate an Event("input")
in input_drag()
.
- I thought preventDefault was stopping the bubbling up but commenting these out has no effect. (They are for handling browsers like Firefox which behaves slightly differently.)
Here is the input definition from the pyscript code which constructs the UI.
(Adding this to the inner html to create the input field):
'<input id="%s-scrubbable" class="scrub-num" pattern="[0-9]+" py-input="update_UI()">' %(id)
The useful part of the css:
.scrub-num {
width: 3em;
text-align: center;
cursor: ew-resize; }
And the pyscript code:
def clamp(value, minimum, maximum):
return max(minimum, min(value, maximum))
@when("pointerdown", selector=".scrubbable-num")
def input_initiate_drag(evt):
if not(evt.button != 0 ): # allow manual numeric entry to override
# 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
# remember our startX
evt.target.xoffset = evt.target.initialValue - evt.clientX
evt.target.setPointerCapture(evt.pointerId)
@when("pointermove", selector=".scrubbable-num")
def input_drag(evt):
if hasattr(evt.target, "xoffset") and evt.target.xoffset:
x = evt.clientX
evt.target.value = round(clamp(x + evt.target.xoffset, -270, 270))
# we need to fire an input(maybe) event so the UI can continue to update while dragging
@when("pointerup", selector=".scrubbable-num")
def input_stop_drag(evt):
evt.target.xoffset = None
# if the browser takes over
@when("pointercancel", selector=".scrubbable-num")
def pointer_up(evt):
evt.target.onpointerup(evt)
# for some browsers
@when("touchstart",selector=".scrubbable-num")
@when("dragstart",selector=".scrubbable-num")
def prevent_default(evt):
evt.target.preventDefault()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论