How I can right justify the QKeySequence in PyQt5?
copy_absolute_path_action = (
create_action(self, _("Copy Absolute Path"), QKeySequence(
get_shortcut('explorer', 'copy absolute path')),
triggered=self.copy_absolute_path))
copy_relative_path_action = (
create_action(self, _("Copy Relative Path"), QKeySequence(
get_shortcut('explorer', 'copy relative path')),
triggered=self.copy_relative_path))
copy_file_clipboard_action = (
create_action(self, _("Copy File to Clipboard"),
QKeySequence(get_shortcut('explorer', 'copy file')),
icon=ima.icon('editcopy'),
triggered=self.copy_file_clipboard))
save_file_clipboard_action = (
create_action(self, _("Paste File from Clipboard"),
QKeySequence(get_shortcut('explorer', 'paste file')),
icon=ima.icon('editpaste'),
triggered=self.save_file_clipboard))
I want the key shortcuts to be right justified and the rest unchanged.
Thanks in advance
In this case the solution is to implement a QProxyStyle:
from PyQt5 import QtCore, QtGui, QtWidgets
class MenuProxyStyle(QtWidgets.QProxyStyle):
def drawControl(self, element, option, painter, widget=None):
shortcut = ""
if element == QtWidgets.QStyle.CE_MenuItem:
vals = option.text.split("\t")
if len(vals) == 2:
text, shortcut = vals
option.text = text
super(MenuProxyStyle, self).drawControl(element, option, painter, widget)
if shortcut:
margin = 10 # QStyleHelper::dpiScaled(5)
self.proxy().drawItemText(painter, option.rect.adjusted(margin, 0, -margin, 0),
QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter,
option.palette, option.state & QtWidgets.QStyle.State_Enabled,
shortcut, QtGui.QPalette.Text)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
menu = QtWidgets.QMenu("File", self)
self._proxy = MenuProxyStyle(menu.style())
menu.setStyle(self._proxy)
self.menuBar().addMenu(menu)
# create icons
data = [("Copy Absolute Path", "Ctrl+Alt+C"),
("Copy Relative Path", "Ctrl+Shift+C"),
("Copy File to Clipboard", "Ctrl+C")]
for text, shortcut in data:
action = QtWidgets.QAction(self,
text=text,
shortcut=QtGui.QKeySequence(shortcut))
menu.addAction(action)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Related
Adapting a code from PyQt4 to PyQt6 I was able to simulate a QCheckBox (select all) in the QHeaderView of a QTableView. However, clicks in regions outside the QCheckBox rectangle in the same column or in other columns of the table, act on the QCheckBox.
class CheckBoxHeader(QtWidgets.QHeaderView):
clicked = QtCore.pyqtSignal(bool)
def __init__(self, orientation = Qt.Orientation.Horizontal, parent = None):
super(CheckBoxHeader,self).__init__(orientation, parent)
self.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Stretch)
self.isChecked = False
def paintSection(self, painter, rect, logicalIndex):
painter.save()
super(CheckBoxHeader,self).paintSection(painter, rect, logicalIndex)
painter.restore()
if logicalIndex==3:
option = QtWidgets.QStyleOptionButton()
option.rect= QtCore.QRect(503,1,20,20)
option.state= QtWidgets.QStyle.StateFlag.State_Enabled | QtWidgets.QStyle.StateFlag.State_Active
if self.isChecked:
option.state|= QtWidgets.QStyle.StateFlag.State_On
else:
option.state|= QtWidgets.QStyle.StateFlag.State_Off
self.style().drawControl(QtWidgets.QStyle.ControlElement.CE_CheckBox, option, painter)
def mousePressEvent(self, event):
if self.isChecked:
self.isChecked = False
else:
self.isChecked = True
self.clicked.emit(self.isChecked)
self.viewport().update()
class Ui_teste(object):
def setupUi(self, janela):
janela.setObjectName("cadastro_clientes")
janela.resize(700, 400)
janela.setAccessibleName("")
janela.setTabShape(QtWidgets.QTabWidget.TabShape.Rounded)
data = [
("Joe", "Senior Web Developerrwyeriweyrtiwyrtiwyetrwetruwtruw", "joe#example.com"),
("Lara", "Project Manager", "lara#example.com"),
("David", "Data Analyst", "david#example.com"),
("Jane", "Senior Python Developer", "jane#example.com"),
]
self.tableWidget = QtWidgets.QTableWidget(janela)
self.tableWidget.setGeometry(5,5,600,320)
self.tableWidget.setColumnCount(4)
self.tableWidget.setHorizontalHeaderLabels(["Nome", "Profissão", "Email"," "])
self.tableWidget.setRowCount(len(data))
self.header = CheckBoxHeader(parent=self.tableWidget)
self.header.setMaximumSectionSize(10)
self.header.clicked.connect(self.on_headerClick)
self.tableWidget.setHorizontalHeader(self.header)
self.tableWidget.show()
self.tableWidget.setRowCount(0)
for index, c in enumerate(data):
rows = self.tableWidget.rowCount()
self.tableWidget.setRowCount(rows + 1)
self.tableWidget.setItem(rows, 0, QtWidgets.QTableWidgetItem(str(c[0])))
self.tableWidget.setItem(rows, 1, QtWidgets.QTableWidgetItem(str(c[1])))
self.tableWidget.setItem(rows, 2, QtWidgets.QTableWidgetItem(str(c[2])))
pWidget = QtWidgets.QWidget()
pCheckBox = QtWidgets.QCheckBox()
pCheckBox.clicked.connect(self.on_changeCheckBox)
pLayout = QtWidgets.QHBoxLayout(pWidget)
pLayout.addWidget(pCheckBox)
pLayout.setAlignment(Qt.AlignmentFlag.AlignCenter)
pLayout.setContentsMargins(0,0,0,0)
pWidget.setLayout(pLayout)
self.tableWidget.setCellWidget(rows, 3, pWidget)
def on_headerClick(self,isCheck):
qtde_rows = self.tableWidget.rowCount()
if isCheck:
for i in range(qtde_rows):
self.tableWidget.cellWidget(i, 3).layout().itemAt(0).widget().setChecked(True)
else:
for i in range(qtde_rows):
self.tableWidget.cellWidget(i, 3).layout().itemAt(0).widget().setChecked(False)
#print(isCheck)
def on_changeCheckBox(self, isCheck):
if not isCheck:
self.tableWidget.horizontalHeader().isChecked = False
self.tableWidget.horizontalHeader().viewport().update()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
testes = QtWidgets.QMainWindow()
ui = Ui_teste()
ui.setupUi(testes)
testes.show()
sys.exit(app.exec())
What I need is for only the rectangle region of the QCheckBox to be selectable. Can anybody help me?
QTableView with QHeaderView and select all QCheckBox
I am using a QListView and a QFileSystemModel to display the contents of a directory. I'm trying to emulate the Windows File Explorer, where if the file/folder text is long enough it wraps to display the full name of the object.
As it looks in File Explorer
On my view I've tried setGridSize(QtCore.QSize(80, 80)) to give me enough space, setWordWrap(True), and setTextElideMode(QtCore.Qt.ElideNone)
But the text still gets cropped.
I've looked into using a QStyledItemDelegate in order to wrap the text, but I am unsure how to go about getting the behavior I want.
How can I set the view to show the text wrapping and not cropping any of the text?
Here's the code I've created so far...
import sys
from PySide2 import QtCore
from PySide2 import QtWidgets
from shiboken2 import wrapInstance
class TreeViewDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(TreeViewDialog, self).__init__(parent)
self.setMinimumSize(500, 400)
self.create_widgets()
self.create_layout()
def create_widgets(self):
root_path = r"C:\Users\Documents\Test"
self.model = QtWidgets.QFileSystemModel()
self.model.setRootPath(root_path)
self.list_view = QtWidgets.QListView()
self.list_view.setViewMode(QtWidgets.QListView.IconMode)
self.list_view.setResizeMode(QtWidgets.QListView.Adjust)
self.list_view.setFlow(QtWidgets.QListView.LeftToRight)
self.list_view.setMovement(QtWidgets.QListView.Snap)
self.list_view.setModel(self.model)
self.list_view.setRootIndex(self.model.index(root_path))
self.list_view.setGridSize(QtCore.QSize(80, 80))
self.list_view.setUniformItemSizes(True)
self.list_view.setWordWrap(True)
self.list_view.setTextElideMode(QtCore.Qt.ElideNone)
def create_layout(self):
main_layout = QtWidgets.QHBoxLayout(self)
main_layout.setContentsMargins(2, 2, 2, 2)
main_layout.addWidget(self.list_view)
if __name__ == "__main__":
app = QtWidgets.QApplication.instance()
if not app:
app = QtWidgets.QApplication(sys.argv)
tree_view_dialog = TreeViewDialog()
tree_view_dialog.show()
sys.exit(app.exec_())
So I was able to get the behavior I was looking for by implementing a custom QStyledItemDelegate and implementing the paint() and sizeHint() methods and setting setItemDelegate() on my QListView.
delegate = FileNameDelegate(self)
self.list_view.setItemDelegate(delegate)
Here's my delegate class.
class FileNameDelegate(QtWidgets.QStyledItemDelegate):
"""Delegate to wrap filenames."""
def paint(self, painter, option, index):
if not index.isValid():
return
painter.save()
# Selected
if option.state & QtWidgets.QStyle.State_Selected:
painter.fillRect(option.rect, option.palette.highlight())
# Icon
icon = index.data(QtCore.Qt.DecorationRole)
mode = QtGui.QIcon.Normal
state = QtGui.QIcon.On if option.state & QtWidgets.QStyle.State_Open else QtGui.QIcon.Off
icon_rect = QtCore.QRect(option.rect)
icon_rect.setSize(QtCore.QSize(option.rect.width(), 40))
icon.paint(painter, icon_rect, alignment=QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, mode=mode, state=state)
# Text
text = index.data(QtCore.Qt.DisplayRole)
font = QtWidgets.QApplication.font()
font_metrics = QtGui.QFontMetrics(font)
padding = 8
rect = font_metrics.boundingRect(option.rect.left()+padding/2, option.rect.bottom()-icon_rect.height()+padding/2,
option.rect.width()-padding, option.rect.height()-padding,
QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop | QtCore.Qt.TextWrapAnywhere,
text)
color = QtWidgets.QApplication.palette().text().color()
pen = QtGui.QPen(color)
painter.setPen(pen)
painter.setFont(font)
painter.drawText(rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop | QtCore.Qt.TextWrapAnywhere, text)
painter.restore()
def sizeHint(self, option, index):
if not index.isValid():
return super(FileNameDelegate, self).sizeHint(option, index)
else:
text = index.data()
font = QtWidgets.QApplication.font()
font_metrics = QtGui.QFontMetrics(font)
rect = font_metrics.boundingRect(0, 0, option.rect.width(), 0,
QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop | QtCore.Qt.TextWrapAnywhere,
text)
size = QtCore.QSize(option.rect.width(), option.rect.height()+rect.height())
return size
I now get this when running the code.
Starting the program, the QIcon is aligned on the left (it's standard i guess) with the text right to it.
Instead I want the icon to be centered on top with the text below it.
I tried using setStyleSheet with show_all.setStyleSheet("QIcon { vertical-align: top }") and show_all.setStyleSheet("QPushButton { text-align: bottom }").
How can I achieve this?
QPushButton doesn't allow to choose the layout of its icon and label. Also, remember that while Qt features style sheets to style widgets, not all CSS known properties and selectors are available. Furthermore, style sheets only work on widgets, so using the QIcon selector isn't supported, since QIcon is not a QWidget subclass.
The most simple solution is to use a QToolButton and set the toolButtonStyle correctly:
self.someButton = QtWidgets.QToolButton()
# ...
self.someButton.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
The alternative is to subclass the button, provide a customized paint method and reimplement both sizeHint() and paintEvent(); the first is to ensure that the button is able to resize itself whenever required, while the second is to paint the button control (without text!) and then paint both the icon and the text.
Here's a possible implementation:
from PyQt5 import QtCore, QtGui, QtWidgets
class CustomButton(QtWidgets.QPushButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._icon = self.icon()
if not self._icon.isNull():
super().setIcon(QtGui.QIcon())
def sizeHint(self):
hint = super().sizeHint()
if not self.text() or self._icon.isNull():
return hint
style = self.style()
opt = QtWidgets.QStyleOptionButton()
self.initStyleOption(opt)
margin = style.pixelMetric(style.PM_ButtonMargin, opt, self)
spacing = style.pixelMetric(style.PM_LayoutVerticalSpacing, opt, self)
# get the possible rect required for the current label
labelRect = self.fontMetrics().boundingRect(
0, 0, 5000, 5000, QtCore.Qt.TextShowMnemonic, self.text())
iconHeight = self.iconSize().height()
height = iconHeight + spacing + labelRect.height() + margin * 2
if height > hint.height():
hint.setHeight(height)
return hint
def setIcon(self, icon):
# setting an icon might change the horizontal hint, so we need to use a
# "local" reference for the actual icon and go on by letting Qt to *think*
# that it doesn't have an icon;
if icon == self._icon:
return
self._icon = icon
self.updateGeometry()
def paintEvent(self, event):
if self._icon.isNull() or not self.text():
super().paintEvent(event)
return
opt = QtWidgets.QStyleOptionButton()
self.initStyleOption(opt)
opt.text = ''
qp = QtWidgets.QStylePainter(self)
# draw the button without any text or icon
qp.drawControl(QtWidgets.QStyle.CE_PushButton, opt)
rect = self.rect()
style = self.style()
margin = style.pixelMetric(style.PM_ButtonMargin, opt, self)
iconSize = self.iconSize()
iconRect = QtCore.QRect((rect.width() - iconSize.width()) / 2, margin,
iconSize.width(), iconSize.height())
if self.underMouse():
state = QtGui.QIcon.Active
elif self.isEnabled():
state = QtGui.QIcon.Normal
else:
state = QtGui.QIcon.Disabled
qp.drawPixmap(iconRect, self._icon.pixmap(iconSize, state))
spacing = style.pixelMetric(style.PM_LayoutVerticalSpacing, opt, self)
labelRect = QtCore.QRect(rect)
labelRect.setTop(iconRect.bottom() + spacing)
qp.drawText(labelRect,
QtCore.Qt.TextShowMnemonic|QtCore.Qt.AlignHCenter|QtCore.Qt.AlignTop,
self.text())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = CustomButton('Alles anzeigen', icon=QtGui.QIcon.fromTheme('document-new'))
w.setIconSize(QtCore.QSize(32, 32))
w.show()
sys.exit(app.exec_())
Alternatively, try it:
import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import (QApplication, QWidget, QGridLayout,
QToolBar, QAction)
class Widget(QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
add_action = QAction(QIcon("img/add.png"), "Add", self)
add_action.triggered.connect(self.addValue)
sub_action = QAction(QIcon("img/min.png"), "Sub", self)
sub_action.triggered.connect(self.subValue)
toolbar = QToolBar()
toolbar.setContentsMargins(0, 0, 0, 0)
toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon | Qt.AlignLeading)
toolbar.setIconSize(QSize(50, 50))
toolbar.addAction(add_action)
toolbar.addAction(sub_action)
rootGrid = QGridLayout(self)
rootGrid.addWidget(toolbar)
def addValue(self):
print("def addValue:")
def subValue(self):
print("def subValue:")
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Widget()
main.show()
sys.exit(app.exec_())
I am using PySide2 and I cant find any documentation on how to use the paint() function in a QStyledItemDelegate subclass. I am rather new to classes but is so far understandable but having trouble with PySide2.
I would like to replace my QtWidgets.QListWidgetItem with my own ListWidgetItem and display them correctly, like this:
So on the left of the ListWidgetItem an icon a bit to the right the name of the ListWidgetItem and underneath the description.
Here is the code:
from PySide2 import QtWidgets, QtCore, QtGui
from PySide2.QtGui import *
import sys
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__()
self.setWindowTitle('Test Window')
self.setStyleSheet("background-color: rgb(65, 65, 65);")
mainWidget = QtWidgets.QWidget(self)
self.setCentralWidget(mainWidget)
self.boxLayout = QtWidgets.QVBoxLayout()
mainWidget.setLayout(self.boxLayout)
# Add Widgets
self.textField = QtWidgets.QLineEdit()
self.listView = QtWidgets.QListWidget()
self.textField.textChanged.connect(self.onTextChanged)
self.boxLayout.addWidget(self.textField)
self.boxLayout.addWidget(self.listView)
self.textField.setFocus()
def onTextChanged(self, ):
titles = ['Monkey', 'Giraffe', 'Dragon', 'Bull']
descriptions = ['Almost a homo sapiens sapiens', 'I am a Giraffe!', 'Can fly and is hot on spices', 'Horny...']
if self.textField.text() == '' or self.textField.text().isspace() or self.textField.text() == ' ':
if self.listView.count() > 0:
self.listView.clear()
else:
if self.listView.count() > 0:
self.listView.clear()
for x in range(len(titles)):
if self.textField.text() in titles[x]:
item = ListWidgetItem(titles[x])
self.listView.addItem(item)
self.listView.setCurrentRow(0)
continue
class ListWidgetItem(QtWidgets.QListWidgetItem):
def __init__(self, title = '', description = '', icon = QtGui.QIcon()):
super(ListWidgetItem, self).__init__()
self.title = title
self.description = description
self.icon = icon
class ListViewStyle(QtWidgets.QStyledItemDelegate):
def __init__(self, parent, itemModel):
super(ListViewStyle, self).__init__(parent)
self.itemModel = itemModel
def sizeHint(self, option, index):
if index:
return QtCore.QSize(40, 40)
def paint(self, painter, option, index):
super(ListViewStyle, self).paint(painter, option, index)
if __name__ == '__main__':
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
#sys.exit(app.exec_())
Info: In onTextChanged() the ListWidgetItem will be added to the QListWidget but not drawn correctly, basically empty.
Does QListWidgetItem have any notable difference to QListView?
The delegate only paints and is not interested in what element provides the information since that class uses the QModelIndex and the same model to obtain the information, so in my previous solution I used a QStandardItemModel that uses QStandardItem and in your current case a QListWidget with QListWidgetItem is indifferent. My delegate expects only that the information of the title, description and icon are related to TitleRole, DescriptionRole and IconRole, respectively.
On the other hand it is not good to delete the items but it is better to hide or make them visible when necessary.
Considering the above, the solution with QListWidget is as follows:
import sys
from PySide2 import QtWidgets, QtCore, QtGui
TitleRole = QtCore.Qt.UserRole + 1000
DescriptionRole = QtCore.Qt.UserRole + 1001
IconRole = QtCore.Qt.UserRole + 1002
class ListWidgetItem(QtWidgets.QListWidgetItem):
def __init__(self, title="", description="", icon=QtGui.QIcon()):
super(ListWidgetItem, self).__init__()
self.title = title
self.description = description
self.icon = icon
#property
def title(self):
return self.data(TitleRole)
#title.setter
def title(self, title):
self.setData(TitleRole, title)
#property
def description(self):
return self.data(DescriptionRole)
#description.setter
def description(self, description):
self.setData(DescriptionRole, description)
#property
def icon(self):
return self.data(IconRole)
#icon.setter
def icon(self, icon):
self.setData(IconRole, icon)
class StyledItemDelegate(QtWidgets.QStyledItemDelegate):
def sizeHint(self, option, index):
return QtCore.QSize(50, 50)
def paint(self, painter, option, index):
super(StyledItemDelegate, self).paint(painter, option, index)
title = index.data(TitleRole)
description = index.data(DescriptionRole)
icon = index.data(IconRole)
mode = QtGui.QIcon.Normal
if not (option.state & QtWidgets.QStyle.State_Enabled):
mode = QtGui.QIcon.Disabled
elif option.state & QtWidgets.QStyle.State_Selected:
mode = QtGui.QIcon.Selected
state = (
QtGui.QIcon.On
if option.state & QtWidgets.QStyle.State_Open
else QtGui.QIcon.Off
)
iconRect = QtCore.QRect(option.rect)
iconRect.setSize(QtCore.QSize(40, 40))
icon.paint(
painter, iconRect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, mode, state
)
titleFont = QtGui.QFont(option.font)
titleFont.setPixelSize(20)
fm = QtGui.QFontMetrics(titleFont)
titleRect = QtCore.QRect(option.rect)
titleRect.setLeft(iconRect.right())
titleRect.setHeight(fm.height())
color = (
option.palette.color(QtGui.QPalette.BrightText)
if option.state & QtWidgets.QStyle.State_Selected
else option.palette.color(QtGui.QPalette.WindowText)
)
painter.save()
painter.setFont(titleFont)
pen = painter.pen()
pen.setColor(color)
painter.setPen(pen)
painter.drawText(titleRect, title)
painter.restore()
descriptionFont = QtGui.QFont(option.font)
descriptionFont.setPixelSize(15)
fm = QtGui.QFontMetrics(descriptionFont)
descriptionRect = QtCore.QRect(option.rect)
descriptionRect.setTopLeft(titleRect.bottomLeft())
descriptionRect.setHeight(fm.height())
painter.save()
painter.setFont(descriptionFont)
pen = painter.pen()
pen.setColor(color)
painter.setPen(pen)
painter.drawText(
descriptionRect,
fm.elidedText(description, QtCore.Qt.ElideRight, descriptionRect.width()),
)
painter.restore()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__()
self.setWindowTitle("Test Window")
self.setStyleSheet("background-color: rgb(65, 65, 65);")
mainWidget = QtWidgets.QWidget(self)
self.setCentralWidget(mainWidget)
self.boxLayout = QtWidgets.QVBoxLayout()
mainWidget.setLayout(self.boxLayout)
# Add Widgets
self.textField = QtWidgets.QLineEdit()
self.listView = QtWidgets.QListWidget()
self.textField.textChanged.connect(self.onTextChanged)
self.boxLayout.addWidget(self.textField)
self.boxLayout.addWidget(self.listView)
self.fill_model()
self.textField.setFocus()
self.listView.setItemDelegate(StyledItemDelegate(self))
def fill_model(self):
titles = ["Monkey", "Giraffe", "Dragon", "Bull"]
descriptions = [
"Almost a homo sapiens sapiens",
"I am a Giraffe!",
"Can fly and is hot on spices",
"Horny...",
]
for title, description in zip(titles, descriptions):
it = ListWidgetItem(title=title, description=description)
self.listView.addItem(it)
#QtCore.Slot(str)
def onTextChanged(self, text):
text = text.strip()
if text:
for i in range(self.listView.count()):
it = self.listView.item(i)
if it is not None:
it.setHidden(text.lower() not in it.title.lower())
else:
for i in range(self.listView.count()):
it = self.listView.item(i)
if it is not None:
it.setHidden(False)
if __name__ == "__main__":
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Widget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
hlay = QHBoxLayout(self)
self.listview = QListView()
self.listview2 = QListView()
hlay.addWidget(self.listview)
hlay.addWidget(self.listview2)
path = r'C:\Users\Desktop\Project'
self.fileModel = QFileSystemModel()
self.fileModel.setFilter(QDir.NoDotAndDotDot | QDir.Files)
self.listview.setRootIndex(self.fileModel.index(path))
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I want to display the files in my listview from my folder with path described in the code and able to
select them, the files I selected will be displayed in my listview2, However, the listview doesn't show
the files in this path. Can anyone help me with it?
The files are not displayed because you have not set a rootPath in the QFileSystemModel.
On the other hand the second QListView must have a model where items are added or removed as they are selected or deselected, for this you must use the selectionChanged signal of selectionModel() of the first QListView, that signal transports the information of the selected and deselected items.
To change the color you must obtain the QStandardItem and use the setData() method with the Qt::BackgroundRole role. In the example in each second the color is changed randomly
import sys
import random
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super(Widget, self).__init__(*args, **kwargs)
self.listview = QtWidgets.QListView()
self.listview2 = QtWidgets.QListView()
path = r'C:\Users\Desktop\Project'
self.fileModel = QtWidgets.QFileSystemModel(self)
self.fileModel.setRootPath(path)
self.fileModel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Files)
self.listview.setModel(self.fileModel)
self.listview.setRootIndex(self.fileModel.index(path))
self.listview.selectionModel().selectionChanged.connect(self.on_selectionChanged)
self.listview.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.model = QtGui.QStandardItemModel(self)
self.listview2.setModel(self.model)
hlay = QtWidgets.QHBoxLayout(self)
hlay.addWidget(self.listview)
hlay.addWidget(self.listview2)
timer = QtCore.QTimer(self, interval=1000, timeout=self.test_color)
timer.start()
def on_selectionChanged(self, selected, deselected):
roles = (QtCore.Qt.DisplayRole,
QtWidgets.QFileSystemModel.FilePathRole,
QtWidgets.QFileSystemModel.FileNameRole,
QtCore.Qt.DecorationRole)
for ix in selected.indexes():
it = QtGui.QStandardItem(ix.data())
for role in roles:
it.setData(ix.data(role), role)
it.setData(QtGui.QColor("green"), QtCore.Qt.BackgroundRole)
self.model.appendRow(it)
filter_role = QtWidgets.QFileSystemModel.FilePathRole
for ix in deselected.indexes():
for index in self.model.match(ix.parent(), filter_role, ix.data(filter_role), -1, QtCore.Qt.MatchExactly):
self.model.removeRow(index.row())
def test_color(self):
if self.model.rowCount() > 0:
n_e = random.randint(0, self.model.rowCount())
rows_red = random.sample(range(self.model.rowCount()), n_e)
for row in range(self.model.rowCount()):
it = self.model.item(row)
if row in rows_red:
it.setData(QtGui.QColor("red"), QtCore.Qt.BackgroundRole)
else:
it.setData(QtGui.QColor("green"), QtCore.Qt.BackgroundRole)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())