How to enforce StatusBar redraws



  • Hello again;

    when I wish to use the global status bar in Python (c4d.StatusSetBar() etc) I need to relinquish control to C4D so the GUI can be updated and the status bar is actually redrawn. If I'm in a Python script and just keep setting the status bar, it's only updated after the script has ended:

        for p in xrange(101):
            c4d.StatusSetBar(p)
            time.sleep(0.05)
    

    So, this code obviously doesn't work.
    Adding EventAdd() doesn't work either, as this will only add a redraw to the message queue, which is not evaluated until the script ends.
    Using c4d.GeSyncMessage(c4d.EVMSG_CHANGE) seems promising but it always returns False.
    DrawViews only redraws the viewports.

    Is it really impossible to relinquish control from a Python script to enable C4D to evaluate the message queue once, and then return to the script? Or enforce the update of the status bar in some other way? It's definitely not an insurmountable technical issue, because if I call MessageDialog in between...

        for p in xrange(101):
            c4d.StatusSetBar(p)
            if p in [5,20,30,40,50,75,100]:
                gui.MessageDialog("xxx")
            time.sleep(0.05)
    

    ...not only the dialog appears, but also the status bar is updated. And the really strange effect: The status bar seems to update distinctly several times between message dialogs at some points. Try [0,100] as timing for the dialogs to appear... what's causing these intermediate updates? Why doesn't that work without the dialog calls?

    (We do not need to discuss calling the status bar updates in asynchronous worker threads or through the message system; I just want to know whether there is a way to call it from a script, and to solve the dialog riddle...)



  • Hi,

    I am not quite sure what you would consider "obviously not working", but the following Script Manager snippet will update the statusbar 100 times in R21.

    import c4d
    import time
    
    class MyDialog(c4d.gui.GeDialog):
        """
        """
        pass
    
    def main():
        """
        """
        # If you have something bocking in the way, like a modal dialog for
        # example, this will not work, due to the code not being reached until
        # the dialog closes in this case.
        dialog = MyDialog()
        # dialog.Open(c4d.DLG_TYPE_MODAL)
    
        # An async dialog is however fine, but the loop running on the main
        # thread will block that dialog until it has finished.
        dialog.Open(c4d.DLG_TYPE_ASYNC)
        for i in range(101):
            c4d.StatusSetText(str(i))
            c4d.StatusSetBar(i)
            time.sleep(.05)
        print "end"
    
    if __name__ == "__main__":
        main()
    

    Cheers,
    zipit



  • @zipit: Actually I wouldn't consider your code a solution to the problem. This is something, though I'm aware it's done quite often (and at least for quick tests I used such code myself for sure), that's a bit dangerous and most likely not supported by Maxon. The context owning the dialog, which is used to defer the status bar updates, does no longer exist after the execution of the script. It's basically a a zombie dialog performing the status bar updates. In my view, such code shouldn't be recommended, nor be used in anything that's released to the public.

    Edit: While it's no good practice to leave a dialog open after script execution ended, my comment was wrong and stupid.



  • Hi,

    you mean that I didn't close the dialog before script ended? Yeah, that is probably not the most cleanest code, but I think @Cairyn can handle it. The point was only the demonstrate the blocking behaviour of different dialog types. If you mean something else, something status bar related, I am all ears, because I do not really understand how the dialog should influence the status bar as MyDialog literally does nothing.

    Currently I do not see anything inherently bad with that snippet, except from the fact that putting such giant blocking loop into main thread is a bad idea in the first place.

    Cheers,
    zipit



  • @zipit Thanks, but this doesn't work in my setup. What happens (on my system at least) is that an empty dialog appears. The status bar still doesn't update before the Python script ends.

    I found that it does work if I dock the status bar in the same window as the script manager (I do not need the separate dialog in that case), so obviously it depends on what internal message loop is still evaluated and which one is blocked.

    Normally my status bar resides in a separate window on the second screen (together with material manager, Takes manager and other stuff) which is classed as "Independent Window" (the latter doesn't matter for the message loop apparently, if the window is not independent, it doesn't work either).

    There is no documentation on how the windows' various message loops interplay with the Python interpreter (beyond the obvious modal dialogs), but what I experimentally find, baffles me a little.



  • @zipit You are right. Forget my comment. I shouldn't have posted before my first coffee. Indeed I was triggered by the dialog not being closed, but then brain activity obviously went back to power save...



  • Hi,

    so it seems to be more a multi monitor setup problem. Although I would not rule it out categorically and also have not tested it, I would doubt that Python is here the culprit and would more suspect this to be a general design flaw of Cinema.

    You could try to invoke between the status bar calls c4d.gui.GeUpdateUI(), but I would not hold my breath.

    Cheers,
    zipit



  • @zipit said in How to enforce StatusBar redraws:

    c4d.gui.GeUpdateUI()

    Nope, that doesn't work reliably either. It's definitely a question of how the message loops are forwarding messages to each other, and how the status bar is updated internally, and how the windows are parented to each other. Without the C4D source code, it's probably impossible to come to a definitive answer.

    Here's what I checked:

    1. Script Manager and Status Bar are both docked in the main window. Works.
    2. Script Manager and Status Bar are both docked in the Independent Window. Works.
    3. Script Manager and Status Bar are both docked in the Dependent Window. Works.
    4. Script Manager is undocked, and the Status Bar is docked within that window. Works. Though, that may be the same case as 2/3.

    As long as Status Bar and Script Manager share a MS Windows window, the update message is apparently propagated and the Bar progresses. This works even if I use a keyboard shortcut to start the script while the main window/viewport is the active window.

    1. Script Manager is docked in the Independent Window, and Status Bar is docked there too but then I undock the Status Bar. Works. (Seems the undocked Status Bar is internally still a child of the Independent Window.)
    2. Script Manager is docked in the Independent Window, and Status Bar is docked in the Main Window; then I undock the Status Bar. Doesn't work. (While this seems the same as 5, here the Status Bar seems to stay a child of the Main Window.)
    3. Script Manager is docked in the (In)dependent Window, and Status Bar is docked in the Main Window. Doesn't work.
    4. Like 7 but vice versa. Doesn't work.

    I gather that while the Python interpreter is running, the Script Manager still has some update loop running (not a normal message loop, as you can't move the window edit any code there) that is taking care of the status bar updates when in the same window (hierarchy?).

    I also suppose that the status bar gets some exceptional updates, as the Console never updates (I tried to insert some print commands into the timer loop) before the script ends, regardless of the window hierarchy.

    Okay, that was enlightening, thanks for the comments. I suppose Maxon needs to expand the status bar updates across the whole window hierarchy, so the bar would work everywhere, but I also have a feeling that they won't bother with such a minor issue 😁



  • hi,

    just to let you know i've asked the dev about that one :)

    Cheers,
    Manuel



  • Thanks. In the meantime I made some more experiments, and it seems the problem is limited to when you click Execute in the Script Manager or the Customize Command window.

    I created a keyboard shortcut and embedded the script as icon in the GUI. In both cases, the status bar gets correctly updated regardless of what window was active / received the key press, and where the icon resides (same window, other window, undocked toolbar). It seems the Python interpreter is not blocking the message loops in these cases.

    That makes the question somewhat academic, because shortcut or icon (or menu but I did not check that) would be the common ways to start the script; starting it from the Script Manager is common during development but irrelevant in practice.

    I'm curious to hear what the developers will say about the message loops though.