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)
Related
edit : Problem solved, I was indeed a idiot and it was indeed a very silly mistake. The solution was that I forgot to give an instance of UI when creating a Joueur instance... Sorry and thanks to all who tried to help me.
I've been trying to code a chess game. I am now in the process of testing it and I got a "weird" error (from my experience I probably just messed up, but my situation is not exactly what people on every other post on this error seem to be and I have been searching my code and documentation for clues for some hours with no success : so here I am).
So to summarize the code (I won't put code I have successfully run before and will only show what should be relevant):
I have a board class which is basically my model and my controller. In the constructor I ask for arguments two players, a UI class, and a hash class. Before I implemented the last two, and added it in the constructor, the code ran just fine.
class Plateau:
def __init__(self, j1, j2, UI, Hash):
self.UI = UI
self.UI.ajoutePlateau(self)
self.Hash = Hash
self.Hash.hashPlateau(self)
# some code to deal with the rest
...
#
self.UI.initAffichage()
I then have a UI interface and a ConsolUI (UITerminal in my code) which inherit from it. It is as expected what show to the user the board and also what ask the human player (if there is one) what he wants to play.
class UI:
def __init__(self):
self.Plateau = None
def ajoutePlateau(self, Plateau):
self.Plateau = Plateau
# a bunch of method that throw an exception and are overrided by the child class
...
#
class UITerminal(UI):
def __init__(self):
#super(UITerminal, self).__init__(self)
#super().__init__(self)
#UI.__init__(self)
#super(UITerminal, self).__init__()
super().__init__()
#UI.__init__()
# the method that had to be overridden
...
#
I have also tried few version of the UITerminal constructor (which are above and put on comment).
or even just nothing since it should not even be needed (I think...).
Then there is the hash, which is build the same way as the UI : an interface, a child.
class Hash:
def __init__(self):
pass
# a bunch of method that throw an exception and are overridden by the child class
...
#
class ZombristHash(Hash):
def __init__(self):
#super(ZombristHash, self).__init__(self)
#super().__init__(self)
#Hash.__init__(self)
#super(ZombristHash, self).__init__()
super().__init__()
#Hash.__init__()
# a bunch of code to init the zombristhash
...
#
same as with the UI, I tried multiple way to call the interface constructor.
Then I have my main, which is only 1 line and is what throw the error :
p = Plateau(Humain("j1"), Humain("j2"), UITerminal(), ZombristHash())
and the error is :
Traceback (most recent call last): File "plateau.py", line 298, in <module> p = Plateau(Humain("j1"), Humain("j2"), UITerminal(), ZombristHash())
TypeError: __init__() missing 1 required positional argument: 'UI'.
From what I understand, he tells me I haven't given the board constructor a UI as argument, but I did so I do not understand what is happening.
I tried on quamran's suggestion this :
p = Plateau(None, None, None, None)
And it seems to think it was ok now...
Traceback (most recent call last):
File "plateau.py", line 298, in <module>
p = Plateau(None, None, None, None)
File "plateau.py", line 13, in __init__
self.UI.ajoutePlateau(self)
AttributeError: 'NoneType' object has no attribute 'ajoutePlateau'
As #Jacques Gaudin noticed, you need to change this :
class ZombristHash(Hash):
def __init__(self):
Hash.__init__()
to this :
class ZombristHash(Hash):
def __init__(self):
super().__init__()
and same for UI.
Finally, you get something like this :
class Plateau:
def __init__(self, j1, j2, UI, Hash):
self.UI = UI
self.UI.ajoutePlateau(self)
self.Hash = Hash
class Hash:
def __init__(self):
pass
class ZombristHash(Hash):
def __init__(self):
super().__init__()
class UI:
def __init__(self):
self.Plateau = None
def ajoutePlateau(self, Plateau):
self.Plateau = Plateau
class UITerminal(UI):
def __init__(self):
# super(UITerminal, self).__init__()
super().__init__()
class Humain():
def __init__(self, j):
pass
p = Plateau(Humain("j1"), Humain("j2"), UITerminal(), ZombristHash())
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 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().
I have the following code:
class Functions(QObject):
mysig = Signal(filename)
def __init__(self, parent=None):
super(Functions, self).__init__(parent)
self.result = None
def showDialog(self, filename):
self.mysig.emit(filename)
def grabResult(self):
while not self.result:
time.sleep(5)
return result #this is the question
def setResult(self, result):
self.result = result
The other part of the code has this:
class Dialog(QDialog):
anotherSig = Signal(str)
fun = Functions()
def __init__(self, parent=None, filename=filename):
self.filename = filename
#Here it displays a picture based on the filename parameter
def okButtonClicked(self):
text = self.lineedit.text()
fun.setResult(text)
#Tried also this:
self.anotherSig.emit(text)
The Functions() class is called from a worker QThread (not shown here).
I guess my question is this: how do I tell my Functions class that the user has entered the the text and clicked the OK button? I tried connecting that anotherSig Signal, but when I try to do so, Qt complains about QPixmaps not being safe to be set from a different thread, and it doesn't work.
The method that I am using here "works", but I feel it's not very reliable. Plus, it only works when all of the relevant methods in the Functions class are #classmethod - this way, for some reason, it doesn't work. The setResult is called (I added a print statement to make sure), but the grabResult still shows self.result as None.
This code is not working because the call to showDialog is happening on the instantiation of a Functions object that is an attribute of what ever object is off on the other thread. Your fun in Dialog, which you set the result on, is a different instantiation.
To move the results back to the original Functions object I think you need to connect anotherSig of the Dialog object to the setResult function on the Functions object you want to get the results back.
Does something like this work (hard to test this with out a good bit of boiler plate).
class Functions(QObject):
mysig = Signal(filename,Functions)
def __init__(self, parent=None):
super(Functions, self).__init__(parent)
self.result = None
def showDialog(self, filename):
self.mysig.emit(filename,self)
def grabResult(self):
while not self.result:
time.sleep(5)
return result #this is the question
#QtCore.Slot(str)
def setResult(self, result):
self.result = result
def connection_fun(filename,fun):
d = Dialog(filename)
# what ever else you do in here
d.anotherSig.connect(fun.setResult))
Using time.sleep causes your application to freeze. One method for making your class wait is using QEventLoop like this:
loop = QEventLoop()
myDialog.mySignal.connect(loop.quit)
loop.exec_()