GTK+ GUI for configuring PD Buddy Sink devices
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

pd-buddy-gtk.py 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. #!/usr/bin/env python3
  2. import sys
  3. import serial
  4. import serial.tools.list_ports
  5. import gi
  6. gi.require_version('Gtk', '3.0')
  7. from gi.repository import Gtk, Gio, GObject, GLib
  8. def update_header_func(row, before, data):
  9. """Add a separator header to all rows but the first one"""
  10. if before is None:
  11. row.set_header(None)
  12. return
  13. current = row.get_header()
  14. if current is None:
  15. current = Gtk.Separator.new(Gtk.Orientation.HORIZONTAL)
  16. row.set_header(current)
  17. def pdb_send_message(sp, message):
  18. """Send a message over the serial port and return the response"""
  19. # Open the serial port
  20. # FIXME handle exceptions
  21. sp = serial.Serial(sp.device, baudrate=115200, timeout=0.01)
  22. sp.write(bytes(message, 'utf-8') + b'\r\n')
  23. sp.flush()
  24. answer = sp.readlines()
  25. sp.close()
  26. # Remove the echoed command and prompt
  27. answer = answer[1:-1]
  28. return answer
  29. class ListRowModel(GObject.GObject):
  30. def __init__(self, serport):
  31. GObject.GObject.__init__(self)
  32. self.serport = serport
  33. class SelectListStore(Gio.ListStore):
  34. def __init__(self, stack, list_empty, list_frame):
  35. Gio.ListStore.__init__(self)
  36. self.stack = stack
  37. self.list_empty = list_empty
  38. self.list_frame = list_frame
  39. self.update_items()
  40. GLib.timeout_add(1000, self.update_items)
  41. def update_items(self):
  42. # Clear the old entries
  43. self.remove_all()
  44. # Search for the serial ports
  45. for serport in serial.tools.list_ports.grep("1209:0001"):
  46. self.append(ListRowModel(serport))
  47. # Set the visible child
  48. # FIXME: This is rather poor organization
  49. if self.get_n_items():
  50. self.stack.set_visible_child(self.list_frame)
  51. else:
  52. self.stack.set_visible_child(self.list_empty)
  53. return True
  54. class SelectListRow(Gtk.ListBoxRow):
  55. def __init__(self, lrm):
  56. Gtk.EventBox.__init__(self)
  57. self.serial_port = lrm.serport
  58. self._builder = Gtk.Builder()
  59. self._builder.add_from_file("data/select-list-row.ui")
  60. self._builder.connect_signals(self)
  61. name = self._builder.get_object("name")
  62. name.set_text(self.serial_port.description)
  63. device = self._builder.get_object("device")
  64. device.set_text(self.serial_port.device)
  65. self.add(self._builder.get_object("grid"))
  66. self.show_all()
  67. def on_identify_clicked(self, button):
  68. pdb_send_message(self.serial_port, 'identify')
  69. class Handler:
  70. def __init__(self, builder):
  71. self.builder = builder
  72. def on_pdb_window_realize(self, *args):
  73. # Get the list
  74. sl = self.builder.get_object("select-list")
  75. ss = self.builder.get_object("select-stack")
  76. se = self.builder.get_object("select-none")
  77. sf = self.builder.get_object("select-frame")
  78. liststore = SelectListStore(ss, se, sf)
  79. sl.bind_model(liststore, SelectListRow)
  80. # Add separators to the list
  81. sl.set_header_func(update_header_func, None)
  82. def on_pdb_window_delete_event(self, *args):
  83. Gtk.main_quit(*args)
  84. def on_select_list_row_activated(self, box, row):
  85. # Get voltage and current widgets
  86. voltage = self.builder.get_object("voltage-combobox")
  87. current = self.builder.get_object("current-spinbutton")
  88. self.serial_port = row.serial_port
  89. pdb_send_message(self.serial_port, 'load')
  90. tmpcfg = pdb_send_message(self.serial_port, 'get_tmpcfg')
  91. # Get information
  92. for line in tmpcfg:
  93. if line.startswith(b'v:'):
  94. v = line.split()[1]
  95. if v == b'5.00':
  96. voltage.set_active_id('voltage-five')
  97. elif v == b'9.00':
  98. voltage.set_active_id('voltage-nine')
  99. elif v == b'15.00':
  100. voltage.set_active_id('voltage-fifteen')
  101. if v == b'20.00':
  102. voltage.set_active_id('voltage-twenty')
  103. elif line.startswith(b'i:'):
  104. i = float(line.split()[1])
  105. current.set_value(i)
  106. # Hide the Save button
  107. rev = self.builder.get_object("header-sink-save-revealer")
  108. rev.set_reveal_child(False)
  109. # Show the Sink page
  110. hst = self.builder.get_object("header-stack")
  111. hsink = self.builder.get_object("header-sink")
  112. hsink.set_subtitle(row.serial_port.device)
  113. hst.set_visible_child(hsink)
  114. st = self.builder.get_object("stack")
  115. sink = self.builder.get_object("sink")
  116. st.set_visible_child(sink)
  117. def on_header_sink_back_clicked(self, data):
  118. # Show the Select page
  119. hst = self.builder.get_object("header-stack")
  120. hselect = self.builder.get_object("header-select")
  121. hst.set_visible_child(hselect)
  122. st = self.builder.get_object("stack")
  123. select = self.builder.get_object("select")
  124. st.set_visible_child(select)
  125. def on_header_sink_save_clicked(self, button):
  126. rev = self.builder.get_object("header-sink-save-revealer")
  127. rev.set_reveal_child(False)
  128. pdb_send_message(self.serial_port, 'write')
  129. def on_voltage_combobox_changed(self, combo):
  130. rev = self.builder.get_object("header-sink-save-revealer")
  131. rev.set_reveal_child(True)
  132. pdb_send_message(self.serial_port,
  133. 'set_v {}'.format(int(combo.get_active_text())*1000))
  134. def on_current_spinbutton_changed(self, spin):
  135. rev = self.builder.get_object("header-sink-save-revealer")
  136. rev.set_reveal_child(True)
  137. pdb_send_message(self.serial_port,
  138. 'set_i {}'.format(int(spin.get_value()*1000)))
  139. class Application(Gtk.Application):
  140. def __init__(self, *args, **kwargs):
  141. super().__init__(*args, application_id="com.clayhobbs.pd-buddy-gtk",
  142. **kwargs)
  143. self.window = None
  144. def do_startup(self):
  145. Gtk.Application.do_startup(self)
  146. self.builder = Gtk.Builder()
  147. self.builder.add_from_file("data/pd-buddy-gtk.ui")
  148. self.builder.connect_signals(Handler(self.builder))
  149. def do_activate(self):
  150. # We only allow a single window and raise any existing ones
  151. if not self.window:
  152. # Windows are associated with the application
  153. # when the last one is closed the application shuts down
  154. self.window = self.builder.get_object("pdb-window")
  155. self.add_window(self.window)
  156. self.window.set_wmclass("PD Buddy Configuration",
  157. "PD Buddy Configuration")
  158. self.window.present()
  159. if __name__ == "__main__":
  160. app = Application()
  161. app.run(sys.argv)