How to do it...

In the previous recipes, one of our main challenges was how to combine two GUI technologies that were designed to be the one-and-only GUI toolkit for an application. We found various simple ways to combine them.

We will again launch the wxPython GUI from a tkinter GUI main event loop and start the wxPython GUI in its own thread, which runs within the Python.exe process.

In order to do this, we will use a shared global multiprocessing Python Queue.

While it is often best to avoid global data, in this recipe it is a practical solution and Python globals are really only global in the module they have been declared.

Here is the Python code that makes the two GUIs communicate with each other to a certain degree. In order to save space, this is not pure OOP code. Neither are we showing the creation code for all widgets. That code is the same as in the previous recipes:

# Communicate.py 
import tkinter as tk
from tkinter import ttk
from threading import Thread

win = tk.Tk()
win.title("Python GUI")

from queue import Queue
sharedQueue = Queue()
dataInQueue = False

def putDataIntoQueue(data):
global dataInQueue
dataInQueue = True
sharedQueue.put(data)

def readDataFromQueue():
global dataInQueue
dataInQueue = False
return sharedQueue.get()
#===========================================================
import wx
class GUI(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
parent.CreateStatusBar()
button = wx.Button(self, label="Print", pos=(0,60))
self.Bind(wx.EVT_BUTTON, self.writeToSharedQueue, button)

#--------------------------------------------------------
def writeToSharedQueue(self, event):
self.textBox.AppendText("The Print Button has been clicked!n")
putDataIntoQueue('Hi from wxPython via Shared Queue.n')
if dataInQueue:
data = readDataFromQueue()
self.textBox.AppendText(data)
text.insert('0.0', data) # insert data into tkinter GUI

#============================================================
def wxPythonApp():
app = wx.App()
frame = wx.Frame(None, ,
size=(300,180))
GUI(frame)
frame.Show()
runT = Thread(target=app.MainLoop)
runT.setDaemon(True)
runT.start()
print(runT)
print('createThread():', runT.isAlive())
#============================================================
action = ttk.Button(win, text="Call wxPython GUI", command=wxPythonApp)
action.grid(column=2, row=1)

#======================
# Start GUI
#======================
win.mainloop()

Running the preceding code first creates the tkinter part of the program and, when we click the button in this GUI, it runs the wxPython GUI. Both are running at the same time as before but, this time, there is an extra level of communication between the two GUIs:

Communicate.py

The tkinter GUI is shown on the left-hand side in the preceding screenshot, and by clicking the Call wxPython GUI button, we invoke an instance of the wxPython GUI. We can create several instances by clicking the button several times.

All of the created GUIs remain responsive. They do not crash nor freeze.

Clicking the Print button on any of the wxPython GUI instances writes one sentence to its own TextCtrl widget and then writes another line to itself, as well as to the tkinter GUI. You will have to scroll up to see the first sentence in the wxPython GUI.

The way this works is by using a module-level queue and a tkinter Text widget.

One important element to note is that we create a thread to run the wxPython app.MainLoop, as we did in the previous recipe:

def wxPythonApp(): 
app = wx.App()
frame = wx.Frame(None, ,
size=(300,180))
GUI(frame)
frame.Show()
runT = Thread(target=app.MainLoop)
runT.setDaemon(True)
runT.start()

We create a class which inherits from wx.Panel and name it GUI and then instantiate an instance of this class in the preceding code.

We create a button-click event callback method in this class, which then calls the procedural code that was written above it. Because of this, the class has access to the functions and can write to the shared queue:

    #------------------------------------------------------ 
def writeToSharedQueue(self, event):
self.textBox.AppendText("The Print Button has been clicked!n")
putDataIntoQueue('Hi from wxPython via Shared Queue.n')
if dataInQueue:
data = readDataFromQueue()
self.textBox.AppendText(data)
text.insert('0.0', data) # insert data into tkinter

We first check whether the data has been placed in the shared queue in the preceding method and, if that is the case, print the common data to both GUIs.

The putDataIntoQueue() line places data into the queue and readDataFromQueue() reads it back out, saving it in the data variable. text.insert('0.0', data) is the line that writes this data into the tkinter GUI from the Print button's wxPython callback method.

The following are the procedural functions (not methods, for they are not bound) which are being called in the code and make it work:

from multiprocessing import Queue 
sharedQueue = Queue()
dataInQueue = False

def putDataIntoQueue(data):
global dataInQueue
dataInQueue = True
sharedQueue.put(data)

def readDataFromQueue():
global dataInQueue
dataInQueue = False
return sharedQueue.get()

We used a simple Boolean flag named dataInQueue to communicate when the data is available in the queue.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.51.36