To count no. times a function is called? - python

is there any method to count how many times a function is called in python?
I'm using checkbutton in GUI. I have written a function for that checkbutton command, i need to perform some actions based on the checkbutton status, i mean based on whether it is ticked or not. My checkbutton and button syntax are like this
All = Checkbutton (text='All', command=Get_File_Name2,padx =48, justify = LEFT)
submit = Button (text='submit', command=execute_File,padx =48, justify = LEFT)
so i thougt of countin no. of times the command function is called, and based on its value i can decide whether it is ticked or not. please help

You can write decorator that will increment special variable after function call:
from functools import wraps
def counter(func):
#wraps(func)
def tmp(*args, **kwargs):
tmp.count += 1
return func(*args, **kwargs)
tmp.count = 0
return tmp
#counter
def foo():
print 'foo'
#counter
def bar():
print 'bar'
print foo.count, bar.count # (0, 0)
foo()
print foo.count, bar.count # (1, 0)
foo()
bar()
print foo.count, bar.count # (2, 1)

If checking whether the checkbutton is ticked or not is the only thing you need, why not just do checkbutton.ticked = true?
One way to implement this is to create a sub class out of Checkbutton (or - if you can - edit the existing Checkbutton class) and just add self.ticked attribute to it.
class CheckbuttonPlus(Checkbutton):
def __init__(self, text, command, padx, justify, ticked=False):
super().__init__(text, command, padx, justify)
self.ticked = ticked
And edit your function so that it changes your CheckbuttonPlus -object's ticked to not ticked.
I don't know how your classes are constructed, but you should find the method from Checkbutton class that activates the function, and then overwrite it in CheckbuttonPlus -class (incase you cannot edit the existing Checkbutton class, in which case, you don't even need CheckbuttonPlus class at all).
Edit: If you're using Tkinter Checkbutton (looks quite like it), you might wanna check this:
Getting Tkinter Check Box State

Related

Tkinter - How to change the value of an argument for an event binding with lambda function?

I have a list named chosenTestHolder (imported from the my_config file) that consists of several objects each with the attribute 'sentence'.
When pressing the button 'Press' for the first time, the attribute 'sentence' of the first object in the chosenTestHolder should be displayed in the text widget. The next time the button 'Press' is pressed the attribute 'sentence' of the second object in chosenTestHolder should be displayed and so on.
I am using lambda event for binding the 'Press' button and tries to use a new sentences as its first arguments after each pressing of the 'Press' button. However, it keeps showing the first sentence.
When searching Stackoverflow I have seen in
Using lambda function to change value of an attribute that you can't use assignments in lambda expressions but by reading that I still have not figured out how to solve my problem.
Grateful for help! Code is below!
main.py
from tkinter import font
import tkinter as tk
import tkinter.ttk as ttk
import my_config
import Testlist as tl
class TestWidgetTest:
def __init__(self):
ram = tk.Frame(root)
ram.grid(in_=root,row=0, column=0)
self.myText = tk.Text(ram, height = 5)
self.myText.grid(row=0,column=1)
my_config.counter = 0
self.myButton = tk.Button(ram, text = 'Press')
self.myButton.grid(row =1, column =0, columnspan =2)
indata =[my_config.chosenTestHolder[my_config.counter] , self.myText]
self.myButton.bind('<ButtonRelease-1>',lambda event, arg=indata : self.TagConfigure(event, arg))
def TagConfigure(self, event, arg):
arg[1].delete('1.0',tk.END)
arg[1].insert('1.0',arg[0].sentence)
my_config.counter += 1
root = tk.Tk()
TestWidgetTest()
root.mainloop()
my_config.py
import Testlist as tl
testListHolder = [ ['Fabian was very tired'],
['Thomas light the fire'],
['Anna eat a red apple ']]
chosenTestHolder = []
count = 0
while count <(len(testListHolder)):
chosenTestHolder.append(tl.Testlist(testListHolder[count][0]))
count += 1
counter = 0
Testlist.py
class Testlist:
def __init__(self, sentence):
self.sentence = sentence
Your issue is the assignment of indata.
You do only assign in init.
To get your code working you need to re-configure your sentecte...
indata =[my_config.chosenTestHolder[my_config.counter] , self.myText]
self.myButton.bind('&ltButtonRelease-1&gt',lambda event, arg=indata : self.TagConfigure(event, arg))
I would advise to keep track of the current sentence as an instance variable.
class Test_widget(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, args, kwargs)
self.sentences=["a", "b", "c", "d"] # the data
self.show = tk.StringVar() # the current displayed data
self.show.set("NULL")
self.counter=0 # the indexer
tk.Label(self, textvariable=self.show).grid(row=0)
tk.Button(self, command=self.click).grid(row=1)
def click(self, event):
self.show.set("%s"%self.sentences[self.counter]) # use the indexer to access the data
self.counter = self.counter + 1 # modify the indexer
if self.counter = len(self.sentences): # make sure you dont run in index-err
self.counter = 0
As you see, there is no need at all for the lambdas.
Edit
As to your questions:
The change in your original code was not intended.
I do not see a use case where you can use a lambda for its use inside your code.
At least none where a lambda is necessary.
Please remember to use lambda only and exclusively if there are
no ( == NULL ) other options.
Using inheritance (thats what the mechanism is called), you can inherit functions, "default" behaviour from other classes. It is a common mechanism in programming and not exclusive to python.
It is used like any normal object except you have to call the constructor of the base class (what I do using tk.Frame.__init__(self, args, kwargs) inside the init method. For more information on inheritance please refer to the uncounted manuals and tutorials available for that topic (google is your friend now that you know what the mechanism is called).

How to return a drop-down value PyQT to another method Python

I am trying to create a PyQt Dropdown menu(combo box), whose value I need to pass to another function.
Here is a snippet of my code
def combo_box(self):
combo = QtGui.QComboBox(self)
...#Filled in code here
for i in range(0,len(arr)):
combo.addItem(arr[i])
#I want to get the value of the selected Drop-down content to be stored in value
value = combo.activated[str].connect(self.ComboValue)
print value
def ComboValue(self,Text):
print Text
return Text
When I print the variable Text in the ComboValue method it prints it right, but when I print value from the combo_box method it prints None.
I wanted to know why this happens, and is there an alternative to return the value to the other method?
combo.activated[str].connect(self.ComboValue) is signal and signal never return you anything back, so that's why you getting None. Take a look http://pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html
You may use this value = str(combo.currentText()), but don't know you want this.
And change combo.activated[str].connect to combo.currentIndexChanged [str].connect to get your values properly
create the combobox, then wait till user activates an item:
def combo_box(self):
combo = QtGui.QComboBox(self)
...#Filled in code here
for i in range(0,len(arr)):
combo.addItem(arr[i])
combo.activated.connect(self.ComboValue)
# save it and wait for user to do something:
self.combo = combo
def ComboValue(self, Text):
# user has selected an item, save it:
self.value = Text
assert combo.currentText() == Text
... do something with it ...
Not tested so details may be wrong.

Reference an object from within the object

So I am making a menu structure. It goes as follows: there is a menu class which has a label, a list of tuples which are its items and a 'previous menu'.
So my idea is that when in Main, previous will be None. The items in the menus are either functions or another menu. (A function can also be something that generates a menu) When you then select option 1 I want to set the 'previous' parameter in the option_1_menu to main_menu
class Menu:
def __init__(self, label, options):
self.label = label
self.options = options
self.previous = None
def set_previous(self,previous_menu):
self.previous = previous_menu
def evaluate(self, choice):
if isinstance(self.options[int(choice) - 1][1],Menu):
print('this is a menu')
# Set the previous menu to this menu
self.options[int(choice) - 1][1].set_previous(self)
# Return the new menu
return self.options[int(choice) - 1][1]
# t_current_menu(self.options[int(choice) - 1][1])
else:
print('This is a function')
self.options[int(choice) - 1][1]()
So my question is about the line where I set the previous menu to this menu. I basically set the 'previous' one to the one I am currently in. Can I reference to self in that way I do there to accomplish this?
Note: the 'this is a function' part is still very much WiP
Sure, self is a perfectly valid "reference to a Menu instance" so there is no problem calling someothermenu.set_previous(self). Adding a unit test as #Nsh suggests is also never a bad idea (for this or any other functionality:-).

Problems with a bind function from tkinter in Python

I am working on an application that is supposed to support both running from a console and from a GUI. The application has several options to choose from, and since in both running modes the program is going to have the same options obviously, I made a generalisation:
class Option:
def __init__(self, par_name, par_desc):
self.name = par_name
self.desc = par_desc
class Mode():
def __init__(self):
self.options = []
self.options.append(Option('Option1', 'Desc1'))
self.options.append(Option('Option2', 'Desc2'))
self.options.append(Option('Option3', 'Desc3'))
self.options.append(Option('Option4', 'Desc4'))
self.options.append(Option('Option5', 'Desc5'))
#And so on
The problem is that in GUI, those options are going to be buttons, so I have to add a new field to an Option class and I'm doing it like this:
def onMouseEnter(par_event, par_option):
helpLabel.configure(text = par_option.desc)
return
def onMouseLeave(par_event):
helpLabel.configure(text = '')
return
class GUIMode(Mode):
#...
for iOption in self.options:
iOption.button = Button(wrapper, text = iOption.name, bg = '#004A7F', fg = 'white')
iOption.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event, iOption))
iOption.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
#...
There is also a "help label" showing the description of the option every time a mouse hovers over it, so there I am binding those functions.
What is happening is that while I am indeed successfully adding a new field with a button, the bind function seems to mess up and the result is this:
Help label is always showing the description of the last option added, no matter over which button I hover. The problem seems to go away if I directly modify the Option class instead, like this:
class Option:
def __init__(self, par_name, par_desc):
self.name = par_name
self.desc = par_desc
self.button = Button(wrapper, text = self.name, bg = '#004A7F', fg = 'white')
self.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event, self))
self.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
But I obviously can't keep it that way because the console mode will get those fields too which I don't really want. Isn't this the same thing, however? Why does it matter if I do it in a constructor with self or in a loop later? I therefore assume that the problem might be in a way I dynamically add the field to the class?
Here is the full minimal and runnable test code or whatever it is called, if you want to mess with it: http://pastebin.com/0PWnF2P0
Thank you for your time
The problem is that the value of iOption is evaluated after the
for iOption in self.option:
loops are complete. Since you reset iOption on each iteration, when the loop is completed iOption has the same value, namely the last element in self.options. You can demonstrate this at-event-time binding with the snippet:
def debug_late_bind(event):
print(iOption)
onMouseEnter(event, iOption)
for iOption in self.options:
iOption.button = Button(wrapper, text = iOption.name,
bg = '#004A7F', fg = 'white')
iOption.button.bind('<Enter>', debug_late_bind)
which will show that all events that iOption has the same value.
I split out the use of iOption to debug_late_bind to show that iOption comes in from the class scope and is not evaluated when the bind() call is executed. A more simple example would be
def print_i():
print(i)
for i in range(5):
pass
print_i()
which prints "4" because that is the last value that was assigned to i. This is why every call in your code to onMouseEnter(par_event, iOption) has the same value for iOption; it is evaluated at the time of the event, not the time of the bind. I suggest that you read up on model view controller and understand how you've tangled the view and the controller. The primary reason this has happened is that you've got two views (console and tk) which should be less coupled with the model.
Extracting the .widget property of the event is a decent workaround, but better still would be to not overwrite the scalar iOption, but instead use list of individual buttons. The code
for n, iOption in enumerate(self.options):
would help in creating a list. In your proposed workaround, you are encoding too much of the iOption model in the tkinter view. That's bound to bite you again at some point.
I don't know what the actual problem was with my original code, but I kind of just bypassed it. I added a dictionary with button as a key and option as a value and I just used the par_event.widget to get the option and it's description, which is working fine:
buttonOption = {}
def onMouseEnter(par_event):
helpLabel.configure(text = buttonOption[par_event.widget].desc)
return
def onMouseLeave(par_event):
helpLabel.configure(text = '')
return
class GUIMode(Mode):
def run(self):
#...
for iOption in self.options:
iOption.button = Button(wrapper, text = iOption.name, bg = '#004A7F', fg = 'white')
iOption.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event))
iOption.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
buttonOption[iOption.button] = iOption
#...

How to make Tkinter checkbuttons work individually and default as checked

I am trying to have a series of checkboxes which can be selected or not selected - then when the user presses the button, the letters allocated to the checkboxes should be added to an inputted string and then printed.
I am having 2 major problems;
The when the user presses a checkbox, all of the checkboxes are selected.
I would like to have the default being that all of them as checked, but I cannot find how to do this. If this cannot be answered, it is less important than the 1st problem, and hence doesn't matter as much.
This is my code so far;
import Tkinter
class MENU():
def __init__(self,NewData):
self.SCREEN = Tkinter.Tk()
self.NewData = NewData
self.Selection = {"A":1,"B":1,"C":1,"D":1}
self.A = Tkinter.Checkbutton(self.SCREEN,text="A",variable=self.Selection["A"]).pack()
self.B = Tkinter.Checkbutton(self.SCREEN,text="B",variable=self.Selection["B"]).pack()
self.C = Tkinter.Checkbutton(self.SCREEN,text="C",variable=self.Selection["C"]).pack()
self.D = Tkinter.Checkbutton(self.SCREEN,text="D",variable=self.Selection["D"]).pack()
self.BtnFinish = Tkinter.Button(self.SCREEN,text="Finish selection",command=self.FinishSelection)
self.BtnFinish.pack()
self.SCREEN.mainloop()
def FinishSelection(self):
SelectionString = ""
for n in self.Selection:
if self.Selection[n]:
SelectionString+=n
self.NewData+="\nQuestions\n"+SelectionString+"\n"
print self.NewData
MENU("")
If it matters at all, this is for Python 2.7.3. Additionally, my thanks, and also apologies to those of you who likely have to point out something incredibly obvious/basic, which, as a beginner for Tkinter, I have not realised.
You must use one of the Tkinter objects StrintVar, IntVar, BooleanVar or DoubleVar normally (StringVar) as the value of the variable argument. You can't use a normal python variable. You'll have to create an individual variable for each.
For example:
self.Selection = {"A": Tkinter.BoolVar(), "B": Tkinter.BoolVar(), ...}
self.Selection["A"].set(True)
...
Then, to get the value you'll need to use the get method:
value = self.Selection["A"].get()

Categories