sciter-sdk / pysciter

Python bindings for Sciter
https://sciter.com
MIT License
396 stars 40 forks source link

set_html error : TypeError: html must be a bytes type #30

Closed martinlombana closed 3 years ago

martinlombana commented 3 years ago

This error is probably on my side, as I am unaware of the correct transformation to apply to a single string. What I am trying to do is to inject pure HTML (as I will be injecting generated jinja2 templates)

I am trying to do a simple test in a simple frame:

     def on_event(self, source, target, code, phase, reason):
        # events from html controls (behaviors)
        he = sciter.Element(source)

        if code == sciter.event.BEHAVIOR_EVENTS.DOCUMENT_READY:
            main_content = frame.get_root()
            main_content.set_html("<b>hello</b>")

I get: TypeError: html must be a bytes type

I tried encoding the string, or passing it as

b"<b>hello</b>"

or even:

"<b>hello</b>".encode('utf-8')

But it is not working.

Is there any special transformation that must be done that I am unaware of?

Thanks for the help,

martinlombana commented 3 years ago

As an extended test, I did:

main_content.set_html(bytes("<b>hello</b>", 'utf8')) But then I get:

ctypes.ArgumentError: argument 2: <class 'TypeError'>: expected LP_c_byte instance instead of bytes

martinlombana commented 3 years ago

@pravic , you can easily reproduce like this, this is a self contained example that you can just run. No files needed. Thanks!

PS: I also realized that the Document_Ready event gets picked twice. Is that normal somehow?: image

Thanks!

(Edited, and copied the right example!)

import sciter
from sciter import SCITER_RT_OPTIONS, Element

class RootEventHandler(sciter.EventHandler):
    def __init__(self, el, frame):
        super().__init__(element=el)
        self.parent = frame
        pass

    def on_event(self, source, target, code, phase, reason):
        he = sciter.Element(source)

        if code == sciter.event.BEHAVIOR_EVENTS.DOCUMENT_READY:
            print('sciter.event.BEHAVIOR_EVENTS.DOCUMENT_READY', flush=True)

            main_content = frame.get_root()
            element: Element = main_content.find_first('p')
            current_html = element.get_html()

            # This works
            print('current_html-> ', current_html, flush=True)

            # What is unexpected????? :(
            element.set_text('Oh, just the text')

            # This fails
            element.set_html(b"<b>I did</b>")
                        #element.set_html("<b>I did</b>".encode())

        pass

class Frame(sciter.Window):

    def __init__(self):
        super().__init__(ismain=True, uni_theme=True, debug=False, size=(500, 500), resizeable=False)
        self.set_dispatch_options(enable=True, require_attribute=False)
        pass

if __name__ == "__main__":
    print("Sciter version:", sciter.version(as_str=True))
    sciter.set_option(SCITER_RT_OPTIONS.SCITER_SET_DEBUG_MODE, True)
    frame = Frame()
    frame.setup_debug()
    # frame.load_file("index.html")

    frame.load_html(b"<html><body><p>I want to be substituted</p></body></html>")

    ev2 = RootEventHandler(frame.get_root(), frame)
    frame.run_app()
martinlombana commented 3 years ago

Ok, I found the bug in the DOM, @pravic

The thing is in dom.py, line 469, you pass the bytes html object to the C api, while, I think, what we need to pass is the raw bytes:

Before (not working):

 def set_html(self, html: bytes, where=SET_ELEMENT_HTML.SIH_REPLACE_CONTENT):
        """Set inner or outer html of the element."""
        if not html:
            self.clear()
            return self
        if not isinstance(html, bytes):
            raise TypeError("html must be a bytes type")
        ok = _api.SciterSetElementHtml(self, html, len(html), where)
        self._throw_if(ok)
        return self

After (working well):

    def set_html(self, html: bytes, where=SET_ELEMENT_HTML.SIH_REPLACE_CONTENT):
        """Set inner or outer html of the element."""
        if not html:
            self.clear()
            return self
        if not isinstance(html, bytes):
            raise TypeError("html must be a bytes type")
        raw_html = (ctypes.c_byte * len(html)).from_buffer_copy(html)
        ok = _api.SciterSetElementHtml(self, raw_html, len(raw_html), where)
        self._throw_if(ok)
        return self

Do you (@pravic or @c-smile) want me to submit a pull request in the future for other bugs like this?

pravic commented 3 years ago

@martinlombana ctypes has a number implicit conversions but there was a mistake in a function declaration, see the fix.

pravic commented 3 years ago

Document_Ready event gets picked twice.

Since you're overloading a self.on_event, check the phase for events - it can be SINKING, BUBBLING, HANDLED.

pravic commented 3 years ago

I believe, the fix resolves this issue. Feel free to add more comments if need.

martinlombana commented 3 years ago

Thanks.

Where can I see the an explanation of the phases, and all the possible events?

And thanks for the fix. Are you going to release a new version (i tried the upgrade but no new version was found) or should I install from GIT from now now?

c-smile commented 3 years ago

Check https://sciter.com/developers/for-native-gui-programmers/#events-propagation

c-smile commented 3 years ago

Events are listed in the header include/sciter-x-behavior.h , in particular here are all mouse events: https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-behavior.h#L135

martinlombana commented 3 years ago

Thanks,

I was interested in the KEY events as well. But when I do this, the program does not register any key events, at all. Is there any other way that this should be done? I want to access the KEY_DOWN and KEY_UP events and the KEY_CODE.

(I shall note, that I am getting other events, like clicks, etc)

    class RootEventHandler(sciter.EventHandler):
    def __init__(self, el, frame):
        super().__init__(element=el)
        self.parent = frame
        pass

    def on_event(self, source, target, code, phase, reason):
        he = sciter.Element(source)

        print('he-> ', he, flush=True)

        if code == sciter.event.KEY_EVENTS.KEY_DOWN:
            print ('KEY DOWN')

        if code == sciter.event.BEHAVIOR_EVENTS.DOCUMENT_READY:
            print('sciter.event.BEHAVIOR_EVENTS.DOCUMENT_READY', flush=True)

            main_content = frame.get_root()
            element: Element = main_content.find_first('p')
            current_html = element.get_html()

            # This works
            print('current_html-> ', current_html, flush=True)

            # What is unexpected????? :(
            element.set_text('Oh, just the text')

            # This fails
            string_bytes = b"<b>now!</b>"
            element.set_html(string_bytes)
            # element.get_expando('what', True)

        pass
pravic commented 3 years ago

@martinlombana I'll make a new release, yes.

Please, ask other questions in a different issue if they are not related to set_html.

But the answer is here: https://github.com/sciter-sdk/pysciter/blob/master/sciter/event.py#L16. Pass a proper combination of EVENT_GROUPS to the EventHandler constructor.

martinlombana commented 3 years ago

Sure. Sorry about that! --


Martín Lombana

TODOLOGIC.COM http://TODOLOGIC.COM | APPS & MORE

+34 633 63 02 35 | +34 946 759 137

INSTAGRAM @ https://instagram.com/todologicTODOLOGIC https://instagram.com/todologic