diff --git a/src/python/about.ui b/src/python/about.ui
new file mode 100644
index 00000000..b69e79ba
--- /dev/null
+++ b/src/python/about.ui
@@ -0,0 +1,91 @@
+
+
+ About
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ About
+
+
+ false
+
+
+ true
+
+
+
+
+ 30
+ 240
+ 341
+ 32
+
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Ok
+
+
+
+
+
+ 10
+ 20
+ 381
+ 211
+
+
+
+ <!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>
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ About
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ About
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/src/python/adgui.py b/src/python/adgui.py
new file mode 100755
index 00000000..a17731b2
--- /dev/null
+++ b/src/python/adgui.py
@@ -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()
diff --git a/src/python/adgui.ui b/src/python/adgui.ui
new file mode 100644
index 00000000..3700a970
--- /dev/null
+++ b/src/python/adgui.ui
@@ -0,0 +1,180 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 780
+ 746
+
+
+
+ AVRDUDE GUI
+
+
+
+
+
+ 0
+ 660
+ 781
+ 41
+
+
+
+
+ 15
+
+
+ 15
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ -- %
+
+
+
+ -
+
+
+ false
+
+
+ 0
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ --:--
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 821
+ 651
+
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+
+
+
+
+
+ Device ...
+
+
+
+
+ Programmer ...
+
+
+
+
+ Exit
+
+
+
+
+ false
+
+
+ Device Memories
+
+
+
+
+ About ...
+
+
+
+
+
+
+ actionExit
+ triggered()
+ MainWindow
+ close()
+
+
+ -1
+ -1
+
+
+ 399
+ 299
+
+
+
+
+
diff --git a/src/python/device.ui b/src/python/device.ui
new file mode 100644
index 00000000..bb24b3d2
--- /dev/null
+++ b/src/python/device.ui
@@ -0,0 +1,157 @@
+
+
+ Device
+
+
+
+ 0
+ 0
+ 398
+ 217
+
+
+
+ Device selection
+
+
+
+
+ 280
+ 140
+ 81
+ 61
+
+
+
+ Qt::Vertical
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+ 180
+ 50
+ 191
+ 25
+
+
+
+
+
+
+ 20
+ 10
+ 131
+ 191
+
+
+
+ -
+
+
+ Families enabled:
+
+
+
+ -
+
+
+ AT90
+
+
+ true
+
+
+
+ -
+
+
+ ATtiny
+
+
+ true
+
+
+
+ -
+
+
+ ATmega
+
+
+ true
+
+
+
+ -
+
+
+ ATxmega
+
+
+ true
+
+
+
+ -
+
+
+ AVR…D/E…
+
+
+ true
+
+
+
+ -
+
+
+ Other
+
+
+ true
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Device
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Device
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+