Arduino PID with Motor, Ultrasonic Sensor, and a paper basket type coffee filter

Using a PID to control a Motor via PWM using the Ultrasonic sensor HC-SR04 to control a coffee filter distance from the sensor.

Presentation

Zip Code (Arduino Uno PID Motor and HC-SR04 with coffee filter)

Text Arduino Uno Code for PID Motor and HC-SR04 with coffee filter

/*
 * Ultrasonic Simple
 * Prints the distance read by an ultrasonic sensor in
 * centimeters. They are supported to four pins ultrasound
 * sensors (like HC-SC04) and three pins (like PING)))
 * and Seeed Studio sensors).
 *
 * The circuit:
 * * Module HR-SC04 (four pins) or PING))) (and other with
 *   three pins), attached to digital pins as follows:
 * ---------------------    --------------------
 * | HC-SC04 | Arduino |    | 3 pins | Arduino |
 * ---------------------    --------------------
 * |   Vcc   |   5V    |    |   Vcc  |   5V    |
 * |   Trig  |   12    | OR |   SIG  |   13    |
 * |   Echo  |   13    |    |   Gnd  |   GND   |
 * |   Gnd   |   GND   |    --------------------
 * ---------------------
 * Note: You do not obligatorily need to use the pins defined above
 * 
 * By default, the distance returned by the read()
 * method is in centimeters. To get the distance in inches,
 * pass INC as a parameter.
 * Example: ultrasonic.read(INC)
 *
 * created 3 Apr 2014
 * by Erick Simões (github: @ErickSimoes | twitter: @AloErickSimoes)
 * modified 23 Jan 2017
 * by Erick Simões (github: @ErickSimoes | twitter: @AloErickSimoes)
 * modified 03 Mar 2017
 * by Erick Simões (github: @ErickSimoes | twitter: @AloErickSimoes)
 * modified 11 Jun 2018
 * by Erick Simões (github: @ErickSimoes | twitter: @AloErickSimoes)
 *
 * This example code is released into the MIT License.
 */
//above is from orignal UltrsonicSimple example to give mentioning credit

//this code below is a modification from the example for the coffee filter motor and distance

#include <Ultrasonic.h> //ES's Ultrasonic library  ( I used the 4pin version of HC-SR04)
#include <PID_v1.h>   //BB's PID

#define MotorPWM 6  // motor control via the TIP120

/*
 * Pass as a parameter the trigger and echo pin, respectively,
 * or only the signal pin (for sensors 3 pins), like:
 * Ultrasonic ultrasonic(13);
 */
Ultrasonic ultrasonic(12, 13);
double distance,distance_avg;
double motor;
double Setpoint=60; // cm
double avg=100;
double avg_meas;
unsigned long Setpoint_change=0;

//Specify the links and initial tuning parameters
double Kp=1, Ki=10, Kd=0.0;


PID myPID(&distance,&motor, &Setpoint, Kp, Ki, Kd,REVERSE);  // reverse not direct

void setup() {
  Setpoint=42;
  Serial.begin(9600);
    //turn the PID on
  myPID.SetMode(AUTOMATIC);   
  myPID.SetOutputLimits(0,255);
  myPID.SetSampleTime(50);
}

void loop() {
  // Pass INC as a parameter to get the distance in inches

 for (int i=0;i<avg;i++) {
  distance_avg = ultrasonic.read();
  avg_meas=avg_meas + distance_avg;
 }
 avg_meas=avg_meas/avg; 
 distance=avg_meas;
  
  myPID.Compute();
  //motor=map(distance,0,90,0,255);
  Serial.print("Setpoint:");
  Serial.print(Setpoint);
  Serial.print(",");   
  Serial.print("MotorPWM:");
  Serial.print(motor);   //motor is getting updated by the myPID.Compute
  //motor=255-motor;  // if closer to sensor less cm also less motor
  Serial.print(",");   
  analogWrite(MotorPWM, motor);
  Serial.print(" Distance_CM:");
  Serial.println(distance);
  delay(2);
  avg_meas=0;
}

Notes

  • Presentation: Arduino® Uno with BB’s PID with Motor , Distance Sensor with ES’s Ultrasonic library, and Coffee filter
  • Programming Language used: Arduino® IDE
  • Hardware Use: Motor, HHC-SR04 distance Sensor, TIP120 transistor ,Arduino® Uno ,Solderless Breadboard. Various jumper type wire.
  • Helpful resource: Arduino communities and Arduino’s IDE library manager.
  • Arduino® products of respective companies
  • Presentation shown to spark ideas of use.
  • This presentation is not connected to or endorsed by any company.
  • Use at your own risk.
  • Tags: PID, Kp, Ki, kd, derivative , integral, Serial plotter
  • Title Tag: Arduino® Uno and BB’s PID, Motor, Distance Sensor, HC-SR04, TIP120, PID, coffee filter
  • Presentation is part of Blog content: https://testengineerresource.com/
Posted in Test Sector | Tagged , , , , , , , , , | Leave a comment

Using Arduino and BB’s PID with Simple LED Brightness setup to Learn PID

Presentation

Arduino Code for this project

//********************************************************
// * PID Basic Example used as starting point with change below
// * Reading analog input 0 to control analog PWM output 6
// ********************************************************/

#include <PID_v1.h>

#define PIN_INPUT 0
#define PIN_OUTPUT 6

//Define Variables we'll be connecting to
double Setpoint, Input, Output;
double Input_Map,avg_meas;
//Specify the links and initial tuning parameters
double Kp=0.04, Ki=11.00, Kd=0.0001;
double error;
double avg=100;
unsigned long Setpoint_change=0;


PID myPID(&Input,&Output, &Setpoint, Kp, Ki, Kd,DIRECT);  //

void setup()
{
  //initialize the variables we're linked to
  Input = analogRead(PIN_INPUT);

  Setpoint = 700;
  Serial.begin(9600);
  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(0,255);
  myPID.SetSampleTime(20);
}

void loop()
{

    if(Setpoint_change==100){
    Setpoint=500;
    }
  if(Setpoint_change==200){
    Setpoint=300;
  }
  if(Setpoint_change==300){
    Setpoint=700;
    }
  if(Setpoint_change==400){
    Setpoint=512;
    }        
  
  

 for (int i=0;i<avg;i++) {
  Input_Map = analogRead(PIN_INPUT);
   avg_meas=avg_meas + Input_Map;
   }
  avg_meas=avg_meas/avg; 
  Input=avg_meas;
   myPID.Compute();
  //delay(1); //ms
  analogWrite(PIN_OUTPUT, Output);
  Setpoint_change=Setpoint_change+1;
  Serial.print("Input:");
  Serial.print(Input);
  Serial.print(",");
  Serial.print("Output:");
  Serial.print(Output);
  Serial.print(",");
  Serial.print("Setpoint:");
  Serial.print(Setpoint);
  Serial.print(",");
  error=Setpoint-Input;   //was input-setpoint  but not traditonal
  Serial.print("error:");
  Serial.print(error);
  Serial.print(",");
  Serial.print("maxout:");
  Serial.print(255);
  Serial.println(","); 
  
}

Zip code

Notes

  • Presentation: Arduino® Uno with BB’s PID with Light and detect circuit
  • Programming Language used: Arduino® IDE
  • Hardware Use: Snapino™ (using the phototransistor), Keyes(using its One Led light module has transistor in its module) other jumper I got from MicroCenter store.
  • Helpful resource: Stackoverflow, Search Engines , Bing chat AI, and Arduino communities.
  • Bing is a Microsoft’s product.
  • Arduino® ,Snapino™, and Keyes are products of respective companies
  • Presentation shown to spark ideas of use.
  • This presentation is not connected to or endorsed by any company.
  • Use at your own risk.
  • Tags: PID, Kp, Ki, kd, derivative , integral, Serial plotter
  • Title Tag: Arduino® Uno and BB’s PID
  • Presentation is part of Blog content: https://testengineerresource.com/
Posted in Test Sector | Tagged , , , , , , | Leave a comment

Python-A way to show digital patterns(bits) in Matplotlib

Presentation on a way to show digital patterns (bits) visually in Matplotlib

drawstyle was helpful but did not show the pattern correctly 100%(for me)

below is my presentation followed by the code.

Presentation

Python Code Text

# -*- coding: utf-8 -*-
"""
Created on Fri May 19 21:01:14 2023

@author: aleja
"""

import matplotlib.pyplot as plt

#this demo uses same size bits for all data
#real data (you will use for any processing _ the visual data is only for plots)
data0=[1,1,1,1,0,0,0,0]
data1=[1,1,0,0,0,0,0,0]
data2=[1,0,1,0,1,0,1,0]
data3=[0,0,0,0,1,1,1,1]
data4=[0,0,0,0,0,0,0,1]
plots=[data0,data1,data2,data3,data4] #show this digital pattern

#the many step way to make [0-------n] in data1
# x_data_pt=[]
# x= len(data1)
# print(x) 
# for i in range(x):   
#     x_data_pt.append(i)
# print (x_data_pt)
#fix my plot so it looks ok (fix width digital bit)
def Make_My_Dig_Plot_look_ok(datas_holder):
    data_long=[]
    visual_plots=[]
    for n,data in enumerate(plots):
        data_long=[]
        for bit in data:        
            for i in range(1):
             data_long.append(bit)
        data_long.append(bit)
        visual_plots.insert(n,data_long)
    print(visual_plots)
    return visual_plots        #for visual

new_plots=Make_My_Dig_Plot_look_ok(plots)
x_data_pt = [(i-0.5) for i in range(len(new_plots[0]))]  #the one line way and a slight 1/2 bit shift 
# print(x_data_pt)
plt.clf()
plt.close()
fig,axs=plt.subplots(len(new_plots),sharex=True)  # add sharex if you dont want all x axis number on each, odd but works num="Title"
mngr = plt.get_current_fig_manager()
mngr.window.setGeometry(20,60,640, 545)  #Location and size
mngr.window.setWindowTitle("A way to show digital bits multiple patterns")  #I am using this way to put title on figure window
# https://stackoverflow.com/questions/38307438/set-matplotlib-default-figure-window-title
fig.suptitle('Digitial Pattern', fontsize=16)
for plotnum in range(len(plots)):
#     #none of these are exactly what i want with the Origianl data is the closest but its chops the left and right half width (dont like that look)
#     with the function fixing step=post is the best for me
      #if plotnum==0:axs[plotnum].set_title("Digital Patterns")
      axs[plotnum].plot(x_data_pt,new_plots[plotnum],drawstyle="steps-post")
      axs[plotnum].set_ylabel("Data"+str(plotnum))  #how to put y axis label
#     axs[plotnum].plot(x_data_pt,plots[plotnum],drawstyle="steps-mid") #almost right but chops my least significant bit and most sig bit width
#     #axs[plotnum].plot(x_data_pt,plots[plotnum],drawstyle="steps-pre")
#     #axs[plotnum].plot(x_data_pt,plots[plotnum],drawstyle="steps")
    
    

Python Code Zip

Notes

  • Presentation: Showing a possible way to show digital pattern (bits) in Matplotlib
  • Programming Language used: Python 3.9 in Spyder5.4.3,Matplotlib 3.7.1
  • Presentation app: Microsoft’s PowerPoint
  • Helpful resource: Stackoverflow, Search Engines , Bing chat AI, and Python communities.
  • Bing is a Microsoft’s product.
  • Python, Tkinter, and Matplotlib are products of respective companies
  • Presentation shown to spark ideas of use.
  • This presentation is not connected to or endorsed by any company.
  • Use at your own risk.
  • Tags: Python, Python3.9, Matplotlib , Digital (bits) Patterns
  • Title Tag: Showing ‘a’ possible way to show digital pattern (bits) in Matplotlib
Posted in Test Sector | Tagged , , , | Leave a comment

Python – A version of Producer Consumer Pattern with Queues and Threading – Simulated Temp Chamber Demo

Presentation on a version of producer consumer pattern using queues and threading.

Used Python,tkinter, and Matplotlib

Presentation is followed with a short video and zip code

May17,2023 added the Ipython console output image on post so you can see the thread stopping at window close.

Presentation

Other Info

On window close(x) you can see the three thread stop

Short Video about 50sec

python code zip

Notes

  • Presentation: A Producer consumer type pattern , using temperature chamber testing as example (temperature, ps, uut are simple simulator for demo.)
  • Programming Language used: Python 3.9 in Spyder5.4.3,Matplotlib 3.7.1
  • Presentation app: Microsoft’s PowerPoint
  • Helpful resource: Stackoverflow, Search Engines , Bing chat AI, and Python communities.
  • Bing is a Microsoft’s product.
  • Python, Tkinter, and Matplotlib are products of respective company
  • Presentation shown to spark ideas of use.
  • This presentation is not connected to or endorsed by any company.
  • Use at your own risk.
  • Tags: Python, Python3.9, Tkinter , Matplotlib ,GUI, List, StringVar, Simulated Temperature chamber Testing, Threading,Queues
  • Title: Python – A version of Producer Consumer Pattern with Queues and Threading – Simulated Temp Chamber Demo
Posted in Test Sector | Tagged , , , , , , , | Leave a comment

InProcess: Python – A version of Producer Consumer Pattern

Python Tkinter and Matplotlib. Still working on it , but this is a preview

Using more Classes and Threads the video below is at normal speed (running a quick temp chamber profile. Most temperature profile don’t run this fast in real because temperature change is a slow process , but for the video and my viewer running faster profile. Currently plotting is adding 100ms Plot redraw. Working on it. (I am using “canvas.draw_idle()” which is faster than “canvas.draw()”) . Its the StringVar() variable are a bit blocking. Spend alot of time on the matplotlib section.

Classes

  • Temperature Chamber (simulated)
  • Power Supply(simulated)
  • UUT(simulated)
  • Sequence (to be added)

Short Video 60sec

Without updating the center Profile section and DegC in label tkinter StringVar (very fast) 14sec video

With the Profile and DegC StringVar() variables updating the Label in the center of the gui 60sec video

Notes

  • Presentation: In-process, A version of Producer Consumer type pattern (with classes and threading)
  • Programming Language used: Python 3.9 in Spyder 5.4.3,Matplotlib 3.7.1
  • Presentation app: Microsoft’s PowerPoint
  • Help support: StackOverFlow, search engines, and yes the new Bing Chat-AI engine.
  • Bing mention is a Microsoft Product.
  • Python and Tkinter are products of respective company
  • Presentation shown to spark ideas of use.
  • This presentation is not connected to or endorsed by any company.
  • Use at your own risk.
  • Tags: Python, Python3.9, Tkinter , Canvas ,GUI, List, StringVar, ListSelect, Classes, Threading. A producer consumer type pattern.

Thank you to all my viewer and support from the python communities

Posted in Test Sector | Tagged , , , , , | Leave a comment

Tkinter GUI and (non-tk) Matplotlib shrinking the tk GUI

Presentation on “my” journey to find a solution for the unexpected shrinking of the Tkinter GUI when making a plot.

Notes

  • Presentation: Tkinter GUI with external non-Tkinter Matplotlib plot – the issue- Tkinter GUI was shrinking when Matplotlib plot created, I am using spyder with ipython for development
  • Programming Language used: Python 3.9 in Spyder 5.4.3,Matplotlib 3.7.1 my adventure to find a solution .
  • Lot of thanks to the python community, Stackoverflow of course, and search engine and Ai engines.
  • Presentation app: Microsoft’s PowerPoint
  • Window 10 : a Microsoft Product
  • Python ,Tkinter and Matplotlib are products of respective company.
  • Presentation shown to spark ideas of use.
  • This presentation is not connected to or endorsed by any company.
  • Use at your own risk.
  • Tags: Python, Python3.9, Tkinter , Matplotlib, Shrinking Tkinter GUI, SetProcessDpiAwareness(0)

Thanks to all the python communities and my viewers

Posted in Test Sector | Tagged , , , , , , | Leave a comment

Python – Sequence Editor example

a Python tkinter Sequence Editor example with a simple UUT(Magnetic crane in canvas)

Presentation

Short Video of Sequence Editor with sequence running aprox 18sec

Python code text

# -*- coding: utf-8 -*-
"""
Created on Thu Apr 20 11:16:31 2023

@author: aleja

App: Sequence Editor (a simplified one level Sequence editor)
Plan: Use a previous app to create the Sequence Editor
      1) Command List with tab section
      2) A List to hold the Sequence being created
      3) Some button to Move command to Sequence List
      4) create some simplified UUT(something to control with the sequence list)
Window layout
 MainWindow
   TopFrame(title and company labels)
  Frame1(command list), LeftFrame(Sequence List),TopFrame (simulated UUT to test out sequence)

      

"""

#from tkinter import *
import tkinter as tk
import tkinter.ttk as ttk
#import math
import time
#import threading
import webbrowser



class MyApp:
    def __init__(self,tk):
        #GUI setup below
        self.myWindow=tk.Tk()
        self.myWindow.title("myWindow-SequenceEditor GUI")
        w_w=920
        w_h=400
        self.myWindow.minsize(w_w,w_h)
        #self.myWindow.maxsize(w_w,w_h)   #fix widow size
        
        
        
        # if you ever notice that some frame is sticking to bottom look at these setting
        self.myWindow.grid_rowconfigure(1,weight=1) #dont want top row frame to grow(row=0)
        self.myWindow.grid_columnconfigure(2,weight=1)
        self.myWindow.configure(background='gray')
 
        self.SearchEngine=""      

    def mainloop(self):
        tk.mainloop()
    
    def MakeFrames(self):
        
        #Top         
        self.TopFrame=tk.Frame(self.myWindow,width=100,height=100,bg='lightblue')
        self.TopFrame.grid(row=0, column=0, sticky='NESW', padx=10, pady=10, columnspan=3) #spans 3 cols (3 frames row1)
        self.TopFrame.rowconfigure(0,weight=1)
        self.TopFrame.columnconfigure(0,weight=1) #the lastcol goes along with rigth side expanding
                
        row1Height=300
        self.LeftFrame=tk.Frame(self.myWindow,width=300,height=row1Height,bg='lightblue')
        self.LeftFrame.grid(row=1, column=0, sticky='NWS', padx=(10,10), pady=(0,10), columnspan=1,rowspan=3) #padding ALSO takes two arguments tuple
        self.LeftFrame.rowconfigure((1,2,3),weight=1)
        self.LeftFrame.columnconfigure((0,1,2),weight=1) #the lastcol goes along with rigth side expanding
  
        self.CenterFrame=tk.Frame(self.myWindow,width=200,height=row1Height,bg='lightblue')
        self.CenterFrame.grid(row=1, column=1, sticky='NWES', padx=(0,0), pady=(0,10), columnspan=1,rowspan=3) #padding ALSO takes two arguments tuple
        self.CenterFrame.rowconfigure((1),weight=1)
        self.CenterFrame.columnconfigure((1),weight=1) #the lastcol goes along with rigth side expanding


        #some UUT to control with sequence sim frame
        self.RightFrame=tk.Frame(self.myWindow,width=350,height=row1Height,bg='lightblue')
        self.RightFrame.grid(row=1, column=2, sticky='NEWS', padx=(10,10), pady=(0,10), columnspan=1,rowspan=3) #padding ALSO takes two arguments tuple
        self.RightFrame.rowconfigure(1,weight=1)
        self.RightFrame.columnconfigure(2,weight=1) #the lastcol goes along with rigth side expanding




    def TopFrameWidget(self,strAppTitle="YourAppTitle",strAppCompany="YourCompanyName"):
        self.AppTitle=strAppTitle
        self.AppCompany=strAppCompany
        #TopFrame info
        AppTitleLabel=tk.Label(self.TopFrame, width=100, text=self.AppTitle)
        CompanyName=tk.Label(self.TopFrame, text=self.AppCompany)
        AppTitleLabel.grid(row=0, column=0, padx=10, pady=10, columnspan=2) #span 2 so it does not affect component button
        CompanyName.grid(row=0, column=3, padx=10, sticky='E') #Keep
        
    # Notebook add so this is only title and key words.
    #in a bigger app this will be loaded from your own file but I need it to be self contain for demo
    def Show_Keyword_Selection_ListBox(self):
        self.List_Of_Commands=["MoveLeft","MoveRight","MoveUp","MoveDown",\
                              "MagnetOn","MagnetOff","Reset"]  #The List of choices possible to get list from a file but demo is only this file

        self.List_Of_Actions=["AlarmLightOn","AlarmLightOff"]  #The List of choi            
 
        #add your tab list here
        self.LOKW_List=[self.List_Of_Commands,self.List_Of_Actions] 
            
        self.List_Of_Keyword_Var=tk.StringVar(value=self.List_Of_Commands) # you can put the starting list in StringVar
        
   
        #Sequence Title
        KW_Sel_LBox_Title=tk.Label(self.LeftFrame,text="Command Selection",bg='lightblue')
        KW_Sel_LBox_Title.grid(row=0,column=0,padx=10,pady=(10,0),sticky="N")
  
  
        
    def Show_Move_To_Right_Button(self):
       
        MovToRight_btn=tk.Button(self.LeftFrame,text="--->",command=lambda:self.Add_To_Query())
        #print(self.KW_Sel_LBox.curselection())
        MovToRight_btn.grid(row=1,column=1,padx=10,pady=10)
        #self.KW_Sel_LBox.focus()
        #self.KW_Sel_LBox.select_set(0,0)

    def Seq_Clicked(self,event=None):
        print(str(event))
        print("Seq_Clicked")
        #Test if Empty
        SeqListIndex=self.Seq_Lb.curselection()
        if (SeqListIndex):
            print("something is blue")
            self.action_mode.set("Seq_Selected")
        else:
            self.action_mode.set("Seq_Bottom")
            
    
    def Show_Sequence(self):
        self.Seq_Lb=tk.Listbox(self.CenterFrame,width=15,height=10,font=("Arial",14,''))
        self.Seq_Lb.bind("<<ListboxSelect>>",self.Seq_Clicked)
        self.Seq_Lb.grid(row=0,column=0,columnspan=2,rowspan=3,sticky='NWS',padx=10,pady=(20,10))
        self.Seq_Lb.columnconfigure(0,weight=1)
        self.Seq_Lb.rowconfigure(0,weight=1)     
        #attach a Scrollbar to txt1
        vscroll=tk.Scrollbar(self.CenterFrame,orient='vertical',command=self.Seq_Lb.yview)
        self.Seq_Lb['yscroll']=vscroll.set
        vscroll.grid(row=0,column=0,columnspan=2,rowspan=3,sticky='NSE',padx=10,pady=(20,10))
        

    # #move to seq list button
    # def Show_Move_Down_Right_Button(self):       
    #     MovTo_Down_btn=tk.Button(self.LeftFrame,text="|\n|\nV",command=lambda:self.Add_Q_To_Query())
    #     MovTo_Down_btn.grid(row=1,column=2,padx=10,pady=10)
    #     self.KW_Sel_LBox.focus()
    #     self.KW_Sel_LBox.select_set(0)
        
       
    def Add_To_Query(self):
        tab_index=self.tab_Holder.index('current')   # a way to get the index of tab selected    
        print(self.tab_Holder.tab(self.tab_list[tab_index],"text")) # a way to get selected tab name
        #print(self.scrollv_text_list[tab_index].curselection())
        self.KW_Sel_LBox=self.scrollv_text_list[tab_index]  
        IndexsList=list(self.KW_Sel_LBox.curselection())  #tuple to list (incase LBox is multiselect future)
        #print(IndexsList)
        if IndexsList:  #only if something in list
            for Index in IndexsList:                #for now only select one item at a time
             strText=self.KW_Sel_LBox.get(Index)
            #NO Delete  self.KW_Sel_LBox.delete(Index)
            #self.KW_Sel_LBox.selection_clear(0,'end') #way to clear all(no blue)
            #self.KW_Sel_LBox.selection_set(0)    #set to top of list
            
            #self.strText_KW=self.strText_KW+strText
            print(self.action_mode.get())
            if (self.action_mode.get()!="SingleStepRun"):
                Seq_IndexList=list(self.Seq_Lb.curselection())  #tuple to list   only one since multiselect not on
                if (Seq_IndexList): #someone select in seq list
                  for SeqIndex in Seq_IndexList:
                      self.Seq_Lb.insert(SeqIndex,strText)
                else:      
                  self.Seq_Lb.insert('end',strText)
                  self.Seq_Lb.see('end') #move scroll to end
            else:
                self.SingleStepRunCmd=strText
                print(self.SingleStepRunCmd)
                self.Run_Seq(True)


    def Show_Clear_Button(self):
        self.Clr_btn=tk.Button(self.CenterFrame,text="Clear Seq?",command=lambda:self.ClrQuery())
        self.Clr_btn.grid(row=3,column=0,padx=10,pady=(10,20),sticky="N")
        self.Clr_btn.rowconfigure(3,weight=1)

    def ClrQuery(self):
        print(self.myWindow.winfo_width(),self.myWindow.winfo_height())
        self.strText=""
        self.strText_KW=""
        self.Seq_Lb.focus()
        self.Seq_Lb.delete(0,'end')
        self.Seq_Lb.selection_clear(0,'end')
        
    def Delete_Selected_SeqList(self):
        IndexsList=list(self.Seq_Lb.curselection())  #tuple to list (incase LBox is multiselect future)
        #print(IndexsList)
        if IndexsList:  #only if something in list selected
            for Index in IndexsList:                #for now only select one item at a time
             self.Seq_Lb.delete(Index)        
            
    def Show_Del_Button(self):
        self.Del_btn=tk.Button(self.CenterFrame,text="Del Sel",command=lambda:self.Delete_Selected_SeqList())
        self.Del_btn.grid(row=3,column=1,padx=10,pady=(10,20),sticky="N")
        self.Del_btn.rowconfigure(3,weight=1)        

    def Show_RunSeq_Button(self):
        self.Clr_btn=tk.Button(self.CenterFrame,text="Run Seq",command=lambda:self.Run_Seq())
        self.Clr_btn.grid(row=3,column=2,padx=10,pady=(10,20),sticky="N")
        self.Clr_btn.rowconfigure(3,weight=1)

    
    def Run_Seq(self,SingleStepMode=False):
        self.Seq_Lb.focus()
        print (self.Seq_Lb.get(0,"end"))
        #self.Seq_List=list(self.Seq_Lb.get(0,"end"))
        if(SingleStepMode==False):
          self.Seq_List=list(self.Seq_Lb.get(0,"end"))
        else:
          self.Seq_List=[]  
          self.Seq_List.append(self.SingleStepRunCmd)  
            
        
        for i,item in enumerate(self.Seq_List):
            self.Seq_Lb.select_set(i)
            if (item=="MoveLeft" and self.Stop_Move_Left==0): 
                if(self.Magnet_Field==1 and self.Magnet_Ball==1):self.Move_Left()
                print(self.Magnet_Field)
                self.Move_Magnet_Left()
                self.Move_Chain_Left()
                if(self.Magnet_Field==1):
                    self.Magnet_On()
                
            if (item=="MoveRight" and self.Stop_Move_Right==0):
                if(self.Magnet_Field==1 and self.Magnet_Ball==1):self.Move_Right()
                self.Move_Magnet_Right()
                self.Move_Chain_Right()
                if(self.Magnet_Field==1):
                    self.Magnet_On()
                
            if (item=="MoveUp"):
                self.Stop_Move_Down=0
                self.Stop_Magnet_Down=0
                if(self.Magnet_Field==1 and self.Magnet_Ball==1 and self.Stop_Move_Up==0):self.Move_Up()
                if(self.Stop_Magnet_Up==0):
                    self.Move_Magnet_Up()
                    self.Move_Chain_Up()
                if(self.Magnet_Field==1):
                    self.Magnet_On()
                
            if (item=="MoveDown"):
                self.Stop_Move_Up=0
                self.Stop_Magnet_Up=0
                if(self.Magnet_Field==1 and self.Magnet_Ball==1 and self.Stop_Move_Down==0):self.Move_Down()
                if(self.Stop_Magnet_Down==0):
                    self.Move_Magnet_Down()
                    self.Move_Chain_Down()
                if(self.Magnet_Field==1):
                    self.Magnet_On()
                
            if (item== "Reset"):  self.Move_Center()
            if (item=="MagnetOn"): self.Magnet_On()
            if (item=="MagnetOff"): self.Magnet_Off()
            if (item=="AlarmLightOn"):self.Alarm_Light(1)
            if (item=="AlarmLightOff"):self.Alarm_Light(0)
            self.myWindow.update()
            self.Seq_Lb.select_clear(i)
            time.sleep(0.05)   #simple for now 
            print(i,item)
            
        
     
    #used by Show_Tabs via notebook event
    def Tab_Changed_Event(self,event=None):
        print("Tab changed")
        tab_index=self.tab_Holder.index('current')   # a way to get the index of tab selected 
        self.KW_Sel_LBox=self.scrollv_text_list[tab_index]
        print(self.tab_Holder.tab(self.tab_Holder.select(),"text"))
        self.scrollv_text_list[tab_index].focus()
        self.scrollv_text_list[tab_index].selection_clear(0,'end')
        self.scrollv_text_list[tab_index].select_set(0)
    
        


    def Show_Tabs(self):
        self.tab_Holder=ttk.Notebook(self.LeftFrame,style='Custom.TNotebook',width=280,height=250)
        self.tab_Holder.grid(row=0,column=0,sticky='NS',columnspan=1,rowspan=3,padx=20,pady=30)
        self.tab_Holder.rowconfigure(1,weight=1)
        self.tab_Holder.columnconfigure(1,weight=1)
        
        #need to use newer way ,this is still old way dont need tab name just order in a List
        tab1=tk.Frame(self.tab_Holder)  #tab obj to notebook but not added yet
        tab1.columnconfigure(0,weight=1) #lessons learn You must tell tab object that thing inside can auto grow
        tab1.rowconfigure(0,weight=1)
        tab2=tk.Frame(self.tab_Holder)
        tab2.columnconfigure(0,weight=1)
        tab2.rowconfigure(0,weight=1)

        self.tab_list=[tab1,tab2] # for adding scroll text obj in each tab


        #self,tab_Holder.bind("<<NotebookTabChanged>>",tab_event_handler)
        self.tab_Holder.add(tab1,text="SequenceCommand_KW")
        self.tab_Holder.add(tab2,text="SequenceAction_KW")
        #self.tab_Holder.add(tab3,text="Other_KW")

        self.scrollv_text_list=[]
        
        for i,tab in enumerate(self.tab_list):     # enumerate Leason Learn cool
            txt1=tk.Listbox(tab,width=30,height=20,font=("Arial",14,''),exportselection =False)
            txt1.grid(row=0,column=0,columnspan=2,rowspan=3,sticky='NWES',padx=20,pady=(20,20))
            txt1.bind("<<ListboxSelect>>",self.Selected_Cmd)
            txt1.columnconfigure(0,weight=1)
            txt1.rowconfigure(0,weight=1)     
            #attach a Scrollbar to txt1
            vscroll=tk.Scrollbar(tab,orient='vertical',command=txt1.yview)
            txt1['yscroll']=vscroll.set
            vscroll.grid(row=0,column=1,sticky='NSE',padx=20,pady=20)
            self.scrollv_text_list.append(txt1)            
            for KW in self.LOKW_List[i]:
                txt1.insert('end',KW)
        self.tab_Holder.select(self.tab_list[0])    #default tab of notebook
        self.scrollv_text_list[0].focus()
        self.scrollv_text_list[0].select_set(0)
        self.tab_Holder.bind("<<NotebookTabChanged>>",self.Tab_Changed_Event)

    def Selected_Cmd(self,event=None):
        print("Command Selected")
        IndexsList=list(self.KW_Sel_LBox.curselection())
        if(IndexsList): #nothing selected dont do anything
            str_command=(self.KW_Sel_LBox.get(IndexsList))
            print(str_command)
            #List that required one arg
            OneArg_List=["MoveLeft","MoveRight","MoveUp","MoveDown"]  #future as possible arg tab
            #if(str_command in OneArg_List):
            #     print("required amount arg")
        


    def Show_SimUUT(self):
        
        UUT_Width=300
        UUT_Height=300
        Dot_Padding=10
        Center_Dot_x=UUT_Width/2+Dot_Padding
        Center_Dot_y=UUT_Height/2+Dot_Padding
        Center_Dot_radius=20
        self.Floor_y=UUT_Height-10
        
        self.canv1=tk.Canvas(self.RightFrame,width=UUT_Width+30, height=UUT_Height+30, bg='lightblue', bd=0, highlightthickness=0)
        self.canv1.grid(row=0, column=0, columnspan=1,padx=10,pady=10,sticky="NSEW")
        self.Ball=self.canv1.create_oval(Center_Dot_x-Center_Dot_radius,Center_Dot_y-Center_Dot_radius,\
                          Center_Dot_x+Center_Dot_radius,Center_Dot_y+Center_Dot_radius,fill='blue') 
        self.Ball_Reset=self.canv1.coords(self.Ball)

        #Magnet
        self.Magnet_Field=0 #0=off 1=on
        self.Stop_Move_Left=0
        self.Stop_Move_Right=0
        self.Stop_Move_Up=0
        self.Stop_Move_Down=0
        self.Stop_Magnet_Down=0
        self.Stop_Magnet_Up=0
        self.Magnet_Field=1
        
        
        self.Magnet_Width=50
        self.Magnet_Height=20
        self.Magnet_Top_short=30
        self.Magnet_X1=UUT_Width/2-Dot_Padding-Center_Dot_radius
        self.Magnet_Y1=UUT_Height/2-self.Magnet_Height-Center_Dot_radius/2
        
        self.Magnet_Ploy_List=[self.Magnet_X1+self.Magnet_Top_short,self.Magnet_Y1,\
                              self.Magnet_X1+self.Magnet_Width,self.Magnet_Y1,\
                              self.Magnet_X1+self.Magnet_Width+self.Magnet_Top_short,self.Magnet_Y1+self.Magnet_Height,\
                              self.Magnet_X1,self.Magnet_Y1+self.Magnet_Height]

        
        self.Magnet=self.canv1.create_polygon(self.Magnet_Ploy_List, fill="brown",outline="black")
        self.Magnet_Reset=self.canv1.coords(self.Magnet)
        #Top part of beam
        self.canv1.create_rectangle(10,10,300,20, fill='yellow',outline='black')
        #Left side vert beam
        self.canv1.create_rectangle(10,10,20,300, fill='yellow',outline='black')
        #chain
        self.Chain_Thick=10
        self.Chain=self.canv1.create_rectangle(150-self.Chain_Thick/2+10,10,150+self.Chain_Thick/2+10,120,fill='orange',outline='black')
        self.Chain_Reset=self.canv1.coords(self.Chain)
        
        
        
    def Move_Left(self):
        #almost sure thare a better way to enum this
        x1=0
        y1=1
        x2=2
        y2=3
        self.Ball_coords=self.canv1.coords(self.Ball)
        print(self.Ball_coords)
        self.Ball_coords[x1]=self.Ball_coords[x1]-10
        self.Ball_coords[x2]=self.Ball_coords[x2]-10
        self.canv1.coords(self.Ball,self.Ball_coords)
        #self.canv1.coords(self.Ball, 10,10 ,20,20)
        

        
    def Move_Right(self):
        #almost sure thare a better way to enum this
        x1=0
        y1=1
        x2=2
        y2=3
        self.Ball_coords=self.canv1.coords(self.Ball)
        print(self.Ball_coords)
        self.Ball_coords[x1]=self.Ball_coords[x1]+10
        self.Ball_coords[x2]=self.Ball_coords[x2]+10
        self.canv1.coords(self.Ball,self.Ball_coords)
        #self.canv1.coords(self.Ball, 10,10 ,20,20)        

    def Move_Up(self):
        #almost sure thare a better way to enum this
        x1=0
        y1=1
        x2=2
        y2=3
        self.Ball_coords=self.canv1.coords(self.Ball)
        print(self.Ball_coords)
        self.Ball_coords[y1]=self.Ball_coords[y1]-10  #remember up is down in cavs coord
        self.Ball_coords[y2]=self.Ball_coords[y2]-10
        self.canv1.coords(self.Ball,self.Ball_coords)
        #self.canv1.coords(self.Ball, 10,10 ,20,20)

    def Move_Down(self):
        #almost sure thare a better way to enum this
        x1=0
        y1=1
        x2=2
        y2=3
        self.Ball_coords=self.canv1.coords(self.Ball)
        print(self.Ball_coords)
        self.Ball_coords[y1]=self.Ball_coords[y1]+10
        self.Ball_coords[y2]=self.Ball_coords[y2]+10
        self.canv1.coords(self.Ball,self.Ball_coords)
        #self.canv1.coords(self.Ball, 10,10 ,20,20)        
    
    def Move_Center(self):
        #almost sure thare a better way to enum this
        x1=0
        y1=1
        x2=2
        y2=3
        self.canv1.coords(self.Ball, self.Ball_Reset)
        self.canv1.coords(self.Magnet,self.Magnet_Reset)
        self.canv1.coords(self.Chain,self.Chain_Reset)
        #Magnet
        self.Magnet_Field=0 #0=off 1=on
        self.Stop_Move_Left=0
        self.Stop_Move_Right=0
        self.Stop_Move_Up=0
        self.Stop_Move_Down=0
        self.Stop_Magnet_Down=0
        self.Stop_Magnet_Up=0
        self.Magnet_Field=1
        self.Magnet_Ball=1
        self.Light=0


    def Move_Magnet_Left(self):
        self.Magnet_coords=(self.canv1.coords(self.Magnet))
        Xs=[0,2,4,6]
        Ys=[1,3,5,7]
        if(self.Stop_Move_Left==0):
            for x in Xs:
                self.Magnet_coords[x]-=10
                if(self.Magnet_coords[x]<=20):self.Stop_Move_Left=1
            self.canv1.coords(self.Magnet,self.Magnet_coords)
            print("Magnet Position",self.Magnet_coords)
        self.Stop_Move_Right=0    
            
    def Move_Magnet_Right(self):
        self.Magnet_coords=(self.canv1.coords(self.Magnet))
        Xs=[0,2,4,6]
        Ys=[1,3,5,7]
        if(self.Stop_Move_Right==0):
            for x in Xs:
                self.Magnet_coords[x]+=10
                if(self.Magnet_coords[x]>=300):self.Stop_Move_Right=1
            self.canv1.coords(self.Magnet,self.Magnet_coords)
            print("Magnet Position",self.Magnet_coords)
        self.Stop_Move_Left=0              

    def Move_Magnet_Up(self):
        self.Magnet_coords=(self.canv1.coords(self.Magnet))
        Xs=[0,2,4,6]
        Ys=[1,3,5,7]
        
        for y in Ys:
            self.Magnet_coords[y]-=10
            if(self.Magnet_coords[y]<=30):
                self.Stop_Move_Up=1
                self.Stop_Magnet_Up=1
        self.canv1.coords(self.Magnet,self.Magnet_coords)
        self.Stop_Magnet_Down=0

    def Move_Magnet_Down(self):
        self.Magnet_coords=(self.canv1.coords(self.Magnet))
        Xs=[0,2,4,6]
        Ys=[1,3,5,7]
        for y in Ys:
           self.Magnet_coords[y]+=10
           if(self.Magnet_coords[y]>=240):
               self.Stop_Magnet_Down=1
               self.Stop_Move_Down=1
        self.canv1.coords(self.Magnet,self.Magnet_coords)
        self.Stop_Magnet_Up=0

    def Move_Chain_Left(self):
        x1=0
        y1=1
        x2=2
        y2=3
        self.Chain_coords=self.canv1.coords(self.Chain)
        self.Chain_coords[x1]-=10
        self.Chain_coords[x2]-=10
        self.canv1.coords(self.Chain,self.Chain_coords)

    def Move_Chain_Right(self):
        x1=0
        y1=1
        x2=2
        y2=3
        self.Chain_coords=self.canv1.coords(self.Chain)
        self.Chain_coords[x1]+=10
        self.Chain_coords[x2]+=10
        self.canv1.coords(self.Chain,self.Chain_coords)


    def Move_Chain_Up(self,amount=10):
        x1=0
        y1=1
        x2=2
        y2=3
        self.Chain_coords=self.canv1.coords(self.Chain)
        self.Chain_coords[y2]-=amount
        self.canv1.coords(self.Chain,self.Chain_coords) 
        
    def Move_Chain_Down(self,amount=10):
        x1=0
        y1=1
        x2=2
        y2=3
        self.Chain_coords=self.canv1.coords(self.Chain)
        self.Chain_coords[y2]+=amount
        self.canv1.coords(self.Chain,self.Chain_coords)            
        
        
    def Magnet_On(self):        
        self.Magnet_Field=1
        #self.Magnet_Ball=0   #not connected yet
        print("Magnet file is :",self.Magnet_Field)
        x1=0
        y1=1
        x2=2
        y2=3
        self.Magnet_coords=(self.canv1.coords(self.Magnet))
        Xs=[0,2,4,6]
        Ys=[1,3,5,7]
        self.Ball_coords=self.canv1.coords(self.Ball)
        print("ball",self.Ball_coords)
        print("Mag",self.Magnet_coords)
        i=1
        list_of_coords_moves=[]
        Magnet_coords_Ys=[]
        Magnet_coords_Xs=[]
        #need  to see if  ball is near magnet and near center
        self.Magnet_Field_Power=30
        for item in Ys:
            Magnet_coords_Ys.append(self.Magnet_coords[item])
        for item in Xs:
            Magnet_coords_Xs.append(self.Magnet_coords[item])
        #check if ball is near the magnet and near center
        while (any(y < self.Ball_coords[y1]-20  for y in Magnet_coords_Ys)
               and any((self.Ball_coords[y1]-y)<self.Magnet_Field_Power for y in Magnet_coords_Ys)
               and ((self.Ball_coords[x2]+self.Ball_coords[x1])/2-(max(Magnet_coords_Xs)+min(Magnet_coords_Xs))/2 < 5)):#must also be close
            self.Magnet_Ball=1
            print("Magnet_ has the ball",self.Magnet_Ball)
            if(any(y < self.Ball_coords[y1]-20 for y in Magnet_coords_Ys)): #slow down
                i=i-1.5 #some accel
            else:
                i=i-1.5
            print(i)    
            self.Ball_coords[y1]=self.Ball_coords[y1]+i
            self.Ball_coords[y2]=self.Ball_coords[y2]+i
            self.canv1.coords(self.Ball,self.Ball_coords)
            #print(self.Ball_coords)
            self.Ball_coords=self.canv1.coords(self.Ball)
            list_of_coords_moves.append(self.Ball_coords)
            time.sleep(0.1)
            self.myWindow.update()
        if(self.Magnet_Ball==1):
            self.Magnet_coords=(self.canv1.coords(self.Magnet))
            self.Ball_coords[y1]=self.Magnet_coords[7]
            self.Ball_coords[y2]=self.Magnet_coords[7]+40
            self.canv1.coords(self.Ball,self.Ball_coords)  #sometime coarse and fine fix
            
        print(*list_of_coords_moves,sep="\n")   #this way printed  correctly
        #self.canv1.coords(self.Ball, 10,10 ,20,20)         
                        
                
    def Magnet_Off(self):
        self.Magnet_Field=0
        self.Magnet_Ball=0
        print("Magnet file is :",self.Magnet_Field)
        #almost sure thare a better way to enum this
        x1=0
        y1=1
        x2=2
        y2=3
        
        self.Ball_coords=self.canv1.coords(self.Ball)
        print(self.Ball_coords)
        i=1
        list_of_coords_moves=[]
        while (self.Ball_coords[y2]<self.Floor_y):
            if(self.Ball_coords[y2]<(self.Floor_y-100)):
                i=i+1.5 #some accel
            else:
                i=i+0.5
            print(i)    
            self.Ball_coords[y1]=self.Ball_coords[y1]+i
            self.Ball_coords[y2]=self.Ball_coords[y2]+i
            self.canv1.coords(self.Ball,self.Ball_coords)
            #print(self.Ball_coords)
            self.Ball_coords=self.canv1.coords(self.Ball)
            list_of_coords_moves.append(self.Ball_coords)
            time.sleep(0.1)
            self.myWindow.update()
        print(*list_of_coords_moves,sep="\n")   #this way printed  correctly
        #self.canv1.coords(self.Ball, 10,10 ,20,20)    

    def Alarm_Light(self,AL_State):
         self.Light=0
         self.AL_State=AL_State
         if(self.AL_State==1 and self.Already_Blinking==0):
             print("Alarm Light On")
             self.canv1.itemconfigure(self.AlarmLight, fill="red")
             self.Alarm_Light_Blink()
         if(self.AL_State==0):
             print("Alarm Light Off")
             self.canv1.itemconfigure(self.AlarmLight, fill="black")
 
    def Alarm_Light_Blink(self): # must prevent calling two or you will get double up window.after
        if(self.AL_State==1):
          self.Already_Blinking=1
          if (self.Light==1):
              self.canv1.itemconfigure(self.AlarmLight, fill="red")
              self.Light=0
          else:
              self.canv1.itemconfigure(self.AlarmLight, fill="black")
              self.Light=1
          self.myWindow.after(1000,self.Alarm_Light_Blink)
        else:
          self.Already_Blinking=0
 
    def Show_Alarm_Light(self):
          AL_Dia=20
          AL_Loc_X=280
          AL_Loc_Y=20
          
          
          self.AlarmLight=self.canv1.create_oval(AL_Loc_X,AL_Loc_Y,AL_Loc_X+AL_Dia,AL_Loc_Y+AL_Dia,fill="black",outline="black")
          self.Already_Blinking=0
          
    def Seq_Macro(self):  #future
        Macro_Seq1=('Reset', 'AlarmLightOn', 'MagnetOff', 'MoveLeft', 'MoveLeft', 'MoveLeft', 'MoveLeft',\
                    'MoveLeft', 'MoveDown', 'MoveDown', 'MoveDown', 'MoveDown', 'MoveDown', 'MoveLeft',\
                    'MoveLeft', 'MoveDown', 'MoveDown', 'MoveDown', 'MoveDown', 'MoveRight', 'MoveRight',\
                    'MoveRight', 'MoveRight', 'MoveRight', 'MoveRight', 'MoveRight', 'MagnetOn')


    def Mode_Change(self):
        strM=self.action_mode.get()
        if(strM=="Seq_Bottom"):
            self.Seq_Lb.selection_clear(0,'end')
            print(strM)
        if(strM=="Seq_Selected"):
            print(strM)
        if(strM=="SingleStepRun"):           
            self.Seq_Lb.selection_clear(0,'end')
            print(strM)
        print("inside Mode_Change")

    def Show_Modes(self):
        Modes=["Seq_Bottom","Seq_Selected","SingleStepRun"]
        Modes=[
                ("Seq_Bottom","Seq_Bottom"),
                ("Seq_Selected","Seq_Selected"),
                ("<-SingleStepRun","SingleStepRun")]
        self.action_mode=tk.StringVar()
        for i,mode in enumerate(Modes):
            print(i,mode)
            m_rb=tk.Radiobutton(self.LeftFrame,text=mode[0],variable=self.action_mode,value=mode[1],command=lambda:self.Mode_Change())  #odd RADIOBUTTON show on tk but Radiobutton it should be
            m_rb.grid(row=i,column=2,rowspan=2)
        self.action_mode.set("Seq_Bottom")
            
            


            
class Basket():
       def __init__(self,mainGUI=MyApp,x=0,y=0): # if i did not add =MyApp then mainGUI.canv1 will help me find canv1
           super().__init__()  #dont really know what to do with this yet (in learning curve)
           self.mainGUI=mainGUI #(yes i feel i am not doing it corretly or easier, had to do this why so other method of this class could see the arg mainGUI , maybe this is the way
           self.Basket_X1=x
           self.Basket_Y1=y
           self.Basket_W=76
           self.Basket_H=50
           self.Basket_Rim_H=10
           self.canvas=mainGUI.canv1 #feeling i should not do it this way (now this class need to know about other class canvas what if multiple canvas)
           self.Basket_coords=[self.Basket_X1,self.Basket_Y1,self.Basket_X1+self.Basket_W,self.Basket_Y1+self.Basket_Rim_H]
           self.canvas.create_rectangle(self.Basket_coords,fill="orange")
           self.BK_Under_RimY1=self.Basket_Y1+self.Basket_Rim_H
           #wire mesh of basket
           self.Basket_Border=1
           #verticle mesh |
           for Bx in range(self.Basket_X1+self.Basket_Border,self.Basket_X1+self.Basket_Border+self.Basket_W+self.Basket_Border+1,5):
               self.canvas.create_line(Bx,self.BK_Under_RimY1+self.Basket_Border,Bx,self.BK_Under_RimY1+self.Basket_Border+self.Basket_H+2,fill='gray',width=2)
           #horizontal mesh -
           for By in range(self.Basket_Y1+2,self.Basket_Y1+self.Basket_H+3,5):
               self.canvas.create_line(self.Basket_X1+1,By+self.Basket_Rim_H,self.Basket_X1-1+self.Basket_W,By+self.Basket_Rim_H,fill='gray',width=2)

           #a place to put basket text in canv1 which is canvas in this class
           self.Ball_In_Basket=0
           self.canvas_Text=self.canvas.create_text(150,310,text="",anchor="w",justify='left')
               
       def Monitor_Basket_For_Ball_Drop(self):
              #did not know how to use the super so it know  about mainGUI in the init arg (sorry) so i had to create self.mainGUI
              x1=0
              y1=1
              x2=2  
              y2=3
              self.Ball_coords=self.canvas.coords(self.mainGUI.Ball)
              print("HELLO")#Main GUI ball
              print("ball in basket=",self.Ball_In_Basket)
              print(self.Ball_coords)
              if (
                      self.Ball_coords[x1]>self.Basket_X1 and self.mainGUI.Ball_coords[x2]<self.Basket_X1+self.Basket_W
                      and self.Ball_coords[y2]>self.Basket_Y1 and self.mainGUI.Magnet_Ball==0):
                print("Ball inside basket,Excellent")
                self.canvas.itemconfigure(self.canvas_Text, text= "Ball inside basket. Excellent")
                self.Ball_In_Basket=1
              else:
                self.Ball_In_Basket=0  
              if (self.Ball_In_Basket==0):
                    #print("no ball in basket yet")
                    self.canvas.itemconfigure(self.canvas_Text, text= "Can you get ball """"inside"""" the basket")                 
              self.mainGUI.myWindow.after(1000,self.Monitor_Basket_For_Ball_Drop)

                                      
         
         
         
            
            
            
            

def main():
    #start steps to of app      
    root=MyApp(tk)
    root.MakeFrames()
    root.TopFrameWidget("Sequence Editor-with Magnetic Crane as UUT","TestEngineerResource.com")
    root.Show_Keyword_Selection_ListBox()
    root.Show_Sequence()   
    root.Show_Move_To_Right_Button()
    root.Show_Clear_Button()
    root.Show_Tabs()
    root.Show_SimUUT()
    root.Show_Del_Button()
    root.Show_RunSeq_Button()
    root.Show_Alarm_Light()
    root.Alarm_Light(True)
    root.Move_Center()
    MyBasket=Basket(root, 220, 235)  #just want to start doing more classes , ball and magnet should be class also
    MyBasket.Monitor_Basket_For_Ball_Drop()  
    root.Show_Modes() 
    #last line in main
    root.mainloop()  
           
if __name__ =="__main__":
    main()      
        
        
        
        

Download Python code

Notes

  • Presentation: Sequence editor GUI with a simple UUT (Magnetic Crane and ball)
  • Programming Language used: Python 3.9 in Spyder 5.4.3
  • Presentation app: Microsoft’s PowerPoint
  • Python and Tkinter are products of respective company
  • Presentation shown to spark ideas of use.
  • This presentation is not connected to or endorsed by any company.
  • Use at your own risk.
  • Tags: Python, Python3.9, Tkinter , Canvas ,GUI, List, Dictionary, StringVar, Calling, Event binding , ListSelect, Classes, Python Sequence Editor example. Magnetic Crane example, Listbox. radiobutton
  • Future possibilities (currently in this demo there is no file save of the sequence or reading of a save sequence to keep the demo self contain to one file(the script) but in actual use most likely a “save sequence” and “get sequence” will need to be added. Maybe something for you to do if needed.
Posted in Test Sector | Tagged , , , , , , | Leave a comment

Python – Analog Meter with New Chart-Class this time

converted a previous python project over to Class type with new Chart /scope adventure

Short Video on GUI (tkinter)

Python Code Text (tkinter)

# -*- coding: utf-8 -*-
"""
Created on Tue Apr 18 06:52:07 2023

@author: aleja

Using Class with the Analog Meter and Chart

"""

#from tkinter import *
import tkinter as tk
import math
import time
import threading


class MyApp:
    def __init__(self,tk):
        #GUI setup below
        self.myWindow=tk.Tk()
        self.myWindow.title("myWindow-Analog Meter and Chart GUI")
        w_w=1000
        w_h=500
        self.myWindow.minsize(w_w,w_h)
        self.myWindow.maxsize(w_w,w_h)
        print(self.myWindow.winfo_x(),self.myWindow.winfo_y())
        #if you want to move the operator table to see rest of belt comment out
        #next time use resize=false,false
        
        # if you ever notice that some frame is sticking to bottom look at these setting
        self.myWindow.grid_rowconfigure(1,weight=1) #dont want top row frame to grow(row=0)
        self.myWindow.grid_columnconfigure(1,weight=1)
        self.myWindow.configure(background='gray')

    def mainloop(self):
        tk.mainloop()
    
    def MakeFrames(self):
        #Left side button and input
        self.frame1=tk.Frame(self.myWindow,width=120,height=300,bg='lightblue')
        self.frame1.grid(row=0, column=0, sticky='NSWE', padx=10, pady=10, columnspan=1,rowspan=2)
        self.frame1.rowconfigure(0,weight=1)
        self.frame1.columnconfigure(0,weight=0) #the lastcol goes along with rigth side expanding    

        #frame2  Right 
        # adjust r,c,sticky, row and column config as need
        self.frame2=tk.Frame(self.myWindow,width=350,height=350,bg='blue')
        self.frame2.grid(row=1, column=1, sticky='NESW', padx=(0,10), pady=(0,10), columnspan=1) #padding ALSO takes two arguments tuple
        self.frame2.rowconfigure(0,weight=0)
        self.frame2.columnconfigure(1,weight=0) #the lastcol goes along with rigth side expanding
        
        #Top frame
        self.frame3=tk.Frame(self.myWindow,width=100,height=100,bg='lightblue')
        self.frame3.grid(row=0, column=1, sticky='NESW', padx=(0,10), pady=10, columnspan=1) #padding ALSO takes two arguments tuple
        self.frame3.rowconfigure(1,weight=0)
        self.frame3.columnconfigure(1,weight=1) #the lastcol goes along with rigth side expanding

    def TopFrameWidget(self,strAppTitle="YourAppTitle",strAppCompany="YourCompanyName"):
        self.AppTitle=strAppTitle
        self.AppCompany=strAppCompany
        #frame3 info
        AppTitleLabel=tk.Label(self.frame3, width=100, text=self.AppTitle)
        CompanyName=tk.Label(self.frame3, text=self.AppCompany)
        AppTitleLabel.grid(row=0, column=0, padx=0, pady=10, columnspan=2) #span 2 so it does not affect component button
        CompanyName.grid(row=0, column=3, padx=10, sticky='E') #Keep

    def MakeAnalogMeter(self,width=300,height=300):
# ===============Meter_Info =================
        self.Meter_Width=width
        self.Meter_Height=height
        #Meter Povit Point Center Dot

        #Meter Povit Point Center Dot
        self.Dot_Padding=10
        self.Center_Dot_x=self.Meter_Width/2+self.Dot_Padding
        self.Center_Dot_y=self.Meter_Height/2+self.Dot_Padding
        self.Center_Dot_radius=10
        
        self.canv2=tk.Canvas(self.frame2,width=self.Meter_Width+30, height=self.Meter_Height, bg='lightblue', bd=0, highlightthickness=0)
        self.canv2.grid(row=0, column=0, columnspan=1,padx=20,pady=10)
        self.canv2.create_oval(self.Center_Dot_x-self.Center_Dot_radius,self.Center_Dot_y-self.Center_Dot_radius,\
                          self.Center_Dot_x+self.Center_Dot_radius,self.Center_Dot_y+self.Center_Dot_radius,fill='blue')
        self.Reading_Value2=self.canv2.create_text(self.Meter_Width/2-15,225,fill="black",text="",font=('Arial',20,''),anchor="center")

        #===  Two Arc for Major and Minor tick marks
        arcx=self.Center_Dot_x
        arcy=self.Center_Dot_y
        arc_circle_radius=100
        coord_arc=(arcx-arc_circle_radius,arcx-arc_circle_radius,\
                   arcx+arc_circle_radius,arcx+arc_circle_radius)
        arc_startDeg=0
        arc_endDeg=180
        self.canv2.create_arc(coord_arc,start=arc_startDeg,extent=arc_endDeg)
        
        arc_circle_radius=120
        coord_arc=(arcx-arc_circle_radius,arcx-arc_circle_radius,\
                   arcx+arc_circle_radius,arcx+arc_circle_radius)
        self.canv2.create_arc(coord_arc,start=arc_startDeg,extent=arc_endDeg)
        
        #=== create first starting Meter Pointer red arrow line
        MeterValue=0  #unscale 0-180
        Deg=MeterValue-90
        RadDeg=math.radians(Deg)
        line_length=115
        line_x=line_length * math.sin(RadDeg)
        line_y=line_length * math.cos(RadDeg)
        
         
        self.MeterPointer=self.canv2.create_line(self.Center_Dot_x,self.Center_Dot_y,self.Center_Dot_x+line_x,self.Center_Dot_y-line_y,fill='red',width=3,arrow='last')# remember y going up is less
        
        #======= Major and Minor ====== Tick markers
        #major line
        for iDeg in range(-90,91,10):   #because of axis  0-180  = -90 to 90
            RadDeg=math.radians(iDeg)
            subline_length=100
            subline_x1=subline_length * math.sin(RadDeg)
            subline_y1=subline_length * math.cos(RadDeg)
            subline_length=120
            subline_x2=subline_length * math.sin(RadDeg)
            subline_y2=subline_length * math.cos(RadDeg)
            self.canv2.create_line(self.Center_Dot_x+subline_x1,self.Center_Dot_y-subline_y1,\
                              self.Center_Dot_x+subline_x2,self.Center_Dot_y-subline_y2,fill='black',width=3)# remember y going up is less
        #minor line
        for iDeg in range(-90,91,2):
            RadDeg=math.radians(iDeg)
            subline_length=100
            subline_x1=subline_length * math.sin(RadDeg)
            subline_y1=subline_length * math.cos(RadDeg)
            subline_length=120
            subline_x2=subline_length * math.sin(RadDeg)
            subline_y2=subline_length * math.cos(RadDeg)
            self.canv2.create_line(self.Center_Dot_x+subline_x1,self.Center_Dot_y-subline_y1,\
                              self.Center_Dot_x+subline_x2,self.Center_Dot_y-subline_y2,fill='gray',width=1)# remember y going up is less

    def Update_Meter_MajorText(self,EngMin=0,EngMax=10):
        self.EngMin=EngMin
        self.EngMax=EngMax
        self.MaxDeg=180  #keep dont change for demo, it important for 180 deg display meter
        self.MinDeg=0    #Keep dont change for demo
        self.Meter_Text_Canv_List=[]
        """
        Updates the Meters Major Text Number
        """
        #Number text on Major ticks
        MeterNumMajor=0
        
        for iDeg in range(-90,91,10):
            MeterNumMajor=self.Deg_to_EngNum(iDeg+90)
            RadDeg=math.radians(iDeg)
            Text_Radius_Loc=140
            Text_x1=Text_Radius_Loc * math.sin(RadDeg)
            Text_y1=Text_Radius_Loc * math.cos(RadDeg)
            M_T_C=self.canv2.create_text(self.Center_Dot_x+Text_x1,self.Center_Dot_y-Text_y1,fill="black",text=round(MeterNumMajor,1),font=('Arial',9,''))
            self.Meter_Text_Canv_List.append(M_T_C)
            MeterNumMajor=MeterNumMajor+10

    def EngNum_to_Deg(self,EngNum):
        M_slope=((self.MaxDeg-self.MinDeg)/(self.EngMax-self.EngMin))
        Deg=M_slope*(EngNum-self.EngMin)
        return Deg
    
    def Deg_to_EngNum(self,Deg0_180):
        M_slope=((self.EngMax-self.EngMin)/(self.MaxDeg-self.MinDeg))
        EngNum=M_slope*(Deg0_180)+self.EngMin
        return EngNum

    def InputVerticalSlider(self):
        #binding keys and mouse
        #=== Veritical Slider used to change Meter Pointer (reading) ===============================
        self.MeasNum=tk.DoubleVar()  
        self.VSlider=tk.Scale(self.frame1,from_=self.EngMax, to=self.EngMin,length=150,variable=self.MeasNum,digits=3,resolution=0.1)
        self.VSlider.grid(row=0,column=0) #dont forget the odd underscore on from
        #Becasue the slider would generate tons of event using release and I DONT KNOW how have ,not found ScaleChanged event
        self.VSlider.bind('<ButtonRelease-1>',lambda event: self.Update_Meter(event,"Mouse Released"))
        #important <Up> is not equal to <KeyRelease-Up>  Up only was causing a missing event that cause a one resolution offset
        self.VSlider.bind('<KeyRelease-Up>',lambda event: self.Update_Meter(event,"Up key Pressed"))  #just using UP will give on missing event which cause odd 0.1 offest
        self.VSlider.bind('<KeyRelease-Down>',lambda event: self.Update_Meter(event,"Down key Pressed"))
        #Not working this way VSlider.bind('<ButtonRelease-1>,<Up>,<Down>',Update_Meter)
        self.VSlider.focus_set() #focus is important
        self.frame1.focus_force() #focus is important for demo so the frame will listen to the up and down keys both the keypad, and keyboard area
        #interesting ctrl and up key moved it in big steps, not the shift key the ctrl key

    def Buttons_Left_side(self):
        Button_side_Label=tk.Label(self.frame1,text="Sim_Input")
        Button_side_Label.grid(row=0,column=0,padx=10,pady=10,stick="N")
        
        Zero_VSlider_Btn=tk.Button(self.frame1, text='Min',width=15, command=lambda:self.Zero_Slider())
        Zero_VSlider_Btn.grid(row=1, column=0,padx=19,pady=2)  
        
        Zero_to_10_Btn=tk.Button(self.frame1, text='Eng(0-10)',width=15, command=lambda:self.Meter_and_Slider_Update(0,10))
        Zero_to_10_Btn.grid(row=2, column=0,padx=10,pady=2) 
        
        Zero_to_5_Btn=tk.Button(self.frame1, text='Eng(0-5)',width=15, command=lambda:self.Meter_and_Slider_Update(0,5))
        Zero_to_5_Btn.grid(row=3, column=0,padx=10,pady=2) 
        
        Four_to_20_Btn=tk.Button(self.frame1, text='Eng(4-20)',width=15, command=lambda:self.Meter_and_Slider_Update(4,20))
        Four_to_20_Btn.grid(row=4, column=0,padx=10,pady=2)
        
        N10_to_P10_Btn=tk.Button(self.frame1, text='Eng(N10-P10)',width=15, command=lambda:self.Meter_and_Slider_Update(-10,10))
        N10_to_P10_Btn.grid(row=5, column=0,padx=10,pady=2)  
        
        self.Clr_n_Sine_Btn=tk.Button(self.frame1, text='Clr and Sine',width=15, command=lambda:self.Thread_Call_Sine())
        self.Clr_n_Sine_Btn.grid(row=6, column=0,padx=10,pady=2)  

        self.Clr_Btn=tk.Button(self.frame1, text='Clear Plot',width=15, command=lambda:self.Clear_Plot_Lines())
        self.Clr_Btn.grid(row=7, column=0,padx=10,pady=2)  


    def RightSideCanvas(self):
        """ 
        Description: Right side canvas for text and chart use \n
        Arguments: no arg\n
        Other: \n
        Other: 
        """
        #=== Canvas 3
        # canv3 side info on Right
        self.canv3=tk.Canvas(self.frame2,width=self.Meter_Width, height=self.Meter_Height, bg='black', bd=0, highlightthickness=0)
        self.canv3.grid(row=0, column=1, columnspan=1,padx=20,pady=10)


    def Chart_Init(self,C_Width,C_Height):
        """ 
        Description: Initial Chart canvas with outer canvas \n
        Arguments: no arg\n
        Other: \n
        Other: 
        """
        self.Chart_Width=C_Width
        self.Chart_Height=C_Width
        #Graph Initialize
        self.List_Of_Points=[(0,self.Chart_Height),(0,self.Chart_Height)]
        self.Point_Sample_Index=0
        UnTuple=[a for x in self.List_Of_Points for a in x] #stackoverflow help credit "Python-tkiner How to create-line with multiple points P.C. answer
        self.Plot_Line=self.canv3.create_line(*UnTuple,fill='yellow',width=3)
        #print(UnTuple)
        #scope outer border canvas
        self.canv4=tk.Canvas(self.frame2,width=self.Chart_Width+100, height=self.Chart_Height+100, bg='lightgray', bd=1, highlightthickness=1)
        self.canv4.grid(row=0, column=1, columnspan=1,padx=20,pady=10)
        tk.Misc.lift(self.canv3)# stackoverflow help

    def Chart_Grid_Lines(self):
        #===== Chart Grid Line
        for x_div in range(11):
            self.canv3.create_line(x_div*self.Chart_Width/10,0,x_div*self.Chart_Width/10,self.Chart_Height,fill='green',width=2)
            print(x_div)
        for y_div in range(11):
            self.canv3.create_line(0,y_div*self.Chart_Height/10,self.Chart_Width,y_div*self.Chart_Height/10,fill='green',width=2)
        tk.Misc.lift(self.canv3)# stackoverflow 
        #move here to resolve text behind grid lines
        #some info on right side convas - Reading will get a value when updating vertical slider(called scale in python)
        self.Info_Label=self.canv3.create_text(10,0,fill="white",text="Use the mouse in demo \n to move the slider or \n the Up, Down keys",anchor='nw',font=('Arial',14,''))
        self.Reading_Value=self.canv3.create_text(50,100,fill="white",text="",font=('Arial',20,''),anchor="center")
        self.Event_Label_Info=self.canv3.create_text(10,150,fill="white",text="",font=('Arial',14,''))

    def Chart_Border_Text(self):
        self.Y_Chart_Text_List=[]
        for y_scope_text in range(11):
            self.Y_Chart_Text=self.canv4.create_text(40,y_scope_text*self.Chart_Height/10+50,text=str(self.EngMax-y_scope_text),font=('Arial',9,''))
            self.Y_Chart_Text_List.append(self.Y_Chart_Text)       
        for x_scope_text in range(11):
         self.canv4.create_text(x_scope_text*self.Chart_Width/10+50,self.Chart_Height+60,text=str(x_scope_text*30),font=('Arial',9,''))
        self.canv4.create_text(20,self.Chart_Height/2+50,text="Amplitude",angle=90,font=('Arial',12,''))
        self.canv4.create_text(self.Chart_Width/2+50,self.Chart_Height+80,text="Sample",font=('Arial',12,''))
        
        self.ChartTitle=self.canv4.create_text(self.Chart_Width/2+50,20,text="Your Chart Title",font=('Arial',12,'bold'))


    def Chart_Border_Text_Update(self):
        i=0
        Y_Div=(self.EngMax-self.EngMin)/10
        for Y_Chart_txt in self.Y_Chart_Text_List:
            self.canv4.itemconfigure(Y_Chart_txt,text=str(round(self.EngMax-Y_Div*i,1)))
            i+=1
 
    def Update_Chart_Title(self,strText=""):
        self.canv4.itemconfigure(self.ChartTitle,text=strText)
        
        
                   

    #button functions
    #==== Main Update of Meter Pointer
    def Update_Meter(self,event=None,EventMsg="NoMsg"): # I am a relative novice for this event obj arg , its in my learning curve,it did not hiddern this demo
        """ 
        Description: Update the Meters red pointer \n
        Arguments: arg event is tkinter obj when bind events, EventMsg is user string\n
        Other: currently Dont know how to use event object directly yet, so using EventMsg in this demo
        \nOther: using default None so i can use this function without event also.
        """
        self.Point_Sample_Index
    
        if event==None:
            #just a note to remind when a function call was made that as event=None
            str_note="reminder,Please note event arg was  empty so dont used it"
            #print(str_note)
        #Update the pointer on the meter
        #MeterValue=(MeasNum.get())
        self.MeterValue=self.MeasNum.get()  
        #VSlider.set(MeterValue)
        Deg=(self.EngNum_to_Deg(self.MeterValue))-90
        RadDeg=math.radians(Deg)
        line_length=115
        line_x=line_length * math.sin(RadDeg)
        line_y=line_length * math.cos(RadDeg)
        self.canv2.coords(self.MeterPointer,self.Center_Dot_x,self.Center_Dot_y,self.Center_Dot_x+line_x,self.Center_Dot_y-line_y)
        #canv2.create_line(Center_Dot_x,Center_Dot_y,Center_Dot_x+line_x,Center_Dot_y-line_y,fill='red',width=3,arrow='last')# remember y going up is less
        self.canv2.itemconfigure(self.Reading_Value2,text=str(round(self.MeterValue,3)),anchor='w')
        self.canv3.itemconfigure(self.Reading_Value,text=str(round(self.MeterValue,3)),anchor='w')
        self.canv3.itemconfigure(self.Event_Label_Info,text=str(EventMsg),anchor='w')
        #put Point in list
        if(self.EngMin==0):self.Scale_MeterValue=(self.Meter_Height/self.EngMax-self.EngMin) * self.MeterValue*(1)+self.Meter_Height/(self.EngMax-self.EngMin)*0  #leave at bottom -0
        if(self.EngMin<0):self.Scale_MeterValue=(self.Meter_Height/(self.EngMax-self.EngMin)) * self.MeterValue*(1)+self.Meter_Height/(self.EngMax-self.EngMin)*10  #ref up 10
        if(self.EngMin>0):self.Scale_MeterValue=(self.Meter_Height/(self.EngMax-self.EngMin)) * self.MeterValue*(1)+self.Meter_Height/(self.EngMax-self.EngMin)*-4  #ref down 4
        #so chart also update when meter called
        self.Chart_Border_Text_Update()
        self.Update_Chart_Plot()
 
    def Update_Chart_Plot(self):
         """ 
         Description: Updates the chart with scrolling left effect \n
         Arguments: no arg\n
         Other: \n
         Other: 
         """
         #==========chart style scroll left=================
         max_chart_points=self.Chart_Width
         if(self.Point_Sample_Index>max_chart_points): 
            self.List_Of_Points.pop(0)
            
            New_LOP=[]
            i=0
            for Point in self.List_Of_Points:
                #New_LOP
                #tuple are unchangeable (has no index)
                t_to_lst=list(Point)
                t_to_lst[0]=i  #to start shifting graph left(the x values=sample index)
                Point=tuple(t_to_lst)
                #print(Point)
                New_LOP.append(Point) #because point shift x  for each y it all new
                i=i+1
            self.List_Of_Points= New_LOP
            self.Point_Sample_Index=i
            
    
         self.List_Of_Points.append((self.Point_Sample_Index,self.Chart_Height-int(self.Scale_MeterValue)))
         self.Point_Sample_Index +=1
     
         UnTuple=[a for x in self.List_Of_Points for a in x] #stackoverflow help credit "Python-tkiner How to create-line with multiple points P.C. answer
         #self.canv3.delete(self.Plot_Line,'all') #seem to delete ALL "line type" even the grid lines,interesting
         self.canv3.delete(self.Plot_Line) #needed but not work complete working in thread completely
         self.Plot_Line=self.canv3.create_line(*UnTuple,fill='yellow',width=3)
         #print(UnTuple)   
         self.myWindow.update()
    



    def Zero_Slider(self):
        self.VSlider.focus_force
        if (self.EngMin<0):
            self.MeasNum.set(0)
        else:    
            self.MeasNum.set(self.EngMin)
        self.Update_Meter(None,"Min/Zero Button or Function Call") #warning not using event arg or in function 
        return


    def Meter_and_Slider_Update(self,Emin=0,Emax=10):
        """ 
        Description: Update Meter and Slider \n
        Arguments: Eng Min and Eng Max\n
        Other:Used when moving slider or changing range \n
        Other: 
        """        
        self.EngMax=Emax
        self.EngMin=Emin
        print(self.EngMin,self.EngMax)
        #first delete current text
        for M_T_C_item in self.Meter_Text_Canv_List:
            self.canv2.delete(M_T_C_item)
        self.Update_Meter_MajorText(self.EngMin,self.EngMax)
        self.VSlider.configure(from_=self.EngMax,to=self.EngMin)
        self.VSlider.focus_force
        if(self.EngMin<0):
           self.VSlider.set(0)
           self.MeasNum.set(0)
        else:
           self.VSlider.set(self.EngMin)
           self.MeasNum.set(self.EngMin)
        self.Update_Meter(None,"Button released:"+str(self.EngMin)+"--"+str(self.EngMax)) #warning not using event arg or in function
        #self.canv3.delete(self.Plot_Line)
        return

    def Thread_Call_Sine(self):
        threading.Thread(target=self.Clr_n_Sine,daemon=True).start() #remember no ()
        
    
    def Clr_n_Sine(self):
        #if real hardware connected must check sin amplitude for your application
        #demo sinewave
        self.Clr_n_Sine_Btn['state']='disabled'
        #print(MeterValue,MeasNum.get(),VSlider.get())
        self.VSlider.set(0)
        #also clear List of point
        self.Point_Sample_Index
        self.List_Of_Points=[(0,self.Chart_Height),(0,self.Chart_Height)] #need two points same at zero
        UnTuple=[a for x in self.List_Of_Points for a in x]
        self.canv3.delete(self.Plot_Line)
        if (self.EngMin<0):
            self.Num_Of_90s=7
            self.StartingPhase=-90
        else:
            self.Num_Of_90s=8
            self.StartingPhase=0
        for angle in range(self.StartingPhase,90*self.Num_Of_90s,1):
            #for amp 8
            #time.sleep(0.005)  #commented out to see full speed cpU% wil increase since not delay
            max_amp=(self.EngMax-self.EngMin)/2
            offset=(self.EngMax+self.EngMin)/2
            y=max_amp*math.sin(math.radians(angle-90))+offset
            x=5*math.cos(math.radians(angle))
            self.VSlider.focus_force
            self.MeasNum.set(y)
            self.Update_Meter(None,"sin function")
            #self.Update_Chart_Plot()
        print(angle)   
        self.Clr_n_Sine_Btn['state']='normal'
        self.Chart_Border_Text_Update()
        self.Zero_Slider() #zero out or min slider
        
    def Clear_Plot_Lines(self):
        self.VSlider.focus_force
        self.canv3.delete(self.Plot_Line)
        self.List_Of_Points.clear()    #Dont for the () ,yes i forget
        self.List_Of_Points=[(0,self.Chart_Height),((0,self.Chart_Height))]#but need min two point for create line
        self.Point_Sample_Index=0
        self.canv3.itemconfigure(self.Info_Label,text="")
        self.canv3.itemconfigure(self.Event_Label_Info,text="")







    
def main():
    #start steps to of app      
    root=MyApp(tk)
    root.MakeFrames()
    root.TopFrameWidget("Analog Meter Display and Chart","TestEngineerResource.com")
    root.MakeAnalogMeter(300,300)
    root.Update_Meter_MajorText(0,10)
    root.InputVerticalSlider()
    root.Buttons_Left_side()
    root.RightSideCanvas()
    root.Chart_Init(300,300)
    root.Chart_Grid_Lines()
    root.Chart_Border_Text()
    root.Update_Chart_Title("Chart Demo")
    
    #last line in main
    root.mainloop()  
           
if __name__ =="__main__":
    main()
        

Python – Analog Meter and Chart Download

Notes

Presentation: Analog Meter Display and Chart GUI using Vertical slider for input to meter, show different scale changes
Programming Language used: Python 3.7 in Spyder
Presentation app: Microsoft’s PowerPoint
Python and Tkinter are products of respective company
Presentation shown to spark ideas of use.
This presentation is not connected to or endorsed by any company.
Use at your own risk.
Tags: Python, Python3.7, Tkinter , Canvas ,GUI, List, Dictionary, StringVar, Calling, Event binding , Default None for Event, Key and Mouse binding, KeyRelease-Up, KeyRelease-Down, Chart, sinewave

Posted in Test Sector | Tagged , , , , , , , , | Leave a comment

Python tkinter and time delay

Presentation on Different ways to create time delay in tkinter

Short Video (about 35sec)

Python code Text

# -*- coding: utf-8 -*-
"""
Created on Sun Apr 16 11:41:30 2023

@author: aleja

Analog Meter GUI and Demo for learning

"""


from tkinter import *
#for some reason from tkinter.ttk import * cause bg lightblue error
#from tkinter.ttk import Combobox
#import math
import time
#import datetime
#import os    #for path filename join
import threading


#GUI setup below
myWindow=Tk()
myWindow.title("myWindow-Tkinter and TimeDelay GUI")
w_w=850
w_h=400
myWindow.minsize(w_w,w_h)
#myWindow.maxsize(w_w,w_h) #if you want to move the operator table to see rest of belt comment out
#next time use resize=false,false

# do this or frame (last frame row number, in window will not follow window (expanding)
myWindow.grid_rowconfigure(1,weight=1)  #Important This was the reason for  on frame being stuck at bottom was row 0 but 3frames
myWindow.grid_columnconfigure(1,weight=1)
myWindow.configure(background='gray')


#***FRAMES buttons
# adjust r,c,sticky, row and column config as need
frame1=Frame(myWindow,width=130,height=300,bg='lightblue')
frame1.grid(row=0, column=0, sticky='NS', padx=10, pady=10, columnspan=1,rowspan=4)
frame1.rowconfigure(0,weight=1)
frame1.rowconfigure(1,weight=1)
frame1.rowconfigure(2,weight=1)
frame1.rowconfigure(3,weight=1)
frame1.columnconfigure(0,weight=1) #the lastcol goes along with rigth side expanding



#frame2  Right 
# adjust r,c,sticky, row and column config as need
frame2=Frame(myWindow,width=300,height=300,bg='blue')
frame2.grid(row=1, column=1, sticky='NESW', padx=(0,10), pady=(0,10), columnspan=1,rowspan=4) #padding ALSO takes two arguments tuple
frame2.rowconfigure(0,weight=1)
frame2.rowconfigure(1,weight=1)
frame2.rowconfigure(2,weight=1)
frame2.columnconfigure(1,weight=1) #the lastcol goes along with rigth side expanding

#Top frame
frame3=Frame(myWindow,width=100,height=100,bg='lightblue')
frame3.grid(row=0, column=1, sticky='NESW', padx=(0,10), pady=10, columnspan=2) #padding ALSO takes two arguments tuple
frame3.rowconfigure(1,weight=0)
frame3.columnconfigure(1,weight=1) #the lastcol goes along with rigth side expanding



#frame3 info
AppTitleLabel=Label(frame3, width=40,text="   Time Delay methods Display               ")
CompanyName=Label(frame3, text="YourCompanyName")
AppTitleLabel.grid(row=0, column=0, padx=0, pady=10, columnspan=2) #span 2 so it does not affect component button
CompanyName.grid(row=0, column=3, padx=10, sticky='E') #Keep


#place to put time
Time_Label1=Label(frame2,height=2,text="Put time Here",font=('Arial',20,''))
Time_Label1.grid(row=0, column=0, padx=10, pady=10, columnspan=2,sticky='WE') #span 2 so it does not affect component button
Label.config(Time_Label1,text="")



#place to put time
Time_Label3=Label(frame2,height=2,text="Put time Here",font=('Arial',20,''))
Time_Label3.grid(row=1, column=0, padx=10, pady=10, columnspan=2,sticky='WE') #span 2 so it does not affect component button
Label.config(Time_Label3,text="")

Time_Label4=Label(frame2,height=2,text="Put time Here",font=('Arial',20,''))
Time_Label4.grid(row=2, column=0, padx=10, pady=10, columnspan=2,sticky='WE') #span 2 so it does not affect component button
Label.config(Time_Label4,text="")

Label_List=[Time_Label3,Time_Label4]

def GetTime():
    TimeNow=time.time()
    delay_sec=2
    time.sleep(delay_sec) #this is blocking type function in tkinter main thread so remember avoid
    TimeAfter=time.time()
    #TimeNow_datetime=datetime.datetime.now()    
    #Label.config(Time_Label,text=TimeNow) # time from some long ago time ref still good for delta
    #Label.config(Time_Label,text="Date Time factional time: "+str(TimeNow_datetime))
    #Label.config(Time_Label,text="Time format: "+str(TimeNow_datetime.strftime("%H:%M:%S")))
    #Label.config(Time_Label,text="Date m/d/Y format: "+str(TimeNow_datetime.strftime("%m/%d/%Y")))#
    
    deltaTime=TimeAfter-TimeNow
    Label.config(Time_Label1,text=str(delay_sec)+"sec delay: "+str(deltaTime))

#thread starter
def GetTime_w_Thread():
    threading.Thread(target=GetTime, daemon=True).start() #important WITHOUT the () on the function otherwise it blocks
    print("threading notice same function that had time.sleep but not in tkinter thread,its own")

#Two Parts  The Thread and a non-Thread checker
#Testing non-Thread and Thread call to this functino
Done_Flag=[0,0] #two thread use
def GetTime2(StartingTime=None,Which_Label=0,delay_sec_arg=0.0):
    global Label_List
    global Done_Flag
    global Button_List
    print(Which_Label)
    #(Button_List[Which_Label])['state']='disabled' #I do see that a large time.sleep in thread 
    #can still affect button state from changing quickly even if time.sleep is after the button state
    #many have stated to not have tkinter type call in thread so need to work that
    myWindow.update
    delay_sec=delay_sec_arg
    TimeNow=time.time()
    time.sleep(0.001) #this is used to testout if function is "really" running "move GUI test" thread base , adjust to make thread slow or fast to see is_alive affect
    #the very first thread run is ok but since 'after' is calling not thread it blockable again (move the gui after press)
    deltaTime=TimeNow-StartingTime
    Label.config(Label_List[Which_Label],text=str(delay_sec)+"sec delay: " + str("{:0.3F}".format(deltaTime)))
    if(deltaTime>=delay_sec):
      Label.config(Label_List[Which_Label],text=str(delay_sec)+"sec delay: " + str("{:0.3F}".format(deltaTime))) #remember the : infront of format number
      Done_Flag[Which_Label]=1
      Button_List[Which_Label]['state']='normal'
    #Done do it here   if(Done_Flag[Which_Label]==0):myWindow.after(100,lambda:GetTime2(StartingTime,Which_Label,delay_sec)) #to low a after time WILL overload CPU time
    return

#non-thread checker
def Start_Checking_The_Thread(TD,StartingTime,Which_Label,delay_sec_arg):
    global Done_Flag
    print(TD.is_alive())
    if(TD.is_alive()): 
        print("Thread is still alive") #if the threaded function is slow you will see alive more offen
    else:
        print("Thread is done") # if the thread function is quick you will be done more offen
    if(Done_Flag[Which_Label]==0):
        #NO this was not really running a thread TD=threading.Thread(target=GetTime2(StartingTime,Which_Label,delay_sec_arg), daemon=True)
        TD=threading.Thread(target=GetTime2,args=(StartingTime,Which_Label,delay_sec_arg,), daemon=True) #important no () on target function
        TD.start()
        TD.is_alive()
        # if you make after to small it will comsume CPU percent - just know this d- alway how slow can i go still be ok with app (100ms can give +/-100ms error to time)
        myWindow.after(50,lambda:Start_Checking_The_Thread(TD,StartingTime,Which_Label,delay_sec_arg)) #to0 low an after time WILL overload CPU time
        #too low a ms after time WILL overload CPU time and tkinter thread , need to find a balance that works for your app
        #with 50ms: accuracy +codetime0+/-50ms not absolute since on Windows you are not the only app(aka NOT Realtime computer)
        #check with the task manager(windows) to see if cpu using to to high if it is you need to increase the after time
    else:        
        print("done checking") #to0 low an after time WILL overload CPU time
    return

#thread starter button event
def GetTime_w_Thread2(Which_Label=0,delay_sec_arg=0.0):
    global Label_List
    global Button_List
    global Done_Flag
    Done_Flag[Which_Label]=0
    global StartingTime
    (Button_List[Which_Label])['state']='disabled'
    StartingTime=time.time()
    print(StartingTime)
    #in "this" particle case both non-thread and thread working "Simalarly" but not the same (non-Thread can be blocked)
    #GetTime2(StartingTime,Which_Label,delay_sec_arg) #this works but NOT Threading
    TD=threading.Thread(target=GetTime2(StartingTime,Which_Label,delay_sec_arg), daemon=True)
    TD=threading.Thread(target=GetTime2,args=(StartingTime,Which_Label,delay_sec_arg,), daemon=True)

    TD.start()
    print("Thread",TD.is_alive())
    #threading.Thread(target=GetTime2,args=(StartingTime,Which_Label,delay_sec_arg), daemon=True).start()
    Start_Checking_The_Thread(TD,StartingTime,Which_Label,delay_sec_arg)
    print("Thread",TD.is_alive())

b_width=20
GetTime_Btn=Button(frame1, text='GetDelta with \n time.sleep delay 2sec \n freeze tk GUI for \n that time',\
                   width=b_width, command=lambda:GetTime())
GetTime_Btn.grid(row=0, column=0,padx=10,pady=10) 

GetTime_Btn=Button(frame1, text='GetDelta with \n time.sleep delay 2sec\n and thread \n does NOT freeze tk GUI for \n that time',\
                   width=b_width, command=lambda:GetTime_w_Thread())
GetTime_Btn.grid(row=1, column=0,padx=10,pady=10)

StartTime=time.time()
GetTime2_Btn=Button(frame1, text='GetDelta with 2s\n "after" \n and thread',width=b_width, command=lambda:GetTime_w_Thread2(0,2.0))
GetTime2_Btn.grid(row=2, column=0,padx=10,pady=10)

GetTime3_Btn=Button(frame1, text='GetDelta2 with 3s \n "after \n and thread',width=b_width, command=lambda:GetTime_w_Thread2(1,3.0))
GetTime3_Btn.grid(row=3, column=0,padx=10,pady=10)

Button_List=[GetTime2_Btn,GetTime3_Btn]
   
#main Window  GUI looping
myWindow.mainloop()

Python code download

Notes

  • Presentation: Python Learn Tkinter and time delay examples
  • Programming Language used: Python 3.7 in Spyder
  • Presentation app: Microsoft’s PowerPoint
  • Python and Tkinter are products of respective company
  • Lots of Help and credit to “Stackoverflow” ,product of respective company
  • Presentation shown to spark ideas of use.
  • This presentation is not connected to or endorsed by any company.
  • Use at your own risk.
  • Tags: Python, Python3.7, Tkinter, GUI, List, Thread ,Tkinter and Timedelay, TimeDelay, After function, Python Learn Tkinter and time delay examples
Posted in Test Sector | Tagged , , , , , , , , | Leave a comment

Python – MCC USB-1208FS-Plus

Using Python to see connection and simple measurement using MCC’s USB-1208FS-Plus

Presentation

Python code text see in console result

# -*- coding: utf-8 -*-
"""
Created on Thu Apr 13 08:52:18 2023

@author: aleja
"""

from mcculw import ul
from mcculw.enums import ULRange
from mcculw.ul import ULError
import time


board_num=0
channel=0
ai_range=ULRange.BIP10VOLTS
try:
   for i in range (10): 
    my_value=ul.a_in(board_num,channel,ai_range)
    eng_units_value=ul.to_eng_units(board_num,ai_range,my_value)
#    print("Raw Value;"+str(my_value))
    print("Eng Value:"+'{:0.3f}'.format(eng_units_value))
    time.sleep(0.1)
except ULError as e:
    print("A message from UL code:" +str(e.errorcode)+"msg:" + str(e.message)) 
    

Down of simple measurement to see on console

Notes

  • Presentation: Python and MCC DAQ 1208FS-Plus
  • MCC, Measurement Computing Corp is own by NI (previously know as National Instruments)
  • Programming Language used: Python 3.7 in Spyder
  • Presentation app: Microsoft’s PowerPoint
  • Python of respective company
  • Presentation shown to spark ideas of use.
  • This presentation is not connected to or endorsed by any company.
  • Use at your own risk.
  • Tags: MCC DAQ USB-1208FS-Plus, MCC,Python, MCCULW for python,PIP
  • Future possibilities: Making a Python Tkinter to show readings
  • DaqDevDiscovery01 example form MCC github code (see presentation)
Posted in Test Sector | Tagged , , , , | Leave a comment