purpose of wx.App when just showing a frame is enough - python

What is the use of the wx.App class (apart from what the documentation says), when you can just create a frame and .Show(True) it?
When should a wx.App class be used, or why shouldn't you create a frame and just show it?

You have to create a wx.App. If you try to instantiate wxPython classes before creating the app, it will fail:
>>> import wx
>>> frame = wx.Frame(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python26\lib\site-packages\wx-2.8-msw-unicode\wx\_windows.py", line 505, in __init__
_windows_.Frame_swiginit(self,_windows_.new_Frame(*args, **kwargs))
wx._core.PyNoAppError: The wx.App object must be created first!
There will always be one, and only one, wx.App. Instantiating it initializes wxPython - creates the window thread, etc.

The wx.App does a bunch of behind the scenes stuff to make your application work. It does the main loop, which is what waits for the user to do something and then responds. You have to have it. As FogleBird mentioned. By the way, you should NOT have more than one. Doing so will cause weird issues, if it works at all.

Sub-classing wx.App is a valid reason when you have to do some extra functionality in your app. This applies for initiating a connection to a database or connecting over a network, loading some external data/files before firing up the main.
class MyApp(wx.App):
def OnInit(self):
# Do some work here
return True
def __init__(self):
main_frame = MyFrame(*args, **kwargs)
main_frame.Show()
def OnExit(self):
# Clean up and close the resources from the OnInit() method

Related

PyQt5: restricting dock widget to only move inside frame

Im creating an application with PyQt5. I wanted to create a widget that lives inside a frame and that the user can move and resize at will. Following this answer i came to the conclusion that i could use QDockWidgets to do this.
Thing is: i want my QDockWidget to only exist and be movable inside a frame of my mainwindow. It seems that setAllowedAreas() could do this restriction for me but it can only be used with QtDockWidgetAreas, and preexisting areas are just default areas of the main window.
So currently my code is like this:
#!/usr/local/bin/pydm
from PyQt5.QtWidgets import QDockWidget
from pydm import Display
class Window(Display): ##Main window class.
def __init__(self, args, macros):
super(Window, self).__init__(None, args, macros,
ui_filename="main.ui")
self.pushButton.clicked.connect(self.createDock)
def createDock(self):
self.dock=QDockWidget()
self.dock.raise_()
self.dock.show()
(pydm is amodule that inherits all classes from PyQt and allows all default functionalities to work just fine.)
The main.ui file created with QtDesigner is just a widget window with a frame and a pushbutton. The generated window is in the image below:
So obviously my DockWidget can go outside the frame. I tried self.dock.setAllowedAreas(self.myFrame) but i got the expected error when clicking the button:
Traceback (most recent call last):
File "./main.py", line 16, in createDock
self.dock.setAllowedAreas(self.frame)
TypeError: setAllowedAreas(self, Union[Qt.DockWidgetAreas, Qt.DockWidgetArea]): argument 1 has unexpected type 'QFrame'
i also tried:
from PyQt5.QtCore import Qt
and in the createDock(self) function:
myArea = Qt.DockWidgetArea(self.frame)
self.dock.setAllowedAreas(myArea)
But i got:
Traceback (most recent call last):
File "./main.py", line 17, in createDock
myArea = Qt.DockWidgetArea(self.frame)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'QFrame'
Looking at the QDockWidgetArea it doesnt look that i can make a custom Area at all. At least not that i could find.
Is there any way at all to achieve the functionality that i want?

AttributeError when trying to place a label

I am trying to make a basic clicker game in Python using Tkinter. I am getting an AttributeError when trying to place a label that was created in another function from the same class (see code snip). The code running the tkinter window is NOT in the main thread, and the main thread is acting as the console. I am running aClass.funcB from the console, AFTER aClass.funcA has already executed.
class aClass:
def funcA(self):
self.aLabel = Label(top)
global window
window = self
def funcB():
global window
self = window
self.aLabel.place(<argsHere>)
The error I am getting is:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<filePathHere>", line 334, in funcB
self.aLabel.place(<argsHere>)
AttributeError: 'NoneType' object has no attribute 'aLabel'
If anyone wants to look at the actual code (that I dont understand parts of myself) please tell me.
EDIT: Here is a link to the github with the actual files https://github.com/LeotomasMC/Click-Clicker
EDIT 2: On a side note, if anyone could tell me how the window actually starts in this instance, that would be great
Try and remove all the self as I think that it keeps it to only that def, because of that funcB doesn't know that aLabel exists.
Methods in Python classes need self as first argument:
def funcB(self):
global window
self = window
self.aLabel.place(<argsHere>)
You need this argument to access aLabel.

wxPython + weakref proxy = closing wx.Frame does not yield None

I'm currently making a Python application with wxWigets that has two windows. The first one is the main "controller" window, and the second one is intended to be a data display window.
I want to have some mechanism in place for the first window to know wherever the second window was already spawned, and if yes, if it was closed by the user. I though about using Python's weakref.proxy(), since based on my little understanding of the language, it seemed that if an object is closed/destroyed/deallocated/GC'ed, any attempts to call my proxy would return a None value, which could be conveniently checked with Python's is None / is not None operators.
As long as the window is spawned once, the proxy works as intended, and returns None if the window is not yet spawned, or a reference to the object otherwise. But as soon as I close the secondary window, the proxy object won't revert to None as expected, and my application will crash with a ReferenceError: weakly-referenced object no longer exists.
I remember trying to solve this previously and the most functioning solution I found was checking the object's class name against an internal wx class, like:
if windowObject.__class__.__name__ is not "_wxPyDeadObject": #do stuff
This, however, seems like a very hackish solution to me, and I'd like to know if there's any better way out other than the above. Below is some basic code which reproduces this issue of mine.
import wx
import weakref
class SillyWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent=None, title="Spawned Window")
self.Show()
class ExWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent=None, title="Main Window")
self.panel = wx.Panel(self)
self.button = wx.Button(self.panel, label="Spawn window!")
self.Bind(wx.EVT_BUTTON, self.spawn, self.button)
self.txt = wx.TextCtrl(self.panel, pos=(0,100))
self.wind = None
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.update, self.timer)
self.timer.Start(50)
self.Show()
def spawn(self,event):
if self.wind is None: # Preventing multiple spawning windows
self.wind = weakref.proxy(SillyWindow())
def update(self,event):
if self.wind is not None:
self.txt.SetValue(str(self.wind))
else:
self.txt.SetValue("None")
app = wx.App(False)
frame = ExWindow()
app.MainLoop()
As you've seen, when a wx widget object has been destroyed the Python proxy object's class is swapped with one that changes it to raise an exception when you try to use it. It also has a __nonzero__ method so you can do things like this instead of digging into the guts of the object to find it's class name:
if not windowObject:
# it has been destroyed already
return
Another thing to keep in mind is that top-level windows are not destroyed at the time they are closed or their Destroy method has been called. Instead they are added to a pending delete list which is processed as soon as there are no more pending events. You can test for that case (closed but not yet destroyed) by calling the frame's IsBeingDeleted method. Also, the C++ parts of the UI objects hold their own reference to the Python object too, although that will be decref'd when the C++ object is destroyed. So some or all of these things could be interfering with your weafref approach. Personally I would just use an if statement like the above. Short. Sweet. Simple.
P.S. Some of the details I've mentioned here are specific to wxPython Classic, and are not handled the same in Phoenix. However using an if statement like the above still works.

Update status bar in main window from another script pyqt

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
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.
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.

How can I properly read out spin buttons in pyGTK?

For a small timer app I want to write a GTK interface where I can set the desired time. Here is a picture of the interface:
However, I am having trouble reading out the fields of the spin buttons. My envisaged procedure for this is the following:
Read out the buttons using methods for each button
Here is one of the methods that does this:
# Get the fields of the spinbuttons
def get_seconds(self, widget, spin):
self.rSeconds = spin.get_value_as_int()
It is then called like this:
button = gtk.Button("Start")
button.connect("clicked", self.get_seconds, spinnerS)
Create a timer object with the data from the buttons
This is planned to be accomplished using this method:
# Create the timer object ...
def prepare_timer(self, widget, hours, minutes, seconds, title, text):
self.timer = eggTimer(hours, minutes, seconds, title, text)
Which is called here:
button.connect("clicked", self.prepare_timer, self.rHours, self.rMinutes, self.rSeconds, "some title", "some text")
Unfortunately, when running the script I get the following error message:
Traceback (most recent call last):
File "GTKInterface.py", line 140, in <module>
SpinButtonExample()
File "GTKInterface.py", line 126, in __init__
button.connect("clicked", self.prepare_timer, self.rHours, self.rMinutes, self.rSeconds, "Title", "Text")
AttributeError: SpinButtonExample instance has no attribute 'rSeconds'
To check whether there really is no instance of that variable, I programmed a short method to print it:
def returnS(self, widget):
print self.rSeconds
And surprisingly this method can "see" self.rSeconds. This makes me wonder what determines the visibility of the variable. What am I doing wrong to read this out?
You try to pass the attribute self.rHours to the connect method, but at that point the attribute doesn't exist yet (the clicked handlers haven't executed yet).
Note that even if you fill in self.rHours before calling connect, it will pass the value at the time of connecting, not at the time of the handler executing.
You can solve this by passing self.rHours etc directly to eggTimer in prepare_timer.
But it would be even easier to just combine all the click handlers into one, and use local variables instead of self.rHours etc. There's no reason to split your code over many click handlers like this.
Edit: BTW, you can also use nested functions instead of methods:
...
def prepare_timer(widget):
self.timer = eggTimer(
spinnerH.get_value_as_int(),
spinnerM.get_value_as_int(),
spinnerS.get_value_as_int(),
"Title", "Text")
button.connect("clicked", prepare_timer)
...
Keep it simple!
Going off of adw's answer recommending one click handler, a simple addition to your pastebin code would be:
def read_and_prepare(self,spinnerS,spinnerM,spinnerH,title,text):
self.get_seconds(spinnerS)
self.get_minutes(spinnerM)
self.get_hours(spinnerH)
self.prepare_timer(elf.rHours, self.rMinutes, self.rSeconds, title, text)
and only have
button.connect("clicked", self.read_and_prepare,spinnerS,spinnerM,spinnerH,"Title","Text")
for the connection code.
You could also probably redesign a bit to avoid all the get_* calls and just read the values in the click handler.

Categories