How to solve an issue in a simple TR Tkinter program - python

Major programming noob here, and my very first question in stackoverflow. I'm trying to make a time reaction tester, with a simplistic Tkinter GUI.
Here's the code
#!/usr/bin/env python
import tkinter as tk
import time
import random
class TRTest(tk.Frame):
def __init__(self, master = None):
super().__init__()
self.grid()
self.canv()
self.createWidgets()
self.Test
self.totalt = []
def createWidgets(self):
self.quitbtn = tk.Button(self, text = "Quit", command=self.master.destroy)
self.quitbtn.grid(row = 3, column = 3)
self.label = tk.Label(self, text = "TRTest")
self.label.grid(row = 1, column = 3)
self.testbtn = tk.Button(self, text = "Start test", command = self.Test)
self.testbtn.grid(row = 1, column = 2)
def canv(self):
self.scrn = tk.Canvas(self, width = 400, height = 400, bg = "#E8E8E8")
self.scrn.grid(row = 2, column = 3)
def fixate(self):
self.scrn.create_line(190, 200, 210, 200, width = 2)
self.scrn.create_line(200, 190, 200, 210, width = 2)
def createCircle(self):
self.scrn.create_oval(150, 150, 250, 250, fill = "red", tag = "circle")
def Test(self):
if len(self.totalt) == 0:
self.fixate()
self.update()
self.bind_all("<Key>", self.gettime)
self.after(random.randint(1000, 5000))
self.t0 = time.clock()
self.createCircle()
def gettime(self, event):
t = time.clock() - self.t0
self.totalt.append(t)
self.scrn.delete("circle")
if len(self.totalt) < 6:
self.Test
else:
print(self.totalt)
a = tk.Tk()
b = TRTest(a)
b.mainloop()
It should be making 5 trials and returning the list of the measured TR times, but it freezes before showing a second circle. Where's the problem?

In addition to the answer from Patrick Haugh, you aren't using the .after method correctly.
Change this line of code
self.after(random.randint(1000, 5000))
to
self.after(random.randint(1000, 5000),self.Test)
and move it to after
if len(selftotalt) < 6:
This will call the self.Test function again after a random period of time between 1 and 5 seconds.
If more 6 or more reaction times have been captures it will no longer call the function again but print out the times.
Full code that seem to work for me
import tkinter as tk
import time
import random
class TRTest(tk.Frame):
def __init__(self, master = None):
super().__init__()
self.grid()
self.canv()
self.createWidgets()
self.totalt = []
def createWidgets(self):
self.quitbtn = tk.Button(self, text = "Quit", command=self.master.destroy)
self.quitbtn.grid(row = 3, column = 3)
self.label = tk.Label(self, text = "TRTest")
self.label.grid(row = 1, column = 3)
self.testbtn = tk.Button(self, text = "Start test", command = self.Test)
self.testbtn.grid(row = 1, column = 2)
def canv(self):
self.scrn = tk.Canvas(self, width = 400, height = 400, bg = "#E8E8E8")
self.scrn.grid(row = 2, column = 3)
def fixate(self):
self.scrn.create_line(190, 200, 210, 200, width = 2)
self.scrn.create_line(200, 190, 200, 210, width = 2)
def createCircle(self):
self.scrn.create_oval(150, 150, 250, 250, fill = "red", tag = "circle")
def Test(self):
if len(self.totalt) == 0:
self.fixate()
self.update()
self.bind_all("<Key>", self.gettime)
self.t0 = time.clock()
self.createCircle()
def gettime(self, event):
t = time.clock() - self.t0
self.totalt.append(t)
self.scrn.delete("circle")
if len(self.totalt) < 6:
self.after(random.randint(1000, 5000),self.Test)
else:
print(self.totalt)
a = tk.Tk()
b = TRTest(a)
b.mainloop()

I think you are trying to run the method Test with
self.Test
Try instead
self.Test()

Related

Open a new frame so it changes and displays a new page

I am trying to create a login page, when the user clicks the login button it is supposed to display a new page (not Toplevel, and create a new window), but it doesn't seem to work.
root = Tk()
root.geometry('670x466')
accounts = []
class Goode_brothers:
def __init__(self, parent):
self.myFrame = Frame(parent)
self.myFrame.pack()
self.load = Image.open('new-dip-project\\food.jpg')
self.render = ImageTk.PhotoImage(self.load)
self.img = Label(parent, image = self.render)
self.img.place(x = -26, y =0)
self.img_login = PhotoImage(file = 'new-dip-project\\button (3).png')
self.b1 = Button(parent,image = self.img_login, command = self.read_info, bd = 0, bg = '#3b353b', activebackground = '#3b353b')
self.b1.place(x = 275, y = 340)
self.img_register = PhotoImage(file = 'new-dip-project\\register.png')
self.b2 = Button(parent,image = self.img_register, command = self.openNewWindow, bd = 0, bg = '#3b353b', activebackground = '#3b353b')
self.b2.place(x = 265, y = 400)
self.canvas = Canvas(parent, width = 400, height = 120)
self.canvas.pack()
self.img4 = ImageTk.PhotoImage(Image.open('new-dip-project\\goode.png'))
self.canvas.create_image(20, 20, anchor=NW, image=self.img4)
self.email = Entry(parent)
self.email.place(x = 340, y = 180)
self.password = Entry(parent)
self.password.place(x = 354, y = 250)
self.img_label = PhotoImage(file = 'new-dip-project\\label-image.png')
self.name = Label(parent, image = self.img_label, text = "Email:", bg = '#3c3a3b').place(x = 197,y = 178)
self.img_label_pass = PhotoImage(file = 'new-dip-project\\label_pass.png')
self.name = Label(parent, image = self.img_label_pass, text = "Password:", bg = '#3c3a3b').place(x = 177,y = 245)
def openMenu(self):
self.myFrame.destroy
self.myFrame2 = Frame()
self.myFrame2.pack()
def read_info(self):
with open("emails.txt") as read_ep:
for line in read_ep:
accounts.append(line.strip().split(", "))
credential = [self.email.get(), self.password.get()]
if credential in accounts:
self.openMenu()
else:
self.ep_notexist = Label(root, text = "Your Email or Password is incorrect, Please try again", font=("open sans", "8"))
self.ep_notexist.place(x = 210, y = 300)
self.ep_notexist.after(4000, self.ep_notexist.destroy)
First destroy all the widgets placed of login page by using
for wid in root.winfo_children():
wid.destroy()
and then pack your new frame. It will work.
(if you don't want to destroy widgets you can also use wid.pack_forget() if you have used pack to place widgets)
You actually do not destroy the frame in the below line:
def openMenu(self):
self.myFrame.destroy # <--- frame is not destroyed actually
It should be:
def openMenu(self):
self.myFrame.destroy()

Delete an instance of the object

I'm trying to understand the class notion in Python and having decided to do a little exercise I found myself facing a problem.
What I'm trying to do is to create a circle (on a left-click) and then I expect the program to delete the circle (on a right-click).
Well, here comes the second part of my problem.
My code:
from tkinter import *
class Application:
def __init__(self):
self.fen = Tk()
self.fen.title('Rom-rom-roooooom')
self.butt1 = Button(self.fen, text = ' Quit ', command = self.fen.quit)
self.can1 = Canvas(self.fen, width = 300, height = 300, bg = 'ivory')
self.can1.grid(row = 1)
self.butt1.grid(row = 2)
self.fen.bind("<Button-1>", self.create_obj)
self.fen.bind("<Button-3>", self.delete_obj)
self.fen.mainloop()
def create_obj(self, event):
self.d = Oval()
self.can1.create_oval(self.d.x1, self.d.y1, self.d.x2, self.d.y2, fill='red', width = 2)
def delete_obj(self, event):
self.can1.delete(self.d)
class Oval:
def __init__(self):
self.x1 = 50
self.y1 = 50
self.x2 = 70
self.y2 = 70
appp = Application()
Here, the program understands that 'd' is an instance of class Oval, but it doesn't delete the object on a right click:
def delete_obj(self, event):
self.can1.delete(self.d)
From the tkinter docs, create_oval returns an object id, which is an integer. To remove the circle, use the Canvas.delete method:
from tkinter import *
import time
class Application:
def __init__(self):
self.fen = Tk()
self.fen.title('Rom-rom-roooooom')
self.butt1 = Button(self.fen, text = ' Quit ', command = self.fen.quit)
self.can1 = Canvas(self.fen, width = 300, height = 300, bg = 'ivory')
self.can1.grid(row = 1)
self.butt1.grid(row = 2)
self.fen.bind("<Button-1>", self.create_obj)
self.fen.mainloop()
def create_obj(self, event):
d = self.can1.create_oval(150,150, 170, 170, fill='red', width = 2)
time.sleep(3)
self.can1.delete(d)
appp = Application()

Radiobuttons and Checkbuttons not appearing in window

I'm writing a program that draws a rectangle or oval in a top frame depending on whether or not the user selects it via a radiobutton. There is a check button that determines whether the oval is filled as well. Both buttons are on the bottom frame. But for some reason when I run the code, it displays the window, but not the buttons themselves. How do I fix this?
Here's my code:
from tkinter import *
class GeometricFigures:
def __init__(self):
self.window = Tk()
self.window.title("Radiobuttons and Checkbuttons")
self.canvas = Canvas(self.window, width = 300, height = 100, bg = "white")
self.canvas.pack()
def drawButtons(self):
self.bottomframe = Frame(self.window)
self.bottomframe.pack()
self.check = IntVar()
cbtFilled = Checkbutton(self.bottomframe, variable = self.check, value = 0,
text = "Filled", command = self.processCheckbutton).pack(side = LEFT)
self.radio = IntVar()
rbRectangle = Radiobutton(self.bottomframe, variable = self.radio, value = 1,
text = "Rectangle", command = self.processRadiobutton.pack())
rbOval = Radiobutton(self.bottomframe, text = "Oval", variable = self.radio,
value = 2, command = self.processRadiobutton.pack())
cbtFilled.grid(row = 1, column = 2)
rbRectangle.grid(row = 1, column = 3)
rbOval.grid(row = 1, column = 4)
def processCheckbutton(self):
print("The check button is " +
("checked " if self.check.get() == 1 else "unchecked"))
def processRadiobutton(self):
print(("Rectangle" if self.radio.get() == 1 else "Oval")
+ " is selected ")
def drawRect(self):
self.canvas.create_rectangle(30, 10, 270, 60, tags = "rect")
def drawFillOval(self):
self.canvas.create_oval(30, 10, 270, 60, fill = 'blue', tags = "oval")
def drawOval(self):
self.canvas.create_oval(30, 10, 270, 60, tags = "oval")
def main(self):
test = GeometricFigures()
if self.check.get() == 1:
test.drawFillOval()
if self.radio.get() == 1:
test.drawRect()
else:
test.drawOval()
test.drawButtons()
if __name__ == '__main__':
main()
Thanks!

Using Tkinter to program an application need add buttons with same function. How to identify which button I clicked and change the line automatically?

The following things I want is to add multiple different tasks, and there is a task start button which is used to trig a timer.
class Pomodoro_app(Tk):
def add_task(self):
global time
time = StringVar()
time.set("Start")
task_content = askstring(title = 'Add a Task', prompt = "Input a task")
task_label = Label(self, text = task_content, font = ("Arial, 12")).grid(column = 0, row = 3)
task_start_button = Button(self, textvariable = time, command = self.start_working).grid(column = 1, row = 3)
def createWidgets(self):
self.welcome_label = Label(self, text = "Welcome to the Challenge!", font = ("Arial, 12")).grid(column = 0, row = 0, columnspan = 2)
self.add_task_button = Button(self, text = "Add Task", width = 20, command = self.add_task).grid(column = 0 , columnspan = 2)
def __init__(self):
"""GUI Initiation"""
Tk.__init__(self)
self.createWidgets()
"""window Initiation"""
self.resizable(False, False)
x = (self.winfo_screenwidth() - self.winfo_reqwidth()) / 2
y = (self.winfo_screenheight() - self.winfo_reqheight()) / 2
self.geometry('250x400+%d+%d' % (x, y))
Using lambda (or inner function), you can pass additional argument to callback.
For example:
class Pomodoro_app(Tk):
def add_task(self):
global time
time = StringVar()
time.set("Start")
task_content = askstring(title = 'Add a Task', prompt = "Input a task")
task_label = Label(self, text = task_content, font = ("Arial, 12")).grid(column = 0, row = self.next_row)
task_start_button = Button(self, textvariable = time, command = lambda: self.start_working(task_content)).grid(column = 1, row = self.next_row)
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.next_row += 1
def start_working(self, task_name):
print('start working', task_name)
def createWidgets(self):
self.welcome_label = Label(self, text = "Welcome to the Challenge!", font = ("Arial, 12")).grid(column = 0, row = 0, columnspan = 2)
self.add_task_button = Button(self, text = "Add Task", width = 20, command = self.add_task).grid(column = 0 , columnspan = 2)
def __init__(self):
"""GUI Initiation"""
Tk.__init__(self)
self.createWidgets()
"""window Initiation"""
self.resizable(False, False)
x = (self.winfo_screenwidth() - self.winfo_reqwidth()) / 2
y = (self.winfo_screenheight() - self.winfo_reqheight()) / 2
self.geometry('250x400+%d+%d' % (x, y))
self.next_row = 3

how to update a listbox in one window in one class using a different class python

what i have is a window that opens up and it has a list box. this is created using one class. when i click the search button and results are found using a different class, i want the list box to update without having to open up another window. below is my code so far\n
from Tkinter import *
class addTask:
def __init__(self):
self.addWindow = Tk()
self.addWindow.configure(background = "black")
self.addWindow.geometry("450x450")
self.addWindow.resizable(width = False, height = False)
self.addWindow.title("Add Task")
self.addNameLabel = Label(self.addWindow,text="Add the name of the task",font = ("Helvetica",10,"italic"),bg = "black",fg = "white")
self.addNameLabel.place(relx=0.01, rely=0.05)
self.nameWiget = Text (self.addWindow, width = 63, height = 1)
self.nameWiget.place(relx=0.0, rely=0.1)
self.addDateLabel = Label(self.addWindow,text="Add the date of the task",font = ("Helvetica",10,"italic"),bg = "black",fg = "white")
self.addDateLabel.place(relx=0.01, rely=0.2)
self.dateWiget = Text (self.addWindow, width = 63, height = 1)
self.dateWiget.place(relx=0.0, rely=0.25)
self.addTaskLabel = Label(self.addWindow,text="Add the details of the task",font = ("Helvetica",10,"italic"),bg = "black",fg = "white")
self.addTaskLabel.place(relx=0.01, rely=0.35)
self.taskWiget = Text (self.addWindow, width = 63, height = 1)
self.taskWiget.place(relx=0.0, rely=0.4)
addButton = Button(self.addWindow,height = 5, width = 20, text="Add Task",highlightbackground="black",font=("Helvetica",10,"bold"),command=lambda:self.saveFuntion())
addButton.place(relx=0.25, rely=0.55)
def saveFuntion(self):
nameInfo = (self.nameWiget.get(1.0, END))
dateInfo = self.dateWiget.get(1.0, END)
taskInfo = self.taskWiget.get(1.0, END)
print nameInfo
task1 = Task(nameInfo,dateInfo,taskInfo)
task1.save()
self.nameWiget.delete(1.0,END)
class Task:
def __init__(self,name,date,task):
self.__name = name
self.__date = date
self.__task = task
def save(self):
fileName = open("dataFile.txt","a")
fileName.write(self.__name)
fileName.write(self.__date)
fileName.write(self.__task)
class editTask:
def __init__(self):
self.editWindow = Tk()
self.newWindow = Tk()
self.newWindow.geometry("450x750")
self.editWindow.configure(background = "black")
self.editWindow.geometry("450x750")
self.editWindow.resizable(width = False, height = False)
self.editWindow.title("Edit Task")
self.listBox = Listbox(self.editWindow,heigh = 15, width = 30)
self.listBox.place(relx = 0.2, rely = 0.6)
#drop down menu
self.var = StringVar(self.editWindow)
self.var.set("Select search critria")
self.choices = ["Name","Date"]
self.option = OptionMenu(self.editWindow,self.var,*self.choices)
self.option.configure(bg = "black")
self.option.place(relx = 0.5, rely = 0.2)
#edit label and text box
self.editLabel = Label(self.editWindow,text="Add the name of the task",font = ("Helvetica",10,"italic"),bg = "black",fg = "white")
self.editLabel.place(relx=0.01, rely=0.05)
self.editInfoWiget = Text (self.editWindow, width = 63, height = 1)
self.editInfoWiget.place(relx=0.0, rely=0.1)
# search button
searchButton = Button(self.editWindow,height = 5, width = 20, text="Search for Task",highlightbackground="black",font=("Helvetica",10,"bold"),command=lambda:self.searchFuntion())
searchButton.place(relx=0.3, rely=0.4)
def searchFuntion(self):
critria = self.var.get()
info = self.editInfoWiget.get(1.0,END)
thing = info.split("\n")
thing2 = thing[0]
search = searchCritria(critria,thing2)
search.search()
# def openListBox(self):
class searchCritria():
def __init__(self,critria,info):
self.__critria = critria
self.__info = info
def search(self):
self.file = open("dataFile.txt", "r+")
fileData = self.file.readlines()
self.file.close()
lengthOfFile = len(fileData)
counter = 1
self.name = []
self.date = []
self.details = []
for i in range (lengthOfFile):
split = fileData[i].split("\n")
while counter == 1:
self.name.append(split)
break
while counter == 2:
self.date.append(split)
break
while counter == 3:
self.details.append(split)
break
counter = counter +1
if counter > 3:
counter = 1
if self.__critria == "Name":
for x in range (len(self.name)):
self.taskName = self.name[x]
self.taskName2 = self.taskName[0]
if self.__info == self.taskName2:
openWindow = True
else :
openWindow = False
if openWindow == True:
editTask().listBox.insert(END,self.taskName2)
if self.__critria == "Date":
for x in range (len(self.date)):
self.taskDate = self.date[x]
self.taskDate2 = self.taskDate[0]
if self.__info == self.taskDate2:
print "found"
else :
print"not found"
class editTask2():
def __init__(self):
self.edit2Window = Tk()
self.edit2Window.configure(background = "black")
self.edit2Window.geometry("450x350")
self.edit2Window.resizable(width = False, height = False)
self.edit2Window.title("Edit Task")
any help would be great
thanks
The biggest problem in your code is that you're creating multiple instances of Tk. Tkinter simply isn't designed to work that way. You should only ever create a single instance of Tk. If you need additional windows, create instances of Toplevel.
Also, you need to call the mainloop function of this instance of Tk exactly once. Without it, your GUI will not work.
If you want to update a listbox in another class than where it was created, the concept is pretty simple: if A needs to update B, A needs a reference to B. So, either pass in a reference to the listbox when you create the other class, or give the other class a method you can call where you pass in the reference to the listbox after it was created.

Categories

Resources