Customising the Spreadsheet

The functionality of the Spreadsheet View can be extended by registering a custom column class via Python.

Creating custom columns allows you to display extra information in the Spreadsheet view, e.g. notes/metadata in Tags, or certain properties of a Clip which are not displayed by default.

You can also customise the appearance of cells, e.g. the colour, size, font or icon displayed.

Text data stored in your custom cells is also searchable in the Spreadsheet’s search filter box.

Registering Custom Columns

To create your custom columns, you need to create a custom column class, which inherits from a ‘new style’ python object class.

You then inject your custom column class (e.g. MyCustomColumns) into the hiero.ui.customColumn module, like so:

hiero.ui.customColumn = MyCustomColumns()

Note

hiero.ui.customColumn is a specially reserved module location that Hiero looks for when creating custom Spreadsheet columns. It can only contain a single custom column class.

Once you have registered your custom columns, you can right-click in the Spreadsheet View, to select which columns you would like to display.

An example of creating custom spreadsheet columns can be found here, or by running the code below:

# Shows how to add custom columns to the Spreadsheet view
import hiero.ui
from PySide2 import (QtCore, QtWidgets, QtGui)

_itemData = dict() # Just for example purposes. In real life, you'd store this in the model

class CustomSpreadsheetColumns(QtCore.QObject):
  """
    A class defining custom columns for Hiero's spreadsheet view. This has a similar, but
    slightly simplified, interface to the QAbstractItemModel and QItemDelegate classes.
  """

  def numColumns(self):
    """
      Return the number of custom columns in the spreadsheet view
    """
    return 3

  def columnName(self, column):
    """
      Return the name of a custom column
    """
    if column == 0:
      return "Python Tags"
    if column == 1:
      return "Python Name"
    if column == 2:
      return "Python Row"
    return ""

  def getData(self, row, column, item):
    """
      Return the data in a cell
    """
    if column == 0:
      return ""
    if column == 1:
      if item in _itemData:
        return str(_itemData[item])
      return item.name()
    if column == 2:
      return str(row+1)
    return None

  def setData(self, row, column, item, data):
    """
      Set the data in a cell
    """
    print( "setData", (self, row, column, item, data) )
    _itemData[item] = str(data)

  def getTooltip(self, row, column, item):
    """
      Return the tooltip for a cell
    """
    return "Tooltip: "+str(row)+"/"+str(column)+": "+item.name()

  def getFont(self, row, column, item):
    """
      Return the tooltip for a cell
    """
    if column == 1:
      return QtGui.QFont("Courier", 24)
    return None

  def getBackground(self, row, column, item):
    """
      Return the background colour for a cell
    """
    if column == 1:
      return QtGui.QColor(64, 255, 64)
    return None

  def getForeground(self, row, column, item):
    """
      Return the text colour for a cell
    """
    if column == 1:
      return QtGui.QColor(255, 64, 64)
    return None

  def getIcon(self, row, column, item):
    """
      Return the icon for a cell
    """
    if column == 1:
      return QtGui.QIcon("icons:Add.png")
    return None

  def getSizeHint(self, row, column, item):
    """
      Return the size hint for a cell
    """
    if column == 0:
      return QtCore.QSize(300, 32)
    return None

  def paintCell(self, row, column, item, painter, option):
    """
      Paint a custom cell. Return True if the cell was painted, or False to continue
      with the default cell painting.
    """
    if column == 0:
      if option.state & QtWidgets.QStyle.State_Selected:
        painter.fillRect(option.rect, option.palette.highlight())
      iconSize = 20
      r = QtCore.QRect(option.rect.x(), option.rect.y()+(option.rect.height()-iconSize)/2, iconSize, iconSize)
      tags = item.tags()
      if len(tags) > 0:
        painter.save()
        painter.setClipRect(option.rect)
        for tag in item.tags():
          QtGui.QIcon(tag.icon()).paint(painter, r, QtCore.Qt.AlignLeft)
          r.translate(r.width()+2, 0)
        painter.restore()
        return True
    return False

  def createEditor(self, row, column, item, view):
    """
      Create an editing widget for a custom cell
    """
    tags = item.tags()
    if column == 0 and len(tags) > 0:
      cb = QtWidgets.QComboBox()
      for tag in tags:
        cb.addItem(QtGui.QIcon(tag.icon()), tag.name())
      cb.currentIndexChanged.connect(self.indexChanged);
      print( "createEditor:", (self, row, column, item, view) )
      return cb
    return None

  def setEditorData(self, row, column, item, editor):
    """
      Update the custom editor from the model data. Return True if this was done.
    """
    print( "setEditorData:", (self, row, column, item, editor) )
    if column == 1:
      try:
        print( " - setEditorData:", _itemData[item] )
        text = str(_itemData[item])
        editor.setText(text)
        return True
      except:
        print( "No existing data for item" )
    return False

  def setModelData(self, row, column, item, editor):
    """
      Update the model data from the custom editor. Return True if this was done.
    """
    print( "setModelData:", (self, row, column, item, editor) )
    if column == 1:
      _itemData[item] = str(editor.text())
      print( " - setModelData:", _itemData[item] )
      return True
    return False

  def dropMimeData(self, row, column, item, data, items):
    """
      Handle a drag and drop operation
    """
    print( "dropMimeData", (self, row, column, item, data.formats(), items) )
    return None


  def indexChanged(self, index):
    """
      This method is called when our custom widget changes index.
    """
    print( "ComboBox index changed:", index )

# Register our custom columns
hiero.ui.customColumn = CustomSpreadsheetColumns()