How to remove an entire row in Python Tkinter? - python

I'm using the grid technique in Tkinter, Python. The code below runs when the user presses a button. But I can't get it to remove the widgets it created (all of which are on row 5) when the user clicks the button again.
Here is the procedure:
def create_equation(self):
R = int(self.entReactants.get())
for i in range(R):
entRCompound = Entry(self.kcFrame, width=10)
entRCompound.grid(row=5,column=i*2)
entRCompound.config(bg="SpringGreen")
if i == R-1:
lblReverse = GenLabel(self.kcFrame,text=" ⇌ ")
lblReverse.grid(row=5, column=i*2+1)
lblReverse.config(bg="aqua")
else:
lblPlus = GenLabel(self.kcFrame,text=" + ")
lblPlus.grid(row=5, column=i*2+1)
lblPlus.config(bg="aqua")
for i in range(int(self.entProducts.get())):
entPCompound = Entry(self.kcFrame, width=10)
entPCompound.grid(row=5,column=R*2+i*2)
entPCompound.config(bg="orange")
if i != int(self.entProducts.get())-1:
lblPlus = GenLabel(self.kcFrame,text=" + ")
lblPlus.grid(row=5, column=R*2+i*2+1)
lblPlus.config(bg="aqua")
When the button is pressed, I want it to delete the widgets it created when the button was last pressed and create the new set of widgets. This code is executed for the first press of the button as well, so there would not be anything on row 5 before it is pressed.
Any ideas?

Related

How do I enable right click in entry and output widget for pasting and copying respectively in Tkinter?

I'm still working on the translator app, with the help of Python Dictionary. But I have this challenge: I want to be able to right click in the entry widget and paste keys as well as right click in the output widget and copy values. I'm only able to do so with keyboard shortcut; for convenience sake, I want to be able to do so with the mouse. Thanks. Below is the code:
from tkinter import *
import tkinter. messagebox
root=Tk()
root.geometry('250x250')
root.title("Meta' Translator")
root.configure(background="#35424a")
from playsound import playsound
#Entry widget object
textin = StringVar()
#press ENTER key to activate translate button
def returnPressed(event):
clk()
def clk():
entered = ent.get().lower() #get user input and convert to lowercase
output.delete(0.0,END)
if len(entered) > 0:
try:
textin = exlist[entered]
except:
textin = 'Word not found'
output.insert(0.0,textin)
def play():
text = output.get("0.0", "end").strip("\n")
if text == "əsɔ́":
playsound("hoe.mp3")
elif text == "jam":
playsound("axe.mp3")
elif text == "ɨghə́":
playsound("eye.mp3")
else:
# If there is no sound file for the translation:
playsound("eze.mp3")
#heading
lab0=Label(root,text='Translate English Words to Meta\'',bg="#35424a",fg="silver",font=
('none 11 bold'))
lab0.place(x=0,y=2)
#Entry field
ent=Entry(root,width=15,font=('Times 18'),textvar=textin,bg='white')
ent.place(x=30,y=30)
#focus on entry widget
ent.focus()
#Search button
but=Button(root,padx=1,pady=1,text='Translate',command=clk,bg='powder blue',font=('none 18
bold'))
but.place(x=60,y=90)
#press ENTER key to activate Translate button
root.bind('<Return>', returnPressed)
#output field
output=Text(root,width=15,height=1,font=('Times 18'),fg="black")
output.place(x=30,y=170)
#play button
play_button=Button(root,padx=1,pady=1,text='Play',command=play,bg='powder blue',font=('none
10 bold'))
play_button.place(x=100,y=210)
#prevent sizing of window
root.resizable(False,False)
#Dictionary
exlist={
"hat":"ɨ̀də̀m",
"hoe":"əsɔ́",
"honey":"jú",
"chest":"ɨgɔ̂",
"eye":"ɨghə́",
"ear":"ǝ̀tǒŋ",
"axe":"jam"
}
root.mainloop()
Since your code has a lot of dependency, it cannot be run on another system, so here is a common example which you should be able to implement to your code easily:
from tkinter import *
root = Tk()
def popup(event):
try:
menu.tk_popup(event.x_root,event.y_root) # Pop the menu up in the given coordinates
finally:
menu.grab_release() # Release it once an option is selected
def paste():
clipboard = root.clipboard_get() # Get the copied item from system clipboard
e.insert('end',clipboard) # Insert the item into the entry widget
def copy():
inp = e.get() # Get the text inside entry widget
root.clipboard_clear() # Clear the tkinter clipboard
root.clipboard_append(inp) # Append to system clipboard
menu = Menu(root,tearoff=0) # Create a menu
menu.add_command(label='Copy',command=copy) # Create labels and commands
menu.add_command(label='Paste',command=paste)
e = Entry(root) # Create an entry
e.pack(padx=10,pady=10)
e.bind('<Button-3>',popup) # Bind a func to right click
root.mainloop()
I have explained it with comments to understand on-the-go, nothing complicated. The menu just pops up when you right click on the entry as the function is binded to the entry alone. I think, without clipboard_clear() it would append all the items in the tkinter clipboard to the system clipboard.

Waiting inside a for loop for button click in Tkinter

I am trying create a for loop based on the decimal value of the option menu button.
If the option button chosen is 2, then the for loop circulates for two times.
But the for loop needs to wait until the button created inside the for loop gets clicked.
Below code shows the same.
Option menu has range of 1 to 10 i.e. user can choose 1 to 10 device to be created.
The moment the option menu is choose, the command button for the option menu gets operated i.e. comp_gt_data.
This basically circulates in a for loop to take the configuration of the N number of devices.
Once after taking the configuration for the 1st device, I create a "Enter the Next Device Configuration" button inside the for loop and wanted the button to be clicked to take the next device configuraiton.
But the "Enter Next Device ...." button is not getting created and keeps circulating in while loop.
I tried using time.sleep(60) but still didn't work.
The "Enter Next Device.." basically tries to clear the old device configuration and tries to set cvadt_val_loop_exit=0 so that the while loop can exit.
QUERY: HOW TO Basically wait inside a for loop for a button to be clicked to proceed further?
self.cvo1 = OptionMenu(root, cvagt_val, *cvagt_list, command=self.comp_gt_data)
self.cvo1.pack()
def comp_gt_data(self, *args):
if cvagt_val.get()!=0:
cvagt_val_loop = cvagt_val.get()
for cvagt_loop in range(1, cvagt_val_loop+1):
self.cvad_l0 = Label(root, text="Enter the %d Device Config"% cvagt_loop)
self.cvad_l0.pack()
self.cvad_l1 = Label(root, text="How Many D~S You Want To Create: ")
self.cvad_l1.pack()
self.cvad_e1 = Entry(root)
self.cvad_e1.pack()
self.cvad_l2 = Label(root, text="How Many MS You Want To Create: ")
self.cvad_l2.pack()
self.cvad_e2 = Entry(root)
self.cvad_e2.pack()
self.cvad_l3 = Label(root, text="How Many SD You Want To Create: ")
self.cvad_l3.pack()
self.cvad_e3 = Entry(root)
self.cvad_e3.pack()
if cvagt_loop < cvagt_val_loop:
if not (self.cv_next_dev_cfg_button):
self.cv_next_dev_cfg_button = Button(root, text="Enter Next Device Configuration", command=self.cv_next_dev_cfg_button_code)
self.cv_next_dev_cfg_button.pack()
# Waiting For cvadt_val_loop_exit bit to get set when the cv_next_dev_cfg_button is clicked but its not happening and circulating in the while loop
cvadt_val_loop_exit = 1
while(cvadt_val_loop_exit):
print "Waiting For Next Device Config Button To Be Hit"
time.sleep(60)
else:
self.cv_next_dev_cfg_button_code()
if not (self.return_button):
self.return_button = Button(root, text="Return To Main Menu", command=self.return_button_code)
self.return_button.pack()
else:
print "Choose Atleast One Device To Create"
def cv_next_dev_cfg_button_code(self):
if (self.cvad_l0):
self.cvad_l0.pack_forget(); self.cvad_l0.destroy(); self.cvad_l0 = None
if (self.cvad_l1):
self.cvad_l1.pack_forget(); self.cvad_l1.destroy(); self.cvad_l1 = None
if (self.cvad_l2):
self.cvad_l2.pack_forget(); self.cvad_l2.destroy(); self.cvad_l2 = None
if (self.cvad_l3):
self.cvad_l3.pack_forget(); self.cvad_l3.destroy(); self.cvad_l3 = None
if (self.cvad_e1):
self.cvad_e1.pack_forget(); self.cvad_e1.destroy(); self.cvad_e1 = None
if (self.cvad_e2):
self.cvad_e2.pack_forget(); self.cvad_e2.destroy(); self.cvad_e2 = None
if (self.cvad_e3):
self.cvad_e3.pack_forget(); self.cvad_e3.destroy(); self.cvad_e3 = None
if (self.cv_next_agt_cfg_button):
self.cv_next_agt_cfg_button.pack_forget(); self.cv_next_agt_cfg_button.destroy(); self.cv_next_agt_cfg_button = None
cvadt_val_loop_exit = 0
A for() is not the answer here. Instead call a function the correct number of times and wait however long you want in between. This should give you enough to get started.
try:
import Tkinter as tk ## Python 2.x
except ImportError:
import tkinter as tk ## Python 3.x
class ButtonForLoop():
def __init__(self, top):
self.top=top
self.but_1=tk.Button(top, text="Start for()", command=self.start_for)
self.but_1.grid(row=0, column=0)
self.num_for=0
## for simplicity, assume 3 iterations through the loop
self.max_num=3
def next_pass(self):
## create button again
self.but_1=tk.Button(top, text="Pass # %d" % (self.num_for),
command=self.start_for)
self.but_1.grid(row=0, column=0)
def start_for(self):
""" button clicked so call next_pass() the correct
number of iterations
"""
## destroy whatever button is showing
self.but_1.destroy()
if self.num_for < self.max_num:
self.num_for += 1
self.top.after(500, self.next_pass)
else:
self.top.quit()
top=tk.Tk()
BF=ButtonForLoop(top)
top. mainloop()

Replacing Buttons linked to input boxes with checkboxes

This is my current code for selecting different options and have them appearing in the box (Minecraft ArmorStand Generator).
from tkinter import *
default = "/summon ArmorStand ~ ~ ~ {CustomNameVisible:1}"
NoAI = ",NoAI:1"
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
root = Tk()
def addNOAI():
inputbox.insert(45, NoAI)
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
Button(text="Add NoAI",command=addNOAI,relief = FLAT, bg = "#eF651A", fg = "white", width= 25, height = 2).place(x=10,y=123)
root.title("WIP")
root.wm_state('zoomed')
root.mainloop()
What I'd like to to do is replace the buttons with tick boxes, to prevent the buttons being pressed multiple times. If they click the button, add the text, if they untick, remove it.. I'm not sure where to start with this so any hint in the right direction would be nice.
I've got a working solution, you can try it below.
from tkinter import *
default = "/summon ArmorStand ~ ~ ~ {CustomNameVisible:1}"
NoAI = ",NoAI:1"
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
root = Tk()
def addNOAI():
state = var.get()
if state == 1: #if the state is checked
inputbox.insert(45, NoAI) #then add the text
else: #if the state is not check
inputbox.delete(0, 7) #delete the text
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
var = IntVar() #sets up variable for check button
c = Checkbutton(text="Add NoAI", command=addNOAI, variable=var) #defining check button variable and command
c.place(x=10,y=123)
root.title("WIP")
root.wm_state('zoomed')
root.mainloop()
The only problem is at the moment, you are deleting everything in the entry box (more accurately from position 0, to position 7). I assume that there will be multiple check buttons, all adding their own strings to the entry box.
As a solution, I would suggest extracting everything from the entry box, finding the string you want, taking it out, and putting everything back in again. Here's an example.
def addNOAI():
state = var.get()
if state == 1: #if the state is checked
inputbox.insert(45, NoAI) #then add the text
else: #if the state is not check
contents = inputbox.get() #gets all of contents
position = contents.find(NoAI) #finds the first position of desired string to remove
newcontents = contents[:position]+contents[position+7:] #gets string before the word, and after the word, and joins them
inputbox.delete(0, 'end') #clears input box for new entry
inputbox.insert(45, newcontents) #re-inserts the string
Here, when the user unchecks the box, the program finds the starting position of the string within the contents of the inputbox. Because you know how long the string will be (in this case 7), you can remove the string from the current contents of the inputbox, and place this inside a new variable. Now you have a new string, without the one that was unchecked, you can clear the inputbox, and put the new one in.
Hope this helps!

Trying to produce function for a custom number of tkinter Entry fields.

I've been trying to create a piece of code that would take a integer as a argument and create that number of tkinter entry fields. With a submit button at the end that would retrieve the data from the fields add these data to a list then close the window.
I have been able to get it working however I cant find a way to convert this to a callable function; a requirement to use it with the rest of my program.
This is the code I have produced so far, thanks:
import tkinter as tk
b = input("Enter: ")
b = int(b)
root = tk.Tk()
newdict = dict()
outputs = list()
for i in range(b):
newdict["entry" + str(i)] = tk.Entry(root)
newdict["entry" + str(i)].pack()
button1 = tk.Button(root, text="Submit", command=lambda: Get(newdict))
button1.pack()
def Get(newdict):
for j in range(b):
outputs.append(newdict["entry" + str(j)].get())
root.quit()
root.mainloop()
print(outputs)
The basic idea is to create a window, then use the wait_window method to wait for the window to be destroyed. Once it has been destroyed you can return some value.
The problem is that the values you want to fetch must not be attributes of the window, since it will have been destroyed by the time you are ready to fetch them. You need to set up your code to save the values before the window is destroyed.
A simple way is to provide an "OK" button which gets the values and then destroys the window. Another way would be to put a trace on variables associated with each entry, and save the values immediately as they are edited.
Which method you choose depends on what behavior you want when the user clicks the window control to close the window (eg: the red circle on OSX, the [x] button on windows, etc). Do you want to return what they had input, or do you treat that as a cancel action and return nothing?
Here's a simple example using an OK button. This example assumes that you aren't already running a GUI, and that this is to be run as part of a non-GUI application.
import tkinter as tk
class Dialog(object):
def show(self, num_fields):
self.num_fields = num_fields
self.root = tk.Tk()
self.entries = []
for i in range(num_fields):
entry = tk.Entry(self.root)
entry.pack(fill="x")
self.entries.append(entry)
ok = tk.Button(self.root, text="OK", command=self.ok)
ok.pack(side="bottom", anchor="e", pady=(10,0), padx=10)
# wait for the window to be destroyed, then
# return the values. If the user clicks the OK button
# the values will be set; if they cancel the dialog
# this will return None.
self.values = None
self.root.wait_window()
return self.values
def ok(self):
# save all the values, then destroy the window
self.values = []
for i in range(self.num_fields):
self.values.append(self.entries[i].get())
self.root.destroy()
Assuming you're running a non-gui program, here's an example of how you would use this class:
b = input("Enter: ")
b = int(b)
result = Dialog().show(b)
print("result:", result)

How do I get my program to update the increment in the GUI window? (python 3)

So I'm trying to create a program in a GUI window that tells the user to press the button and displays the amount of time the button has been pressed. Here's what the window looks like:
The problem is that clicking the button is not affecting the number of presses, thus it always remains at 0.
Here's my code so far:
import tkinter
presses = 0
canHitEnter = True
def updateButtonPress(): #?????????????????????
global presses
presses = presses + 1
if going():
pressesLabel.after(500, updateButtonPress)
def updateDisplay():
global presses
pressesLabel.config(text = 'PRESSES: ' + str(presses))
empty.after(100, updateDisplay)
def going():
global presses
return True
def start(event):
global canHitEnter
if canHitEnter == False:
pass
else:
updateButtonPress()
canHitEnter = False
gui = tkinter.Tk()
gui.title('Press the Button')
gui.geometry('500x400')
startLabel = tkinter.Label(gui, text = 'press enter to start the game', font = 16)
startLabel.pack()
pressesLabel = tkinter.Label(gui, text = 'presses: ' + str(presses), font = 14)
pressesLabel.pack()
buttonLabel = tkinter.Button(gui, text = 'press', command = updateButtonPress)
buttonLabel.pack()
empty = tkinter.Label(gui, text = '_')
empty.pack()
gui.bind('<Return>', start)
gui.mainloop()
I don't understand why it's ignoring the presses = presses + 1 part in updatebuttonPress(), what exactly am I doing wrong?
You aren't ever calling your updateDisplay function to set the label to have the new value of the presses variable. Just call updateDisplay within your updateButtonPress function.

Categories

Resources