How can I test a function that is embedded in a method - python

I recently started doing python. The course I was on ended with an introduction to testing with doctest. I have written a program that uses Tkinter to display widgets and it works :-) . I am using version 3.7. However, testing it is another matter. I can test simple functions and methods, but I hit difficulties when I have a function inside a method. I am pasting below a stripped-down version of what I am trying to achieve. I tried first with doctest and it threw up an error:
"AttributeError: 'function' object has no attribute 'c_square'".
# counter.py
from tkinter import *
import doctest
count = 0
delay = 1000
class MyClass:
def __init__(self, master):
master.geometry("1000x500")
master.resizable(0, 0)
master.title("Display a counter")
frame1 = Frame(master)
frame1.pack()
label1 = Label(frame1, font = ('Courier', 15 , 'bold'))
label1.grid(row = 0, column = 0)
self.my_counter(label1)
label2 = Label(frame1, font = ('Courier', 15 , 'bold'))
label2.grid(row = 0, column = 1)
self.square_of_count(label2)
# This method recursively increments a counter and displays the count.
def my_counter(self, lbl):
def increment_count():
global count
global delay
count += 1
string = str(count)
lbl.config(text = string)
lbl.after(delay, increment_count)
increment_count()
# This method takes the square of the counter and displays the result.
def square_of_count(self, lbl):
def c_square():
global count
squ = count * count
string = str(squ)
lbl.config(text=string)
lbl.after(delay, c_square)
return squ
c_square()
def test_c_square(number):
"""
>>> test_c_square(2)
4
"""
global count
count = number
master = Tk()
frame1 = Frame(master)
label = Label(frame1, font = ('Courier', 15 , 'bold'))
return MyClass.square_of_count.c_square(MyClass.square_of_count.c_square)
def main():
""" # main body commented out for test purposes.
root = Tk()
a = MyClass(root)
root.mainloop()
"""
doctest.testmod(verbose=True)
if __name__ == "__main__":
main()
I am using a separate test function, so that I can initialise my counter.
Then someone suggested that I try unittest, so I wrote this :
import unittest
import counter
class TestCounter(unittest.TestCase):
counter.count = 2
print("count = ", counter.count)
def square_of_count(self):
result = counter.c_square()
self.assertEqual(result, 4)
result = counter.c_square()
self.assertNotEqual(result, 3)
if __name__ == '__main__':
unittest.main()
This runs without throwing up any errors, the purpose of it is to set a value to the variable 'count' and read back the result. But I get the same response whatever value I test for, so I do not believe it is working right. I also tried variations on a theme, but I just got error messages.
Can someone please point out what I am doing wrong, I have looked about various forums and tutorials but have not seen this question asked before.
I would appreciate an answer that is easy to follow, I am asperger / dyslexic and find it difficult to learn new material. A correction with explanation would be most helpful. Thank you.

First of all, avoid this kind of nesting the functions. In your particular case I would highly suggest refactoring of a code in manner of creating some help private methods which you will call from the main ones, or even create whole new utility class:
class Util:
def _init_(self):
self.name = "Utility"
def add_two_numbers(self, first, second):
if(isinstance(first, int) and isinstance(second, int)):
return first+second
class SomeFancyClass:
def __init__(self):
self.util = Util()
self.constant = 4
# This method recursively increments a counter and displays the count.
def my_fancy_math(self, first, second):
return self.constant * self.util.add_two_numbers(first, second)
FancyVar = SomeFancyClass()
print(FancyVar.my_fancy_math(5, 6))
In case you dont want to change your code (for some reason), there is extremely dirty way to access your inner function. Again, a bit stupidly modified example made from your code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# counter.py
from tkinter import *
import doctest
import types
count = 0
delay = 1000
class MyClass:
def __init__(self, smth1):
self.something = smth1
# This method recursively increments a counter and displays the count.
def my_counter(self, lbl):
def increment_count():
global count
global delay
count += 1
string = str(count)
lbl.config(text=string)
lbl.after(delay, increment_count)
increment_count()
# This method takes the square of the counter and displays the result.
def square_of_count(self, lbl):
def test_function1(self, first, second):
return first+second
def c_square():
global count
squ = count * count
string = str(squ)
lbl.config(text=string)
lbl.after(delay, c_square)
return squ
c_square()
def test_function(self, st1):
print(st1)
def test_c_square(number):
global count
count = number
master = Tk()
frame1 = Frame(master)
label = Label(frame1, font=('Courier', 15, 'bold'))
return MyClass.square_of_count.c_square(MyClass.square_of_count.c_square)
def main():
doctest.testmod(verbose=True)
if __name__ == '__main__':
# main()
print('done')
test_function = types.FunctionType(MyClass.square_of_count.__code__.co_consts[1],
{}, None, (), ())
obj = MyClass("Hi")
sum1 = test_function("", 1, 2)
print(sum1)

Related

How to get the value of an Entry created in a def?

I'm working on a project and i would like to get the Value of an Entry created in a def (turned on by a button on Tkinter)
So I have my main tkinter menu, with a button which will call the def "panier".
The def "panier" is creating the Entry "value" and another button to call a second def "calcul".
The second def "calcul" will do things with the value of Entry...
But then, in the def "calcul", when i'm trying to do value.get() it tells "NameError: name 'value' is not defined"
Here is the code, btw the Entry must be created by the def...
from tkinter import *
def panier():
value=Entry(test)
value.pack()
t2=Button(test,text="Validate",command=calcul)
t2.pack()
def calcul(value):
a=value.get()
#here will be the different calculations I'll do
test=Tk()
t1=Button(test,text="Button",command=panier)
t1.pack()
test.mainloop()
Appreciate every feedback :)
You can make the variable global like this:
from tkinter import *
def panier():
global value
value = Entry(test)
value.pack()
t2 = Button(test, text="Validate", command=calcul)
t2.pack()
def calcul():
a = value.get()
print(a)
#here will be the different calculations I'll do
test = Tk()
t1 = Button(test, text="Button", command=panier)
t1.pack()
test.mainloop()
The global value line makes the variable global so you can use it anywhere in your program.
You can also pass in the variable as an argument like what #JacksonPro suggested
t2 = Button(test, text="Validate", command=lambda: calcul(value))
This is one way to do it. Globally create a collection (list or dictionary) to hold a reference to the Entry. When you create the Entry, add it to the collection. I made it with either a list or dictionary for holding the references, so toggle the commented variations in all three places to try it both ways.
import tkinter as tk
def panier():
for item in ('value', ):
ent = tk.Entry(test)
collection.append(ent)
# collection[item] = ent
ent.pack()
t2 = tk.Button(test,text="Validate",command=calcul)
t2.pack()
def calcul():
a = collection[0].get()
# a = collection['value'].get()
print(a)
collection = []
# collection = {}
test = tk.Tk()
t1 = tk.Button(test, text="Button", command=panier)
t1.pack()
test.mainloop()

How can I have imported modules access tkinter widgets and variables?

I had this Cafe Management System whom I've separated the gui and functions into modules.
Here are some snippets:
main.py
from tkinter import *
import checkbox_operation
import receipt_operation
class cafemanagementsystem:
def __init__(self, cms):
self.cms = cms
cms.title("Cafe Management System")
self.b1var = IntVar(value=1)
self.b1v = StringVar()
self.b1v.set("0")
self.b1 = Checkbutton(self.bevmenu, command=self.check, text="Latte", variable=self.b1var, onvalue=1, offvalue=0).grid()
self.b1a = Entry(self.bevmenu, bd=2, textvariable=self.b1v)
self.b1a.grid()
self.rcpt = Text(self.rcptmain, width=50, height=30, bd=4)
self.rcpt.grid()
self.btnrcpt = Button(self.rcptbtn, command=receipt_operation.receipt, text="Receipt").grid()
self.btnrst = Button(self.rcptbtn, command=receipt_operation.reset, text="Reset").grid()
def check(self):
checkbox_operation.check(self)
def receipt(self):
receipt_operation.receipt(self)
checkbox_operation.py
def check(cafemanagementsytem_inst):
if b1var.get() == 1:
b1a.config(state=NORMAL)
elif b1var.get() == 0:
b1a.config(state=DISABLED)
b1v.set("0")
receipt_operation.py
def receipt():
rcpt.insert(END, "Offical Receipt\n")
rcpt.insert(END, "Latte \t\t\t\t" + b1v.get() + "\n")
My problem is that I can't get check() and receipt() to work. Also, def check(self) and def receipt(self) gives the following error:
TypeError: check()/receipt() takes 0 positional arguments but 1 was given
Are there any solutions for this? Also, please tell me if the way I wrote the code contributed to the problem as I've been stuck in this problem for almost a week.
You need to pass the proper variables into those other functions. I'm wondering why you have checkbox_operation in a separate file, when it should be part of the class. It's not good practice that those external functions need to have such detailed knowledge of the internal workings of the class.
...
def check(self):
checkbox_operation.check(self)
def receipt(self):
receipt_operation.receipt(self)
def check(cafe):
if cafe.b1var.get() == 1:
cafe.b1a.config(state=NORMAL)
elif cafe.b1var.get() == 0:
cafe.b1a.config(state=DISABLED)
cafe.b1v.set("0")
def receipt(cafe):
cafe.rcpt.insert(END, "Offical Receipt\n")
cafe.rcpt.insert(END, "Latte \t\t\t\t" + cafe.b1v.get() + "\n")

Choosing from a List of methods in a tkinter Button

Good Day,
I'm new to this forum (and quite new to programming), so I hope my question is properly formulated.
I've been trying to create a GUI in python using tkinter, and I want to have two buttons calling methods of two different classes. One method is defining an integer, the second one is reporting content. I'd have a list of objects of the latter class, and I want to choose the right instance by the integer. Here's a MWE:
import tkinter as tk
class data:
def __init__(self, content):
self.content = content
def report(self):
print("This is reported as self.content:" + str(self.content)) #This doesnt report the correct value for some reason?
print("The Class does register the correct idx:" + str(Selector.idx))
print("Using the Dict the correct value can be returned:" + str(vocables[Selector.idx].content))
class increment:
def __init__(self):
self.idx = 0
def increase(self):
self.idx += 1
print(self.idx)
vocables[self.idx].report()
root = tk.Tk()
Selector = increment()
vocables = []
for id in range(10):
vocables.append(data(id))
# print(vocables[id].content)
CheckVocable = tk.Button(root, text="Report", command=vocables[Selector.idx].report)
CheckVocable.pack()
NextVocable = tk.Button(root, text="Increase Index", command=Selector.increase)
NextVocable.pack()
root.mainloop()
I do not understand why the print of line 8 always reports the value of the first item in the list (vocabules[0] in this instance) instead of my desired value, which is returned in all other print cases. Am I messing up the work with classes or is the button behavior confusing me?
Thanks in advance!

reccomending, according to the selected listbox value

the code is a bit long, hope you understand it, i have this small app, where i have to implement data from 2 files, and then showing some of the values(of data) in list box. on the other side i have combined the values in nested dictionary. Now i have to recommend movie according the the value i will select from list box. Maybe the code i have written is wrong in some parts, though in the last part, when i have to write the code of recommendation, i have no clue how i have to write the code, i so it will recommend me movie, for further explanation, i will be writing in comments part if needed. (Data&recommendations file: https://www.dropbox.com/sh/b949hapk5lmm94i/AAD29ykbvWq83wZbFpvvqw69a?dl=0)
from Tkinter import *
import tkFileDialog
import csv
from recommendations import *
class STproject:
def __init__(self): #1
self.variableclasses()
self.buttonsnlabels()
self.dictionary()
def variableclasses(self):
self.all_vars = [StringVar() for _ in range(2)]
self.var1 = StringVar()
self.var2 = StringVar()
self.var1.set(0)
self.var2.set(0)
def buttonsnlabels(self):
self.rdbttn=Radiobutton(root,text='user based recommendation',variable = self.var1,value=1,command=lambda:self.dictionary())#, command=lambda :self.selec1())
self.rdbttn.grid(row=0,column=0)
self.rdbttn2=Radiobutton(root,text='movie based recommendation',variable = self.var1,value=2,command=lambda:self.dictionary())#,command=lambda :self.selec1())
self.rdbttn2.grid(row=1,column=0)
self.rdbttn3=Radiobutton(root,text='pearson',variable = self.var2,value=1)#,command=lambda :self.selec2())
self.rdbttn3.grid(row=2,column=0)
self.rdbttn4=Radiobutton(root,text='euclidean',variable = self.var2,value=2)#,command=lambda :self.selec2())
self.rdbttn4.grid(row=3,column=0)
self.ratingbutton=Button(root,text='Upload Rating',command=lambda :self.file(self.all_vars[0]))
self.ratingbutton.grid()
self.ratingbutton=Button(root,text='Upload Movies',command=lambda :self.file(self.all_vars[1]))
self.ratingbutton.grid()
self.lb1 = Listbox(root)
self.lb1.grid(row=0,column=1)
self.lb2 = Listbox(root)
self.lb2.grid(row=0,column=1)
self.lb3 = Listbox(root)
self.lb3.grid(row=0,column=1)
def file(self, v):
result = tkFileDialog.askopenfilename()
if result:
v.set(result)
# self.dictionary()
def dictionary(self):
# self.lb1.delete(END, 0) #clear listbox
# self.lb1.update_idletasks()
if self.var1.get()==1:
self.d = {}
if all(i.get() for i in self.all_vars): #process only if all 2 files are selected
with open(self.all_vars[0].get(),"r") as a, open(self.all_vars[1].get(),"r") as b:
for line1,line2,line3 in zip(csv.reader(a),csv.reader(b),csv.reader(a)):
self.d[line1[0]]={line2[1]:line3[2]}
self.lb1.insert('end',line2[1])
print self.d
else:
self.d = {}
if all(i.get() for i in self.all_vars): #process only if all 2 files are selected
with open(self.all_vars[0].get(),"r") as a, open(self.all_vars[1].get(),"r") as b:
for line1,line2,line3 in zip(csv.reader(a),csv.reader(b),csv.reader(a)):
self.d[line1[0]]={line2[1]:line3[2]}
self.lb1.insert('end', line1[1])
print self.d
def recoms(self):
# selection=self.lb1.curselection()
# print selection
if self.var2.get()==1:
getRecommendations()
else:
pass
root=Tk()
root.title('SteelBox Inc. Calculator')
application=STproject() #2
root.mainloop() #3

Data from child window

In the imported code, the variable 'values' is set correctly with the date selected by the user.
The def selection is called at the exit of the calendar.
I'm stuck. I don't know how to catch it and use it in my main code.
Thanks a lot.
# MAIN CODE (simplified)
from tkinter import *
import calendarWidget
def manageCalendarWindow():
root4 = Tk()
data = {}
app = calendarWidget.Calendar(root4, data)
root4.mainloop()
manageCalendarWindow()
#how to get the date?
-.-.-.-.-.-.-.
# CALENDAR WIDGET (simplified)
class Calendar:
def setup(self, y, m)
(...)
for w, week in enumerate(self.cal.monthdayscalendar(y, m), 2):
for d, day in enumerate(week):
if day:
b = tk.Button(self.parent, width=1, text=day, relief = 'flat',\
command=lambda day=day:self.selection(day, calendar.day_name[(day-1) % 7]))
self.wid.append(b)
b.grid(row=w, column=d)
def selection(self, day, name):
self.day_selected = day
self.month_selected = self.month
self.year_selected = self.year
self.day_name = name
#data
self.values['day_selected'] = day
self.values['month_selected'] = self.month
self.values['year_selected'] = self.year
self.values['day_name'] = name
self.values['month_name'] = calendar.month_name[self.month_selected]
self.setup(self.year, self.month)
print(self.values) # <--- here the value is correct
self.parent.destroy()
-.-.-.-.-.-.-.-
THIS WORKS:
def manageCalendarWindow():
root4 = Tk()
data = {}
app = calendarWidget.Calendar(root4, data)
root4.mainloop()
return app
app=manageCalendarWindow()
print(app.year_selected,app.month_selected,app.day_selected)
THIS NOT:
class enterWindows():
def B_CalendarWindow(self):`
app=self.manageCalendarWindow()
print("year: ",app.year_selected)
print("and... this will never be printed!")
def manageCalendarWindow(self):
root4 = Tk()
data = {}
app = calendarWidget.Calendar(root4, data)
root4.mainloop()
return app
Everything local to the function manageCalendarWindow() is garbage collected when the function exits. This includes app (the class instance). You would have to return it to keep it alive. Note also that there is no self.month in the code you posted but I assume that comes from cutting the amount of code back for this post.
def manageCalendarWindow():
root4 = Tk()
data = {}
app = calendarWidget.Calendar(root4, data)
root4.mainloop()
return app
## or
## return app.day_name
app=manageCalendarWindow()
print(app.day_name)
## or
##day_name=manageCalendarWindow()
##print(day_name)
A simple proof-of concept program that gets a variable from a class instantiated within the class.
class SubClass():
def __init__(self):
self.variable="SubClass variable"
class MainClass():
def __init__(self):
app=self.instantiate_class()
print(app.variable) ## prints the string
def instantiate_class(self):
app=SubClass()
print("app =", app) ## not empty
return app
MainClass()
As mentioned in my comments, there can be only one mainloop in the code. The others will be simply ignored. So it's not possible to use it to wait for a response from a child window.
The solution I used is
app = myCalendar.Calendar(personal_path, root4, gui_language)
root4.wait_window(app.parent)
return app
The code opens the window and waits the result using wait_window().
Thanks all.
Ciao.

Categories