Tkinter - Entry box - formatted for date - python

I would like to have an entry box for typing in dates so that the user can only type it one way.
The entry box before typing would look like this (without the underscores) __/__/____, and the /'s are not deleted when the user types the date.
Thanks

Thanks to #Bryan Oakley's major hint, I have come up with this working code.
(Update: threw a few years of programming experience and some recent readers' sharp eyes at improving the code.)
from __future__ import print_function
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
class DateEntry(tk.Frame):
def __init__(self, master, frame_look={}, **look):
args = dict(relief=tk.SUNKEN, border=1)
args.update(frame_look)
tk.Frame.__init__(self, master, **args)
args = {'relief': tk.FLAT}
args.update(look)
self.entry_1 = tk.Entry(self, width=2, **args)
self.label_1 = tk.Label(self, text='/', **args)
self.entry_2 = tk.Entry(self, width=2, **args)
self.label_2 = tk.Label(self, text='/', **args)
self.entry_3 = tk.Entry(self, width=4, **args)
self.entry_1.pack(side=tk.LEFT)
self.label_1.pack(side=tk.LEFT)
self.entry_2.pack(side=tk.LEFT)
self.label_2.pack(side=tk.LEFT)
self.entry_3.pack(side=tk.LEFT)
self.entries = [self.entry_1, self.entry_2, self.entry_3]
self.entry_1.bind('<KeyRelease>', lambda e: self._check(0, 2))
self.entry_2.bind('<KeyRelease>', lambda e: self._check(1, 2))
self.entry_3.bind('<KeyRelease>', lambda e: self._check(2, 4))
def _backspace(self, entry):
cont = entry.get()
entry.delete(0, tk.END)
entry.insert(0, cont[:-1])
def _check(self, index, size):
entry = self.entries[index]
next_index = index + 1
next_entry = self.entries[next_index] if next_index < len(self.entries) else None
data = entry.get()
if len(data) > size or not data.isdigit():
self._backspace(entry)
if len(data) >= size and next_entry:
next_entry.focus()
def get(self):
return [e.get() for e in self.entries]
if __name__ == '__main__':
win = tk.Tk()
win.title('DateEntry demo')
dentry = DateEntry(win, font=('Helvetica', 40, tk.NORMAL), border=0)
dentry.pack()
win.bind('<Return>', lambda e: print(dentry.get()))
win.mainloop()

I have discovered a very simple solution. I hope someone can find it useful.
self.entry1=tk.Entry(self)
self.entry1.insert(END, " / / ")
self.entry1.config(fg="grey")
def datemask(self, event):
if len(self.entry1.get()) is 2:
self.entry1.insert(END,"/")
elif len(self.entry1.get()) is 5:
self.entry1.insert(END,"/")
elif len(self.entry1.get()) is 11:
self.entry1.delete(10, END)
Bind the entry widget
self.entry1.bind('<KeyRelease>', self.datemask)

Related

clearing multiple labels values

I am losing my peanuts here. I am trying to clear two label values but i get an error
AttributeError: 'Label' object has no attribute 'delete'
basically if i were to click the calculate subtotal button then click the divide total button. I get my intended values. Now if I were to click on the clear values button i get an error. Literally shaking my head as I type this. Anyone care to explain why this is the case?
try:
import Tkinter as tk
except:
import tkinter as tk
class GetInterfaceValues():
def __init__(self):
self.root = tk.Tk()
self.totalValue = tk.StringVar()
self.root.geometry('500x200')
self.calculateButton = tk.Button(self.root,
text='Calculate Subtotal',
command=self.getSubtotals)
self.divideTotalButton = tk.Button(self.root,
text='Divide total',
command=self.divide)
self.textInputBox = tk.Text(self.root, relief=tk.RIDGE, height=1, width = 6, borderwidth=2)
self.firstLabel = tk.Label(self.root, text="This is the subtotal:")
self.secondLabel = tk.Label(self.root, text="This is the Divide Total:")
self.clearTotalButton = tk.Button(self.root, text='clear the values',command = self.clear)
self.firstLabel.pack(side="bottom")
self.secondLabel.pack(side="bottom")
self.textInputBox.pack()
self.calculateButton.pack()
self.divideTotalButton.pack()
self.clearTotalButton.pack()
self.root.mainloop()
def getTextInput(self):
result = self.textInputBox.get("1.0", "end")
return result
def getSubtotals(self):
userValue = int(self.getTextInput())
self.firstLabel["text"] = self.firstLabel["text"] + str(userValue * 5)
def divide(self):
userValue = int(self.getTextInput())
self.secondLabel["text"] = self.secondLabel["text"] + str(userValue / 10)
def clear(self):
self.firstLabel["text"] = self.firstLabel.delete("1.0","end")
app = GetInterfaceValues()
try:
import Tkinter as tk
except:
import tkinter as tk
class GetInterfaceValues():
def __init__(self):
self.root = tk.Tk()
self.totalValue = tk.StringVar()
self.root.geometry('500x200')
self.calculateButton = tk.Button(self.root,
text='Calculate Subtotal',
command=self.getSubtotals)
self.divideTotalButton = tk.Button(self.root,
text='Divide total',
command=self.divide)
self.textInputBox = tk.Text(self.root, relief=tk.RIDGE, height=1, width = 6, borderwidth=2)
self.firstLabelDefault = "This is the subtotal:"
self.secondLabelDefault = "This is the Divide Total:"
self.firstLabel = tk.Label(self.root, text=self.firstLabelDefault)
self.secondLabel = tk.Label(self.root, text=self.secondLabelDefault)
self.clearTotalButton = tk.Button(self.root, text='clear the values',command = self.clear)
self.firstLabel.pack(side="bottom")
self.secondLabel.pack(side="bottom")
self.textInputBox.pack()
self.calculateButton.pack()
self.divideTotalButton.pack()
self.clearTotalButton.pack()
self.root.mainloop()
def getTextInput(self):
result = self.textInputBox.get("1.0", "end")
return result
def getSubtotals(self):
userValue = int(self.getTextInput())
self.firstLabel["text"] = self.firstLabel["text"] + str(userValue * 5)
def divide(self):
userValue = int(self.getTextInput())
self.secondLabel["text"] = self.secondLabel["text"] + str(userValue / 10)
def clear(self):
self.firstLabel["text"] = self.firstLabelDefault
self.secondLabel["text"] = self.secondLabelDefault
self.textInputBox.delete("1.0", "end")
app = GetInterfaceValues()
You may have confused the methods of tkinter.Text and tkinter.Label. The method you called was tkinter.label.delete, which is not defined (does not exist), however it does exist for the tkinter.Text. Therefore, the only way to 'reset' would be to change the text attribute of the tkinter.Labels back to a 'default' string. It would perhaps be more appropriate to use another widget instead.

How to add a scrollbar to window made with tkinter

A simple quiz game
I got this code and I need scrollbars, I tried to search how to add it on stackoverflow (ScrolledWindow with tix...) but I still can't get something that works properly. Could someone help me?
from tkinter import *
from random import randint
root = Tk()
root.title("Quiz")
root.geometry("400x300")
class Window:
def __init__(self, question, answer):
self.text = [question, answer]
self.createLabel()
# self.createText()
self.createEntry()
self.createButton()
def retrieve_input(self):
# inputValue = self.textBox.get("1.0", "end-1c")
# print(inputValue)
if self.mystring.get() == self.text[1]:
print("Esatto. è " + self.text[1])
self.left['text'] = "Esatto"
def createLabel(self):
self.labelframe = LabelFrame(root, text="Domanda:")
self.labelframe.pack(fill="both", expand="yes")
self.left = Label(self.labelframe, text=self.text[0])
self.left.pack()
def createText(self):
self.textBox = Text(height=1)
self.textBox.pack()
def createEntry(self):
self.mystring = StringVar()
self.myentry = Entry(root, textvariable=self.mystring).pack()
def createButton(self):
self.but = Button(text="Click", command=self.retrieve_input)
self.but.pack()
for i in range(10):
one = randint(1, 10)
two = randint(1, 10)
Window("Quanto fa " + str(one) + "+" + str(two) + "?", str(one + two))
root.mainloop()
output
With ScrolledFrame it can look like this
I renamed Window into Question because it makes more sense
I use self.question and self.answer instead of self.text = [question, answer] to make it more readable.
I put classes and functions before root = tk.Tk() to make it more readable.
I use import tkinter as tk instead of from tkinter import * to make it more readable.
Question gets inner frame from ScrolledFrame and use as parent for LabelFrame. Other widgets use labelframe as parent.
BTW: you had entry = Entry(..).pack() which assign None to entry because pack()/grid()/place() returns None. I put pack() in next line and now I can get text directly from Entry (without StringVar)
Code
import tkinter as tk
from random import randint
# --- classes ---
class ScrolledFrame(tk.Frame):
def __init__(self, parent, vertical=True, horizontal=False):
super().__init__(parent)
# canvas for inner frame
self._canvas = tk.Canvas(self)
self._canvas.grid(row=0, column=0, sticky='news') # changed
# create right scrollbar and connect to canvas Y
self._vertical_bar = tk.Scrollbar(self, orient='vertical', command=self._canvas.yview)
if vertical:
self._vertical_bar.grid(row=0, column=1, sticky='ns')
self._canvas.configure(yscrollcommand=self._vertical_bar.set)
# create bottom scrollbar and connect to canvas X
self._horizontal_bar = tk.Scrollbar(self, orient='horizontal', command=self._canvas.xview)
if horizontal:
self._horizontal_bar.grid(row=1, column=0, sticky='we')
self._canvas.configure(xscrollcommand=self._horizontal_bar.set)
# inner frame for widgets
self.inner = tk.Frame(self._canvas, bg='red')
self._window = self._canvas.create_window((0, 0), window=self.inner, anchor='nw')
# autoresize inner frame
self.columnconfigure(0, weight=1) # changed
self.rowconfigure(0, weight=1) # changed
# resize when configure changed
self.inner.bind('<Configure>', self.resize)
self._canvas.bind('<Configure>', self.frame_width)
def frame_width(self, event):
# resize inner frame to canvas size
canvas_width = event.width
self._canvas.itemconfig(self._window, width = canvas_width)
def resize(self, event=None):
self._canvas.configure(scrollregion=self._canvas.bbox('all'))
class Question:
def __init__(self, parent, question, answer):
self.parent = parent
self.question = question
self.answer = answer
self.create_widgets()
def get_input(self):
value = self.entry.get()
print('value:', value)
if value == self.answer:
print("Esatto. è " + self.answer)
self.label['text'] = "Esatto"
def create_widgets(self):
self.labelframe = tk.LabelFrame(self.parent, text="Domanda:")
self.labelframe.pack(fill="both", expand=True)
self.label = tk.Label(self.labelframe, text=self.question)
self.label.pack(expand=True, fill='both')
self.entry = tk.Entry(self.labelframe)
self.entry.pack()
self.button = tk.Button(self.labelframe, text="Click", command=self.get_input)
self.button.pack()
# --- main ---
root = tk.Tk()
root.title("Quiz")
root.geometry("400x300")
window = ScrolledFrame(root)
window.pack(expand=True, fill='both')
for i in range(10):
one = randint(1, 10)
two = randint(1, 10)
Question(window.inner, "Quanto fa {} + {} ?".format(one, two), str(one + two))
root.mainloop()

How do I enable multiple selection of values from a combobox?

Python 3.4.3, Windows 10, Tkinter
I am attempting to create a combobox that allows for multiple selections from the dropdown. I have found similar work for listbox (Python Tkinter multiple selection Listbox), but cannot get it work with the combobox.
Is there a simple way to enable multiple selection from the dropdown of the combobox?
By design the ttk combobox doesn't support multiple selections. It is designed to allow you to pick one item from a list of choices.
If you need to be able to make multiple choices you can use a menubutton with an associated menu, and add checkbuttons or radiobuttons to the menu.
Here's an example:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
menubutton = tk.Menubutton(self, text="Choose wisely",
indicatoron=True, borderwidth=1, relief="raised")
menu = tk.Menu(menubutton, tearoff=False)
menubutton.configure(menu=menu)
menubutton.pack(padx=10, pady=10)
self.choices = {}
for choice in ("Iron Man", "Superman", "Batman"):
self.choices[choice] = tk.IntVar(value=0)
menu.add_checkbutton(label=choice, variable=self.choices[choice],
onvalue=1, offvalue=0,
command=self.printValues)
def printValues(self):
for name, var in self.choices.items():
print "%s: %s" % (name, var.get())
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Try this maybe...
import tkinter as Tkinter
import tkinter.font as tkFont
import tkinter.ttk as ttk
class Picker(ttk.Frame):
def __init__(self, master=None,activebackground='#b1dcfb',values=[],entry_wid=None,activeforeground='black', selectbackground='#003eff', selectforeground='white', command=None, borderwidth=1, relief="solid"):
self._selected_item = None
self._values = values
self._entry_wid = entry_wid
self._sel_bg = selectbackground
self._sel_fg = selectforeground
self._act_bg = activebackground
self._act_fg = activeforeground
self._command = command
ttk.Frame.__init__(self, master, borderwidth=borderwidth, relief=relief)
self.bind("<FocusIn>", lambda event:self.event_generate('<<PickerFocusIn>>'))
self.bind("<FocusOut>", lambda event:self.event_generate('<<PickerFocusOut>>'))
self._font = tkFont.Font()
self.dict_checkbutton = {}
self.dict_checkbutton_var = {}
self.dict_intvar_item = {}
for index,item in enumerate(self._values):
self.dict_intvar_item[item] = Tkinter.IntVar()
self.dict_checkbutton[item] = ttk.Checkbutton(self, text = item, variable=self.dict_intvar_item[item],command=lambda ITEM = item:self._command(ITEM))
self.dict_checkbutton[item].grid(row=index, column=0, sticky=Tkinter.NSEW)
self.dict_intvar_item[item].set(0)
class Combopicker(ttk.Entry, Picker):
def __init__(self, master, values= [] ,entryvar=None, entrywidth=None, entrystyle=None, onselect=None,activebackground='#b1dcfb', activeforeground='black', selectbackground='#003eff', selectforeground='white', borderwidth=1, relief="solid"):
if entryvar is not None:
self.entry_var = entryvar
else:
self.entry_var = Tkinter.StringVar()
entry_config = {}
if entrywidth is not None:
entry_config["width"] = entrywidth
if entrystyle is not None:
entry_config["style"] = entrystyle
ttk.Entry.__init__(self, master, textvariable=self.entry_var, **entry_config, state = "readonly")
self._is_menuoptions_visible = False
self.picker_frame = Picker(self.winfo_toplevel(), values=values,entry_wid = self.entry_var,activebackground=activebackground, activeforeground=activeforeground, selectbackground=selectbackground, selectforeground=selectforeground, command=self._on_selected_check)
self.bind_all("<1>", self._on_click, "+")
self.bind("<Escape>", lambda event: self.hide_picker())
#property
def current_value(self):
try:
value = self.entry_var.get()
return value
except ValueError:
return None
#current_value.setter
def current_value(self, INDEX):
self.entry_var.set(values.index(INDEX))
def _on_selected_check(self, SELECTED):
value = []
if self.entry_var.get() != "" and self.entry_var.get() != None:
temp_value = self.entry_var.get()
value = temp_value.split(",")
if str(SELECTED) in value:
value.remove(str(SELECTED))
else:
value.append(str(SELECTED))
value.sort()
temp_value = ""
for index,item in enumerate(value):
if item!= "":
if index != 0:
temp_value += ","
temp_value += str(item)
self.entry_var.set(temp_value)
def _on_click(self, event):
str_widget = str(event.widget)
if str_widget == str(self):
if not self._is_menuoptions_visible:
self.show_picker()
else:
if not str_widget.startswith(str(self.picker_frame)) and self._is_menuoptions_visible:
self.hide_picker()
def show_picker(self):
if not self._is_menuoptions_visible:
self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1 )
self.picker_frame.lift()
self._is_menuoptions_visible = True
def hide_picker(self):
if self._is_menuoptions_visible:
self.picker_frame.place_forget()
self._is_menuoptions_visible = False
if __name__ == "__main__":
import sys
try:
from Tkinter import Tk, Frame, Label
except ImportError:
from tkinter import Tk, Frame, Label
root = Tk()
root.geometry("500x600")
main =Frame(root, pady =15, padx=15)
main.pack(expand=True, fill="both")
Label(main, justify="left", text=__doc__).pack(anchor="w", pady=(0,15))
COMBOPICKER1 = Combopicker(main, values = [1, 2, 3, 4])
COMBOPICKER1.pack(anchor="w")
if 'win' not in sys.platform:
style = ttk.Style()
style.theme_use('clam')
root.mainloop()

Python GUI interface not opening widget?

I'm trying to create a widget that's like a to do list: but my program doesn't seem to run for some reason. It doesn't give an error, but it doesn't work..?
would anyone know what's wrong with my code?
from Tkinter import *
import tkFont
class App:
def getTasks(self):
return self.todo
def getCompleted(self):
return self.done
def __init__(self, master):
self.todo = todo.todoList()
self.master = master
self.frame - Frame(master)
self.frame.grid()
self.saveButton = Button(self.frame, text="Save", command=self.save)
self.saveButton.grid()
self.restoreButton = Button(self.frame, text="Restore", command=self.res)
self.restoreButton.grid(row=0, column=1)
self.addButton = Button(self.frame, text="Add", command = self.add)
self.addButton.grid(row=0, column=2)
self.doneButton = Button(self.frame, text = "Done", command = self.done)
self.doneButton.grid(row=0, column=3)
self.button = Button(self.frame, text="QUIT", command=self.quit)
self.button.grid(row=0, column=4)
label = Label(self.frame, text="New Task: ")
label.grid()
self.entry = Entry(self.frame)
self.entry.grid(row=0, column=4)
frame1 = LabelFrame(self.frame, text="Tasks")
frame1.grid(columnspam = 5)
self.tasks = Listbox(frame1)
self.task.grid()
frame2=LabelFrame(self.frame, text="Completed")
frame2.grid(columnspan=5)
self.completed= Listbox(frame2)
self.completed.grid()
def save(self):
self.todo.saveList("tasks.txt")
def restore(self):
self.todo.restoreList("tasks.txt")
items = self.todo.getTasks()
self.tasks.delete(0, END)
for item in items:
self.tasks.insert(END, item)
items = self.todo.getCompleted()
self.completed.delete(0,END)
for item in items:
self.completed.insert(END,item)
def add(self):
task = self.entry.get()
self.todo.addTask(task)
self.tasks.insert(END,task)
def done(self):
selection = self.tasks.curselection()
if len(selection) == 0:
return
task = self.tasks.get(selection[0])
self.todo.completeTask(task)
self.tasks.delete(selection[0])
self.completed.insert(END,task)
would anyone know what the error is?
You need to actually make an instance of App and an instance of Tkinter.Tk:
...
if __name__ == '__main__':
root = Tk()
app = App(root)
root.mainloop()
Note, I haven't tried the rest of your code, so I don't know if it will work, but this should at least start giving you errors to work with.

How to select multiple checkboxes with SHIFT in tkinter?

The question is quite simple,I've created many checkboxes in a Text widget using window_create .Here is the code:
import tkinter as tk
root = tk.Tk()
sb = tk.Scrollbar(orient="vertical")
text = tk.Text(root, width=40, height=20, yscrollcommand=sb.set)
sb.config(command=text.yview)
sb.pack(side="right",fill="y")
text.pack(side="top",fill="both",expand=True)
for i in range(30):
cb = tk.Checkbutton(text="checkbutton %s" % i,padx=0,pady=0,bd=0)
text.window_create("end", window=cb)
text.insert("end", "\n")
root.mainloop()
And here is what it looks like:
I want to select multiple checkboxes,which is troublesome if I have to click every checkbox.So is there a way that SHIFT can be used here?
You should bind the '<Shift-Button-1>' event to every checkbutton, and also the '<Button-1> to indicate the start of the range which should be selected. Also, consider to wrap your code in a class for better readability:
class App:
def __init__(self, root):
self.start = 0
self.root = root
self.sb = tk.Scrollbar(orient="vertical")
text = tk.Text(root, width=40, height=20, yscrollcommand=self.sb.set)
self.sb.config(command=text.yview)
self.sb.pack(side="right",fill="y")
text.pack(side="top", fill="both", expand=True)
self.chkbuttons = [tk.Checkbutton(text="checkbutton %s" % i,padx=0,pady=0,bd=0)
for i in range(30)]
for cb in self.chkbuttons:
text.window_create("end", window=cb)
text.insert("end", "\n")
cb.bind("<Button-1>", self.selectstart)
cb.bind("<Shift-Button-1>", self.selectrange)
def selectstart(self, event):
self.start = self.chkbuttons.index(event.widget)
def selectrange(self, event):
start = self.start
end = self.chkbuttons.index(event.widget)
sl = slice(min(start, end)+1, max(start, end))
for cb in self.chkbuttons[sl]:
cb.toggle()
self.start = end
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
root.mainloop()

Categories

Resources