I've recently started learning how to use Tkinter. I'm working on the front-end for a small project to design an Enigma Machine, and a part of it involves creating a keyboard-like layout of buttons to simulate the plugboard. When clicking one button, I want it to go to a method I've created to handle all button presses and tell the method which letter the button corresponds to, and act accordingly. The main task right now is to make it so that pressing one button and then pressing another button after that draws a line between both buttons.
What I've added below is a scuffed version of the actual program to cut out repeated code like the button declarations.
from tkinter import *
from tkinter import ttk
from PIL import ImageTk, Image
class Steckerbrett:
root = Tk()
frm = ttk.Frame(root, padding=10)
frm.grid(row=0, column=0)
ttk.Label(frm, text="Enigma Machine").grid(column=0, row=0)
ttk.Label(frm, text="Steckerbrett").grid(column=0, row=1)
ttk.Label(frm, text="").grid(column=0, row=2)
# plugboard keys row 1
frm1 = ttk.Frame(root, padding=10)
frm1.grid(row=1, column=0)
ttk.Button(frm1, text="Q", command=connection).grid(column=0, row=0)
ttk.Button(frm1, text="W", command=connection).grid(column=1, row=0)
# i have declarations like this for all 26 letters
root.mainloop()
def connection(self, letter):
ttk.Label(self.frm, text=letter).grid(column=0, row=2)
sb = Steckerbrett()
The issue here is that since 'command' doesn't allow entering a function with arguments, I can't pass the letter to the function, and I can not make a custom method for every single button, because I would need to process first button presses and second button presses differently to make the connections. Is there a way to use a single function in the command but customize it to each button? Alternatively, if there isn't, are there any other solutions I can try? I am willing to try using other graphics packages as well.
Related
import tkinter
win=tkinter.Tk()
win.configure(background='grey')
k=False
def g():
k=True
v=tkinter.Button(win, text='click', command=g)
v.pack()
while k==True:
win.configure(background='black')
win.mainloop()
There's no reason why that while loop would run after the button is clicked, since (as you know) your program is run "from top to bottom", and control remains in win.mainloop() until the window is closed. (You can find that out by adding print("bye!") after that call.)
You might want to just directly call .configure(). (I gave the button some padding here so you can see the background change; otherwise the button may take up the entirety of the window and you won't see a change.)
import tkinter
win = tkinter.Tk()
def change_color():
win.configure(background='black')
button = tkinter.Button(win, text='click', command=change_color)
button.pack(padx=10, pady=10)
win.mainloop()
In a program I am working on there is a tkinter label/button which starts the card game (the program I am using) and a other window that has a string stating 'Welcome to the card game'.
Here is the tkinter section of the code:
import tkinter
window = tkinter.Tk()
print()
from tkinter import *
def quit():
global root
root.quit()
root = Tk()
while True:
label = tkinter.Label(window, text = "Welcome to the card game! (During name registration only use characters)").pack()
Button(root, text="Start Game", command=quit).pack()
root.mainloop()
However when I run the program they each appear in their own window screens when it would be more convenient for the user to have the options in one single window.
Is there anyway to merge them?
EDIT - (Having the button and text using root has fixed the problem.)
There is a lot going on here that should not be in such a small set of code.
Lets break it down.
First your imports. You are importing from tkinter multiple times. You only need to import once and you can use everything with the proper prefix. The preferred method is import tkinter as tk this way you don't overwrite any other imports or built in methods.
Next we need to get rid of one of your instances of Tk() as tkinter should only ever have one. For other windows use Toplevel().
In your quit function you do not need to define global as you are not assigning values here so the function will look in the global namespace for root.
Next Lets delete the empty print statement.
Next make sure both your label and button have the same container assigned to them. This is the reason why you are seeing them in different windows.
Next rename your function as quit is a built in method and should not be overwritten.
Lastly we remove the while statement as the mainloop() is already looping the Tk instance. You do not need to manage this yourself.
Here is what your code should look like (a 2nd window serves no purpose here):
import tkinter as tk
def root_quit():
root.quit()
root = tk.Tk()
tk.Label(root, text="Welcome to the card game! (During name registration only use characters)").pack()
tk.Button(root, text="Start Game", command=root_quit).pack()
root.mainloop()
Here is an example using Toplevel just so you can get an idea of how it is used.
import tkinter as tk
def root_quit():
root.quit()
def game_window():
top = tk.Toplevel(root)
tk.Button(top, text='exit', command=root_quit).pack()
root = tk.Tk()
tk.Label(root, text="Welcome to the card game! (During name registration only use characters)").pack()
tk.Button(root, text="Start Game", command=game_window).pack()
root.mainloop()
I am making a calculator using tkinter and I wish to do the following:
Disable keyboard input for the Entry widget so that the user can only input through the buttons.
Even after disabling keyboard input for the Entry widget, I wish to be able to change the background and foreground of the widget.
I wish to hide the console window because it is totally useless in the use of this calculator.
I don't want to let the user resize the root window. How do I disallow the resizing of the root window?
Here is my code so far...
from tkinter import *
root = Tk()
root.title("Calculator")
root.config(background="black")
operator = ""
textVar = StringVar()
def valInput(number):
global operator
operator+=str(number)
textVar.set(operator)
display = Entry(root, textvariable=textVar, font=("Arial", 14, "bold"), bg="lightblue", fg="black", justify="right")
display.grid(row=0, column=0, columnspan=4)
btn7 = Button(root, font=("Arial", 12, "bold"), bg="orange", fg="red", text="7", command= lambda : valInput(7))
btn7.grid(row=1, column=0)
"""
And more buttons...
"""
root.mainloop()
As you can see, I can input into the Entry widget using buttons but later on, after the calculator is complete, if the user inputs characters like abcd... it will cause problems and show errors. How do I disallow keyboard entry so that I can avoid these errors?
I want to make my calculator a bit colorful. I changed the color of the root window, the buttons and also the color of the Entry widget. Is there any way to change the color of the widget even after it is disabled?
I don't need the console window while using this calculator. How do I hide it?
If I resize the root window, the calculator becomes ugly, besides, resizing the window isn't necessary. So how do I prevent the user from resizing the window?
To be able to disable keyboard input in Entry(args)
Set the state to disabled:
display = Entry(root, state=DISABLED)
To be able to disable the feature of resizing the tkinter window (so that you can't drag and stretch it.
root.resizable(0,0)
To be able to make the command prompt window disappear. (I just want the tkinter window.
Rename the file with a .pyw extension (assuming you are using windows)
Don't use from tkinter import * it's really not recommended because it pollutes the main namespace with every public name in the module. At best this makes code less explicit, at worst, it can (and it will) cause name collisions.
Have the right reflexes, use import tkinter or import tkinter as tk instead
this should work, you have to use the disabledbackground option :
import tkinter as tk
root = tk.Tk()
display = tk.Entry(root,font=('Arial', 20, 'bold'), disabledbackground='lightblue', state='disabled')
display.pack()
root.resizable(0,0)
root.mainloop()
Friends, I'm trying to make hover effect in python(3.5.2) on a button, I use the below code.
from tkinter import *
root = Tk()
root.geometry("200x200+400+400")
myBtn = Button(root, text="TEST")
myBtn.pack(padx=10, pady=10)
myBtn.bind("<Enter>", lambda event, h=myBtn: h.configure(bg="red"))
myBtn.bind("<Leave>", lambda event, h=myBtn: h.configure(bg="yellow"))
root.mainloop()
It works strange.
when I hover mouse over the button, first time nothing happens
and when I leave, it gives yellow (satisfying the second event binded).
I couldn't find where I made mistake. could some one point me out
In your <Enter> binding you just need to set activebackground instead of the plain background (bg is a synonym of background).
import tkinter as tk
root = tk.Tk()
root.geometry("200x200+400+400")
myBtn = tk.Button(root, text="TEST")
myBtn.pack(padx=10, pady=10)
myBtn.bind("<Enter>", lambda event, h=myBtn: h.configure(activebackground="red"))
myBtn.bind("<Leave>", lambda event, h=myBtn: h.configure(bg="yellow"))
root.mainloop()
I changed the import statement from the messy "star" import, which imports 135 Tkinter names into your namespace. This requires a little more typing, but it makes the code easier to read & maintain because it makes it obvious which names come from Tkinter.
I want to keep a menu cascade open, after a command button within the cascade is clicked. So it basically only closes when the user clicks anywhere else (like it would normally too). Can't seem to find a proper option or a method to open said menu in the callback. The invoke() function only works on buttons wihtin the cascade right? How would you go about that?
Yes, I know this was asked a long time ago, but I was curious if there was any way to accomplish this with tkinter, so I fiddled about for a while and figured out how to do it. I was unable to come up with a way to properly place the persistent menu where it was when it originally opened, but I have managed to make it persist in any location you request (I use upper-left corner of root window). And yes, I know this isn't a nice proper class based implementation, but I was just going for as simple a test as I could write without obscuring it with too many extraneous details.
try:
from tkinter import *
from tkinter.ttk import *
except:
from Tkinter import *
from ttk import *
root = Tk()
var = StringVar()
def menu_click(menu, item):
global root
var.set(item)
menu.post(root.winfo_rootx(), root.winfo_rooty())
root.option_add('*tearOff', False) # remove tearoff from all menus
Label(root, textvariable=var).pack() # just to give menu clicks some feedback
root.geometry('400x300')
menubar = Menu(root)
root['menu'] = menubar
menu_test = Menu(menubar)
menubar.add_cascade(menu=menu_test, label='Test')
menu_test.add_command(label='One', command=lambda: menu_click(menu_test, 'One'))
menu_test.add_command(label='Two', command=lambda: menu_click(menu_test, 'Two'))
menu_test.add_command(label='Three', command=lambda: menu_click(menu_test, 'Three'))
menu_cas = Menu(menu_test)
menu_test.add_cascade(menu=menu_cas, label='Four')
menu_cas.add_command(label='One', command=lambda: menu_click(menu_cas, 'Fourty One'))
menu_cas.add_command(label='Two', command=lambda: menu_click(menu_cas, 'Fourty Two'))
menu_cas.add_command(label='Three', command=lambda: menu_click(menu_cas, 'Fourty Three'))
root.mainloop()