Dynamically modifying/refreshing menu contents in PyGTK - python

I am trying to implement a list of recently opened items in the menu of a GUI I am writing with PyGTK. I initialize the menu like so:
self.filemenu = gtk.Menu()
self.init_file_menu()
self.fileitem = gtk.MenuItem("File")
self.fileitem.set_submenu(self.filemenu)
menubar = gtk.MenuBar()
menubar.append(self.fileitem)
outerframe.pack_start(menubar, False, False)
def init_file_menu(self):
for widget in self.filemenu.get_children():
self.filemenu.remove(widget)
openitem = gtk.MenuItem("Open")
self.filemenu.append(openitem)
openitem.connect("activate", self.open_file)
self.filemenu.append(gtk.SeparatorMenuItem())
for recentitem in self.settings['recentfiles']:
item = gtk.MenuItem(os.path.basename(recentitem))
self.filemenu.append(item)
item.connect("activate", self.open_file, recentitem)
self.filemenu.show()
self.settings['recentitems'] is a deque that is modified when opening a file; init_file_menu is then called again. My goal is to empty the menu, then repopulate it with the right items in the right order. This code works fine when populating the menu at startup. But for some reason while the calls to Menu.remove all work fine and empty the menu, the append calls do not re-add the items to it and it remains empty. If I call its get_children method, I see that they are all there internally, but the interface has not been updated to reflect this. How do I refresh the displayed menu and make the recent items list work as expected?
I would also accept direction on how to use the RecentChooserMenu widget if that is what I am looking for.

Since gtk.Menu is a container, you need to .show every item after adding it to menu (Because you don't want to call menu.show_all() shortcut which shows the menu itself), so you do item.show() just after menu.append(item)

Related

How to speed up browsing application via pywinauto?

From application that I want to automate I get pane with set of buttons. Clicking on the button adds new buttons set to Pane, and deletes other buttons from source set. Text in buttons are unique in one set, but aren't unique between them. Text on button is the only property I can use to handle it and click, so after clicking, the button is appended to list that contain already clicked buttons. Difference between children of pane and that array are buttons with unique text.
So my function is like:
def click_button(textOnButton, clickedButtons):
children = paneWindow.children() # get buttons currently present
handles = []
for item in children:
handles.append((item.handle, item.texts()[0]))
unique_buttons = list(set(handles) - set(clickedButtons)) # remove clicked buttons
button = [t for t in handles if t[1].startswith(textOnButton)]
paneWindow.childWindow(handle=button[0][0]).click_input()
clickedButtons.append((button[0][0], button.[0][1]))
clickedButtons = []
click_button("FOO", clickedButtons)
click_button("IPSUM", clickedButtons)
It works correctly, but just one function execution takes.. 2 to 3 seconds what is certainly unacceptable. Most timeconsuming is getting pane children and clicking on button. Anyone has an idea how to speed it up?
It's considerable for me to change tool from pywinauto to something else. Python isn't required.

Tkinter get OptionMenu Option List

I'm using Python 3, Tkinter module. I've looked at ttk library and one of the widgets there is an Option Menu. It's great, but I was wondering if there is a way to retrieve the list of options that are currently in use by the menu.
In this example:
Options_List=["option1","option2"]
My_Menu = OptionMenu(master, variable, *Options_List))
I'm aware that it may seem trivial. Just retrieve the Options_List variable. But now let's assume I'm making loads of options (using the same, or different lists):
Options_List=["option1","option2","option3"]
Menu_List = []
for Option in range(3):
My_Menu = OptionMenu(master, variable, *Options_List))
Menu_List.append(My_Menu)
Options_List.del(-1) #removes last item
I just took advantage of the fact that when the Option Menu is assigned, the options are the copy of the Options_List variable, not a reference to it, so when the code is executed, they all refer to their own version of Options_List.
Option output would yield:
Menu_List[0] -> ["option1","option2","option3"]
Menu_List[1] -> ["option1","option2"]
Menu_List[2] -> ["option1"]
Now you can see that I can't just retrieve Options_List, because each Option Menu has its own list to work off.
So, any ideas? Is there any way I can get hold of the list of options that my nth Option Menu is using?
The option menu is nothing more than a standard button with a menu attached to it. So, to get the values in the option menu you simply need to get the menu associated with the optionmenu, and use the methods available on the menu to get the items on the menu.
For example, let's assume that om represents the optionmenu. To get the menu you can do this:
menu = om['menu']
menu now is a reference to a Menu object. You can find out the index of the last item with the index method:
last_index = menu.index("end")
With that, you can iterate over the items in the menu. If you want the label, you can use entrycget to get the value of that attribute:
values = []
for i in range(last_index+1):
values.append(menu.entrycget(i, "label"))
With that, values will contain the values that appear on the menu.

How can I bind command to Entry box when it is in focus?

http://code.activestate.com/recipes/580770-combobox-autocomplete/
Working with the class above. As of right now, a drop down list only appears when you start typing in into the entry box, auto completing the word you're typing in.
How can i create a method that makes the full list appear when the entry box is in focus?
You can bind to the <FocusIn> event to call a function when the widget gains focus.
def do_something(event):
...
entry = tk.Entry(...)
entry.bind('<FocusIn>', do_something)

wxPython - Using filedialog to update a textctrl

I am trying to create a basic dialog to edit a settings file. I have some list of necessary files
options = ["dog", "cat", "chicken"]
I then have a column with the option names in StaticText and a column of TextCtrl to input the file location. Next to each TextCtrl is a "Select File" button.
How can I have each button select a file and then update the TextCtrl with the location? At the moment, I iterate through the options list to generate the text and buttons. I know if I were to define everything by hand, I could have a handler unique to each button which updates the the corresponding TextCtrl, but I feel like there is a better when. I would like it to be modifiable so I can just add an option to the options list, and another row of options is added.
Currently I am binding each button to self.OnSelect, but I'm not sure how to use that to update the correct TextCtrl. To reiterate I know how to use FileDialog, I am just having troubles tying each button to its corresponding TextCtrl.
selectBtn = wx.Button(self, wx.ID_FILE, label="Select File")
selectBtn.Bind(wx.EVT_BUTTON, self.OnSelect)
Should I have a unique ID instead of using wx.ID_FILE?
I am relatively new to GUI desgin so any help is appreciated. Even if you think what I'm doing may be ugly and something else would be better, please let me know
My answer was derived from here: wxPython how to put a text in TextCtrl with a button inserted by "Add" Button
By using lambda to create anonymous functions, the solution is quite simple. You can have a handler, like:
def OnSelect(self, event, ctrl):
name, path = selectFile() #gets name and path using FileDialog
ctrl.SetValue(path)
The buttons can be bound like
selectBtn = wx.Button(self, wx.ID_FILE, label="Select File")
selectEvent = lambda event, ctrl=txt: self.OnSelect(event, ctrl)
selectBtn.Bind(wx.EVT_BUTTON, selectEvent)
Works like a charm.

Don't set a Gtk.TreeView's selection when focusing?

The following code displays a window with a button and tree view. A handle for the 'clicked' signal is attached to the button and focuses the tree view. When the window is initially displayed, the tree selection has no selected items, but when the tree view receives focus, the first item is automatically selected. Is there a way to keep a selection from being made when the tree view receives focus?
Before click, button has focus and tree selection has no selected items. After click, tree view has focus, but an item has been selected.
The issue that arises from this is that I have an interface that keeps some things in sync by attaching to the 'changed' signal on the tree selection of the tree view. When the window is displayed, depending on where the tree views are in the interface, they may receive focus by default. That causes a 'changed' signal, and unexpected synchronization happens. It's possible to call set_can_focus(False) for all the tree views, but that:
only prevents keyboard cycling focus, not programmatic focus, and the selection still turns on with programmatic focus; and
seems to disable the ability to deselect a selection (e.g., by control-clicking on a row).
Similarly I can use grab_default to ensure that something else gets focus first when the window is displayed, but it doesn't keep a stray focus event from making an unexpected selection.
Based on a posted answer that says that says that selection mode SINGLE "requires at least one item to be selected", and that that explains why an element is selected on focus, I looked more into the selection mode constants. Of these, SINGLE and BROWSE seem most relevant. The pygtk documentation, GTK Selection Mode Constants, only says that:
gtk.SELECTION_SINGLE A single selection allowed by clicking.
gtk.SELECTION_BROWSE A single selection allowed by browsing with the pointer.
The GTK+3 documentation, enum GtkSelectionMode, goes into a bit more detail:
GTK_SELECTION_SINGLE Zero or one element may be selected.
GTK_SELECTION_BROWSE Exactly one element is selected. In some
circumstances, such as initially or during a search operation, it’s
possible for no element to be selected with GTK_SELECTION_BROWSE. What
is really enforced is that the user can’t deselect a currently
selected element except by selecting another element.
I don't see anything here to suggest that at least one element must be selected when the selection mode is SINGLE.
Here's code to reproduce the window and serve as an example.
from gi.repository import Gtk
# A ListStore with some words
list_store = Gtk.ListStore(str)
for selection in "Can a machine think?".split():
list_store.append([selection])
# A TreeView with a single column
tree_view = Gtk.TreeView(model=list_store)
cell_renderer = Gtk.CellRendererText()
tree_view_column = Gtk.TreeViewColumn(cell_renderer=cell_renderer,text=0,title='Words')
tree_view.append_column(tree_view_column)
# A button to focus the list
focus = Gtk.Button(label='Focus List')
focus.connect('clicked',lambda *_: tree_view.grab_focus())
# A Box to hold everything, and a Window for the Box.
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
box.add(focus) # button on top gets initial focus
box.add(tree_view) # tree_view below doesn't, and has no selected items
window = Gtk.Window()
window.add(box)
window.show_all()
Gtk.main()
Looking at the source in root/gtk/gtktreeview.c for tree_view.grab_focus(), we can see that gtk_tree_view_focus_to_cursor always gets called, and selects the first element. You can work around this, in some cases, though.
This is a nasty hack.
It overrides the grab_focus method, stores the selection before calling grab_focus, and clears the selection afterwards if there was no selection before.
def tree_view_grab_focus():
selection = tree_view.get_selection()
_, selected = selection.get_selected()
Gtk.TreeView.grab_focus(tree_view)
if selected is None:
selection.unselect_all()
tree_view.grab_focus = tree_view_grab_focus
Unfortunately it only applies when calling grab_focus from Python, other callers (such as GTK's keyboard navigation) don't.

Categories

Resources