I'm writing a unit test for a Qt application using the Squish framework. The squish scripting language is Python.
In order to make my test robust, I need to make sure that the GUI has completed an operation before checking the results.
In this GUI, I have a QTableView with an associated model. Certain operations will change the data in the model and the table will update. The update is sequenced internally by Qt.
My issue is that I need to know when the table has completed updating before I check to see whether it has the correct data.
I'm looking for ideas how to do this.
Squish not only views the QTableView GUI surface but will also access the underlying QAbstractItemModel. The data you see in your checks should therefore be live already.
But Qt GUIs are indeed full of asynchronous processing through timers, sometimes threads and signals. If you want to be notified on changes that are accompanied by a signal there's the installSignalHandler() function. If you want to wait for a particular state of to appear there's the waitFor() function. In the case of the table you may want to use
waitFor("mytable.model().rowCount() == 30")
for example.
Related
I'm writing some test functions for a form I made. There are a couple of QMessageBox that are invoked(one through QMessageBox.question method and one through the QMessageBox.information method. While my custom widget is not shown on screen, these two actually show up on screen.
I tried dismissing them by looping through widgets I get in QApplication.topLevelWidgets() and dismissing the right one, however, it seems my code only continues executing after I manually dismiss the MessageBox.
So my question is two-fold:
1) How do I keep the QMessageBox (or any widget really) from showing on screen during testing.
2) How can I programmatically accept/reject/dismiss this widget.
You can set up a timer to automatically accept the dialog. If the timeout is long, the dialog will still display for a while:
w = QtGui.QDialog(None)
t = QtCore.QTimer(None)
t.timeout.connect(w.accept)
t.start(1)
w.exec_()
For your specific case, if you don't want to touch the code being testes, you can have the timer run a function to accept all current modal widgets, as you were suggesting:
def accept_all():
for wid in app.topLevelWidgets():
if wid.__class__ == QtGui.QDialog: #or QMessageBox, etc:
wid.accept()
t = QtCore.QTimer(None)
t.timeout.connect(accept_all)
t.start(10)
I decided to use the mock module instead. It seemed better since the other solution would actually draw on screen, which is not optimal for testing.
If you have the same problem and would like to mock a question QMessageBox you can something like this:
#patch.object(path.QMessageBox, "question", return_value=QtGui.QMessageBox.Yes)
Would simulate a MessageBox in which the Yes button was clicked.
I think it makes sense with Qt testing (including PySide/PyQt) to mock your GUI interaction and do dedicated GUI testing separately as necessary.
For mocking GUI interaction, I'd use the mock library, as I myself do regularly. The drawback of this is that you have to depend on mock definitions, which may drift out of sync with respect to your production application. On the other hand, your tests will be speedier than involving the actual GUI.
For testing the GUI itself, I'd write a separate layer of tests using a GUI testing tool such as Froglogic Squish. It'll typically lead to more involved/slower tests, but you'll test your application directly, and not merely simulate the GUI layer. My approach in this regard is invest in such a tool if the budget allows, and run these tests as necessary keeping in mind they'll be relatively slow.
I'm using Qt Designer to create UI designs which I'm then converting into python code. Since I'm quite new to Qt I'd like to ask: is there a way I could implement a multi-screen application? I.e. having a next button clicked and getting a new set of options/widgets etc within the same window.
To be honest, I was developing using Kivy, and as slick as that is (especially with multiple screens) it depends on PyGame, which proves to be an enormous portability headache, so I had to switch to something else, and PyQt was the next feasible option (or so it seems).
It's called a QWizard. It is not called a multi screen application, but if you search for wizard instead, you find lots of information.
Links
Example with C++ code
[PyQt QWizard documentation] http://pyqt.sourceforge.net/Docs/PyQt4/qwizard.html
I'm developing a GUI with PyQt. The GUI has a qListWidget, a qTableWidget, and a plot implemented with Mayavi. The list refers to shapes that are plotted (cylinders and cones for example). When a shape is selected in the list, I want the shape's properties to be loaded into the table (from a dictionary variable) and the shape to be highlighted in the plot. I've got the Mayavi plotting working fine. Also, if the table is edited, I need the shape to be re-plotted, to reflect the new property value (like for a cylinder, if the radius is changed).
So, when a list item is selected -> update the table with the item's properties (from a dictionary variable), highlight the item on the plot
When the table is edited -> update the dictionary variable and re-plot the item
The Problem: when I select a list item and load data into the table, the qTableWidget ItemChanged signal fires every time a cell is updated, which triggers re-plotting the shape numerous times with incomplete data.
Is there a typical means of disabling the GUI event loop while the table is being programmatically updated? (I have experience with Excel VBA, in that context setting Application.EnableEvents=False will prevent triggering a WorksheetChange event every time a cell is programmatically updated.)
Should I have a "table update in progress" variable to prevent action from being taken while the table is being updated?
Is there a way to update the Table Widget all at once instead of item by item? (I'll admit I'm intentionally avoiding Model-View framework for the moment, hence the qListWIdget and qTableWidget).
Any suggestions?
I'm a first time poster, but a long time user of StackOverflow, so I just want to say thanks in advance for being such an awesome community!
blockSignals(bool) is intended for suppressing QObjects and their subclasses from emitting signals, thus preventing any other objects from receiving them in slots. But this is a QObject method. If you are specifically trying to prevent one object from emitting signals in response to changes that you are making, which might trigger calculations or some other expensive processing in a slot, then this is what you want.
But if your situation is that making repeated changes is causing expensive paint operations over and over (or other expensive events being generated on the widget), then you have the ability to disable updates with updatesEnabled(bool). A benefit of this method is that it recursively disables the children of the target widget, preventing them from being updated as well. So nothing in the hierarchy will receive updates until you enable again.
mainWidget.setUpdatesEnabled(False)
# do a bunch of operations that would trigger expensive events
# like repaints
mainWidget.setUpdatesEnabled(True)
Ultimately it depends on whether the source of your problem comes from triggering signals, or triggering widget events. Blocking the signals will still allow the widget to process its events, but just not notify any other listeners about it. updatesEnabled is a common way to wrap a number of list/table/tree updates. When it is enabled again afterwards, a single post update will be performed.
Signals can be temporarily blocked for any object that inherits QObject:
self.tableWidget.blockSignals(True)
# perform updates, etc
self.tableWidget.blockSignals(False)
If you disable the entire event loop, the app becomes unresponsive. And, even if the user doesn't notice, the OS might, and put up some kind of "hang" notification (like OS X's brightly-colored spinning beachball, which no user will ever miss).
You might want to disable repaints without disabling the event loop entirely. But even that's probably too drastic.
All you're really trying to do is make sure the table stops redrawing itself (without changing the way you've implemented your table view, which you admit isn't ideal, but you have reasons for).
So, just disable the ItemChanged updates. The easiest way to do this, in almost every case, is to call blockSignals(True) on the widget.
In the rare cases where this won't work (or when you're dealing with ancient code that's meant to be used in both Qt4-based and earlier projects), you can still get the handler(s) for the signal, stash them away, and remove them, then do your work, then restore the previous handler(s).
You could instead create a flag that the handlers can access, and change them so they do nothing if the flag is set. This is the traditional C way of doing things, but it's usually not what you want to do in Python.
I am attempting to create my first OS-level GUI using wxPython. I have the book wxPython in Action and have looked at the code demos. I have no experience with event-driven programming (aside from some Javascript), sizers, and all of the typical GUI elements. The book is organized a little strangely and assumes I know far more about OS GUI programming than I actually do. I'm fairly recent to object-oriented programming, as well. I'm aware that I am clearly out of my depth.
My application, on the GUI side, is simple: mostly a set of reminder screens ("Turn on the scanner," "Turn on the printer," etc) and background actions in Python either in the filesystem or from hitting a web service, but it is just complex enough that the Wizard class does not quite seem to cover it. I have to change the names on the "Back" and "Next" buttons, disable them at times, and so forth.
What is the standard process for an application such as mine?
1) Create a single wxFrame, then put all of my wxPanels inside of it, hiding all but one, then performing a sequence of hides and shows as the "Next" button (or the current equivalent) are triggered?
2) Create multiple wxFrames, with one wxPanel in each, then switch between them?
3) Some non-obvious fashion of changing the names of the buttons in wxWizard and disabling them?
4) Something I have not anticipated in the three categories above.
I don't have a good understanding of your application, but trying to force wxWizard to suit your needs sounds like a bad idea.
I suggest checking out the Demos available from the wxPython website. Go through each demo and I bet you'll find one that suits your needs.
I've personally never used wxWizard as I find it too cumbersome. Instead, I create a sequence of dialogs that do what I need.
I'm not familiar with PowerBuilder but I have a task to create Automatic UI Test Application for PB. We've decided to do it in Python with pywinauto and iaccesible libraries. The problem is that some UI elements like newly added lists record can not be accesed from it (even inspect32 can't get it).
Any ideas how to reach this elements and make them testable?
I'm experimenting with code for a tool for automating PowerBuilder-based GUIs as well. From what I can see, your best bet would be to use the PowerBuilder Native Interface (PBNI), and call PowerScript code from within your NVO.
If you like, feel free to send me an email (see my profile for my email address), I'd be interested in exchanging ideas about how to do this.
I didn't use PowerBuilder for a while but I guess that the problem that you are trying to solve is similar to the one I am trying to address for people making projects with SCADA systems like Wonderware Intouch.
The problem with such an application is that there is no API to get or set the value of a control. So a pywinauto approach can't work.
I've made a small tool to simulate the user events and to get the results from a screencapture. I am usig PIL and pytesser ORM for the analysis of the screen captures. It is not the easiest way but it works OK.
The tool is open-source and free of charge and can be downloaded from my website (Sorry in french). You just need an account but it's free as well. Just ask.
If you can read french, here is one article about testing Intouch-based applications
Sorry for the self promotion, but I was facing a similar problem with no solution so I've written my own. Anyway, that's free and open-source...
I've seen in AutomatedQa support that they a recipe recommending using msaa and setting some properties on the controls. I do not know if it works.
If you are testing DataWindows (the class is pbdwxxx, e.g. pbdw110) you will have to use a combination of clicking at specific coordinates and sending Tab keys to get to the control you want. Of course you can also send up and down arrow keys to move among rows. The easiest thing to do is to start with a normal control like an SLE and tab into the DataWindow. The problem is that the DataWindow is essentially just an image. There is no control for a given field until you move the focus there by clicking or tabbing. I've also found that the DataWindow's iAccessible interface is a bit strange. If you ask the DataWindow for the object with focus, you don't get the right answer. If you enumerate through all of the children you can find the one that has focus. If you can modify the source I also advise that you set AccessibleName for your DataWindow controls, otherwise you probably won't be able to identify the controls except by position (by DataWindow controls I mean the ones inside the DataWindow, not the DataWindow itself). If it's an MDI application, you may also find it useful to locate the MicroHelp window (class fnhelpxxx, e.g. fnhelp110, find from the main application window) to help determine your current context.
Edited to add:
Sikuli looks very promising for testing PowerBuilder. It works by recognizing objects on the screen from a saved fragment of screenshot. That is, you take a screenshot of the part of the screen you want it to find.