AFAIK, there’s nothing in py5 or JPype that would make Java threads any safer!
If you feel the need to use SwingUtilities.invokeLater() in Processing’s Java Mode, you should also take the same precautions on py5 too!
“py5 Swing Demo II” is using SwingUtilities.invokeLater() now:
"""
* py5 Swing Demo II (v1.0.0)
* GoToLoop & Svan (2024/Jun/04)
*
* https://Discourse.Processing.org/t/
* swing-components-in-default-processing-window-py5/15
"""
import py5
from java.awt.Color import GREEN
from javax.swing import JSlider, JLabel, JButton, JColorChooser, SwingUtilities
DIAM = 'Diameter: %d'
USE_INVOKE_LATER = True
this = py5._instance
fill = GREEN
def settings(): this.size(600, 660)
def setup():
this.noLoop()
this.fill(fill.getRGB())
global w, h, cx, cy
w, h = this.width, this.height
cx, cy = w >> 1, h >> 1
if USE_INVOKE_LATER: # delegate GUI creation to run on the EDT thread
SwingUtilities.invokeLater(createGUI)
while 'slider' not in globals(): this.delay(100)
else: createGUI() # keep Swing GUI creation on the Animation thread
def draw():
this.background(0o320)
this.circle(cx, cy, slider.getValue())
def mouse_wheel(e): slider.setValue(slider.getValue() - e.get_count() * 10)
def createGUI():
global slider, label
slider = JSlider(0, w - 60, cx)
slider.setBounds(180, 20, 200, 24)
slider.addChangeListener(slideChange)
slider.setToolTipText('circle diameter')
(label := JLabel(DIAM % cx)).setBounds(75, 20, 80, 24)
btn = JButton('Color')
btn.setBounds(400, 20, 80, 24)
btn.addActionListener(btnAction)
(window := this.getSurface()).setTitle('py5 Swing Demo')
(canvas := window.getNative()).setBounds(0, 60, w, h - 60)
frame = canvas.getFrame()
frame.add(slider); frame.add(label); frame.add(btn)
slider.repaint(); label.repaint(); btn.repaint()
def slideChange(_):
label.setText(DIAM % slider.getValue())
this.redraw()
def btnAction(_, TITLE = 'Choose color:'):
global fill
fill = JColorChooser.showDialog(None, TITLE, fill) or fill
this.fill(fill.getRGB())
this.redraw()
__name__ == '__main__' and py5.run_sketch()
This new version is exactly the same as previous “py5 Swing Demo”, but w/ a constant USE_INVOKE_LATER, which we can toggle True or False before running the code.
When USE_INVOKE_LATER is False, it behaves exactly like its previous original version btW; using Processing’s Animation thread to build its Swing GUI components.
Also notice I had to pause the sketch execution inside setup() by invoking delay() inside a while loop, awaiting for createGUI() under EDT thread, so draw() doesn’t start running until global variable slider exists:
if USE_INVOKE_LATER: # delegate GUI creation to run on the EDT thread
SwingUtilities.invokeLater(createGUI)
while 'slider' not in globals(): this.delay(100)
As you can notice, we can pass any Python function as an argument for SwingUtilities.invokeLater(), just like other listener methods such as addChangeListener() or addActionListener().