0

I'm creating a GUI to run a fatigue analysis program in Python using PyQt. When the user clicks 'Start' in the main window to start the analysis a function in another file is called, which then chugs away (potentially for hours) until complete.

I'd like to find a way to update the status bar in the main window from within the separate fatigue analysis file. I don't care how I achieve this, but I'd really rather not just copy and paste everything from the fatigue analysis file into the GUI file (in which case I'd know how to update the status bar).

So far I've tried starting the GUI using the code below which is global (MainWindow is a class with all the stuff to set up the GUI, and updateStatus is a method within that class to update the status bar of the GUI):

app = QtGui.QApplication(sys.argv)
TopWindow = MainWindow(LastData)
TopWindow.show()
sys.exit(app.exec_())

In the Fatigue Analysis file I have the following code, which is called within a loop:

TopWindow.updateStatus(PercentComplete)

This throws the error below:

NameError: global name 'TopWindow' is not defined

I don't understand why this doesn't work, but it definitely doesn't! Any ideas for a workaround?

Thanks,

Pete

Peter Greaves
  • 327
  • 5
  • 16

2 Answers2

3

I believe you misunderstood the scope of a variable. Let me explain.

When you write the following code,

def myfunction():
    a = 5
    print(a) # OK

b = a # This line fails

you will get a failure, because a is local to myfunction, that is to say it only exists inside that function, and the last statement refers to a variable that is not known to python. The same holds for modules. Variables defined in one module are not accessible to the other modules... Unless you really want to do crappy things.

A nice solution is to think your modules as a main one, calling functionalities from the other ones. These functionalities can be classes or functions, in other words, you will pass the reference to your main window as an argument.

Main module

import fatigue_analysis

class MainWindow(QtGui.Window):
    # ....
    def start_file_compute(self, path_to_file):
        # Pass a reference to self (MainWindow instance) to the helper function
        fatigue_analysis.start(path_to_file, self)
    # ....

fatigue_analysis.py

def start(path_to_file, top_window):
    # ....
    top_window.updateStatus(PercentComplete)
    # ....

That said, you may consider using QtCore.QThread to run your long computation while leaving the GUI responsive. In this case, beware that Qt do not like very much that one thread tries to modify a member of another thread (i.e. you main window). You should use signals to pass information from on thread to another.

Community
  • 1
  • 1
Cilyan
  • 7,883
  • 1
  • 29
  • 37
  • Spot on, I thought because I instanced the main window globally it would be available in the sub function but your suggestion worked, thanks! Just need to multi thread it now as it freezes during execution. – Peter Greaves Feb 05 '14 at 14:20
  • Could you point me in the direction of an example using signals between threads? I'm really struggling to get it to work. – Peter Greaves Feb 05 '14 at 16:38
  • 1
    I have some sample applications, but they are rather complicated for a demo, so I wrote you one from scratch: https://gist.github.com/Cilyan/8831684 I'm far from knowing everything as a Qt programmer, so there may be some things that could be improved... – Cilyan Feb 05 '14 at 19:55
  • I don't have the points yet to offer a precious metal for your trouble but I would if I could! thanks very much, I'm sure this will be useful to someone else too, particularly with the comments. What version of python are you using? – Peter Greaves Feb 06 '14 at 10:15
  • No problems. I usually use Python 3.3, this example was done with 3.2 as I have PyQt installed only for that version in this PC. Python 2.x should work the same, if it doesn't you may need to replace `super` by the parent class and a reference to self, e.g. `QtGui.QApplication.__init__(self, sys.argv)` instead of `super().__init__(sys.argv)`. – Cilyan Feb 06 '14 at 10:34
1

You can do the following:

In the MainWindow class, you should first create a statusbar: self.statusbar = self.statusBar()

Then, from the analysis code, pass a reference to the MainWindow object and from there you can update the statusbar as you wish using this:

main_window_object.statusbar.showMessage(PercentComplete)

EDIT: I do not know what the LastData is, but you would need to inherit from the QtGui.QMainWindow ideally.

gravetii
  • 9,273
  • 9
  • 56
  • 75