views:

77

answers:

3

So I made a Winforms GUI in Visual Studio using C#, but for the project I am working on I want the majority of the code to be written in Python. I am hoping to have the "engine" written in python (for portability), and then have the application interface be something I can swap out.

I made the C# project compile to a .dll, and was able to import the classes into an IronPython script and start the GUI fine.

The problem is that running the GUI stops the execution of the Python script unless I put it into a separate thread. However, if I put the GUI into a separate thread and try and use the original python thread to change state information, I get an exception about modifying a control from a different thread than what created it.

Is there any good way to communicate with the GUI thread or a way to accomplish what I am trying to do?

The C# driver of the GUI:

public class Program
{
    private static MainWindow window;

    [STAThread]
    static void Main()
    {
        Program.RunGUI();
    }

    public static void RunGUI()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        window = new MainWindow();
        Application.Run(window);
    }

    public static void SetState(GameState state)
    {
        window.State = state;
    }
}

And the python script:

import clr
clr.AddReferenceToFile("TG.Model.dll")
clr.AddReferenceToFile("TG.UI.dll")
from TG.Model import GameState
from TG.UI import Program
import thread
import time


def main():
    print "Hello!"
    state = GameState()
    print state.CharacterName
    print dir(Program)
    thread.start_new_thread(Program.RunGUI, ())
    #Program.RunGUI()
    time.sleep(2)
    Program.SetState(state)
    raw_input()


if __name__ == "__main__":
    main()
+2  A: 

Put everything after the call to Program.RunGUI() in an event handler.

C#:

public static void RunGUI(EventHandler onLoad)
{   
    ...

    window = new MainWindow();
    window.Load += onLoad;
    Application.Run(window);
    window.Load -= onLoad; //removes handler in case RunGUI() is called again
}

Python:

def onload(sender, args):
    time.sleep(2)
    Program.SetState(state)
    raw_input()  

def main():
    ...
    Program.RunGUI(onload)
Mark Cidade
This solution seems like exactly what I was looking for, but when I try doing this as shown the code in the onload() function is not run. If I just put a print statement in there for example nothing is printed out after the GUI starts up. Any ideas why? Thanks for your help!
ghills
I'm not sure why. Try instantiating the MainWindow class from python and add the event handler directly—that's worked for me in the past.
Mark Cidade
You can also break into the C# code to see if the event handler is indeed registered.
Mark Cidade
Ah figured it out - the onload function needed to have the parameters for the event handler. Thanks again!
ghills
Cool. I updated the code in my answer to include the parameters.
Mark Cidade
+1  A: 

Controls have thread affinity, and can only be accessed by the thread that created them. There is a BackgroundWorker object that handles passing data between threads rather nicely.

Marco
A: 

If you want to modify GUI from within a thread other than GUI's thread, you must use Invoke method.

xport