How to speed up browsing application via pywinauto? - python

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.

Related

Preselected Radio Button Not Functioning wxPython

I'm working to develop an application where the user enters information into text boxes that are generated when a radio button is selected and the information would be stored in a CSV file. When the application is opened, the first radio button is selected. While this is not an issue, none of the text boxes appear. If one of the radio buttons is selected then the first one is selected the text boxes appear no problem.
Here is the code that generate the radio buttons:
self.radioStaticBox = wx.StaticBox(self.panel,-1,"Material Type: ")
self.radioStaticBoxSizer = wx.StaticBoxSizer(self.radioStaticBox, wx.VERTICAL)
self.radioBox = sc.SizedPanel(self.panel, -1)
self.radioBox.SetSizerType("horizontal")
self.isoRadioButton = wx.RadioButton(self.radioBox,-1, "Isotropic")
self.orthoRadioButton = wx.RadioButton(self.radioBox,-1, "Orthotropic")
self.orthotRadioButton = wx.RadioButton(self.radioBox,-1, "Orthotropic (with thickness)")
self.isoRadioButton.SetValue(True)
self.radioBox.Bind(wx.EVT_RADIOBUTTON, self.set_type)
And the function that the radio buttons are being bound to:
def generate_params(self, event):
self.matStaticBoxSizer.Clear(True)
if self.matType == "Iso":
idSb = wx.StaticBox(self.panel, 0, "Name:")
idSbs = wx.StaticBoxSizer(idSb, wx.HORIZONTAL)
self.idText = wx.TextCtrl(self.panel)
idSbs.Add(self.idText, 0, wx.ALL|wx.LEFT, self.margin)
....
Thanks for the help!
In short, you appear to be defining part of the display in the initial section of your code and then another section in the define function generate_params, this will by definition, excuse the pun, ensure that you do not see what is defined there until that function executes.
Define all of the display items together in the initial section and then populate them within your functions, as appropriate to the function.
In other words, define the screen in one place, the beginning, populate values as and when.

Trying to check multiple qt radio buttons with python

I need to check multiple radio buttons from a qt ui with python.
Up to now we are using something similar to:
if main.ui.radioButton_1.isChecked():
responses["q1"] = "1"
elif main.ui.radioButton_2.isChecked():
responses["q1"] = "2"
elif main.ui.radioButton_3.isChecked():
responses["q1"] = "3"
if main.ui.radioButton_4.isChecked():
responses["q2"] = "1"
elif main.ui.radioButton_5.isChecked():
responses["q2"] = "2"
elif main.ui.radioButton_6.isChecked():
responses["q2"] = "3"
...
Since there are very many buttons and many different categories (q1, q2, ...) I was thinking of optimizing it a bit. So this is what I hoped would work (adopted from How to get the checked radiobutton from a groupbox in pyqt):
for i, button in enumerate(["main.ui.radioButton_" + str(1) for i in range(1, 8)]):
if button.isChecked():
responses["q1"] = str(i - 1)
I get why this doesn't work but writing it I hoped it would.
So I tried to iterate through the buttons using something similar to (Is there a way to loop through and execute all of the functions in a Python class?):
for idx, name, val in enumerate(main.ui.__dict__.iteritems()):
and then use some modulo 3 and such to assign the results. But that doesn't work either. Not sure if it's because i used __ dict __ or something else. The error I got was:
TypeError: 'QLabel' object is not iterable
Now some people could say that implicit is better that explicit and also because of readability the if elif chain is good the way it is but there are 400+ lines of that. Also after reading this post, Most efficient way of making an if-elif-elif-else statement when the else is done the most?, I thought there must be a better and more efficient way of doing this (see examples 3.py and 4.py of the of the accepted answer). Because I need to check the Boolean value of main.ui.radioButton_1.isChecked() and then assign thevalue according to the Buttons group (q1, q2,...), I haven't managed to implement the solution using dictionaries as described in the post.
Am I stuck with the if elif chain or is there a way to not only reduce the LOC but also make the code more efficient (faster)?
It looks like you have used Qt Designer to create your ui, so I would suggest putting each set of radio buttons in a QButtonGroup. This will give you a simple, ready-made API for getting the checked button in a group without having to query each button individually.
In Qt Designer, buttons can be added to a button-group by selecting them, and then choosing Assign to button group > New button group from the context menu. The button IDs (which you will need to use later) are assigned in the order the buttons are selected. So use Ctrl+Click to select each button of a group in the correct order. The IDs start at 1 for each group and just increase by one for each button that is added to that group.
When a new button-group is added, it will appear in the Object Inspector. This will allow you to select it and give it a more meaningful name.
Once you've created all the groups, you can get the checked button of a group like this:
responses["q1"] = str(main.ui.groupQ1.checkedId())
responses["q2"] = str(main.ui.groupQ2.checkedId())
# etc...
This could be simplified even further to process all the groups in a loop:
for index in range(1, 10):
key = 'q%d' % index
group = 'groupQ%d' % index
responses[key] = str(getattr(main.ui, group).checkedId())
Another way to do it is using signals. If you had lots of radio button in an application, I suspect this kind of approach would be noticeably faster. For example:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MoodExample(QGroupBox):
def __init__(self):
super(MoodExample, self).__init__()
# Create an array of radio buttons
moods = [QRadioButton("Happy"), QRadioButton("Sad"), QRadioButton("Angry")]
# Set a radio button to be checked by default
moods[0].setChecked(True)
# Radio buttons usually are in a vertical layout
button_layout = QVBoxLayout()
# Create a button group for radio buttons
self.mood_button_group = QButtonGroup()
for i in xrange(len(moods)):
# Add each radio button to the button layout
button_layout.addWidget(moods[i])
# Add each radio button to the button group & give it an ID of i
self.mood_button_group.addButton(moods[i], i)
# Connect each radio button to a method to run when it's clicked
self.connect(moods[i], SIGNAL("clicked()"), self.radio_button_clicked)
# Set the layout of the group box to the button layout
self.setLayout(button_layout)
#Print out the ID & text of the checked radio button
def radio_button_clicked(self):
print(self.mood_button_group.checkedId())
print(self.mood_button_group.checkedButton().text())
app = QApplication(sys.argv)
mood_example = MoodExample()
mood_example.show()
sys.exit(app.exec_())
I found more information at:
http://codeprogress.com/python/libraries/pyqt/showPyQTExample.php?index=387&key=QButtonGroupClick
http://www.pythonschool.net/pyqt/radio-button-widget/

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.

How to make GtkMenuToolButton open the same menu when 'clicked' signal is emitted?

I am using GtkMenuToolButton and it has a button and a menu. When you click on the arrow the menu is opened. I'd like to make the button open that same menu as well. Simply emitting "show-menu" in the "clicked" callback did not work. Please help how to make this work.
I have currently ended up doing this:
Instead of GtkMenuToolButton I have GtkToolItem with custom content
In custom content I have GtkMenuButton
Inside that one, I delete the default GtkArrow and replace it with 1x2 GtkGrid which has a Label + GtkArrow
As a whole it does what I want =)
When you create the menu, save a reference to it as self.tool_button_menu or something; then in the clicked callback, call
self.tool_button_menu.popup(None, None, None, None, 0, Gtk.get_current_event_time())
The first two Nones are the parent menu and the parent menu item (not applicable). The second two Nones are a positioning callback function (more on that in a minute) and data to pass to it. 0 is the mouse button if the menu was initiated by a mouse button press (but you should pass 0, because I think in your case it's either a mouse button release or a key press.) And the last parameter is the timestamp to give to the menu popup event.
Now the positioning function. It takes two parameters and returns three:
def positioning_function(menu, data=None):
# ...magic...
return x, y, push_in
push_in should be True if you want the menu to be repositioned so that it always fits on the screen. Seems like a good idea. You can get good values for x and y by looking at the tool button's get_allocation(); read the x, y, width and height attributes of that object and calculate a nice place to put the menu.

Dynamically modifying/refreshing menu contents in PyGTK

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)

Categories

Resources