Open probonopd opened 4 months ago
Is it even possible using QListView?
Would it be possible by subclassing QListView? How?
Or would we have to go to a solution without using QListView altogether?
#!/usr/bin/env python
import sys
from PyQt6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QGraphicsTextItem
from PyQt6.QtGui import QPixmap, QColor, QFont, QPainter, QIcon, QDragEnterEvent, QDrag
from PyQt6.QtCore import Qt, QPointF, QByteArray, QDataStream, QMimeData, QIODevice
class DraggableGraphicsPixmapItem(QGraphicsPixmapItem):
def __init__(self, pixmap):
super().__init__(pixmap)
self.setFlag(QGraphicsPixmapItem.GraphicsItemFlag.ItemIsMovable, True)
self.setAcceptHoverEvents(True)
self.setCursor(Qt.CursorShape.OpenHandCursor)
def mouseMoveEvent(self, event):
if event.buttons() == Qt.MouseButton.LeftButton:
mime_data = QMimeData()
data_stream = QByteArray()
stream = QDataStream(data_stream, QIODevice.OpenModeFlag.WriteOnly)
stream << QPointF(event.pos())
mime_data.setData("application/x-dnditemdata", data_stream)
drag = QDrag(self.scene().views()[0])
drag.setMimeData(mime_data)
drag.setPixmap(self.pixmap())
drag.setHotSpot(event.pos().toPoint() - self.pos().toPoint())
drag.exec(Qt.DropAction.MoveAction)
class DraggableGraphicsTextItem(QGraphicsTextItem):
def __init__(self, text):
super().__init__(text)
self.setTextInteractionFlags(Qt.TextInteractionFlag.TextEditorInteraction)
def mouseMoveEvent(self, event):
if event.buttons() == Qt.MouseButton.LeftButton:
mime_data = QMimeData()
data_stream = QByteArray()
stream = QDataStream(data_stream, QIODevice.OpenModeFlag.WriteOnly)
stream << QPointF(event.pos())
mime_data.setData("application/x-dnditemdata", data_stream)
drag = QDrag(self.scene().views()[0])
drag.setMimeData(mime_data)
drag.setPixmap(QPixmap())
drag.setHotSpot(event.pos().toPoint() - self.pos().toPoint())
drag.exec(Qt.DropAction.MoveAction)
class IconView(QGraphicsView):
def __init__(self):
super().__init__()
# Create a QGraphicsScene
self.scene = QGraphicsScene()
self.setScene(self.scene)
# Example data: icon paths and text
icon = QIcon.fromTheme("applications-internet")
data = [{"icon": icon.pixmap(48, 48), "text": f"Item {i}"} for i in range(1, 21)]
# Add items to the scene
x = 0
y = 0
item_width = 100
item_height = 50 # Adjust item height as needed
for entry in data:
# Add pixmap (icon)
pixmap = QPixmap(entry["icon"])
pixmap_item = DraggableGraphicsPixmapItem(pixmap)
# Center pixmap horizontally
pixmap_item.setPos(x + (item_width - pixmap.width()) / 2, y)
self.scene.addItem(pixmap_item)
# Add text item below the icon
text_item = DraggableGraphicsTextItem(entry["text"])
text_item.setDefaultTextColor(QColor("black"))
text_item.setFont(QFont("Arial", 10))
# Center text horizontally under the icon
text_width = text_item.boundingRect().width()
text_height = text_item.boundingRect().height()
text_item.setPos(x + (item_width - text_width) / 2, y + item_height)
self.scene.addItem(text_item)
# Update x and y positions for the next item
x += item_width + 20 # Horizontal spacing between items
# Check if next item exceeds viewport width
if x + item_width > self.viewport().width():
# Check x to see if we are in an even or odd row
if y == item_height / 2:
x = 0
else:
x = item_width / 2 # Reset x to start a new line
y += item_height * 0.5 # Vertical spacing between items
# Set scene rect to ensure all items are visible
total_height = y + item_height + text_height
self.scene.setSceneRect(0, 0, self.viewport().width(), total_height)
# Adjust view settings
self.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setWindowTitle("Custom Icon View")
self.resize(600, 400)
# Viewport gets white background
self.viewport().setStyleSheet("background-color: white;")
if __name__ == "__main__":
app = QApplication(sys.argv)
view = IconView()
view.show()
sys.exit(app.exec())
Going down this route would be very tedious since we need to implement everything ourselves...
In icon view, when aligning to the grid, we (optionally) want every second line of items to be moved to the right by 50% of the grid.
Classic Finder used to have this:
Source: https://vintageapple.org/macbooks/pdf/The_System_7_Book_1991.pdf
This allows for much longer file names compared to the items being aligned in a normal grid, while we could reduce the vertical spacing a lot.
We can do:
However, the code above achieves this by varying the width of the items rather than their position, which is not what we want:
How can we achieve this properly? Also see