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:-).
Related
I have this code which is suppose to create buttons dynamically based on items in a list, and then by clicking a button adding it to an external .txt.
I can create the buttons, but for some reason binding each button to an indiviual version of the writeFile() function does not work. I always get the error message "python solve got multiple values for argument 'item'", which confuses me quite bit since I am already using self as the first keyword in both function.
class MAIN(Screen):
items = ["Bike", "Car", "Boat", "Airplane"]
def __init__(self, **kw):
super().__init__(**kw)
self.list_of_btns = []
def writeFile(self, item):
with open(f"./orders/TEST", "w") as file:
file.write(f"\n-{item}")
def create(self, list=items): #Creates Categorie Buttons
self.h = 1
for i in list:
self.h = self.h - 0.2
_btn = Button(text= f"{i}", size_hint=(.2,.22), pos_hint={"center_y":self.h, "center_x":.5})
add_fun = partial(self.writeFile, item=i)
_btn.bind(on_press=add_fun)
self.list_of_btns.append(_btn)
self.add_widget(_btn)
Your writeFile() method should expect an argument that is the Button that was pressed. Try modifying the signature of that method to:
def writeFile(self, button, item):
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.
I have a bunch of QActions, some of them are (for the moment) in two submenus and main menu.
As you can see in the code below, actions are created without parent None because the actions are shared to the buttons with a same menu.
That slot on_connect should create an instance of a class Wire.
The only thing that stops me to create an instance of wire class is jack_connector, that it should be the button which was pressed and shown the menu. The other parameters are okay, that's the only that I care for the moment.
I figure out that I can obtain the value what I need with self.sender().parent().objectName()
but at this point the QActions have parent set None , so that's why i need to set the button who shows the menu as the parent at runtime.
I already know that can be done with .setParent() method, but I don't know how to do that to all the actions, during the button press event.
Here it is the most relevant code:
scene = MyScene()
menu = QMenu()
widget_container = QWidget()
#dictonaries for dragbuttons (used later for connecting them)
jacks_dic = {}
inputs_dic = collections.OrderedDict()
wire_dic = {}
...
#pyqtSlot(str)
def on_connect(self, input):
print 'connected'
jack_connector = self.sender().parent().objectName() #sender's parent of QAction should be the button
wire_dic['wire_1'] = Wire( jack_connector , widget_container.findChild( DragButton, 'btn_' + input ) , None, scene)
#Load Menu options for Jacks dragbuttons
#create sub-menus
submenus_dic = collections.OrderedDict()
submenus_dic['AIF1TX1_submenu'] = QMenu("AIF1TX1 (L) Record to Device")
submenus_dic['AIF1TX2_submenu'] = QMenu("AIF1TX2 (R) Record to Device")
actions_dic = collections.OrderedDict()
for input in inputs_dic:
#Create an Action
actions_dic[ input ] = QtGui.QAction( input, None)
#TODO: Find a way to set parent a QAction after click
#TODO: Research how to connect every action to a slot()
actions_dic[ input ].triggered[()].connect( lambda input=input: on_connect(actions_dic[ input ], input) )
#Condition to add to a submenu
if input[:-2] == 'AIF1TX1' :
submenus_dic['AIF1TX1_submenu'].addAction( actions_dic[ input ] )
if input[:-2] == 'AIF1TX2' :
submenus_dic['AIF1TX2_submenu'].addAction( actions_dic[ input ] )
#Add SubMenus to Main Menu
for submenu in submenus_dic:
menu.addMenu(submenus_dic[ submenu ] )
The problem is that the one action is being triggered and you are trying to find out what menu was open and activated the action. There are several approaches that you can take.
You might want to explore the QAction's menu(), parentWidget() and associatedWidgets() methods with the QMenu's activeAction() method. You could also check what menu is visible.
QMenus create a widget representing the action when you add an action to the menu. So you could manually create these widgets and create a separate set of triggers to pass another parmeter into your function.
It really sounds like you need separate class.
class MyMenu(QMenu):
newWire = QtCore.Signal(str)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.wire_dic = collections.OrderedDictionary()
self.action1 = QAction("text")
self.action1.triggered.connect(self.action1method)
self.addAction(self.action1)
# end Constructor
def action1method(self):
# Maybe use self.windowTitle()
self.wire_dic['wire_1'] = Wire(self.title(), widget_container.findChild( DragButton, 'btn_' + input ) , None, scene)
self.newWire.emit("wire_1")
# end action1method
# end class MyMenu
m1 = MyMenu("Menu1")
m2 = MyMenu("Menu2")
menubar.addMenu(m1)
menubar.addMenu(m2)
That way each object is associated with it's own action without you manually creating and managing a bunch of duplicate actions. This should be a lot easier to manage and the actions will all operate the same way.
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
#...
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