How to avoid double inhertince - python

I am trying to devlop a qt application and I have decided to make an abstract class named window that inherts from QWidget each window, diaoluge will inhert from her, and will include the basic proprties that window should have. Right now one of the classes that inherts from window also inhert from QMainWindow. My problam is that both QMainWindow and window inherts from QWidget. That makes me think that my way of soltion is sub-optimal and there is a proper way to do the genraliztion I try to do, any advice will be welcomed.
Heres the code for refrence (delted unimportent parts) with the double inhertince:
class Window(QtGui.QWidget , ):
_metaclass__ = ABCMeta
def __init__(self, win_name):
super(Window, self).__init__()
self.name = win_name
self.initUI()
def initUI(self, ):
self.setWindowTitle(self.name)
self.show()
class Main_Window(QtGui.QMainWindow, Window):
def __init__(self, win_name):
super(Main_Window, self).__init__(win_name)
self.initUI()
def initUI(self, ):
self.statusBar().showMessage('chk')
self.setGeometry(300, 300, 250, 150)
super(Main_Window, self).initUI()

Another option instead using inheritance would be using composition, here's a little example:
import sys
from PyQt5.Qt import * # noqa
class GuiBuilder():
def __init__(self, widget, name):
self.widget = widget
self.name = name
self.init_ui()
def init_ui(self):
self.widget.setWindowTitle(self.name)
class MainWindow(QMainWindow):
def __init__(self, name):
super().__init__()
self.gui_builder = GuiBuilder(self, name)
self.init_ui()
def init_ui(self):
self.statusBar().showMessage('chk')
self.setGeometry(300, 300, 250, 150)
self.gui_builder.init_ui()
def main():
app = QApplication(sys.argv)
for title in ["window1", "window2", "window3"]:
ex = MainWindow(title)
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
As you can see I've refactored a bit your posted code, changed Window by GuiBuilder, Pep8 style, ... Of course, the above example doesn't make too much sense and you'd just use this approach if there was a good reason for it. KISS

Related

PyQt6: How can I fetch position of mouse pointer from QGraphicsScene and use the variables in the other class?

I'm new to pyqt6 and even to python. As the title indicates, I got stuck with handling mouse position variables. I was expecting to show coodinates of mouse pointer in the QLabel correspond to the mouse movement. I was able to fetch coordinates from mouseMoveEvent in the QGraphicsScene class to getPosition in the Window class. But, that coodinates couldn't be passed to the other function in Window class.
Here is my code so far.
import sys
from PyQt6 import QtWidgets
from PyQt6.QtWidgets import QVBoxLayout, QWidget, QLabel
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
def mouseMoveEvent(self, event):
self.posX = event.scenePos().x()
Window.getPosition(Window, event.scenePos().x())
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
self.setMouseTracking(True)
scene = GraphicsScene(self)
self.setScene(scene)
class Window(QWidget):
def __init__(self):
super().__init__()
self.Layout = QVBoxLayout()
self.gw = GraphicsView() # an image is meant to be set here.
self.Layout.addWidget(self.gw)
self.label = QLabel("Coordinate: x") # wanna show coorinates here correspond to the mouse movement.
self.Layout.addWidget(self.label)
self.setLayout(self.Layout)
def getPosition(self, posX):
self.label.setText("Coordinate: x" + str(self.posX))
self.repaint()
print(posX)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec())
and got an ERROR like this:
AttributeError: type object 'Window' has no attribute 'label'
It seemas the self in the getPosition function is set to GraphicsScene class after being called (unexpectedly for me). And I have no idea this approach works or not after reading several web documents and asking help for chatGPT. Possibly, I took wrong approach to layout of Widgets.
Any suggestion would be helpful because I'm stuck whole this week with dipression.
Thanks.
Using class Window as parameter to setPosition was wrong, one needs to use an instance of this class. I did this by climbing up the parent() methods and there may be prettier ways to achieve the same. However, it works for now and I did not want to throw in too many changes.
I marked notable changes with comments.
#!/usr/bin/env python3
import sys
from PyQt6 import QtWidgets
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
def mouseMoveEvent(self, event):
self.posX = event.scenePos().x()
self.parent().parent().setPosition(event.scenePos().x()) # <-- crawl up the ancestry
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
self.setMouseTracking(True)
scene = GraphicsScene(self)
self.setScene(scene)
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.Layout = QtWidgets.QVBoxLayout()
self.gw = GraphicsView(self) # <-- pass self here
self.Layout.addWidget(self.gw)
self.label = QtWidgets.QLabel("Coordinate: x") # wanna show coorinates here correspond to the mouse movement.
self.Layout.addWidget(self.label)
self.setLayout(self.Layout)
def setPosition(self, posX): # <-- this is a setter, not a getter
self.label.setText("Coordinate: x" + str(posX)) # <-- use argument posX
self.repaint()
print(posX)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec())

Why does this PyQt5 Qlistwidget signal + slot not working?

I've been trying to make this work, but it just doesn't. I get no errors, just plain noncompliance. It just does not want to add the QListWidget items, or change the QLabel.
I made a MainWindowClass. It it's main widget I have a layout and a button. In it's dockwidget I have a QListWidget.
I made a signal from the button and connected it to a slot in the dockwidget's list.
The connection is there. When I press the button, the method in the dockwidgetcontents class is running.
But it does not add the items to the listwidget. And does not produce any errors.
Here is the code:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class AppClass:
def __init__(self):
super().__init__()
app = QApplication(sys.argv)
window = MainWindowClass()
window.show()
sys.exit(app.exec_())
class MainWindowClass(QMainWindow):
def __init__(self):
super().__init__()
self.init_UI()
self.TheDockWidget()
def init_UI(self):
self.setGeometry(100, 100, 400, 200)
self.setWindowTitle("Test App")
self.setCentralWidget(MainWidgetClass())
def TheDockWidget(self):
self.dockWidget = QDockWidget('Status:')
self.dockWidget.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
self.addDockWidget(Qt.RightDockWidgetArea, self.dockWidget)
self.dockWidget.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
self.dockWidget.setWidget(DockWidgetContents())
class DockWidgetContents(QWidget):
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
layout = QVBoxLayout()
self.setLayout(layout)
self.button3 = QPushButton()
self.button3.setText("button3")
layout.addWidget(self.button3)
self.listwidget = QListWidget()
layout.addWidget(self.listwidget)
self.listwidget.addItem("Ready.")
self.label = QLabel("initial")
layout.addWidget(self.label)
#pyqtSlot(str, str, int)
def _add_item(self, strA, strB, int1):
self.label.setText("yes") # why does this not work??
self.listwidget.addItem(strA) # why does this not work??
self.listwidget.addItem(strB) # why does this not work??
self.listwidget.addItem(str(int1)) # why does this not work??
print(strA, strB, int1) # but this works fine.
class MainWidgetClass(QWidget):
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
mainLayout = QGridLayout()
self.setLayout(mainLayout)
mainLayout.addWidget(TopLeftWidgetClass(), 0, 0)
class TopLeftWidgetClass(QWidget):
signal = pyqtSignal(str, str, int)
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
layout = QHBoxLayout()
self.setLayout(layout)
self.button1 = QPushButton("button1")
layout.addWidget(self.button1)
self.button1.clicked.connect(self.start)
def start(self):
otherClass = DockWidgetContents()
self.signal.connect(otherClass._add_item)
self.signal.emit("one", "two", 3)
if __name__ == '__main__':
AppClass()
I also read all the suggested questions when I typed my question into the form, but I must be not understanding something vital or prerequisite to this.
While I find all your answers to other questions extremely valuable, I'd appreciate if the answer would point me to the reference that I'm missing, so I can understand the problem, rather than just copy/paste the fixed code.
The main problem is that you're trying to connect to a new instance of DockWidgetContents, which gets also immediately deleted as soon as start() returns.
That new instance is obviously useless, as one already exists, but you have no direct ways to get it, because you create all classes "on the fly" when you add widgets to layouts and parents.
Remember that, while creating "instances on the fly" is not technically a problem, it doesn't allow you to keep references to those instances.
You have to create appropriate references to widgets and correctly connect them.
Here you can see the modifications required:
class MainWindowClass(QMainWindow):
def __init__(self):
super().__init__()
self.init_UI()
self.TheDockWidget()
self.mainWidget.topLeftWidget.signal.connect(
self.dockWidgetContents._add_item)
def init_UI(self):
self.setGeometry(100, 100, 400, 200)
self.setWindowTitle("Test App")
self.mainWidget = MainWidgetClass()
self.setCentralWidget(self.mainWidget)
def TheDockWidget(self):
self.dockWidget = QDockWidget('Status:')
self.dockWidget.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
self.addDockWidget(Qt.RightDockWidgetArea, self.dockWidget)
self.dockWidget.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
self.dockWidgetContents = DockWidgetContents()
self.dockWidget.setWidget(self.dockWidgetContents)
class MainWidgetClass(QWidget):
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
mainLayout = QGridLayout()
self.setLayout(mainLayout)
self.topLeftWidget = TopLeftWidgetClass()
mainLayout.addWidget(self.topLeftWidget, 0, 0)

How to insert a Qwidget inside Qwidget

I am lost with all the parenting/initialising issues and have no idea why this does not work.
So I create a Label, then I create another Label with some painting in it, them I make a widget that contains the two, then I would like to put this new widget inside the main window... but nothing appears
import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Labhtml(QLabel):
def __init__(self):
super().__init__()
label = QLabel('html')
class Bar(QLabel):
def __init__(self):
super().__init__()
self.resize(100, 5)
def paintEvent(self, e):
qp = QPainter(self)
qp.setBrush(QColor(200, 0, 0))
qp.drawRect(0,0,200,3)
class Wid(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
widget = QWidget()
html = Labhtml()
bar = Bar()
self.layout = QVBoxLayout(widget)
self.layout.addWidget(html)
self.layout.addWidget(bar)
class Example(QScrollArea):
def __init__(self):
super().__init__()
widget = QWidget()
layout = QVBoxLayout(widget)
layout.addWidget(Wid(widget))
self.setWidget(widget)
self.setWidgetResizable(True)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
First for the class Labhtml, when you inherit from QLabel, you can use the methods and the attributes of the base class, or use the instantiation mechanism to pass some parameters :
class Labhtml(QLabel):
def __init__(self):
super().__init__()
self.setText('html')
Then you don't need to create another widget inside the Wid class, but you have to refer to self instead :
class Wid(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
html = Labhtml()
bar = Bar()
self.layout = QVBoxLayout(self)
self.layout.addWidget(html)
self.layout.addWidget(bar)
About the instantiation mechanism you could also write the classes by declaring a new text argument (the same used for the Qlabel), and pass it when you create your instance :
class Labhtml(QLabel):
def __init__(self, text):
super().__init__(text)
class Wid(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
html = Labhtml('html')

Writing a custom QPushButton class in Python

I've recently started learning PyQt on my own and I've come in some trouble trying to write a custom class that inherits from QPushButton so I can adjust its attributes. I'm trying to pass a text as an argument whenever I initialize an object of this class. I am pretty sure there's something wrong with my init but I haven't found it yet.
Here is the code:
import sys
from PySide import QtGui, QtCore
class mainb(QtGui.QPushButton):
def __init__(Text,self, parent = None):
super().__init__(parent)
self.setupbt(Text)
def setupbt(self):
self.setFlat(True)
self.setText(Text)
self.setGeometry(200,100, 60, 35)
self.move(300,300)
print('chegu aqui')
self.setToolTip('Isso é muito maneiro <b>Artur</b>')
self.show()
class mainwindow(QtGui.QWidget):
def __init__(self , parent = None):
super().__init__()
self.setupgui()
def setupgui(self):
self.setToolTip('Oi <i>QWidget</i> widget')
self.resize(800,600)
self.setWindowTitle('Janela do Artur')
af = mainb("Bom dia",self)
self.show()
"""
btn = QtGui.QPushButton('Botão',self)
btn.clicked.connect(QtCore.QCoreApplication.instance().quit)
btn.resize(btn.sizeHint())
btn.move(300, 50)
"""
def main():
app = QtGui.QApplication(sys.argv)
ex = mainwindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You are using super in wrong way, super must get a instance and another thing your first arg is Text, that's wrong that should be self. I fixed some more and the below code should work for you
import sys
from PySide import QtGui, QtCore
class mainb(QtGui.QPushButton):
def __init__(self, Text, parent = None):
super(mainb, self).__init__()
self.setupbt(Text)
def setupbt(self, Text):
self.setFlat(True)
self.setText(Text)
self.setGeometry(200,100, 60, 35)
self.move(300,300)
print('chegu aqui')
self.setToolTip('Isso muito maneiro <b>Artur</b>')
self.show()
class mainwindow(QtGui.QWidget):
def __init__(self , parent = None):
super(mainwindow, self).__init__()
self.setupgui()
def setupgui(self):
self.setToolTip('Oi <i>QWidget</i> widget')
self.resize(800,600)
self.setWindowTitle('Janela do Artur')
newLayout = QtGui.QHBoxLayout()
af = mainb("Bom dia",self)
newLayout.addWidget(af)
self.setLayout(newLayout)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = mainwindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Your def setupbt(self) does not seem to take the text as argument. Try def setupbt(self, Text): instead.

How to access variables from one class to another class in PyQt4?

I want to get a string from the main window to use in a window triggered with a click. I know how to do it by putting all statements into a single class, but now I'm trying to do the same thing with one class per window. Here is the code:
import sys
from PyQt4 import QtGui
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__()
self.initUI()
def initUI(self):
self.value = QtGui.QLineEdit('23')
self.button = QtGui.QPushButton('Open Dialog')
self.button.clicked.connect(self.openDialog)
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.value)
vbox.addWidget(self.button)
self.setLayout(vbox)
def openDialog(self):
self.entry = self.value.text()
print(self.entry)
Dialog().exec_()
class Dialog(QtGui.QDialog):
def __init__(self, parent=Window):
super().__init__()
win = Window()
self.text = win.entry
self.label = QtGui.QLabel(self.text)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.label)
self.setLayout(hbox)
def main():
app = QtGui.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
But I'm getting the error "AttributeError: 'Window' object has no attribute 'entry'" and I don't know any other way to try fix it. Can someone help me with it?
Create an instance of Dialog in the openDialog method, so that you can access its attributes directly. That way, the two classes can operate more independently, and you won't need to access the Window class from within the Dialog class:
def openDialog(self):
dialog = Dialog(self)
dialog.label.setText(self.value.text())
dialog.exec_()
print(dialog.label.text())
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.label = QtGui.QLabel(self)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.label)
self.setLayout(hbox)
Here
win = Window()
self.text = win.entry
you declare a new window and accesing its entry field but on your window class
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__()
self.initUI()
the entry field is not constructed.
So
Either you want to create a new window, so you have to put the self.entry on the constructor
You want to use the existing window an access its entry after calling openDialog
Edit:
Maybe here
class Dialog(QtGui.QDialog):
def __init__(self, parent=Window):
super().__init__()
you are initializing the class wrong. The parent constructor should be called with parent=Window too. Then from inside the dialog you could reference the window by doing self.parent

Categories

Resources