Python Tk Menu Delete command , Menu.reDraw()? - python-3.x

I have a TK Menu widget, and would like to remove the command when clicked on, after doing some side effect stuff. All the code is in a DocumentView class, hence the self.varnames
self.root= Tk()
self.root.title("Document Screen")
self.root.geometry(str(DocHeight)+"x"+str(DocHeight))
self.mainMenu = Menu(self.root)
self.allMembersMenu = Menu(self.updateMembersMenu)
for member in self.allUsers:
self.allMembersMenu.add_command(label=member,command=lambda i= member:
self.removeUser(i))
This makes a command for every user in the system. Remove user is defined as follows
def removeUser(self,uname):
print("Remove User Function , uname: {}".format(uname))
x=0
mem = self.currentDoc.getMembers()
delmem=""
for i in range(0,len(mem)):
if (mem[i]==uname):
x=i
delmem = mem[i]
break
self.allMembersMenu.delete(x)
self.allUserMenu.destroy
self.currentDoc.removeMember(delmem)
Using the print function I know the correct variables are being passed, but no change is occurring in the Menu, is there some sort of Menu.reDraw() method I'm missing?
If that doesn't exist is there a way to destroy this submenu and redraw it?

import tkinter as tk
root = tk.Tk()
root.geometry("400x600")
root.option_add("*tearOff", False)
menuBar = tk.Menu(root)
root.config(menu=menuBar)
usersMenu = tk.Menu(menuBar)
delUserMenu = tk.Menu(menuBar)
menuBar.add_cascade(menu=usersMenu, label="Users")
usersMenu.add_cascade(menu=delUserMenu, label="Delete")
usernamelist = ["Ali", "Ahmed", "Muhamed"]
def del_user(username):
delUserMenu.delete(delUserMenu.index(username))
print(username, "deleted!")
def command(username): return lambda :del_user(username)
for username in usernamelist:
delUserMenu.add_command(label=username, command=command(username))
root.mainloop()

Related

populating one combobox based on another combo box using tkinter python

from tkinter import *
from tkinter.ttk import Combobox
v1=[]
root = Tk()
root.geometry('500x500')
frame1=Frame(root,bg='#80c1ff',bd=5)
frame1.place(relx=0.5,rely=0.1,relwidth=0.75,relheight=0.1,anchor='n')
lower_frame=Frame(root,bg='#80c1ff',bd=10)
lower_frame.place(relx=0.5,rely=0.25,relwidth=0.75,relheight=0.6,anchor='n')
v=[]
def maincombo():
Types=["MA","MM","MI","SYS","IN"]
combo1=Combobox(frame1,values=Types)
combo1.place(relx=0.05,rely=0.25)
combo2=Combobox(frame1,values=v)
combo2.bind('<<ComboboxSelected>>', combofill)
combo2.place(relx=0.45,rely=0.25)
def combofill():
if combo1.get()=="MA":
v=[1,2,3,45]
combo2=Combobox(frame1,values=v)
combo2.place(relx=0.45,rely=0.25)
if combo1.get()=="MM":
v=[5,6,7,8,9]
combo2=Combobox(frame1,values=v)
combo2.place(relx=0.45,rely=0.25)
maincombo()
root.mainloop()
I want to populate the one combobox based on selection of other combobox I,e types.But failed to do so with simple functions.
Looking at you code, most of what you need is already there. The changes I have made are as follows:
Bound to combo1 rather than combo2 (as combo1 is the one you want to monitor)
Set combo1 and combo2 as global variables (so they can be used in the combofill method)
Set the combofill method to accept the event arg (it would raise a TypeError otherwise)
Use the .config method on combo2 rather than creating a new one each time
Set combo2 to be empty when neither "MA" or "MM" are selected
Here is my implementation of that:
from tkinter import *
from tkinter.ttk import Combobox
v1=[]
root = Tk()
root.geometry('500x500')
frame1=Frame(root,bg='#80c1ff',bd=5)
frame1.place(relx=0.5,rely=0.1,relwidth=0.75,relheight=0.1,anchor='n')
lower_frame=Frame(root,bg='#80c1ff',bd=10)
lower_frame.place(relx=0.5,rely=0.25,relwidth=0.75,relheight=0.6,anchor='n')
v=[]
def maincombo():
global combo1, combo2
Types=["MA","MM","MI","SYS","IN"]
combo1=Combobox(frame1,values=Types)
combo1.place(relx=0.05,rely=0.25)
combo1.bind('<<ComboboxSelected>>', combofill)
combo2=Combobox(frame1,values=v)
combo2.place(relx=0.45,rely=0.25)
def combofill(event):
if combo1.get()=="MA":
v=[1,2,3,45]
elif combo1.get()=="MM":
v=[5,6,7,8,9]
else:
v=[]
combo2.config(values=v)
maincombo()
root.mainloop()
A couple other ideas for potential future consideration:
I would recommend using the grid manager rather than the place manager as it will stop widgets overlapping, etc. (on my system, combo2 slightly covers combo1)
Use a dictionary rather than if ... v=... elif ... v= ... and then use the get method so you can give the default argument. For example:
v={"MA": [1,2,3,45],
"MM": [5,6,7,8,9]}. \
get(combo1.get(), [])
EDIT:
Responding to the question in the comments, the following is my implementation of how to make a "toggle combobox" using comma-separated values as requested.
As the combobox has already overwritten the value of the text area when our <<ComboboxSelected>> binding is called, I had to add a text variable trace so we could keep track of the previous value of the text area (and therefore append the new value, etc.). I am pretty sure that explanation is completely inadequate so: if in doubt, look at the code!
from tkinter import *
from tkinter.ttk import Combobox
root = Tk()
def log_last():
global last, cur
last = cur
cur = tx.get()
def append_tx(event):
if last:
v = last.split(",")
else:
v = []
v = list(filter(None, v))
if cur in v:
v.remove(cur)
else:
v.append(cur)
tx.set(",".join(v))
combo.selection_clear()
combo.icursor("end")
last, cur = "", ""
tx = StringVar()
combo = Combobox(root, textvariable=tx, values=list(range(10)))
combo.pack()
combo.bind("<<ComboboxSelected>>", append_tx)
tx.trace("w", lambda a, b, c: log_last())
root.mainloop()

Why is it saying a isnt defined even though i have defined you

so i defined a but when i try to type out a using keybaord.type it just says that it isnt defined
i tried making a global that didnt work i tried moving postions of the code and that didnt work ive tried many other things they didnt work either
from tkinter import *
import webbrowser
from pynput.keyboard import Key, Controller
import time
menu = Tk()
menu.geometry('200x300')
def webop(): # new window definition
global a
def hh():
a = "" + txt.get()
while True:
keyboard = Controller()
time.sleep(1)
keyboard.type(a)
keyboard.press(Key.enter)
keyboard.release(Key.enter)
sp = Toplevel(menu)
sp.title("Spammer")
txt = Entry(sp, width=10)
txt.grid(row=1,column=1)
btn = Button(sp, text='spam', command=hh)
btn.grid(row=1,column=2)
def enc():
window = Toplevel(menu)
window.title("nou")
button1 =Button(menu, text ="Spammer", command =webop) #command linked
button2 = Button(menu, text="Fake bot", command = enc)
button1.grid(row=1,column=2)
button2.grid(row=2,column=2)
menu.mainloop()
global a underneath def webop() gives webop access to the variable a in the enclosing scope (the scope up there where you're doing your imports). Since you haven't defined a in that scope, you're getting the error.
Either way, you generally should avoid using globals like that and pass data to functions using arguments. In order to pass arguments into your Button command you can use a closure.
You should move the part of your code accessing a to the part where that value is set
It is unclear what you're trying to achieve here since when you run webop your program will reach while True and continuously loop there and never reach the code below your while loop
For instance
def hh(a):
a = "" + txt.get()
while True:
keyboard = Controller()
time.sleep(1)
keyboard.type(a)
keyboard.press(Key.enter)
keyboard.release(Key.enter)
btn = Button(sp, text='spam', command=hh)
An alternative approach achieves the same thing using functools partial. See https://www.delftstack.com/howto/python-tkinter/how-to-pass-arguments-to-tkinter-button-command/

Is there a way to change a buttons colour when the button is made in a function?

I have made a button within a function and when the button is clicked a command is run to change the button color.
However this does not work as I get an error, but I need to create the button in the function.
It works when the button is defined outside the function and I assume the issue is that the data is forgotten after a function ends.
from tkinter import *
root = Tk()
def ColourChange(Letter):
NameButton.config(bg = "red")
def Change():
Letter = "a"
NameButton=Button(root, text = "This", command = lambda Letter = Letter:
ColourChange(Letter)
NameButton.pack()
Change()
When I click the button I would like the color of the background to change.
The actual error is
NameButton.config(bg="red") NameError: name 'NameButton' is not defined"
Set your global variable so it can be access by other function.Also move NameButton.pack() to new line after NameButton=Button(root,text="This",command=lambda Letter=Letter: ColourChange(Letter)).
from tkinter import *
root=Tk()
def ColourChange(Letter):
NameButton.config(bg="red")
def Change():
global NameButton # global variable
Letter="a"
NameButton=Button(root,text="This",command=lambda Letter=Letter: ColourChange(Letter))
NameButton.pack()
#NameButton.pack()
Change()

Tkinter: displaying label with automatically updated variable

I know there are a lot of questions about this subject, but after long researches I didn't find any that could solve my problem.
I'm trying to display with a label (using tkinter) a variable that I get from the I²C bus. The variable is therefore updated very regularly and automatically. The rest of the window should stay available for the user.
For now, the only way I found to display the label with the updated variable and to keep the rest of the window available for the user is to do so:
window = tk.Tk()
window.title("Gestionnaire de périphériques")
window.minsize(1024,600)
labelValThermo = tk.Label(a_frame_in_the_main_window,text = "")
labelValThermo.grid(row = 1, column = 1)
while True:
if mcp.get_hot_junction_temperature() != 16.0625:
labelValThermo.configure(text = "Température thermocouple: {} °C".format(mcp.get_hot_junction_temperature()))
window.update()
time.sleep(0.75)
The variable that comes from the I²C and got updated is mcp.get_hot_junction_temperature
The fact is that I know it's not the best way to force the update in an infinite loop. This should be the role of the mainloop(). I found out that the after() method could solve my problem but I don't know how to run it. I tried the following code that didn't work:
def displayThermoTemp():
if mcp.get_hot_junction_temperature() != 16.0625:
labelValThermo.configure(text = "Température thermocouple: {} °C".format(mcp.get_hot_junction_temperature()))
labelValThermo.after(500,displayThermoTemp)
window = tk.Tk()
labelValThermo = tk.Label(thermoGraphFrame,text = "")
labelValThermo.after(500, displayThermoTemp)
labelValThermo.grid(row = 1, column = 1)
window.mainloop()
Does anyone have the right syntax ?
How To use after() ?
The after() calls the function callback after the given delay in ms. Just define it inside the given function and it'll run just like a while loop till you call after_cancel(id) .
Here is an example:
import tkinter as tk
Count = 1
root = tk.Tk()
label = tk.Label(root, text=Count, font = ('', 30))
label.pack()
def update():
global Count
Count += 1
label['text'] = Count
root.after(100, update)
update()
root.mainloop()
Update your function with this and call it once before mainloop().
def displayThermoTemp():
if mcp.get_hot_junction_temperature() != 16.0625:
labelValThermo.configure(text = "Température thermocouple: {} °C".format(mcp.get_hot_junction_temperature()))
labelValThermo.after(500,displayThermoTemp)
# 100ms = 0.1 secs
window(100, displayThermoTemp)
after has the following syntax:
after(delay_ms, callback=None, *args)
You need to place the command in your callback function and update the window on which the widget is (in your case the labelValThermo looks to be on the root window:
def displayThermoTemp():
if mcp.get_hot_junction_temperature() != 16.0625:
labelValThermo.configure(text = "Température thermocouple: {} °C".format(mcp.get_hot_junction_temperature()))
root.after(500,displayThermoTemp)

Display objects attributes in tkinter label

I would like a label that updates when I push a button. The label is a formated string that prints some attributes of an object.
This is what I tried. It displays properly but won't update.
from class_mnoply import *
from tkinter import *
Player1=Player("Hat")
message=str('''
____________________
{0}
Bank account: ${1}
Dice1: {2}
Dice2: {3}
____________________
'''.format(Player1.name, Player1.bank, Player1.dice1, Player1.dice2))
mainWin = Tk()
topFrame=Frame(mainWin)
topFrame.pack(side=TOP)
button1 Button(mainWin,text="ThrowDice",fg="red",command=Player1.rollDice())
button1.pack(side=BOTTOM)
plLabel = Label(mainWin, textvariable=message)
plLabel.pack()
mainWin.mainloop()
You have 1 typo in the following statement and 1 potential error:
button1 Button(mainWin,text="ThrowDice",fg="red",command=Player1.rollDice())
Can you guess what is the typo? If no, you are just missing the = sign.
On the other hand, you are assigning the return value of Player1.rollDice() to command, but this is not what you want. What you want is just set the Player1.rollDice method as the command that is called when button1 is pressed. Here's the correct syntax (note the absence at the end of ()):
button1 = Button(mainWin,text="ThrowDice",fg="red",command=Player1.rollDice)
Then, where is message defined in the following statement:
plLabel = Label(mainWin, textvariable=message)
There's no need to use a StringVar object, but if you want, you have first to declare it:
message = StringVar()
and finally you can use it as textvariable for your label.
Assuming you don't know what lambda is, this is a working example of what you are trying to do (without using a StringVar variable):
from tkinter import *
def set_label_text():
plLabel.config(text='Hello World!')
mainWin = Tk()
topFrame=Frame(mainWin)
topFrame.pack(side=TOP)
button1 =Button(mainWin,text="ThrowDice",fg="red",
command=set_label_text) # note the absence of ()
button1.pack(side=BOTTOM)
plLabel = Label(mainWin)
plLabel.pack()
mainWin.mainloop()

Resources