mirror of
https://github.com/avrdudes/avrdude.git
synced 2026-09-28 20:09:12 +03:00
First skeleton of a very basic GUI
This adds a few .ui files (created with Qt designer), and a skeleton to create a basic GUI out of them. Only functionality so far is the selection of a device.
This commit is contained in:
91
src/python/about.ui
Normal file
91
src/python/about.ui
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>About</class>
|
||||
<widget class="QDialog" name="About">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>240</y>
|
||||
<width>341</width>
|
||||
<height>32</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTextBrowser" name="textBrowser">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>20</y>
|
||||
<width>381</width>
|
||||
<height>211</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This is a GUI demonstrator for the SWIG_Avrdude Python bindings.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>About</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>About</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
175
src/python/adgui.py
Executable file
175
src/python/adgui.py
Executable file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
builddir = None
|
||||
if os.name == 'posix':
|
||||
# Linux, *BSD, MacOS
|
||||
sysname = os.uname()[0].lower()
|
||||
builddir = f'build_{sysname}/src'
|
||||
elif os.name == 'nt':
|
||||
# Windows
|
||||
for candidate in ['build_msvc/src', 'build_mingw64/src']:
|
||||
if os.path.exists(candidate):
|
||||
builddir = candidate
|
||||
os.add_dll_directory(os.path.realpath(candidate))
|
||||
break
|
||||
|
||||
if builddir == None:
|
||||
print("Cannot determine build directory, module loading might fail.", file=sys.stderr)
|
||||
else:
|
||||
sys.path.append(builddir)
|
||||
sys.path.append(builddir + '/python')
|
||||
|
||||
import swig_avrdude as ad
|
||||
|
||||
|
||||
|
||||
def avrdude_init():
|
||||
ad.init_config()
|
||||
|
||||
found = False
|
||||
for d in [builddir, "/etc", "/usr/local/etc"]:
|
||||
p = pathlib.Path(d + "/avrdude.conf")
|
||||
if p.is_file():
|
||||
ad.read_config(d + "/avrdude.conf")
|
||||
return (True, f"Found avrdude.conf in {d}\n")
|
||||
|
||||
return (False, "Sorry, no avrdude.conf could be found.\n")
|
||||
|
||||
def classify_devices():
|
||||
result = {
|
||||
'at90': [],
|
||||
'attiny': [],
|
||||
'atmega': [],
|
||||
'atxmega': [],
|
||||
'avr_de': [],
|
||||
'other': []
|
||||
}
|
||||
avr_de_re = re.compile('AVR\d+[DE][A-Z]\d+')
|
||||
part = ad.lfirst(ad.cvar.part_list)
|
||||
while part:
|
||||
p = ad.ldata_avrpart(part)
|
||||
part = ad.lnext(part)
|
||||
if not p.id.startswith('.'):
|
||||
if p.desc.startswith('AT90'):
|
||||
result['at90'].append(p.desc)
|
||||
elif p.desc.startswith('ATtiny'):
|
||||
result['attiny'].append(p.desc)
|
||||
elif p.desc.startswith('ATmega'):
|
||||
result['atmega'].append(p.desc)
|
||||
elif p.desc.startswith('ATxmega'):
|
||||
result['atxmega'].append(p.desc)
|
||||
elif avr_de_re.match(p.desc):
|
||||
result['avr_de'].append(p.desc)
|
||||
else:
|
||||
result['other'].append(p.desc)
|
||||
return result
|
||||
|
||||
from PySide2.QtWidgets import *
|
||||
from PySide2.QtCore import QFile, QIODevice
|
||||
|
||||
from ui_adgui import Ui_MainWindow
|
||||
from ui_about import Ui_About
|
||||
from ui_device import Ui_Device
|
||||
|
||||
class About(QDialog):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.ui = Ui_About()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
class Device(QDialog):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.ui = Ui_Device()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super(MainWindow, self).__init__()
|
||||
self.ui = Ui_MainWindow()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
class adgui():
|
||||
def __init__(self, argv):
|
||||
self.logstring = "Welcome to AVRDUDE!\n"
|
||||
self.app = QApplication(sys.argv)
|
||||
|
||||
self.window = MainWindow()
|
||||
self.window.show()
|
||||
self.about = About()
|
||||
self.device = Device()
|
||||
|
||||
self.window.ui.actionAbout.triggered.connect(self.about.show)
|
||||
self.window.ui.actionDevice.triggered.connect(self.device.show)
|
||||
self.window.ui.loggingArea.setPlainText(self.logstring)
|
||||
|
||||
(success, message) = avrdude_init()
|
||||
self.initialized = success
|
||||
self.logstring += message
|
||||
self.window.ui.loggingArea.setPlainText(self.logstring)
|
||||
if not success:
|
||||
self.window.ui.actionDevice.setEnabled(False)
|
||||
self.window.ui.actionProgrammer.setEnabled(False)
|
||||
# essentially, only Exit and Help work anymore
|
||||
else:
|
||||
self.devices = classify_devices()
|
||||
self.update_device_cb()
|
||||
self.device.ui.at90.stateChanged.connect(self.update_device_cb)
|
||||
self.device.ui.attiny.stateChanged.connect(self.update_device_cb)
|
||||
self.device.ui.atmega.stateChanged.connect(self.update_device_cb)
|
||||
self.device.ui.atxmega.stateChanged.connect(self.update_device_cb)
|
||||
self.device.ui.avr_de.stateChanged.connect(self.update_device_cb)
|
||||
self.device.ui.other.stateChanged.connect(self.update_device_cb)
|
||||
|
||||
# rough equivalent of avrdude_message2()
|
||||
# first argument is either "stdout" or "stderr"
|
||||
#
|
||||
# install callback with ad.set_msg_callback(msg_callback)
|
||||
def msg_callback(self, target: str, lno: int, fname: str, func: str,
|
||||
msgmode: int, msglvl: int, msg: str):
|
||||
if ad.cvar.verbose >= msglvl:
|
||||
s = ""
|
||||
if msgmode & ad.MSG2_PROGNAME:
|
||||
s += ad.cvar.progname
|
||||
if ad.cvar.verbose >= ad.MSG_NOTICE and (msgmode & ad.MSG2_FUNCTION) != 0:
|
||||
s += " " + func + "()"
|
||||
if ad.cvar.verbose >= ad.MSG_DEBUG and (msgmode & ad.MSG2_FILELINE) != 0:
|
||||
n = os.path.basename(fname)
|
||||
s += f" [{n}:{lno}]"
|
||||
if (msgmode & ad.MSG2_TYPE) != 0:
|
||||
s += " " + message_type(msglvl)
|
||||
s += ": "
|
||||
elif (msgmode & ad.MSG2_INDENT1) != 0:
|
||||
s = (len(ad.cvar.progname) + 1) * ' '
|
||||
elif (msgmode & ad.MSG2_INDENT2) != 0:
|
||||
s = (len(ad.cvar.progname) + 2) * ' '
|
||||
s += msg
|
||||
try:
|
||||
self.logstring += s
|
||||
except UnicodeDecodeError:
|
||||
self.logstring += "message contained invalid characters\n"
|
||||
self.window.ui.loggingArea.setPlainText(self.logstring)
|
||||
|
||||
def update_device_cb(self):
|
||||
fams = list(self.devices.keys())
|
||||
#fams.sort()
|
||||
self.device.ui.devices.clear()
|
||||
for f in fams:
|
||||
obj = eval('self.device.ui.' + f + '.isChecked()')
|
||||
if obj:
|
||||
for d in self.devices[f]:
|
||||
self.device.ui.devices.addItem(d)
|
||||
|
||||
|
||||
def main():
|
||||
gui = adgui(sys.argv)
|
||||
|
||||
sys.exit(gui.app.exec_())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
180
src/python/adgui.ui
Normal file
180
src/python/adgui.ui
Normal file
@@ -0,0 +1,180 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>780</width>
|
||||
<height>746</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>AVRDUDE GUI</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>660</y>
|
||||
<width>781</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="percentage">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-- %</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="time">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>--:--</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QPlainTextEdit" name="loggingArea">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>821</width>
|
||||
<height>651</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="undoRedoEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>780</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionDevice"/>
|
||||
<addaction name="actionProgrammer"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
<addaction name="actionDevice_Memories"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuSettings">
|
||||
<property name="title">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuHelp">
|
||||
<property name="title">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menuSettings"/>
|
||||
<addaction name="menuHelp"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="actionDevice">
|
||||
<property name="text">
|
||||
<string>Device ...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionProgrammer">
|
||||
<property name="text">
|
||||
<string>Programmer ...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExit">
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDevice_Memories">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Device Memories</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="text">
|
||||
<string>About ...</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>actionExit</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
<y>299</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
157
src/python/device.ui
Normal file
157
src/python/device.ui
Normal file
@@ -0,0 +1,157 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Device</class>
|
||||
<widget class="QDialog" name="Device">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>398</width>
|
||||
<height>217</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Device selection</string>
|
||||
</property>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>280</x>
|
||||
<y>140</y>
|
||||
<width>81</width>
|
||||
<height>61</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="devices">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>180</x>
|
||||
<y>50</y>
|
||||
<width>191</width>
|
||||
<height>25</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>10</y>
|
||||
<width>131</width>
|
||||
<height>191</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="heading">
|
||||
<property name="text">
|
||||
<string>Families enabled:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="at90">
|
||||
<property name="text">
|
||||
<string>AT90</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="attiny">
|
||||
<property name="text">
|
||||
<string>ATtiny</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="atmega">
|
||||
<property name="text">
|
||||
<string>ATmega</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="atxmega">
|
||||
<property name="text">
|
||||
<string>ATxmega</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="avr_de">
|
||||
<property name="text">
|
||||
<string>AVR…D/E…</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="other">
|
||||
<property name="text">
|
||||
<string>Other</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Device</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Device</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user