Andereoo / TkinterWeb

Python bindings for Tkhtml.
MIT License
146 stars 16 forks source link

Documentation request: node() and text() functions #64

Closed rodneyboyd closed 1 year ago

rodneyboyd commented 1 year ago

The Tkinterwebnode() andtext()functions sound intriguing but it's not clear what they do. Could we have more info, like what args you can pass? This is also an indirect way of asking whether there is a way to retrieve a node if you know, for example, its id attribute, and whether you can modify the text of a node if you have its handle? Thanks.

Andereoo commented 1 year ago

Hi!

From the Tkhtml docs (http://tkhtml.tcl.tk/tkhtml.html):

pathName node ? ?-index? x y?

This command is used to retrieve one or more document node handles from the current document. If the x and y parameters are omitted, then the handle returned is the root-node of the document, or an empty string if the document has no root-node (i.e. an empty document).

If the x and y arguments are present, then a list of node handles is returned. The list contains one handle for each node that generates content currently located at viewport coordinates (x, y). Usually this is only a single node, but floating boxes and other overlapped content can cause this command to return more than one node. If no content is located at the specified coordinates or the widget window is not mapped, then an empty string is returned.

If the -index option is specified along with the x and y coordinates, then instead of a list of node handles, a list of two elements is returned. The first element of the list is the node-handle associated with the generated text closest to the specified (x, y) coordinates. The second list value is a byte (not character) offset into the text obtainable by [nodeHandle text] for the character closest to coordinates (x, y). The index may be used with the [pathName tag] commands.

The document node can be queried and manipulated using the interface described in the "NODE COMMAND" section.

pathName text bbox node1 index1 node2 index2 pathName text index offset ?offset...? pathName text offset node index pathName text text

The [pathName text] commands allow an application to query and interact with the text of the displayed document. This can be used, for example, to search for a string within an Html document, or to copy a region of text to the system clipboard.

The [pathName text text] command returns a string containing the raw, unformatted text of the displayed document. Each block box is seperated from the next by a newline character. Each block of whitespace is collapsed to a single space, except within blocks with the CSS 'white-space' property set to "pre".

The [pathName text index] command is used to transform from a character offset in the string returned by [pathName text text] to a node/index pair that can be used with the [pathName tag] commands. The return value is a list of two elements, the node-handle followed by the index.

Command [pathName text offset] is the reverse of [pathName text index]. Given a node-handle and index of the type similar to that used by the [pathName tag] commands, this command returns the corresponding character offset in the string returned by [pathName text text] command.

You can also use the yourframe.html.search(selector) function to get the node-handles that match a given CSS selector (eg. a[href], #yourid, or .classname).

As for changing the text, I would use the method in https://github.com/Andereoo/TkinterWeb/issues/45#issuecomment-1366047829. That's the best method I know of at this point, but there's always a chance that I will discover a better way in the future. The Tkhtml docs are pretty limited when it comes to altering the webpage.

Let me know if this helps.

Andereoo commented 1 year ago

I also noticed that if changing the text is all you are trying to achieve, you can use the element's attribute instead. It's a little simpler. Make sure that you are using the latest version of TkinterWeb.

from tkinterweb import HtmlFrame
import tkinter as tk

html = """
<html>
<head>
<style>
p[myattribute=true]:after {
content: "True";
}
p[myattribute=false]:after {
content: "False";
}
</style>
</head>
<body>
<p id="change" myattribute=true></p>
</body>
</html>
"""

root = tk.Tk()
frame = HtmlFrame(root, messages_enabled=False)
frame.load_html(html)
frame.pack(fill="both", expand=True)

state = "true"
def change(event):
    global state
    target = frame.html.search("#change")
    if state == "true":
        frame.html.get_node_attribute(target, "myattribute", value="false")
        state = "false"
    else:
        frame.html.get_node_attribute(target, "myattribute", value="true")
        state = "true"

root.bind_all("<Return>", change)
root.mainloop()

Press 'Enter' to see the text change

Here, the given attribute has a corresponding text value specified in CSS. Changing the attribute (via frame.html.get_node_attribute(target, "attribute", value="new_value") changes the element's text. frame.add_css(css_code) can also be used to add new text options dynamically.

rodneyboyd commented 1 year ago

Thanks. It looks like I don't have an immediate use fornode()but now that I know about it maybe I'll think of one. I guess I should study tkhtml. However,search(selector)has proved very useful and did exactly what I needed!

Andereoo commented 1 year ago

Sounds good!

If you have any other questions or need help making something work, don't hesitate to reach out!