ashground
ashground

Reputation: 269

How do I change the colour of a specific branch within QTreeView?

I have a QTreeView, and I've figured out how to set its style by using setStyleSheet in my main class:

self.view.setStyleSheet("""
    QTreeView::item {
        margin: 2px;
    }
""")

That will style the entire QTreeView. But I want certain items in the tree to be bolded. When I create the branches (using [the parent widget].appendRow("the name of the item")), is there a way to 'tag' or isolate specific items so it can be styled the same way? I think the answer has something to do with the 'AccessibleName' or 'ObjectName' properties, but I'm having trouble finding documentation on it.

Update: This is what I have so far:

#!/usr/bin/python
# -*- coding: utf-8 -*-

from __future__ import division
from __future__ import print_function
from future_builtins import *

import os, sys
from PySide.QtCore import *
from PySide.QtGui import *

path_to_media = '/Volumes/show/vfx/webisodes/%d1%/seq/%d2%/%d3%/renders/2d/comp/'

class FileTree(QTreeView):
    """Main file tree element"""
    def __init__(self):
        QTreeView.__init__(self)
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Space or event.key() == Qt.Key_Return:
            index = self.selectedIndexes()[0]
            crawler = index.model().itemFromIndex(index)
            if crawler.uri:
                print("launching", crawler.uri)
                p = os.popen(('open -a "RV.app" "'+ crawler.uri) +'"', "r")
        QTreeView.keyPressEvent(self, event)

class Branch(QStandardItem):
    """Branch element"""
    def __init__(self, label, uri = None, tag = None):
        QStandardItem.__init__(self, label)
        self.uri = uri

class AppForm(QMainWindow):
    def __init__(self, parent = None):
        super(AppForm, self).__init__(parent)
        self.model = QStandardItemModel()
        self.view = FileTree()
        self.view.setStyleSheet("""
            QTreeView::item {
                margin: 2px;
            }
        """)
        self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.view.setModel(self.model)
        self.setCentralWidget(self.view)
        self.Grow()
        # self.view.setSortingEnabled(True)

    def Grow(self):
        """Populates FileTree (using BuildBranch())"""
        global path_to_media
        self.path = {}
        self.path['source'] = path_to_media
        self.path['parts'] = []
        self.path['depth'] = 0
        self.path['crawl'] = {}
        for i in self.path['source'].split('%'):
            if i[0] == "d" and i[1].isdigit():
                self.path['depth'] += 1
            else:
                self.path['parts'].append(i)
        self.BuildBranch(self.path['parts'], self.path['depth'], parentWidget = self.model.invisibleRootItem())

    def BuildBranch(self, parts, depth, uri = '', count = 0, parent = '', parentWidget = ''):
        """Recursively crawls folder structure and adds appropriate branches"""
        if not uri: uri = parts[0]
        else: uri += parent + parts[count]
        try:
            if os.listdir(uri):
                for i in os.listdir(uri):
                    if i[0] != '.':
                        if count != depth:
                            if os.path.isdir(uri):
                                thisWidget = Branch(i)
                                parentWidget.appendRow(thisWidget)
                                self.BuildBranch(parts, depth, uri, count + 1, i, parentWidget = thisWidget)
                            else:
                                thisWidget = Branch(i)
                                parentWidget.appendRow(thisWidget)
                        elif count == depth:
                            thisWidget = Branch(i, uri + i, 'media')
                            parentWidget.appendRow(thisWidget)
            else:
                print("nothing here; nuking " + parent)
                # Need to add code to nuke unused branch
        except OSError:
            print("Folder structure error... nuking the branch")
            # Need to add code to nuke unused branch

def main():
    app = QApplication(sys.argv)
    form = AppForm()
    form.resize(800, 600)
    form.setWindowTitle('Qt Dailies')
    form.show()
    app.exec_()

if __name__ == "__main__":
    main()

Update 2: Okay, I modified my Branch class so that if 'bold' is passed to it, it makes the branch bold (in theory)...

class Branch(QStandardItem):
    def __init__(self, label, uri = None, tag = None):
        QStandardItem.__init__(self, label)
        self.uri = uri
        if tag == 'bold':
            self.setData(self.createBoldFont(), Qt.FontRole)
    def createBoldFont(self):
        if self.font: return self.font
        self.font = QFont()
        self.font.setWeight(QFont.Bold)
        return self.font

... but while the code runs, nothing seems to happen. What am I still not getting?

Upvotes: 1

Views: 1804

Answers (2)

Kaleb Pederson
Kaleb Pederson

Reputation: 46479

Qt's model-view architecture allows for data that describes the different roles being performed. For example, there is a role for editing data, displaying data, etc. You're interested in the font role (i.e. Qt::FontRole) as the font has a weight enum of which bold is one value.

When you build your branches you first need to identify which items should be bolded. I'll assume you have a method like such that can identify whether or not they should be bold:

def should_be_bolded(self, item):
    return 1 # your condition checks

Now just set the weight on the font and set the font role of the item using its setData method:

def BuildBranch(...):
    thisWidget = Branch(i)
    if self.should_be_bolded(thisWidget):
        thisWidget.setData(self.createBoldFont(), Qt.FontRole)

def createFont(self):
    if self.font: return self.font
    self.font = QFont()
    self.font.setWeight(QFont.Bold)
    return self.font

But wait... you already have a subclass of QtandardItem, so you can use that:

class Branch(QStandardItem):
    """Branch element"""
    def __init__(self, label, uri = None, tag = None):
        QStandardItem.__init__(self, label)
        self.uri = uri
        if self.should_be_bolded():
            self.bold_myself()

You'll have to fix the should_be_bolded and bold_myself methods, cleaning up accordingly, but hopefully you get the point.

Stephen pointed out that you can also subclass one of the QAbstractItemModels, like the QStandardItemModel you're using, and return a specific Qt.FontRole. His way makes that knowledge implicit in the model. Decide where that knowledge best belongs and place it in the most appropriate place, whether it's in the item, the tree-creation algorithm, the model, or even a view model.

Upvotes: 1

Stephen Terry
Stephen Terry

Reputation: 6279

In your model's data() method you can add code to set the font depending on the content of the item. For example, if you wanted to bold everything in one particular row,

def data(self, index, role):

    if role == QtCore.Qt.FontRole:
       if index.row() == 1:
           boldFont = QtGui.QFont()
           boldFont.setBold(True)
           return boldFont

You just need a way to retrieve the name of your branch when given an index for it. That depends on the implementation of your tree model.

The Qt Model/View tutorial has a good example, although it's in C++. Look at section 2.2 (Extending the Read Only Example with Roles).

Upvotes: 0

Related Questions