Odd behavior when trying to assign parentless widget in Qt - python

I have the following code:
from PyQt5 import QtWebEngineWidgets, QtWidgets
class Q(QtWebEngineWidgets.QWebEnginePage):
pass
app = QtWidgets.QApplication([])
l = QtWebEngineWidgets.QWebEngineView()
print(type(l.page()))
l.setPage(Q(l))
print(type(l.page()))
p = Q()
l.setPage(p)
print(type(l.page()))
l.setPage(Q())
print(type(l.page()))
app.exec_()
And here's the output:
<class 'PyQt5.QtWebEngineWidgets.QWebEnginePage'>
<class '__main__.Q'>
<class '__main__.Q'>
<class 'PyQt5.QtWebEngineWidgets.QWebEnginePage'>
First I create a new instance of the QWebEnginePage-derived Q-class, set the view as its parent and assign it as the view's page. It works as expected.
Next I do the same, but without giving the parent. Instead, I create a temporary variable that holds a new Q and assign it. It still works as expected.
Finally, I directly assign a dynamically created parentless Q. For some reason this doesn't work and the page resets to the default class.
Why does this happen?

The QWebEngineView does not take ownership of the QWebEnginePage, and it does not re-parent it.
For the third example, the page has no parent, but it's kept alive because python holds a global reference to it.
For the last example, there is no parent and no external reference, so the page gets garbage-collected before it is set. It is effectively equivalent to calling setPage(None), which will remove the previously set page and restore the default.

Related

Variable from __init__ changed in one function, but not recognized as changed to other function

Sup, i'm doing a little account registration just for learning propose.
I created a class called Accounts and did many different functions to work with. With previous acknowledgment i knew that i need to start them from a function called def __init__(self)
class Account:
def __init__(self):
self.contas = {}
self.bancodecontas = open("x.txt", 'r')
def getaccounts(self):
for linha in self.bancodecontas:
linha = linha.strip()
conta = linha.split(",")
login = conta[0]
senha = conta[1]
self.contas[login] = senha
def accountsprinting(self):
for login, senha in self.contas.items():
print("Login= ", login, "Senha= ", senha)
getaccounts() is working fine, i tested a print(self.contas) in the end of it and it printed all accounts from inside my x.txt. The problem start when i need to call accountsprinting(), i tried to start it with print(self.contas) but shows me a empty dictionary, which means it is not accessing the "new" self.contas. I did the exact samething in a different type of project and it worked fine, i know i'm missing something really obvious here, so i'm asking sorry beforehand for my lack of attention.
Thanks for your time, good codding.
EDIT 1
People asked for the entire program, this is my entire program. I'm using PyCharm, i created this as a accounts.py, a root file or resources file, and i'm going to be importing this class to another main.py to use the respective functions. I know i must call Accounts().getaccounts() first, then i must call the other functions, so i can first fill my "accounts database". Even doing this:
Adding print(self.contas) to the end of getaccounts() and the start of accountsprinting()
And doing on the same .py:
Account().getaccounts()
Account().accountsprinting()
Or doing on different .py:
from AccountManager import Account
Account().getaccounts()
Account().accountsprinting()
The output is the same:
{'Bruno': '666', 'Bruno2': '444', 'Pedro': '2222a', 'Breno': '092b'}
{}
EDIT 2
Adding self.getaccounts()to def __init__(self) as #Darkonaut said, really worked, on the same .py and even doing an import from another .py, but i would like to understand why without it, it doesn't work, makes no sense to me.
Thanks a lot =)
You need to create AND use the same instance to call both methods with your expected results.
acc = Account()
accounts = acc.getaccounts()
acc.accountsprinting()
Also, you import Accounts but you call Account in your code.
You forgot self.getaccounts() in the last line of your __init__ method, hence contas remains empty because it never get's filled.
TL;DR Make sure you keep a reference to a newly created instance to keep it alive
and perform following method calls on this instance.
__init__ is a method which is being called after an instance get's created
and initializes the instance. You create an instance of a class every time
you call a class like you did with Account().
But if you don't keep a (external) reference to this newly created instance,
you can't address the instance to invoke further methods on it. What happens in your code:
Account().getaccounts() # new instance of Account created,
# i.a. __init__ called, finally getaccounts called
Account().accountsprinting() # new instance of Account created,
# i.a. __init__ called, finally accountsprinting called
Both instances are quickly garbage collected (CPython implementation of Python assumed), because you don't hold an external reference to them, like you would do if you assign a new instance to a name like: acc = Account().
You can check that you get a new object every time you call Account() by comparing identity with Account(1) is Account(1) # False or by looking at the id numbers:
id(Account(1))
# 88311904
id(Account(1))
# 88312408
As a side note:
It doesn't have to be a named reference like acc above, you could also hold an
implicit, unnamed reference by placing the new instances in a list for example and the list would keep the reference to the instances and thus, keeping them alive:
class Account:
def __init__(self, x):
self.x = x
lst = [Account(1), Account(2)]
lst[0].x
# 1
lst[1].x
# 2
self in your code is an (internal) reference connecting (binding) class and instance.
If you assign to self like you do with self.contas[login] = senha within your instance-method getaccounts, you do this only for the actual instance where you are calling getaccounts upon. So when you call Account().getaccounts() and
later Account().accountsprinting() you are doing this for two different
instances and not on the same. Hence the second instance has an empty contas dict because for this instance you didn't call getaccounts() before.

PyQt5 double spin box returning none value

I have this: (there are class methods, which inherience from QWizard)
def getForms(self):
return [
(
QtWidgets.QLabel("Name"),
QtWidgets.QLineEdit()
),
(
QtWidgets.QLabel("Roll"),
QtWidgets.QDoubleSpinBox()
)
]
def registerFields(self, page, forms):
page.registerField("name*", forms[0][1])
page.registerField("roll", forms[1][1])
And in other place in code
id = self.currentId()
if id == 1:
print self.field("name") # this rightly give me a name from LineEdit
print self.field("roll") # but this give me just None, why?
When I changed
QtWidgets.QDoubleSpinBox()
to
QtWidgets.QSpinBox()
Line:
print self.field("roll")
works fine.
Why do I get None instead double value?
EDIT
I've just noticed that when I'm trying make 'roll' field as a mandatory.
page.registerField("roll*", forms[1][1])
And I fill this 'spinbox' in program, I can not click 'next' (next is disabled). I have spinbox in my form in program. I can set the value there. But this looks like this field is not connected with QWizard(?)?
The QWizardPage class only has internal knowledge of a few widget types. When registering a widget it does not know about, you need to specify the property for reading the value, along with the signal that is emitted when a value is changed, as a string.
For QDoubleSpinBox this would be:
page.registerField("roll", forms[1][1], "value", "valueChanged")
The list of widget types QWizardPage knows about is listed in the c++ documentation here.
You can also register this information globally using a method of QWizard, so that you don't have to specify it each time you call registerField(). To do this, call:
my_wizard.setDefaultProperty("QDoubleSpinBox", "value", "valueChanged")
Note: This is a method of the wizard, not the page.

Pyside getting a widget inside a QtabWidget

I have a QtabWidget and I made a ListWidget inside that tabwidget
self.tabWidget = QtGui.QTabWidget(self.centralwidget)
self.listWidget = QtGui.QListWidget(self.tabWidget)
Then I made a tab:
self.tab1 = QtGui.QWidget()
self.tabWidget.addTab(self.tab1,"hi")
What I'm trying to do is get the listview for inside QTabWidget for tab1.
print self.tabWidget.currentWidget()
It prints out a pointer:
PySide.QtGui.QWidget object at 0x0000000004EA84A4
I want the QListWidget so I can call functions like addItem etc.
Edit: I also have another question. I'm using Pyside and theres a function called retranslateUI and setupUI. I want to add a signal for my QTabWidget,
self.tabWidget.currentChanged.connect(self.showStreamList(self.tabWidget.tabText(self.tabWidget.currentIndex())))
but I'm not sure where to put it. I'm putting it in retranslateUI because thats there button.clicked.connects are but when I run the program, I think it executes this command first. The GUI doesn't even display. In general, where should I group these signals/event listeners?
Taking your comment into account, you seem to want to dynamically add QListWidgets to a QTabWidget and want individual access to each QListWidget.
QTabWidget's addTab() method takes a QWidget and a string as its arguments. A QListWidget, as the name implies, is derived / subclassed from QWidget. Therefore, the addTab() method will accept a QListWidget, if you pass it one. So self.tabWidget.addTab(self.listWidget,"hi") should work just fine.
Next, accessing them. QTabWidget has a method to access any tab by its index, sensibly called widget(index). Therefore, if you want to access the n-th widget, you can get it by calling self.tabWidget.widget(n).
You could therefore get any list widget and do stuff with it:
lw = self.tabWidget.widget(0) # get the 0th widget
lw.addItem(...)

findChild returns None with custom widget

I have created a custom Qt widget in Python and managed to get it to load into the form at runtime however when I try and use findChild to grab it back of the form instance I get None back.
The widget is loaded and I can see it if I print out the names and objects on the form:
DEBUG:root:<PyQt4.QtGui.QCheckBox object at 0x11358030>
DEBUG:root:Near_Other_Infrastructure
DEBUG:root:<PyQt4.QtGui.QCheckBox object at 0x113582B8>
DEBUG:root:photo
DEBUG:root:<imagewidget.QMapImageWidget object at 0x113586A8>
This is the code:
images = self.forminstance.findChild(QMapImageWidget)
Update:
Seems doing this works:
images = self.forminstance.findChild(QWidget, "photo")
and it returns DEBUG:root:<imagewidget.QMapImageWidget object at 0x113586A8>
although I would really perfer to just get the control via the type without using the name.
Any ideas?
I also had this problem.
One easy solution is to find a non-custom base class and cast.
Custom* ptr = dynamic_cast<QWidget*>(root->findChild<QWidget*>("MyWidgetName"));
if (ptr)
{
//...whatever
}

Passing of a variable (an ID) to another compiled UI in pyqt

I've been stumped for more than an hour on how to pass a variable, specifically an ID in sqlite which was recently accessed, to be used to another UI generated. I'm using eric4 (with python, pyqt, qtdesigner and sqlite).
Basically the program I'm coding makes members and each member has a unique ID when the information of the member is generated. When there is a new member made, the ID assigned to the new member must be passed to another part of the program. But there are also instances where there must be modifications made in the member, the ID must be known to display the right information of the member.
Basically, when a new member is added, it first inputs the the information in the database. What I did is like this in the dialog code of that ui named newmember.py:
def on_button_Save_released(self):
Nik = unicode(self.LineEdit_Nickname.text())
self.NMem = NewMem()
self.NMem.input_data(Nik)
self.close()
The NewMem is a class in another py file which has access to the database. Parts of the input_data method goes like this:
cur.execute("insert into Members (Nick) values (?)",(Nik))
I added this code so that it will know what ID the new member is assigned::
CurrentID = cur.lastrowid
return CurrentID
So I changed this line self.NMem.input_data(Nik) in the ui dialog code newmember.py into this
ID = self.NMem.input_data(Nik)
so that the ID will be passed.
Now, the dialog code will open another window and I want the returned ID be used to another ui. Basically the whole method in ui dialog code in newmember.py is like this so far:
def on_button_Save_released(self):
Nik = unicode(self.LineEdit_Nickname.text())
self.NMem = NewMem()
ID = self.NMem.input_data(Nik)
self.close()
self.Pts = Points()
self.Pts.show()
The Points() is a class in another ui dialog code which will show in the Points ui the information of the member. But the returned ID information must be passed to the Points() ui so that the right information be displayed. I have a hunch that I would be needing to change some parts in the compiled ui form so that it knows the ID to be displayed but how will I pass it in there compiled ui form?
Any help is very much appreciated bows deeply
Edit: Is there a way in this code -
self.Pts = Points()
self.Pts.show()
be the variable ID be also incorporated and will pass into the ui? Like
self.Pts = Points
self.Pts.show(ID)
that the ID will be going to the ui also?
Edit2: Is there a way how to pass variables in classes of a ui in python just like how it was shown here - http://www.daniweb.com/forums/thread64758.html
I believe using a Qt model with a QDataWidgetMapper is the Qt way of solving this problem. An example here. (QSLTableModel or subclass QAbstractTableModel if you want to program the database communication yourself).
You can also check SQL Widget mapper example in the Qt docs.
Personally I prefer to use a custom model because I've previously stumbled upon deployment problems using Qt's SQL, but your mileage may vary.

Categories