Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add block parameter to webview.start using multiprocessing #1384

Closed
louisnw01 opened this issue May 16, 2024 · 13 comments
Closed

Add block parameter to webview.start using multiprocessing #1384

louisnw01 opened this issue May 16, 2024 · 13 comments
Labels

Comments

@louisnw01
Copy link
Contributor

Description

Hey @r0x0r,

I'd like to request an implementation of a non-blocking webview window, which runs the webview in a seperate process. This would eliminate the need for a callback function in webview.start, and would greatly increase the flexibility of this library.

A basic example:

window = webview.create_window()

webview.start(block=False)

window.evaluate_js("console.log('hello')")

while 1:
    sleep(1)

I implemented a very primitive and specialized version of this for my library; here is the source: https://github.com/louisnw01/lightweight-charts-python/blob/main/lightweight_charts/chart.py

I was running into a few issues with that, and I remember you mentioning that this was in the works, so I just wanted to bring it to your attention!

Thanks!

Louis

@r0x0r
Copy link
Owner

r0x0r commented May 16, 2024

The problem with multiprocessing is that a Window object cannot be serialised because of threading events. Unability to serialise Window is rather a show stopper. Non-blocking start() can be implemented without multiprocessing on all the platforms but Cocoa.
I have tried to implement this, but could not find an adequate solution. Help is welcomed.

@louisnw01
Copy link
Contributor Author

The problem with multiprocessing is that a Window object cannot be serialised because of threading events. Unability to serialise Window is rather a show stopper. Non-blocking start() can be implemented without multiprocessing on all the platforms but Cocoa. I have tried to implement this, but could not find an adequate solution. Help is welcomed.

Why do you need to serialise the Window object?

Louis

@r0x0r
Copy link
Owner

r0x0r commented May 16, 2024

You might want to access a Window objects in your application code. For that you need to pass them between processes and they need to be serialised first.

@louisnw01
Copy link
Contributor Author

You might want to access a Window objects in your application code. For that you need to pass them between processes and they need to be serialised first.

What I mean is the Window object (in its current state) can sit in the other process.

Then another 'window' object (which would replace the old one) could be used which only communicates with the other object running in the other process

@louisnw01
Copy link
Contributor Author

Essentially all you would need to write is a wrapper for a new window class, and a controller which sits in the other process and keeps everything up to date

@r0x0r
Copy link
Owner

r0x0r commented May 16, 2024

I am afraid I do not follow. How would you rewrite following code in multiprocessing? This is identical to passing a function with a parameter to start

from time import sleep
from threading import Thread
import webview

"""
Loading new HTML after the window is created
"""


def load_html(window):
    sleep(5)
    window.load_html('<h1>This is dynamically loaded HTML</h1>')


if __name__ == '__main__':
    window = webview.create_window('Load HTML Example', html='<h1>This is initial HTML</h1>')

    t = Thread(target=load_html, args=(window,))
    t.start()
    webview.start()

@louisnw01
Copy link
Contributor Author

I am afraid I do not follow. How would you rewrite following code in multiprocessing? This is identical to passing a function with a parameter to start

from time import sleep
from threading import Thread
import webview

"""
Loading new HTML after the window is created
"""


def load_html(window):
    sleep(5)
    window.load_html('<h1>This is dynamically loaded HTML</h1>')


if __name__ == '__main__':
    window = webview.create_window('Load HTML Example', html='<h1>This is initial HTML</h1>')

    t = Thread(target=load_html, args=(window,))
    t.start()
    webview.start()

Something like this:

from time import sleep
from threading import Thread
import webview


#
#  this runs in a seperate process
#
class ActualWindow:
    
    ...

    def loop(self):
        while 1:
            method_name, args = self.mp_queue.get()
            method = getattr(self, method_name)
            method(*args)


#
#  dummy Window class, which is instansiated in the main process
#
class Window():
    
    ...

    def load_html(self, html: str):
        self.mp_queue.put(('load_html', (html)));


if __name__ == '__main__':
    window = webview.create_window('Load HTML Example', html='<h1>This is initial HTML</h1>')

    webview.start(block=False)

    sleep(5)
    window.load_html('<h1>This is dynamically loaded HTML</h1>')

@r0x0r
Copy link
Owner

r0x0r commented May 17, 2024

The thing is pywebview itself is using window objects internally. In your example you create a window object in the main process. How do you pass it to the process in webview.start()?

@louisnw01
Copy link
Contributor Author

The thing is pywebview itself is using window objects internally. In your example you create a window object in the main process. How do you pass it to the process in webview.start()?

The 'actual' window object is the one running in the other process ie the same process that webview is running in.

The only communication between the two processes is the 'dummy' classes or methods that send over a queue, which control the actual classes instantiated in the other process

@lanzz
Copy link
Contributor

lanzz commented May 18, 2024

If all that is needed is a non-blocking webview.start(), then why not just keep the current threading implementation, but instead of running the backend logic callback in a thread, just create the main window in a thread and return immediately, allowing the backend logic to be implemented in the main thread? Considering that webview.start() will already start all windows but the first in new threads, it's even a bit unexpected that it starts the first one in the main thread, requiring a separate callback for the backend logic to run that in a thread.

@r0x0r
Copy link
Owner

r0x0r commented May 19, 2024

@louisnw01 honestly I don't know if it is possible to split Window object in such a way. If you or somebody else get it working keeping API the same I can accept a PR.

@lanzz Cocoa requires GUI to be run on the main thread. Other platforms can be run in a thread as you described.

Copy link

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@github-actions github-actions bot added the stale label Jun 19, 2024
Copy link

The message to post on the issue when closing it. If none provided, will not comment when closing an issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants