I am new in Python programming and I am learning how to create user interfaces. I would like to create a very basic interface which has the following flow: using a while loop, the interface shows all the questions included into a list of questions. Each time a question is presented, two button (Yes-No) appears under the question. Only when one of them is clicked, the interface will show the next questions.
I attach here the code I tried.
import tkinter as tk
questions=['Question 1','Question 2','Question 3','Question 4', 'Question 5']
root = tk.Tk()
root.minsize(300, 300)
answers=['Yes','No']
b_list = []
def ask():
count=0
while count<len(questions):
lab=tk.Label(root,text=questions[count])
lab.pack()
count+=1
for i in range(2):
b = tk.Button(root, text = answers[i],command=ask)
b.grid(row = 0, column = i)
b_list.append(b)
root.mainloop()
Such code doesn't work at all. I guess also that I made a mistake into the while loop, asking to display all the questions, instead of one at once. Any idea to make such code working?
Thanks for your time!!
There are two main reasons enabling your code not to run:
In one script you can't write GUI with two or more geometry managers. This is not only for tkinter but also for other GUI third party library like PyQt.
The label should display different messages or questions in each round. So it means it's necessary for you to modify the content with StringVar. It behaved as a variable of string. You can learn more here
I don't have a clear idea of why you wanna store button and the place where you save the result made by users.
Here is my code:
import tkinter as tk
questions = ['Question 1', 'Question 2',
'Question 3', 'Question 4', 'Question 5']
answers = ['Yes', 'No']
root = tk.Tk()
root.minsize(300, 300)
def ask():
if questions:
lab_text.set(questions.pop(0))
for index, answer in enumerate(answers):
lab_text = tk.StringVar()
lab = tk.Label(root, textvariable=lab_text)
lab.grid(row=0, column=0)
b = tk.Button(root, text=answer, command=ask)
b.grid(row=1, column=index)
#initialize label
ask()
root.mainloop()
This can also be done in an object orientated manner which may hold up better for future proofing.
Please see my commented version of the script below for an explanation and example:
from tkinter import *
import random
class App:
def __init__(self, root):
self.root = root
self.array = ["Question1", "Question2", "Question3", "Question4", "Question5"] #list containing questions
self.answer = [] #empty list for storing answers
self.question = Label(self.root, text=self.array[len(self.answer)]) #creates a text label using the element in the 0th position of the question list
self.yes = Button(self.root, text="Yes", command=self.yescmd) #creates button which calls yescmd
self.no = Button(self.root, text="No", command=self.nocmd) #creates button which calles nocmd
self.question.pack()
self.yes.pack()
self.no.pack()
def yescmd(self):
self.answer.append("yes") #adds yes to the answer list
self.command() #calls command
def nocmd(self):
self.answer.append("no") #adds no to the answer list
self.command() #calls command
def command(self):
if len(self.answer) == len(self.array): #checks if number of answers equals number of questions
self.root.destroy() #destroys window
print(self.answer) #prints answers
else:
self.question.configure(text=self.array[len(self.answer)]) #updates the text value of the question label to be the next question
root = Tk()
App(root)
root.mainloop()
This is essentially only different in the sense that we're just configuring the label to show the next element in the questions list rather than destroying it or popping the list.
Related
I tried creating a program that will take in the symptoms of a person and return the disease they have. This is the GUI part of the project.
from tkinter import *
root = Tk()
root.title("Health GUI")
root.geometry("1000x625")
symptoms_list = []
def print_symptoms():
print(symptoms_list)
def typeSymptoms():
gap3 = Label(text="").pack()
symptoms_entry = Text(width=50, height=20)
symptoms_entry.pack()
symptoms_list.append(symptoms_entry.get(1.0, END))
done_symptoms = Button(text="I have written my symptoms", width=25, height=5, command=lol)
done_symptoms.pack()
gap1 = Label(text="").pack()
title = Label(text="HEALTH GUI", font=30).pack()
gap2 = Label(text="").pack()
start_button = Button(text="Click here to start", width=30, height=5, command=typeSymptoms, font=20).pack()
root.mainloop()
Just for simplicity, I tried printing out the symptoms given by the user to the console but it gives me a list with '\n'. Please help. Thanks!(PS: I lerned Tkinter day before yesterday so I don't know much)
At the moment, your variable symptoms_list just holds the contents of the newly created Text widget, since you append this content at startup.
If you want to add the symptoms to the list, you need to have your function lol() that you call when pressing the button.
This function should look something like:
def lol():
symptoms_text = symptoms_entry.get(1.0, END)
symptoms_list = symptoms_text.split('\n')
print_symptoms()
However, your widgets and the symptoms_list would have to be global variables in order for this program to work. It would probably be better, while you are getting acquainted with Tkinter, to learn how to create a dialog as Class with attributes. That makes sharing values between methods so much easier.
I am currently trying to make a GUI to an existing python program using Tkinter. The program gives the user two options from which the user must choose to either accept or decline. Before using Tkinter the options were placed in the terminal and awaited for a raw_input. (y/n). How can I make this so the canvas text updates with the new data and awaits for the users button click?
To make my question more specific: How can I run another programs code while the Tkinter mainloop is running and make these two interact?
Example code below.
from Tkinter import *
root = Tk()
root.resizable(width=False, height=False)
root.geometry('{}x{}'.format(500,550))
root.wm_title("Tkinter test")
BtnFrame = Frame (root)
BtnFrame.pack(side = BOTTOM)
BtnFrame.place(y=450, x=20)
canvas_1 = Canvas(root, width = "200", height ="300")
canvas_2 = Canvas(root, width = "250", height ="300")
canvas_1.pack(side = LEFT)
canvas_2.pack(side = RIGHT)
textfield_1 = canvas_1.create_text(100,50)
textfield_2 = canvas_2.create_text(100,50,)
def update_textfiel_1(text):
global textfield_1
canvas_1.delete(textfield_1)
textfield = canvas.create_text(100,50,text = text)
def update_textfiel_2(text):
global textfield_2
canvas_2.delete(textfield_2)
textfield1 = canvas1.create_text(100,50,text = text)
Accept = Button(BtnFrame, text="Accept", width=25)
Decline = Button(BtnFrame, text="Decline", width=25)
Accept.pack(side = LEFT)
Decline.pack(side = RIGHT)
root.mainloop()
First off you have some inconsistent variable names in your update_textfiel functions, you can greatly simplify it by using .itemconfigure (documentation for methods on canvas widget)
def update_textfiel_1(new_text):
canvas_1.itemconfigure(textfield_1, text=new_text)
def update_textfiel_2(new_text):
canvas_2.itemconfigure(textfield_2, text=new_text)
If I understand correctly you want a way to have a function that will simply wait for the user to press one of the buttons and then return the result, this is very easy with tkMessageBox:
question = """Do you accept {}?
if you say no you will instead get {}"""
#this message can GREATLY be improved
# But I really don't understand what you are using it for...
def user_confirmation(name1, name2):
response = tkMessageBox.askyesno("Accept or Decline",question.format(name1,name2))
print(response)
if response: # == True
return name1
else:
return name2
I have not yet found a way to make a blocking function that works with the window you have currently...
I am working on a group project where we must create a simple program and we have chosen to make a multiple choice game using Tkinter. We have constructed most of the game already, but are having a problem when keeping a count of the correct answers. We are using Radiobuttons to list the answers for each question, however if the user clicks the button more than once it keeps incrementing the count as many times as they click it. Here is the code that we have. Please excuse the messiness, as we have not quite gone through to clean it up, as well as it is a bit of a beginner project and we are not the most experienced group of programmers.
(I am purposefully not including the complete code because the file paths for the images we have are directly linked to the home computer so they would not be able to be used anyways)
root = Tk()
counter = 0
d = ''
var = StringVar()
def next():
global i,img,groups,listanswer, questions, randint, key,d, counter
s = randint(1,4)
key = random.choice(list(questions.keys()))
img = ImageTk.PhotoImage(Image.open(key))
panel = Label(root, image = img)
panel.grid(column=0, row=0)
b = []
c = listanswer.index(str(questions.get(key)))
d = listanswer[c]
b.append(d)
listanswer.remove(d)
def selection():
global counter, d, sel
sel = str(var.get())
if sel == d:
counter +=1
i=1
while i<5:
a=random.choice(listanswer)
b.append(a)
if s!=i:
Radiobutton(root, text=a, padx=20,variable=var,
value=a,command=selection).grid(column=0, row=i)
listanswer.remove(a)
i+=1
R1 = Radiobutton(root, text=d, padx=20,variable=var, value=d,command =
selection).grid(column=0, row=s)
listanswer=listanswer+b
questions.pop(key)
counterlabel.configure(text='%g' %counter)
counterlabel=Label(root,width=8)
counterlabel.grid(column=1, row=5)
counterval=Label(root, width=10, text='Correct:')
counterval.grid(column=0,row=5)
next=Button(root,text='next',command=next)
next.grid(column=2, row=2)
var = IntVar()
label = Label(root)
label.grid()
root.mainloop()
If I understood the correctly every time you click on the radiobutton the code will check if that answer is correct. If it is, you increase the counter.
Instead, I recommend checking all answers when any of the radiobuttons is clicked, and set the counter accordingly (i.e. the counter resets every time you click).
Hopefully this helps!
I am a newbie at programming and my program is not stellar but hopefully it's ok because I have only been using it for a couple days now.
I am having trouble in my class "Recipie". In this class I am having trouble saving the text in my Entry widget. I know to use the .get() option but when I try to print it, it doesn't (whether it is within that defined method or not). So that is my main concern. I want it to save the text entered as a string when I press the button: b.
My other minor question is, how can I move the label. When I have tried I have used the height and width options, but that just expands the label. I want to move the text to create a title above my Entry boxes. Is label the right widget to use or would it be easier to use a message box widget? So it would look like, for example (but like 8 pixels down and 20 to the right):
ingredients
textbox
button labeled as: add an ingredient
And I am not sure the option .pack(side="...") or .place(anchor="...") are the right options to use for my buttons or entry boxes or labels.
And if you could add comments to your code explaining what you did, that would be so helpful.
import Tkinter
class Cookbook(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.title("Cookbook")
self.geometry("500x500+0+0")
self.button = []
for r in range(1):
for c in range(1):
b = Button(self).grid(row=r,column=c)
self.button.append(b)
class Button(Tkinter.Button):
def __init__(self,parent):
b = Tkinter.Button.__init__(self, parent, text="Add A New Recipie", height=8, width=15, command=self.make_window)
def make_window(self):
popwindow = Recipie()
popwindow.name()
popwindow.ingredients()
popwindow.addingredient()
popwindow.savebutton()
popwindow.save()
class Recipie(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.title("New Recipie")
self.geometry("500x500")
def name(self):
name = Tkinter.Label(self, text="Title:")
name.pack() #used to be name.place(anchor="nw")
self.insert_name = Tkinter.Entry(self) #edited with the answer below, used to be insert_name = Tkinter.Entry(self)
self.insert_name.pack() #edited with the answer from below, used to be insert_name.pack()
self.insert_name.focus_set() #edited with the answer from below, used to be insert_name.focus_set()
def ingredients(self):
self.e = Tkinter.Entry(self) #edited with the answer from below, used to be e.get()
self.e.pack() #edited with the answer from below, used to be e.pack()
self.e.focus_set() #edited with the answer from below, used to be e.focus_set()
def addingredient(self):
but = Tkinter.Button(self, text="Add Ingredients", width=15, command=self.ingredients)
but.pack(side="bottom")
def procedure(self):
txt = Tkinter.Label(self, text="List the Steps:")
txt.place(anchor="n")
self.p = Tkinter.Entry(self) #edited with the answer from below, used to be p = Tkinter.Entry(self)
self.p.place(anchor="nw") #edited with the answer from below, used to be p.place(anchor="nw")
self.p.focus_set() #edited with the answer from below, used to be p.focus_set
def savebutton(self):
print self.insert_name.get() #edited with the answer from below
print self.e.get() #edited with the answer from below
print self.p.get() #edited with the answer from below
def save(self):
b = Tkinter.Button(self, text="Save Recipie", width=15,command=self.savebutton)
b.pack()
top = Cookbook()
top.mainloop()
Part 1...
You are currently defining your entry widget as a local variable inside the ingredients method (i.e. these are variables that only exist inside the method). To maintain a reference to the entry widget you are creating, you can assign it as an instance attribute to your Recipie object.
i.e.
e = Tkinter.Entry(self)
e.pack()
e.focus_set()
becomes
self.e = Tkinter.Entry(self)
self.e.pack()
self.e.focus_set()
and
print e.get()
becomes
print self.e.get()
Some required reading before you continue with Python:
Python classes
Explaining 'self' in Python
Part 2...
So in answer to the second part of the question on how to position your labels. It just looks like you need to alter the way in which you are packing widgets. An example of how to pack entry widgets is a clean way (emulating the functionality of your example) would be:
import Tkinter as tk
class IngredientAdder(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.geometry("500x500+0+0")
self.init_gui()
# function to add new ingredients
def add_ingredient_entry(self):
entry = tk.Entry(self)
entry.pack(side=tk.TOP)
self.ingredient_entries.append(entry)
# get contents of all entry boxes
def save_recipe(self):
for ingredient in self.ingredient_entries:
print ingredient.get()
print "[Recipe saved]"
# build initial widgets
def init_gui(self):
# this is a list of ingredients entry boxes
self.ingredient_entries = []
# put a label at the top of the window and anchor it there
tk.Label(self,text="Ingredients").pack(anchor=tk.N,side=tk.TOP)
# Put these two buttons at the bottom of the window and anchor them there
tk.Button(self,text="Save recipe",command=self.save_recipe).pack(anchor=tk.S,side=tk.BOTTOM)
tk.Button(self,text="Add ingredient",command=self.add_ingredient_entry).pack(anchor=tk.S,side=tk.BOTTOM)
# new ingredients will be added between the label and the buttons
self.add_ingredient_entry()
cookbook = IngredientAdder()
cookbook.mainloop()
I'm trying to create a program in python using tkinter, and this program is supposed to have a list of books created by the user. On the main window (the one with the list), there should be a menubar with the option to add a book to the list. When clicked, this option should open another window, this time with one entrybox, where the user should enter the book's title and an add button, to add the button to the list.
The list is saved in a .txt file.
This is the program I wrote so far:
import sys
from tkinter import *
def newBook():
def add():
BookTitle = v.get()
bookTitle = '\n' + BookTitle
books = open('c:/digitalLibrary/books.txt', 'a')
books.write(bookTitle)
books.close()
addWindow = Tk()
v = StringVar()
addWindow.geometry('250x40+500+100')
addWindow.title('digitalLibrary - Add Book')
newBookEntry = Entry(addWindow,textvariable=v)
newBookEntry.pack()
addButton = Button(addWindow, text='ADD', command=add)
addButton.pack()
def refresh():
books = open('c:/digitalLibrary/books.txt', 'r')
bookList = books.readlines()
books.close()
for i in range (0, len(bookList)):
bookOne = Label(text=bookList[i])
bookOne.grid(row=i, column=0, sticky=W)
def quitProgram():
tfQuit = messagebox.askyesno(title='Close Program', message='Are you sure?')
if tfQuit:
window.destroy()
window = Tk()
menubar = Menu(window)
window.geometry('400x400+200+100')
window.title('digitalLibrary')
booksmenu = Menu(menubar, tearoff=0)
booksmenu.add_command(label='Add Book', command=newBook)
booksmenu.add_command(label='Delete Book')
booksmenu.add_command(label='Close Program', command=quitProgram)
menubar.add_cascade(label='digitalLibrary', menu=booksmenu)
books = open('c:/digitalLibrary/books.txt', 'r')
bookList = books.readlines()
books.close()
for i in range (0, len(bookList)):
bookOne = Label(window, text=bookList[i])
bookOne.grid(row=i, column=0, sticky=W)
refreshButton = Button(window, text='Refresh', command=refresh)
refreshButton.grid(row=0, column=1)
window.config(menu=menubar)
window.mainloop()
It seems logical to me that this should work, but it just doesn't. When I click the ADD button on the Add Book window, all it does is add the line break to the .txt file.
I know that it works if I use the OS library and create a separate python file for the add book window, but I'd rather put it all in one code, if possible.
I've tried many things, and tried searching it in the web, but I got nowhere.
The root cause of your problem is that you are creating more than once instance of Tk. You cannot do this. If you want to create a popup window, create an instance of Toplevel. A proper Tkinter application creates exactly once instance of Tk with exactly one invocation of mainloop.
If your main goal is to simply get input from the user (versus learning how to write your own dialog), you might want to consider using one of the built-in dialogs.
For example:
import tkinter.simpledialog as tkSimpleDialog # python 3.x
...
def newBook():
BookTitle = tkSimpleDialog.askstring("Add Book","What is the name of the book?")
if BookTitle is not None:
bookTitle = '\n' + BookTitle
books = open('/tmp/books.txt', 'a')
books.write(bookTitle)
books.close()