I created a widget in Kivy language, it has some properties which I'm using some of them inside this widget's python code. Since I'm using "id" keyword inside .kv file, they all have id's. I need to use same ids inside Python code. Here is the workaround:
CcaSwitch:
id: out0_bit3
name: "out0_bit3"
I'm using self.name in place of self.id in python code. How can I achieve same goal without a "duplicate" entry for each widget?
Thanks.
Edit
I'm using the "name" variable inside Python code like this:
class CcaSwitch(BoxLayout):
name = StringProperty()
def __init__(self, *args, **kwargs):
# ...
Clock.schedule_interval(self.refresh, 5)
def refresh(self, dt):
self.comm.publish(get_bool_status(self.name), self.routing_key)
So, I need a string, which identifies this widget instance itself, in a DRY way.
I would like to achieve something like that:
In .kv file:
CcaSwitch:
id: out0_bit3
In Python I'd like to use it like:
class CcaSwitch(BoxLayout):
def __init__(self, *args, **kwargs):
# ...
self.name = self.id.__name__
Clock.schedule_interval(self.refresh, 5)
def refresh(self, dt):
self.comm.publish(get_bool_status(self.name), self.routing_key)
You don't say why you need to use these ids in python. But you can access a widget that has an id through its parent's ids dictionary (this is a special dictionary that can be queried using dot notation, e.g. if the dict variable is x, and it has element 'apple' in it, you can get its value with x.apple).
So for your example, if you have in .kv:
<ExampleWidget>:
CcaSwitch:
id: out0_bit3
state: 'ON'
you can .py:
w = ExampleWidget()
print(w.ids.out0_bit3.state)
w.ids.out0_bit3 in this case returns a reference to that CcaSwitch widget.
EDIT
As I said in the comments, there's only one way to get the id value as a string - using its parent's ids dictionary. So the following will only work if the CcaSwitch widget has a parent widget (i.e. it's not the main widget):
class CcaSwitch(Widget):
name = ''
def __init__(self, *args, **kwargs):
super(CcaSwitch, self).__init__(**kwargs)
Clock.schedule_once(self.load_name)
Clock.schedule_interval(self.refresh, 5)
def load_name(self, *l):
for id_str, widget in self.parent.ids.iteritems():
if widget.__self__ is self:
self.name = id_str
return
def refresh(self, dt):
self.comm.publish(get_bool_status(self.name), self.routing_key)
What it does, is go through the ids dict of its parent, and extracts its id string. You cannot call self.load_name directly from init, because it might not have the parent yet etc. So we schedule it for the next frame.
If the widget has no parent, you're out of luck and you either have to do like you did before, or if the name doesn't have to be human readable or the same across different runs, you can in init set self.name = str(self).
Related
I am trying to have the user upload a file to the application on one QWizardPage, and then be able to re-use that same file path on another QWizardPage. However, from my code
class ExecutePage(QtWidgets.QWizardPage):
def __init__(self,*args,**kwargs):
super().__init__()
def initializePage(self):
self.setTitle("Choose file to execute")
self.setSubTitle("Please choose a file to execute")
self.myTextBox = QtWidgets.QTextEdit(self)
self.myTextBox.setGeometry(QtCore.QRect(100, 0, 350, 50))
self.uploader = QtWidgets.QPushButton("upload",self)
self.uploader.clicked.connect(self.get_file_name)
def get_file_name(self):
self.name = QtWidgets.QFileDialog.getOpenFileName(self.uploader,'Choose File to Run')[0]
self.registerField("file_name",self.name,"currentText")
class ConclusionPage(QtWidgets.QWizardPage):
def __init__(self,*args,**kwargs):
super().__init__()
def initializePage(self):
self.setSubTitle(self.field("file_name"))
I am getting an error
TypeError: registerField(self,str,QWidget,property: str = None,
changedSignal: PYQT_SIGNAL = 0): argument 2 has unexpected type 'str'
Is there a simple way to convert this specific string (self.name) into a QWidget that is able to be passed onto other pages in the Wizard (i.e. in this example, onto the subtitle field in the Conclusion Page)?
I've looked through the documentation but can't figure it out so would appreciate a few pointers. Thanks.
You can only use registerField() method to pass a qproperty to the QWidget, in the case of QFileDialog it is not possible since there is no q-property associated with the selection also getOpenFileName() is a static method and getting the object is a complicated task, There are 2 possible solutions, the first is to create a class that inherits from QFileDialog and has a qproperty associated with the selection or use the magic of python to pass the values. The last one is the method I will use.:
class ExecutePage(QtWidgets.QWizardPage):
...
def get_file_name(self):
name, _ = QtWidgets.QFileDialog.getOpenFileName(self.uploader,'Choose File to Run')
self.wizard().file_name = name
class ConclusionPage(QtWidgets.QWizardPage):
...
def initializePage(self):
if hasattr(self.wizard(), 'file_name'):
self.setSubTitle(self.wizard().file_name)
I have a GUI, and through it I load some data. When a file is loaded, its filename is used as an identifier, which populates the GUI and also a dictionary underneath, to keep track of the current state for each file.
However, with this approach I can't get any autocomplete from the MetaData class, e.g. when I want to access data.container.[GUIcurrentFile].one_of_many_attributes.
Is there a way around this? Perhaps keeping files in memory in an entirely different fashion? What do people normally do in this scenarios? I'm not too familiar with GUI development.
class Data:
def __init__(self):
self.container = dict()
def load(self, name):
self.container[name] = MetaData()
class MetaData:
def __init__(self):
self.one_of_many_attributes = None
# This is instantiated in the main part of the GUI, e.g. self.data = Data()
data = Data()
## Series of events happening through the GUI
# Grab loaded file through a GUI
GUIcurrentFile = "file1"
data.load(GUIcurrentFile)
GUIcurrentFile = "file2"
data.load(GUIcurrentFile)
# Each file has separate attributes
data.container[GUIcurrentFile].one_of_many_attributes = "foo"
# File is removed from GUI, and can easily be removed from dictionary internally
data.container.pop(GUIcurrentFile)
Okay, so type hinting finally clicked for me. I hope the original title makes sense, in relation to this answer. Else, feel free to edit it.
Defining MetaData first, it's very straight forward to add type hinting for PyCharm, if a method is implemented to return an object of type "MetaData".
class MetaData:
def __init__(self):
self.foo = None
self.really_long_name = None
class Data:
def __init__(self):
self.container = dict()
def load(self, name):
self.container[name] = MetaData()
def get(self, name) -> MetaData: # specify what dict lookup returns
return self.container[name]
data = Data()
data.load("file1")
data.get("file1").foo
I created the following class:
import loader
import pandas
class SavTool(pd.DataFrame):
def __init__(self, path):
pd.DataFrame.__init__(self, data=loader.Loader(path).data)
#property
def path(self):
return path
#property
def meta_dict(self):
return loader.Loader(path).dict
If the class is instantiated the instance becomes a pandas DataFrame which I wanted to extend by other attributes like the path to the file and a dictionary containing meta information (called 'meta_dict').
What I want is the following: the dictionary 'meta_dict' shall be mutable. Namely, the following should work:
df = SavTool("somepath")
df.meta_dict["new_key"] = "new_value"
print df.meta_dict["new_key"]
But what happens is that every time I use the syntax 'df.meta_dict' the method 'meta_dict' is called and the original 'meta_dict' from loader.Loader is returned such that 'df.meta_dict' cannot be changed. Therefore, the syntax leads to "KeyError: 'new_key'". 'meta_dict' shall be called only once and then never again if it is used/called a second/third... time. The second/third... time 'meta_dict' should just be an attribute, in this case a dictionary.
How can I fix this? Maybe the whole design of the class is bad and should be changed (I'm new to using classes)? Thanks for your answers!
When you call loader.Loader you'll create a new instance of the dictionary each time. The #property doesn't cache anything for you, just provides a convenience for wrapping complicated getters for a clean interface for the caller.
Something like this should work. I also updated the path variable so it's bound correctly on the class and returned in the path property correctly.
import loader
import pandas
class SavTool(pd.DataFrame):
def __init__(self, path):
pd.DataFrame.__init__(self, data=loader.Loader(path).data)
self._path = path
self._meta_dict = loader.Loader(path).dict
#property
def path(self):
return self._path
#property
def meta_dict(self):
return self._meta_dict
def update_meta_dict(self, **kwargs):
self._meta_dict.update(kwargs)
Another way to just cache the variable is by using hasattr:
#property
def meta_dict(self):
if not hasattr(self, "_meta_dict"):
self._meta_dict = loader.Loader(path).dict
return self._meta_dict
I have QT based GUI, wherein I read contents of LineEdit from a text file. The GUI is represented by a class (there are many such GUI selected on user's choice), whereas I want to keep the function of reading and displaying outside of class. So, for this I created an object of the said class and passed it to outer function. But, it does not display the words read from a file in the allocated boxes in GUI. Moreover when I equate the object to self, then only the texts are displayed. Following is the code fragment.
class MainClass(QtGui.QWidget, Ui_MyClass):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
def fillBoxes(self):
self.messageBox.setText("Reading File !!")
time.sleep(2)
classObj = MainClass() # object
print "entering func.."
ret = readFile(classObj,'REX011')
print self, classObj
def readFile(resClass,res):
......functioning related to file read....
.....
resClass.messageBox.setText("File Read Complete!!") # No display
The console output for print statement above yields
<myFile.MainClass object at 0xb36682b4>
<myFile.MainClass object at 0xb36ac26c>
i.e. both self and objClass are differently located. Why is this happening? should they not be co-located so that all messages are displayed. Is it OK to create the object of the class within the same class definition?
Instead of doing:
classObj = MainClass() # object
ret = readFile(classObj,'REX011')
can I do this safely??
ret = readFile(self,'REX011')
classObj = MainClass() # object will create another, new, instance of MainWindow(). That is not what you need. ret = readFile(self,'REX011') should do what you need.
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().