Tkinter: How to distinguishing between long and short button click - python

i am trying to achieve this behaviour: When user clicks the button for a shorter time the program runs the first method, which lets say adds one. However, when user keeps the button pressed for a longer time the program runs the second method, which opens a dialog window. The only solution which came to my mind was using time thread.
The problem is that the program is acting really strangely.Sometimes (not always that's also weird) when the dialog pops up my computer kind of freezez(not exactly, the processes are running). I am able to move with my mouse but I am not able to click or write anything with my keyboard.
Here is a code:
from Tkinter import *
import threading
import os
class Dialog(Toplevel):
def __init__(self, parent, title = None):
Toplevel.__init__(self, parent)
self.transient(parent)
if title:
self.title(title)
self.parent = parent
self.result = None
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=5, pady=5)
#self.buttonbox()
self.grab_set()
if not self.initial_focus:
self.initial_focus = self
self.protocol("WM_DELETE_WINDOW", self.cancel)
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
parent.winfo_rooty()+50))
self.initial_focus.focus_set()
self.wait_window(self)
#
# construction hooks
def body(self, master):
# create dialog body. return widget that should have
# initial focus. this method should be overridden
pass
def buttonbox(self):
# add standard button box. override if you don't want the
# standard buttons
box = Frame(self)
w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
w.pack(side=LEFT, padx=5, pady=5)
w = Button(box, text="Cancel", width=10, command=self.cancel)
w.pack(side=LEFT, padx=5, pady=5)
self.bind("<Return>", self.ok)
self.bind("<Escape>", self.cancel)
box.pack()
#
# standard button semantics
def ok(self, event=None):
if not self.validate():
self.initial_focus.focus_set() # put focus back
return
self.withdraw()
self.update_idletasks()
self.apply()
self.cancel()
def cancel(self, event=None):
# put focus back to the parent window
self.parent.focus_set()
self.destroy()
#
# command hooks
def validate(self):
return 1 # override
def apply(self):
pass # override
class MyDialog(Dialog):
def body(self, master):
master.grid_columnconfigure(0, weight=1)
self.e1 = Entry(master)
self.e1.grid(column=0,row=0,columnspan = 4)
self.buttons = {}
self.buttons['previous'] = Button(master,text =u'/')
self.buttons['previous'].grid(column=0,row =1,columnspan = 2)
self.buttons['next'] = Button(master,text =u'*')
self.buttons['next'].grid(column=2,row =1,columnspan = 2)
self.buttons['minus'] = Button(master,text =u'-')
self.buttons['minus'].grid(column=3,row =2)
self.buttons['plus'] = Button(master,text =u'+')
self.buttons['plus'].grid(column=3,row =3)
self.buttons['enter'] = Button(master,text =u'Enter')
self.buttons['enter'].grid(column=3,row =4,rowspan = 2)
self.buttons['zero'] = Button(master,text =u'0')
self.buttons['zero'].grid(column=0,row =5)
self.buttons['del'] = Button(master,text =u'←')
self.buttons['del'].grid(column=1,row =5)
rows = range(4,1,-1)
columns = range(0,3)
i = 1
for row in rows:
for column in columns:
Button(master,text = i).grid(row= row,column = column)
i+=1
return self.e1 # initial focus
def leftClick():
print 'add one'
def longerClick(): #if the timer ends this will run
d = MyDialog(master) #create dialog
print 'longerClick'
def handleButtonPress(event):
global t
t = threading.Timer(0.8,longerClick) #setTimer
t.start()
print t
def handleButtonRelease(event): #here is a problem probably
global t
t.cancel()
leftClick()
master = Tk()
t = None
master.geometry('{}x{}'.format(300, 300))
button = Button(master, text = 'Stlac')
button.pack()
button.bind('<ButtonPress-1>',handleButtonPress)
button.bind('<ButtonRelease-1>',handleButtonRelease)
mainloop()
So when button is pressed I setup timer. When user releases the button before timer ends, the timer is canceled.When not, longerClick method runs.
If you know where the problem is I would really aprecciate your help. Also if you know a better way how to detect longer button press that would also help me. I have read that it is possible in PYQT although I have to use Tkinter.

Related

Clearing the screen in Tkinter

I have tried all the other posts on this topic but none of them have worked for me...
Here is my code:
from tkinter import *
window=Tk()
window.geometry('600x400')
window.title('hello world')
def wrong():
root=Tk()
text=Text(root)
text.insert(INSERT,"WRONG!, you stupid idiot!!!!")
text.pack()
def right():
root=Tk()
text=Text(root)
text.insert(INSERT,"CORRECT, good job!")
text.pack()
def reset():
hide_widgets()
class UIProgram():
def setupUI(self):
buttonlist=[]
button= Button(window,text='Sanjam',command=wrong).pack()
button2=Button(window,text='Sunny the Bunny',command=wrong).pack()
button3= Button(window, text='Sunjum',command=right).pack()
button4= Button(window, text='bob',command=wrong).pack()
button5= Button(window, text='next',command=reset)
button5.pack()
self.label=Label(window)
self.label.pack()
window.mainloop()
program= UIProgram()
program.setupUI()
I am aware of pack_forget() and tried it, but it keeps giving me an error. Also, is it possible to make a command(like the 'reset' one I have) and use that in the command for a clear screen button. Please help, I am new to Tkinter and don't know much about these things..
Thanks
Example code
I'm tired to describe the same mistakes hundreds of times - some comments in code.
import tkinter as tk
# --- classes ---
class UIProgram():
def __init__(self, master):
self.master = master # use to add elements directly to main window
self.buttons = [] # keep buttons to change text
# frame to group buttons and easily remove all buttons (except `Next`)
self.frame = tk.Frame(self. master)
self.frame.pack()
# group button in frame
button = tk.Button(self.frame, text='Sanjam', command=self.wrong)
button.pack()
self.buttons.append(button)
button = tk.Button(self.frame, text='Sunny the Bunny', command=self.wrong)
button.pack()
self.buttons.append(button)
button = tk.Button(self.frame, text='Sunjum', command=self.right)
button.pack()
self.buttons.append(button)
button = tk.Button(self.frame, text='bob', command=self.wrong)
button.pack()
self.buttons.append(button)
# button outside frame
button_next = tk.Button(self.master, text='Next >>', command=self.reset)
button_next.pack()
self.label = tk.Label(self.frame)
self.label.pack()
def wrong(self):
# create second window with message and closing button
win = tk.Toplevel()
tk.Label(win, text="WRONG!, you stupid idiot!!!!").pack()
tk.Button(win, text='close', command=win.destroy).pack()
def right(self):
# create second window with message and closing button
win = tk.Toplevel()
tk.Label(win, text="CORRECT, good job!").pack()
tk.Button(win, text='close', command=win.destroy).pack()
def reset(self):
# remove frame with all buttons
self.frame.pack_forget()
tk.Label(self.master, text="frame removed").pack()
# or only remove text in labels
#for button in self.buttons:
# button['text'] = '-- place for new text --'
# --- main ---
root = tk.Tk()
root.geometry('600x400')
root.title('hello world')
program = UIProgram(root)
root.mainloop()
BTW: if you do var = Widget(...).pack() then you assign None to var because pack()/grid()/place() return None. You have to do it in two lines
var = Widget(...)
var.pack()
or in one line if you don't need var
Widget(...).pack()

Login and show menu items by if

I'm trying to create a login with a tkinter Entry.
Compare the input with the password and if it is correct you are supposed to get access to more stuff in the menu bar.
If I understand correctly I need to update the window in order for the menu to update, but I cant figure out how.
And the variable "moberg" don't seems to update to True. Might be that one is global(?) and the other one belongs to a class. But I cant figure out how to make that work either.
Here is a sample of what I've done so far.
from tkinter import *
moberg="no"
class PCMsyntax(Frame):
def update():
print("updated") #only for visual, remove later
app.mainloop.after(1, update)
def __init__(self, master):
super().__init__(master)
self.pack(fill=BOTH, expand=True)
self.initUI()
def initUI(self):
menubar = Menu(self.master)
self.master.config(menu=menubar)
syntaxMenu = Menu(menubar, tearoff=False)
submenu = Menu(syntaxMenu)
syntaxMenu.add_cascade(label='Arithmetic Exp', underline=0, command='')
syntaxMenu.add_cascade(label='Assign & Compare', underline=0, command='')
syntaxMenu.add_separator()
syntaxMenu.add_cascade(label='Math', menu=submenu, underline=0)
submenu.add_command(label="abs()", command='')
if moberg == True:
syntaxMenu.add_cascade(label='No public access', menu=submenu, underline=0)
submenu.add_command(label="onlyForLabTech()")
menubar.add_cascade(label="Syntax", underline=0, menu=syntaxMenu)
menubar.add_cascade(label="Login", underline=0, command=self.onLogin)
def onLogin(self):
self.newWindow = Toplevel(self.master)
self.app = Password(self.newWindow)
class Password():
def __init__(self, master):
self.master = master
self.frame = Frame(self.master)
self.pwd = Entry(self.master, width=30, bg="whitesmoke", foreground="whitesmoke")
self.pwd.pack()
self.verifyButton = Button(self.frame, text = 'Verify', width = 25, command = self.verify_password)
self.verifyButton.pack()
self.frame.pack()
def verify_password(self):
user_entry = self.pwd.get()
if str(user_entry) == "test":
moberg=True
print(moberg) #only for visual, remove later
PCMsyntax.update
self.master.destroy()
else:
moberg=False
print(moberg) #only for visual, remove later
self.master.destroy()
def main():
root = Tk()
root.geometry("560x600")
app = PCMsyntax(master=root)
app.mainloop()
if __name__ == '__main__':
main()
You could achieve something like what you're looking for with the below:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.public = Frame(self.root, borderwidth=1, relief="solid") #public, visible frame
self.hidden = Frame(self.root, borderwidth=1, relief="solid") #hidden, private frame
self.label1 = Label(self.public, text="This text and everything over here is public.") #this is inside the public frame and so is visible
self.entry1 = Entry(self.public) #so is this
self.label2 = Label(self.hidden, text="This text and everything over here is hidden and only appears after login.") #this one is in the hidden frame and so is private before login
self.button1 = Button(self.public, text="Login", command=self.command) #this is in the public frame
self.public.pack(side="left", expand=True, fill="both") #we pack the public frame on the left of the window
self.label1.pack() #then we pack all the widgets for both frames here and below
self.entry1.pack()
self.label2.pack()
self.button1.pack()
def command(self): #whenever the login button is pressed this is called
if self.button1.cget("text") == "Login" and self.entry1.get() == "password": #we check if the button is in login state or not and if the password is correct
self.hidden.pack(side="right", expand=True, fill="both") #if it is we pack the hidden frame which makes it and it's contents visible
self.button1.configure({"text": "Logout"}) #we then set the button to a logout state
elif self.button1.cget("text") == "Logout": #if the button is in logout state
self.hidden.pack_forget() #we pack_forget the frame, which removes it and it's contents from view
self.button1.configure({"text": "Login"}) #and then we set the button to login state
root = Tk()
App(root)
root.mainloop()
This is fairly self explanatory and where it isn't I've explained what's happening.
This is just one way of many of achieving what it is you're looking for.
I'd also like to point out that login systems are complicated and if you're looking to use this for anything serious or as a package to sell then the way I have done this is not secure.
I'd recommend looking at other ways people handle sensitive information in tkinter.

Blocking Input Dialog Box

How can I get a blocking modal input dialog box in standard Python?
I need the user to input a value before the code can proceed.
Here is some not-working test code, but the idea is that I should be able to call MyDialog from anywhere in the script, so this is just a simplified example.
import tkinter
class MyDialog:
def __init__(self, prompt):
self.top = tkinter.Toplevel()
tkinter.Label(self.top, text=prompt).pack()
self.e = tkinter.Entry(self.top)
self.e.pack(padx=5)
tkinter.Button(self.top, text="OK", command=self.ok).pack(pady=5)
def ok(self):
self.top.destroy()
return self.e.get()
root = tkinter.Tk()
userName = MyDialog('Enter your name')
tkinter.Label(root, text="Hello {}".format(userName)).pack()
root.mainloop()
The dialog should not only disable the master window, but block whatever code called it. And it should be able to pass the value back to the calling code.
The solution requires two critical pieces. First, use grab_set to block all events in the other window (or, more correctly, send all events to the dialog window). Second, use wait_window to prevent the method from returning until the dialog has been destroyed.
That being said, you shouldn't be using it like in your example. You need to have the mainloop running before you create the window. It might work OK on some platforms, but in general you shouldn't expect your GUI to behave properly until mainloop is running.
Here's a simple example:
import Tkinter as tk
class MyDialog(object):
def __init__(self, parent, prompt):
self.toplevel = tk.Toplevel(parent)
self.var = tk.StringVar()
label = tk.Label(self.toplevel, text=prompt)
entry = tk.Entry(self.toplevel, width=40, textvariable=self.var)
button = tk.Button(self.toplevel, text="OK", command=self.toplevel.destroy)
label.pack(side="top", fill="x")
entry.pack(side="top", fill="x")
button.pack(side="bottom", anchor="e", padx=4, pady=4)
def show(self):
self.toplevel.grab_set()
self.toplevel.wait_window()
value = self.var.get()
return value
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.button = tk.Button(self, text="Click me!", command=self.on_click)
self.label = tk.Label(self, text="", width=40)
self.label.pack(side="top", fill="x")
self.button.pack(padx=20, pady=20)
def on_click(self):
result = MyDialog(self, "Enter your name:").show()
self.label.configure(text="your result: '%s'" % result)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()

How to create a custom messagebox using Tkinter in Python with changing message and changing button state

I wanted to cerate a message window on top of root window which looks similar from this link. A message window with a message and an inactive button untill some portion of the code are done and then to display a message with an active button. This is how the code structure looks like(I have cut through specific details to just clear the point how my code flow looks like)
The second window is not working, instead the window appears at the end of the function. Can somebody help.
from Tkinter import *
import tkMessageBox
def a():
return
def b():
. . .
. . .
msg_window = Toplevel()
msg_window.grapset()
msg_window.title( ___ )
msg_geometry( ___ )
msgLabel = Label(window)
msgLabel["text"]="Processing...."
msgLabel.pack()
btn = Button(msg_window, text="Ok", command=msg_window.destroy())
btn.pack()
btn.config(state=DISABLED)
a()
c()
d()
d()
msgLabel["text"]="Done"
msgLabel.pack()
btn = Button(msg_window, text="Ok", command=msg_window.destroy())
btn.pack()
btn.config(state=NORMAL)
e()
if tkMessageBox.askyesno( ___ ):
do something
else:
do something
pass
def c():
return
def d():
return
def e():
return
root = Tk()
frame = Frame( ___ )
frame.pack()
but_find = Button( ___ )
but_find.pack()
but_start = Button( ___ )
but_start.pack()
but_exit = Button( ___ )
but_exit.pack()
root.mainloop()
Explanation:
I am building an application where the root window contains 3 buttons. When start button is clicked, function b is called, which in turn calls various other functions inside function b itself. This is the point where, i want to create a second window that shows up above the root window and making the root window inactive until function b gets completed or until the second window is destroyed.
What i want is..
The second window(contains 2 things- a message and a button) should start inside the function b after some steps. It should display the message Processing.... and an inactive button called Ok before function a starts and should appear until function d gets completed. Then, i want to display Done with an active 'Ok' button that destroys the second window. Then make the root window active.
from tkinter import *
class MessageBox(object):
def __init__(self,title='Mess', msg='', b1='OK', b2='',b3='',b4=''):
# Required Data of Init Function
self.title = title # Is title of titlebar
self.msg = msg # Is message to display
self.b1 = b1 # Button 1 (outputs '1')
self.b2 = b2 # Button 2 (outputs '2')
self.b3 = b3 # Button 3 (outputs '3')
self.b4 = b4 # Button 4 (outputs '4')
self.choice = '' # it will be the return of messagebox according to button press
# Just the colors for my messagebox
self.tabcolor = 'red' # Button color for Active State
self.bgcolor = 'blue' # Button color for Non-Active State
self.bgcolor2 = 'yellow' # Background color of Dialogue
self.textcolor = 'Green' # Text color for Dialogue
# Creating Dialogue for messagebox
self.root = Toplevel()
# Removing titlebar from the Dialogue
self.root.overrideredirect(True)
# Setting Geometry
self.root.geometry("300x120+100+100")
# Setting Background color of Dialogue
self.root.config(bg=self.bgcolor2)
# Creating Label For message
self.msg = Label(self.root,text=msg,
font=("Helvetica",9),
bg=self.bgcolor2,
fg=self.textcolor,
#anchor='nw'
)
self.msg.place(x=10,y=28,height=60,width=280)
# Creating TitleBar
self.titlebar = Label(self.root,text=self.title,
bg=self.bgcolor2,
fg=self.textcolor,
bd=0,
font=("Helvetica",10,'bold')
)
self.titlebar.place(x=100,y=5)
# Creating Close Button
self.CloseBtn = Button(self.root,
text='x',
font=("Helvetica",12),
command = lambda : self.closed(),
bd=0,
activebackground='red',
activeforeground='white',
background=self.bgcolor2,
foreground=self.textcolor)
self.CloseBtn.place(x=260,y=0,width=40)
# Changing Close Button Color on Mouseover
self.CloseBtn.bind("<Enter>", lambda e,: self.CloseBtn.config(bg='red',fg='white'))
self.CloseBtn.bind("<Leave>", lambda e,: self.CloseBtn.config(bg=self.bgcolor2,fg=self.textcolor))
# Creating B1
self.B1 = Button(self.root,text=self.b1,command=self.click1,
bd=0,
font=("Helvetica",10),
bg=self.bgcolor,
fg='white',
activebackground=self.tabcolor,
activeforeground=self.textcolor)
self.B1.place(x=225,y=90,height=25,width=60)
# Getting place_info of B1
self.B1.info = self.B1.place_info()
# Creating B2
if not b2=="":
self.B2 = Button(self.root,text=self.b2,command=self.click2,
bd=0,
font=("Helvetica",10),
bg=self.bgcolor,
fg='white',
activebackground=self.tabcolor,
activeforeground=self.textcolor)
self.B2.place(x=int(self.B1.info['x'])-(70*1),
y=int(self.B1.info['y']),
height=int(self.B1.info['height']),
width=int(self.B1.info['width'])
)
# Creating B3
if not b3=='':
self.B3 = Button(self.root,text=self.b3,command=self.click3,
bd=0,
font=("Helvetica",10),
bg=self.bgcolor,
fg='white',
activebackground=self.tabcolor,
activeforeground=self.textcolor)
self.B3.place(x=int(self.B1.info['x'])-(70*2),
y=int(self.B1.info['y']),
height=int(self.B1.info['height']),
width=int(self.B1.info['width'])
)
# Creating B4
if not b4=='':
self.B4 = Button(self.root,text=self.b4,command=self.click4,
bd=0,
font=("Helvetica",10),
bg=self.bgcolor,
fg='white',
activebackground=self.tabcolor,
activeforeground=self.textcolor)
self.B4.place(x=int(self.B1.info['x'])-(70*3),
y=int(self.B1.info['y']),
height=int(self.B1.info['height']),
width=int(self.B1.info['width'])
)
# Making MessageBox Visible
self.root.wait_window()
# Function on Closeing MessageBox
def closed(self):
self.root.destroy() # Destroying Dialogue
self.choice='closed'#Assigning Value
# Function on pressing B1
def click1(self):
self.root.destroy() # Destroying Dialogue
self.choice='1' # Assigning Value
# Function on pressing B2
def click2(self):
self.root.destroy() # Destroying Dialogue
self.choice='2' # Assigning Value
# Function on pressing B3
def click3(self):
self.root.destroy() #Destroying Dialogue
self.choice='3' # Assigning Value
# Function on pressing B4
def click4(self):
self.root.destroy() #Destroying Dialogue
self.choice='4' #Assigning Value
root = Tk()
def func():
a = MessageBox(msg='Hello I m your multiline message',
title='Hello World',
b1='Button 1',
b2='Button 2',
)
print(a.choice)
Button(root,text='Click Me',command=func).pack()
root.mainloop()
Try developing your own dialog. Example:
import Tkinter as tk
class CustomDialog(tk.Toplevel):
def __init__(self, title, message, command1=self.ok, command2=self.ok, command3=self.ok, buttontext1="button1", buttontext2="button2", buttontext3="button3"):
self.base = tk.Toplevel()
self.base.title(title)
self.label = tk.Label(self.base, text=message)
self.label.pack()
self.label.grid(row=0, column=0, columnspan=3, sticky=N)
self.button1 = tk.Button(self.base, text=buttontext1, command=command1)
self.button1.pack()
self.button1.grid(row=1, column=0, sticky=N)
self.button2 = tk.Button(self.base, text=buttontext2, command=command2)
self.button2.pack()
self.button2.grid(row=1, column=1, sticky=N)
self.button3 = tk.Button(self.base, text=buttontext3, command=command3)
self.button3.pack()
self.button3.grid(row=1, column=2, sticky=N)
def ok(self, event=None):
self.destroy()
def baseconfig(self, option, value):
self.base[option] = value
def labelconfig(self, option, value):
self.label[option] = value
def buttonconfig(self, number, option, value):
exec "self.button%s[option] = value" % str(number)
def customDialog(title, message, command1=self.ok, command2=self.ok, command3=self.ok, buttontext1="button1", buttontext2="button2", buttontext3="button3", button1ret=None, button2ret=None, button3ret=None):
def _button1press():
command1()
return button1ret
def _button2press():
command2()
return button2ret
def _button3press():
command3()
return button3ret
dialog = CustomDialog(title, message, _button1press, _button2press, _button3press, buttontext1, buttontext2, buttontext3)
And to disable the first button, call self.buttonconfig(1, state, DISABLED) (1 refers to the number of the button). To enable it, use self.buttonconfig(1, state, NORMAL).

Message Box in Python

Is there a UI library to create a message box or input box in python?
Your best bet is the tkMessageBox module, which should work on all systems (as Python will typically come with Tkinter).
If you can restrict yourself to a specific operating system, better choices might be available.
Simple message boxes and input boxes can be created using EasyGui, a small library using Tkinter, which Python comes with.
You can get EasyGui here: http://easygui.sourceforge.net/
I've heard good things about wx python, which is also multi-platform.
from Tkinter
import *
import os
class Dialog(Toplevel):
def __init__(self, parent, title = None):
Toplevel.__init__(self, parent)
self.transient(parent)
if title:
self.title(title)
self.parent = parent
self.result = None
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=5, pady=5)
self.buttonbox()
self.grab_set()
if not self.initial_focus:
self.initial_focus = self
self.protocol("WM_DELETE_WINDOW", self.cancel)
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
parent.winfo_rooty()+50))
self.initial_focus.focus_set()
self.wait_window(self)
#
# construction hooks
def body(self, master):
# create dialog body. return widget that should have
# initial focus. this method should be overridden
pass
def buttonbox(self):
# add standard button box. override if you don't want the
# standard buttons
box = Frame(self)
w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
w.pack(side=LEFT, padx=5, pady=5)
w = Button(box, text="Cancel", width=10, command=self.cancel)
w.pack(side=LEFT, padx=5, pady=5)
self.bind("<Return>", self.ok)
self.bind("<Escape>", self.cancel)
box.pack()
#
# standard button semantics
def ok(self, event=None):
if not self.validate():
self.initial_focus.focus_set() # put focus back
return
self.withdraw()
self.update_idletasks()
self.apply()
self.cancel()
def cancel(self, event=None):
# put focus back to the parent window
self.parent.focus_set()
self.destroy()
#
# command hooks
def validate(self):
return 1 # override
def apply(self):
pass # override

Categories

Resources