I have a project where I have multiple "Widgets" in a frame. I pass a list of Widgets onto a class called GUI which displays all these "Widgets". I have a button on my frame which needs to delete one of the widgets from the list. Which it does. But the widget itself does not disappear from the frame.
So to clarify
The GUI class has a list of objects [Object, Object, Object, Object]. So It now displays 4 Widget objects. When I press the delete button the new list looks like [Object, Object, Object] but the list in the GUI class is still [Object, Object, Object, Object]. How can I make it so the GUI always uses the newest version of the list?
The code to clarify
import random
from tkinter import *
import datetime
root = Tk()
root.configure(background="white")
root.title("Project 2.1")
Widgets = []
class GUI:
def __init__(self, widgets, master):
self.widgets = widgets
self.master = master
self.master.geometry("{0}x{1}+0+0".format(master.winfo_screenwidth(), master.winfo_screenheight()))
for widget in range(len(widgets)):
widgets[widget].widgetFrame.pack(side=LEFT, pady=20, padx=20, fill=Y)
if len(widgets) == 0:
NoInputLabel = Label(master, text="No modules connected, please connect a module", font='Helvetica 16 bold')
NoInputLabel.pack(side=TOP)
removeButton = Button(master, text="Remove widget", command=self.removeWidget)
removeButton.pack()
print(self.widgets)
def removeWidget(self):
self.widgets = self.widgets[:-1]
print(self.widgets)
class Widget:
def __init__(self, master, name):
self.master = master
self.name = name
colorArray = ["#e3e0f3", "#eebddd", "#80a3db", "#036fa0"]
self.widgetFrame = Frame(master, bg="white")
# widgetFrame.pack(side=LEFT, pady=20, padx=50)
widgetTop = Frame(self.widgetFrame, bg="white")
widgetTop.pack(side=TOP)
widgetCenter = Frame(self.widgetFrame, bg="white")
widgetCenter.pack(side=TOP)
widgetBottom = Frame(self.widgetFrame, bg="white")
widgetBottom.pack(side=TOP, fill=X)
self.WidgetName = Label(widgetTop, text=name, font='Helvetica 16 bold', bg=random.choice(colorArray))
self.WidgetName.pack(fill=X)
self.temperatureSliderLabel = Label(widgetTop, text="Temperature (°C)", bg="white")
self.temperatureSliderLabel.pack()
self.temperatureSlider = Scale(widgetTop, orient=HORIZONTAL, length=250, from_=0, to=40, bg="white")
self.temperatureSlider.pack()
self.lightSliderLabel = Label(widgetTop, text="Light Intensity (%)", bg="white")
self.lightSliderLabel.pack()
self.lightSlider = Scale(widgetTop, orient=HORIZONTAL, length=250, bg="white")
self.lightSlider.pack()
self.maxRolloutPositionLabel = Label(widgetTop, text="Rolling distance (cm): ", bg="white")
self.maxRolloutPositionLabel.pack()
self.maxRolloutEntry = Entry(widgetTop, bg="white")
self.maxRolloutEntry.pack(side=LEFT)
self.submitFormButton = Button(widgetTop, text="Submit", command=self.setSensors, bg="white")
self.submitFormButton.pack(side=LEFT)
self.openSunblindButton = Button(widgetCenter, text="Open sunblind", command=self.openSunblind, state=NORMAL,
bg="#28a745", fg="white")
self.openSunblindButton.pack(side=LEFT)
self.closeSunblindButton = Button(widgetCenter, text="Close sunblind", command=self.closeSunblind,
state=NORMAL, bg="#dc3545", fg="white")
self.closeSunblindButton.pack(side=LEFT)
self.setSunblindStatusButton = Button(widgetCenter, text="Automatic", command=self.setSunblindStatus,
bg="#6c757d", fg="white")
self.setSunblindStatusButton.pack(side=LEFT)
self.sunblindFrame = Frame(widgetBottom, bg="white")
self.sunblindFrame.pack(fill=X)
self.sunblindStatusLabel = Label(self.sunblindFrame, text="Sunblind status:", bg="white", anchor="w")
self.sunblindStatusLabel.pack(side=LEFT, pady=5)
self.sunblindStatus = Label(self.sunblindFrame, text="Manual", bg="white")
self.sunblindStatus.pack(side=RIGHT, pady=5)
self.temperatureFrame = Frame(widgetBottom, bg="white")
self.temperatureFrame.pack(fill=X)
self.temperatureValueLabel = Label(self.temperatureFrame, text="Temperature: ", justify=LEFT, bg="white",
anchor="w")
self.temperatureValueLabel.pack(side=LEFT, pady=5)
self.temperatureValue = Label(self.temperatureFrame, text="", bg="white")
self.temperatureValue.pack(side=RIGHT, pady=5)
self.lightSliderFrame = Frame(widgetBottom, bg="white")
self.lightSliderFrame.pack(fill=X)
self.lightSliderValueLabel = Label(self.lightSliderFrame, text="Light Intensity: ", justify=LEFT, bg="white",
anchor="w")
self.lightSliderValueLabel.pack(side=LEFT, pady=5)
self.lightSliderValue = Label(self.lightSliderFrame, text="", bg="white")
self.lightSliderValue.pack(side=RIGHT, pady=5)
self.rolloutFrame = Frame(widgetBottom, bg="white")
self.rolloutFrame.pack(fill=X)
self.rolloutLabel = Label(self.rolloutFrame, text="Roll-out position: ", justify=LEFT, bg="white", anchor="w")
self.rolloutLabel.pack(side=LEFT, pady=5)
self.rolloutValue = Label(self.rolloutFrame, text="", bg="white")
self.rolloutValue.pack(side=RIGHT, pady=5)
self.variable = StringVar(widgetBottom)
self.variable.set(self.name)
self.chooseArduino = OptionMenu(widgetBottom, self.variable, "Living Room", "Bedroom", "Study", "Sex Dungeon",
"Bingo club")
self.chooseArduino.pack()
self.setNameButton = Button(widgetBottom, text="Set name", command=self.setArduinoName)
self.setNameButton.pack()
def setSensors(self):
print("Set the temperature and light of the sensors")
self.temperatureValue.config(text=str(self.temperatureSlider.get()) + "°C")
self.temperature = self.temperatureSlider.get()
self.lightSliderValue.config(text=str(self.lightSlider.get()))
self.lightIntensity = self.lightSlider.get()
self.maxRolloutPositionLabel.config(text="Rolling distance (cm): " + str(self.maxRolloutEntry.get()))
def setName(self, widgetName):
self.widgetName = widgetName
def openSunblind(self):
print("Set the sunblind to an open state")
self.rolloutValue.config(text="Rolled out")
def closeSunblind(self):
print("Set the sunblind to an closed state")
self.rolloutValue.config(text="Rolled in")
def setSunblindStatus(self):
if self.setSunblindStatusButton.config('text')[-1] == 'Automatic':
self.openSunblindButton.config(state=DISABLED)
self.closeSunblindButton.config(state=DISABLED)
self.setSunblindStatusButton.config(text='Manual')
print("Sunblind is set to: " + self.setSunblindStatusButton['text'])
self.sunblindStatus.config(text="Automatic")
else:
self.openSunblindButton.config(state=NORMAL)
self.closeSunblindButton.config(state=NORMAL)
self.setSunblindStatusButton.config(text='Automatic')
print("Sunblind is set to: " + self.setSunblindStatusButton['text'])
self.sunblindStatus.config(text="Manual")
def setArduinoName(self):
self.WidgetName.config(text=self.variable.get())
def getTemperature(self):
return self.temperature
def getLightIntensity(self):
return self.lightIntensity
Arduino1 = Widget(root, "Arduino 1")
Arduino2 = Widget(root, "Arduino 2")
Arduino3 = Widget(root, "Arduino 3")
Arduino4 = Widget(root, "Arduino 4")
Arduino5 = Widget(root, "Arduino 5")
Widgets.append(Arduino1)
Widgets.append(Arduino2)
Widgets.append(Arduino3)
Widgets.append(Arduino4)
Widgets.append(Arduino5)
Visueel = GUI(Widgets, root)
root.mainloop()
Image
You could try something like
def removeWidget(self):
widget = self.widgets.pop()
widget.widgetFrame.pack_forget()
print(self.widgets)
Related
I am new to python, the above figure is two UI, I was trying to insert the two tab from the right side UI into the left side UI. But I had met some error and no idea how to solve.
Now below is the original coding of the left side UI
import tkinter as tkk
from tkinter import *
from tkinter import messagebox
import os.path
import hashlib
import sys
import time
import getpass
from tkinter import filedialog
import platform
import getpass
import os,sys
import tkinter.font as font
from tkinter import ttk
from tkinter import StringVar
import Consts
import shutil
class Page(tkk.Frame):
def __init__(self, *args, **kwargs):
tkk.Frame.__init__(self, *args, **kwargs)
def show(self):
self.lift()
class Page1(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs,bg='white')
my_system=platform.uname()
#computer information
comsys=my_system.system
comnode=my_system.node
comrel=my_system.release
comver=my_system.version
commac=my_system.machine
compro=my_system.processor
comuser=getpass.getuser()
label1 = tkk.Label(self, text="System: "+comsys,bg='white')
label1.grid(row=1,column=1,pady=10,sticky='w')
label2 = tkk.Label(self, text="Computer Name: "+comnode,bg='white')
label2.grid(row=2,column=1,pady=10,sticky='w')
label3 = tkk.Label(self, text="Release: "+comrel,bg='white')
label3.grid(row=3,column=1,pady=10,sticky='w')
label4 = tkk.Label(self, text="Version: "+comver,bg='white')
label4.grid(row=4,column=1,pady=10,sticky='w')
label5 = tkk.Label(self, text="Machine: "+commac,bg='white')
label5.grid(row=5,column=1, pady=10,sticky='w')
label6 = tkk.Label(self, text="Processor: "+compro,bg='white')
label6.grid(row=6,column=1, pady=10,sticky='w')
label7 = tkk.Label(self, text="Username: "+comuser,bg='white')
label7.grid(row=7,column=1,pady=10,sticky='w')
#computer usage hold first, no idea how to do
class Page2(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs,bg='white')
tabControl=ttk.Notebook(self)
qsFrame = ttk.Frame(tabControl)
fsFrame = ttk.Frame(tabControl)
csFrame = ttk.Frame(tabControl)
#tab
tabControl.add(qsFrame, text='Quick Scan')
tabControl.add(fsFrame, text='Full Scan')
tabControl.add(csFrame, text='Custom Scan')
tabControl.pack(expand=1,fill="both")
class Page3(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs,bg='white')
label = tkk.Label(self, text="This is page 3")
label.grid(row=2,column=1)
def mytools():
total, used, free = shutil.disk_usage("/")
print("Total:%d GB" %(total // (2**30)))
print("Used:%d GB" %(used // (2**30)))
print("Free:%d GB" %(free // (2**30)))
if free <= total/2:
clean = os.popen('Cleanmgr.exe/ sagerun:1').read()
#print(clean)
def btn1():
if __name__ =="__main__":
mytools()
button1=ttk.Button(self,text="Clean Up",command=btn1)
button1.grid(row=3,column=2)
class Page4(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs,bg='white')
label = tkk.Label(self, text="This is page 4")
label.grid(row=2,column=1)
class MainView(tkk.Frame):
def __init__(self, *args, **kwargs):
tkk.Frame.__init__(self, *args, **kwargs)
p1 = Page1(self)
p2 = Page2(self)
p3 = Page3(self)
p4 = Page4(self)
buttonframe = tkk.Frame(self)
container = tkk.Frame(self,bg='white')
buttonframe.pack(side="left", fill="x", expand=False)
container.pack(side="left", fill="both", expand=True)
buttonframe.grid_rowconfigure(0,weight=1)
buttonframe.grid_columnconfigure(0,weight=1)
container.grid_rowconfigure(0,weight=1)
container.grid_columnconfigure(0,weight=1)
p1.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
p2.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
p3.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
p4.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
stats_btn = tkk.PhotoImage(file='C:/FYP/SecuCOM2022/icon&pic/stats.png')
scanner_btn = tkk.PhotoImage(file='C:\FYP\SecuCOM2022\icon&pic\scanner.png')
speedup_btn = tkk.PhotoImage(file='C:\FYP\SecuCOM2022\icon&pic\speedup.png')
settings_btn = tkk.PhotoImage(file='C:\FYP\SecuCOM2022\icon&pic\settings.png')
#logo
logo=tkk.PhotoImage(file="C:\FYP\SecuCOM2022\icon&pic\g (1).png")
label=tkk.Label(buttonframe,image=logo)
label.grid(row=0,column=0, padx=10,pady=10)
logo.image = logo
b1 = tkk.Button(buttonframe, image=stats_btn, command=p1.show, borderwidth=0)
b2 = tkk.Button(buttonframe, image=scanner_btn, command=p2.show, borderwidth=0)
b3 = tkk.Button(buttonframe, image=speedup_btn, command=p3.show, borderwidth=0)
b4 = tkk.Button(buttonframe, image=settings_btn, command=p4.show, borderwidth=0)
b1.image = stats_btn
b2.image = scanner_btn
b3.image = speedup_btn
b4.image = settings_btn
b1.grid(row=1,column=0,padx=10,pady=10)
b2.grid(row=2,column=0,padx=10,pady=10)
b3.grid(row=3,column=0,padx=10,pady=10)
b4.grid(row=4,column=0,padx=10,pady=10)
if __name__ == "__main__":
root= Tk()
main = MainView(root)
main.pack(side="top", fill="both", expand=True)
main.grid_rowconfigure(0,weight=1)
main.grid_columnconfigure(0,weight=1)
root.title("SecuCOM2022")
root.geometry("600x300")
root.maxsize(600,375)
root.minsize(600,375)
root.iconbitmap('C:\FYP\SecuCOM2022\icon&pic\g.png')
root.mainloop()
root.mainloop()
#GUI end`
Next below here is the right side UI coding, there are multiple files for it. I just show files related with the UI only.
Consts.py
ENTRY_WIDTH = 50
FileReportTab.py
from tkinter import filedialog
from tkinter import messagebox
from tkinter import ttk
from tkinter import StringVar
import time
import os.path
import sys
from VTPackage import Consts
class FileReportTab:
def __init__(self, root, frame, vtClient):
self.root = root
self.frame = frame
self.vtClient = vtClient
self.mainVTURLframe = ttk.LabelFrame(frame, text=' File report')
self.mainVTURLframe.grid(column=0, row=1, padx=8, pady=4)
ttk.Label(self.mainVTURLframe, text="Progress:").grid(column=0, row=1, sticky='W') # <== right-align
self.progressBar = ttk.Progressbar(self.mainVTURLframe, orient='horizontal', length=300, mode='determinate')
self.progressBar.grid(column=1, row=1)
ttk.Label(self.mainVTURLframe, text="File path:").grid(column=0, row=2, sticky='W') # <== right-align
self.filePath = StringVar()
filePathEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=self.filePath, state='readonly')
filePathEntry.grid(column=1, row=2, sticky='W')
ttk.Label(self.mainVTURLframe, text="Status:").grid(column=0, row=3, sticky='W') # <== right-align
self.status = StringVar()
statusEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=self.status, state='readonly')
statusEntry.grid(column=1, row=3, sticky='W')
ttk.Label(self.mainVTURLframe, text="Positive Indications:").grid(column=0, row=4, sticky='W') # <== right-align
self.positiveIndications = StringVar()
positiveIndicationsEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=self.positiveIndications, state='readonly')
positiveIndicationsEntry.grid(column=1, row=4, sticky='W')
ttk.Label(self.mainVTURLframe, text="SHA1:").grid(column=0, row=5, sticky='W') # <== right-align
self.sha1 = StringVar()
sha1Entry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=self.sha1, state='readonly')
sha1Entry.grid(column=1, row=5, sticky='W')
ttk.Label(self.mainVTURLframe, text="SHA256:").grid(column=0, row=6, sticky='W') # <== right-align
self.sha256 = StringVar()
sha256Entry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=self.sha256, state='readonly')
sha256Entry.grid(column=1, row=6, sticky='W')
chooseFileButton = ttk.Button(self.mainVTURLframe, text="Choose File", width=40, command=self._scanFile).grid(column=1, row=0)
self.scanCheckingTimeInterval = 25000 # This is the amount of time we are going to wait before asking VT again if it already processed our scan request
for child in self.mainVTURLframe.winfo_children():
child.grid_configure(padx=4, pady=2)
def showResults(self, results):
try:
#self.file_Path = self.filePath
self.sha1.set(results["sha1"])
self.sha256.set(results["sha256"])
self.positiveIndications.set(results["positives"])
if results["positives"] == 0:
messagebox.showwarning("Analysis Info","File is Safe.\nOur Scanners found nothing Malicious")
elif results["positives"] <= 5:
messagebox.showwarning("Analysis Alert", "Given File may be Malicious")
elif results["positives"] >= 5:
messagebox.showwarning("Analysis Alert", f"Given File is Malicious.\nAdvice you remove the file from your System!")
res = messagebox.askyesno("Analysis Alert","The given file is highly Malicious.\nDo you want to Delete it permanently?")
if res == 1:
print("Attemting to delete file...")
time.sleep(1)
os.remove(self.filePath1)
#if os.PathLike(_scanFile.filePath):
# os.remove(self.filePath)
else:
print("This file cannot be deleted. Please do not use the fie. It's Malicious")
except Exception as e:
messagebox.showerror('Error', e)
def checkStatus(self):
try:
self.scanResult = self.vtClient.get_file_report(self.scanID)
print(self.scanResult)
if self.scanResult["response_code"] == -2: # By reading the next line, you can understand what is the meaning of the -2 response ode
self.status.set("Scanning...")
self.progressBar['value'] = self.progressBar['value'] + 5
self.root.update_idletasks()
self.mainVTURLframe.after(self.scanCheckingTimeInterval, self.checkStatus)
else:
self.hasScanFinished = True
self.showResults(self.scanResult)
self.status.set("Finished!")
self.progressBar['value'] = 100
except Exception as e:
if "To much API requests" in str(e):
pass
def _scanFile(self):
try:
self.progressBar['value'] = 0
self.filePath1 = filedialog.askopenfilename(initialdir="/", title="Select file for VT", filetypes=(("EXE files", "*.exe"), ("all files", "*.*")))
if (self.filePath): # Only if the user chose a file, we will want to continue the process
self.filePath.set(self.filePath1)
self.status.set("Sending file...")
self.progressBar['value'] = 10
self.root.update_idletasks()
self.scanID = self.vtClient.scan_file(self.filePath1)
self.hasScanFinished = False
if not self.hasScanFinished:
self.scanResult = self.vtClient.get_file_report(self.scanID)
print(self.scanResult)
self.checkStatus()
# We could have been using time.sleep() or time.wait(), but then our UI would get stuck.
# by using after, we are initiating a callback in which does not blocks our event loop
except Exception as e:
messagebox.showerror('Error', e)
URLreportTab.py
from tkinter import ttk
from tkinter import StringVar
from VTPackage import Consts
class URLreportTab:
def __init__(self, root, frame, vtClient):
self.root = root
self.frame = frame
self.mainVTURLframe = ttk.LabelFrame(frame, text=' URL report tab!')
# using the tkinter grid layout manager
self.mainVTURLframe.grid(column=0, row=0, padx=8, pady=4)
ttk.Label(self.mainVTURLframe, text="URL:").grid(column=0, row=0, sticky='W') # What does sticky does? Sticky sayes where to stick the label to : N,S,E,W
urlEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH)
urlEntry.grid(column=1, row=0, sticky='E')
ttk.Label(self.mainVTURLframe, text="Positive Indications:").grid(column=0, row=1, sticky='W') # <== right-align
Positive = StringVar()
PositiveEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=Positive, state='readonly')
PositiveEntry.grid(column=1, row=1, sticky='W')
ttk.Label(self.mainVTURLframe, text="Detections:").grid(column=0, row=2, sticky='W') # <== right-align
detections = StringVar()
detectionsEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=detections, state='readonly')
detectionsEntry.grid(column=1, row=2, sticky='W')
self.notificationFrame = ttk.LabelFrame(self.frame, text=' Notifications', width=40)
# using the tkinter grid layout manager
self.notificationFrame.grid(column=0, row=1, padx=8, pady=10, sticky='W')
ttk.Label(self.notificationFrame, text="Errors:").grid(column=0, row=0, sticky='W') # <== increment row for each
Error = StringVar()
ErrorEntry = ttk.Entry(self.notificationFrame, width=Consts.ENTRY_WIDTH, textvariable=Error, state='readonly')
ErrorEntry.grid(column=1, row=0, sticky='W')
def _cleanErrorMessage(): # We could have been doing this without a function, but it is more neat that way
Error.set("")
def _getReport():
# the _ notation before a function means that this function is internal to the class only. As python cannot really prevent you from using it outside the class (as C# for example) the notation is being used to warn other developers not to call this function outside the class
try:
_cleanErrorMessage() # Starting with cleaning the error message bar
if not urlEntry.get():
print('Please enter a URL')
Error.set("Please enter a URL!")
return
urlToCheck = urlEntry.get()
response = vtClient.get_url_report(urlToCheck)
print(response)
Positive.set(response["positives"])
scans = response["scans"]
findings = set()
for key, value in scans.items():
if value["detected"]:
findings.add(value["result"])
detections.set(",".join([str(finding) for finding in findings]))
except Exception as e:
print(e)
Error.set(e)
checkURLinVTButton = ttk.Button(self.mainVTURLframe, text='Check Now!', command=_getReport).grid(column=2, row=0)
# Instead of setting padding for each UI element, we can just iterate through the children of the main UI object.
for child in self.mainVTURLframe.winfo_children():
child.grid_configure(padx=4, pady=2)
for child in self.notificationFrame.winfo_children():
child.grid_configure(padx=4, pady=2)
VTApp.py
import tkinter as tk
import configparser
from tkinter import Menu
from tkinter import ttk
from tkinter import messagebox
from VTPackage import URLreportTab
from VTPackage import FileReportTab
from VTPackage import VTClient
config = configparser.ConfigParser()
config.read('config.ini')
class VTApp:
def __init__(self):
# Loading the config file
self.config = configparser.ConfigParser()
self.config.read('config.ini')
self.virusTotalAPIkey = config['VirusTotal']['apiKey']
self.vtClient = VTClient.VTClient(self.virusTotalAPIkey)
self.root = tk.Tk()
self.root.title("Virus Total UI")
self.menuBar = Menu()
self.root.config(menu=self.menuBar)
self.fileMenu = Menu(self.menuBar, tearoff=0)
self.fileMenu.add_command(label="New")
self.fileMenu.add_separator()
self.menuBar.add_cascade(label="File", menu=self.fileMenu)
if not self.vtClient.is_API_key_valid():
messagebox.showerror('Error', "API key is not valid! Check your config file")
def _quit():
self.root.quit() # The app will exist when this function is called
self.root.destroy()
exit()
self.fileMenu.add_command(label="Exit", command=_quit) # command callback
self.tabControl = ttk.Notebook(self.root) # Create Tab Control
self.urlFrame = ttk.Frame(self.tabControl)
self.urlTab = URLreportTab.URLreportTab(self.root, self.urlFrame, self.vtClient)
self.tabControl.add(self.urlFrame, text='URL')
self.fileFrame = ttk.Frame(self.tabControl)
self.fileTab = FileReportTab.FileReportTab(self.tabControl, self.fileFrame, self.vtClient)
self.tabControl.add(self.fileFrame, text='File')
self.tabControl.pack(expand=1, fill="both") # Pack to make visible
def start(self):
self.root.mainloop()
Main.py
from VTPackage import VTApp
vtApp = VTApp.VTApp()
vtApp.start()
This is the original code, Sorry for the spacing error, I copy&paste from vsc and it seem like the got some spacing error after Class. So basically this is the original code and I try like import VTApp and code inside class Page2 like
vtApp = VTApp.VTApp()
vtApp.start()
and change some coding in the VTApp.py but it doesn't work.... Does anyone know how to make the script works? I been trying and trying for a week and still couldn't get the solution.
You cannot move a widget from one window to another in tkinter. You will have to recreate the tab in the other window.
May i know where and how do i need to do. I want to add a checkbox in every row and when it checked, the button of update or delete will only effect to the checked row.
I am new in python and currently i'm doing this for my project gui, is that anyone can help or if any suggestion you're welcome. Thanks
Below is my code:
from tkinter import *
from tkinter import messagebox
import mysql.connector
win = Tk()
win.title("Admin Signup")
win.geometry("750x400+300+90")
frame1 = Frame(win)
frame1.pack(side = TOP, fill=X)
frame2 = Frame(win)
frame2.pack(side = TOP, fill=X)
frame3 = Frame(win)
frame3.pack(side = TOP, padx = 10, pady=15)
frame4 = Frame(win)
frame4.pack(side = TOP, padx = 10)
frame5 = Frame(win)
frame5.pack(side = LEFT, padx = 10)
lbl_title = Label(frame1, text = "User List", font = ("BOLD 20"))
lbl_title.pack(side = TOP, anchor = "w", padx = 20, pady = 20)
btn_register = Button(frame2, text = "Register User")
btn_register.pack(side = TOP, anchor = "e", padx=20)
lbl01 = Label(frame3, text="Username", width=17, anchor="w", relief="raised")
lbl01.grid(row=0, column=0)
lbl02 = Label(frame3, text="Password", width=17, anchor="w", relief="raised")
lbl02.grid(row=0, column=1)
lbl03 = Label(frame3, text="Full Name", width=17, anchor="w", relief="raised")
lbl03.grid(row=0, column=2)
lbl04 = Label(frame3, text="Ic Number", width=17, anchor="w", relief="raised")
lbl04.grid(row=0, column=3)
lbl05 = Label(frame3, text="Staff Id", width=17, anchor="w", relief="raised")
lbl05.grid(row=0, column=4)
mydb = mysql.connector.connect(
host = "localhost",
user = "username",
password = "password",
database = "adminacc"
)
mycursor = mydb.cursor()
mycursor.execute("SELECT * FROM acc")
i = 0
for details in mycursor:
for j in range(len(details)):
e = Entry(frame4, width=17, relief=SUNKEN)
e.grid(row=i, column=j)
e.insert(END, details[j])
e.config(state=DISABLED, disabledforeground="blue")
i = i+1
btn_update = Button(frame5, text = "Update")
btn_update.grid(row=0, column=0, padx=15)
btn_delete = Button(frame5, text = "Delete")
btn_delete.grid(row=0, column=1)
win.mainloop()
Since every row behaves the same, suggest to use a class to encapsulate the behavior:
class AccountInfo:
def __init__(self, parent, details, row):
self.entries = []
# create entry box for each item in 'details'
for col, item in enumerate(details):
e = Entry(parent, width=17, relief=SUNKEN, disabledforeground='blue', bd=2)
e.grid(row=row, column=col)
e.insert(END, item)
e.config(state=DISABLED)
self.entries.append(e)
# create the checkbutton to select/deselect current row
self.var = BooleanVar()
Checkbutton(parent, variable=self.var, command=self.state_changed).grid(row=row, column=col+1)
def state_changed(self):
state = NORMAL if self.selected else DISABLED
# enable/disable entries except username
for e in self.entries[1:]:
e.config(state=state)
#property
def selected(self):
return self.var.get()
#property
def values(self):
return tuple(x.get() for x in self.entries)
Then using the class to create the required rows for each record retrieved from database:
mycursor.execute("SELECT * FROM acc")
accounts = [] # used to store the rows (accounts)
for row, details in enumerate(mycursor):
acc = AccountInfo(frame4, details, row)
accounts.append(acc)
The saved accounts can then be used in the callbacks of Update and Delete buttons:
def update_accounts():
for acc in accounts:
if acc.selected:
print(acc.values)
# do whatever you want on this selected account
btn_update = Button(frame5, text="Update", command=update_accounts)
Same logic on Delete button.
Note that you can modify the AccountInfo class to add functionalities that suit what you need.
https://github.com/Rodneyst/lv100roxas-gmail.com/blob/master/finalproject
Can look over the full code here. The link goes to the full code, I pull all the answer with one button using stringvar. Please answer I have 2 days left and haven't had any luck.
# Username widget
self.prompt_label5 = tkinter.Label(self.top_frame,
text='Enter your Name:' ,bg="red", fg="yellow", font="none 12 bold")
self.name_entry = tkinter.Entry(self.top_frame,
width=10)
# weight widget
self.prompt_label1 = tkinter.Label(self.top_frame,
text='Enter your weight(lbs):' ,bg="red", fg="yellow", font="none 12 bold")
self.weight_entry = tkinter.Entry(self.top_frame,
width=10)
def getworkout(self):
weight = float(self.weight_entry.get())
height = float(self.height_entry.get())
How would I validate these user inputs? Please help — I cannot figure it out. I want to get a pop-up window to show when the user inputs an integer for name and when they enter a string for weight and height, ValueErrors. Please help!
Code is messy, so make it look better :) But I think it does, what you asked about. I edited it in a hurry. Name also accept more signs then just letters (excluding integers) - it's easy to validate if necessery.
import tkinter
from tkinter import ttk
from tkinter import messagebox
import random
# GUI
class WorkoutGUI:
def __init__(self):
# Main window
self.main_window = tkinter.Tk()
self.main_window.title("Workout Generator")
self.main_window.geometry('1200x600')
# Frames
self.top_frame = tkinter.Frame()
self.mid_frame = tkinter.Frame()
self.bottom_frame = tkinter.Frame()
validation1 = self.top_frame.register(entry_validation_1)
validation2 = self.top_frame.register(entry_validation_2)
# TOP FRAME
# username widget
self.prompt_label1 = tkinter.Label(self.top_frame,
text='Enter your Name:', bg="red", fg="yellow", font="none 12 bold")
self.name_entry = tkinter.Entry(self.top_frame,
width=10, validate='key', validatecommand=(validation1, '%S'))
# weight widget
self.prompt_label2 = tkinter.Label(self.top_frame,
text='Enter your weight(lbs):', bg="red", fg="yellow", font="none 12 bold")
self.weight_entry = tkinter.Entry(self.top_frame,
width=10, validate='key', validatecommand=(validation2, '%S'))
# height widget
self.prompt_label3 = tkinter.Label(self.top_frame,
text='Enter your height(in):', bg="red", fg="yellow",
font="none 12 bold")
self.height_entry = tkinter.Entry(self.top_frame,
width=10, validate='key', validatecommand=(validation2, '%S'))
# gender widget
self.prompt_label4 = tkinter.Label(self.top_frame,
text='Select your gender:', bg="red", fg="yellow",
font="none 12 bold")
self.gender_entry = tkinter.IntVar()
self.gender_entry.set(1)
# radio buttons for gender choice
self.rb1 = tkinter.Radiobutton(self.top_frame,
text='Male',
variable=self.gender_entry,
value=1)
self.rb2 = tkinter.Radiobutton(self.top_frame,
text='Female',
variable=self.gender_entry,
value=2)
# goal widget
self.prompt_label5 = tkinter.Label(self.top_frame,
text='Select your goal:', bg="red", fg="yellow",
font="none 12 bold")
self.goal_entry = tkinter.IntVar()
self.goal_entry.set(1)
# radio buttons for goal choice
self.rb3 = tkinter.Radiobutton(self.top_frame,
text='Lose Weight',
variable=self.goal_entry,
value=1)
self.rb4 = tkinter.Radiobutton(self.top_frame,
text='Maintain',
variable=self.goal_entry,
value=2)
self.rb5 = tkinter.Radiobutton(self.top_frame,
text='Build Muscle Mass',
variable=self.goal_entry,
value=3)
# packing widgets
self.prompt_label1.pack(side='top')
self.name_entry.pack(side='top')
self.prompt_label2.pack(side='top')
self.weight_entry.pack(side='top')
self.prompt_label3.pack(side='top')
self.height_entry.pack(side='top')
self.prompt_label4.pack(side='top')
self.rb1.pack(side='top')
self.rb2.pack(side='top')
self.prompt_label5.pack(side='left')
self.rb3.pack(side='left')
self.rb4.pack(side='left')
self.rb5.pack(side='left')
# MIDDLE FRAME
self.descr_label = tkinter.Label(self.mid_frame,
text='Workout for you:')
self.value = tkinter.StringVar()
self.workout_label = tkinter.Label(self.mid_frame,
textvariable=self.value,
width=250, height=20, bg="white")
# packing widgets
self.descr_label.pack(side='top')
self.workout_label.pack(side='bottom')
# BOTTOM FRAME
# buttons
self.display_button = tkinter.Button(self.bottom_frame,
text='Get Workout!',
command=self.getworkout)
self.quit_button = tkinter.Button(self.bottom_frame,
text='Quit',
command=self.main_window.destroy)
# pack the buttons
self.display_button.pack(side='left')
self.quit_button.pack(side='right')
# packing frames
self.top_frame.pack()
self.mid_frame.pack()
self.bottom_frame.pack()
# fucnction to get outputs
def getworkout(self):
# Gather User information
name = str(self.name_entry.get())
gymSerial = random.randint(0, 10000)
weight = float(self.weight_entry.get())
height = float(self.height_entry.get())
gender = (self.gender_entry.get())
goal = (self.goal_entry.get())
def entry_validation_1(x):
if x.isdecimal():
messagebox.showwarning("Wrong input!", "Name must be a string - not integer.")
return False
else:
return True
def entry_validation_2(y):
if y.isdecimal():
return True
else:
messagebox.showwarning("Wrong input!", "Weight and height must be a numerical value - not string")
return False
def main():
root = tkinter.Tk()
ex = WorkoutGUI()
root.mainloop()
if __name__ == '__main__':
main()
Link is not working, but I wrote my own code as an example. I think it's very close to what you need.
The method is called entry validation and you can find some more info here:
https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/entry-validation.html
Feel free to ask if you have any concerns :)
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
# tkinter main window
root = tk.Tk()
root.title('Validation')
root.geometry('300x300')
def entry_validation_1(x):
if x.isdecimal():
messagebox.showwarning("Wrong input!", "Name must be a string - not integer.")
return False
else:
return True
def entry_validation_2(y):
if y.isdecimal():
return True
else:
messagebox.showwarning("Wrong input!", "Weight and height must be a numerical value - not string")
return False
validation1 = root.register(entry_validation_1)
validation2 = root.register(entry_validation_2)
name = tk.Entry(root, width='9', justify='center', validate='key', validatecommand=(validation1, '%S'))
weight_or_height = tk.Entry(root, width='9', justify='center', validate='key', validatecommand=(validation2, '%S'))
label_name = tk.Label(root, text='name - INTEGER NOT ACCEPTED')
label_weight_or_height = tk.Label(root, text='weight or height - STRING NOT ACCEPTED')
name.grid(row=0, column=0, padx=0, pady=0)
label_name.grid(row=0, column=1, padx=0, pady=0)
weight_or_height.grid(row=1, column=0, padx=0, pady=0)
label_weight_or_height.grid(row=1, column=1, padx=0, pady=0)
tk.mainloop()
I'm building my first app with Tkinter and I'm stuck with instance of class.
I followed this tutorial https://pythonprogramming.net/change-show-new-frame-tkinter/ but some questions remain :
- how does mean controller as argument for the init line in the differents class ?
- I would like to create instance of classA in classB and inversely but I'm getting wrong with arguments !
- About organisation code, I created 2 class (one for each frame) and writting function related in the good one, should I split them in a 3rd class ? What is the most convenient way ?
Actualy the code is running but I would like to add some setup tools in the 2nd window !
What do I must do to can modify some parameters in the SettingsPage Class and update and use them in the StartPage class ? For example I would like to modify ecartementlogo.
setp = SettingsPage(controller, StartPage) give me AttributeError: type object 'StartPage' has no attribute 'tk'
Sorry for posting all the code but I not sure about what can I remove for the example
import tkinter as tk
from PIL import Image, ImageDraw, ImageColor, ImageFont, ImageChops, ImageColor, ImageFont, ImageTk as PIL
import os
import utilitary as utilitary
import tkinter.ttk as ttk
from tkinter import filedialog
from configparser import ConfigParser
LARGE_FONT= ("Verdana", 12)
basewidth = 400
config = ConfigParser()
config.read('config.ini')
class DotaCasterKit(tk.Tk):
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.iconbitmap(self,default='icone.ico')
tk.Tk.wm_title(self, "vs Creator")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, SettingsPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
menu = tk.Menu(container)
menu.config(background='#2B547E', fg='#2B547E')
tk.Tk.config(self, menu=menu)
file = tk.Menu(menu)
file.add_command(label="Préférences", command=self.show_settings)
file.add_command(label="Exit",command=quit)
menu.add_cascade(label="File", menu=file)
refresh = tk.Menu(menu)
menu.add_cascade(label="Refresh", menu=refresh)
def show_settings(self):
frame = self.frames[SettingsPage]
frame.tkraise()
class StartPage(tk.Frame):
def choose_background(self):
print(self.path_slogos)
self.background_file = filedialog.askopenfilename(initialdir='C:/Users/.../VS')
self.background = PIL.Image.open(self.background_file).convert('RGBA')
self.background_name = os.path.basename(self.background_file)
self.var1.set(self.background_name)
self.miniature = self.background
self.wpercent = (self.basewidth/float(self.miniature.size[0]))
self.hsize = int((float(self.miniature.size[1])*float(self.wpercent)))
self.miniature = self.miniature.resize((self.basewidth,self.hsize))
self.miniature = PIL.PhotoImage(self.miniature)
self.canvas.itemconfig(self.image_on_canvas, image =self.miniature)
def choose_slogos_path(self):
self.path_slogos = filedialog.askdirectory(initialdir='C:/Users/.../Logos')
self.var2.set(os.path.basename(self.path_slogos))
return self.path_slogos
def create_list_logos(self):
self.path_slogos = filedialog.askdirectory(initialdir='C:/Users/.../Logos')
self.var2.set("Dossier : "+os.path.basename(self.path_slogos))
self.files = []
self.list_files_names =[]
print(self.path_slogos)
for r, d, f in os.walk(self.path_slogos):
for file in f:
if '.png' in file and 'background' not in file:
self.files.append(os.path.join(r, file))
name = os.path.basename(file)
name = name[:-4]
self.list_files_names.append(name)
self.liste_1.config(values=self.list_files_names)
self.liste_2.config(values=self.list_files_names)
self.liste_1.current(0)
self.liste_2.current(0)
return self.list_files_names
def create_img(self):
self.composition = self.background
self.ecartementlogo = 550
rift_middle = ImageFont.truetype(os.path.join('C:/Users...rift/', 'fort_foundry_rift_bold.otf'), 150)
text_middle = 'VS'
text_match_format = self.entry_format_match.get()
rift_match_format = ImageFont.truetype(os.path.join('C:/Users...rift/', 'fort_foundry_rift_bold.otf'), 60)
rift_score = ImageFont.truetype(os.path.join('C:/Users...rift/', 'fort_foundry_rift_bold.otf'), 50)
self.score_1 = self.entry_score__1.get()
self.score_2 = self.entry_score__2.get()
self.1=self.liste_1.get()
self.2=self.liste_2.get()
self.logo_1 = PIL.Image.open(self.path_slogos+'/'+self.1+'.png').convert('RGBA')
self.logo_2 = PIL.Image.open(self.path_slogos+'/'+self.2+'.png').convert('RGBA')
#logo 1
self.composition = utilitary.draw_image_advanced(self.composition, self.logo_1,
[960-int(self.ecartementlogo), 550],
[None, 300],
1)
#logo 2
self.composition = utilitary.draw_image_advanced(self.composition, self.logo_2,
[960+int(self.ecartementlogo), 550],
[None, 300],
1)
image_draw = ImageDraw.Draw(self.composition)
#insert text (VS + score)
utilitary.draw_text_center_align(image_draw, [960, 450], text_middle, font=rift_middle, fill=utilitary.colors['white'])
utilitary.draw_text_center_align(image_draw, [960, 600], text_match_format, font=rift_match_format, fill=utilitary.colors['white'])
utilitary.draw_text_center_align(image_draw, [960-self.ecartementlogo, 700], self.score_1, font=rift_score, fill=utilitary.colors['light_red'])
utilitary.draw_text_center_align(image_draw, [960+self.ecartementlogo, 700], self.score_2, font=rift_score, fill=utilitary.colors['light_red'])
if self.var4.get()==0:
pass
if self.var4.get()==1:
config.set('main', 'default_file_background', self.background_file)
config.set('main', 'default_path_slogos', self.path_slogos)
with open('config.ini', 'w') as f:
config.write(f)
print(self.background_file)
print(self.path_slogos)
print("settings saved")
print("image created")
self.miniature = self.composition
self.wpercent = (self.basewidth/float(self.miniature.size[0]))
self.hsize = int((float(self.miniature.size[1])*float(self.wpercent)))
self.miniature = self.miniature.resize((self.basewidth,self.hsize))
self.miniature = PIL.PhotoImage(self.miniature)
self.canvas.itemconfig(self.image_on_canvas, image =self.miniature)
def save_img(self):
self.var5.set("Saved as " +self.1 + '_'+self.2+'.png')
self.composition.save('C:/.../'+self.1 + '_'+self.2+'.png')
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
setp = SettingsPage(controller, StartPage)
### this line don't work ### wrong arguments
self.background_file = config.get('main', 'default_file_background')
self.path_slogos = config.get('main', 'default_path_slogos')
self.group3 = tk.LabelFrame(self, text=" A & Score")
self.group3.pack (side="left", padx=5, pady=5)
self.group4 = tk.LabelFrame(self, text=" B & Score")
self.group4.pack (side="right", padx=5, pady=5)
self.liste_1 = ttk.Combobox(self.group3)
self.liste_1.pack(side="top", padx=5, pady=5)
self.liste_2 = ttk.Combobox(self.group4)
self.liste_2.pack(side="top", padx=5, pady=5)
self.liste_1, self.liste_2 = utilitary.initial_list_logos(self.path_slogos, self.liste_1, self.liste_2)
self.liste_1.current(0)
self.liste_2.current(0)
self.group1 = tk.LabelFrame(self, text="Background")
self.group1.pack (side="top", padx=5, pady=5)
self.button_choose_background_file = tk.Button(self.group1, text="Choose Background")
self.button_choose_background_file.config(command=self.choose_background)
self.button_choose_background_file.pack (side="top", padx=5, pady=5)
self.var1 = tk.StringVar()
self.var1.set(os.path.basename(self.background_file))
self.label_name_background_file = tk.Label(self.group1, textvariable=self.var1)
self.label_name_background_file.pack (side="bottom", padx=5, pady=5)
self.group2 = tk.LabelFrame(self, text="Logos s",labelanchor='ne')
self.group2.pack (side="top", padx=5, pady=5)
self.var2 = tk.StringVar()
self.var2.set("Dossier : "+os.path.basename(self.path_slogos))
self.label_path_slogo = tk.Label(self.group2, textvariable=self.var2)
self.label_path_slogo.pack (side="bottom", padx=5, pady=5)
self.button_list_logos = tk.Button(self.group2, text="Choose logos path")
self.button_list_logos.config(command=self.create_list_logos)
self.button_list_logos.pack (side="top", padx=5, pady=5)
self.score_1 = tk.StringVar()
self.score_1.set("")
self.entry_score__1 = tk.Entry(self.group3,textvariable=self.score_1, width=5, justify='center')
self.entry_score__1.pack(side="bottom", padx=5, pady=5)
self.score_2 = tk.StringVar()
self.score_2.set("")
self.entry_score__2 = tk.Entry(self.group4,textvariable=self.score_2, width=5, justify='center')
self.entry_score__2.pack(side="bottom", padx=5, pady=5)
self.button_show = tk.Button(self, text="Show Image")
self.button_show.config(command=self.create_img)
self.button_show.pack (side="bottom", padx=5, pady=5)
self.button_save_img = tk.Button(self, text="Save as image")
self.button_save_img.config(command=self.save_img)
self.button_save_img.pack (side="bottom", padx=5, pady=5)
self.var5 = tk.StringVar ()
self.var5.set('')
self.label_name_save = tk.Label(self, textvariable =self.var5)
self.label_name_save.pack (side="bottom", padx=5, pady=5)
self.format_match = tk.StringVar()
self.format_match.set("Format")
self.entry_format_match = tk.Entry(self,textvariable=self.format_match, width=10, justify='center')
self.entry_format_match.pack()
self.var4 = tk.IntVar ()
self.var4.set(0)
self.button_save_settings = tk.Checkbutton(self, text="Save settings", variable = self.var4)
self.button_save_settings.pack(side = "bottom")
self.ecartementlogo = tk.IntVar ()
self.var4.set(550)
self.background=PIL.Image.open(self.background_file).convert('RGBA')
self.basewidth = 400
self.miniature = self.background
self.wpercent = (self.basewidth/float(self.miniature.size[0]))
self.hsize = int((float(self.miniature.size[1])*float(self.wpercent)))
self.miniature = self.miniature.resize((self.basewidth,self.hsize))
print(self.miniature.size)
self.miniature = PIL.PhotoImage(self.miniature)
self.canvas = tk.Canvas(self,width=400, height=225)
self.image_on_canvas = self.canvas.create_image(0, 0, anchor="nw", image=self.miniature)
self.canvas.pack()
class SettingsPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
startp = StartPage(controller, SettingsPage)
###this line work ### arguments are correct
label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Edition Page",
command=lambda: controller.show_frame(StartPage))
button1.pack()
scale_x_logo_ = tk.Scale(self, orient='horizontal', from_=0, to=startp.background.size[0]/2,
resolution=10, length=350,
label='Placement horizontal logo', variable= startp.ecartementlogo)
scale_x_logo_.pack()
app = DotaCasterKit()
app.geometry("%dx%d%+d%+d" % (850,620,100,100))
app.mainloop()
This line used in the SettingsPage Class is ok : startp = StartPage(controller, SettingsPage)
I would say I need to write the reverse one but I having error with arguments (controller, StartPage) BUT this one setp = SettingsPage(controller, StartPage) don't work (in the init line of StartPage Class)
The issue is here:
class StartPage(tk.Frame):
# ... Omitting irrelevant stuff
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
setp = SettingsPage(controller, StartPage) # Right here
# ... More irrelevant stuff
You are passing the class StartPage to the SettingsPage, rather than an instance of that class. SettingsPage then tries to interpret the class as a widget, but a class is not a widget and does not have a tk attribute.
The best way to fix this is to remove that line, since you never use the variable setp. The corresponding line in SettingsPage.__init__ should also be removed.
You should also read the link suggested by #Brian Oakley. Your code currently has many issues. I have fixed one of them, and his link should help you fix more.
I have this code:
def show_hide(a, b, c, d):
if not b[0]["text"]: # check if text's length is 0
b[0].configure(text="{}".format(d[0]), bd=2, bg="white", command=lambda: activate_deactivate(a[0], c[0])) # configure button_1
c[0]["text"] = "Status: {}".format(a[0].get()) # set text for label_1
b[1].configure(text="{}".format(d[1]), bd=2, bg="white", command=lambda: activate_deactivate(a[1], c[1])) # configure button_2
c[1]["text"] = "Status: {}".format(a[1].get()) # set text for label_2
else:
b[0].configure(text="", bd=0, bg="#F0F0F0", command=None) # hide the button_1
c[0]["text"] = "" # hide the label_1
b[1].configure(text="", bd=0, bg="#F0F0F0", command=None) # hide the button_2
c[1]["text"] = "" # hide the label_2
My button which calls this function has this command value:
command=lambda: show_hide([status, status_2], [button, button_2], [label, label_2], ["Perform action #1", "Perform action #2"]))
By using it I can show/hide buttons but rewriting the same thing changing a digit in a few places would be tedious. To fix it I tried using this code instead of the original:
def show_hide(a, b, c, d):
for i in range(0, len(a)): # iterates over indexes of items in a,b,c,d lists (all 4 have same length) and passes them to the if/else statement
if not b[i]["text"]: # check if text length is 0
b[i].configure(text="{}".format(d[i]), bd=2, bg="white", command=lambda: activate_deactivate(a[i], c[i])) # configure buton
c[i]["text"] = "Status: {}".format(a[i].get()) # set label's text
else:
b[i].configure(text="", bd=0, bg="#F0F0F0", command=None) # hide button
c[i]["text"] = "" # hide label
Theoretically, this should work fine and using just 4 lines of code (not counting for/if/else lines) do the same thing as original function which would need 4 lines for EACH status+button+label created. BUT, as a matter of fact, it makes my 2 buttons work totally wrong.
I don't really understand what is wrong with it so I can't fully describe the problem, but you can see for yourself by using the test-script I made and replacing the show_hide function definition with the one using for-loop:
import tkinter as tk
import random
class ActionButton(tk.Button):
def __init__(self, *args, **kwargs):
tk.Button.__init__(self, *args, **kwargs)
self.configure(text="", font="courier 20", bd=0)
class ActionLabel(tk.Label):
def __init__(self, *args, **kwargs):
tk.Label.__init__(self, *args, **kwargs)
self.configure(text="", font="courier 14")
def multi(*args):
for func in args:
return func
def show_hide(a, b, c, d):
if not b[0]["text"]:
b[0].configure(text="{}".format(d[0]), bd=2, bg="white", command=lambda: activate_deactivate(a[0], c[0]))
c[0]["text"] = "Status: {}".format(a[0].get())
b[1].configure(text="{}".format(d[1]), bd=2, bg="white", command=lambda: activate_deactivate(a[1], c[1]))
c[1]["text"] = "Status: {}".format(a[1].get())
else:
b[0].configure(text="", bd=0, bg="#F0F0F0", command=None)
c[0]["text"] = ""
b[1].configure(text="", bd=0, bg="#F0F0F0", command=None)
c[1]["text"] = ""
def activate_deactivate(a, c):
if a.get() == "Can be done":
a.set("To be done")
c.configure(text="Status: {}".format(a.get()), fg="blue")
else:
a.set("Can be done")
c.configure(text="Status: {}".format(a.get()), fg="black")
def step_forward(a, b, c):
if a.get() == "To be done":
b.configure(text="", bd=0, bg="#F0F0F0", state="disabled")
c["text"] = ""
result = random.choice(["success", "failure"])
if result == "success":
a.set("Accomplished")
c["fg"] = "green"
else:
a.set("Failed")
c["fg"] = "red"
else:
b.configure(text="", bd=0, bg="#F0F0F0", command=None)
c["text"] = ""
root = tk.Tk()
status = tk.StringVar()
status.set("Can be done")
status_2 = tk.StringVar()
status_2.set("Can be done")
main = tk.Button(root, text="Show/Hide", bg="white", font="courier 30",
command=lambda: show_hide([status, status_2],
[button, button_2],
[label, label_2],
["Perform action #1", "Perform action #2"]))
main.pack()
frame = tk.Frame(root, pady=10)
frame.pack()
frame_1 = tk.Frame(frame, padx=10)
frame_1.pack(side="left")
frame_2 = tk.Frame(frame, padx=10)
frame_2.pack(side="left")
button = ActionButton(frame_1)
button.grid(row=0, column=0)
label = ActionLabel(frame_1)
label.grid(row=1, column=0)
button_2 = ActionButton(frame_2)
button_2.grid(row=0, column=1)
label_2 = ActionLabel(frame_2)
label_2.grid(row=1, column=1)
next_day = tk.Button(root, text="Next day", bg="white", font="courier 30",
command=lambda: multi(step_forward(status, button, label),
step_forward(status_2, button_2, label_2)))
next_day.pack()
root.mainloop()
I hope there's someone who may know how to fix this and maybe even has an idea about how the function could be changed to perform everything properly.