How can I avoid keyboard conflict in python Tkinter? - python

I want to make a keyboard quick operation command with Tkinter.The keyboard event will invoke a function:when I press 'b',executive function 'buy',and when I press 's',executive function 'sell'.But there is a entry in my GUI.When I input a number in this entry,I'll press 'b' to invoke function 'buy' or press 's' to invoke function 'sell'.Of course the entry will display 'b' or 's'.I want to invoke function when I press 's' or 'b' and the entry will just distinguish and display numbers.How can I achieve this purpose ?
Here is my code:
# -*- coding: utf-8 -*-
from Tkinter import *
import tkFont
import datetime
class TradeSystem(object):
"""docstring for TradeSystem"""
def __init__(self):
self.root = Tk()
self.root.geometry('465x180')
self.root.resizable(width=True, height=False)
Label(self.root, text = 'Volume',font = tkFont.Font(size=15, weight='bold')).grid(row=0, column=0)
self.e1_str = StringVar()
self.e1 = Entry(self.root,bg = '#D2E48C',width = 10,textvariable = self.e1_str)
self.e1.grid(row = 1, column = 0)
self.v = IntVar()
self.Cb = Checkbutton(self.root,variable = self.v,text = 'Keyboard active',onvalue = 1,offvalue = 0,command = self.Keyeve)
self.Cb.grid(row = 3,column = 0)
self.currenttime = StringVar()
Label(self.root,textvariable = self.currenttime).grid(row=4, column=0,sticky = NW)
self.t_loop()
self.root.mainloop()
def buy(self,event):
print 'This is buy function.'
def sell(self,event):
print 'This is sell function.'
def rb(self):
self.root.bind('<KeyPress-b>',self.buy)
self.root.bind('<KeyPress-s>',self.sell)
def Keyeve(self):
if self.v.get():
self.rb()
else:
self.root.unbind('<KeyPress-b>')
self.root.unbind('<KeyPress-s>')
def t_loop(self):
self.currenttime.set(datetime.datetime.now().strftime("%Y-%m-%d,%H:%M:%S"))
self.root.after(1000,self.t_loop)
if __name__ == '__main__':
TradeSystem()
I input some numbers in entry self.e1,and when the keyboard active is 'on',I press 'b' to invoke function 'buy',like:
And fucntion 'buy' worked.
I just want the entry to distinguish numbers and when I press 'b' after I complete input numbers function 'buy' is invoked immediately.How can I achieve that?

Separate the text input from command hotkeys by using a modifier key, like Ctrl:
self.root.bind('<Control-b>',self.buy)
self.root.bind('<Control-s>',self.sell)
self.root.bind('<Control-B>',self.buy)
self.root.bind('<Control-S>',self.sell)
Note that the above has bound both the uppercase and lowercase keys, so that it still works if Caps Lock is on.

Related

Collecting variables from a GUI

I have a simple GUI where the user selects a file, which becomes a variable for my main code. Here, my variable output should be the database path (gui_db_path) which the user inputs. When I run this code, called gui_test.py, the variable is printable, and prints to the console.
class GUI:
def __init__(self, window):
# 'StringVar()' is used to get the instance of input field
self.input_db_text = StringVar()
window.title("HyPep 1.0")
window.geometry("700x700")
ttk.Label(window, text='Database sequences .csv:').grid(row=1,column=0)
ttk.Button(window, text = "Browse", command = lambda: self.set_path_database_field()).grid(row = 1,column=2, ipadx=5, ipady=0)
ttk.Entry(window, textvariable = self.input_db_text, width = 70).grid( row = 1, column = 1, ipadx=1, ipady=1)
ttk.Button(window, text = "Analyze").grid(row = 10,column=1, ipadx=5, ipady=15)
def set_path_database_field(self):
self.path_db = askopenfilename()
self.input_db_text.set(self.path_db)
def get_database_path(self):
""" Function provides the database full file path."""
return self.path_db
if __name__ == '__main__':
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
print(gui.path_db, '\n', gui.path_qu)
gui_db_path = gui.path_db
print(gui_db_path)
My issue is that I need to retrieve this variable for use in another file, user_input.py, but is no longer callable. My code for user_input.py is:
from gui_test import gui_db_path
print(gui_db_path)
Instead of printing to the console in this instance, I get:
ImportError: cannot import name 'gui_db_path' from 'gui_test'
I'm sure there is a simple solution that I am missing, can anyone shed some light?
...
Update: much closer, need to expand the solution:
How would I go about expanding this to retrieve multiple paths? I have been trying this:
gui_test.py:
...
def get_db_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_database_path()
def get_qu_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_query_path()
user_input.py:
from gui_test import get_db_path
from gui_test import get_qu_path
gui_db_path = get_db_path()
gui_qu_path = get_qu_path()
Note that the code inside if __name__ == '__main__' block will not be executed when the file is imported. You need to put those code inside a function instead and returns the path at the end of the function:
gui_test.py
...
def get_db_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_database_path()
Then import this function inside user_input.py:
from gui_test import get_db_path
gui_db_path = get_db_path()
print(gui_db_path)

PYTHON TKINTER > e = Entry() > e.bind('<ENTER>', function)

I am not allowed to add images yet to question posts.
Question below:
My app currently uses a window that is coded in a class.
My ultimate goal is to press enter while entering letters and numbers into an entry widget and press enter, then the function would update text that correlates to a label in my main window.
Detailed description below:
I cannot figure out how to create and entry and then bind the enter key so that when I run my app, I can click in the entry, type a value and press enter.
I see plenty of button references and I can get the button to work, but I am trying to learn how to do things and do not want to rely on buttons in this instance.
I saw in some other posts that if you call .get with an entry object, that the python code will just execute it and move on. I tested with a print statement in the function I want to call upon pressing enter, and the print statement appeared in the terminal before I typed anything in the entry widget. I then tried to type and press enter, and nothing would occur.
Should I abandon binding the ENTER key and stick with buttons in tkinter as a rule, or is there a proper way to do this? In my code example, you will see up_R is the function I am trying to execute when pressing Enter. If I use up_R(), it executes immediately. If I use up_R, then I get a TCL Error.
Specific Partial code located below:
def up_R():
print('Makes it here')
self.R.update_disp(self.e.get())
self.e.bind('<ENTER>',up_R)
The full code is below if required for assistance:
#NOAA SPACE WEATHER CONDITIONS
from tkinter import *
class window:
def __init__(self):
#main window
self.window = Tk()
self.window.title('NOAA SPACE WEATHER CONDITIONS')
self.window.geometry('800x600')
#window organization
self.window.grid_rowconfigure(0, weight = 1)
self.window.grid_rowconfigure(1, weight = 1)
self.window.grid_columnconfigure(0, weight = 1)
self.window.grid_columnconfigure(1, weight = 1)
#temp entry frame
self.e = Entry(self.window)
self.e.grid(row = 1, column = 0, sticky=N)
self.e.insert(END, 'R entry')
#init class R
self.R = R()
#init class S
self.S = S()
#init class g
self.G = G()
#frame for RSG
self.frame = Frame(self.window)
self.frame.grid(row = 0, column = 0, columnspan = 2, padx=10, pady=10)
#disp class R
self.rf = Frame(self.frame, highlightbackground='black', highlightcolor='black', highlightthickness=1)
self.rf.pack(side = LEFT)
self.rl = Label(self.rf, text = self.R.dkey, bg='#caf57a')
self.rl.pack(side=TOP)
self.rl_lower = Label(self.rf, text= self.R.tile_text, bg='#caf57a')
self.rl.pack(side=BOTTOM)
#Value update methods
# self.R.update_disp(self.e.get())
# #action
def up_R():
print('Makes it here')
self.R.update_disp(self.e.get())
self.e.bind('<ENTER>',up_R())
#main window call - goes at end of class
self.window.mainloop()
class R:
def __init__(self):
d = {'R':'None','R1':'Minor','R2':'Moderate','R3':'Strong','R4':'Severe','R5':'Extreme'}
self.dkey = 'R'
self.tile_text = d[self.dkey]
print(d[self.dkey])
def update_disp(self, dkey):
self.dkey = dkey
class S:
d = {'S1':'Minor','S2':'Moderate','S3':'Strong','S4':'Severe','S5':'Extreme'}
pass
class G:
d = {'G1':'Minor','G2':'Moderate','G3':'Strong','G4':'Severe','G5':'Extreme'}
pass
t = window()
The ENTER should be changed with Return, and the function should accept an event
Also, don't forget in a 'class' to use self in the method and self.method to call it.
def up_R(self, event):
print('Makes it here')
self.R.update_disp(self.e.get())
self.rl.config(text=self.R.dkey)
self.e.bind('<Return>', self.up_R)

Buiding a BackSpace and Enter function in custom Python tkinter program

I'm building a simulator program on python 3 Tkinter window. The progress is now disrupt due to a bug in the input program I build myself.
The broken code that I'm working on right now:
from tkinter import *
root = Tk()
prompt = ' Press any key '
label1 = Label(root, text=prompt, bg='black', fg='green')
label1.grid(row=0, column=0)
prompt2=''
def key(event):
global prompt2
if event.char == event.keysym:
prompt2 = prompt2 + event.char
elif len(event.char) == 1:
prompt2 = prompt2 + event.char
else:
pass
label1.config(text=prompt2)
root.bind_all('<Key>', key)
root.update()
root.mainloop()
So, what I want to do (and trying to do) is:
Build a function that allow the user to use BackSpace and stop them from using Enter (as I don't want them to move to a new line).
Set a variable to True, give the program a signal for when to start (taking input) and when to stop.
But I can't seem to do this. Any suggestions for 1 or 2? Please tell me in the comment section.
Thanks in advance!
Why are you comparing event.char to event.keysym? That will always be True for printable characters, and always be False for character sequences and special characters (like ⇦ and ↲).
How about this (untested)?
import string
import tkinter as tk
# See side note 1
def key_event(label, event):
label_text = label.cget("text")
if event.keysym == "backspace":
label.config(text=label_text[:-1])
elif event.keysym == "return":
# Disallow 'return'
pass
elif event.char in string.ascii_lowercase:
# Character is an ASCII letter
label.config(text=label_text + event.char)
def main():
root = tk.Tk()
prompt = " Press any key "
label = tk.Label(root, text=prompt, bg="black", fg="green")
label.grid(row=0, column=0)
label.bind("<Key>", lambda e: key_event(label, e))
# See side note 2
label.focus()
# Give keyboard focus
root.mainloop()
Side note 1: avoid using wildcard imports (from ... import *). Instead, alias tkinter 'as' tk:
import tkinter as tk
root = tk.Tk()
...
Side note 2: avoid using global variables, prefer using a class (provided you know how to use them properly, see below section for an example), or for really small pieces of code, share variables using lambdas like I did.
import string
import tkinter as tk
class EditableLabel(tk.Label):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.bind("<Key>", self._key_event)
def _key_event(self, event):
keysym, char = event.keysym, event.char
text = self.cget("text")
if keysym == "backspace":
self.config(text=text[:-1])
elif keysym == "return":
pass
elif char in string.ascii_lowercase + string.digits:
self.config(text=text + char)
class MyApplication(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._add_and_draw_widgets()
def _add_and_draw_widgets(self):
for i in range(3):
EditableLabel(self, text="Label #{}".format(i)).grid(column=0, row=i)
def main():
root = tk.Tk()
app = MyApplication(root)
app.grid(column=0, row=0)
root.mainloop()
if __name__ == "__main__":
main()

Update tkinter canvas when button is clicked

I want to update the name of the child when you press one of the buttons below. With this lambda function, I always get the last name no matter which button is pressed. Is it possible to fix it using lambda functions? As far as I have seen lambda functions work well with define values but I have not seen any examples with variables. How can I fix it? Thanks,
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------------------------------
__title__= 'Control'
__date__ = '10/07/2017'
__version__ = '0.0.1'
from tkinter import *
from tkinter import ttk
class Panel():
def __init__(self, database, name):
self.root = Tk()
self.root. title('Control V' + __version__)
self.root.configure(bg='beige')
self.window = ttk.Frame(self.root, borderwidth=2, relief="raised",
padding=(10,10))
self.window.grid(column=0, row=0)
self.window.option_add("*Font", "Helvetica 12")
# Row 0
self.header = ttk.Label(self.window, text=name, padding=(1,1),
anchor='center')
self.header.grid(row=0, column=0, columnspan=3)
# Row 1
for col, record in enumerate(database, 0):
name = database[col]['Letter']
label = ttk.Button(self.window, text=name,
command=lambda *args: self.get_name(col, database))
label.grid(row=1, column=(col), sticky='ew')
# All set
self.root.mainloop()
def get_name(self, row, database):
name = database[row]['Name']
self.header.configure(text=name)
def main():
database = [
{'Letter': 'A', 'Name': 'Zara'},
{'Letter': 'B', 'Name': 'Ezra'},
{'Letter': 'C', 'Name': 'Amy'},
]
name = database[0]['Name']
my_panel = Panel(database, name)
return 0
if __name__ == '__main__':
main()
If you change get_name to a curried function:
def get_name(row, database):
name = database[row]['Name']
def _get_name():
self.header.configure(text=name)
return _get_name
and replace the command declaration with this returned function:
label = ttk.Button(self.window, text=name,
command=self.get_name(col, database))
the result is as expected.
(What this does is define the return value of the button click when you create the button, rather than trying to pass in a parameter later. Your original code had all the buttons depend on 'col' (the variable). This remains the last thing it was set to (2->Amy), which explains why you were always getting that 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