Динамическое создание кнопок

Недавно начал писать код на PyQt5, и решил написать программу, где будет много кнопок. Чтобы было удобно, решил сделать кнопку в функции и в нужном моменте вызывать её указав настройки.

import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QMessageBox, QLabel
from PyQt5.QtCore import pyqtSignal, QSize
from PyQt5.QtGui import QIcon, QFontDatabase, QFont
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import pyqtSignal, QSize
from PyQt5.QtGui import QIcon, QFontDatabase, QFont

  
class Button(QPushButton):
    mouseMoved = pyqtSignal()
    def mouseMoveEvent(self, event):
        self.mouseMoved.emit()

class Example(QWidget): 
    def __init__(self):
        super().__init__()
#--------------------------------------------------------------------------------------------------------
        #def кнопки, теперь надо просто прописать название def'а и прописать ему данные!
        def skins(btn_name, text, resize, resize_2, move, move_2, mains, color):
            self.btn_name = QPushButton(self)
            self.btn_name.setMouseTracking(True);
            self.btn_name.setText(text)
            self.btn_name.setStyleSheet("background-image: url(image/img2.png); color: " + color + "; border-radius: 4px;}QPushButton:pressed {background-image: url(image/img2.1.png) }")
            self.btn_name.setFont(QtGui.QFont("Pusia-Bold.otf", 17, QtGui.QFont.Bold))
            self.btn_name.resize(resize, resize_2)
            self.btn_name.move(move, move_2)
            self.btn_name.clicked.connect(mains)
        skins(self.btn1, "текст", 300, 150, 0, 1550, self.ObjectKill, "White")
    def ObjectKill(self):
        self.btn1.close()
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_()) 

Но мне выдаёт ошибку:

AttributeError: Example' object has no attribute 'btn1'

Что делать?

Example - это класс.


Длинный вариант (так я делал раньше):

import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QMessageBox, QLabel
from PyQt5.QtCore import pyqtSignal, QSize
from PyQt5.QtGui import QIcon, QFontDatabase, QFont
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import pyqtSignal, QSize
from PyQt5.QtGui import QIcon, QFontDatabase, QFont

  
class Button(QPushButton):
    mouseMoved = pyqtSignal()
    def mouseMoveEvent(self, event):
        self.mouseMoved.emit()

class Example(QWidget): 
    def __init__(self):
        super().__init__()
        self.btn_name = QPushButton(self)
        self.btn_name.setMouseTracking(True);
        self.btn_name.setText(text)
        self.btn_name.setStyleSheet("background-image: url(image/img2.png); color: White; border-radius: 4px;}QPushButton:pressed {background-image: url(image/img2.1.png) }")
        self.btn_name.setFont(QtGui.QFont("Pusia-Bold.otf", 17, QtGui.QFont.Bold))
        self.btn_name.resize(300, 150)
        self.btn_name.move(0, 1550)
        self.btn_name.clicked.connect(self.main)
    def main(self):
        print() 
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

И вот как я хочу реализовать это:

import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QMessageBox, QLabel
from PyQt5.QtCore import pyqtSignal, QSize
from PyQt5.QtGui import QIcon, QFontDatabase, QFont
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import pyqtSignal, QSize
from PyQt5.QtGui import QIcon, QFontDatabase, QFont

  
class Button(QPushButton):
    mouseMoved = pyqtSignal()
    def mouseMoveEvent(self, event):
        self.mouseMoved.emit()

class Example(QWidget): 
    def __init__(self):
        super().__init__()
        skins(self.btn1, "Текст", 300, 150, 0, 1550, self.ObjectKill, "White")
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

Как это возможно сделать?


Ответы (2 шт):

Автор решения: S. Nick

Я попробую еще раз объяснить вам, что для того чтобы вам помочь, вы должны предоставить минимально-воспроизводимый пример, который демонстрирует проблему.

Воспроизводимый означает, что ваш код, который размещен в теле вопроса как обычный текст, копируется и запускается, и мы получаем ошибку, о который вы пишите.

Я не буду обсуждать вашу идею о таком способе размещения кнопок.
Но если вам так надо, то ваше задача может выглядеть примерно так.

import sys
import random
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.Qt import *


class Label(QLabel):
    def __init__(self, pixmap, parent=None):
        super().__init__(parent)
        self.pixmap = pixmap
        self.start = None

        self.setPixmap(self.pixmap.scaled(self.pixmap.size(),
                Qt.KeepAspectRatio, Qt.SmoothTransformation))
        self.setSizePolicy(QSizePolicy.Expanding,
                QSizePolicy.Expanding)
        self.setAlignment(Qt.AlignCenter)
        self.setMinimumSize(100, 100)   
        
    def mousePressEvent(self, event):
        if event.buttons() == Qt.LeftButton: 
            self.start = event.pos() 
            self._start = event.globalPos()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and self.start: 
            self.end = event.pos()
            self.window().selection.setGeometry(
                QRect(self.start, self.end).normalized())
            self.window().selection.show()
        

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        
        self.colors = ['#CF0000', '#FC5404', '#1CC5DC', '#F8F7DE', '#42240C',]
        self.selection = QRubberBand(QRubberBand.Rectangle, self) 
        self.selection.setGeometry(QRect())
        self.start = QPointF() 
        self.end = QPointF()

        self.pixmap = QPixmap("boy.jpg")        
        self.label  = Label(self.pixmap, self)  
        self.setFixedSize(self.pixmap.size())
        
        self.lineEdit = QLineEdit(placeholderText='Введите название для кнопки')
        self.lineEdit.setMaxLength(10)
        
        self.pushButton = QPushButton('Создать кнопку')
        self.pushButton.clicked.connect(self.create_button)
        
        layout = QGridLayout(self)    
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.label, 0, 0, 1, 2) 
        layout.addWidget(self.lineEdit, 1, 0, 1, 1)
        layout.addWidget(self.pushButton, 1, 1, 1, 1)        
        layout.setColumnStretch(0, 1)
        layout.setColumnStretch(1, 1)
                
    def create_button(self):
        if self.selection.geometry() == QRect():
            msg = QMessageBox.information(
                self, 
                'Внимание', 
                'Выберите прямоугольник для размещения кнопки.'
            )
            return
            
        x, y = self.selection.geometry().x(), self.selection.geometry().y(),
        size = self.selection.geometry().size()

        self.selection.hide()
        self.selection.setGeometry(QRect())
        
        text = self.lineEdit.text() if self.lineEdit.text() else '???'
        
        btn_name = QPushButton(self.label)
        btn_name.setText(text)
        btn_name.clicked.connect(
            lambda ch, text=btn_name.text(): print(f'Вы кликнули: {text}')
        )
        
        color = random.choice(self.colors)
        btn_name.setStyleSheet(f"""
            QPushButton {{
                border-image: url(Ok.png);   /* <---- установите свое изображение */ 
                color: {color};              /* <---- color                       */ 
                border-radius: 4px;
            }}
            QPushButton:pressed {{
                border-image: url(lena.jpg); /* <---- установите свое изображение */ 
            }}
        """) 
        
        btn_name.setFont(QtGui.QFont(
            "Pusia-Bold.otf", 17, QtGui.QFont.Bold)) 
        btn_name.resize(size) 
        btn_name.move(x, y)
        btn_name.show()
        self.lineEdit.clear()
    


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

P.S. Изображение boy.jpg вы можете взять ЗДЕСЬ

Фоновый изображения для кнопок установите любые свои.


введите сюда описание изображения

введите сюда описание изображения

→ Ссылка
Автор решения: S. Nick

Я не уверен, что правильно вас понял.

Абсолютное позиционирование для размещения виджетов - это плохая практика.
Вам надо изучить Layout Management

В общем динамическое создание виджетов выглядит примерно так:

import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.Qt import *

  
class Button(QPushButton):
   def __init__(self, num, text, size, color):                 # !!!
        super().__init__()  

        self.setText(f'{text} {num}')                          # !!! {text} {num}
        self.setFixedSize(*size)                               # !!! (*size) 
        
        self.setStyleSheet(f"""
            QPushButton {{                                    /* <---- {{         */
            border-image: url(Ok.png);                        /* <---- border-... */
            color: {color};                                   /* <---- {color}    */ 
            border-radius: 4px;
            }}                                                /* <---- }}         */
            QPushButton:pressed {{
                border-image: url(im.png) 
            }}
        """)   
        self.setFont(QtGui.QFont("Pusia-Bold.otf", 17, QtGui.QFont.Bold))  


class Example(QWidget): 
    def __init__(self):
        super().__init__()
        
        many_buttons = 16                    # хотим создать 16 кнопок
        column = 4                           # хотим разместить эти кнопки в 4 колонки
        size = (150, 150)                    # размер кнопки, например 150х150 
        color = '#84142D'                    # пускай цвет текста будет такой
        
        layout = QGridLayout(self)
        
        for step in range(many_buttons):
            btn = Button(step+1, 'Кнопка', size, color)               # !!!
            btn.clicked.connect(lambda ch, b=btn : self.onClicked(b))
            layout.addWidget(btn, step // column, step % column)        
        
    def onClicked(self, btn):
        # тут выполняются какие-то действия по нажатию на кнопку
        # допустим мы хотим скрыть кнопку, на которуй нажали
        btn.hide() 
        

if __name__ == '__main__':        
    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())

введите сюда описание изображения

введите сюда описание изображения

→ Ссылка