Search code examples
pythonmultithreadingwxpythongeneratorsimulation

Using a Simulation (MyHDL) and wxPython together


I am using the package MyHDL to do hardware simulation, but I want to put a GUI around it so that users can interactively change signals and see the other signals update.

The problem is, MyHDL uses a simulator as follows

  • Simulator has several generators
  • Each generator can either be listening for signal changes, or call yield delay(x) in order to tell the simulator to wait to call it again for x ticks.
  • Call Simulator.run() to start loop of simulation
  • Runs loop to completion

wxPython obviously uses an event loop. Therefore, I clearly can't run both of these without tying the other one up.

My first (dumb) method was as follows

    def Hack():
        @instance
        def inst():
            yield delay(1)
            self._app.MainLoop()
        return inst
    MyHack = Hack()
    self._instances.append(MyHack)
    self._simulator = Simulation(*self._instances)
    self._simulator.run()

This worked, but the inst() generator would only run once, so the Simulation really did nothing.

Then I realized this was a situation that called for multithreading. I tried the following:

    self._simulator = Simulation(*self._instances)
    p = Process(target=StartSim, args=(self._simulator,))
    p.start()
    #self._simulator.run()
    self._app.MainLoop()
    p.join()

def StartSim(sim):
    sim.run()

But of course I didn't think about thread safety initially. Also, all of those generator functions I have in the simulator can't be passed to the thread.

I'm thinking I could spend some time really working on a solid thread worker class which gets created earlier and which can SOMEHOW get the generator functions needed via some kind of message passing. However, it seems to me like it would be way easier if I could define some kind of generator that will call "yield delay(1)" for every step of the wxPython loop. Something like this in a class:

def OnIdle(self):
    yield delay(10)

And then going with my original method. The problem is, I need the code to be something like this:

    def Hack():
        @instance
        def inst():
            self._app.InitMainLoop()
            while(self._app.InMainLoop())
                yield delay(1)
                self._app.DoMainLoopBody()
        return inst

So, after that long-winded explanation, is there a good way to do this? Can I define my own MainLoop or change it in some way? Or has anyone ever worked with MyHDL and threading?


Solution

  • Nevermind, I "solved" it using app.ExitLoop()

    I realized I could wrap my own loop around app.MainLoop() and whenever I get an event that the Simulator cares about, the handler calls app.ExitLoop(), giving control back to the simulator and then it starts the main loop for wx again.

    It's not perfect, it's definitely a hack, but it's working