Cowardlydoomsday
Posts: 97
Joined: Wed Sep 23, 2015 11:58 pm

Exiting my Tkinter window w/ switch

Mon Apr 18, 2016 9:27 pm

I am writing a script where I need a physical button to exit out of the Tkinter window my script has created. Below is my script:

Code: Select all

import RPi.GPIO as GPIO
import os as sysCmd
import Tkinter as tk
from Tkinter import *
import ttk
import time
import picamera
import datetime
from PIL import Image

## GPIO settings
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(12, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.setup(10, GPIO.OUT)
GPIO.setup(16, GPIO.OUT)
GPIO.output(10, GPIO.HIGH)

## Call picamera and settings
camera = picamera.PiCamera()
camera.hflip = True
camera.vflip = True
camera.resolution = (800, 480)
debounce = .25
delay = 3

## Physical button switch to exit out of program but leave RPi on
def ButtonSwitch():
    while GPIO.input(12) == 0:
        time.sleep(0.25) ## Debounce
        start_time = time.time()
        while GPIO.input(12) == 0:
            if time.time() - start_time > delay - debounce:
                print("Button was Pressed")
                #sysCmd.system('sudo killall idle')
                self.quit()
    root.after(3000, ButtonSwitch)
                
## Initiate the GUI and widgets
class MainScreen(Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.createWidget()

    ## Creating buttons
    def createWidget(self):
        self.pack()
        bttn1 = tk.Button(self, text = "LEDS"   , command = self.LEDs).grid   (row = 1, column = 1, pady = 5, padx = 5)
        bttn2 = tk.Button(self, text = "Quit"   , command = self.quit).grid   (row = 2, column = 1, pady = 5, padx = 5)
        bttn3 = tk.Button(self, text = "Capture", command = self.takePic).grid(row = 3, column = 1, pady = 5, padx = 5)

    ## Turning on and off LED's
    def LEDs(self):
        GPIO.output(16, GPIO.HIGH)
        time.sleep(2.5)
        GPIO.output(16, GPIO.LOW)
        #time.sleep(2.5)

    ## Setting up time stamp
    def takePic(self):
        now = datetime.datetime.now()
        dateTimeStamp = now.strftime('%Y%m%d-%H%M%S')
        global photoName
        photoName = dateTimeStamp + ".jpg"

    ## Taking picture with datetime stamp
        try:
            camera.start_preview()
            camera.annotate_background = picamera.Color('blue')
            camera.annotate_text = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            time.sleep(5)
            camera.stop_preview()
            self.fileName = photoName
            camera.capture(self.fileName)
        finally:
            camera.close()

    ## Overlaying the watermark onto the original image
        basePic = (self.fileName)
        watermarkPic = 'Overlay.png'
        base = Image.open(basePic)
        watermark = Image.open(watermarkPic)
        xyoffset = (0, 240)
        base.paste(watermark, xyoffset, mask = watermark)
        base.save(self.fileName)

## Mainloop
root = tk.Tk()
MainScreen(root)
root.after(3000, ButtonSwitch)
root.geometry('125x125+25+25')
root.mainloop()
root.destroy()
'sudo killall idle will close the python script but not the tk window. Is there a terminal command that will close just the window I specify?

Davies
Posts: 150
Joined: Sat Apr 04, 2015 4:24 pm

Re: Exiting my Tkinter window w/ switch

Mon Apr 18, 2016 10:41 pm

Hi, im fairly new to python and certainly not the best to be giving answers but i did notice an error in your code that i had also made recently..
in def ButtonSwitch(): you finish a while: with a root.after this would open multiple instances and cause the code to crash over time, it wants to have a time.sleep(3) rather than the root.after and allow the while to handle the continuous running. also id open it in a thread rather than a root.after but there are probably better ways.
also i dont think it wants the root.destroy at the end, that is infact the code to close the tkinter window your asking about.
theres probably better ways to do this but see here on this tkinter numpad code, the numpad opens in a seperate window to the main and is opened as boot rather than root, then boot.destroy is called to close that window while leaving root open..

Code: Select all

import Tkinter as tk
from Tkinter import *
from functools import partial

root = tk.Tk()
root.geometry("200x100")

num_run = 0
btn_funcid = 0


def click(btn):
    global num_run
    text = "%s" % btn
    if not text == "Del" and not text == "Close":
        e.insert(END, text)
    if text == 'Del':
        e.delete(0, END)
    if text == 'Close':
        boot.destroy()
        num_run = 0
        root.unbind('<Button-1>', btn_funcid)


def numpad():
    global boot
    boot = tk.Tk()
    boot['bg'] = 'green'
    lf = tk.LabelFrame(boot, text=" keypad ", bd=3)
    lf.pack(padx=15, pady=10)
    btn_list = [
        '7',  '8',  '9',
        '4',  '5',  '6',
        '1',  '2',  '3',
        '0',  'Del',  'Close']
    r = 1
    c = 0
    n = 0
    btn = list(range(len(btn_list)))
    for label in btn_list:
        cmd = partial(click, label)
        btn[n] = tk.Button(lf, text=label, width=10, height=5, command=cmd)
        btn[n].grid(row=r, column=c)
        n += 1
        c += 1
        if c == 3:
            c = 0
            r += 1


def close(event):
    global num_run
    if num_run == 1:
        boot.destroy()
        num_run = 0
        root.unbind('<Button-1>', btn_funcid)


def run(event):
    global num_run, btn_funcid
    if num_run == 0:
        num_run = 1
        numpad()
        btn_funcid = root.bind('<Button-1>', close)


e = Entry(root, width=10, background='white', textvariable=file, justify=CENTER, font='-weight bold')
e.bind('<Button-1>', run)


e.grid(padx=10, pady=5, row=17, column=1, sticky='W,E,N,S')

root.mainloop()

Cowardlydoomsday
Posts: 97
Joined: Wed Sep 23, 2015 11:58 pm

Re: Exiting my Tkinter window w/ switch

Mon Apr 18, 2016 11:22 pm

Thanks for your replay. I might of not made it clear but I do want this button to close the Tkinter window. I have another program running right now that is basically the same block that shuts down my PI after the button is held down for 3.5 seconds. And I have no problems with that. The self.quit line in the block was a trial that failed and I just forgot to take it out. I do have a button that appears in the window to exit the program, hence the reason for the root.destroy in the root window.

Davies
Posts: 150
Joined: Sat Apr 04, 2015 4:24 pm

Re: Exiting my Tkinter window w/ switch

Mon Apr 18, 2016 11:53 pm

i misread the indentation on that root.after, it wouldnt cause the problem i suggested.
so are you saying you want to close the tkinter window but keep some of the script code running? rather than calling root.destroy and closing tkinter and root?

Cowardlydoomsday
Posts: 97
Joined: Wed Sep 23, 2015 11:58 pm

Re: Exiting my Tkinter window w/ switch

Tue Apr 19, 2016 12:00 am

I actually got it to work! When the button is pressed for 3 to 4 seconds, the program calls on a terminal command of "sudo killall python". Then it closes the python shell which in turns closes the tkinter window.

Code: Select all

import RPi.GPIO as GPIO
import os as sysCmd
import Tkinter as tk
from Tkinter import *
import ttk
import time
import picamera
import datetime
from PIL import Image

## GPIO settings
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(12, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.setup(10, GPIO.OUT)
GPIO.setup(16, GPIO.OUT)
GPIO.output(10, GPIO.HIGH)

## Call picamera and settings
camera = picamera.PiCamera()
camera.hflip = True
camera.vflip = True
camera.resolution = (800, 480)
debounce = .25
delay = 3

## Physical button switch to exit out of program but leave RPi on
def ButtonSwitch():
    while GPIO.input(12) == 0:
        time.sleep(0.25) ## Debounce
        start_time = time.time()
        while GPIO.input(12) == 0:
            if time.time() - start_time > delay - debounce:
                print("Button was Pressed")
                sysCmd.system('sudo killall python')
    root.after(3000, ButtonSwitch)
                
## Initiate the GUI and widgets
class MainScreen(Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.createWidget()

    ## Creating buttons
    def createWidget(self):
        self.pack()
        bttn1 = tk.Button(self, text = "LEDS"   , command = self.LEDs).grid   (row = 1, column = 1, pady = 5, padx = 5)
        bttn2 = tk.Button(self, text = "Quit"   , command = self.quit).grid   (row = 2, column = 1, pady = 5, padx = 5)
        bttn3 = tk.Button(self, text = "Capture", command = self.takePic).grid(row = 3, column = 1, pady = 5, padx = 5)

    ## Turning on and off LED's
    def LEDs(self):
        GPIO.output(16, GPIO.HIGH)
        time.sleep(2.5)
        GPIO.output(16, GPIO.LOW)
        #time.sleep(2.5)

    ## Setting up time stamp
    def takePic(self):
        now = datetime.datetime.now()
        dateTimeStamp = now.strftime('%Y%m%d-%H%M%S')
        global photoName
        photoName = dateTimeStamp + ".jpg"

    ## Taking picture with datetime stamp
        try:
            camera.start_preview()
            camera.annotate_background = picamera.Color('blue')
            camera.annotate_text = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            time.sleep(5)
            camera.stop_preview()
            self.fileName = photoName
            camera.capture(self.fileName)
        finally:
            camera.close()

    ## Overlaying the watermark onto the original image
        basePic = (self.fileName)
        watermarkPic = 'Overlay.png'
        base = Image.open(basePic)
        watermark = Image.open(watermarkPic)
        xyoffset = (0, 240)
        base.paste(watermark, xyoffset, mask = watermark)
        base.save(self.fileName)

## Mainloop
root = tk.Tk()
MainScreen(root)
root.after(3000, ButtonSwitch)
root.geometry('125x125+25+25')
root.mainloop()
root.destroy()
Hope this will help you in any future development.

tom.slick
Posts: 190
Joined: Wed Jan 06, 2016 9:23 pm

Re: Exiting my Tkinter window w/ switch

Tue Apr 19, 2016 1:20 am

Try this

Code: Select all

import RPi.GPIO as GPIO
import os as sysCmd
import Tkinter as tk
from Tkinter import *
import ttk
import time
import picamera
import datetime
from PIL import Image

## GPIO settings
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(12, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.setup(10, GPIO.OUT)
GPIO.setup(16, GPIO.OUT)
GPIO.output(10, GPIO.HIGH)

## Call picamera and settings
camera = picamera.PiCamera()
camera.hflip = True
camera.vflip = True
camera.resolution = (800, 480)
debounce = .25
delay = 3

                
## Initiate the GUI and widgets
class MainScreen(Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.root = parent
        self.createWidget()
        self.root.after(3000, self.ButtonSwitch)

    ## Physical button switch to exit out of program but leave RPi on
    def ButtonSwitch(self):
        while GPIO.input(12) == 0:
            time.sleep(0.25) ## Debounce
            start_time = time.time()
            while GPIO.input(12) == 0:
                if time.time() - start_time > delay - debounce:
                    print("Button was Pressed")
                    self.root.destroy()
        self.root.after(3000, self.ButtonSwitch)

    ## Creating buttons
    def createWidget(self):
        self.pack()
        bttn1 = tk.Button(self, text = "LEDS"   , command = self.LEDs).grid   (row = 1, column = 1, pady = 5, padx = 5)
        bttn2 = tk.Button(self, text = "Quit"   , command = self.quit).grid   (row = 2, column = 1, pady = 5, padx = 5)
        bttn3 = tk.Button(self, text = "Capture", command = self.takePic).grid(row = 3, column = 1, pady = 5, padx = 5)

    ## Turning on and off LED's
    def LEDs(self):
        GPIO.output(16, GPIO.HIGH)
        time.sleep(2.5)
        GPIO.output(16, GPIO.LOW)
        #time.sleep(2.5)

    ## Setting up time stamp
    def takePic(self):
        now = datetime.datetime.now()
        dateTimeStamp = now.strftime('%Y%m%d-%H%M%S')
        global photoName
        photoName = dateTimeStamp + ".jpg"

    ## Taking picture with datetime stamp
        try:
            camera.start_preview()
            camera.annotate_background = picamera.Color('blue')
            camera.annotate_text = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            time.sleep(5)
            camera.stop_preview()
            self.fileName = photoName
            camera.capture(self.fileName)
        finally:
            camera.close()

    ## Overlaying the watermark onto the original image
        basePic = (self.fileName)
        watermarkPic = 'Overlay.png'
        base = Image.open(basePic)
        watermark = Image.open(watermarkPic)
        xyoffset = (0, 240)
        base.paste(watermark, xyoffset, mask = watermark)
        base.save(self.fileName)

## Mainloop
root = tk.Tk()
root.geometry('125x125+25+25')

MainScreen(root)

root.mainloop()
Last edited by tom.slick on Tue Apr 19, 2016 7:30 pm, edited 1 time in total.

Cowardlydoomsday
Posts: 97
Joined: Wed Sep 23, 2015 11:58 pm

Re: Exiting my Tkinter window w/ switch

Tue Apr 19, 2016 3:55 pm

tom.slick, the code you gave me did not work. gave an error of the "ButtonSwitch" needs to be a global.

tom.slick
Posts: 190
Joined: Wed Jan 06, 2016 9:23 pm

Re: Exiting my Tkinter window w/ switch

Tue Apr 19, 2016 7:31 pm

Sorry I forgot to change something.

I edited the original post, it should work now

Return to “Python”