I am using Tkinter and I want to know how to change the Frame and at the same time send a letter to the arduino when a button is pressed. I have this but it doesn't work with two statements at the same time in the command, they just work separately.
Also, I don't know how to simplify the code and make the "def" as global so I don't have to put them in each class.
I hope to make myself understood, English is not my first language.
import serial
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
import time
ser = serial.Serial("/dev/ttyACM0", 9600, timeout=.1) #14:50
ser.reset_input_buffer()
time.sleep(1)
status1=0
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne, PageTwo, PageThree):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="ESTADO A", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Ir a Estado B",
Here's my question
command=[lambda:controller.show_frame("PageOne"), self.Forward])
button1.pack()
def Reset(self):
global status1
ser.write(b'R')
status1 = (ser.readline())
def Backward(self):
global status1
ser.write(b'A')
status1 = (ser.readline())
def Forward(self):
global status1
ser.write(b'D')
status1 = (ser.readline())
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.Forward
self.controller = controller
label = tk.Label(self, text="ESTADO B", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Ir a A", command=self.Forward)
button2 = tk.Button(self, text="Ir a C", command=self.Backward)
button3 = tk.Button(self, text="RESET", command=self.Reset)
button1.pack()
button2.pack()
button3.pack()
def Reset(self):
global status1
ser.write(b'R')
status1 = (ser.readline())
def Backward(self):
global status1
ser.write(b'A')
status1 = (ser.readline())
def Forward(self):
global status1
ser.write(b'D')
status1 = (ser.readline())
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="ESTADO C", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Ir a B", command=self.Forward)
button2 = tk.Button(self, text="Ir a D", command=self.Backward)
button3 = tk.Button(self, text="RESET", command=self.Reset)
button1.pack()
button2.pack()
button3.pack()
def Reset(self):
global status1
ser.write(b'R')
status1 = (ser.readline())
def Backward(self):
global status1
ser.write(b'A')
status1 = (ser.readline())
def Forward(self):
global status1
ser.write(b'D')
status1 = (ser.readline())
class PageThree(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="ESTADO D", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Ir a C",command=self.Forward)
button2 = tk.Button(self, text="RESET", command=self.Reset)
button1.pack()
button2.pack()
def Reset(self):
global status1
ser.write(b'R')
status1 = (ser.readline())
def Backward(self):
global status1
ser.write(b'A')
status1 = (ser.readline())
def Forward(self):
global status1
ser.write(b'D')
status1 = (ser.readline())
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
Arduino code, I'm using an Atmega2560 and a RGB led.
#include "StateMachineLib.h"
//constantes para LED RGB
const int ledRGB[]={34,35,36};//{ledRojo, ledVerde, ledAzul}
int ledRGBSize = sizeof(ledRGB) / sizeof(int);
// Enumeraciones para facilitar el uso
enum State
{
PosicionA = 0,
PosicionB = 1,
PosicionC = 2,
PosicionD = 3
};
enum Input
{
Reset = 0,
Forward = 1,
Backward = 2,
Unknown = 3,
};
StateMachine stateMachine(4, 9);
Input input;
// Sacado como función independiente por claridad
void setupStateMachine()
{
// Configurar transiciones
// Ejemplo con funciones lambda
stateMachine.AddTransition(PosicionA, PosicionB, []() { return input == Forward; });
stateMachine.AddTransition(PosicionB, PosicionA, []() { return input == Backward; });
stateMachine.AddTransition(PosicionB, PosicionC, []() { return input == Forward; });
stateMachine.AddTransition(PosicionB, PosicionA, []() { return input == Reset; });
stateMachine.AddTransition(PosicionC, PosicionB, []() { return input == Backward; });
stateMachine.AddTransition(PosicionC, PosicionD, []() { return input == Forward; });
stateMachine.AddTransition(PosicionC, PosicionA, []() { return input == Reset; });
stateMachine.AddTransition(PosicionD, PosicionC, []() { return input == Backward; });
stateMachine.AddTransition(PosicionD, PosicionA, []() { return input == Reset; });
// Configurar eventos de estado
// Ejemplo con funciones normales
stateMachine.SetOnEntering(PosicionA, outputA);
stateMachine.SetOnEntering(PosicionB, outputB);
stateMachine.SetOnEntering(PosicionC, outputC);
stateMachine.SetOnEntering(PosicionD, outputD);
// Ejemplo con funciones lambda
stateMachine.SetOnLeaving(PosicionA, []() {Serial.println("Leaving A"); });
stateMachine.SetOnLeaving(PosicionB, []() {Serial.println("Leaving B"); });
stateMachine.SetOnLeaving(PosicionC, []() {Serial.println("Leaving C"); });
stateMachine.SetOnLeaving(PosicionD, []() {Serial.println("Leaving D"); });
}
void setup()
{
Serial.begin(9600);
for(int i=0; i<ledRGBSize; i++){ //ledLCD
pinMode(ledRGB[i],OUTPUT); }
Serial.println("Starting State Machine...");
setupStateMachine();
Serial.println("Start Machine Started");
stateMachine.SetState(PosicionA, false, true);
}
void loop()
{
// Recibir un comando por puerto serie
input = static_cast<Input>(readInput());
// Actualizar el estado de la maquina
stateMachine.Update();
}
// Funcion auxiliar que simula la recepcion de un evento
int readInput()
{
Input currentInput = Input::Unknown;
if (Serial.available())
{
char incomingChar = Serial.read();
switch (incomingChar)
{
case 'R': currentInput = Input::Reset; break;
case 'A': currentInput = Input::Backward; break;
case 'D': currentInput = Input::Forward; break;
default: break;
}
}
return currentInput;
}
// Acciones de estado
// Visualizan el estado del ejemplo
void outputA()
{
Serial.println("A B C D");
Serial.println("X ");
Serial.println();
ledRGB_apagar();
delay(500);
ledRGB_color("rojo");
}
void outputB()
{
Serial.println("A B C D");
Serial.println(" X ");
Serial.println();
ledRGB_apagar();
delay(500);
ledRGB_color("verde");;
}
void outputC()
{
Serial.println("A B C D");
Serial.println(" X ");
Serial.println();
ledRGB_apagar();
delay(500);
ledRGB_color("azul");
}
void outputD()
{
Serial.println("A B C D");
Serial.println(" X");
Serial.println();
ledRGB_apagar();
delay(500);
ledRGB_color("morado");
}
//******************************
void ledRGB_color(String color){
if (color == "rojo"){
digitalWrite(ledRGB[0], HIGH);
digitalWrite(ledRGB[1], LOW);
digitalWrite(ledRGB[2], LOW);
}
if (color == "verde"){
digitalWrite(ledRGB[1], HIGH);
digitalWrite(ledRGB[0], LOW);
digitalWrite(ledRGB[2], LOW);
}
if (color == "azul"){
digitalWrite(ledRGB[2], HIGH);
digitalWrite(ledRGB[0], LOW);
digitalWrite(ledRGB[1], LOW);
}
if (color == "morado"){
digitalWrite(ledRGB[1], LOW);
digitalWrite(ledRGB[0], HIGH);
digitalWrite(ledRGB[2], HIGH);
}
}
//******************************
void ledRGB_apagar(){
//digitalWrite(ledRGB[0,1,2], LOW);
digitalWrite(ledRGB[0], LOW);
digitalWrite(ledRGB[1], LOW);
digitalWrite(ledRGB[2], LOW);
}
You forgot ()
to execute second function
tk.Button(..., command=lambda:[controller.show_frame("PageOne"), self.Forward()] )
But preferred and more readable would be to create separated function for this
tk.Button(..., command=self.next_page )
def next_page(self):
self.controller.show_frame("PageOne")
self.Forward()
To make it more useful you could set this as variable
self.next_page_name = "PageOne"
tk.Button(..., command=self.next_page )
def next_page(self):
self.controller.show_frame(self.next_page_name)
self.Forward()
And now you can use OOP
to reduce code - as you said: make "def" global
.
First create class with functions forward
, backward
, reset
(PEP8: lower_case_names
for methods. See more PEP 8 -- Style Guide for Python Code )
class BasePage(tk.Frame):
def reset(self):
global status1
ser.write(b'R')
status1 = ser.readline()
def backward(self):
global status1
ser.write(b'A')
status1 = ser.readline()
def forward(self):
global status1
ser.write(b'D')
status1 = ser.readline()
def next_page(self):
self.controller.show_frame(self.next_page_name)
self.forward()
And next use BasePage
to create other pages
class StartPage(BasePage):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
label = tk.Label(self, text="ESTADO A", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
self.next_page_name = 'PageOne'
button1 = tk.Button(self, text="Ir a Estado B", command=next_page)
button1.pack()
class PageOne(BasePage):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
label = tk.Label(self, text="ESTADO B", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
self.next_page_name = 'PageTwo'
button1 = tk.Button(self, text="Ir a A", command=self.next_page)
button2 = tk.Button(self, text="Ir a C", command=self.backward)
button3 = tk.Button(self, text="RESET", command=self.reset)
button1.pack()
button2.pack()
button3.pack()
class PageTwo(BasePage):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
label = tk.Label(self, text="ESTADO C", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
self.next_page_name = 'PageThree'
button1 = tk.Button(self, text="Ir a B", command=self.next_page)
button2 = tk.Button(self, text="Ir a D", command=self.backward)
button3 = tk.Button(self, text="RESET", command=self.reset)
button1.pack()
button2.pack()
button3.pack()
class PageThree(BasePage):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
label = tk.Label(self, text="ESTADO D", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
self.next_page_name = '???'
button1 = tk.Button(self, text="Ir a C",command=self.next_page)
button2 = tk.Button(self, text="RESET", command=self.reset)
button1.pack()
button2.pack()
Similar way you could add to BasePage
function which create label and buttons
class BasePage(tk.Frame):
# ... code ...
def create_gui(self, label_text, buttons):
label = tk.Label(self, text=label_text, font=self.controller.title_font)
label.pack(side="top", fill="x", pady=10)
for text,func in buttons:
tk.Button(self, text=text, command=func).pack()
And later use it with different paramaters in different pages
class StartPage(BasePage):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
self.next_page_name = 'PageOne'
self.create_gui("ESTADO A", [("Ir a Estado B", next_page)])
class PageOne(BasePage):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
self.next_page_name = 'PageTwo'
self.create_gui("ESTADO B",
[("Ir a A", next_page), ("Ir a C", self.backward), ("RESET", self.reset)])
class PageTwo(BasePage):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
self.next_page_name = 'PageThree'
self.create_gui("ESTADO C",
[("Ir a B", next_page), ("Ir a D", self.backward), ("RESET", self.reset)])
class PageThree(BasePage):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
self.next_page_name = '???'
self.create_gui("ESTADO D",
[("Ir a C", next_page), ("RESET", self.reset)])