开发者

Simple hierarchical listing in PyQT

I'm trying to create a UI with 3 columns - when you select an item in the left column, the select item is passed to a function, and it returns the items for the middle column (and of course same for selecting items in the middle column)

Should be simple, but I can't find any easy way to do this..

I first tried QColumnView, as it seemed perfect.. however implementing the QAbstractItemModel seems excessively complicated, and I couldn't find any useful examples of this.

Next, since there is a fixed number of levels, I made three QListView, and modified this example QAbstractListModel

..however there seemed to be no useful signal I could use to trigger the updating of the other levels of the hierarchy. According to the PyQT docs, QListView only has the "indexesMoved" signal. There is also "clicked" and "pressed", however these didn't trigger when changing items with the k开发者_C百科eyboard

The last options is QListWidget, which would work as it has the required signals (itemChanged etc), but the way list items are created is a bit tedious (making QListWidgetItem with the parent set to the QListWidget instance)

Edit: QListWidget does pretty much what I need:

self.first_column = QListWidget()
self.first_column.itemSelectionChanged.connect(self.update_second_column)


Any QAbastractItemView has a QItemSelectionModel accessible via the selectionModel method. The QItemSelectionModel has signals that may help you:

currentChanged ( const QModelIndex & current, const QModelIndex & previous )
currentColumnChanged ( const QModelIndex & current, const QModelIndex & previous )
currentRowChanged ( const QModelIndex & current, const QModelIndex & previous )
selectionChanged ( const QItemSelection & selected, const QItemSelection & deselected )

Hope it helps.


QListView inherits from QAbstractItemView (I think you knew this), so it gets a few signals, hopefully one (or a few) of them works for you.

This is working for me (connect signals when initializing your QMainWindow or main QWidget, as in the SaltyCrane example):

connect(your_list_view, SIGNAL("clicked(const QModelIndex&)"), handler_slot)

...

def handler_slot(idx):
    #idx is a QModelIndex
    #QModelIndex.data() returns a QVariant, Qt4's generic data container
    celldata = idx.data()
    #Choose the proper datatype to ask the QVariant for (e.g. QVariant.toInt())
    actualvalue = celldata.toInt()
    #QVariant.toInt() happens to return a tuple
    print actualvalue[0]

Depending on the type of data in your model, you'll want to choose the right data type to ask QVariant for.

The sneaky part here is getting the QListView to tell you which cell was clicked (i.e. using clicked(const QModelIndex&) vs clicked()). I think I spent some time looking at the C++ documentation for Qt4 before I realized you could get more out of the signals.

From here, I would have the handler_slot() call a setData() method on the model for your second QListView (using data generated by the function you originally planned to use).

I'd be glad to elaborate if I haven't quite answered your question.

Edit: Working with arrow keys

Hmm, it seems weird that there isn't a default QListView signal for arrow movement, but we can make our own.

(This almost seems out-of-style for Qt4's signals and slots modus operandi)

QListView reimplements a method keyPressEvent(self, QKeyEvent) which acts as a callback function when certain keys are pressed. You can read more. We can use this to grab the keyevent(s) that we want, and emit our own signal.

class ArrowableListView(QListView):
    def keyPressEvent(self, keyevent):
        #Maintain original functionality by calling QListView's version
        QListView.keyPressEvent(self, keyevent)

        #QListView.selectedIndexes returns a list of selected cells, I'm just taking the first one here
        idx = self.selectedIndexes()[0]

        #We'll emit a signal that you can connect to your custom handler
        self.emit(SIGNAL("my_signal"), idx)

Once a keypress occurs, we ask the QListView what the selected indices (!) are. Of course, you can choose to filter out certain keys and choose whether to handle multiple selected cells (I think you can set a selection mode where this isn't possible, see QListView documentation).

Now that we have the selected indices (list of QModelIndex), we pass the first one (a QModelIndex) along with our signal. Because we're defining this signal in python, we don't have to set a function prototype; I have no idea if this is bad style.

Now all you have to do is connect the signal to a handler:

self.connect(your_list_view, SIGNAL("my_signal"), handler_slot)

and write your handler.

I hope this isn't too nasty of a workaround.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜