BigRoy / usd-qtpy

Python Qt components for building custom USD tools.
MIT License
59 stars 9 forks source link

Prim Hierarchy Editor: Allow listing and setting the default prim #4

Closed BigRoy closed 10 months ago

BigRoy commented 10 months ago

We should expose the functionality to set the default prim.

There maybe also be reasons to set the default prim per layer (e.g. see here) so we might want to expose that as well however being able to set the USD stage's root layer default prim would be the first goal.

Prism Editor has a similar feature which displays it nicely:

prism_show_ref_var_dft

We might want to look into something similar.


Prism implements it as:

The scenegraph is a QTreeWidget with a QStyledItemDelegate assigned. The colored labels are drawn in the paintEvent of the QStyledItemDelegate using some QRect, QFont and QPen objects. The eye is a QToolButton, which was added using the setItemWidget on the QTreeWidgetItem object.

Responding to clicks on those labels like:

... the Reference Editor gets opened from a mouseClickEvent function on the Scenegraph widget, which checks if the mouseclick happened on the "REF" rect of the item under the cursor.

BigRoy commented 10 months ago

Here's a quick prototype mimicking somewhat what Prism does with a styled delegate:

from qtpy import QtCore, QtGui, QtWidgets

class Delegate(QtWidgets.QStyledItemDelegate):
    """Draws rounded rects 'tags' to the right hand side of the widget.

    The tags to be drawn should be returned by index's data via the
    BlockTagsRole on this class. The returned data should be a list
    with dicts defining each tag:
        {
            "text": "text",                  # text value in the block
            "background-color": "#FFFFFF",   # background color
            "color": "#FF9999"               # text color
        } 
    """

    RectDataRole = QtCore.Qt.UserRole + 1001

    def paint(self, painter, option, index):  

        super(Delegate, self).paint(painter, option, index)

        if index.column() != 0:
            return
        # For the first column we want to paint in the state
        # of default prim, variants and references/payloads
        rect = QtCore.QRect(option.rect)

        corner_radius = 5
        padding_topbottom = 2
        padding_sides = 4
        width = 30
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        blocks = [
            {"text": "REF", "background-color": "#333355"},
            {"text": "VAR", "background-color": "#335533"},
            {"text": "DFT", "background-color": "#553333"},

        ]

        block_rects = []
        for i, block_data in enumerate(blocks):
            text = block_data.get("text", "")
            background_color = QtGui.QColor(block_data.get("background-color", "#FF9999"))
            text_color = QtGui.QColor(block_data.get("color", "#FFFFFF"))
            painter.setPen(text_color)

            # Calculate left by computing offset from right hand side (align right)
            i = i + 1
            right = rect.right()
            left = right - (width * i) - (2 * i * padding_sides) + padding_sides
            block_rect = QtCore.QRect(left, rect.top() + padding_topbottom, width, rect.height() - padding_topbottom * 2)

            # Draw the block rect
            path = QtGui.QPainterPath()
            path.addRoundedRect(block_rect, corner_radius, corner_radius)
            painter.fillPath(path, background_color)

            # Draw text in the block - vertically centered
            point = block_rect.topLeft()
            point.setY(point.y() + block_rect.height() * 0.5)

            painter.drawText(block_rect, QtCore.Qt.AlignCenter, text)
            block_rects.append(block_rect)

        # This is a bit of a hack, we store the created QRect on the
        # item so that on the view we can respond to clicks on these blocks
        # easily
        model = index.model()
        model.setData(index, block_rects, self.RectDataRole)

class View(QtWidgets.QListView):
    def mousePressEvent(self, event):
        point = event.position().toPoint()
        index = self.indexAt(point)
        rects = index.data(Delegate.RectDataRole) or []
        for rect in rects:
            if rect.contains(point):
                print(rect)

delegate = Delegate()

view = View()
model = QtGui.QStandardItemModel()
for value in ["Cube", "Sphere", "Cone"]:
    item = QtGui.QStandardItem()
    item.setText(value)
    model.appendRow(item)
view.setModel(model)
view.setItemDelegate(delegate)
view.show()
view.setStyleSheet("QListView::item { padding: 5px; }") 

Which in Maya generates this: image

Where the little labels are clickable to report that they were clicked. I guess even if a bit of a hack we could start by using something along these lines.