Динамическое создание QPushButton с последующим сохранением в ini файл
проблема с динамическим созданием кнопок. Пишу приложение, нужно:
- Динамически создавать кнопки, которые по сути будут ярлыками на внешние программы.
- При необходимости, удалять нужную кнопку, чтобы остальные смещались согласно макету.
- Сохранять конфигурацию в ini файл, чтобы после перезапуска все созданные кнопки сохранялись.
Что есть на сейчас:
- Кнопки динамически создаются согласно Grid макету и могут запускать прикрепленный к ним exe файл, но только тот, что был прикреплен последним.
- Удаление работает только на последнюю созданную кнопку по клику по ней правой кнопкой мыши.
- Получается сохранить конфиг кнопки в файл settings.ini - но не могу извлечь данные при запуске программы.
Если у кого будут мысли, буду безмерно благодарен. Прикладываю минимально работающий пример.
Файл main.py:
from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QGroupBox, QPushButton, QMessageBox, QMenu
from PyQt5.QtCore import QEvent, QSettings, Qt
from PyQt5.QtGui import QIcon
class Main(QWidget):
settings = QSettings("settings.ini", QSettings.IniFormat)
def __init__(self):
super(Main, self).__init__()
self.setWindowTitle('Образец')
self.resize(500, 300)
grid = QGridLayout()
grid.addWidget(self.prog_group_box(), 0, 0, 1, 1)
grid.addWidget(self.b_new_prog(), 1, 0, Qt.AlignRight)
self.setLayout(grid)
def prog_group_box(self):
groupBox1 = QGroupBox(self)
self.grid_prog = QGridLayout()
groupBox1.setLayout(self.grid_prog)
return groupBox1
def saved_conf(self):
# Сохранение созданной кнопки в ini файл
self.settings.setValue('button', self.new_but)
self.settings.setValue('label', self.click_newprog._leProgLabel.text())
self.settings.setValue('source', self.click_newprog._leReview.text())
self.settings.sync()
# def restore_settings(self):
# Функция восстановления сохраненного конфига из ini файла
# test = self.settings.setValue('button')
# print(f'RESTORE: {test}')
def delete_setting(self):
# Функция удаления данных из ini файла с сохраненной конфигурацией
self.settings = QSettings("settings.ini", QSettings.IniFormat)
self.settings.remove('button')
def b_new_prog(self):
self.b_new_prog = QPushButton(self)
self.b_new_prog.setText("Добавить")
self.b_new_prog.setMinimumSize(129, 43)
self.b_new_prog.clicked.connect(self.click_newprog)
return self.b_new_prog
def click_newprog(self):
from second import Second
self.click_newprog = Second(self)
self.click_newprog.show()
def create_new_button(self):
self.new_but = QPushButton()
self.new_but.setFixedSize(70, 70)
self.new_but.clicked.connect(self.btnClicked)
self.new_but.setText(self.click_newprog._leProgLabel.text())
self.grid_prog.addWidget(self.new_but, 0, 0)
i = self.grid_prog.count() - 1
self.grid_prog.addWidget(self.new_but, 1 + i // 8, i % 8)
self.new_but.installEventFilter(self)
def btnClicked(self):
import os
os.startfile(self.click_newprog._leReview.text())
# Контекстное меню
def eventFilter(self, source, event):
if event.type() == QEvent.ContextMenu and source is self.new_but:
menu = QMenu()
menu.addAction('Удалить')
if menu.exec_(event.globalPos()):
reply = QMessageBox.question(
self,
'Message', "Вы действительно хотите удалить?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.Yes:
self.new_but.hide()
self.delete_setting()
else:
pass
return super().eventFilter(source, event)
if __name__ == ('__main__'):
import sys
app = QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
файл second.py:
from PyQt5.QtWidgets import (QLabel, QVBoxLayout, QLineEdit, QPushButton,
QHBoxLayout, QFileDialog, QMessageBox, QDialog)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
class Second(QDialog):
def __init__(self, Main):
super().__init__()
self.main = Main
self.setMinimumSize(300, 160)
self.setWindowFlags(Qt.Dialog)
vbox = QVBoxLayout()
hbox = QHBoxLayout()
hbox.addWidget(self._lProgLabel())
hbox.addWidget(self._leProgLabel())
vbox.addLayout(hbox)
vbox.addWidget(self._lReview())
hbox2 = QHBoxLayout()
hbox2.addWidget(self._leReview())
hbox2.addWidget(self._bReview())
vbox.addLayout(hbox2)
hbox3 = QHBoxLayout()
hbox3.addWidget(self.b_create())
hbox3.addWidget(self.b_close())
vbox.addLayout(hbox3)
self.setLayout(vbox)
def _lProgLabel(self):
_lProgLabel = QLabel(self)
_lProgLabel.setText('<center style=font-size:10pt><FONT FACE="Century Gothic">Название кнопки:</center>')
return _lProgLabel
def _leProgLabel(self):
self._leProgLabel = QLineEdit(self)
self._leProgLabel.setFont(QFont('Century Gothic', 10))
return self._leProgLabel
def _lReview(self):
_lreview = QLabel(self)
_lreview.setText(
'<center style=font-size:10pt><FONT FACE="Century Gothic">Укажите путь к программе или нажмите "Обзор":</center>')
return _lreview
def _leReview(self):
self._leReview = QLineEdit(self)
self._leReview.setFixedSize(250, 30)
self._leReview.setFont(QFont('Century Gothic', 10))
return self._leReview
def _bReview(self):
_breview = QPushButton()
_breview.setFixedSize(90, 30)
_breview.setText("Обзор")
_breview.clicked.connect(self.browseFiles)
return _breview
def browseFiles(self):
fname = QFileDialog.getOpenFileName(self, 'Open file', 'C:\Program Files', 'exe files (*.exe)')
self._leReview.setText(fname[0])
def b_create(self):
b_create = QPushButton(self)
b_create.setText("Добавить")
b_create.setMinimumSize(10, 40)
b_create.clicked.connect(self.create_button)
return b_create
def b_close(self):
b_close = QPushButton(self)
b_close.setText("Отмена")
b_close.setMinimumSize(10, 40)
b_close.clicked.connect(self._cancel)
return b_close
def _cancel(self):
self.close()
def showMessageBox(self, title, message):
msgBox = QMessageBox()
msgBox.setWindowTitle(title)
msgBox.setText(message)
msgBox.setStyleSheet("font: 12px;"
"font-family: Century Gothic;")
msgBox.setStandardButtons(QMessageBox.Ok)
msgBox.exec_()
# Проверка ввода данных
def create_button(self):
if len(self._leReview.text()) == 0:
self.showMessageBox('Внимание!',
'<center><br/><u style=font-size:10pt><FONT FACE="Arial"><b>Вы не заполнили поля</b></u></center></FONT>')
else:
self.main.create_new_button()
self.main.saved_conf()
self.close()
if __name__ == ('__main__'):
w = Second()
w.show()
Ответы (2 шт):
Автор решения: Sergey Tatarincev
→ Ссылка
Ваша проблема в том что вы каждый раз переназначаете self.new_but поэтому у вас и работает только последний добавленый.
В вашей задаче достаточно в слотах ипспользовать не self.new_but а источник сигнала (кто его вызвал) - self.sender()
Вот вам пример на минималках:
from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QGroupBox, QPushButton, QMessageBox, QMenu, QFileDialog
from PyQt5.QtCore import QEvent, Qt, QFileInfo
class Main(QWidget):
def __init__(self):
super(Main, self).__init__()
self.setWindowTitle('Образец')
self.resize(500, 300)
grid = QGridLayout()
grid.addWidget(self.prog_group_box(), 0, 0, 1, 1)
grid.addWidget(self.b_new_prog(), 1, 0, Qt.AlignRight)
self.setLayout(grid)
def prog_group_box(self):
groupBox1 = QGroupBox(self)
self.grid_prog = QGridLayout()
groupBox1.setLayout(self.grid_prog)
return groupBox1
def b_new_prog(self):
self.b_new_prog = QPushButton(self)
self.b_new_prog.setText("Добавить")
self.b_new_prog.setMinimumSize(129, 43)
self.b_new_prog.clicked.connect(self.click_newprog)
return self.b_new_prog
def click_newprog(self):
fname = QFileDialog.getOpenFileName(self, 'Open file', '', '')
new_but = QPushButton()
new_but.setFixedSize(70, 70)
new_but.clicked.connect(self.btnClicked)
fi = QFileInfo(fname[0])
new_but.setText(fi.baseName())
new_but.setProperty("filename",fname[0])
self.grid_prog.addWidget(new_but, 0, 0)
i = self.grid_prog.count() - 1
self.grid_prog.addWidget(new_but, 1 + i // 8, i % 8)
new_but.installEventFilter(self)
def btnClicked(self):
QMessageBox.information(self.sender(), "", self.sender().property("filename"))
# Контекстное меню
def eventFilter(self, source, event):
if event.type() == QEvent.ContextMenu and source.metaObject().className()=='QPushButton':
menu = QMenu()
menu.addAction('Удалить')
if menu.exec_(event.globalPos()):
reply = QMessageBox.question(
self,
'Message', "Вы действительно хотите удалить?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.Yes:
source.hide()
else:
pass
return super().eventFilter(source, event)
if __name__ == ('__main__'):
import sys
app = QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
Автор решения: Djony Cooper
→ Ссылка
Вот решение, если у кого будет такой же вопрос:
Изменения коснутся только файла: main.py:
from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QGroupBox, QPushButton, QMessageBox, QMenu
from PyQt5.QtCore import QEvent, QSettings, Qt
from PyQt5.QtGui import QIcon
class Main(QWidget):
def __init__(self):
super(Main, self).__init__()
self.setWindowTitle('Образец')
self.resize(500, 300)
grid = QGridLayout()
grid.addWidget(self.prog_group_box(), 0, 0, 1, 1)
grid.addWidget(self.b_new_prog(), 1, 0, Qt.AlignRight)
self.setLayout(grid)
self.settings = QSettings("settings.ini", QSettings.IniFormat)
self.dictButtons = {}
def prog_group_box(self):
groupBox1 = QGroupBox(self)
self.grid_prog = QGridLayout()
groupBox1.setLayout(self.grid_prog)
return groupBox1
def saved_conf(self):
# Сохранение созданной кнопки в ini файл
self.settings.beginGroup(self.new_but.text())
self.settings.setValue('exe', self.dictButtons[self.new_but.text()])
self.settings.endGroup()
self.settings.sync()
def restore_settings(self):
# Функция восстановления сохраненного конфига из ini файла
for i in self.settings.allKeys():
self.restore_buttons(i.split('/')[0], self.settings.value(i))
def restore_buttons(self, name, exe):
self.new_but = QPushButton()
self.new_but.setFixedSize(70, 70)
self.new_but.clicked.connect(self.btnClicked)
self.new_but.setText(name)
self.grid_prog.addWidget(self.new_but, 0, 0)
i = self.grid_prog.count() - 1
self.grid_prog.addWidget(self.new_but, 1 + i // 8, i % 8)
self.new_but.installEventFilter(self)
self.dictButtons[name] = exe
def delete_setting(self, button):
# Функция удаления данных из ini файла с сохраненной конфигурацией
del self.dictButtons[button.text()]
self.settings.remove(button.text())
def b_new_prog(self):
self.b_new_prog = QPushButton(self)
self.b_new_prog.setText("Добавить")
self.b_new_prog.setMinimumSize(129, 43)
self.b_new_prog.clicked.connect(self.click_newprog)
return self.b_new_prog
def click_newprog(self):
from second import Second
self.click_newprog = Second(self)
self.click_newprog.show()
def create_new_button(self):
self.new_but = QPushButton()
self.new_but.setFixedSize(70, 70)
self.new_but.clicked.connect(self.btnClicked)
self.new_but.setText(self.click_newprog._leProgLabel.text())
self.grid_prog.addWidget(self.new_but, 0, 0)
i = self.grid_prog.count() - 1
self.grid_prog.addWidget(self.new_but, 1 + i // 8, i % 8)
self.new_but.installEventFilter(self)
self.dictButtons[self.new_but.text()] = self.click_newprog._leReview.text()
def btnClicked(self):
import os
button = self.sender()
os.startfile(self.dictButtons[button.text()])
# Контекстное меню
def eventFilter(self, source, event):
button = source
if event.type() == QEvent.ContextMenu:
menu = QMenu()
menu.addAction('Удалить')
if menu.exec_(event.globalPos()):
reply = QMessageBox.question(
self,
'Message', "Вы действительно хотите удалить?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.Yes:
button.hide()
self.delete_setting(button)
else:
pass
return super().eventFilter(source, event)
if __name__ == ('__main__'):
import sys
app = QApplication(sys.argv)
w = Main()
w.show()
w.restore_settings()
sys.exit(app.exec_())