I think my answer is related to this answer but can't quite get my head around it.
I am now realising that my code isn't structured very well, however the current setup is:
main_run.py
app = QtGui.QApplication(sys.argv)
app.processEvents()
ui1 = new_main_ui.Ui_Form_Main()
ui1.show()
sys.exit(app.exec_())
new_main_ui
class Ui_Form_Main(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.setupUi(self)
...etc...
within this class are Qlabel objects which have their text updated as buttons are pressed on the UI.
within new_main_ui there is a call to another module (sqr_pull.py) which does some stuff. Halfway through sqr_pull.py I want to update a Qlabel in my UI, but I can't figure out how to reference the UI instance (ui1) without getting:
NameError: name 'ui1' is not defined
I've tried trying to pass variables through as I go using sys.modules[__name__] as follows:
in main_run: new_main_ui.parent1 = sys.modules[__name__]
in new_main_ui: sqr_pull.parent2 = sys.modules[__name__]
and then in sqr_pull trying to change using `parent2.QLabel.setText("blahblahblah")
but it again doesn't recognise the instance name. What's the best way to do this?
The clean way to give a function access to an object is to pass this object to the function...
# worker.py
def work(ui):
some_result = do_something()
ui.update(some_result)
other_result = do_something_else()
# ui.py
import worker
class Ui(object):
def some_action(self):
worker.work(self)
def update(self, data):
self.show_data_somewhere(data)
Hacking with sys.modules or whatever will only result in some unmaintainable mess.
Related
I'm a beginner with Kivy and I'm trying to create gui for a python app.
I have a backend programmed in python, working alone (keyboard as input) and now I want frontend with kivy. I have two questions:
- How can I run both components (back and front end) at once?
- How can I share objects of existing classes in my backend to display information in the frontend (kivy)?
classtest.py
class Test(object):
def __init__(self, attr):
self.attr = attr
gui.py
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.add_widget(Label(text='User Name'))
self.username = TextInput(multiline=False)
self.add_widget(self.username)
self.add_widget(Label(text='password'))
self.password = TextInput(password=True, multiline=False)
self.add_widget(self.password)
print self.username.text
class Login(App):
def build(self):
Window.borderless = True
return LoginScreen()
main.py
import classtest, gui
users = ['user_name1', 'user_name2', 'user_name3']
gui.Login().run()
for u in users:
test = classtest.Test(u) # this should update the user text field on the windows login automatically, but how?
In example, How can I update an element of the login window when an instance attribute value change?
Thanks many many times!
It won't update because... of a loop :P Kivy for each App().run() or a similar "run" command launches a loop and your:
for u in users:
test = classtest.Test(u)
is written after that loop. So basically it won't even execute while your app is running. Just put print('something') into that for loop and you'll see.
Example:
while True:
<do something>
<change a variable in that loop> # == nope
Which for you means that you need either:
write that into gui.py file or
set those things right in the classtest.py
The second option also depends when you use the class. If outside of the main loop, then you are in the same situation as right now, therefore - use that Test() inside of gui.py.
You won't be able to use any code after run() while the app is running. Maybe with some dirty trick, but that will bring you only troubles. The code you wrote could be used in some "cleaning", which you can also do in App.on_stop method(inside main loop).
I've implemented a timer using QTimer inside a Singleton. The Singleton is implemented using the Borg pattern. If I start a QTimer with single shot inside a function of the Singleton it won't be executed. The same call in a function outside the Singleton works well.
This is the code:
#!/usr/bin/env python
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication
class Borg():
_shared_state = {}
def __init__(self):
self.__dict__ = self._shared_state
class Timers(Borg):
def __init__(self):
Borg.__init__(self)
def update_not_working(self):
QTimer().singleShot(2000, Timers().update_not_working)
print('update not working')
def update_working():
QTimer().singleShot(2000, update_working)
print('update working')
if __name__ == '__main__':
app = QApplication(sys.argv)
print('start timer')
Timers().update_not_working()
update_working()
sys.exit(app.exec_())
The output is (no error, no exception):
start timer
update not working
update working
update working
....
Why is one call working and the other not? Is there something wrong with my implementation of the Borg or with the usage of QTimer?
print self in update_not_working and print Timers() in update working show that the Timers object before the event loop started is different from the one within:
update not working
<__main__.Timers instance at 0xb52162cc>
update working
<__main__.Timers instance at 0xb52162cc>
update working
<__main__.Timers instance at 0xb521650c>
update working
<__main__.Timers instance at 0xb521650c>
update working
<__main__.Timers instance at 0xb521650c>
update working
<__main__.Timers instance at 0xb521650c>
#classmethod should help here because it allows to call the method on an instance OR on the class as you do in the single shot statement.
Compare: When should I use #classmethod and when def method(self)?
This is actually just a matter of normal garbage-collection.
If you add some debugging code to your example like this:
class Timers(Borg):
def __init__(self):
Borg.__init__(self)
print('init:', self)
def update_not_working(self):
QTimer().singleShot(1, Timers().update_not_working)
print('update not working')
def __del__(self):
print('deleted:', self)
it will produce output like this:
start timer
init: <__main__.Timers object at 0x7f194bf53eb8>
init: <__main__.Timers object at 0x7f1940cfdb00>
deleted: <__main__.Timers object at 0x7f1940cfdb00>
update not working
deleted: <__main__.Timers object at 0x7f194bf53eb8>
update working
update working
As you can see, both of the Timers instances get deleted long before the single-shot timer sends its timeout() signal. And when they deleted, their instance-methods get deleted as well, which will automatically disconnect them from the signal. This shows that the Borg pattern does not produce a true singleton: it just mimics some of the behaviour of one.
If you use a real singleton class, like this:
class Timers2(object):
_instance = None
def __new__(cls):
if Timers2._instance is None:
Timers2._instance = object.__new__(cls)
return Timers2._instance
def update_not_working(self):
QTimer().singleShot(2000, Timers2().update_not_working)
print('update not working')
your example will work as expected. This is because there is only ever one instance, and it is kept alive by being cached as a class attribute.
Finally, the reason why the update_working() succeeds, is because it is a globally defined function. As with the class attribute, this ensures that it won't get garbage-collected until the script completes.
class Borg():
_shared_state = {}
def __init__(self):
self.__dict__ = self._shared_state
class Timers(Borg):
def __init__(self):
Borg.__init__(self)
#classmethod
def update_not_working(cls):
QTimer().singleShot(2000, Timers().update_not_working)
print('update not working')
def update_working():
QTimer().singleShot(2000, update_working)
print('update working')
if __name__ == '__main__':
app = QApplication(sys.argv)
print('start timer')
Timers().update_not_working()
update_working()
sys.exit(app.exec_())
Some environment basics
Python Version: 3.4.2
OS: Windows 8.1
Searching so far, I suspect this other question is related to my issue at hand, but I'm not sure how I'm replicating enough of the same conditions--probably my lack of in-depth python knowledge.
Simplified code to reproduce issue:
Base Class
from PySide.QtGui import *
class Interface(QWidget):
'''
Wrapper base class for GUI input QWidgets:
- buttons
- text fields
- checkboxes
- line edit
- dropdown menu (combo box)
'''
def __init__(self, parent, name, title_txt=None, qt_obj=None,
update_log_method=None):
print('Interface base class constructor has been called.') #DEBUG
self._parent = parent
self.title = None
self.name = name #also text that appears on component
self.qt_obj = qt_obj
self.inheritted_log_method = update_log_method
# don't want to create an empty text QLabel, or one with
# the text reading "None".
if title_txt:
self.title = QLabel(text=title_txt, parent=parent)
print('Interface base class constructor has been completed.') #DEBUG
def get_name(self):
return self.name
def update_log(self, message, level="INFO"):
''' '''
self.inheritted_log_method(message, level)
Inheriting Class
class IFPushButton(Interface):
''' '''
def __init__(self, name, parent, icon=None, update_log_method=None):
''' '''
# print('\n\nCHECKPOINT: pre IFPushButton super()\n\n') #DEBUG
super(IFPushButton, self).__init__(
parent=parent,
name=name,
qt_obj=QPushButton(icon, name, parent),
update_log_method=update_log_method)
self.behaviors = {}
self.qt_obj.clicked.connect(self.activate)
Something to kick it all off
if __name__ == '__main__':
# setup
import sys
app = QApplication(sys.argv)
qmw = QMainWindow()
qcw = QWidget() #central widget
qcl = QVBoxLayout(qcw) #central layout
# experimental
name = 'named button'
ifpb = IFPushButton(name=name, parent=None, icon=None, update_log_method=None)
print("as long a I don't touch the ifpb instance, everything seems to be okay.")
print("...but the second I do...")
qcl.addWidget(ifpb)
self.show()
print("name of created push button:", ifpb.get_name())
# proper teardown
sys.exit(app.exec_())
I run this all inside one module, interface.py, and when I run it...
C:\Path\To\Module> python interface.py
Interface base class constructor has been called.
Interface base class constructor has been completed.
as long a I don't touch the ifpb instance, everything seems to be okay.
...but the second I do...
Traceback (most recent call last):
File "c_interface.py", line 167, in <module>
qcl.addWidget(ifpb)
RuntimeError: '__init__' method of object's base class (IFPushButton) not called.
The part that confuses me is how the print statements in the base class, Intefrace, are obviously being called as they are printing--but it still raises a RuntimeError saying that it hasn't been initialized, and of course fails to get so far as to create the app window. Most of the related messages I've found on stackoverflow are related to people initializing things incorrectly with the super() method--but I have quintuple-checked my super inits, and everything I see tells me it should be working, with the exception of what I linked above.
If I could understand more about why this is happening I'm hoping I can find a way to work around it. Any assistance is much appreciated--thanks in advance!
In the meantime I'm going to try to find how I might be unintentionally deepcopy-ing a C++ object...
EDIT: included the url in the link to other stack overflow post.
Adding a super call to the Interface class constructor is required:
def __init__(self, parent, name, title_txt=None, qt_obj=None, update_log_method=None):
super(Interface, self).__init__(parent)
...
Also, you're calling self.show(), where you probably mean qmw.show().
Am struggling to comprehend how to split code in (Py)Qt. The aim is to have the design & navigation tabs in QMainWindow, each tab triggering code in other files. So far it only launches with the ActionClass in the same document / putting in an external document causes 'app not defined' when clicking the tab. The below works without errors, but is clunky.
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.u = Ui_MainWindow()
self.u.setupUi(self)
self.u.tabs.currentChanged.connect(self.TabsChanged)
def TabsChanged(self, i):
if i == self.u.tabs.indexOf(self.u.tabFirst): ActionClass.__init__
class ActionClass(Main):
def __init__(self):
app.u.lineEdit.setText("test")
app = Main()
app.show()
sys.exit(app.exec_())
The examples I keep seeing have all code in one document. Is there another way to do this e.g. where the ActionClass is in another file/writing u.lineEdit.setText instead of app.u.lineEdit.setText. It seems inheritance & an instance of Main can't be accessed from the ActionClasses doc, so I can't see how they would communicate back to the Main?
Much appreciated
As suggest #M4rtini you can separate your code into python modules. And then import them (use them) in your main module.
For instance the code you posted can be separated in to files:
# actions_class.py
class ActionClass(Main):
def __init__(self):
app.u.lineEdit.setText("test")
and
# main.py
from action_class import ActionClass # This line no need much explanation ;)
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.u = Ui_MainWindow()
self.u.setupUi(self)
self.u.tabs.currentChanged.connect(self.TabsChanged)
def TabsChanged(self, i):
if i == self.u.tabs.indexOf(self.u.tabFirst): ActionClass.__init__
app = Main()
app.show()
sys.exit(app.exec_())
In order to understand how import works see the link I left you above.
More explanation
Lest's see:
The correct way of executin code inside a __init__ method is creating an instance. See the example below.
class A:
def __init__(self):
print("Executing A.__init__")
print("Doing things wrong")
A.__init__ # This don't print enything
print("Doing things well")
A() # This works as expected.
So, you line reads:
if i == self.u.tabs.indexOf(self.u.tabFirst): ActionClass.__init__
and should reads:
if i == self.u.tabs.indexOf(self.u.tabFirst): ActionClass()
On the other hand, is a bad practice put code that's not for initialize the instance inside the __init__ methods.
If you don't need the instance but yet you want to store the functions inside a class (something like a c++ namespace) you creating
use #staticmethod decorator.
class A:
#staticmethod
def foo():
print("Oh, wow, a static method in Python!")
A.foo()
So, your ActionClass could be rewritten as:
class ActionClass(Main):
#staticmethod
def do_action:
app.u.lineEdit.setText("test")
ans then you can use it like this:
if i == self.u.tabs.indexOf(self.u.tabFirst): ActionClass.do_action()
The code is along the lines of this:
class Solver(QDialog):
def __init__(self, parent=None):
blabla
def solver(self):
return qlineedit1.text()
class access(QMainWindow):
prda = Solver().solver()
print prda
The problem is that prda is an empty string. If i put "print qlineedit1.text()" in the Solver class, the text is displayed as it should be. However, when "transferred" to a different class, the string is empty. The weirdest part that it is there - if I do type(prda), I get QString type output.
So, how would I get prda to assume the value of qlineedit1.text() ? I was thinking of writing the text to file in Solver class, and then reading it from access class, but there has to be another solution. By the way, the Solver class and the access class are two dialogs.
Help?
Since next code works as expected, all i can think out without seeing more of your code is that your qlineedit1 is empty.
from PyQt4 import QtGui
class Solver(QtGui.QDialog):
def __init__(self, parent=None):
print "in Solver init"
def solver(self):
return "in solver() method"
class access(QtGui.QMainWindow):
prda = Solver()
print prda.solver()
#this will work too:
#prda = Solver().solver()
#print prda
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
acc=access()
acc.show()
sys.exit(app.exec_())