The following source code will create an entry level Python/Py5 Editor which will run on all three platforms (initially developed for MacOS). It was written using Swing components with RSyntaxTextArea used for syntax highlighting. Setup is non-trivial and probably more suited to advanced programmers. Thonny is likely a better choice for beginners.
Before running, RSyntaxTextArea.jar will need to be downloaded from here:Download rsyntaxtextarea_1.5.1.zip (RSyntaxTextArea) and added to your Processing’s ‘libraries’ folder as described here: RSyntaxTextArea With Swing In Processing.
The second chore is to install py5 following instructions found on the py5 website: https://py5coding.org. An ‘environment’ setup is helpful on MacOS and Linux; I’m not sure how beneficial it is for Windows. I used ‘Miniconda3’(instructions also available on net), but a more robust ‘Anaconda’ or ‘venv’ environment is also possible.
Py5 source code may be run in two of the five modes, imported
and module
, described on the the website. “Module mode” also works for pure Python code (eg, TKinter, Numpy, MatPlotLib, Shapely, Trimesh, GraphWin, Pyglet, PyObjc, etc).
The editor uses ProcessBuilder to ‘run’ the source code and requires two parameters: a ‘cmdStr’ and a ‘filePath’. The ‘cmdStr’ for ‘imported’ requires the url to an executable file entitled py5-run-sketch
and the ‘module’ mode requires the url to a python
or python3
executable. On macOS ‘py5-run-sketch.exe’ looks like that shown below and is found at ‘/opt/miniconda3/bin’ on my system; ‘python3’ executable is located in the same folder. With Windows 11 those two executables are in different locations:“C:\Users\s\AppData\Local\Programs\Python\Python313\Scripts\py5-run-sketch.exe” for ‘py5-run-sketch’ and at “C:\Users\s\AppData\Local\Programs\Python\Python313\Python.exe” for ‘python’. Obviously these locations could be different on your system depending on how you have set it up. The main thing is that your code has to point to the the py5-run-sketch and python(3) executables on your system.
Before running the demo you will need to set ‘folderStr’ for the default folder you want the chooser to open and the ‘cmdStr’ for each mode based on your operating system. Below are a few simple demos which you may use to check each mode.
After py5 has been installed if the editor returns a ‘No module’ exception for code that you are trying to run then try using pip(3) install missingModule
from the command line (Terminal on macOS); if the module is available it will be downloaded to your system and immediately available to your code.
The auxiliary window in the editor is for comparing code or temporarily parking code for copy/paste procedures; visibility is toggled by the checkbox. The editor is not document based, ie, you can only have one instance of it open at any one time, hence the need for an auxiliary window if you want to have two sketches visible simultaneously.
The editor source code may be exported by Processing into a stand-alone app. With macOS the app icon can be changed to whatever image you wish by right clicking on the icon, selecting ‘Get Info’ and then drag and dropping your image onto the app icon in the upper left corner. You should see a “+” sign when the app accepts the new icon. I have not tried changing the exported Processing app icon on the other two platforms.
The editor is versatile enough that it could be modified to build and run source code for other languages; RSyntaxTextArea reportedly supports about 40 languages.
Why python?
Python prides itself in being simple compared to some other languages. For example instead of: JButton myBtn = new JButton(); you can write: myBtn = JButton() and forget the semicolon.
Another advantage is that there are no curly braces for code blocks; indentation is used instead, usually four spaces per block (two spaces also works).
Py5 allows us to use Processing-like syntax in Python which is a big advantage and decreases the learning curve. The addition of ‘imported mode’ means that you don’t have to use a bunch of ‘py5.’ prefixes like in ‘module mode’ and you have access to JavaFX with all of it’s nice controls and graphics with high resolution. In order to get JavaFX to work I found py5_tools.processing.download_library('JavaFX')
to be helpful. Swing and Control P5 components are also supported. It’s a nice way to write code once you get past the setup.
Source code with 5 separate tabs:
// **** Describes how to use RSyntaxTextArea for syntax highlighting **** //
//https://discourse.processing.org/t/rsyntaxtextarea-with-swing-in-processing/44725
// **** Download jar file for rsyntaxtextarea **** //
//https://sourceforge.net/projects/rsyntaxtextarea/files%2Frsyntaxtextarea%2F1.5.1%2Frsyntaxtextarea_1.5.1.zip/download
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import org.fife.ui.rsyntaxtextarea.*;
import org.fife.ui.rtextarea.*;
import java.io.*;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.swing.text.BadLocationException;
import static java.awt.event.InputEvent.META_DOWN_MASK;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
JFrame frame;
JScrollPane txtScrlPane;
RSyntaxTextArea txtArea;
RSyntaxTextArea auxTextArea;
JTextArea logArea;
JSplitPane splitPane;
JScrollPane logScrlPane;
JScrollPane tbarScrlPane;
JToolBar toolbar;
JButton fileBtn;
JPopupMenu pupMenu;
JMenu openRecentMenu;
List<String> recentFiles = new ArrayList<>();
JLabel folderSelectionLabel;
JRadioButton editBtn;
JFrame aux;
File file;
Process process = null;
final int _wndW = 770;
final int _wndH = 700;
// **** Change folderStr and cmdStr for your system **** //
// **** Two command strings, one for each mode: Lines 474,478,480 **** //
String folderStr = "/Users/s/py5_code";
String cmdStr = "";
String filePath = "";
File dirFile;
void openFile(String selectedFilePath) {
frame.setTitle(selectedFilePath);
filePath = selectedFilePath; //update filePath
Path filePath2 = Paths.get(filePath);
Path dirPath = filePath2.getParent();
dirFile = dirPath.toFile();
txtArea.setText("");
try {
BufferedReader buffer = new BufferedReader(new FileReader(selectedFilePath));
String s = "", s1 = "";
while ((s = buffer.readLine())!= null) {
s1 += s + "\n";
}
txtArea.setText(s1);
buffer.close();
editBtn.setSelected(false);
}
catch (Exception ex) {
logArea.append(ex + "\n");
}
}
void updateOpenRecentMenu() {
openRecentMenu.removeAll();
if (recentFiles.isEmpty()) {
JMenuItem noRecentItem = new JMenuItem("No Recent Files");
noRecentItem.setEnabled(false); // Disable if no recent files
openRecentMenu.add(noRecentItem);
} else {
for (String filePath : recentFiles) {
JMenuItem recentFileItem = new JMenuItem(filePath);
recentFileItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
openFile(filePath);
}
}
);
openRecentMenu.add(recentFileItem);
}
}
}
void addRecentFile(String filePath) {
if (!recentFiles.contains(filePath)) {
recentFiles.add(0, filePath); // Add to the beginning
if (recentFiles.size() > 10) { // Limit to 10 recent files
recentFiles.remove(recentFiles.size() - 1);
}
}
}
String fileExtension(String fileName) {
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1 || lastDotIndex == 0) {
return ""; // No extension
}
return fileName.substring(lastDotIndex + 1);
}
void openAction() {
JFileChooser fileChooser = new JFileChooser(folderStr);
int i = fileChooser.showOpenDialog(fileChooser);
if (i == JFileChooser.APPROVE_OPTION) {
file = fileChooser.getSelectedFile();
filePath = file.getPath();
if (!fileExtension(filePath).equals("py")) {
Object[] options = {"CANCEL" };
JOptionPane.showOptionDialog(frame, "Please make another selection.", "Editor requires .py extension.",
JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
return;
}
dirFile = fileChooser.getCurrentDirectory();
addRecentFile(filePath);
updateOpenRecentMenu();
frame.setTitle(filePath);
try {
BufferedReader buffer = new BufferedReader(new FileReader(filePath));
String s = "", s1 = "";
while ((s = buffer.readLine())!= null) {
s1 += s + "\n";
}
txtArea.setText(s1);
buffer.close();
editBtn.setSelected(false);
}
catch (Exception ex) {
logArea.append(ex + "\n");
}
} else {
logArea.append("Open cancelled.");
}
}
void saveAction() {
if (filePath != null) {
try {
String content = txtArea.getText();
FileWriter fw = new FileWriter(filePath) ;
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
editBtn.setSelected(false);
}
catch (IOException e) {
logArea.append(e + "\n");
}
} else {
saveAsAction();
}
}
void saveAsAction() {
JFileChooser fileChooser = new JFileChooser(folderStr);
fileChooser.setSelectedFile(new File("untitled.py"));
int option = fileChooser.showSaveDialog(frame);
if (option == JFileChooser.APPROVE_OPTION) {
file = fileChooser.getSelectedFile();
filePath = file.getPath();
frame.setTitle(filePath);
if (file == null) {
return;
}
} else {
logArea.append("SaveAs cancelled.");
}
try {
String content = txtArea.getText();
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file) ;
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
frame.setTitle(filePath);
editBtn.setSelected(false);
}
catch (IOException e) {
logArea.append(e + "\n");
}
}
// ===== Sets default folder to open ===== //
void folderSelectionAction() {
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Select folder:");
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
// chooser.setAcceptAllFileFilterUsed(false);
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
file = chooser.getSelectedFile();
File dir = chooser.getCurrentDirectory();
folderStr = dir + "/" + file.getName();
folderSelectionLabel.setText(dir + "/" + file.getName());
} else {
folderSelectionLabel.setText("No selection.");
}
}
void runAction() {
ProcessBuilder processBuilder = new ProcessBuilder(cmdStr, filePath);
processBuilder.environment().put("PYTHONUNBUFFERED", "TRUE");
processBuilder.directory(dirFile);
try {
process = processBuilder.start();
BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String outStr = "";
while ((outStr = stdIn.readLine()) != null) {
logArea.append(outStr + "\n");
}
String errStr = "";
while ((errStr = stdErr.readLine()) != null) {
logArea.append(errStr + "\n");
}
// Wait for the process to complete and get its exit value
int exitCode = process.waitFor();
logArea.append("\nProcess exited with code: " + exitCode + "\n");
}
catch (IOException | InterruptedException e) {
logArea.append(e + "\n");
}
}
void runBtnAction() {
if (editBtn.isSelected()) {
saveAction();
}
thread("runAction");
}
void stopBtnAction() {
process.destroy();
}
void clearAllAction() {
txtArea.setText("");
logArea.setText("");
frame.setTitle("");
editBtn.setSelected(false);
}
void clearLogAction() {
logArea.setText("");
}
void auxOpenAction() {
JFileChooser fileChooser = new JFileChooser(folderStr);
int i = fileChooser.showOpenDialog(fileChooser);
if (i == JFileChooser.APPROVE_OPTION) {
File auxFile = fileChooser.getSelectedFile();
String auxFilePath = auxFile.getPath();
aux.setTitle(auxFilePath);
try {
BufferedReader buffer = new BufferedReader(new FileReader(auxFilePath));
String s = "", s1 = "";
while ((s = buffer.readLine())!= null) {
s1 += s + "\n";
}
auxTextArea.setText(s1);
buffer.close();
}
catch (Exception ex) {
auxTextArea.append(ex + "\n");
}
} else {
auxTextArea.append("Open cancelled.");
}
}
void auxWndAction() {
aux = new JFrame();
aux.setBounds(100, 100, 600, 400);
aux.setResizable(true);
aux.setVisible(true);
auxTextArea = new RSyntaxTextArea();
auxTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_PYTHON);
auxTextArea.setFont(new Font("Menlo", Font.PLAIN, 14));
RTextScrollPane auxTextScrlPane = new RTextScrollPane(auxTextArea);
auxTextArea.setEditable(true);
auxTextArea.setLineWrap(false);
auxTextArea.setWrapStyleWord(true);
aux.add(auxTextScrlPane);
auxTextArea.repaint();
// ==== Aux Toolbar ====== //
JToolBar auxTbar = new JToolBar();
auxTbar.setFloatable(false);
auxTbar.setPreferredSize(new Dimension(_wndW-20, 40));
aux.add(auxTbar, BorderLayout.PAGE_START);
// ====== Aux Open Button ===== //
JButton auxOpenBtn = new JButton("Open file:");
auxTbar.add(auxOpenBtn);
auxOpenBtn.addActionListener(e-> {
auxOpenAction();
}
);
}
void quitBtnAction() {
exit();
}
void toolBar() {
toolbar = new JToolBar();
toolbar.setLayout(null);
toolbar.setFloatable(false);
JScrollPane tbarScrlPane = new JScrollPane(toolbar);
toolbar.setPreferredSize(new Dimension(_wndW-20, 50));
frame.add(tbarScrlPane, BorderLayout.PAGE_START);
}
void txtArea() {
txtArea = new RSyntaxTextArea();
txtArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_PYTHON);
txtArea.setFont(new Font("Menlo", Font.PLAIN, 14));
txtScrlPane = new RTextScrollPane(txtArea);
txtArea.setEditable(true);
txtArea.setLineWrap(false);
txtArea.setWrapStyleWord(true);
txtArea.repaint();
txtArea.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
editBtn.setSelected(true);
}
@Override
public void removeUpdate(DocumentEvent e) {
editBtn.setSelected(true);
}
@Override
public void changedUpdate(DocumentEvent e) {
// For attribute changes, not text changes
}
}
);
frame.add(txtScrlPane);
}
void logArea() {
logArea = new JTextArea();
logScrlPane = new JScrollPane(logArea);
logArea.setEditable(true);
logArea.setFont(new Font("Menlo", Font.PLAIN, 12));
logArea.setLineWrap(false);
logArea.setWrapStyleWord(true);
logArea.repaint();
}
//Create a split pane with the two scroll panes in it.
void splitPane() {
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, txtScrlPane, logScrlPane);
splitPane.setBounds(0, 0, _wndW, _wndH - 30);
splitPane.setOneTouchExpandable(true);
frame.add(splitPane);
splitPane.setDividerLocation(_wndH - 210);
splitPane.repaint();
//Provide minimum sizes for the two components in the split pane
Dimension minimumSize = new Dimension(_wndW, 50);
txtScrlPane.setMinimumSize(minimumSize);
logScrlPane.setMinimumSize(minimumSize);
}
void popUpMenu() {
pupMenu = new JPopupMenu();
fileBtn = new JButton("File");
fileBtn.setBounds(10, 10, 60, 24);
fileBtn.setToolTipText("File menu.");
toolbar.add(fileBtn);
fileBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String s = e.getActionCommand();
if (s.equals("File")) {
// add the popup to the frame
pupMenu.show(toolbar, 10, 35);
}
}
}
);
// ==== Open ==== //
JMenuItem mItemOpen = new JMenuItem("Open...");
mItemOpen.addActionListener(e-> {
openAction();
}
);
pupMenu.add(mItemOpen);
// ==== Open Recent ==== //
openRecentMenu = new JMenu("Open Recent");
pupMenu.add(openRecentMenu);
// ==== Save ==== //
JMenuItem mItemSave = new JMenuItem("Save");
mItemSave.addActionListener(e-> {
saveAction();
}
);
mItemSave.setAccelerator(KeyStroke.getKeyStroke('S', META_DOWN_MASK));
pupMenu.add(mItemSave);
// ==== SaveAs ==== //
JMenuItem mItemSaveAs = new JMenuItem("SaveAs...");
mItemSaveAs.addActionListener(e-> {
saveAsAction();
}
);
pupMenu.add(mItemSaveAs);
}
void folderSelectionLabel() {
folderSelectionLabel = new JLabel();
folderSelectionLabel.setHorizontalAlignment(JLabel.LEFT);
folderSelectionLabel.setBounds(85, 25, 320, 24);
folderSelectionLabel.setText(folderStr);
folderSelectionLabel.setToolTipText("Folder selected.");
toolbar.add(folderSelectionLabel);
folderSelectionLabel.repaint();
}
void folderSelectionBtn() {
JButton folderSelectionBtn = new JButton("Folder->open");
folderSelectionBtn.setBounds(85, 5, 120, 24);
folderSelectionBtn.setToolTipText("Select folder to open.");
toolbar.add(folderSelectionBtn);
folderSelectionBtn.addActionListener(e-> {
folderSelectionAction();
}
);
}
void modeLabel() {
JLabel modeLabel = new JLabel();
modeLabel.setHorizontalAlignment(JLabel.CENTER);
modeLabel.setBounds(220, 5, 45, 24);
modeLabel.setText("Mode:");
toolbar.add(modeLabel);
modeLabel.repaint();
}
void modeCombo() {
String mode[]={"Imported py5", "Module/python"};
JComboBox comboBox = new JComboBox(mode);
comboBox.setBounds(265, 5, 150, 24);
comboBox.setToolTipText("Modes");
comboBox.setSelectedIndex(0);
// **** Change cmdStr for your system **** //
cmdStr = "/opt/miniconda3/bin/py5-run-sketch";
comboBox.addActionListener(e -> {
int cboxSelection = comboBox.getSelectedIndex();
if (cboxSelection == 0) {
cmdStr = "/opt/miniconda3/bin/py5-run-sketch";
} else {
cmdStr = "/opt/miniconda3/bin/python3";
}
}
);
toolbar.add(comboBox);
}
void runBtn() {
runBtn = new RunButton();
runBtn.setBounds(420, 5, 34, 34);
runBtn.setToolTipText("Run code.");
runBtn.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
runBtnAction();
}
}
);
toolbar.add(runBtn);
runBtn.repaint();
}
void stopBtn() {
stopBtn = new StopButton();
stopBtn.setBounds(470, 5, 34, 34);
stopBtn.setToolTipText("Stop app.");
stopBtn.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
stopBtnAction();
}
}
);
toolbar.add(stopBtn);
stopBtn.repaint();
}
void clearAllBtn() {
clearAllBtn = new ClearAllButton();
clearAllBtn.setBounds(520, 5, 34, 34);
clearAllBtn.setToolTipText("Clear All.");
clearAllBtn.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
clearAllAction();
}
}
);
toolbar.add(clearAllBtn);
clearAllBtn.repaint();
}
void clearLogBtn() {
clearLogBtn = new ClearLogButton();
clearLogBtn.setBounds(570, 5, 34, 34);
clearLogBtn.setToolTipText("Clear Log.");
clearLogBtn.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
clearLogAction();
}
}
);
toolbar.add(clearLogBtn);
clearLogBtn.repaint();
}
void editBtn() {
editBtn = new JRadioButton("Edited");
editBtn.setBounds(620, 0, 80, 30);
editBtn.setToolTipText("Text has changed.");
toolbar.add(editBtn);
editBtn.repaint();
}
void auxWnd() {
JCheckBox chkBox = new JCheckBox("AuxWnd");
chkBox.setBounds(610, 25, 90, 24);
toolbar.add(chkBox);
chkBox.setToolTipText("Toggle Auxilliary window.");
chkBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent evt) {
if (evt.getStateChange()==1) {
auxWndAction();
} else {
aux.setVisible(false);
}
}
}
);
chkBox.repaint();
}
void quitBtn() {
quitBtn = new QuitButton();
quitBtn.setBounds(_wndW - 60, 5, 34, 34);
quitBtn.setToolTipText("Quit editor.");
quitBtn.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
quitBtnAction();
}
}
);
toolbar.add(quitBtn);
quitBtn.repaint();
}
void buildWnd() {
toolBar();
txtArea(); // RSyntaxTextArea
logArea();
splitPane();
popUpMenu();
folderSelectionLabel();
folderSelectionBtn();
modeLabel();
modeCombo();
runBtn();
stopBtn();
clearAllBtn();
clearLogBtn();
editBtn();
auxWnd();
quitBtn();
frame.setVisible(true);
}
void setup() {
surface.setVisible(false);
frame = new JFrame();
frame.setBounds(100, 100, _wndW, _wndH);
frame.setTitle("Python/Py5 Editor");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if (editBtn.isSelected()) {
int option = JOptionPane.showConfirmDialog(frame, "Save changes?", "Exit Confirmation", JOptionPane.YES_NO_OPTION);
if (option == JOptionPane.YES_OPTION) {
saveAction();
} else {
frame.dispose(); // Dispose the frame
exit(); // Exit the application
}
} else {
exit();
}
}
}
);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
buildWnd(); // Build components on the EventDispatchThread(EDT).
}
}
);
}
Tab1: “ClearAll”:
class ClearAllButton extends JPanel {
int radius = 13; // Size of Btn
ClearAllButton() {
setOpaque(false);
}
void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g);
BasicStroke bs = new BasicStroke(8.0);
Font myFont = new Font("Menlo",Font.BOLD,22);
g2.setStroke(bs);
g2.setColor (Color.lightGray);
g2.drawOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor (new Color(255, 255, 255));
g2.fillOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor(Color.darkGray);
g2.setFont(myFont);
g2.drawString("X",11,25);
}
}
ClearAllButton clearAllBtn;
Tab2: “ClearLog”
class ClearLogButton extends JPanel {
int radius = 13; // Size of Btn
ClearLogButton() {
setOpaque(false);
}
void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g);
BasicStroke bs = new BasicStroke(8.0);
Font myFont = new Font("Menlo",Font.BOLD,18);
g2.setStroke(bs);
g2.setColor (Color.lightGray);
g2.drawOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor (new Color(255, 255, 255));
g2.fillOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor(Color.darkGray);
g2.setFont(myFont);
g2.drawString("x",12,23);
}
}
ClearLogButton clearLogBtn;
Tab3: “Quit”:
class QuitButton extends JPanel {
int radius = 13; // Size of Btn
QuitButton() {
setOpaque(false);
}
void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g);
BasicStroke bs = new BasicStroke(8.0);
Font myFont = new Font("Menlo",Font.BOLD,22);
g2.setStroke(bs);
g2.setColor(Color.lightGray);
g2.drawOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor(new Color(66,66,66));
g2.fillOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor(Color.white);
g2.setFont(myFont);
g2.drawString("Q",11,25);
}
}
QuitButton quitBtn;
Tab4: “Run”:
class RunButton extends JPanel {
int radius = 13; // Size of Btn
// Coordinates for runBtn triangle
int x[] = {16, 16, 18};
int y[] = {16, 18, 17};
RunButton() {
setOpaque(false);
}
void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g);
BasicStroke bs = new BasicStroke(8.0);
g2.setStroke(bs);
g2.setColor (Color.lightGray);
g2.drawOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor (new Color(24, 70, 183));
g2.fillOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor(Color.white);
g2.drawPolygon(x, y, 3);
}
}
RunButton runBtn;
Tab5: “Stop”:
class StopButton extends JPanel {
int radius = 13; // Size of Btn
StopButton() {
setOpaque(false);
}
void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g);
BasicStroke bs = new BasicStroke(8.0);
g2.setStroke(bs);
g2.setColor (Color.lightGray);
g2.drawOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor (new Color(255, 255, 255));
g2.fillOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor(Color.red);
g.fillRect(11,11,12,12);
}
}
StopButton stopBtn;
Py5 test code for ‘imported mode’ (py5 automatically imported):
def setup():
size(300, 200)
rect_mode(CENTER)
def draw():
rect(mouse_x, mouse_y, 10, 10)
Py5 test code for ‘module mode’ (requires py5 to be imported and uses py5. prefixes):
import py5
def setup():
py5.size(300, 200)
py5.rect_mode(py5.CENTER)
def draw():
py5.rect(py5.mouse_x, py5.mouse_y, 10, 10)
py5.run_sketch()
Python code example for ‘module mode’:
from tkinter import *
wnd = Tk()
C = Canvas(wnd, bg = "yellow", height = 250, width = 300)
myLine = C.create_line(108, 120, 320, 40, fill = "green")
myArc = C.create_arc(180, 150, 80, 210, start=0, extent=220, fill = "red")
oval = C.create_oval(80, 30, 140, 150, fill = "blue")
C.pack()
mainloop()
JavaFX test code for ‘imported mode’:
import javafx
def myBtnAction(event):
print("btn was hit.")
def setup():
size(300, 200, FX2D)
window_title('JavaFX Button')
canvas = get_surface().get_native()
root = canvas.getParent()
btn = javafx.scene.control.Button('Push Me')
btn.setOnAction(myBtnAction)
root.getChildren().add(btn)
Output:
App Icon Change example:
py5-run-sketch.exe for MacOS: