[Tkinter] Help with tkinter for a heating control GUI - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [Tkinter] Help with tkinter for a heating control GUI (/thread-23172.html) |
Help with tkinter for a heating control GUI - tony359 - Dec-14-2019 Hello all, I am quite new to Python and I am training myself on the internet. So please excuse me if my code is wrong/not very readable. I have also posted this on the RaspberryPi forums. As I travel a lot I wanted to control my heater remotely and some time ago I built a basic "ON OFF" switch which I could access via VNC remotely. I also bought a temperature sensor but never made time to make a proper program to control the temperature. I've finally come up with something but I have some questions I cannot answer myself. I'm using tkinter and I made a simple interface. The base control actually works - I'm impressed - but I am struggling with a few things. Question 1. I do not understand how the "window_name.mainloop()" works. I understand it's to keep the graphic elements running but then I do not understand where I should write my other code - the code that decides whether it's time to switch the heating off based on the temperature etc. I ended up putting everything into different functions. I am not sure it's the correct way of doing it, particularly when I call the main function from the main function itself to make it run all the time. I'm sure some of you would shiver! Question 2. Do I understand correctly or variables in functions only live in that function? I am struggling to have the "bypass" button to work - it would set a flag which would prevent the boiler from starting if set to TRUE - but if I understand correctly variables only live into each function independently? In the end I wonder if you could tell me if my approach is correct - I doubt it. Again, apologies for the messy code - it's still working in progress. The lots of "print" are there for debug purposes! :) Thanks for your help! from tkinter import * import tkinter as tk import time import board import busio import adafruit_mcp9808 import RPi.GPIO as GPIO deltaplus = 0.2 #offset before heating status is changed deltaminus = 0.5 #offset before heating status is changed #Setting up GPIOs GPIO.setup(21, GPIO.OUT) GPIO.output(21, GPIO.LOW) GPIO.setup(20, GPIO.OUT) GPIO.output(20, GPIO.HIGH) #Setting up temperature sensor i2c_bus = busio.I2C(board.SCL, board.SDA) mcp = adafruit_mcp9808.MCP9808(i2c_bus) actualtempC = round(mcp.temperature,1) # To increase set temperature by one degree def increase_temp(): value = int(entry_set_temp.get()) value = value + 1 entry_set_temp.delete(0,2) entry_set_temp.insert(0, value) # To decrease set temperature by one degree def decrease_temp(): value = int(entry_set_temp.get()) value = value - 1 entry_set_temp.delete(0,2) entry_set_temp.insert(0, value) #Heating bypass enable - when BYPASS button is pressed def bypassOn(): print("bypassON running") bypassFlag = 1 print("BypassFlag from bypassOn",bypassFlag) heatingOff() #def bypassOff(): # bypassFlag = 0 #Variable initialisation - not working as variables only live in each DEF section? def variableinit(): bypassFlag = 0 #Switch heating off def heatingOff(): GPIO.output(21,GPIO.HIGH) boilerStatus = 0 print("Boilerstatus into heatingoff", boilerStatus) #Switch heating on IF bypass is not on def heatingOn(): print("BypassFlag from heatingOn before IF",bypassFlag) if bypassFlag==0: GPIO.output(21,GPIO.LOW) boilerStatus = 1 print("BypassFlag from heatingOn",bypassFlag) #Main section - I'm sure there's a better way to do this! def thermostat(): print("Bypass beginning of THERMOSTAT:",bypassFlag) actualtempC = round(mcp.temperature,1) setTemp = int(entry_set_temp.get()) #read variable from user set temp field label_actual_temp = tk.Label(master, text = actualtempC, font=("Helvetica", 28)) #Display actual sensor temp label_actual_temp.place(relx = 0.5, rely = 0.1, anchor=N) print("Sensor temperature: ",actualtempC) print("Set temperature: ", int(entry_set_temp.get())) print("Bypass before main IF:",bypassFlag) #thermostat algoritm if actualtempC < setTemp - deltaminus: #if temp is below set temp, minus offset, then switch heating on heatingOn() print("one") if actualtempC > setTemp + deltaplus: #if temp is above set temp, plus offset, then switch heating off heatingOff() print("two") # print("BoilerStatus", boilerStatus) # label_heatingStatus = tk.Label(master, textvariable = boilerStatus, font=("Helvetica", 28)) # label_heatingStatus.place(relx = 0.2, rely = 0.8, anchor=N) print("Bypass:",bypassFlag) print("END") master.after(3000,thermostat) #repeat this section? I'm sure, again, there's a better way to do this! #Variable initialisation variableinit() #define tkinter window master = tk.Tk() master.geometry("800x480") #CURRENT TEMP label label_current_temp = tk.Label(master, text="CURRENT TEMP °C", font=("Helvetica", 28)) label_current_temp.place(relx = 0.5, rely = 0.0, anchor=N) #SET TEMP label label_set_temp = tk.Label(master, text="SET TEMP °C", font=("Helvetica", 28)) label_set_temp.place(relx = 0.5, rely = 0.34, anchor = CENTER) #label_set_temp = tk.Label(master, text="SET TEMP °C", font=("Helvetica", 28)) #label_set_temp.place(relx = 0.5, rely = 0.34, anchor = CENTER) #BYPASS button button_bypass = tk.Button(master, text="BYPASS", width=15, font=("Helvetica", 28), command=bypassOn) button_bypass.place(relx = 0.5, rely = 0.68, anchor = CENTER) #Winter Protection checkbox winter_protection = IntVar() check_winter_protection = Checkbutton(master, text="Winter Protection?", variable = winter_protection, font=("Helvetica", 18)) check_winter_protection.place(relx = 0.5, rely = 0.88, anchor = CENTER) #label_actual_temp = tk.Label(master, text = actualtempC, font=("Helvetica", 28)) #label_actual_temp.place(relx = 0.5, rely = 0.1, anchor=N) #SET TEMP field default_temp = StringVar(master, value="19") # Set field to a default of 19 when programme starts entry_set_temp = tk.Entry(master, font=("Helvetica", 28), width = 7, justify = CENTER, textvariable = default_temp) entry_set_temp.place(relx = 0.5, rely = 0.44, anchor = CENTER) # PLUS button for Set Temp field button_plus = tk.Button(master, text="+", width=1, font=("Helvetica", 8), command=increase_temp) button_plus.place(relx = 0.62, rely = 0.405, anchor = CENTER) #MINUS button for Set Temp field button_minus = tk.Button(master, text="-", width=1, font=("Helvetica", 8), command=decrease_temp) button_minus.place(relx = 0.62, rely = 0.475, anchor = CENTER) # main programme thermostat() master.mainloop() RE: Help with tkinter for a heating control GUI - Larz60+ - Dec-15-2019 Quote:I do not understand how the "window_name.mainloop()" worksmainloop is a process that runs the program, then waits for a quit command from tkinter upon which it terminates, basically it's waiting for an event (interrupt). Quote:Do I understand correctly or variables in functions only live in that function? I am struggling to have the "bypass" button to work - it would set a flag which would prevent the boiler from starting if set to TRUE - but if I understand correctly variables only live into each function independently? This can be overcome forever, if you use classes, which are not trivial, and I can't explain in this post. I can however do two things:
Here's an example that displays a virtual environment (or system) package list, and saves to a requirements.txt (or other name) file that can be used for packaging import tkinter as tk import tkinter.filedialog as fd from pip._internal.operations import freeze class InstalledPackages: def __init__(self, parent): self.w = parent self.bgc1 = '#ffffe5' self.bgc2 = 'Lavender' self.bgc3 = ' LightCyan' self.pkglist = None self.w.geometry('400x400+10+10') self.w.title("Larz60+ Python Package List") self.pkglist = freeze.freeze() self.t1 = None def list_packages(self): w = self.w self.t1 = tk.Text(w, wrap=tk.WORD, undo=0, bg=self.bgc1) self.t1.pack(expand=1, fill=tk.BOTH) self.t1scroll = tk.Scrollbar(self.t1) self.t1.configure(yscrollcommand=self.t1scroll.set) self.t1scroll.config(command=self.t1.yview) self.t1scroll.pack(side=tk.RIGHT, fill=tk.Y) b1 = tk.Button(w, text='Save', command=self.save_requirements_file) b1.pack(side=tk.BOTTOM) self.t1.insert(tk.END, "{Package Name} -- {Version Info}\n\n") for pkg in self.pkglist: self.t1.insert(tk.END, f" {pkg}\n") def save_requirements_file(self): fp = fd.asksaveasfile(mode='w', defaultextension=".txt") gui_text = str(self.t1.get(1.0, tk.END)) # Nothing to do if fp is None fp.write(f"{gui_text}") if __name__ == '__main__': root = tk.Tk() InsPkgs = InstalledPackages(root) InsPkgs.list_packages() root.mainloop()output (my virtual environment) [attachment=754] Note that all variables are stores and initialized in the __init__ method. self.t1 is used across methods, in list_packages and save_requirements_file |