Code using Tkinter works in IDLE but not in terminal - python-3.x

This is (part of) my very first program. It uses Tkinter to output text in a scrolled textbox. I used Python 3.6.4 and IDLE and it works perfectly, but when I run it from terminal/Atom once I click ok after selecting the options from the dropdown menus it just closes without errors, while in IDLE it correctly outputs all the text in the textbox.
I want to use Py2app to make a standalone, but for this the code needs to execute properly from terminal.
Here are the main snippets from the code. I'm just coding for a few months so any detailed help would be much appreciated.
from tkinter import *
from collections import OrderedDict
from tkinter.scrolledtext import *
from collections import Counter
master = Tk()
master.title("App")
master.geometry("600x665")
master.lift()
master.attributes('-topmost', True)
mvar = IntVar()
mvar1 = IntVar()
var = StringVar(master)
var.set("Asc")
var1 = StringVar(master)
var1.set("Ar")
x = OptionMenu(master, var, "Ar", "Ta", "Ge","Can","Le","Vi","Li","Sc","Sa","Cap","Aq","Pi")
x.grid(column =2,row =1)
x1 = OptionMenu(master, var1, "Ar", "Ta", "Ge","Can","Le","Vi","Li","Sc","Sa","Cap","Aq","Pi")
x1.grid(column =2,row =2)
def redirector(inputStr):
txt.insert(INSERT, inputStr)
sys.stdout.write = redirector
def ok():
redirector("Thanks for using the app")
master.quit()
label1 = Label(text=" Welcome to the app",bg="#C2DFFF",font=("Times New Roman",18))
label1.grid(column=0,row=0)
label2 = Label(text="Ma: ",bg="#C2DFFF")
label2.grid(column=0,row=2)
txt = ScrolledText(master, bg="#C2DFFF", width = 97, height= 25, font = "Arial 11")
txt.grid(column = 0, row = 14, columnspan=3)
button = Button(master, text="OK", default ='active',command=ok).grid(column=2,row=11)
button = Button(master, text="Reset", default ='active',command=reset).grid(column=2,row=12)
button = Button(master, text ="Cancel",command = cancel).grid(column=0,row=11)
C1 = Checkbutton(master, state = ACTIVE, variable = mvar)
C1.grid(column = 1, row=2)
C2 = Checkbutton(master, state = ACTIVE, variable = mvar1)
C2.grid(column = 1, row=3)
master.mainloop()
This is how the GUI looks like

You cannot generally reassign sys.stdout.write - it is normally a read-only attribute of a built-in file object. The proper way to do output redirection is to assign a new object to sys.stdout, that has a write() method.
Your code works in IDLE only because IDLE has already replaced the built-in sys.stdout with its own redirection object, which is fully modifiable.

Related

tkinter - how to make separate hyperlink button in my loop?

I'm trying to make some simple program to open url if the condition fits.
Here's the minimal reproducible example.
from tkinter import *
import requests
from bs4 import BeautifulSoup
import webbrowser
import time
def call_back(event):
input_id.delete(0,END)
return None
def open_browse(url):
webbrowser.open_new(url)
win = Tk()
win.geometry("150x150")
win.title("Example")
search_btn = Button(win)
search_btn.config(text="search")
search_btn.config(width=5,height=1)
search_btn.grid(column = 2, row = 2)
def search_chr():
chr_list = ["test1","test2"]
result = [(0,"test_r_1"),(0,"test_r_2")]
var_dict = {}
num = -1
for ch in chr_list:
num += 1
var_dict["output%s" %num] = Entry(win, width = 10)
if result[0] == 0:
pass
else:
link_url = result[num][1]
print(link_url)
var_dict["o-button%s" %num] = Button(win, command=lambda aurl=link_url:open_browse(link_url))
var_dict["output"+str(num)].insert(0, "Text")
var_dict["output"+str(num)].grid(column = 0, row = 0+num, columnspan = 4, sticky=W, padx=5, pady=5)
var_dict["o-button"+str(num)].config(text="URL")
var_dict["o-button"+str(num)].grid(column = 4, row = 0+num, sticky=E, padx=5, pady=5)
var_dict["output"+str(num)].config(state="disabled")
search_btn.config(command = search_chr)
win.mainloop()
So, if you run the code, there would be a button.
And if you click it, There will be two sets of Label with "Text" in it and Button with "URL" in it. When you press the URL button, it should open a browse with a given url.
As you see the printed text in your terminal, the url is supposed to be "test_r_1" and "test_r_2"
But, if you press each button, all buttons are directed to "test_r_2".
It seems that it somehow overwrote the previous "test_r_1" as well.
If anyone can explain how to make each button to link to each url, it would be perfect.
Thanks for stopping by, and I hope you can help me with this.
Okay, I tracked down that I didn't fully understand that how lambda works.
So I changed my search keyword and found this beautiful question and answer.
Tkinter assign button command in loop with lambda
I changed
var_dict["o-button%s" %num] = Button(win, command=lambda aurl=link_url:open_browse(link_url))
into
var_dict["o-button%s" %num] = Button(win, command=lambda link_url=link_url:open_browse(link_url))
and it worked.
Thanks all!

Getting an error while inserting values from the tkinter GUi to the database

Getting an error while inserting values from the tkinter GUi to the sqlite3 database.
Taking input from the user
By the tone of your question, it seems like you are having trouble accessing what is inside the text box.
An answer would be the .get() method. This basically allows you to access what is inside the text box.
Here is a simple code:
from tkinter import *
window = Tk()
window.title("Example")
window.geometry("500x500")
window.configure(bg = "sky blue")
e = Entry(window, bg = "blue", fg = "orange")
e.pack()
def com1():
acess = e.get()
print(acess)
button1 = Button(window, text = "enter", command = com1)
button1.pack()
The e.get() is what takes the stuff inside the Entry widget.
You save it in a variable, then use the variable for whatever you want.
Hope this helps!!!

A reset button for tkinter entries, looped inside a list

I've successfully looped the textvariable for all the entries created to point to DoubleVar(), and its working properly. The problem arose when i tried creating reset button for all the entries. from my code as shown, the program runs, doesn't raise any error, and the values in the entries are not cleared. thanks in advance :)
from tkinter import*
root = Tk()
img = PhotoImage(file = 'background.png')
cc = DoubleVar()
cc.set('##')
dr =Label(root, text='helo world')
sd = []
y = -1
dr.pack()
Entry(root, textvariable =cc).pack()
def clear():
cc.set('')
for i in sd:
i['textvariable'] = DoubleVar().set('')
def create():
global y
y +=1
sd.append(Entry(root, width =5))
for i in sd:
i["textvariable"] = DoubleVar()
sd[y].pack()
Button(root, text = 'push', command = clear).pack()
Button(root, text = 'create', command = create).pack()
root.mainloop()
`
Your reset code is creating new DoubleVars, and setting them to the empty string. You're doing nothing to the original variables.
You don't need to use the variables for this, you can simply call the delete method on each entry widget:
for entry in sd:
entry.delete(0, "end")

iterate through a list and get user response to each item using tkinter GUI

I am being particularly obtuse. I am iterating through a list of technical italian words and wanting to insert a translation using a tkinter interface. There is no problem doing this without the GUI: My problem is that I cannot figure out how to do an iteration, load a word into a ttk.Label and wait for a user entry in a ttk.Entry field. I have searched and found explanations, but I am at a loss how to apply the suggestions. This is my code using a trivial list of words:
from tkinter import ttk
import tkinter as tk
def formd():
list_of_terms = ['aardvark', 'ant','zombie', 'cat', 'dog', 'buffalo','eagle', 'owl','caterpiller', 'zebra', 'orchid','arum lily' ]
discard_list = []
temp_dict={}
list_of_terms.sort()
for item in list_of_terms:
listKey.set(item)
# need to wait and get user input
translation =dictValue.get()
temp_dict[item]=translation
discard_list.append(item)
# check if it has worked
for key, value in temp_dict.items():
print(key, value)
# GUI for dict from list
LARGE_FONT= ("Comic sans MS", 12)
root = tk.Tk()
root.title('Nautical Term Bilingual Dictionary')
ttk.Style().configure("mybtn.TButton", font = ('Comic sans MS','12'), padding = 1, foreground = 'DodgerBlue4')
ttk.Style().configure('red.TButton', foreground='red', padding=6, font=('Comic sans MS',' 10'))
ttk.Style().configure('action.TLabelframe', foreground = 'dodger blue3')
#.......contents frames.....................
nb = ttk.Notebook(root)
page5 = ttk.Frame(nb)
# declare variables
listKey= tk.StringVar()
dictValue = tk.StringVar()
# widgets
keyLabel =ttk.Label( page5, text = "key from list", font=LARGE_FONT).grid(row=3, column = 0)
Keyfromlist =ttk.Label(page5, width = 10, textvariable = listKey).grid(row = 3, column = 1)
valueLabel =ttk.Label( page5, text = "enter translation", font=LARGE_FONT).grid(row=3, column = 2)
listValue =ttk.Entry(page5, textvariable =dictValue, width = 15).grid(row = 3, column = 3)
#listValue.delete(0,'end')
#listValue.focus_set()
# add buttons
b1 = ttk.Button(page5, text="add to dictionary",style = "mybtn.TButton", command = formd)
b1.grid(row = 5, column = 0)
b5 = ttk.Button(page5, text="clear entry", style ="mybtn.TButton")
b5.grid(row = 5, column = 2)
nb.add(page5, text='From List')
nb.pack(expand=1, fill="both")
for child in root.winfo_children():
child.grid_configure(padx =5, pady=5)
if __name__ == "__main__":
root.mainloop()
I wonder whether someone could take the time to suggest a solution, please. How to stop a while loop to get input from user using Tkinter? was the one suggestion that I cannot figure how to use in my example
tkinter doesn't "play nice" with while loops.
Fortunately for you, you don't need to use one.
You can do something like the below:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.list = ['aardvark', 'ant','zombie', 'cat', 'dog', 'buffalo','eagle', 'owl','caterpiller', 'zebra', 'orchid','arum lily' ]
self.text = Label(self.root, text="aardvark")
self.entry = Entry(self.root)
self.button = Button(self.root, text="Ok", command=self.command)
self.text.pack()
self.entry.pack()
self.button.pack()
def command(self):
print(self.text.cget("text")+" - "+self.entry.get())
try:
self.text.configure(text = self.list[self.list.index(self.text.cget("text"))+1])
except IndexError:
self.entry.destroy()
self.button.destroy()
self.text.configure(text = "You have completed the test")
root = Tk()
App(root)
root.mainloop()
This essentially uses the Button widget to iterate to the next text and get the next input.

How to get text from Entry widget

I've looked at several posts on stackOverflow that explain the answer but no matter which I use, I never can get the string from my entry widget; it just detects a string of ""
here's my code:
def buttonTest():
global score
gui.title("Test")
for child in gui.winfo_children():
child.destroy()
global questionText
global questionAnswer
questionText = StringVar()
questionAnswer = 0
question = Label(gui, textvariable = questionText, fg = "black", bg = "white")
question.grid(row = 0, column = 1)
userInput = StringVar()
input = Entry(gui, textvariable = userInput)
input.grid(row = 1, column = 0)
swapQuestion()
checkAns = Button(text = "Check answer", command = partial(checkAnswer, userInput.get(), questionAnswer), fg = "black", width=10)
checkAns.grid(row = 1, column = 2)
Please read and follow this SO help page. Your code is missing lines needed to run and has lines that are extraneous to your question. It is also missing indentation.
Your problem is that you call userInput.get() just once, while creating the button, before the user can enter anything. At that time, its value is indeed ''. You must call it within the button command function, which is called each time the button is pressed.
Here is a minimal complete example that both runs and works.
import tkinter as tk
root = tk.Tk()
user_input = tk.StringVar(root)
answer = 3
def verify():
print(int(user_input.get()) == answer) # calling get() here!
entry = tk.Entry(root, textvariable=user_input)
entry.pack()
check = tk.Button(root, text='check 3', command=verify)
check.pack()
root.mainloop()
Simple example:
from tkinter import *
# Get Entry contents
def print_input():
print(input_variable.get())
window = Tk()
# Create widgets
input_variable = StringVar()
entry_variable = Entry(window, textvariable=input_variable).grid(row=0, column=0)
button_submit = Button(window, text="Submit",command=print_input).grid(row=1, column=0)
window.mainloop()
Where:
input_variable is your variable
entry_variable is the entry box
button_submit calls print_input() to fetch and print the entry_variable's contents which is stored in input_variable

Resources