Python/Tkinter - 带有多个参数的 运行 函数的按钮,更改 Frame 并发送一封信

Python/Tkinter - Button to run function with multiple arguments, change Frame and send a letter

我正在使用 Tkinter,我想知道如何更改框架,同时在按下按钮时向 arduino 发送一封信。 我有这个,但它不能在命令中同时处理两个语句,它们只是分开工作。

此外,我不知道如何简化代码并使“def”成为全局的,所以我不必将它们放在每个 class。

我希望让自己明白,英语不是我的第一语言。

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",

这是我的问题

        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 代码,我使用的是 Atmega2560 和 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);
}

您忘记()执行第二个函数

    tk.Button(..., command=lambda:[controller.show_frame("PageOne"), self.Forward()] )

但更可取的是为此创建单独的函数

    tk.Button(..., command=self.next_page )

def next_page(self):
    self.controller.show_frame("PageOne") 
    self.Forward()

为了使其更有用,您可以将其设置为变量

    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()

现在您可以使用 OOP 来减少代码 - 如您所说:make "def" global.

首先用函数 forwardbackwardreset

创建 class

(PEP8: lower_case_names 方法。查看更多 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()

然后使用 BasePage 创建其他页面

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()

您可以添加到 BasePage 创建标签和按钮的函数的类似方法

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()

然后在不同的页面使用不同的参数

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)])