potocpav / python-concur

Concur UI Framework for Python
MIT License
48 stars 2 forks source link

Create and/or Publish Complex Example Applications #19

Open potocpav opened 4 years ago

potocpav commented 4 years ago

Present these applications front-and-center on the homepage. Right now, the homepage contains just trivial usage examples, which really doesn't look too good. Could be inspired by the Streamlit homepage.

Contributions welcome.

sla-te commented 4 years ago

Ive actually been trying to build my application with concur for a while (currently using PySimpleGUI) , I would be willing to display it if you help me with some very basic things.

For instance I got this snippet:

import concur as c

def test():
    print("Orange")

def app():
    while True:
        key, event = yield from c.orr([
            c.button("test"),
            c.button(f"Banana"),

        ])

        if key == "test":
            test()
        print("Clicked:", event)
        yield

c.main(app())

Now I would like to make test() open another window with some more buttons, how could I do that?

potocpav commented 4 years ago

Hi, sorry for the delay in answering.

The most straightforward way to achieve this is using a state variable for whether the window is open.

import concur as c

def test():
    return c.window("Window", c.orr([
        c.button("test-window"),
        c.button("Banana-window"),
        c.button("Close"),
        ]))

def app():
    window_open = False
    while True:
        key, event = yield from c.orr([
            c.button("Toggle window"),
            test() if window_open else c.nothing(),
        ])

        if key == "Toggle window":
            window_open = not window_open
        elif key == "Close":
            window_open = False

        print("Clicked:", key)
        yield

c.main(app())

I hope this is what you were looking for. Feel free to ask some more. I should be able to answer faster in the future :)

Edit: added a close button to the window.

potocpav commented 4 years ago

It would be great if you could share your application! I will be glad to review the code and/or help you with using Concur.

sla-te commented 4 years ago

Thanks for the snippet! Its almost what I am looking for, What I would need though is a "main" Gui which has some buttons, then if I click one of the buttons it will open another separate window, like starting a new feature. It shouldnt all be inside a "wrapper window" though but all free floating separate windows. As for the application I need some time to prepare it but we can share the whole gui part when its prepared.

potocpav commented 4 years ago

I haven't really used multiple separate OS windows. ImGui supports this use-case, see this issue. The video in the first comment may show what you want.

However, Concur doesn't support this ATM. I don't know how difficult would it be to implement, and how much would it complicate things. There may (or may not) be some subtleties with window position remembering, working on all platforms, etc.

If all you need is a file open/save dialog, then use wxPython.

While there are valid use-cases for multiple windows, a single window may be better from user's perspective. Gimp before 2.8 used multiple OS windows, and it wasn't really working too well. Blender is a complex single-window application with flexible docking. And web-based applications don't support multiple windows either.

sla-te commented 4 years ago

Its indeed the use case I am looking for though. It it worked on Windows that would be perfectly sufficient. Window positioning remembering would not be a big issue as well. Do you think you could throw out a quick and dirty solution for it?

sla-te commented 4 years ago
    key = yield from c.orr([

            c.main_menu_bar(widget=c.orr([
                c.menu(label="File", widget=c.orr([
                    c.menu_item("New", "Ctrl+N"),
                    c.menu_item("Open...", "Ctrl+O"),
                    c.separator(),
                    c.menu_item("Quit", "Ctrl+Q"),
                ])),
            ])),

            c.key_press("Quit", ord('Q'), ctrl=True),
            c.button("Banana"),
            c.button("Toggle window"),
            test() if window_open else c.nothing(),
        ])

So this doesnt seem to drop any warning but if i do it like this:

    key, event = yield from c.orr([

            c.main_menu_bar(widget=c.orr([
                c.menu(label="File", widget=c.orr([
                    c.menu_item("New", "Ctrl+N"),
                    c.menu_item("Open...", "Ctrl+O"),
                    c.separator(),
                    c.menu_item("Quit", "Ctrl+Q"),
                ])),
            ])),

            c.key_press("Quit", ord('Q'), ctrl=True),
            c.button("Banana"),
            c.button("Toggle window"),
            test() if window_open else c.nothing(),
        ])

The ide will mark it all in yellow and say "need more values to unpack", how do I manage that?

potocpav commented 4 years ago

I don't see a difference between those two snippets. Did you accidentally copy the same thing twice?

sla-te commented 4 years ago

My bad, copy paste error, check now

potocpav commented 4 years ago

It runs fine when I try it. Is it possible that the analysis done by IDE is incorrect? What IDE do you use, and how does it analyze the code?

It's also possible that the type annotations in core.py are buggy, removing them may work. I will remove them in master if it fixes the issue.

It may be possible to get rid of the warning by some reformatting, such as

ret = yield from ...
key, event = ret[0], ret[1]

but that's definitely quite ugly.

sla-te commented 4 years ago

yeah, thats what i tried too,, then you get this one: image :D

sla-te commented 4 years ago

Not sure if it is incorrect, im using Pycharm

sla-te commented 4 years ago

Also im looking for a replacement for "PopupGetFile" in PySimpleGUI, any suggestions? Basically just a file brower with which a user can select a file and then its path will be returned.

potocpav commented 4 years ago

About the multiple windows, I found a more in-depth explanation of what needs to be done. It looks like it requires quite involved changes to the GLFW integration layer, and the API isn't stable.

The required changes are implemented in the example GLFW backend in C++ (imgui_impl_glfw.cpp). However, Concur uses a different GLFW backend code written in Python, so the code would have to be re-implemented.

The C++ integration has 850 LOC, and a whole bunch of new functions would have to be exposed through Cython. It is not something I have time to do. And I don't have a Windows installation anyways to test it.

sla-te commented 4 years ago

Ok, thats not a big problem, I have thought about it and I could do it inside a wrapper window.

potocpav commented 4 years ago

I used in wxPython for a file-open dialog, like in this stackoverflow answer. There may be better options on Windows, idk.

potocpav commented 4 years ago

I tested PyCharm, and it is definitely a bug in their handling of generators. I don't think the exact issue is in their bug tracker. It can be replicated by this:

a, b = yield from c.event((123, 456))

and c.event is a dead-simple function with no type annotations. I would suggest just disabling the warning and/or reporting the bug to PyCharm.

sla-te commented 4 years ago

Awesome! Thanks for figuring that out. I will just wrap away the main gui window in a function and have it return the window, that way the whole main loop wont be yellow ^^

sla-te commented 4 years ago

Ok, so now it gets a little tricky. I have a class or lets call it a feature, that I would want to start via click on a button. This is the way I would think of handling it (with the idea to open a new window from within the NiceFeature class inside the thread but that didnt go too well :D):

    elif event == "Nice Feature":
            from src.features.nice_feature.nice_feature import NiceFeature
            t_nice_feature= Thread(target=NiceFeature(settings).run(), daemon=True)
            t_nice_feature.start()
            running_features.append({"feature_name": "Nice Feature", "timestamp": datetime.datetime.now(), "thread_obj": t_nice_feature})

Now before starting the feature, I must give it some settings which it needs, to know what to do. So on clicking the button a new window should open from within which I can define the required settings and then click "start", which then in order should call the run() function of the class NiceFeature to start execution.

sla-te commented 4 years ago

Ok, so I did some trial and error and now tried to create a separate file for every feature containing functions, that will return new customized windows for every function. Then imported all these functions in to the main gui script and ran. So far so good. - Now inside of the NiceFeature class a ThreadPoolExecutor is started and apparently after starting the feature the main gui will be frozen, any suggestions?

sla-te commented 4 years ago

I created a repository to illustrate the problem (and invited you as collaborator, feel free to push changes to master): https://github.com/chwba/concur_gui_test

potocpav commented 4 years ago

I pushed some changes into the repo. Hopefully, it will be helpful for you to go through the commits :-) . Play around with it.

In the last change, I tried to show how the main event loop can be split into multiple loops if the application gets too big. State can be also broken up into several pieces using nested classes, but I didn't do it since the state is so small here.

There is a weird thing going on where the state is mutated in nice_feature_gui, but then also returned. I normally deepcopy state before handling it to functions, but deepcopy doesn't work for futures, and it may require a custom __deepcopy__ override. I didn't do that to keep the code cleaner. If you run into trouble with that, feel free discuss the problems.

sla-te commented 4 years ago

Big hero bro 🗡️ :D - Will tinker around with it soon!

sla-te commented 4 years ago

Ok, I see one thing. as of now executor.shutdown is not being called and afaik if not called, it will not free the resources of the submitted futures which in case we submit thousands of futures will lead to severe performance problems. Please correct me if im wrong but this was my experience from working with the executor in the past.

sla-te commented 4 years ago

I pushed all my attempts to "hack around" the problem to master, maybe you can find a solution

potocpav commented 4 years ago

Let's move this discussion to your repo's issue tracker https://github.com/chwba/concur_gui_test/issues/1