#!/usr/bin/python3.7 ########## ########## ########## this file dated November 23, 2020 ########## there have undoubtably been changes ########## ########## Steve Wolf, PiRV@mail.W8IZ.com ########## ########## from tkinter import * from w1thermsensor import W1ThermSensor import datetime, time, smtplib, os, bs4, sys, serial # used for time, time, SMTP, OS, beautiful soup import RPi.GPIO as GPIO import paho.mqtt.client as mqtt # Import the MQTT library from email.mime.multipart import MIMEMultipart #DONT THINK THIS IS USED from email.mime.text import MIMEText # used for sending the email from requests import get # used for ip address request from w1thermsensor import W1ThermSensor # These are to send text messages import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart email = "xxx@xxx.xxx" pas = "xxx" sms_gateway = 'xxx@xxx.xxx' # The server we use to send emails # and port is also provided by the email provider. smtp = "smtp.xxx.com" port = xxx serverAddress="x.x.x.x" PiRVHomeServerAddress="x.x.x.x" # Initialize sensors as missing FridgeTemp = "OOS" FreezerTemp = "OOS" InsideTemp = "OOS" #This is necessary to allow the GUI to boot prior to opening windows time.sleep(5) global brightValue, JustBooted brightValue=0 # This forces the seven inch pi screent to behave x = os.popen("sudo xinput map-to-output 6 DSI-1") #################### # This is a subroutine to send a message to an email account. # That will be forwarded by the receiving email account to my phone as a text. #################### def SendText(TextToSend): try: # This will start our email server server = smtplib.SMTP(smtp,port) server.starttls() # Now we need to login server.login(email,pas) # Now we use the MIME module to structure our message. msg = MIMEMultipart() msg['From'] = email msg['To'] = sms_gateway # Make sure you add a new line in the subject # msg['Subject'] = TextToSend # Make sure you also add new lines to your body body = TextToSend # and then attach that body furthermore you can also send html content. msg.attach(MIMEText(body, 'plain')) sms = msg.as_string() # print (sms) server.sendmail(email,sms_gateway,sms) # lastly quit the server server.quit() except: print("mail server malfunction") #################### # This is a placeholder for the mqtt message from others #################### def mqttMessageFunction (client, userdata, message): global FridgeTemp, FreezerTemp, InsideTemp, LastFridgeTempTime, LastFreezerTempTime, LastInsideTempTime topic = str(message.topic) message = str(message.payload.decode("utf-8")) print("MQTT " + topic + ": " + message) if message == "MQTT server is running": print("***** PiRVREAR MQTT SERVER REBOOTED*****") if message.startswith("Fridge:") == True: FridgeTemp = message[8:10] LastFridgeTempTime=time.time() if message.startswith("Freezer:") == True: FreezerTemp = message[9:11] LastFreezerTempTime=time.time() if message.startswith("Inside:") == True: InsideTemp = message[8:10] LastFreezerTempTime=time.time() #def mqttConnectionLostFunction() # Print("INTERNAL: MQTT connection lost with error code ") #def mqttConnectFunction (client, userdata, flags, rc): # print("INTERNAL: MQTT connection made with code " + str(rc)) # PiRVClient.subscribe("WS") #PiRV to PiRVREAR MQTT client try: #set up mqtt PiRVClient = mqtt.Client("PiRV_mqtt") PiRVClient.connect(serverAddress, 1883) PiRVClient.subscribe("RV") PiRVClient.on_message = mqttMessageFunction # Attach the messageFunction to subscription PiRVClient.loop_start() # Start the MQTT client PiRVClient.publish("RV", "PiRV has rebooted") except: SendText("*****PiRV unable to set up MQTT Client*****") print("INTERNAL: *****PiRV unable to set up MQTT Client*****") #PiRV to PiHOME MQTT client # try: # #set up mqtt # PiRVhomeClient = mqtt.Client("PiRV_mqtt") # PiRVhomeClient.connect(PiRVHomeServerAddress, 1883) # PiRVhomeClient.subscribe("PiHOME") # PiRVhomeClient.on_message = mqttMessageFunction # Attach the messageFunction to subscription # PiRVhomeClient.loop_start() # Start the MQTT client # PiRVhomeClient.publish("PiHOME", "PiRV has rebooted") # except: # SendText("*****PiRVhome unable to set up MQTT Client*****") # print("INTERNAL: *****PiRVhome unable to set up MQTT Client*****") #set up GPIO GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) switch=26 # the magnetic switch is on GPIO 26 GPIO.setup(switch, GPIO.IN, GPIO.PUD_UP) # The program starts below all the subroutines that will be called. It is at the end. root = Tk() root.title("PiRV by Steve Wolf, PiRV@mail.W8IZ.com") root.configure(bg='gray') # MUST ADD THESE LINES WHEN ON THE ROAD2 # AND # DON'T FORGET TO DISABLE THE SCREEN SAVER! # Now trying "consoleblank=0" in "/boot/cmdline.txt" root.attributes("-fullscreen", True) #root.config(cursor="none") # Initializing TimeOfLastBearing TimeOutTimeInterval = 1200 #1200 = 20 mins TimeOfLastBearing=time.time() TimeOutTime=TimeOfLastBearing + TimeOutTimeInterval ############################################################ # If a device is missing from /dev, the program will crash # if it trys to use it. This checks each device and sets # a True/False flag for each. # If the GPS is missing it # notes that and exits. ############################################################ # Given a path, this checks to see if the device is logged def exists(path): try: os.stat(path) except OSError: return False return True # The test is in a subroutine def test_devices(): global OBDThermistor OBDThermistor=True #test for GPS if exists("/dev/ttyACM0"): print("GPS is in /dev") # Don't need a flag as we exit the program if missing. else: print("GPS is missing from dev") exit() test_devices() #Set up gps gps = serial.Serial("/dev/ttyACM0", baudrate = 9600) # set TimeOfLastTemp global zulutimestr #set brightness to max brightness = os.popen("rpi-backlight --set-brightness 100") brightValue = 100 # show the bearing ShowBearing = True #Setting timezone from system clock if time.daylight > 0: timezone = -int(time.timezone/3600) else: timezone = -int(time.altzone/3600) # used to tell if the time change buttons are on screen TimeChangeOnScreen = 0 # used to tell if the exit buttons are already on screen ExitOnScreen = 0 # temp sensor number 0=fridge, 1=freezer, 2=inside TempSensNumber = 0 ############################################################ # Function to send a text message ############################################################ def SendText(TextToSend): try: # This will start our email server server = smtplib.SMTP(smtp,port) server.starttls() # Now we need to login server.login(email,pas) # Now we use the MIME module to structure our message. msg = MIMEMultipart() msg['From'] = email msg['To'] = sms_gateway # Make sure you add a new line in the subject # msg['Subject'] = TextToSend # Make sure you also add new lines to your body body = TextToSend # and then attach that body furthermore you can also send html content. msg.attach(MIMEText(body, 'plain')) sms = msg.as_string() # print (sms) server.sendmail(email,sms_gateway,sms) # lastly quit the server server.quit() except: print("INTERNAL: *****PiRV SendText failed*****") ############################################################ # Function to allow the user to change time ############################################################ def ButtonTimeChange(): #This is to allow the user to change the timezones global timechange, TimeChangeOnScreen, TimeOfLastBearing, brightValue timechange = 0 #reset screen if blanked by initializing TimeOutTime which is global if brightValue==0: TimeOfLastBearing = time.time() brightness = os.popen("rpi-backlight --set-brightness 100") brightValue=100 #determining if the buttons are already on the screen, if so, leave if TimeChangeOnScreen == 1: return def ButtonAdd(): #this adds one hour to the system time global timechange, time1, time2, timezone, TimeChangeOnScreen #set brightness to max brightness = os.popen("rpi-backlight --set-brightness 100") #increment timechange integer timechange = timechange + 1 #normalize the integer if it reaches 24 if timechange == 24: timechange = 0 #get the system hour and add the timezone integer, restringing it timesofar=ClockButton['text'] timehour=str(int(timesofar[0:2])+timechange) #if the hour goes over 23, normalize it back if int(timehour) > 23: timehour = str(int(timehour) - 24) #write the working time to the screen button time2 = time.strftime(timehour + ':%M:%S') # if time string has changed, update it if time2 != time1:#set brightness to max time1 = time2 ButtonTime.config(text=time2 + " -Change & click") timezone = timezone + timechange ButtonAdd.destroy() ButtonSubtract.destroy() ButtonTime.destroy() TimeChangeOnScreen = 0 return def ButtonSubtract(): #this subtracts one hour from the system time global timechange, time1, time2, timezone, TimeChangeOnScreen #set brightness to max brightness = os.popen("rpi-backlight --set-brightness 100") #decrement timezone integer timechange = timechange - 1 #normalize the integer if it reaches below zero if timechange == -1: timechange = 23 #get the system hour and add the timezone integer, restringing it timesofar=ClockButton['text'] timehour=str(int(timesofar[0:2])+timechange) #if the hour goes over 23, normalize it back if int(timehour) > 23: timehour = str(int(timehour) - 24) #write the working time to6 the screen button time2 = time.strftime(timehour + ':%M:%S') # if time string has changed, update it if time2 != time1: time1 = time2 ButtonTime.config(text=time2 + " - Change & click") timezone = timezone + timechange ButtonAdd.destroy() ButtonSubtract.destroy() ButtonTime.destroy() TimeChangeOnScreen = 0 return def ButtonTime(): #set sytem time to the new time and close change windows global timezone, timechange, TimeChangeOnScreen, time2 ButtonAdd.destroy() ButtonSubtract.destroy() ButtonTime.destroy() TimeChangeOnScreen = 0 return #define buttons ButtonAdd=Button(root, width=0, font=('ariel', 20, 'bold'), activebackground = 'gray', text=" + ", command=ButtonAdd) ButtonTime=Button(root, width = 0, font=('ariel', 12, 'bold'), activebackground = 'gray', text='Return',command=ButtonTime) ButtonSubtract=Button(root, width=0, font=('ariel', 20, 'bold'), activebackground = 'gray', text=" - ", command=ButtonSubtract) #text=time.strftime('%H:%M:%S'+ " - Change & click") # put buttons on screen ButtonAdd.pack (side=LEFT, expand=1, fill=BOTH) ButtonTime.pack (side=LEFT, expand=1, fill=BOTH) ButtonSubtract.pack (side=LEFT, expand=1, fill=BOTH) #this is to prevent multiple copies TimeChangeOnScreen = 1 return ############################################################ # Scrape and act on OBD data ############################################################ def OBDFunction(): global TimeOfLastBearing, brightValue #reset screen if blanked by initializing TimeOutTime which is global if brightValue==0: TimeOfLastBearing = time.time() brightness = os.popen("rpi-backlight --set-brightness 100") brightValue=100 return ############################################################ # Backlight down ############################################################ def BacklightDownFunction(): global brightValue, TimeOfLastBearing #reset screen if blanked by initializing TimeOutTime which is global if brightValue==0: TimeOfLastBearing = time.time() brightness = os.popen("rpi-backlight --set-brightness 100") brightValue=100 BacklightValue.config(text="100") return BacklightDown.config(activebackground = 'white') brightness = os.popen("rpi-backlight --get-brightness").read() brightness = int(brightness) if brightness < 6: TimeOfLastBearing = time.time() brightness = os.popen("rpi-backlight --set-brightness 100") brightValue=100 BacklightValue.config(text="100") return if brightness > 6: brightness = brightness - 2 if brightness == 50: brightess = 10 if brightness > 51: brightness = 50 else: if brightness > 30: brightness = 10 BacklightValue.config(text=str(brightness)) brightness = os.popen("rpi-backlight --set-brightness " + str(brightness)) BacklightDown.config(activebackground = 'gray') return ############################################################ # Backlight value function ############################################################ def BacklightValueFunction(): return ############################################################ # Backlight up ############################################################ def BacklightUpFunction(): global brightValue, TimeOfLastBearing, TimeOutTimeInterval TimeOfLastBearing = time.time() brightness = os.popen("rpi-backlight --set-brightness 100") brightValue=100 BacklightValue.config(text="100") #Sets label value return ############################################################ # Temperature toggling - in/out/back ############################################################ def TempToggle(): global TempSensNumber, TimeOfLastBearing, brightValue #reset screen if blanked by initializing TimeOutTime which is global if brightValue==0: TimeOfLastBearing = time.time() brightness = os.popen("rpi-backlight --set-brightness 100") brightValue=100 #reset background color to gray and away from warning red # TempButton.config(bg = "gray") # temp sensor number 0=driver, 1=fridge, 2=back, 3=outside TempSensNumber = TempSensNumber + 1 if TempSensNumber > 2: TempSensNumber = 0 return ############################################################ # Show the bearing or not ############################################################ def ShowButtonChange(): global ShowBearing, TimeOfLastBearing,brightValue #reset screen if blanked by initializing TimeOutTime which is global if brightValue==0: TimeOfLastBearing = time.time() brightness = os.popen("rpi-backlight --set-brightness 100") brightValue=100 #toggle the show bearing if ShowBearing == True: ShowBearing=False return else: ShowBearing = True return ############################################################ # Exit routine ############################################################ def ProgramExit(): global ExitOnScreen global TimeOfLastBearing global brightValue #reset screen if blanked by initializing TimeOutTime which is global if brightValue==0: TimeOfLastBearing = time.time() brightness = os.popen("rpi-backlight --set-brightness 100") brightValue=100 if ExitOnScreen == 1: return def SureExit(): exit() return def ReturnExit(): global ExitOnScreen ExitOnScreen = 0 areyousurenobutton.destroy() areyousureyesbutton.destroy() return #Are your sure yes button areyousurenobutton = Button(BottomFrame, width=0, font=('ariel', 20, 'bold'), bg = 'gray', activebackground = 'gray', highlightthickness=0, bd=0, text="Return to program", command=ReturnExit) areyousurenobutton.pack(expand=1, fill=BOTH) #Are your sure no button areyousureyesbutton = Button(BottomFrame, width=0, font=('ariel', 20, 'bold'), bg = 'gray', activebackground = 'gray', highlightthickness=0, bd=0, text="I am sure - Exit", command=SureExit) areyousureyesbutton.pack(expand=1, fill=BOTH) ExitOnScreen = 1 return ############################################################ # Defining frames, buttons and lables on the top screen ############################################################ TopFrame = Frame(root, bg='gray', bd=0) TopFrame.pack(expand=True, fill = X) #bearing label BearingButton = Button(TopFrame, width=0, font=('ariel', 40, 'bold'), bg = 'gray', activebackground = 'gray', highlightthickness=1, highlightbackground='black', bd=0, text=" ", command=ShowButtonChange) BearingButton.pack (side = LEFT, fill=BOTH, expand=1) #clock button ClockButton = Button(TopFrame, width=0, font=('ariel', 40, 'bold'), bg = 'gray', activebackground = 'gray', highlightthickness=1, highlightbackground='black', bd=0,command=ButtonTimeChange) ClockButton.pack (side = LEFT, fill=BOTH, expand=1) #Temp label TempButton = Button(TopFrame, width=0, font=('ariel', 20, 'bold'), bg = 'gray', activebackground = 'gray', highlightthickness=1, highlightbackground='black', bd=0, command=TempToggle) TempButton.pack (side = RIGHT, fill=BOTH, expand=1) SpeedFrame = Frame(root, bg='gray', bd=0) SpeedFrame.pack(expand=True, fill = X) #speed label SpeedLabel = Label(SpeedFrame, font=('ariel', 150, 'bold'), bg='gray') SpeedLabel.pack(fill=BOTH, expand=1) BottomFrame = Frame(root, bg='gray', bd=0) BottomFrame.pack(expand=True, fill = X) #function button BacklightDown = Button(BottomFrame, width=0, font=('ariel', 20, 'bold'), bg = 'gray', activebackground = 'white', highlightthickness=0, bd=0, text="Down", command=BacklightDownFunction) BacklightDown.pack(side = LEFT,expand=1, fill=BOTH) #function button BacklightValue = Button(BottomFrame, width=0, font=('ariel', 20, 'bold'), bg = 'gray', activebackground = 'gray', highlightthickness=0, bd=0, text="Down", command=BacklightValueFunction) BacklightValue.pack(side = LEFT,expand=1, fill=BOTH) BacklightValue.config(text="100") #function button BacklightUp = Button(BottomFrame, width=0, font=('ariel', 20, 'bold'), bg = 'gray', activebackground = 'gray', highlightthickness=0, bd=0, text="Up", command=BacklightUpFunction) BacklightUp.pack(side = LEFT,expand=1, fill=BOTH) #function button OBDscreen = Button(BottomFrame, width=0, font=('ariel', 20, 'bold'), bg = 'gray', activebackground = 'gray', highlightthickness=0, bd=0, text="OBD", command=OBDFunction) OBDscreen.pack(side = LEFT,expand=1, fill=BOTH) #exit button exitbutton = Button(BottomFrame, width=0, font=('ariel', 20, 'bold'), bg = 'gray', activebackground = 'gray', highlightthickness=0, bd=0, text="Exit", command=ProgramExit) exitbutton.pack(side = LEFT, expand=1, fill=BOTH) # Report the reboot SendText("PiRV REBOOT") PiRVClient.publish("RV", "PiRV has rebooted") t=time.localtime() CurrentDate = t.tm_mday LastDate = t.tm_mday JustBooted=False global oldlatitude oldlatitude="" global oldlongitude oldlongitude="" global oldspeed oldspeed=0 global oldbearing oldbearing="" global LastFridgeTempTime global LastFreezerTempTime global LastInsideTempTime LastFridgeTempTime=time.time() LastFreezerTempTime=time.time() LastInsideTempTime=time.time() global speedint speedint=0 ############################################################ # Here is the loop ############################################################ def tick(): global time1, time2, speed1, speed2, TimeOfLastTemp, TimeOfLastBearing, TimeOutTime, bearing, brightValue, JustBooted, LastDate, CurrentDate, latitude, longitude, oldlatitude, oldlongitude, oldspeed, oldbearing, LastFridgeTempTime, LastFreezerTempTime, LastInsideTempTime, speedint time1 = "" # We send a ping with a check of the IP at midnight t=time.localtime() CurrentDate = t.tm_mday if JustBooted == False and LastDate != CurrentDate: SendText("PiRV MIDNIGHT ping") LastDate = t.tm_mday #using the GPS to load speed and time data and print it line = gps.readline() data = line.split( b"," ) if data[0] == b"$GPRMC": #Checking to see if the gps IS NOT working if data[2] == b"V": SpeedLabel.config(text="No GPS") BearingButton.config(text="No GPS") # get the current local time from the PC time2 = time.strftime('%H:%M:%S') # if time string has changed, update it if time2 != time1: time1 = time2 ClockButton.config(text=time1) #Checking to see if the gps IS working if data[2] == b"A": lat = str(data[3]) latdir = str(data[4]) #Converts DDDMM.MMMMM > DD deg MM.MMMMM min x = lat.split(".") head = x[0] tail = x[1] deg = head[2:4] min = head[-2:] tail = tail[:-4] latitude = deg + " deg " + min + "." + tail + " min " + latdir[-2:-1] if speedint > 5: if latitude != oldlatitude: PiRVClient.publish("RV", 'Latitude: '+latitude) PiRVhomeClient.publish("PiHOME", 'Latitude: '+latitude) oldlatitude=latitude long = str(data[5]) longdir = str(data[6]) #Converts DDDMM.MMMMM > DD deg MM.MMMMM min x = long.split(".") head = x[0] tail = x[1] deg = head[3:5] min = head[-2:] tail = tail[:-4] longitude = deg + " deg " + min + "." + tail + " min " + longdir[-2:-1] if speedint > 5: if longitude != oldlongitude: PiRVClient.publish("RV", 'Longitude: '+longitude) PiRVhomeClient.publish("PiHOME", 'Longitude: '+longitude) oldlongitude=longitude speedstring = data[7] bearing = data[8] bearing = bearing.decode('utf-8') if bearing != "": bearing=int(float(bearing)) else: bearing=-1 speedknots = float(speedstring) speedmph = speedknots/0.86898 speedint = int(speedmph) if speedmph < 1: SpeedLabel.config(text="0") BearingButton.config(text=" ") if speedint != oldspeed: PiRVClient.publish("RV", 'Speed: 0') PiRVhomeClient.publish("PiHOME", 'Speed: 0') oldspeed=speedint else: SpeedLabel.config(text=speedint) if speedint > 5: if speedint != oldspeed: PiRVClient.publish("RV", 'Speed: '+str(speedint)) PiRVhomeClient.publish("PiHOME", 'Speed: '+str(speedint)) oldspeed=speedint #doing the bearing in alpha and numeric if ShowBearing: #resolving bad bearing if bearing < 0 or bearing > 359: BearingButton.config(text=" ") else: if bearing == 0: bearing = 360 strBearing = str(bearing) if bearing < 10: strBearing = " " + strBearing if bearing < 100: strBearing = " " + strBearing #resolving bearing in degrees to compass points if bearing < 23 or bearing > 337: BearingButton.config(text="N /" + strBearing) TimeOfLastBearing=time.time() if bearing > 22 and bearing < 68: BearingButton.config(text="NE/" + strBearing) TimeOfLastBearing=time.time() if bearing > 67 and bearing < 113: BearingButton.config(text="E /" + strBearing) TimeOfLastBearing=time.time() if bearing > 112 and bearing < 158: BearingButton.config(text="SE/" + strBearing) TimeOfLastBearing=time.time() if bearing > 157 and bearing < 203: BearingButton.config(text="S /" + strBearing) TimeOfLastBearing=time.time() if bearing > 202 and bearing < 248: BearingButton.config(text="SW/" + strBearing) TimeOfLastBearing=time.time() if bearing > 247 and bearing < 293: BearingButton.config(text="W /" + strBearing) TimeOfLastBearing=time.time() if bearing > 292 and bearing < 338: BearingButton.config(text="NW/" + strBearing) TimeOfLastBearing=time.time() if speedint > 5: if oldbearing != BearingButton["text"]: PiRVClient.publish("RV", 'Bearing: '+BearingButton["text"]) PiRVhomeClient.publish("PiHOME", 'Bearing: '+BearingButton["text"]) oldbearing=BearingButton["text"] #now doing the time zulutimestr = data[1] localtimehr = str(int(zulutimestr[:2]) + timezone) #if the hour goes over 23, normalize it back while int(localtimehr) > 23: localtimehr = str(int(localtimehr) - 24) #if the hour goes less than 0, normalize it back while int(localtimehr) < 0: localtimehr = str(int(localtimehr) + 24) localtimeminute = str(int(zulutimestr[2:4])) if (int(localtimeminute) < 10): localtimeminute = "0" + localtimeminute localtimesecond = str(int(zulutimestr[4:6])) if (int(localtimesecond) < 10): localtimesecond = "0" + localtimesecond localtimestr = localtimehr + ':' + localtimeminute + ':' + localtimesecond ClockButton.config(text=localtimestr) # sets the temperature to the sensor selected # temp sensor number 0=fridge, 1=freezer, 2=internal, 3=outside if TempSensNumber == 0: # check if the fridge has not been heard from in 30 minutes if LastFridgeTempTime > time.time() - 1800: TempButton.config(text = FridgeTemp + " fridge") LastFridgeTempTime=time.time() else: TempButton.config(text = "OOS fridge") if TempButton['text'] == "OOS fridge": TempButton.config(bg='LightYellow3') elif int(FridgeTemp) > 45: TempButton.config(bg='maroon') else: TempButton.config(bg='gray') if TempSensNumber == 1: # check if the freezer has not been heard from in 30 minutes if LastFreezerTempTime > time.time() - 1800: TempButton.config(text = FreezerTemp + " freezer") LastFreezerTempTime=time.time() else: TempButton.config(text = "OOS freezer") if TempButton['text'] == "OOS freezer": TempButton.config(bg='LightYellow3') elif int(FreezerTemp) > 30: TempButton.config(bg='maroon') else: TempButton.config(bg='gray') if TempSensNumber == 2: # check if the fridge has not been heard from in 30 minutes if LastInsideTempTime > time.time() - 1800: TempButton.config(text = InsideTemp + " inside") LastInsideTempTime=time.time() else: TempButton.config(text = "OOS inside") if TempButton['text'] == "OOS inside": TempButton.config(bg='LightYellow3') else: TempButton.config(bg='gray') # Here we need to determine if the GPS has not moved in TimeOutTime # minutes. If not, then we need to blank the screen. CurrentTime = time.time() TimeOutTime = TimeOfLastBearing + TimeOutTimeInterval if CurrentTime>TimeOutTime: #set brightness to OFF brightness = os.popen("rpi-backlight --set-brightness 0") brightValue=0 # calls itself every 200 milliseconds to update the time display as needed could use >200 ms, but display gets jerky ClockButton.after(1, tick) tick() root.mainloop( )