Skip to content

Commit f6dc041

Browse files
committed
examples: add a QtWidgets integration example.
1 parent e9767d8 commit f6dc041

4 files changed

Lines changed: 391 additions & 0 deletions

File tree

‎examples/burning_tool/__init__.py‎

Whitespace-only changes.

‎examples/burning_tool/dialog.ui‎

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>Memory25QDialog</class>
4+
<widget class="QDialog" name="Memory25QDialog">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>764</width>
10+
<height>150</height>
11+
</rect>
12+
</property>
13+
<property name="sizePolicy">
14+
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
15+
<horstretch>0</horstretch>
16+
<verstretch>0</verstretch>
17+
</sizepolicy>
18+
</property>
19+
<property name="minimumSize">
20+
<size>
21+
<width>0</width>
22+
<height>150</height>
23+
</size>
24+
</property>
25+
<property name="windowTitle">
26+
<string>Glasgow Flash Burning Tool</string>
27+
</property>
28+
<property name="modal">
29+
<bool>true</bool>
30+
</property>
31+
<layout class="QVBoxLayout" name="verticalLayout">
32+
<item>
33+
<layout class="QHBoxLayout" name="horizontalLayout">
34+
<property name="sizeConstraint">
35+
<enum>QLayout::SizeConstraint::SetMinimumSize</enum>
36+
</property>
37+
<item>
38+
<widget class="QLineEdit" name="fileNameLineEdit">
39+
<property name="minimumSize">
40+
<size>
41+
<width>300</width>
42+
<height>0</height>
43+
</size>
44+
</property>
45+
</widget>
46+
</item>
47+
<item>
48+
<widget class="QPushButton" name="locateFileButton">
49+
<property name="sizePolicy">
50+
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
51+
<horstretch>0</horstretch>
52+
<verstretch>0</verstretch>
53+
</sizepolicy>
54+
</property>
55+
<property name="text">
56+
<string>...</string>
57+
</property>
58+
</widget>
59+
</item>
60+
<item>
61+
<widget class="QPushButton" name="jedecButton">
62+
<property name="sizePolicy">
63+
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
64+
<horstretch>0</horstretch>
65+
<verstretch>0</verstretch>
66+
</sizepolicy>
67+
</property>
68+
<property name="text">
69+
<string>ID</string>
70+
</property>
71+
<property name="default">
72+
<bool>true</bool>
73+
</property>
74+
</widget>
75+
</item>
76+
<item>
77+
<widget class="QPushButton" name="writeButton">
78+
<property name="sizePolicy">
79+
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
80+
<horstretch>0</horstretch>
81+
<verstretch>0</verstretch>
82+
</sizepolicy>
83+
</property>
84+
<property name="text">
85+
<string>Write</string>
86+
</property>
87+
</widget>
88+
</item>
89+
<item>
90+
<widget class="QPushButton" name="verifyButton">
91+
<property name="sizePolicy">
92+
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
93+
<horstretch>0</horstretch>
94+
<verstretch>0</verstretch>
95+
</sizepolicy>
96+
</property>
97+
<property name="text">
98+
<string>Verify</string>
99+
</property>
100+
</widget>
101+
</item>
102+
<item>
103+
<widget class="QPushButton" name="readButton">
104+
<property name="sizePolicy">
105+
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
106+
<horstretch>0</horstretch>
107+
<verstretch>0</verstretch>
108+
</sizepolicy>
109+
</property>
110+
<property name="text">
111+
<string>Read</string>
112+
</property>
113+
</widget>
114+
</item>
115+
</layout>
116+
</item>
117+
<item>
118+
<widget class="QProgressBar" name="progressBar">
119+
<property name="value">
120+
<number>0</number>
121+
</property>
122+
<property name="textVisible">
123+
<bool>false</bool>
124+
</property>
125+
<property name="invertedAppearance">
126+
<bool>false</bool>
127+
</property>
128+
</widget>
129+
</item>
130+
<item>
131+
<widget class="QLabel" name="statusLabel">
132+
<property name="text">
133+
<string>ready</string>
134+
</property>
135+
<property name="scaledContents">
136+
<bool>false</bool>
137+
</property>
138+
<property name="alignment">
139+
<set>Qt::AlignmentFlag::AlignCenter</set>
140+
</property>
141+
</widget>
142+
</item>
143+
</layout>
144+
</widget>
145+
<resources/>
146+
<connections/>
147+
</ui>

‎examples/burning_tool/main.py‎

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
"""An example of a Qt GUI that provides a simple, touchscreen-compatible factory flashing interface.
2+
3+
While the Glasgow Interface Explorer provides a very flexible CLI, it is rather too complicated
4+
for simple, repetitive tasks like programming a QSPI memory. This example demonstrates how to
5+
construct a task-specific GUI.
6+
"""
7+
8+
import os
9+
import asyncio
10+
11+
from glasgow.support import logging
12+
from glasgow.support.progress import QtWidgetsProgressImpl
13+
from glasgow.hardware.assembly import HardwareAssembly
14+
from glasgow.applet.memory._25q import Memory25QInterface
15+
from glasgow.database.jedec import jedec_mfg_name_from_bytes
16+
from glasgow.main import run_async_qtwidgets, setup_logging
17+
18+
from PySide6.QtWidgets import QApplication, QDialog, QFileDialog
19+
20+
from ui_dialog import Ui_Memory25QDialog
21+
22+
23+
logger = logging.getLogger(__name__)
24+
25+
26+
class Memory25QDialog(QDialog, Ui_Memory25QDialog):
27+
def __init__(self):
28+
super().__init__()
29+
30+
self.setupUi(self)
31+
QtWidgetsProgressImpl(self.progressBar, self.statusLabel).register()
32+
33+
self.locateFileButton.clicked.connect(self.locateFile)
34+
self.jedecButton.clicked.connect(lambda: asyncio.ensure_future(self.jedec()))
35+
self.writeButton.clicked.connect(lambda: asyncio.ensure_future(self.write()))
36+
self.verifyButton.clicked.connect(lambda: asyncio.ensure_future(self.verify()))
37+
self.readButton.clicked.connect(lambda: asyncio.ensure_future(self.read()))
38+
39+
def locateFile(self):
40+
fileName, _selectedFilter = QFileDialog.getSaveFileName(self,
41+
caption="Open Binary File", filter="Binary Files (*)",
42+
options=QFileDialog.Option.DontConfirmOverwrite)
43+
self.fileNameLineEdit.setText(fileName)
44+
45+
def setEnabled(self, enabled: bool):
46+
self.fileNameLineEdit.setEnabled(enabled)
47+
self.locateFileButton.setEnabled(enabled)
48+
self.jedecButton.setEnabled(enabled)
49+
self.writeButton.setEnabled(enabled)
50+
self.verifyButton.setEnabled(enabled)
51+
self.readButton.setEnabled(enabled)
52+
53+
async def withAssembly(self, operation):
54+
try:
55+
self.setEnabled(False)
56+
57+
self.progressBar.setValue(0)
58+
self.statusLabel.setText("initializing...")
59+
60+
assembly = await HardwareAssembly.find_device()
61+
assembly.use_voltage({"A": 3.3})
62+
mem_iface = Memory25QInterface(logger, assembly, cs="A0", sck="A1", io="A2:5")
63+
64+
async with assembly:
65+
await mem_iface.qspi.clock.set_frequency(12_000_000)
66+
await mem_iface.initialize()
67+
await operation(mem_iface)
68+
69+
except Exception as e:
70+
self.statusLabel.setText(f"error: {e}")
71+
72+
finally:
73+
self.setEnabled(True)
74+
75+
async def jedec(self):
76+
async def operation(mem_iface: Memory25QInterface):
77+
jedec_mfg_id, device_id = await mem_iface.jedec_id()
78+
jedec_mfg_name = jedec_mfg_name_from_bytes([jedec_mfg_id]) or "unknown"
79+
80+
self.statusLabel.setText(
81+
f"vendor {jedec_mfg_name} ({jedec_mfg_id:02x}) device {device_id:04x}")
82+
83+
await self.withAssembly(operation)
84+
85+
async def write(self):
86+
async def operation(mem_iface: Memory25QInterface):
87+
with open(self.fileNameLineEdit.text(), "rb") as f:
88+
data = f.read()
89+
90+
await mem_iface.write_data(0, data)
91+
self.statusLabel.setText("OK")
92+
93+
await self.withAssembly(operation)
94+
95+
async def verify(self):
96+
async def operation(mem_iface: Memory25QInterface):
97+
with open(self.fileNameLineEdit.text(), "rb") as f:
98+
golden = f.read()
99+
100+
actual = await mem_iface.read_data(0, len(golden))
101+
if golden == actual:
102+
self.statusLabel.setText("OK")
103+
else:
104+
self.statusLabel.setText("Fail!")
105+
106+
await self.withAssembly(operation)
107+
108+
async def read(self):
109+
async def operation(mem_iface: Memory25QInterface):
110+
if size := mem_iface.memory_size:
111+
data = await mem_iface.read_data(0, size)
112+
else:
113+
raise ValueError("failed to identify memory size")
114+
115+
with open(self.fileNameLineEdit.text(), "wb") as f:
116+
f.write(data)
117+
118+
await self.withAssembly(operation)
119+
120+
121+
@run_async_qtwidgets
122+
async def main():
123+
setup_logging(level="DEBUG")
124+
125+
Memory25QDialog().show()

‎examples/burning_tool/ui_dialog.py‎

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# -*- coding: utf-8 -*-
2+
3+
################################################################################
4+
## Form generated from reading UI file 'dialogrzLvaw.ui'
5+
##
6+
## Created by: Qt User Interface Compiler version 6.8.2
7+
##
8+
## WARNING! All changes made in this file will be lost when recompiling UI file!
9+
################################################################################
10+
11+
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
12+
QMetaObject, QObject, QPoint, QRect,
13+
QSize, QTime, QUrl, Qt)
14+
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
15+
QFont, QFontDatabase, QGradient, QIcon,
16+
QImage, QKeySequence, QLinearGradient, QPainter,
17+
QPalette, QPixmap, QRadialGradient, QTransform)
18+
from PySide6.QtWidgets import (QApplication, QDialog, QHBoxLayout, QLabel,
19+
QLayout, QLineEdit, QProgressBar, QPushButton,
20+
QSizePolicy, QVBoxLayout, QWidget)
21+
22+
class Ui_Memory25QDialog(object):
23+
def setupUi(self, Memory25QDialog):
24+
if not Memory25QDialog.objectName():
25+
Memory25QDialog.setObjectName(u"Memory25QDialog")
26+
Memory25QDialog.resize(764, 150)
27+
sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
28+
sizePolicy.setHorizontalStretch(0)
29+
sizePolicy.setVerticalStretch(0)
30+
sizePolicy.setHeightForWidth(Memory25QDialog.sizePolicy().hasHeightForWidth())
31+
Memory25QDialog.setSizePolicy(sizePolicy)
32+
Memory25QDialog.setMinimumSize(QSize(0, 150))
33+
Memory25QDialog.setModal(True)
34+
self.verticalLayout = QVBoxLayout(Memory25QDialog)
35+
self.verticalLayout.setObjectName(u"verticalLayout")
36+
self.horizontalLayout = QHBoxLayout()
37+
self.horizontalLayout.setObjectName(u"horizontalLayout")
38+
self.horizontalLayout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
39+
self.fileNameLineEdit = QLineEdit(Memory25QDialog)
40+
self.fileNameLineEdit.setObjectName(u"fileNameLineEdit")
41+
self.fileNameLineEdit.setMinimumSize(QSize(300, 0))
42+
43+
self.horizontalLayout.addWidget(self.fileNameLineEdit)
44+
45+
self.locateFileButton = QPushButton(Memory25QDialog)
46+
self.locateFileButton.setObjectName(u"locateFileButton")
47+
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
48+
sizePolicy1.setHorizontalStretch(0)
49+
sizePolicy1.setVerticalStretch(0)
50+
sizePolicy1.setHeightForWidth(self.locateFileButton.sizePolicy().hasHeightForWidth())
51+
self.locateFileButton.setSizePolicy(sizePolicy1)
52+
53+
self.horizontalLayout.addWidget(self.locateFileButton)
54+
55+
self.jedecButton = QPushButton(Memory25QDialog)
56+
self.jedecButton.setObjectName(u"jedecButton")
57+
sizePolicy1.setHeightForWidth(self.jedecButton.sizePolicy().hasHeightForWidth())
58+
self.jedecButton.setSizePolicy(sizePolicy1)
59+
60+
self.horizontalLayout.addWidget(self.jedecButton)
61+
62+
self.writeButton = QPushButton(Memory25QDialog)
63+
self.writeButton.setObjectName(u"writeButton")
64+
sizePolicy1.setHeightForWidth(self.writeButton.sizePolicy().hasHeightForWidth())
65+
self.writeButton.setSizePolicy(sizePolicy1)
66+
67+
self.horizontalLayout.addWidget(self.writeButton)
68+
69+
self.verifyButton = QPushButton(Memory25QDialog)
70+
self.verifyButton.setObjectName(u"verifyButton")
71+
sizePolicy1.setHeightForWidth(self.verifyButton.sizePolicy().hasHeightForWidth())
72+
self.verifyButton.setSizePolicy(sizePolicy1)
73+
74+
self.horizontalLayout.addWidget(self.verifyButton)
75+
76+
self.readButton = QPushButton(Memory25QDialog)
77+
self.readButton.setObjectName(u"readButton")
78+
sizePolicy1.setHeightForWidth(self.readButton.sizePolicy().hasHeightForWidth())
79+
self.readButton.setSizePolicy(sizePolicy1)
80+
81+
self.horizontalLayout.addWidget(self.readButton)
82+
83+
84+
self.verticalLayout.addLayout(self.horizontalLayout)
85+
86+
self.progressBar = QProgressBar(Memory25QDialog)
87+
self.progressBar.setObjectName(u"progressBar")
88+
self.progressBar.setValue(0)
89+
self.progressBar.setTextVisible(False)
90+
self.progressBar.setInvertedAppearance(False)
91+
92+
self.verticalLayout.addWidget(self.progressBar)
93+
94+
self.statusLabel = QLabel(Memory25QDialog)
95+
self.statusLabel.setObjectName(u"statusLabel")
96+
self.statusLabel.setScaledContents(False)
97+
self.statusLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
98+
99+
self.verticalLayout.addWidget(self.statusLabel)
100+
101+
102+
self.retranslateUi(Memory25QDialog)
103+
104+
self.jedecButton.setDefault(True)
105+
106+
107+
QMetaObject.connectSlotsByName(Memory25QDialog)
108+
# setupUi
109+
110+
def retranslateUi(self, Memory25QDialog):
111+
Memory25QDialog.setWindowTitle(QCoreApplication.translate("Memory25QDialog", u"Glasgow Flash Burning Tool", None))
112+
self.locateFileButton.setText(QCoreApplication.translate("Memory25QDialog", u"...", None))
113+
self.jedecButton.setText(QCoreApplication.translate("Memory25QDialog", u"ID", None))
114+
self.writeButton.setText(QCoreApplication.translate("Memory25QDialog", u"Write", None))
115+
self.verifyButton.setText(QCoreApplication.translate("Memory25QDialog", u"Verify", None))
116+
self.readButton.setText(QCoreApplication.translate("Memory25QDialog", u"Read", None))
117+
self.statusLabel.setText(QCoreApplication.translate("Memory25QDialog", u"ready", None))
118+
# retranslateUi
119+

0 commit comments

Comments
 (0)