#!/usr/bin/env python3
'''
Program to manipulate a GPIO pin of the Raspberry Pi 4. Should work
on other 40-pin Pi models. Provides PWM output on GPIO18 (physical pin 12)
and can change the duty cycle and frequency of output. Percent limit set
to 100%. Frequency default set at 50 Hz with the limit set to 500 Hz as
the signal has a lot of jitter above that. Oct 2019, Bonedog32.
'''
import RPi.GPIO as GPIO # a Python module to control the GPIO interface. 
import tkinter as tk

GPIO.setwarnings(False) # Turn off "pin in use" warnings
GPIO.setmode(GPIO.BOARD) # use physical pin numbering
GPIO.setup(12, GPIO.OUT) # Set GPIO18 to output pin.
p = GPIO.PWM(12, 50) # physical pin 12 (GPIO18) at 50 Hz


class MyApp: # "Constructor"
    def __init__(self,parent):
        self.myParent = parent

        # set title
        self.myParent.title("GPIO PWM") # set window title
        self.myParent.geometry('320x76')  # set window size

        # Make topmost container with everything else inside
        self.myContainer1 = tk.Frame(parent) # topmost frame: myContainer1
        self.myContainer1.grid()

        #------ constants for controlling layout ------
        button_width = 10

        button_padx = "2m"      # 'm' = millimeters
        button_pady = "2m"
        # -------------- end constants ----------------

        # two more containers, left one for menu buttons, right one for data, horizontal layout.
        # left frame inside myContainer1
        self.left_frame = tk.Frame(self.myContainer1, background="tan",
                    borderwidth=2, relief=tk.RIDGE,
                    height=76, width=50)
        self.left_frame.grid(row=0)

        # right frame inside myContainer1
        self.right_frame = tk.Frame(self.myContainer1, background="gray",
                    borderwidth=2, relief=tk.RIDGE,
                    height=76, width=250)
        self.right_frame.grid(row=0, column=1)
        
        # add buttons for menu selection to left_frame
        self.button1 = tk.Button(self.left_frame, text='Activate', command=self.set_percent)
        self.button1.configure(activebackground="green",background = "lightgreen", width=button_width, padx=button_padx, pady=button_pady)
        self.button4 = tk.Button(self.left_frame, text='Quit', command=self.button4Click)
        self.button4.configure(activebackground = "red", background = "pink", width=button_width, padx=button_padx, pady=button_pady)

        self.button1.grid()
        self.button4.grid()
        
    def button4Click(self):
        #p.ChangeDutyCycle(0) # set output to zero (optional)
        p.stop() # Stop PWM mode on Pi 4
        self.myParent.destroy() # destroy entire window

    def NewFrame(self, color): # new 'right_frame' inside myContainer1. Use 'color' for background
        self.right_frame.destroy() # delete old frame before creating new one
        self.right_frame = tk.Frame(self.myContainer1, background=color,
                    borderwidth=2, relief=tk.RIDGE,
                    height=120, width=250, padx='5m')
        self.right_frame.grid(row=0, column=1)

    def set_percent(self):
        self.button1.configure(activebackground="green",background = "green")
        color = '#9f3' # color input to NewFrame()
        self.NewFrame(color) # call function to build new frame
        # zero variables
        self.dc = 0
        p.start(self.dc)
        self.freq = 50
        # set variable label to accept floats
        self.percent_label_text = tk.DoubleVar(self.right_frame) # assign to right_frame
        self.percent_label_text.set(self.dc) # assign duty cycle field to label
        self.freq_label_text = tk.DoubleVar(self.right_frame) # assign to right frame
        self.freq_label_text.set(self.freq) # assign frequency field to label
        #place label in right_frame
        self.percent_label = tk.Label(self.right_frame, textvariable=self.percent_label_text, bg=color)
        
        # label formats assigned to right_frame
        self.label = tk.Label(self.right_frame, text="Set PWM Percent/Frequency", bg=color)
        self.label1 = tk.Label(self.right_frame, text="Enter %:", bg=color)
        self.label3 = tk.Label(self.right_frame, text="Enter freq: ",bg=color)

        # setup entry fields
        # Place these widgets in desired order of 'event.widget.tk_focusNext().focus()'
        self.percent_entry = tk.Text(self.right_frame, width=10, height=1) # create text entry box
        self.percent_entry.focus_set() # place cursor in text box
        self.percent_entry.bind("<Return>", self.calc_percent) # execute function
        self.percent_entry.bind("<Tab>", FocusNextWindow) # to Freq entry box
        self.freq_entry = tk.Text(self.right_frame, width=10, height=1)
        self.freq_entry.bind("<Return>", self.calc_percent) # execute function
        self.freq_entry.bind("<Tab>", FocusNextWindow) # to Calculate button

        # SCREEN LAYOUT AND POSITIONING

        # position various widgets. 'sticky' used to expand contents to fill complete cell width.
        self.label.grid(row=0, columnspan=2) # Main title
        self.label1.grid(row=1) # Enter percent prompt
        self.percent_entry.grid(row=1, column=1, sticky=tk.W+tk.E) # entry box for percent value
        self.label3.grid(row=3) # enter freq prompt
        self.freq_entry.grid(row=3, column=1, sticky=tk.W+tk.E) # entry box for freq value        

    def calc_percent(self, event=None):
        # 1.0 = start at first char; tk.END = all way to end; strip() = remove newline & spaces.
        pct_text = self.percent_entry.get(1.0, tk.END).strip()
        fq_text = self.freq_entry.get(1.0, tk.END).strip()
        if len(pct_text) > 0: # don't act if blank entry
            dc = float(pct_text)
            if dc <= 100: # limit to 100%
                p.ChangeDutyCycle(dc)
        if len(fq_text) > 0: # don't act if blank entry
            fq = float(fq_text)
            if fq <= 500: # gets unstable above 500 Hz
                p.ChangeFrequency(fq)
        self.percent_entry.delete(1.0, tk.END) # clear percent input box
        self.freq_entry.delete(1.0, tk.END) # clear frequency text box
        self.percent_entry.focus_set() # place cursor in percent text box

def FocusNextWindow(event): # move cursor to next entry field
    event.widget.tk_focusNext().focus()
    return("break")
    
def centerWindow(): # open the Tkinter window in center of screen
    w = 320 # width for the Tk root
    h = 76 # height for the Tk root

    # get screen width and height
    ws = root.winfo_screenwidth() # screen width/2 (for dual screens)
    hs = root.winfo_screenheight() # height of the screen

    # calculate x and y coordinates for the Tk root window
    x = ((ws/2) - (w/2))
    y = (hs/2) - (h/2)

    # set the dimensions of the screen 
    # and where it is placed
    root.geometry('%dx%d+%d+%d' % (w, h, x, y))


# main program loop ------------------------
root = tk.Tk()
centerWindow()
myapp = MyApp(root)
root.mainloop()
