Today, I tested this python script with PyQt6 to create this matrix data for memory game by click on canvas.
I add few lines of source code and I create the memory game window to test that matrix:

Python tutorials with source code, examples, guides, and tips and tricks for Windows and Linux development.

python -m pip install nicegui
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Collecting nicegui
Downloading nicegui-3.9.0-py3-none-any.whl.metadata (11 kB)
...
Successfully installed aiofiles-25.1.0 httptools-0.7.1 ifaddr-0.2.0 lxml-html-clean-0.4.4 markdown2-2.5.5 nicegui-3.9.0pip install mt940==0.8.0

# -*- coding: utf-8 -*-
import sys
import schemdraw
import schemdraw.elements as elm
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout, QScrollArea
from PyQt6.QtGui import QPixmap
def draw_bistabil_grid(state):
"""
state = 0 -> switch spre R3, LED1 aprins
state = 1 -> switch spre R4, LED2 aprins
"""
with schemdraw.Drawing(file='bistabil_grid.png', show=False) as d:
d.config(unit=2.0)
# Y COORDINATES
y_led = 0.0
y_r1 = -2.0
y_rfb = -4.0
y_swT = -7.0
y_gnd = -10.0
# X COORDINATES
x_left = -6.0
x_right = 6.0
x_t1 = -3.0
x_t2 = 3.0
x_sw = 0.0
# COLORS
col_T1 = 'green'
col_T2 = 'orange'
# LED-uri colorate în funcție de state
led1_color = 'red' if state == 0 else 'gray'
led2_color = 'red' if state == 1 else 'gray'
# 1. LED1 and LED2
led1 = d.add(elm.LED().down().at((x_left, y_led)).color(led1_color).label('LED1'))
led2 = d.add(elm.LED().down().at((x_right, y_led)).color(led2_color).label('LED2'))
# 2. R1 and R2
r1 = d.add(elm.Resistor().down().at(led1.end).label('R1'))
r2 = d.add(elm.Resistor().down().at(led2.end).label('R2'))
n_r1 = d.add(elm.Dot().at(r1.end))
n_r2 = d.add(elm.Dot().at(r2.end))
# 3. R3 and R4 (feedback)
d.add(elm.Line().at(n_r1.center).to((x_t1, y_rfb)))
r3 = d.add(elm.Resistor().right().at((x_t1, y_rfb)).label('R3'))
n_r3 = d.add(elm.Dot().at(r3.end))
d.add(elm.Line().at(n_r2.center).to((x_t2, y_rfb)))
r4 = d.add(elm.Resistor().left().at((x_t2, y_rfb)).label('R4'))
n_r4 = d.add(elm.Dot().at(r4.end))
# 4. Transistors
t1 = d.add(
elm.BjtNpn()
.left()
.flip()
.at((x_t1, y_swT))
.anchor('base')
.label('T1')
)
t2 = d.add(
elm.BjtNpn()
.right()
.at((x_t2, y_swT))
.anchor('base')
.label('T2')
)
# BASE CONNECTIONS
d.add(elm.Line().at(n_r4.center).to(t1.base).color(col_T1))
d.add(elm.Line().at(n_r3.center).to(t2.base).color(col_T2))
# COLLECTOR CONNECTIONS
d.add(elm.Line().at(t1.collector).to(n_r1.center).color(col_T1))
d.add(elm.Line().at(t2.collector).to(n_r2.center).color(col_T2))
# -------------------------------
# 5. SWITCH SPDT DESENAT MANUAL
# -------------------------------
# Punctele a, b, c
a = d.add(elm.Dot().at((x_sw - 1, y_swT)))
b = d.add(elm.Dot().at((x_sw, y_swT)))
c = d.add(elm.Dot().at((x_sw + 1, y_swT)))
# Linii FIXE către R3 și R4
d.add(elm.Line().at(n_r3.center).to(a.center))
d.add(elm.Line().at(n_r4.center).to(c.center))
# Linia FIXĂ către GND
d.add(elm.Line().at(b.center).to((b.center[0], y_gnd)))
# Brațul mobil (UNICA linie care se mișcă)
if state == 0:
d.add(elm.Line().at(b.center).to(a.center))
else:
d.add(elm.Line().at(b.center).to(c.center))
# 6. Ground bus
d.add(
elm.Line()
.at((x_left - 2, y_gnd))
.to((x_right + 2, y_gnd))
.label('0V', loc='bottom')
)
d.add(elm.Ground().at((x_left - 2, y_gnd)))
# EMITTERS TO GND
d.add(elm.Line().at(t1.emitter).to((t1.emitter[0], y_gnd)).color(col_T1))
d.add(elm.Line().at(t2.emitter).to((t2.emitter[0], y_gnd)).color(col_T2))
class BistabilGUI(QWidget):
def __init__(self):
super().__init__()
self.state = 0
self.setWindowTitle("Bistabil Animat – Schema completă")
# Scroll area
self.scroll = QScrollArea()
self.label = QLabel()
self.scroll.setWidget(self.label)
self.scroll.setWidgetResizable(True)
self.button = QPushButton("Comută switch-ul")
self.button.clicked.connect(self.toggle_switch)
layout = QVBoxLayout()
layout.addWidget(self.scroll)
layout.addWidget(self.button)
self.setLayout(layout)
self.update_image()
def toggle_switch(self):
self.state = 1 - self.state
self.update_image()
def update_image(self):
draw_bistabil_grid(self.state)
pix = QPixmap("bistabil_grid.png")
self.label.setPixmap(pix)
if __name__ == "__main__":
app = QApplication(sys.argv)
gui = BistabilGUI()
gui.show()
sys.exit(app.exec())
pip install schemdraw
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Collecting schemdraw
Downloading schemdraw-0.22-py3-none-any.whl.metadata (2.2 kB)
Downloading schemdraw-0.22-py3-none-any.whl (148 kB)
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Installing collected packages: schemdraw
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Successfully installed schemdraw-0.22

pip install manim
...
Successfully installed audioop-lts-0.2.2 av-16.1.0 cloup-3.0.8 glcontext-3.0.0 isosurfaces-0.1.2 manim-0.20.1 manimpango-0.6.1 mapbox-earcut-2.0.0 moderngl-5.12.0 moderngl-window-3.1.1 pycairo-1.29.0 pydub-0.25.1 pyglet-2.1.13 pyglm-2.8.3 screeninfo-0.8.1 skia-pathops-0.9.2 srt-3.5.3import subprocess
import sys
import datetime
log_file = "upgrade_log.txt"
def log(msg):
with open(log_file, "a", encoding="utf-8") as f:
f.write(msg + "\n")
print(msg)
def get_installed_packages():
result = subprocess.run(
[sys.executable, "-m", "pip", "list", "--format=freeze"],
capture_output=True,
text=True
)
lines = result.stdout.strip().split("\n")
packages = []
for line in lines:
if "@" in line: # skip direct URL installs
pkg = line.split("@")[0]
else:
pkg = line.split("==")[0]
packages.append(pkg)
return sorted(packages)
def upgrade_package(package):
log(f"\n=== Updating: {package} ===")
try:
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", package])
log(f"[OK] Updated: {package}")
except subprocess.CalledProcessError:
log(f"[FAILED] Could not update: {package}")
def main():
log(f"\n--- Upgrade started at {datetime.datetime.now()} ---\n")
packages = get_installed_packages()
log(f"Found {len(packages)} packages.\n")
for pkg in packages:
upgrade_package(pkg)
log(f"\n--- Upgrade finished at {datetime.datetime.now()} ---\n")
if __name__ == "__main__":
main()python -m pip install build123d
Collecting build123d
Downloading build123d-0.10.0-py3-none-any.whl.metadata (6.7 kB)
...
Successfully installed anytree-2.13.0 asttokens-3.0.1 build123d-0.10.0 cadquery-ocp-7.8.1.1.post1 cadquery_ocp_proxy-7.9.3.1 cadquery_vtk-9.3.1 decorator-5.2.1 executing-2.2.1 ezdxf-1.4.3 ipython-9.10.0 ipython-pygments-lexers-1.1.1 lib3mf-2.5.0 matplotlib-inline-0.2.1 ocp_gordon-0.2.0 ocpsvg-0.5.0 pure-eval-0.2.3 stack_data-0.6.3 svgelements-1.9.6 svgpathtools-1.7.2 traitlets-5.14.3 trianglesolver-1.2 webcolors-24.8.0