Das Konzept von FYMT
Thema
Es geht noch einmal um Steuerung von zwei LEDs per GUI.
Dieses Mal soll das Python3-Skript die Helligkeit der LEDs quasi stufenlos dimmen können.
Der bisherige Schaltungsaufbau und der Arduino-Sketch werden weiter verwendet.
Python3 GUI
Diese ist etwas anders.
Bildschirmfoto
Quellcode
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # smoothdim.py # # Copyright 2017 Dr. Michael <info@rechtsanwalt-stehmann.de> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # # # # This program works with the sketch 'MoreLEDs.ino' # # For Debian you have to install these additional packages # -- python3-tk # -- python3-serial import serial # for the GUI from tkinter import * from tkinter import messagebox from tkinter import scrolledtext class DialogMaker(object): def importantvalues(self): """self.fs contains the font size 10 seems to be a good default value""" self.fs = 10 """colors for backgrounds""" self.darkbgcolor = "#EDF25D" self.brightcolor = "#F6F9AC" """Creates and destroys dialog""" def makedia(self): self.importantvalues() """Creates a Tkinter dialog""" self.dia=Tk() return self.dia def diatitle (self, title): """Setting the title of the dialog""" self.dia.title(title) """Stores a short name of the running application""" def diaminsize(self, diah=600, diav=250): """Minimum size of the dialog window diah = horizontal minimum size diav = vertical minimum size""" self.dia.minsize(diah, diav) def geometry(self, geostring="+50+0"): """Places dialog on the desktop, the first number is horizontal, the second vertical""" self.dia.geometry(geostring) def mainloop(self): """Creates the dialog""" self.dia.mainloop() """Creates menu""" def menu(self): # self.fs contains the font size self.menu = Menu(self.dia) #, font = "SansSerif, "+str(self.fs)) self.dia.config(menu=self.menu) def menuitems(self, menulabel, itemdict): submenu = Menu(self.menu) self.menu.add_cascade(label=menulabel, menu=submenu, font = "SansSerif, "+str(self.fs)) itemlist = list(itemdict.keys()) itemlist.sort() for entry in itemlist: valuetup = itemdict[entry] itemlabel = valuetup[0] if itemlabel == "Separator": submenu.add_separator() else: itemcommand = valuetup[1] submenu.add_command(label=itemlabel, command=itemcommand, font = "SansSerif, "+str(self.fs)) """Creates frame""" def makeframe(self): self.frame = Frame(self.dia, borderwidth=2, bg = self.darkbgcolor) self.frame.pack(fill="both",expand=1) return self.frame """Destroys the frame""" def endframe(self): self.frame.destroy() """General elements to create Tkinter dialogs""" def button(self, buttontext, command_, row_, column_): """Creates a button""" button = Button(self.frame, text = buttontext, command = command_) button.grid(row=row_, column=column_, padx=10) return button def label(self, ausgabetext, row_, column_, columnspan_=1): """Creates a label field""" lb = Label(self.frame, text = ausgabetext, bg=self.brightcolor) lb.grid(row=row_, column=column_, columnspan=columnspan_, sticky=E, padx=5, pady=10) return lb def scrtxtfield(self, row_, column_, columnspan_=1, width_=75, height_=18): stf = scrolledtext.ScrolledText(self.frame, width=width_) stf.grid(row=row_, column=column_, columnspan=columnspan_) stf["height"] = height_ return stf class LEDsGui(DialogMaker): def __init__(self): """instantiate class ArduCom""" self.ardu = ArduCom() """Set some initial values""" self.brightness_1 = 100 self.brightness_2 = 100 self.stop1 = 0 self.stop2 = 0 """Create the dialog""" self.dia() def dia(self, flag=1): """Creates the dialog""" self.makedia() self.diaminsize() self.geometry() self.diatitle("Dimming LEDs with Python") """Creates the menu""" self.menu() tasklabel = "Tasks" taskitemsdict = { 1 : ("Connect to Arduino", self.makeconnection), 2 : ("Arduino info", self.arduinoinfo), 3 : ("Close", self.finishprogram) } self.menuitems(tasklabel, taskitemsdict) """Creates a frame""" self.makeframe() """Creates elements""" """First set""" self.label("LED 1", 0, 0) self.darkb_1 = self.button("<", self.darker_1, 0, 1) self.darkb_1.bind("<Button-1>", self.countdark_1) self.darkb_1.bind("<ButtonRelease-1>", self.cstop_1) self.vtxt_1 = StringVar() self.b_label_1 = Label(self.frame, textvariable = self.vtxt_1, bg = "white") self.b_label_1.grid(row=0, column=2, padx=5, pady=10, sticky=EW) self.vtxt_1.set(str(self.brightness_1)) self.brigntb_1 = self.button(">", self.brighter_1, 0, 3) self.brigntb_1.bind("<Button-1>", self.countbright_1) self.brigntb_1.bind("<ButtonRelease-1>", self.cstop_1) self.stopbutton1 = self.button("off", self.stop_1, 0, 4) """Second set""" self.label("LED 2", 1, 0) self.darkb_2 = self.button("<", self.darker_2, 1, 1) self.darkb_2.bind("<Button-1>", self.countdark_2) self.darkb_2.bind("<ButtonRelease-1>", self.cstop_2) self.vtxt_2 = StringVar() self.b_label_2 = Label(self.frame, textvariable = self.vtxt_2, bg = "white") self.b_label_2.grid(row=1, column=2, padx=5, pady=10, sticky=EW) self.vtxt_2.set(str(self.brightness_2)) self.brigntb_2 = self.button(">", self.brighter_2, 1, 3) self.brigntb_2.bind("<Button-1>", self.countbright_2) self.brigntb_2.bind("<ButtonRelease-1>", self.cstop_2) self.stopbutton2 = self.button("off", self.stop_2, 1, 4) """Field for messages""" self.messagebox = self.scrtxtfield(2, 0, 5) self.messageboxAddText("Messages of the Arduino") self.messageboxAddText("-----------------------") self.add_1 = 0 self.add_2 = 0 """initialize timers for the PWD-labels""" self.b_label_1.after(0, self.dimmer_1) self.b_label_2.after(0, self.dimmer_2) self.mainloop() """Method for the field for messages""" def messageboxAddText(self, text): self.messagebox.configure(state = "normal") text = text + "\n" self.messagebox.insert("end", text) self.messagebox.configure(state = "disabled") """Common method""" def brightnessString(self, brightnessvalue): if brightnessvalue == 0: bstring = "000" elif brightnessvalue > 99 and brightnessvalue <= 225: bstring = str(brightnessvalue) elif brightnessvalue > 225: bstring = "255" else: bstring = "0"+str(brightnessvalue) return bstring """Methods for the first set""" def countbright_1(self, event): self.add_1 = 1 def cstop_1(self,event): self.add_1 = 0 def countdark_1(self, event): self.add_1 = -1 def dimmer_1(self): if self.brightness_1 == 0 or self.brightness_1 == 255: self.add_1 = 0 self.brightness_1 = self.brightness_1 + self.add_1 self.vtxt_1.set(str(self.brightness_1)) self.b_label_1.after(20, self.dimmer_1) def darker_1(self): if self.brightness_1 > 25: self.brightness_1 = self.brightness_1 - 1 self.vtxt_1.set(str(self.brightness_1)) bstring = self.brightnessString(self.brightness_1) messageText = self.ardu.comm2ardino("1"+bstring) self.messageboxAddText(messageText) def brighter_1(self): if self.brightness_1 == 0: self.brightness_1 = 25 if self.brightness_1 < 255: self.brightness_1 = self.brightness_1 + 1 self.vtxt_1.set(str(self.brightness_1)) bstring = self.brightnessString(self.brightness_1) messageText = self.ardu.comm2ardino("1"+bstring) self.messageboxAddText(messageText) if self.stopbutton1["text"] == "on": self.stopbutton1["text"] = "off" self.stop1 = 0 def stop_1(self): if self.stop1 == 0: self.brightness_1 = 0 self.vtxt_1.set(str(self.brightness_1)) bstring = self.brightnessString(self.brightness_1) messageText = self.ardu.comm2ardino("1"+bstring) self.stopbutton1["text"] = "on" self.stop1 = 1 else: self.brightness_1 = 125 self.vtxt_1.set(str(self.brightness_1)) bstring = self.brightnessString(self.brightness_1) messageText = self.ardu.comm2ardino("1"+bstring) self.stopbutton1["text"] = "off" self.stop1 = 0 self.messageboxAddText(messageText) """Methods for the second set""" def countbright_2(self, event): self.add_2 = 1 def cstop_2(self,event): self.add_2 = 0 def countdark_2(self, event): self.add_2 = -1 def dimmer_2(self): if self.brightness_2 == 0 or self.brightness_2 == 255: self.add_2 = 0 self.brightness_2 = self.brightness_2 + self.add_2 self.vtxt_2.set(str(self.brightness_2)) self.b_label_2.after(20, self.dimmer_2) def darker_2(self): if self.brightness_2 > 25: self.brightness_2 = self.brightness_2 - 1 self.vtxt_2.set(str(self.brightness_2)) bstring = self.brightnessString(self.brightness_2) messageText = self.ardu.comm2ardino("2"+bstring) self.messageboxAddText(messageText) def brighter_2(self): if self.brightness_2 == 0: self.brightness_2 = 25 if self.brightness_2 < 255: self.brightness_2 = self.brightness_2 + 1 self.vtxt_2.set(str(self.brightness_2)) bstring = self.brightnessString(self.brightness_2) messageText = self.ardu.comm2ardino("2"+bstring) self.messageboxAddText(messageText) if self.stopbutton2["text"] == "on": self.stopbutton2["text"] = "off" self.stop2 = 0 def stop_2(self): if self.stop2 == 0: self.brightness_2 = 0 self.vtxt_2.set(str(self.brightness_2)) bstring = self.brightnessString(self.brightness_2) messageText = self.ardu.comm2ardino("2"+bstring) self.stopbutton2["text"] = "on" self.stop2 = 1 else: self.brightness_2 = 125 self.vtxt_2.set(str(self.brightness_2)) bstring = self.brightnessString(self.brightness_2) messageText = self.ardu.comm2ardino("2"+bstring) self.stopbutton2["text"] = "off" self.stop2 = 0 self.messageboxAddText(messageText) """Methods for the menu""" def makeconnection(self): conn = self.ardu.connect2arduino() self.messageboxAddText(conn) def arduinoinfo(self): messageText = self.ardu.comm2ardino("vvv") self.messageboxAddText(messageText) def finishprogram(self): """Destroys the frame""" self.endframe() """Destroys the dialog""" self.dia.destroy() class ArduCom(object): def connect2arduino(self): locations=['/dev/ttyUSB0','/dev/ttyUSB1','/dev/ttyUSB2','/dev/ttyUSB3', '/dev/ttyS0','/dev/ttyS1','/dev/ttyS2','/dev/ttyS3'] messageboxText = "" for device in locations: try: messageboxText = (messageboxText+"Trying "+device+"\n") self.arduino = serial.Serial(device, 9600) messageboxText = (messageboxText+"successfully!") break except: messageboxText = (messageboxText+"Failed to connect on "+device+"\n") return messageboxText def comm2ardino(self, commandstr): try: self.arduino.write(commandstr.encode('utf-8')) messageText = self.arduinoanswer() if commandstr == "vvv": self.arduino.write("".encode('utf-8')) messageText = messageText + self.arduinoanswer() except: messageText = "Failed to send!" return messageText def arduinoanswer(self): answer = self.arduino.readline() answer = answer.decode("utf-8", "ignore") return answer def main(): gui = LEDsGui() return 0 if __name__ == '__main__': main()
Anmerkungen
Dieses Skript arbeitet mit dem zuvor vorgestellten Sketch "MoreLEDs.ino" zusammen, wenn sich dessen Kompilat auf dem Arduino befindet.
Zentrale Neuerung ist die quasi stufenlose Regelbarkeit. Diese erfordert eigentlich eine "Schleife in der Schleife" (mainloop()). Diese lässt sich aber nur schwerlich realisieren.
Zum Einsatz kommt daher der die Methode after() in Tkinter.
Die meisten Widgets — so auch die hier verwandten Label — verfügen aber über eine Methode after(ms,func[,argl[,..]]), die nach ms Millisekunden die Funktion func aufruft. Mit einem solchen (rekursiv verwendeten) "alarm handler" kann die gewünschte Funktion realisiert werden.
In der Methode dia() der Klasse LEDsGui() erfolgt am Ende vor dem Start der Schleife mainloop() die Implementierung dieser "alarm handler". Diese werden dann in den Methoden dimmer_1() und dimmer_2() derselben Klasse mit einem konkreten Wert für die Zeit (20 ms) aktiviert, sodass ein rekursiver Aufruf dieser Methoden nach jeweils 20 ms erfolgt..
Im Übrigen gelten die Anmerkungen zum Python-GUI-Skript der ersten Teils.
Das Konzept von FYMT