How to pass a parent to a QMessage subclass - pyqt

How can I properly pass the parent to the QMessgaeBox subclass? If I don't use the parent, the messagebox doesn't appear at the center of the window!
class onCloseMessage(QMessageBox):
def __init__(self):
QMessageBox.__init__(self, QMessageBox.Question, "title", "message",
buttons= QMessageBox.Close)
dlg = onCloseMessage()
dlg.exec()
When I pass a parent, and replace self in the __init__ by the parent, it gives errors. If I use super, how can I then __init__ the QMessageBox?
I tried:
class onCloseMessage(QMessageBox):
def __init__(self, parent):
super().__init__(parent)
QMessageBox.__init__(self, QMessageBox.Question, "title", "message",
buttons= QMessageBox.Close)
But, it didn't work either.

When using super, you shouldn't call the base-class __init__ as well. Instead, pass all the required arguments to super itself, like this:
class onCloseMessage(QMessageBox):
def __init__(self, parent):
super().__init__(QMessageBox.Question, "title", "message",
buttons=QMessageBox.Close, parent=parent)
(NB: you can use Python keyword arguments wherever the Qt signature shows default arguments).

Related

Error while binding tkinter list box binding with a method in same class (Python)

I have written a class to create Listbox objects, I want to bind the list box with a method in the same class but I am getting attribute error. what am I doing wrong here??
class ListObj(tkinter.Listbox):
def __init__(self, window, cname, r, c, rs, cs, sticky, bg, padx=5, pady=5, ipadx=0, ipady=0, **kwargs):
self = tkinter.Listbox(window)
self.grid(row=r, column=c, rowspan=rs, columnspan=cs, sticky=sticky, padx=padx, pady=pady,
ipadx=ipadx, ipady=ipady)
self.bind('<<ListboxSelect>>', self.on_select)
def on_select(self):
pass
output:
AttributeError: 'Listbox' object has no attribute 'on_select'
The line causing the problem is self = tkinter.Listbox(window). You do not need to tell self it is a listbox because it already inherits listbox in the class definition.
Do this instead:
class ListObj(tkinter.Listbox):
def __init__(self, window, **kwargs):
super().__init__()
self.bind('<<ListboxSelect>>', self.on_select)
def on_select(self):
pass
You should use grid on the reference variable outside of the class. It is not the best option to use a geometry manager from inside of a class.
var_name = ListObj(var1, var2, var2 ...)
var_name.grid(configs....)

Retrieving variable from another class

I am programming a GUI using Tkinter. In one of the classes I have defined a variable (entry_filename) and would like to use it in another class. A part of the code is as follows:
class Loginpage(tk.Frame,Search):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
self.controller=controller
self.label_user=tk.Label(self, text="Username")
self.label_user.grid(row=0, column=0)
self.label_pass=tk.Label(self, text="Password")
self.label_pass.grid(row=1, column=0)
self.entry_user=tk.Entry(self)
self.entry_user.focus_set()
self.entry_user.grid(row=0, column=1)
self.entry_pass=tk.Entry(self,show="*")
self.entry_pass.grid(row=1, column=1)
self.button=ttk.Button(self, text="Login",command= self.Logincheck)
self.button.grid(columnspan=2)
def Logincheck(self):
global username
global password
try:
username=self.entry_user.get()
password=self.entry_pass.get()
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect(server, username=username, password=password)#input your username&password
button1 = ttk.Button(self, text="Click to Continue",command= lambda: self.controller.show_frame(Inputpage))
button1.grid(columnspan=2)
except:
tm.showerror("Login error", "Incorrect username/password")
class Inputpage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller=controller
self.filein_label=tk.Label(self,text="Input file name")
self.filein_label.grid(row=0,column=0)
self.entry_filename=tk.Entry(self)
self.entry_filename.focus_set()
self.entry_filename.grid(row=0,column=1)
self.button1 = ttk.Button(self, text="Click to Continue",command= lambda: self.controller.show_frame(Graphpage))
self.button1.grid(columnspan=2)
class Graphpage(tk.Frame,Inputpage):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller=controller
self.label = tk.Label(self, text="Graph Page!", font=LARGE_FONT)
self.label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Back to Input Page",command=lambda: self.controller.show_frame(Inputpage))
button1.pack()
filename=Inputpage.entry_filename.get()
The Graphpage calls the variable filename which is later used to create the graph (that part of the code is omitted here). When the code is run the following error is returned:
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Frame, Inputpage
It seems that I have hit another roadblock in attempting to solve the earlier issue, however, if I can understand the resolution to this, I hope that I can attempt to solve further issues. Thanks for your help
ssh is a local variable inside function LoginCheck so you are not able to retrieve it from another class. One thing possible to do is to define ssh as self.ssh so it will be accessible through instance_of_Loginpage.ssh. It will work only when you will pass an instance of Loginpage into an instance of Graphpage. If you need access to an ssh connection from many places I suggest to create another class just to handle ssh (you can use Borg patter to achieve it).
The culprit is that you should not share
class member variables that way.
If different classes share some common
data, that data is probably another class
and they can inherit from it.
class CommonData():
client = 100
class A(CommonData):
def __init__(self):
print(A.client)
class B(CommonData):
def __init__(self):
print(B.client)
a = A()
b = B()
CommonData.client = 300
print(a.client)
print(b.client)
In above case every instance of A and every instance of B
share all the CommonData class variables, like client.
CommonData.client = 400
class C():
pass
You can use multiple inheritance too.
define all common data as CommonData attributes
and use CommonData as a class to hold data, like
in above example, don't create instances from it:
class D(C, CommonData):
def __init__(self):
print(D.client)
c = C()
d = D()
A simpler option would be to just define
a variable CommonData in the outer scope and
use it from anywhere:
common_data = 500
class A():
def __init__(self):
global common_data
print(common_data)
common_data = 200
# ...
But global variables are generally seen as a bad thing in a program as their use can become a problem for several reasons.
Yet another way is to pass the variable to the object initializer.
That makes the instance to keep its own value copied from
the creation value:
common_data = 600
class A():
def __init__(self, data):
self.common = data
print(self.common)
a = A(common_data)
common_data = 0
print(a.common)
If you run all the code above it will print
100
100
300
300
400
600
600
Edit:
See my comment to your answer and a simple example here.
Here I opt for two global references to tkinter StringVars.
The stringvars exist themselves in the Tk() namespace, like the
widgets; besides they are global Python names.
import tkinter as tk
from tkinter import ttk
class Page1(tk.Toplevel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.title('Page1')
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=input_file1)
self.label1.pack(side=tk.LEFT)
self.entry1.pack()
class Page2(tk.Toplevel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.title('Page2')
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=input_file2)
self.button1 = ttk.Button(self, text='Copy Here', command=copy_filename)
self.label1.pack(side=tk.LEFT)
self.entry1.pack(side=tk.LEFT)
self.button1.pack()
def copy_filename():
input_file2.set(input_file1.get())
root = tk.Tk() # has to exist for the StringVars to be created
root.iconify()
input_file1 = tk.StringVar()
input_file2 = tk.StringVar()
page1 = Page1(root)
page2 = Page2(root)
root.mainloop()
Now in the next example see how I turn the stringvars into variables
of Page1 and Page2 instances (not classes), making them local instead
of global. Then I am forced to pass a reference for the widget page1
object into the widget page2 object.
This looks more close to what you are asking.
About MRO trouble, if you avoid multiple inheritance
it won't happen.
Or you deal with it usually by using super()
In your case the error is because you store the widget in
the object/instance (in self.somename), and then you try
to invoke a widget method qualifying with the class name.
There is no widget there in the class for you to use a method.
So the search using the method resolution order fails,
because there is no corresponding name there.
Note that I have not used multiple inheritance, so I could
have just written tk.Frame. instead of calling super. I like
super because it makes clear in the text that I am invoking the parent
class but super is really needed only when there are multiple parents
and various levels of subclassing (usually forming a diamond shape).
Now the example:
import tkinter as tk
from tkinter import ttk
class Page1(tk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.input_file1 = tk.StringVar()
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=self.input_file1)
self.label1.pack(side=tk.LEFT)
self.entry1.pack()
class Page2(tk.Frame):
# note the page1 reference being
# passed to initializer and stored in a var
# local to this instance:
def __init__(self, parent, page1, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.page1 = page1
self.input_file2 = tk.StringVar()
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=self.input_file2)
self.button1 = ttk.Button(self, text='Copy Here',
command=self.copy_filename)
self.label1.pack(side=tk.LEFT)
self.entry1.pack(side=tk.LEFT)
self.button1.pack()
def copy_filename(self):
# see how the page1 refernce is used to acess
# the Page1 instance
self.input_file2.set(page1.input_file1.get())
root = tk.Tk() # has to exist for the StringVars to be created
page1 = Page1(root)
page2 = Page2(root, page1) # pass a reference to page1 instance
page1.pack(side=tk.LEFT)
page2.pack(side=tk.LEFT)
root.mainloop()

How do i execute a method in a class from another method in a different class?

I would like a text to be updated when i press the search button. But I can't figure out how. Can someone explain what Im doing wrong on a kindergarden level?
Here is some code, I guess it explains what I like to do.
class One():
def setText(self, text):
self.text.config(state=NORMAL)
self.text.delete(1.0,END)
self.text.insert(END, text)
self.text.config(state=DISABLED)
def print(self):
self.setText('You are now searching.')
class PopUp():
def __init__(self, master):
self.searchButton = Button(command=self.onSearch, text ='search')
def onSearch(self):
mainClass = One()
mainClass.print()

Modify a variable inside another class and file

(Non-English native)
This is tricky to explain. I have 2 windows, each one with their own class created by PyQt5, on 2 different .py files. I want to open the 2nd window from a button inside the first one, and then I want that 2nd window to destroy itself when closed. However, in order to do this, I believe I have to set a specific variable in the 1st window to None, but since that is instanced I can't find out how:
First window in myfile.py
class MainForm(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.window_nuevocliente = None
self.actionNuevo_Cliente.triggered.connect(self.open_secondwindow)
def open_secondwindow(self):
if self.window_nuevocliente is None:
self.window_nuevocliente = addnewclient_logic.AddNewClientForm(self)
self.window_nuevocliente.setAttribute(Qt.WA_DeleteOnClose, True)
self.window_nuevocliente.show()
myapp = MainForm()
Second window in addnewclient_logic.py
import myfile
class AddNewClientForm(QtWidgets.QMainWindow, Ui_AddNewClient):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
def closeEvent(self, event):
# Do closing stuff
event.accept()
# Set the first class var window_nuevocliente back to None
myfile.myapp.window_nuevocliente = None
And here is where I'm stuck:
That last line doesn't work. When I close the window, the DeleteOnClose will totally destroy the window, but the first window will still have it assigned on the window_nuevocliente var and so it fails to re-create it from scratch. If I instead omit the check if it's None, the window can be opened multiple times at the same time (and I don't want that).
Managed to fix it by adding the destroyed signal.
def open_secondwindow(self):
if self.window_nuevocliente is None:
self.window_nuevocliente = addnewclient_logic.AddNewClientForm(self)
self.window_nuevocliente.setAttribute(Qt.WA_DeleteOnClose, True)
self.window_nuevocliente.destroyed.connect(self.reset_nuevocliente)
self.window_nuevocliente.show()
def reset_nuevocliente(self):
self.window_nuevocliente = None
I will accept a better solution, though :P
You can eliminate the window_nuevocliente attribute like this:
addnewclient_logic.py:
class AddNewClientForm(QtWidgets.QMainWindow, Ui_AddNewClient):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
self.setupUi(self)
myfile.py:
from addnewclient_logic import AddNewClientForm
class MainForm(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.actionNuevo_Cliente.triggered.connect(self.open_secondwindow)
def open_secondwindow(self):
window_nuevocliente = self.findChild(AddNewClientForm)
if window_nuevocliente is None:
window_nuevocliente = AddNewClientForm(self)
window_nuevocliente.show()
The parent window will hold a reference to the child window until it deletes itself on close - at which point, it will also be removed from the parent's list of children.

pyqt5 and multiple inheritance

I'd like to create a new class that inherits two subclasses of QWidget. I know multi-inheritance isn't possible in pyqt, but how could I manage to have the properties of both parent classes in one subclass?
What I wish I could do is as follows:
class A(QWidget):
def __init__(self, widget, parent=None):
widget.destroyed.connect(self.destroy_handler)
#pyqtSlot()
def destroy_handler(self):
pass
class B (A, QStatusBar):
def __init__(self, widget, parent=None):
A.__init__(self, widget)
QStatusBar.__init__(self, parent)
#pyqtSlot()
def destroyed_handler(self):
print("Destroyed")
I finally found how to do it: first of all, the problems came from A and QStatusBar inheriting QWidget. We can't change QStatusBar, so we must changer A.
A shouldn't inherit QWidget: so let's create another class, AInterface, like that:
class AInterface(QObject):
def __init__(self, a, parent=None)
super().__init__(parent=parent)
self.a = a
self.connect_signal()
def connect_signal(self, widget):
widget.destroyed.connect(self.handler)
#pyqtSlot()
def handler(self):
self.a.handler()
A has now the following implementation:
class A:
def __init__(self, widget):
a.widget = widget
a.interface = AInterface(self)
def handler(self):
pass
Thus, now we can create subclasses inheriting not only A but also any QObject, like this:
class B(QStatusBar, A):
def __init__(self, widget, parent=None):
QStatusBar.__init__(self, parent=parent, wiget=widget)
A.__init__(self, widget)
def handler(self):
self.show('Destroyed', 3000)
Notice the widget=widget in the constructor of QStatusBar: if we don't specify it, a TypeError is thrown...

Resources