0

I wish to make a motor control app. I have 3 windows, which are as follows (unfortunately I cannot upload pictures yet, can I upload them some other way ):

So, what I want to do is when a user clicks then "GO" button the motor rotates and when the user clicks the "STOP" button the motor stops. After the "GO" button has been pressed I would also like to go to a different window such as the window in the top and bottom picture.

However, the GUI freezes after clicking the "GO" button. Is there a way that doesn't freeze the GUI and allow me to get the desired functionality?

#! usr/bin/env python

import threading
import serial
import time
import tkinter as tk




System_State_is_Run=False
class DH_Corp_SCADA2(tk.Tk):
    def __init__(self, *args,**kwargs):
              tk.Tk.__init__(self, *args, *kwargs)
              container=tk.Frame(self)
              container.pack(side="top",fill="both",expand=True)
              container.grid_rowconfigure(0,weight=10)
              container.grid_columnconfigure(0,weight=10)
              self.frames={}
              for F in (HomePage, ControlPage, Speed1Page):
              frame=F(container,self)
              self.frames[F]=frame
              frame.grid(row=0, column=0, sticky="nsew")

              self.show_frame(HomePage)

    def show_frame(self,cont):
        frame=self.frames[cont]
        frame.tkraise()

class HomePage(tk.Frame):
      def __init__(self, parent, controller):
         tk.Frame.__init__(self, parent)
         someLabel=tk.Label(self,text='Welcome to the AGC SCADA....')
         AGC_Control=tk.Button(self,text='AGC Control & Parameters',
              command=lambda:controller.show_frame(ControlPage))
         AGC_Speed=tk.Button(self,text='Monitor Speed',command=lambda:
                               controller.show_frame(Speed1Page))
         Exit_Button=tk.Button(self, text='Exit',command=close_window)
             someLabel.grid(row=0, sticky="W")
         AGC_Control.grid(row=2, column=1, sticky="EW",padx=2,pady=2)
         AGC_Speed.grid(row=3,column=1, sticky="EW", padx=2,pady=2)
         Exit_Button.grid(row=4,column=1, sticky="EW",padx=2,pady=2)


    class ControlPage(tk.Frame):
          def __init__(self, parent, controller):
          tk.Frame.__init__(self, parent)
          someLabel1=tk.Label(self,text='From this page you may change p.')
          Go_Home1=tk.Button(self,text='Home Page',command=lambda:controller.show_frame(HomePage))
          AGC_Speed=tk.Button(self,text='Monitor Speed',command=lambda:controller.show_frame(Speed1Page))
          Exit_Button=tk.Button(self, text='Exit',command=close_window)
          Go_Button=tk.Button(self,text='GO',command=System_State_is_RunXY1)
          Stop_Button=tk.Button(self,text='STOP', command=System_State_is_RunXY2)
           someLabel1.grid(row=0, sticky="W")
          Go_Button.grid(row=2,column=1, sticky="EW",padx=2,pady=2)
          Stop_Button.grid(row=3,column=1, sticky="EW",padx=2,pady=2)
          Go_Home1.grid(row=4, column=1, sticky="EW",padx=2,pady=2)
          AGC_Speed.grid(row=5,column=1, sticky="EW", padx=2,pady=2)
          Exit_Button.grid(row=6,column=1, sticky="EW",padx=2,pady=2)




   class Speed1Page(tk.Frame):
         def __init__(self, parent, controller):
         tk.Frame.__init__(self, parent)
         someLabel2=tk.Label(self,text='From this page you can monitor speed parameter in channel 1 or left motor.')
         Go_Home2=tk.Button(self,text='Home  Page',command=lambda
                            :controller.show_frame(HomePage))
         AGC_Control=tk.Button(self,text='AGC Control & Parameters',command=
                            lambda:controller.show_frame(ControlPage))
         Exit_Button=tk.Button(self, text='Exit',command=close_window)
         someLabel2.grid(row=0, sticky="W")
         Go_Home2.grid(row=2,column=1, sticky="EW", padx=2,pady=2)
         AGC_Control.grid(row=3, column=1, sticky="EW",padx=2,pady=2)
         Exit_Button.grid(row=4,column=1, sticky="EW",padx=2,pady=2)        



def System_State_is_RunXY1():
    global System_State_is_Run
    System_State_is_Run=True
    print(System_State_is_Run)#check if System_State_is_Run changes
    running_car()

def System_State_is_RunXY2():
    global System_State_is_Run
    System_State_is_Run=False
    print(System_State_is_Run)#check if System_State_is_Run changes
    running_car()

def running_car():
     print(System_State_is_Run)#check if System_State_is_Run changes
     if System_State_is_Run==True:
         motor_run_callback()
         running_car()
    else:
         motor_off_callback()
         return


def motor_run_callback():# set motor speed to 25rpm
    ser = serial.Serial('COM6',115200,timeout=0)
    time.sleep(0.025)
    ser.write(bytes('!G 1 250\r','utf-8'))
    ser.close()
    return

def motor_off_callback(): #set motor speed to 0
    ser = serial.Serial('COM6',115200,timeout=0)
    time.sleep(0.025)
    ser.write(bytes('!G 1 0\r','utf-8'))
    ser.close()


def close_window(): 
    app.destroy()


app=DH_Corp_SCADA2()            
app.mainloop()

1 Answers1

0
  • The button calls System_State_is_RunXY1.
  • System_State_is_RunXY1 sets System_State_is_Run to True, and then calls running_car().
    • running_car() sees that System_State_is_Run is True, and calls motor_run_callback(), and then calls itself again.
    • running_car() sees that System_State_is_Run is True, and calls motor_run_callback(), and then calls itself again.
    • running_car() sees that System_State_is_Run is True, and calls motor_run_callback(), and then calls itself again.
    • ... and so on

Nowhere in that logic do you allow the GUI to process events. Since tkinter is single threaded, you must allow the event loop to process events for the GUI to remain responsive.

Whether you are visualizing it or not, you are effectively doing an animation. Animation is typically done with the after method. You call a function which does a tiny bit of work (animates the motor, monitors a port, etc.) and then calls itself again in a few milliseconds. In the time after it finished but before it is called again, the event loop has a chance to handle events such as a click on a "Stop" button.

For a slightly longer description see https://stackoverflow.com/a/11505034/7432

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thanks Bryan, tried replacing "#running_car()" with "self.after(200, running_car)" and changed the function to "def running_car(self):" but the error I get is:Exception in Tkinter callback Traceback (most recent call last): File "C:\Users\Bra Ito\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1549, in __call__ return self.func(*args) File "C:/Users/Bra Ito/Local Settings/Documents/the python/dh corporation SCADA/DH Corp SCADA3_bryan.py", line 81, in System_State_is_RunXY1 running_car() TypeError: running_car() missing 1 required positional argument: 'self' – jkojackson Sep 21 '15 at 13:23
  • @jkojackson: you get that error because `running_car` is not a method on a class so it has no notion of "self". – Bryan Oakley Sep 21 '15 at 13:27
  • Basically whgat is the correct way of modifying the running_car() and System_State_is_RunXY1 to include the "self" and after method – jkojackson Sep 21 '15 at 13:28
  • Please help I am an absolute schmuck at this – jkojackson Sep 21 '15 at 13:29
  • @jkojackson: sorry, I don't have the time to rewrite your code for you. Stackoverflow isn't a code-writing service. Start with the example linked to in the answer. Write a program that _only_ has the go button, stop button, and a function that prints once a second. Get that working, and then worry about all of the other parts of the program. – Bryan Oakley Sep 21 '15 at 13:38
  • @Mathias Ettinger any ideas? – jkojackson Sep 21 '15 at 14:36
  • Thanks guys,i found the necessary changes. – jkojackson Sep 22 '15 at 08:14