Is there an elegant way to delete a widget in QScrollArea in PyQt? - pyqt

I am learning PyQt5 recently and have problems when I want to delete a widget in QScrollArea. Is there an elegant way to visit the element in QScrollArea and delete it when the "delete" button in that element is clicked? Thank you for any help!
class MyWidget(QWidget):
def __init__(self, id):
super().__init__()
self.layout = QHBoxLayout()
self.layout.addWidget(QPlainTextEdit(id))
self.layout.addWidget(QPushButton('Delete'))
self.setLayout(self.layout)
# connect options
connect_options()
def connect_options(self):
pass
class MyList(QScrollArea):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.widget = QWidget()
for x in range(10):
self.layout.addWidget(MyWidget(str(x)))
self.widget.setLayout(self.layout)
self.setMinimumSize(1024, 500)
self.setWidget(self.widget)

First I would suggest you look into QListWidget, It will provide you various functions to handle situations like this.
for your problem, you will need to delete the widget from its parent layout & then delete it from the GUI
class MyWidget(QWidget):
def __init__(self, id):
super().__init__()
self.layout = QHBoxLayout()
self.layout.addWidget(QPlainTextEdit(id))
del_btn = QPushButton('Delete')
del_btn.clicked.connect(self._delete) # connect the click event to your delete function
self.layout.addWidget(del_btn)
self.setLayout(self.layout)
# connect options
self.connect_options()
def connect_options(self):
pass
def _delete(self):
# here you will delete your widget
parent_layout = self.parent().layout()
parent_layout.removeWidget(self) # remove the widget from its parent layout
self.deleteLater() # lets Qt knows it needs to delete this widget from the GUI
del self

Related

Using dynamically added widgets in PyQt/Pyside

I have modified the answer given here as written below. The code is basically creating pushbuttons with a counter as pushButton_0, pushButton_1..
Here, I know that when I press to self.addButton I am creating widgets named like self.pushButton_0, self.pushButton_1 etc. So, my question is, how I'm supposed to use this pushbuttons? Because when I'm trying to do something like self.pushButton_0.clicked.connect(self.x), it' s telling me that "there is no attribute named 'pushButton_0'".
Thanks!
from PyQt4 import QtGui, QtCore
import sys
class Main(QtGui.QMainWindow):
def __init__(self, parent = None):
super(Main, self).__init__()
self.GUI()
def GUI(self):
self.count = 0
# main button
self.addButton = QtGui.QPushButton('button to add other widgets')
self.addButton.clicked.connect(self.addWidget)
# scroll area widget contents - layout
self.scrollLayout = QtGui.QFormLayout()
# scroll area widget contents
self.scrollWidget = QtGui.QWidget()
self.scrollWidget.setLayout(self.scrollLayout)
# scroll area
self.scrollArea = QtGui.QScrollArea()
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setWidget(self.scrollWidget)
# main layout
self.mainLayout = QtGui.QVBoxLayout()
# add all main to the main vLayout
self.mainLayout.addWidget(self.addButton)
self.mainLayout.addWidget(self.scrollArea)
# central widget
self.centralWidget = QtGui.QWidget()
self.centralWidget.setLayout(self.mainLayout)
# set central widget
self.setCentralWidget(self.centralWidget)
def addWidget(self):
self.scrollLayout.addRow(Test(self))
self.count = self.count + 1
print(self.count)
class Test(QtGui.QWidget):
def __init__( self, main):
super(Test, self).__init__()
self.Main = main
self.setup()
def setup(self):
print(self.Main.count)
name = "pushButton_"+str(self.Main.count)
print(name)
self.name = QtGui.QPushButton('I am in Test widget '+str(self.Main.count))
layout = QtGui.QHBoxLayout()
layout.addWidget(self.name)
self.setLayout(layout)
app = QtGui.QApplication(sys.argv)
myWidget = Main()
myWidget.show()
app.exec_()
After hours, I found the problem!
You have to declare the signal while creating the pushbutton!
To fix this, I rewrote the setup function as below;
def setup(self):
print(self.Main.count)
name = "pushButton_"+str(self.Main.count)
print(name)
self.name = QtGui.QPushButton('I am in Test widget '+str(self.Main.count))
self.name.clicked.connect(self.x) # self.x is any function
layout = QtGui.QHBoxLayout()
layout.addWidget(self.name)
self.setLayout(layout)
So know, you will run function x whenever you push the new created pushbuttons!

PyQt5 Dialog window opens without layout

When I load a dialog (QMainWindow) window from within my mainwindow (QMainWindow), it loads without layout, even though the setupUi() function is called.
The important pieces of code are here below, click here for pastebin link to full code
class Ui_Dialog(QMainWindow):
def __init__(self, parent=None):
super(Ui_Dialog, self).__init__(parent)
self.setupUi(self)
def setupUi(self, Dialog):
...
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
self.show()
....
def setupUi(self, Form):
...
self.auto_sap_btn = QPushButton(Form)
self.auto_sap_btn.setGeometry(QRect(0, 0, 61, 25))
self.auto_sap_btn.setObjectName('auto_sap_btn')
self.auto_sap_btn.clicked.connect(self.openDialog)
def openDialog(self):
self.window = Ui_Dialog(self)
self.window.setupUi(self.window)
self.window.move(600, 500)
self.window.show()
Right now my dialog looks like this:
Failed dialog layout
When I load the dialog on its own from its own script created by:
pyuic5 -x dialog.ui -o dialog.py
it looks like this:
Proper dialog layout
What am I missing?
When you create a design based on a Template in Qt Designer, then when you have to pass the appropriate widget, when you created Ui_Dialog you surely used Dialog with Buttons Right so in this case you should use a QDialog instead of QMainWindow:
class Ui_Dialog(QDialog): # change QMainWindow to QDialog
def __init__(self, parent=None):
super(Ui_Dialog, self).__init__(parent)
self.setupUi(self)
[...]
Another mistake is to use the setupUi() method a second time since this method is responsible for filling in the widget, by calling it 2 times you will be adding more widgets unnecessarily:
def openDialog(self):
self.window = Ui_Dialog(self)
self.window.move(600, 500)
self.window.show()

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.

Qtableview in qvboxlayout

Hello I'm trying to embbed a qtableview in a qvbox layout
But I do not manage to keep the qtableview in the qvboxlayout
The code below gives me two windows I do not know why layout.addwidget does not behave as expected.
class QMT(QtGui.QMainWindow):
def __init__(self, parent=None):
super(QMT, self).__init__(parent)
layout=QVBoxLayout(self)
self.view = QtGui.QTableView()
self.name = QtGui.QLabel("Name:")
layout.addWidget(self.view)
layout.addWidget(self.name)
self.initUI() #Windows stuff + show()
self.setLayout(layout)
I answer myself and found basic statement I did not understand about PyQt:
There are 2 steps to using the layout system in Qt: organizing widgets into a layout, and applying a layout to a widget.
So here is something that work :
class QMT(QtGui.QMainWindow):
def __init__(self, parent=None):
super(QMT, self).__init__(parent)
# Create Qtable view widget
self.view = QtGui.QTableView(self)
# Create Canvas for graph
self.fig=Figure(figsize=(5,5), dpi=100)
self.canvas=FigureCanvas(self.fig)
self.graph=self.fig.add_subplot(111)
#Layout management
#Initiate splitter (more convenient for end user)
splitter1 = QtGui.QSplitter(QtCore.Qt.Horizontal)
splitter1.addWidget(self.view)
splitter1.addWidget(self.canvas)
# Create Horizontal Layout Box
hbox = QtGui.QHBoxLayout()
# Add the splitter to the hbox
hbox.addWidget(splitter1)
#initiate widget to be shown
widget = QtGui.QWidget(self)
widget.setLayout(hbox)
#Set widget as central widget
self.setCentralWidget(widget)
# Windows & Menu stuff
self.initUI()

pyqt QGraphicsView mouse events trouble

I'm writing an MDI application with QGraphicsView on each tab.
I add items, move them and there is a group. But there are a number of problems, such as:
a one-time allocation of objects. I can not rubber band select some objects and holding the Shift to add to the selection with other rubber band selection. this can be done for example if it is before the new allocation remember old objects and add it after the separation that was previously. but it is done through mouse events, and they do not work at all
necessary when you click on an object to do some action, but it is also done through mouse events ...
i need the mouse wheel to zoom, and then rests on the mouse events
possible for all of these actions have no ready-made solutions zalezaniya in mouse events, but all the lessons that I find - say that the only mouse events will save me
how to make a QGraphicsView to catch mouse events?
import os
import sys
import sip
import maya.OpenMayaUI as mui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
#----------------------------------------------------------------------
def getMayaWindow():
ptr = mui.MQtUtil.mainWindow()
return sip.wrapinstance(long(ptr), QObject)
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class MainForm(QMainWindow):
def __init__(self):
super(MainForm, self).__init__(getMayaWindow())
self.setGeometry(50,50,600,600)
widget = QWidget()
self.setCentralWidget(widget)
layout = QGridLayout()
widget.setLayout(layout)
mdiArea = QMdiArea()
layout.addWidget(mdiArea)
newItem1 = vrayRectWidget(mdiArea, 'aaaa')
newItem2 = vrayRectWidget(mdiArea, 'bbbb')
newItem1.setMouseTracking(True)
newItem2.setMouseTracking(True)
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class vrayRectWidget(QMdiSubWindow):
def __init__(self, parent, name):
super(vrayRectWidget, self).__init__(parent)
self.setAttribute(Qt.WA_DeleteOnClose)
self.setWindowTitle(name)
self.view = MyView()
self.view.setMouseTracking(True)
self.setWidget(self.view)
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class MyView(QGraphicsView):
def __init__(self):
QGraphicsView.__init__(self)
self.setGeometry(QRect(100, 100, 600, 400))
self.setDragMode(QGraphicsView.RubberBandDrag)
self.setRubberBandSelectionMode(Qt.IntersectsItemShape)
self.setMouseTracking(True)
self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
self.scene = QGraphicsScene(self)
self.scene.setSceneRect(QRectF())
self.setScene(self.scene)
self.setInteractive(True)
for i in range(5):
item = QGraphicsEllipseItem(i*75, 10, 60, 40)
item.setFlag(QGraphicsItem.ItemIsMovable, True)
item.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.scene.addItem(item)
def mousePressEvent(self, event):
print('mousePressEvent')
#----------------------------------------------------------------------
#----------------------------------------------------------------------
# window
def cacheWnd():
wnd = MainForm()
wnd.show()
cacheWnd()
you need an eventfilter in MyView:
def eventFilter(self,obj,event):
if obj == self and event.type() == QtCore.QEvent.MouseButtonPress: # see http://doc.qt.io/qt-5/qevent.html
# alternatively use QtCore.QEvent.GraphicsSceneMousePress
print('mousePressEvent')
return True
return QtWidgets.QGraphicsView.eventFilter(self,obj,event)
and install him at the end of the constructor of MyView:
self.installEventFilter(self)
problem with mouse events resolved.
I started a new class based on QGraphicsScene, and it handles all mouse events:
class GraphicsScene(QGraphicsScene):
def __init__(self, parent=None):
super(GraphicsScene, self).__init__(parent)
self.parent = parent
def mouseReleaseEvent(self, event):
print('mouseReleaseEvent')
return QGraphicsScene.mouseReleaseEvent(self, event)

Resources