Python and PyQT6 / QFileDialog: Cannot get the list of filenames - python

I think I understand Python classes, but obviously I do not (Newbie in Python).
I have a class MainWindow(QWidget) and I want to use the outcome if the file selection dialog in the main program or somewhere else. That means, I want to get (export) the variable filenames in open_file_dialog(self)-
Maybe the code example shows my problem more clearly.
import sys
from PyQt6.QtWidgets import (
QApplication,
QWidget,
QFileDialog,
QGridLayout,
QPushButton,
QLabel,
QListWidget
)
from pathlib import Path
class MainWindow(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle('PyQt File Dialog')
self.setGeometry(100, 100, 300, 150)
layout = QGridLayout()
self.setLayout(layout)
# file selection
file_browser_btn = QPushButton('Browse')
file_browser_btn.clicked.connect(self.open_file_dialog)
self.file_list = QListWidget(self)
layout.addWidget(QLabel('Files:'), 0, 0)
layout.addWidget(self.file_list, 1, 0)
layout.addWidget(file_browser_btn, 2 ,0)
self.show()
def open_file_dialog(self):
dialog = QFileDialog(self)
dialog.setDirectory(r'C:')
dialog.setFileMode(QFileDialog.FileMode.ExistingFiles)
dialog.setNameFilter("Text (*.txt *.eml)")
dialog.setViewMode(QFileDialog.ViewMode.List)
if dialog.exec():
self.lw = QWidget.QListWidget()
filenames = dialog.selectedFiles()
if filenames:
self.file_list.addItems([str(Path(filename)) for filename in filenames])
# y=self.file_list
# super().y=self.file_list
# MainWindow.y=self.file_list
# MainWindow.y=filenames
QWidget.QListWidgetItem('myItem only 1', self.lw)
x=1
# y=self.file_list
# y=filenames
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
# fnames = QStringList()
# print (window.__getattribute__ ('file_list'))
# print (window::file_list())
# print (window.dialog.selectedfiles ())
# print (window.selectedfiles())
# print (window.filenames)
# print (window.file_list [1])
# print (window.y [1])
# print (window.y)
# lst = QWidget.file_list()
# lst = QWidget.lw()
# lst = QWidget.QListWidget()
lst = window.QListWidget()
items = []
for x in range(lst.count()):
items.append(lst.item(x).text())
print(items)
# I want the list of selected files here, nothng works ...
sys.exit(app.exec())

Related

How do I connect three or more windows in PyQt

I have a complex program in which I need to connect multiple windows. Unfortunately, I seem to be not fully understanding the concept/steps necessary to do this so bonus points if anyone can explain the steps/process well. In my current program, I have a list of items. Once I select them by moving them over to the right list widget, I need them to go to the third window. The third window should be activated by clicking the dots on the second window. The program runs and shows the second window appropriately but the signal/slot connection of the dots button does not work. However, the rest of the code is working because if I switch the toolkit to show the third window, that part is performing as expected. My code is below, and again, no errors are being returned, but clicking the dots button on the second window does nothing.
Also, a question - do I instantiate the third window within the second class, or only within the main window? Again, struggling to fully understand the process and I will need to do this multiple more times.
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QListWidget, QLineEdit, QTextEdit, QGridLayout, QHBoxLayout, QVBoxLayout, QSizePolicy, QFileDialog, QTabWidget, QCheckBox
import PyQt5.QtGui as qtg
import glob
import os
from PyQt5.QtCore import Qt, QSettings
import inspect
from PyQt5 import QtCore
import pandas as pd
import pathlib
import pyreadstat
import json
class ThirdWindow(QWidget):
def __init__(self):
super().__init__()
self.layout = QGridLayout()
self.setLayout(self.layout)
self.allVariables = QListWidget()
self.variablesSelected = QListWidget()
#self.allVariables.insertItem(0, 'Hello')
self.layout.addWidget(self.allVariables, 1,0)
self.layout.addWidget(self.variablesSelected, 1, 1)
def setItems(self, items):
self.allVariables.clear()
for item in items:
self.allVariables.addItem(item)
class SecondWindow(QWidget):
def __init__(self):
super().__init__()
##not sure if I am supposed to instantiate this here or only in the main window class
self.thirdWindow = ThirdWindow()
self.layout = QGridLayout(self)
self.by = QLabel("By")
self.byVariables = QLineEdit()
self.byButton = QPushButton("...")
self.layout.addWidget(self.by, 1, 0)
self.layout.addWidget(self.byVariables, 2, 0)
self.layout.addWidget(self.byButton, 2, 1)
def seconddWindowConnections(self):
self.byButton.clicked.connect(self.show_third_window)
#self.buttons['Toolkit'].clicked.connect(self.show_new_window)
def show_third_window(self):
self.thirdWindow.show()
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# Add a title
self.setWindowTitle("GUI Querying Program")
self.layout = QHBoxLayout()
self.setLayout(self.layout)
self.initUI()
self.setButtonConnections()
self.sw = SecondWindow()
self.tw = ThirdWindow()
def initUI(self):
subLayouts = {}
subLayouts['LeftColumn'] = QGridLayout()
self.layout.addLayout(subLayouts['LeftColumn'],1)
# Buttons
self.buttons = {}
self.buttons['addVariable'] = QPushButton('>')
self.buttons['removeVariable'] = QPushButton('<')
self.buttons['Toolkit'] = QPushButton('Toolkit')
self.variables = QListWidget()
self.selectedVariables = QListWidget()
subLayouts['LeftColumn'].addWidget(self.variables, 7,0,4,1)
subLayouts['LeftColumn'].addWidget(self.selectedVariables, 7,1,4,1)
subLayouts['LeftColumn'].addWidget(self.buttons['addVariable'], 10,0,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['removeVariable'], 10,1,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['Toolkit'], 11,1,1,1)
names = ['apple', 'banana', 'Cherry']
self.variables.insertItems(0, names)
def setButtonConnections(self):
self.buttons['addVariable'].clicked.connect(self.add_variable)
self.buttons['Toolkit'].clicked.connect(self.show_new_window)
self.buttons['Toolkit'].clicked.connect(self.add_selected_variables)
def add_variable(self):
for item in self.variables.selectedItems():
self.selectedVariables.addItem(item.clone())
def show_new_window(self):
self.sw.show()
def add_selected_variables(self):
items = []
for i in range(self.selectedVariables.count()):
items.append(self.selectedVariables.item(i).clone())
self.tw.setItems(items)
if __name__ == "__main__":
import sys
app = QApplication([])
mw = MainWindow()
mw.show()
app.exec()
The main issue with your code is that secondWindowConnections is never called so the button actually does nothing. I corrected that and fixed a few other issues I found in my example below. I left out the bits where I made no changes and all the changes I did make I made inline notes explaining them:
class SecondWindow(QWidget):
def __init__(self):
super().__init__()
self.thirdWindow = None # dont initialize until neccessary
self.thirdWindowItems = []
self.layout = QGridLayout(self)
self.by = QLabel("By")
self.byVariables = QLineEdit()
self.byButton = QPushButton("...")
self.layout.addWidget(self.by, 1, 0)
self.layout.addWidget(self.byVariables, 2, 0)
self.layout.addWidget(self.byButton, 2, 1)
self.secondWindowConnections() # Run this to setup the
# signal for the third window.
def secondWindowConnections(self): # this had a typo
self.byButton.clicked.connect(self.show_third_window)
def show_third_window(self):
if self.thirdWindow is None: # if window has been created yet
self.thirdWindow = ThirdWindow() # create window
if not self.thirdWindow.isVisible(): # if window is showing
self.thirdWindow.show() # show window
self.thirdWindow.setItems(self.thirdWindowItems) # send items to window
def send_items(self, items): # this is to collect the variable that
self.thirdWindowItems = items # move to the third window
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# Add a title
self.setWindowTitle("GUI Querying Program")
self.layout = QHBoxLayout()
self.setLayout(self.layout)
self.initUI()
self.setButtonConnections()
self.sw = None # dont initialize until neccessary.
def initUI(self):
subLayouts = {}
subLayouts['LeftColumn'] = QGridLayout()
self.layout.addLayout(subLayouts['LeftColumn'],1)
self.buttons = {}
self.buttons['addVariable'] = QPushButton('>')
self.buttons['removeVariable'] = QPushButton('<')
self.buttons['Toolkit'] = QPushButton('Toolkit')
self.variables = QListWidget()
self.selectedVariables = QListWidget()
subLayouts['LeftColumn'].addWidget(self.variables, 7,0,4,1)
subLayouts['LeftColumn'].addWidget(self.selectedVariables, 7,1,4,1)
subLayouts['LeftColumn'].addWidget(self.buttons['addVariable'], 10,0,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['removeVariable'], 10,1,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['Toolkit'], 11,1,1,1)
names = ['apple', 'banana', 'Cherry']
self.variables.insertItems(0, names)
def setButtonConnections(self):
self.buttons['addVariable'].clicked.connect(self.add_variable)
self.buttons['Toolkit'].clicked.connect(self.show_new_window)
# self.buttons['Toolkit'].clicked.connect(self.add_selected_variables)
# only use one connnect slot
def add_variable(self):
for item in self.variables.selectedItems():
self.selectedVariables.addItem(item.clone())
def show_new_window(self):
if self.sw is None: # check if window has been constructed
self.sw = SecondWindow() # construct window
if not self.sw.isVisible(): # If winow is not showing
self.sw.show() # show window
self.sw.send_items(self.add_selected_variables()) # send selected
# variables to second window
def add_selected_variables(self):
items = []
for i in range(self.selectedVariables.count()):
items.append(self.selectedVariables.item(i).clone())
# self.tw.setItems(items) ... self.tw doesnt exist so return them
return items

Data not being added to table with PyQt5

My code is supposed to add data to a two column table when "add" button is clicked. The problem is that when the "add" button is clicked, only the empty row is being added. Can someone please let me know what is wrong? Below is the part of the code that adds data1 and data2 to a table on the right side of the layout. The function add_entry is where the data is being added.
# Import dependencies
from PyQt5.QtWidgets import (QWidget, QApplication, QTableWidget, QTableWidgetItem,QHBoxLayout, QVBoxLayout, QHeaderView, QPushButton, QDialog,
QLabel, QFileDialog, QMainWindow, QAction, QLineEdit)
from PyQt5.Qt import Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtChart import QChart, QChartView, QLineSeries
import sys
import pandas as pd
import math
# ------------------------------------------------------UI-main----------------------------------------------------------------------------------
# Creates a QApplication instance
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.items=0
# Creates table on the left size
self.table_l = QTableWidget()
self.table_l.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# Creates layout object for the right side
self.layoutRight = QVBoxLayout()
# Creates chart widget
self.chartView = QChartView()
# Smooths the edge of the chart
self.chartView.setRenderHint(QPainter.Antialiasing)
# Creates table on the right size
self.table_r = QTableWidget()
self.table_r.setColumnCount(2)
# self.table_r.setRowCount()
self.table_r.setHorizontalHeaderLabels(('Data1', 'Data2'))
self.table_r.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.table_r.setMaximumSize(600, 300)
self.lineEditData1 = QLineEdit()
self.lineEditData2 = QLineEdit()
# Create push buttons
self.buttonAdd = QPushButton('Add')
self.buttonClear = QPushButton('Clear')
self.buttonQuit = QPushButton('Quit')
self.buttonAdd.setEnabled(False)
self.layoutRight.setSpacing(10)
self.layoutRight.addWidget(self.table_r, 50)
self.layoutRight.addWidget(QLabel('data1'))
self.layoutRight.addWidget(self.lineEditData1)
self.layoutRight.addWidget(QLabel('data2'))
self.layoutRight.addWidget(self.lineEditData2)
self.layoutRight.addWidget(self.buttonAdd)
self.layout = QHBoxLayout()
self.layout.addWidget(self.table_l, 50)
self.setLayout(self.layout)
self.layout.addLayout(self.layoutRight, 50)
# Connect button to function functions
self.buttonQuit.clicked.connect(lambda:app.quit())
self.buttonAdd.clicked.connect(self.add_entry)
self.buttonClear.clicked.connect(self.reset_table)
self.lineEditData1.textChanged[str].connect(self.check_disable)
self.lineEditData2.textChanged[str].connect(self.check_disable)
def add_entry(self):
Data1 = self.lineEditData1.text()
Data2 = self.lineEditData2.text()
try:
Data1Item = QTableWidgetItem(int(Data1))
Data2Item = QTableWidgetItem(float(Data2))
Data2Item.setTextAlignment(Qt.AlignRight | Qt.AlignCenter)
self.table_r.insertRow(self.items)
self.table_r.setItem(self.items, 0, Data1Item)
self.table_r.setItem(self.items, 1, Data2Item)
self.items +=1
# after passing the item, clear the field by entering an empty string
self.lineEditData1.setText('')
self.lineEditData2.setText('')
except ValueError:
pass
# Creates main window object instance
class MainWindow(QMainWindow):
def __init__(self, widget):
super().__init__()
self.setWindowTitle('test')
self.resize(1200, 1200)
self.menuBar = self.menuBar()
self.fileMenu = self.menuBar.addMenu('File')
# import wind speed data
importAction = QAction('Open File', self)
importAction.setShortcut('Ctrl+O')
# exit action
exitAction = QAction('Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(lambda: app.quit())
self.fileMenu.addAction(importAction)
self.fileMenu.addAction(exitAction)
self.setCentralWidget(widget)
if __name__ =='__main__':
# don't auto scale when drag app to a different monitor
#QGuiApplication.setHightDpiScaleFactorRoundingPolicy(Qt.HightDpiScaleFactorRoundingPolicy.PassThrough)
app = QApplication(sys.argv)
w = MyApp()
demo = MainWindow(w)
demo.show()
try:
sys.exit(app.exec())
except SystemExit:
print('Closing window...')
The objective of exceptions is not to hide errors but to know how to prevent them, so they must be as small as possible so as not to hide other errors. In this case, QTableWidgetItem accepts a string as an argument and not numerical values, therefore an exception is thrown preventing the code that adds the items from being executed. The solution is to use the setData() method of the QTableWidgetItem:
def add_entry(self):
data1 = self.lineEditData1.text()
data2 = self.lineEditData2.text()
try:
value1 = int(data1)
value2 = float(data2)
except ValueError:
print("failed conversion")
return
else:
data1_item = QTableWidgetItem()
data1_item.setData(Qt.DisplayRole, value1)
data2_item = QTableWidgetItem()
data2_item.setData(Qt.DisplayRole, value2)
data2_item.setTextAlignment(Qt.AlignRight | Qt.AlignCenter)
row = self.table_r.rowCount()
self.table_r.insertRow(row)
self.table_r.setItem(row, 0, data1_item)
self.table_r.setItem(row, 1, data2_item)
self.lineEditData1.clear()
self.lineEditData2.clear()

Real Time Change of QT Widgets?

I'm trying to create a simple class recitation app where it randomly picks from the list of students.
It works, but I want it to change in real time, so there's a bit of suspense who is being picked.
You can see in the illustration video below that the console updates the names in real time but the QtWidget does not:
https://dl.dropboxusercontent.com/s/rwcbhhj58tevshl/py003_pyqt_real_time_update_widget.mp4?dl=0
import sys
import os
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QApplication, QMainWindow, QSpinBox, QWidget, QPushButton, QTextEdit, QVBoxLayout, QHBoxLayout, QLineEdit, QLabel
from PySide2.QtGui import QPixmap
from time import sleep, perf_counter
import time
import random
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Class Recitation")
# Widget
root_img_path = 'img'
self.image_list = []
for root, dirs, files in os.walk(root_img_path):
for f in files:
img_path = os.path.join(root_img_path, f)
first_name = f.split('_')[0]
last_name = f.split('_')[1]
self.image_list.append( (img_path, first_name + " " + last_name ) )
# creating label
self.label = QLabel(self)
# loading image
#self.pixmap = QPixmap('img/Eijirou_Kirishima_Portrait.png')
self.pixmap = QPixmap(self.image_list[0][0])
# adding image to label
self.label.setPixmap(self.pixmap)
self.name_label = QLabel()
self.name_label.setText(self.image_list[0][1])
self.pick_btn = QPushButton("Pick")
self.pick_btn.setObjectName("Pick")
self.pick_btn.clicked.connect(self.random_pick)
# Layout Creations
hbox = QHBoxLayout()
# hbox.addWidget(self.search_button)
# hbox.addWidget(self.search_bar)
vbox = QVBoxLayout()
vbox.addWidget(self.label)
vbox.addWidget(self.name_label)
vbox.addWidget(self.pick_btn)
# vbox.addWidget(self.result_text_edit)
layout = QVBoxLayout()
layout.addLayout(hbox)
layout.addLayout(vbox)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
def random_pick(self):
choice_list = self.image_list
time_started = time.perf_counter()
counter = 0
for x in range(0,10000):
random_pick = random.choice(choice_list)
self.name_label.setText(random_pick[1])
self.label.setPixmap(random_pick[0])
print (random_pick[1])
sleep(counter)
current_time = time.perf_counter()
time_elapse = current_time - time_started
counter += 0.0001 * (2**4)
if time_elapse >= 5:
break
return random_pick
def change_name(self):
self.name_label.setText("Name Changed")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
Is there a way around this?
Either call QApplication.processEvents() after time.sleep(),
def random_pick(self):
...
for x in range(0,10000):
...
sleep(counter)
QApplication.processEvents()
...
or use QTimer instead, which also avoids the arbitrary loop and is entirely non-blocking.
class MainWindow(QMainWindow):
def __init__(self):
...
self.pick_btn = QPushButton("Pick")
self.pick_btn.setObjectName("Pick")
self.pick_btn.clicked.connect(self.start_timer)
self.timer = QTimer(timeout=self.random_pick)
...
def start_timer(self):
self.time_started = time.perf_counter()
self.counter = 0
self.timer.start(0)
def random_pick(self):
choice_list = self.image_list
random_pick = random.choice(choice_list)
self.name_label.setText(random_pick[1])
self.label.setPixmap(random_pick[0])
current_time = time.perf_counter()
time_elapse = current_time - self.time_started
self.counter += 0.0001 * (2**4)
self.timer.setInterval(self.counter * 1000)
if time_elapse >= 5:
self.timer.stop()
return random_pick

Highlighting portions of text in QPlainTextEdit

I have a list in QPlainTextEdit and want to highlight a specific line, line number 5 say.
I looked at various tutorials and examples, but the documentation of PyQt5 seems rather sparse.
Workable code, I would like to highlight lines.
For a suggestion of another widget I am also grateful. I don't want to edit this list, just show it and highlight lines.
import sys
from PyQt5.QtWidgets import *
app = QApplication(sys.argv)
main = QWidget()
main.resize(250, 150)
main.size
tbox = QPlainTextEdit(main)
for nr in range(1,5):
tbox.appendPlainText('%d'%nr)
## highlight line 2
## wait a while
## unhighlight line 2
## highlight line 4
main.show()
sys.exit(app.exec_())
If the highlighting depends on the text then the solution of #oetzi is the one indicated because although you delete lines the same text will remain highlighted, if instead the highlighting only depends on the position of the line then a possible solution is to use QSyntaxHighlighter.
In the following example you can enter whole numbers separated from spaces that indicate the position of the lines that will be highlighted (the position starts from 0):
import sys
from PyQt5.QtCore import pyqtSlot, QRegExp
from PyQt5.QtGui import QColor, QRegExpValidator, QSyntaxHighlighter, QTextCharFormat
from PyQt5.QtWidgets import (
QApplication,
QLineEdit,
QPlainTextEdit,
QVBoxLayout,
QWidget,
)
class SyntaxHighlighter(QSyntaxHighlighter):
def __init__(self, parent):
super(SyntaxHighlighter, self).__init__(parent)
self._highlight_lines = dict()
def highlight_line(self, line, fmt):
if isinstance(line, int) and line >= 0 and isinstance(fmt, QTextCharFormat):
self._highlight_lines[line] = fmt
tb = self.document().findBlockByLineNumber(line)
self.rehighlightBlock(tb)
def clear_highlight(self):
self._highlight_lines = dict()
self.rehighlight()
def highlightBlock(self, text):
line = self.currentBlock().blockNumber()
fmt = self._highlight_lines.get(line)
if fmt is not None:
self.setFormat(0, len(text), fmt)
class Widget(QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self._lineedit = QLineEdit(textChanged=self.onTextChanged)
regex_validator = QRegExpValidator(QRegExp(r"[0-9 ]+"))
self._lineedit.setValidator(regex_validator)
self._plaintextedit = QPlainTextEdit()
self._highlighter = SyntaxHighlighter(self._plaintextedit.document())
lay = QVBoxLayout(self)
lay.addWidget(self._lineedit)
lay.addWidget(self._plaintextedit)
for i in range(10):
self._plaintextedit.appendPlainText("line %d" % i)
self.resize(320, 240)
#pyqtSlot(str)
def onTextChanged(self, text):
fmt = QTextCharFormat()
fmt.setBackground(QColor("yellow"))
self._highlighter.clear_highlight()
for e in text.split():
line = int(e)
self._highlighter.highlight_line(line, fmt)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
import sys
from PyQt5.QtWidgets import (QPlainTextEdit, QApplication, QWidget)
app = QApplication(sys.argv)
main = QWidget()
main.resize(250, 150)
main.size
tbox = QPlainTextEdit(main)
condition = 0
for nr in range(1, 5):
if condition % 2 == 0:
tbox.appendHtml(f"<span style='background-color: yellow;'>{nr}</p>")
else:
tbox.appendHtml(f"<span style='background-color: white;'>{nr}</p>")
# tbox.appendPlainText('%d' % nr)
condition = condition + 1
main.show()
sys.exit(app.exec_())

Removing dynamically created Qmenu items

I have a Qmenu that I am creating by loading a list with Qsettings and I am trying to be able to remove items from the menu by loading the list in a QListQWidget and deleting the selected items. Currently I am able to delete the menu items in the list widget and it removes them from qsettings as well but I can't figure out how to remove the items from the menu without restarting. I have tried various things such as removeAction etc but haven't been able to figure it out.
Here is my code:
import functools
import sys
from PyQt5 import QtCore
from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, \
QApplication, QAction, QMenu, QListWidgetItem, \
QListWidget, QGridLayout
class MainWindow(QWidget):
settings = QtCore.QSettings('test_org', 'my_app')
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.layout = QHBoxLayout()
self.menu_btn = QPushButton()
self.menu = QMenu()
self.add_menu = self.menu.addMenu("Menu")
self.menu_btn.setMenu(self.menu)
self.open_list_btn = QPushButton('open list')
self.load_items = self.settings.value('menu_items', [])
for item in self.load_items:
self.action = QAction(item[0], self)
self.action.setData(item)
self.add_menu.addAction(self.action)
self.action.triggered.connect(functools.partial(self.menu_clicked, self.action))
self.layout.addWidget(self.menu_btn)
self.layout.addWidget(self.open_list_btn)
self.setLayout(self.layout)
self.open_list_btn.clicked.connect(self.open_window)
def open_window(self):
self.create_menu_item = List()
self.create_menu_item.show()
def menu_clicked(self, item):
itmData = item.data()
print(itmData)
class List(QWidget):
settings = QtCore.QSettings('test_org', 'my_app')
def __init__(self, parent=None):
super(List, self).__init__(parent)
self.menu_items = self.settings.value('menu_items', [])
self.layout = QGridLayout()
self.list = QListWidget()
self.remove_btn = QPushButton('Remove')
self.layout.addWidget(self.list, 1, 1, 1, 1)
self.layout.addWidget(self.remove_btn, 2, 1, 1, 1)
self.setLayout(self.layout)
self.remove_btn.clicked.connect(self.remove_items)
for item in self.menu_items:
self.item = QListWidgetItem()
self.item.setText(str(item[0]))
self.list.addItem(self.item)
def remove_items(self):
self.menu_items = self.settings.value('menu_items', [])
del self.menu_items[self.list.currentRow()]
self.settings.setValue('menu_items', self.menu_items)
listItems = self.list.selectedItems()
if not listItems: return
for item in listItems:
self.list.takeItem(self.list.row(item))
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()
Does anyone have any ideas?
EDIT:
This is the structure of the list in QSettings. I load the menu with this and I load the QlistWidget with this. I am trying to get the menu to remove the items as well when I remove them for the QListWidget.
mylist = ['item_name',['itemdata1', 'itemdata2', 'itemdata3'],
'item_name2',['itemdata1', 'itemdata2', 'itemdata3'],
'item_name3',['itemdata1', 'itemdata2', 'itemdata3']]
I think the data structure that you are using is incorrect because when I execute your code it generates twice as many QActions, the structure I propose is a dictionary where the keys are the name of the QAction and the value of the list of data:
{
'item0': ['itemdata00', 'itemdata01', 'itemdata02'],
'item1': ['itemdata10', 'itemdata11', 'itemdata12'],
...
}
To build the initial configuration use the following script:
create_settings.py
from PyQt5 import QtCore
if __name__ == '__main__':
settings = QtCore.QSettings('test_org', 'my_app')
d = {}
for i in range(5):
key = "item{}".format(i)
value = ["itemdata{}{}".format(i, j) for j in range(3)]
d[key] = value
settings.setValue('menu_items', d)
print(d)
settings.sync()
On the other hand I think that the widget that you want to handle the destruction of QActions should take over the corresponding QMenu as I show below:
import sys
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
layout = QtWidgets.QHBoxLayout(self)
menu_btn = QtWidgets.QPushButton()
open_list_btn = QtWidgets.QPushButton('open list')
layout.addWidget(menu_btn)
layout.addWidget(open_list_btn)
menu = QtWidgets.QMenu()
menu_btn.setMenu(menu)
self.menu_manager = MenuManager("menu_items", "Menu")
menu.addMenu(self.menu_manager.menu)
self.menu_manager.menu.triggered.connect(self.menu_clicked)
open_list_btn.clicked.connect(self.menu_manager.show)
def menu_clicked(self, action):
itmData = action.data()
print(itmData)
class MenuManager(QtWidgets.QWidget):
def __init__(self, key, menuname, parent=None):
super(MenuManager, self).__init__(parent)
self.settings = QtCore.QSettings('test_org', 'my_app')
self.key = key
self.layout = QtWidgets.QVBoxLayout(self)
self.listWidget = QtWidgets.QListWidget()
self.remove_btn = QtWidgets.QPushButton('Remove')
self.layout.addWidget(self.listWidget)
self.layout.addWidget(self.remove_btn)
self.remove_btn.clicked.connect(self.remove_items)
self.menu = QtWidgets.QMenu(menuname)
load_items = self.settings.value(self.key, [])
for name, itemdata in load_items.items():
action = QtWidgets.QAction(name, self.menu)
action.setData(itemdata)
self.menu.addAction(action)
item = QtWidgets.QListWidgetItem(name)
item.setData(QtCore.Qt.UserRole, action)
self.listWidget.addItem(item)
def remove_items(self):
for item in self.listWidget.selectedItems():
it = self.listWidget.takeItem(self.listWidget.row(item))
action = it.data(QtCore.Qt.UserRole)
self.menu.removeAction(action)
self.sync_data()
def sync_data(self):
save_items = {}
for i in range(self.listWidget.count()):
it = self.listWidget.item(i)
action = it.data(QtCore.Qt.UserRole)
save_items[it.text()] = action.data()
self.settings.setValue(self.key, save_items)
self.settings.sync()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I got it figured out. I am not sure of a better way but I did it using object names.
In the MainWindow I set objectNames to self.action using the first item of each list in the list of lists inside the for loop like this:
self.action.setObjectName(item[0])
Then I created this function in the MainWindow class:
def remove_menu_item(self, value):
self.add_menu.removeAction(self.findChild(QAction, value))
Then I added this:
w.remove_menu_item(item.text())
To the remove function in the List class to get the same first item in the list of lists which is now the objectName for the QActions.

Categories

Resources