16. 模型/视图编程

16.1 模型/视图架构

16.1.1 组成部分

1). 模型

2). 视图

3). 委托

16.1.2 简单的例子

#!/usr/bin/env python
# encoding: utf-8

"""
@version : 
@author  : 
@license : 
@contact : ****@massclouds.com
@site    : http://blog.csdn.net/***
@software: PyCharm
@time    : 17-1-5 下午5:19
"""

from PyQt4.QtGui import QApplication,QTreeView,QFileSystemModel, QListView
from PyQt4.QtCore import QDir
import sys


if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 1. 创建 模型 model  */
    model = QFileSystemModel()            # 创建文件系统模型
    model.setRootPath(QDir.currentPath()) # 指定要监视的目录

    # 2. 创建 视图 view  */
    tree = QTreeView()                                 # 创建树型视图
    tree.setModel(model)                               # 为视图指定模型
    tree.setRootIndex(model.index(QDir.currentPath() ))# 指定根索引

    listw = QListView()                                # 创建列表视图
    listw.setModel(model)                              # 为视图指定模型
    listw.setRootIndex(model.index(QDir.currentPath()))# 指定根索引

    tree.setWindowTitle("QTreeView")
    tree.show()
    listw.setWindowTitle("QListView")
    listw.show()

    app.exec_()

16.1.3 小试牛刀

1). QCompleter自动补全

#!/usr/bin/env python
#coding=utf-8

'''
FileName: main.py
'''

import sys
from PyQt4.QtCore import QString, SIGNAL,QStringList,Qt
from PyQt4.QtGui import QApplication, QLineEdit ,QCompleter,\
        QDirModel, QStringListModel

# QCompleter补全文件路径
class FilePath(QLineEdit):

    def __init__(self):
        super(FilePath, self).__init__(None)

        self.setWindowTitle("FilePath")
        completer = QCompleter(self)
        dir_model = QDirModel()
        dir_model.setParent(self)
        completer.setModel(dir_model)
        self.setCompleter(completer)

# QCompleter补全单词
class  CWord(QLineEdit):

    def __init__(self):
        super(CWord, self).__init__(None)

        self.setWindowTitle("CWord")

        word_list = QStringList()
        word_list<<"Java"<<"C++"<<"C#"<<"PHP"<<"Perl"<<"Python"<<"Delphi"<<"Ruby"
        completer2 = QCompleter(word_list, self)
        completer2.setCaseSensitivity(Qt.CaseInsensitive)
        self.setCompleter(completer2)

# QCompleter添加新单词
class AddNewWord(QLineEdit):

    def __init__(self):
        super(AddNewWord, self).__init__(None)

        self.setWindowTitle("AddNewWord")

        completer = QCompleter(self)
        self.string_list_model = QStringListModel(self)
        completer.setCaseSensitivity(Qt.CaseInsensitive)
        completer.setModel(self.string_list_model)
        self.setCompleter(completer)
        self.connect(self, SIGNAL("editingFinished()"), self.editComplete)

        self.word_list = QStringList()

    def editComplete(self):
        text = self.text()
        if QString.compare(text, QString("")) != 0:

            is_contains = self.word_list.contains(text, Qt.CaseInsensitive)
            if not is_contains:
                self.word_list<<text
                self.string_list_model.setStringList(self.word_list)

if __name__ == "__main__":

    app = QApplication(sys.argv)

    # QCompleter补全文件路径
    win_fpath = FilePath()
    win_fpath.show()

    # QCompleter补全单词
    cword = CWord()
    cword.show()

    # QCompleter添加新单词
    add_world = AddNewWord()
    add_world.show()

    app.exec_()

常用的方法

  • void setMaxVisibleItems(int maxItems) 设置最大显示数目
  • void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity) 设置是否区分大小写
  value
Qt::CaseInsensitive 0 (默认) 大小写不敏感
Qt::CaseSensitive 1 大小写敏感
  • void setModelSorting(ModelSorting sorting) 设置排序方式

    QCompleter::ModelSorting取值如下:

      value  
    QCompleter::UnsortedModel 0 该模型是未排序
    QCompleter::CaseSensitivelySortedModel 1 该模型是大小写敏感排序
    QCompleter::CaseInsensitivelySortedModel 2 该模型是大小写不敏感排序的

3). 自动完成的QLineEdit(非使用QCompleter版)

#!/usr/bin/env python
# coding=utf-8

'''
FileName: main.py
'''

from PyQt4.QtGui import QLineEdit, QListView, QStringListModel, QFocusEvent
from PyQt4.QtCore import Qt, SIGNAL, SLOT, QStringList, QString, \
    QPoint, QModelIndex, pyqtSlot


class CompleteLineEdit(QLineEdit):
    def __init__(self, words):
        super(CompleteLineEdit, self).__init__(None)

        self.words = words                # QStringList  整个完成列表的单词
        self.listView = QListView(self)
        self.model = QStringListModel(self)
        self.listView.setWindowFlags(Qt.ToolTip)

        self.connect(self, SIGNAL("textChanged(const QString &)"),
                     self, SLOT("setCompleter(const QString &)"))

        self.connect(self.listView, SIGNAL("clicked(const QModelIndex &)"),
                     self, SLOT("completeText(const QModelIndex &)"))

    def focusOutEvent(self, focus_event):
        # self.listView.hide()
        pass

    @pyqtSlot("QKeyEvent")
    def keyPressEvent(self, e):
        if not self.listView.isHidden():
            key = e.key()
            count = self.listView.model().rowCount()
            currentIndex = self.listView.currentIndex()

            if Qt.Key_Down == key:
                # 按向下方向键时,移动光标选中下一个完成列表中的项
                row = currentIndex.row() + 1
                if (row >= count):
                    row = 0

                index = self.listView.model().index(row, 0)
                self.listView.setCurrentIndex(index)
            elif Qt.Key_Up == key:
                # 按向下方向键时,移动光标选中上一个完成列表中的项
                row = currentIndex.row() - 1
                if (row < 0):
                    row = count - 1

                index = self.listView.model().index(row, 0)
                self.listView.setCurrentIndex(index)
            elif Qt.Key_Escape == key:
                # 按下Esc键时,隐藏完成列表
                self.listView.hide()
            elif Qt.Key_Enter == key or Qt.Key_Return == key:
                # 按下回车键时,使用完成列表中选中的项,并隐藏完成列表
                if (currentIndex.isValid()):
                    text = self.listView.currentIndex().data().toString()
                    self.setText(text)

                self.listView.hide()
            else:
                # 其他情况,隐藏完成列表,并使用QLineEdit的键盘按下事件
                self.listView.hide()
                QLineEdit.keyPressEvent(self,e)
        else:
            QLineEdit.keyPressEvent(self,e)

    # 动态的显示完成列表
    @pyqtSlot("QString")
    def setCompleter(self, text):

        if (text.isEmpty()):
            self.listView.hide()
            return

        if text.length() > 1 and not self.listView.isHidden():
            return

        # 如果完整的完成列表中的某个单词包含输入的文本,则加入要显示的完成列表串中
        sl = QStringList()

        for i in range(self.words.count()):
            if self.words[i].contains(text):
                sl << self.words[i]

        self.model.setStringList(sl)
        self.listView.setModel(self.model)

        if (self.model.rowCount() == 0):
            return

        # Position the text edit
        self.listView.setMinimumWidth(self.width())
        self.listView.setMaximumWidth(self.width())

        p = QPoint(0, self.height())
        x = self.mapToGlobal(p).x()
        y = self.mapToGlobal(p).y() + 1

        self.listView.move(x, y)
        self.listView.show()

    # 点击完成列表中的项,使用此项自动完成输入的单词
    @pyqtSlot("QModelIndex")
    def completeText(self, index):
        text = index.data().toString()
        self.setText(text)
        self.listView.hide()
#!/usr/bin/env python
#coding=utf-8

'''
FileName: main.py
'''

import sys
from PyQt4.QtCore import QStringList
from PyQt4.QtGui import QApplication, QWidget, QPushButton, QHBoxLayout

from completelineEdit import CompleteLineEdit

if __name__ == "__main__":

    app = QApplication(sys.argv)

    sl = QStringList() << "Biao" << "Bin" << "Huang" << "Hua" << "Hello" << "BinBin" << "Hallo"
    widgetw = QWidget()
    edit= CompleteLineEdit(sl)
    button = QPushButton("Button")
    layout = QHBoxLayout()
    layout.addWidget(edit)
    layout.addWidget(button)
    widgetw.setLayout(layout)

    widgetw.show()

    # e = CompleteLineEdit(sl)
    # e.show()

    app.exec_()

16.2 模型类

16.2.1 基本概念

#!/usr/bin/env python
#coding=utf-8

#include <QApplication>
#include <QTreeView>
#include <QDebug>
#include <QStandardItemModel>
from PyQt4.QtCore import Qt, QModelIndex

from PyQt4.QtGui import QApplication, QStandardItemModel,QStandardItem, \
    QPixmap, QIcon, QTreeView, QColor
import sys

if __name__ == "__main__":
    app = QApplication(sys.argv)
    
    view = QTreeView()

    # 1.  模型
    '''
    PyQt使用Model时,如果Model创建时未设置parent,则运行完退出时会报错:
    QObject::startTimer: QTimer can only be used with threads started with QThread

    '''
    # model = QStandardItemModel()         # 创建标准项模型
    model = QStandardItemModel(view)         # 创建标准项模型
    # 获取模型的根项(Root Item),根项是不可见的
    parentItem = model.invisibleRootItem()

    # 创建标准项item0,并设置显示文本,图标和工具提示
    item0 = QStandardItem()
    item0.setText("A")
    pixmap0 = QPixmap(50,50)
    pixmap0.fill(QColor("red"))
    # pixmap0.fill(Qt.red)
    item0.setIcon(QIcon(pixmap0))
    item0.setToolTip("indexA")

    parentItem.appendRow(item0)  # 将item0  作为根项的子项

    # 将创建的标准项作为新的父项
    parentItem = item0
    # 创建新的标准项,它将作为item0的子项
    item1 = QStandardItem()
    item1.setText("B")
    pixmap1 = QPixmap(50,50)
    pixmap1.fill(Qt.blue)
    item1.setIcon(QIcon(pixmap1))
    item1.setToolTip("indexB")
    parentItem.appendRow(item1)

    # 创建新的标准项,这里使用了另一种方法来设置文本、图标和工具提示
    item2 = QStandardItem()
    pixmap2 = QPixmap(50,50)
    pixmap2.fill(Qt.green)
    item2.setData("C", Qt.EditRole)                  # 等同于  setText("C")
    item2.setData("indexC", Qt.ToolTipRole)          # 等同于  setToolTip("indexB")
    item2.setData(QIcon(pixmap2), Qt.DecorationRole) #等同于 setIcon(QIcon(pixmap1))
    parentItem.appendRow(item2)

    # 在树视图中显示模型
    view.setModel(model)
    view.show()

    # 获取item0的索引并输出item0的子项数目,然后输出了item1的显示文本和工具提示
    indexA = model.index(0, 0, QModelIndex())
    print  "indexA row count: " , model.rowCount(indexA)
    indexB = model.index(0, 0, indexA)
    print  "indexB text: " , model.data(indexB, Qt.EditRole).toString()
    print  "indexB toolTip: " , model.data(indexB, Qt.ToolTipRole).toString()

    app.exec_()




16.2.2 创建新的模型

1.

#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtCore import QAbstractListModel, QStringList, QModelIndex, QVariant, Qt, QString


class StringListModel(QAbstractListModel):
    def __init__(self,stringList,parent = None):
        super(StringListModel, self).__init__(parent)
        self.stringList = QStringList(stringList)

    # int rowCount(const QModelIndex &parent = QModelIndex()) const;
    # QVariant data(const QModelIndex &index, int role) const;
    # QVariant headerData(int section, Qt::Orientation orientation,
    #                     int role = Qt::DisplayRole) const;

    def rowCount(self,parent=QModelIndex()):
        return self.stringList.count()

    def data(self,index,role= Qt.DisplayRole):

        if (not index.isValid()):
            return QVariant()

        if (index.row() >= self.stringList.count):
            return QVariant()

        if (role == Qt.DisplayRole):
            return self.stringList[index.row()]
        else:
            return QVariant()

    def headerData(self,section, orientation, role= Qt.DisplayRole):

        if (role != Qt.DisplayRole):
            return QVariant()

        if (orientation == Qt.Horizontal):
            return QString("Column %1").arg(section)
        else:
            return QString("Row %1").arg(section)
#!/usr/bin/env python
#coding=utf-8

#include <QApplication>

#include <QListView>
#include <QTableView>
#include <QDebug>
import sys
from PyQt4.QtCore import QStringList
from PyQt4.QtGui import QApplication, QListView, QTableView
from stringlistmodel import StringListModel

if __name__ == "__main__":

    app = QApplication(sys.argv)

    qlist = QStringList()
    qlist.append("a")
    qlist.append("b")
    qlist.append("c")
    qlist.append("abcd")

    model = StringListModel(qlist)

    qlistView = QListView()
    qlistView.setModel(model)
    qlistView.show()

    tableView = QTableView()
    tableView.setModel(model)
    tableView.show()

    app.exec_()

2.

#!/usr/bin/env python
#coding=utf-8


from PyQt4.QtCore import QAbstractListModel, QStringList, QModelIndex, QVariant, Qt, QString, QAbstractItemModel, SIGNAL


class StringListModel(QAbstractListModel):
    def __init__(self,stringList,parent = None):
        super(StringListModel, self).__init__(parent)
        self.stringList = QStringList(stringList)

    # int rowCount(const QModelIndex &parent = QModelIndex()) const;
    # QVariant data(const QModelIndex &index, int role) const;
    # QVariant headerData(int section, Qt::Orientation orientation,
    #                     int role = Qt::DisplayRole) const;
    # 编辑功能用到的两个函数
    # Qt::ItemFlags flags(const QModelIndex &index) const;
    # bool setData(const QModelIndex &index, const QVariant &value,
    #              int role = Qt::EditRole);

    def rowCount(self, parent=None, *args, **kwargs):
        return self.stringList.count()

    def data(self,index,role= Qt.DisplayRole):

        if (not index.isValid()):
            return QVariant()

        if (index.row() >= self.stringList.count):
            return QVariant()

        if (role == Qt.DisplayRole or role == Qt.EditRole):
            return self.stringList[index.row()]
        else:
            return QVariant()

    def headerData(self,section, orientation, role= Qt.DisplayRole):

        if (role != Qt.DisplayRole):
            return QVariant()

        if (orientation == Qt.Horizontal):
            return QString("Column %1").arg(section)
        else:
            return QString("Row %1").arg(section)

    # 以下是实现编辑功能添加的两个函数
    def flags(self,index):

        if (not index.isValid()):
            return Qt.ItemIsEnabled

        return super(StringListModel, self).flags(index) | Qt.ItemIsEditable

    def setData(self, index, value, role= Qt.DisplayRole):

        if (index.isValid() and role == Qt.EditRole):

            self.stringList.replace(index.row(), value.toString())
            self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"),index, index)
            return  True
        return False
#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtCore import QStringList

from PyQt4.QtGui import QApplication, QListView, QTableView
import sys
from stringlistmodel import StringListModel

if __name__ == "__main__":

    app = QApplication(sys.argv)

    qlist = QStringList()
    qlist.append("a")
    qlist.append("b")
    qlist.append("c")


    listView = QListView()
    tableView = QTableView()
    '''
    PyQt使用Model时,如果Model创建时未设置parent,则运行完退出时会报错:
    QObject::startTimer: QTimer can only be used with threads started with QThread
    '''
    # model = StringListModel(qlist)
    model = StringListModel(qlist,listView)

    listView.setModel(model)
    listView.show()

    tableView.setModel(model)
    tableView.show()

    app.exec_()

3.

#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtCore import QAbstractListModel, QStringList, QModelIndex, QVariant, Qt, QString, QAbstractItemModel, SIGNAL


class StringListModel(QAbstractListModel):
    def __init__(self,stringList,parent = None):
        super(StringListModel, self).__init__(parent)
        self.stringList = QStringList(stringList)

    # int rowCount(const QModelIndex &parent = QModelIndex()) const;
    # QVariant data(const QModelIndex &index, int role) const;
    # QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
    # // 编辑功能用到的两个函数
    # Qt::ItemFlags flags(const QModelIndex &index) const;
    # bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    #
    # // 插入和删除行用到的两个函数
    # bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
    # bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());

    def rowCount(self,parent= QModelIndex()):
        return self.stringList.count()

    def data(self,index,role= Qt.DisplayRole):

        if (not index.isValid()):
            return QVariant()

        if (index.row() >= self.stringList.count):
            return QVariant()

        if (role == Qt.DisplayRole):
            return self.stringList[index.row()]
        else:
            return QVariant()
    def headerData(self,section, orientation, role= Qt.DisplayRole):

        if (role != Qt.DisplayRole):
            return QVariant()

        if (orientation == Qt.Horizontal):
            return QString("Column %1").arg(section)
        else:
            return QString("Row %1").arg(section)

    # 以下是实现编辑功能添加的两个函数

    def flags(self,index):
        if (not index.isValid()):
            return Qt.ItemIsEnabled

        return super(StringListModel, self).flags(index) | Qt.ItemIsEditable

    def setData(self, index, value, role= Qt.DisplayRole):

        if (index.isValid() and role == Qt.EditRole):
            self.stringList.replace(index.row(), value.toString())
            self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"),index, index)
            return  True
        return False

    # 以下是插入和删除行用到的两个函数
    def insertRows(self,position, rows, parent = QModelIndex()):
        self.beginInsertRows(QModelIndex(), position, position+rows-1)

        for i in range(rows):
            self.stringList.insert(position, "")

        self.endInsertRows()
        return True

    def removeRows(self,position, rows, parent = QModelIndex()):

        self.beginRemoveRows(QModelIndex(), position, position+rows-1)

        for i in range(rows):
            self.stringList.removeAt(position)

        self.endRemoveRows()
        return True

#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtCore import QStringList

from PyQt4.QtGui import QApplication, QListView, QTableView
import sys
from stringlistmodel import StringListModel

if __name__ == "__main__":

    app = QApplication(sys.argv)

    qlist = QStringList()
    qlist.append("a")
    qlist.append("b")
    qlist.append("c")


    listView = QListView()
    tableView = QTableView()
    '''

    PyQt使用Model时,如果Model创建时未设置parent,则运行完退出时会报错:

    QObject::startTimer: QTimer can only be used with threads started with QThread

    '''
    # model = StringListModel(qlist)
    model = StringListModel(qlist,listView)

    listView.setModel(model)
    listView.show()

    tableView.setModel(model)
    tableView.show()

    # 插入删除行
    model.insertRows(3, 2)
    model.removeRows(0, 1)

    app.exec_()

16.3 视图类

16.3.1 基本概念

16.3.2 处理项目选择

#!/usr/bin/env python
#coding=utf-8
from PyQt4.QtCore import QString, QModelIndex
from PyQt4.QtGui import QTableView, QMainWindow,  \
    QStandardItemModel, QStandardItem, QItemSelection, \
    QItemSelectionModel
from PyQt4 import uic

class MainWindow(QMainWindow):

    def __init__(self,parent=None):
        super(MainWindow, self).__init__(None)
        uic.loadUi("mainwindow.ui",self)


        model = QStandardItemModel(7, 4, self)
        for row in range(7):
            for column in range(4):
                item = QStandardItem(QString("%1").arg(row * 4 + column))
                model.setItem(row, column, item)

        tableView =  QTableView()
        tableView.setModel(model)
        self.setCentralWidget(tableView)

        #获取视图的项目选择模型
        selectionModel = tableView.selectionModel()
        # 定义左上角和右下角的索引,然后使用这两个索引创建选择
        topLeft = model.index(1, 1, QModelIndex())
        bottomRight = model.index(5, 2, QModelIndex())
        selection = QItemSelection(topLeft, bottomRight)
        # 使用指定的选择模式来选择项目
        selectionModel.select(selection, QItemSelectionModel.Select)

#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtGui import QApplication
import sys
from mainwindow import MainWindow
if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exec_()

QTiemSelectionModel::Toggle

#!/usr/bin/env python
# coding=utf-8

from PyQt4.QtCore import QString, QModelIndex
from PyQt4.QtGui import QTableView, QMainWindow, \
    QStandardItemModel, QStandardItem, QItemSelection, \
    QItemSelectionModel
from PyQt4 import uic

try:
    _fromUtf8 = QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        uic.loadUi("mainwindow.ui", self)

        model = QStandardItemModel(7, 4, self)
        for row in range(7):
            for column in range(4):
                item = QStandardItem(QString("%1").arg(row * 4 + column))
                model.setItem(row, column, item)

        self.tableView = QTableView()
        self.tableView.setModel(model)
        self.setCentralWidget(self.tableView)

        # 获取视图的项目选择模型
        selectionModel = self.tableView.selectionModel()

        # 定义左上角和右下角的索引,然后使用这两个索引创建选择
        topLeft = model.index(1, 1, QModelIndex())
        bottomRight = model.index(5, 2, QModelIndex())
        selection = QItemSelection(topLeft, bottomRight)

        # 使用指定的选择模式来选择项目
        selectionModel.select(selection, QItemSelectionModel.Select)

        self.mainToolBar.addAction(_fromUtf8("当前项目"), self.getCurrentItemData)
        self.mainToolBar.addAction(_fromUtf8("切换选择"), self.toggleSelection)

    # 输出当前项目的内容
    def getCurrentItemData(self, ):

        print  "当前项目的内容:", \
            self.tableView.selectionModel().currentIndex().data().toString()

    # 切换选择的项目
    def toggleSelection(self):
        topLeft = self.tableView.model().index(0, 0, QModelIndex())
        bottomRight = self.tableView.model().index(
            self.tableView.model().rowCount(QModelIndex()) - 1,
            self.tableView.model().columnCount(QModelIndex()) - 1, QModelIndex())
        curSelection = QItemSelection(topLeft, bottomRight)
        self.tableView.selectionModel().select(curSelection, QItemSelectionModel.Toggle)
#!/usr/bin/env python

#coding=utf-8


from PyQt4.QtGui import QApplication
import sys
from mainwindow import MainWindow
if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exec_()
#!/usr/bin/env python
# coding=utf-8

from PyQt4.QtCore import QString, QModelIndex, SIGNAL
from PyQt4.QtGui import QTableView, QMainWindow, \
    QStandardItemModel, QStandardItem, QItemSelection, \
    QItemSelectionModel
from PyQt4 import uic

try:
    _fromUtf8 = QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        uic.loadUi("mainwindow.ui", self)

        model = QStandardItemModel(7, 4, self)
        for row in range(7):
            for column in range(4):
                item = QStandardItem(QString("%1").arg(row * 4 + column))
                model.setItem(row, column, item)

        self.tableView = QTableView()
        self.tableView.setModel(model)
        self.setCentralWidget(self.tableView)

        # 获取视图的项目选择模型
        selectionModel = self.tableView.selectionModel()

        # 定义左上角和右下角的索引,然后使用这两个索引创建选择
        topLeft = model.index(1, 1, QModelIndex())
        bottomRight = model.index(5, 2, QModelIndex())
        selection = QItemSelection(topLeft, bottomRight)

        # 使用指定的选择模式来选择项目
        selectionModel.select(selection, QItemSelectionModel.Select)

        self.mainToolBar.addAction(_fromUtf8("当前项目"), self.getCurrentItemData)
        self.mainToolBar.addAction(_fromUtf8("切换选择"), self.toggleSelection)

        self.connect(selectionModel,SIGNAL("selectionChanged(QItemSelection,QItemSelection)"),
                     self.updateSelection)
        self.connect(selectionModel, SIGNAL("currentChanged(QModelIndex,QModelIndex)"),
                self.changeCurrent)

        # 多个视图共享选择
        self.tableView2 = QTableView()
        self.tableView2.setWindowTitle("tableView2")
        self.tableView2.resize(400, 300)
        self.tableView2.setModel(model)
        self.tableView2.setSelectionModel(selectionModel)
        self.tableView2.show()

    # 输出当前项目的内容
    def getCurrentItemData(self, ):

        print  "当前项目的内容:", \
            self.tableView.selectionModel().currentIndex().data().toString()

    # 切换选择的项目
    def toggleSelection(self):
        topLeft = self.tableView.model().index(0, 0, QModelIndex())
        bottomRight = self.tableView.model().index(
            self.tableView.model().rowCount(QModelIndex()) - 1,
            self.tableView.model().columnCount(QModelIndex()) - 1, QModelIndex())
        curSelection = QItemSelection(topLeft, bottomRight)
        self.tableView.selectionModel().select(curSelection, QItemSelectionModel.Toggle)

    # 更新选择
    def updateSelection(self,selected, deselected):

        mlist = selected.indexes()
        # 为现在选择的项目填充值
        for index in mlist:
            text = QString("(%1,%2)").arg(index.row()).arg(index.column())
            self.tableView.model().setData(index, text)

        mlist = deselected.indexes()
        # 清空上一次选择的项目的内容
        for index in mlist:
            self.tableView.model().setData(index, "")

    # 改变当前项目
    def changeCurrent(self, current, previous):

        print  QString("move(%1,%2) to (%3,%4)")\
            .arg(previous.row()).arg(previous.column()) \
            .arg(current.row()).arg(current.column())
#!/usr/bin/env python
#coding=utf-8



from PyQt4.QtGui import QApplication
import sys
from mainwindow import MainWindow
if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exec_()

16.4 委托类

16.4.1 基本概念

16.4.2 自定义委托

#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtCore import QString, QModelIndex, SIGNAL
from PyQt4.QtGui import QTableView, QMainWindow, \
    QStandardItemModel, QStandardItem, QItemSelection, \
    QItemSelectionModel
from PyQt4 import uic

from spinboxdelegate import SpinBoxDelegate

try:
    _fromUtf8 = QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        uic.loadUi("mainwindow.ui", self)

        model = QStandardItemModel(7, 4, self)
        for row in range(7):
            for column in range(4):
                item = QStandardItem(QString("%1").arg(row * 4 + column))
                model.setItem(row, column, item)

        self.tableView = QTableView()
        self.tableView.setModel(model)
        self.setCentralWidget(self.tableView)

        # 获取视图的项目选择模型
        selectionModel = self.tableView.selectionModel()

        # 定义左上角和右下角的索引,然后使用这两个索引创建选择
        topLeft = model.index(1, 1, QModelIndex())
        bottomRight = model.index(5, 2, QModelIndex())
        selection = QItemSelection(topLeft, bottomRight)

        # 使用指定的选择模式来选择项目
        selectionModel.select(selection, QItemSelectionModel.Select)

        self.mainToolBar.addAction(_fromUtf8("当前项目"), self.getCurrentItemData)
        self.mainToolBar.addAction(_fromUtf8("切换选择"), self.toggleSelection)

        self.connect(selectionModel,SIGNAL("selectionChanged(QItemSelection,QItemSelection)"),
                     self.updateSelection)
        self.connect(selectionModel, SIGNAL("currentChanged(QModelIndex,QModelIndex)"),
                self.changeCurrent)

        # 多个视图共享选择
        self.tableView2 = QTableView()
        self.tableView2.setWindowTitle("tableView2")
        self.tableView2.resize(400, 300)
        self.tableView2.setModel(model)
        self.tableView2.setSelectionModel(selectionModel)
        self.tableView2.show()

        # 使用自定义委托
        delegate = SpinBoxDelegate(self)
        self.tableView.setItemDelegate(delegate)

    # 输出当前项目的内容
    def getCurrentItemData(self, ):

        print  "当前项目的内容:", \
            self.tableView.selectionModel().currentIndex().data().toString()
    # 切换选择的项目
    def toggleSelection(self):
        topLeft = self.tableView.model().index(0, 0, QModelIndex())
        bottomRight = self.tableView.model().index(
            self.tableView.model().rowCount(QModelIndex()) - 1,
            self.tableView.model().columnCount(QModelIndex()) - 1, QModelIndex())
        curSelection = QItemSelection(topLeft, bottomRight)
        self.tableView.selectionModel().select(curSelection, QItemSelectionModel.Toggle)

    # 更新选择
    def updateSelection(self,selected, deselected):

        mlist = selected.indexes()
        # 为现在选择的项目填充值
        for index in mlist:
            text = QString("(%1,%2)").arg(index.row()).arg(index.column())
            self.tableView.model().setData(index, text)

        mlist = deselected.indexes()
        # 清空上一次选择的项目的内容
        for index in mlist:
            self.tableView.model().setData(index, "")

    # 改变当前项目
    def changeCurrent(self, current, previous):

        print  QString("move(%1,%2) to (%3,%4)")\
            .arg(previous.row()).arg(previous.column()) \
            .arg(current.row()).arg(current.column())
#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtGui import QItemDelegate, QSpinBox
from PyQt4.QtCore import Qt


class SpinBoxDelegate(QItemDelegate):
    def __init__(self,parent=None):
        super(SpinBoxDelegate, self).__init__(parent)


    # QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
    #                       const QModelIndex &index) const;
    #
    # void setEditorData(QWidget *editor, const QModelIndex &index) const;
    # void setModelData(QWidget *editor, QAbstractItemModel *model,
    #                   const QModelIndex &index) const;
    #
    # void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option,
    #                           const QModelIndex &index) const;


    # 创建编辑器 */
    def createEditor(self,parent, option, index ):

        editor =  QSpinBox(parent)
        editor.setMinimum(0)
        editor.setMaximum(100)

        return editor


    # 为编辑器设置数据 */
    def setEditorData(self,editor, index):

        value,flag = index.model().data(index, Qt.EditRole).toInt()
        spinBox =  editor
        spinBox.setValue(value)


    # 将数据写入到模型 */
    def setModelData(self, editor, model, index):

        spinBox =  editor
        spinBox.interpretText()
        value = spinBox.value()

        model.setData(index, value, Qt.EditRole)

    # 更新编辑器几何布局 */
    def updateEditorGeometry(self,editor, option, index ):

        editor.setGeometry(option.rect)

#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtGui import QApplication
import sys
from mainwindow import MainWindow
if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exec_()

16.5 项目视图的便捷类

16.5.1 QListWidget

16.5.2 QTreeWidget

16.5.3 QTableWidget

16.6 在项目视图中启用拖放

16.6.1 在便捷类中启用拖放

#!/usr/bin/env python
# coding=utf-8


from PyQt4.QtGui import QListWidgetItem, QApplication, \
    QIcon, QListWidget, QTreeWidget,QTreeWidgetItem, QTableWidget,QTableWidgetItem, QAbstractItemView
from PyQt4.QtCore import Qt, QStringList
import sys

if __name__ == "__main__":
    app = QApplication(sys.argv)

    # #********************* 1.  QListWidget ******************/
    listWidget = QListWidget()

    # 一种添加项目的简便方法
    QListWidgetItem("a", listWidget)

    # 添加项目的另一种方法,这样还可以进行各种设置
    listWidgetItem =  QListWidgetItem()
    listWidgetItem.setText("b")
    listWidgetItem.setIcon(QIcon("./yafeilinux.png"))
    listWidgetItem.setToolTip("this is b!")

    listWidget.insertItem(1, listWidgetItem)
    # 设置排序为倒序
    listWidget.sortItems(Qt.DescendingOrder)
    # 显示列表部件
    listWidget.show()

    #********************** 2. QTreeWidget *******************/
    treeWidget  = QTreeWidget()
    # 必须设置列数
    treeWidget.setColumnCount(2)
    # 设置标头
    headers = QStringList()
    headers.append("name")
    headers.append("year")

    treeWidget.setHeaderLabels(headers)
    # 添加项目
    grade1 =  QTreeWidgetItem(treeWidget)
    grade1.setText(0,"Grade1")
    student =  QTreeWidgetItem(grade1)
    student.setText(0,"Tom")
    student.setText(1,"1986")
    grade2 =  QTreeWidgetItem(treeWidget, grade1)
    grade2.setText(0,"Grade2")
    treeWidget.show()

    #********************* 3. QTableWidget ********************/
    # 创建表格部件,同时指定行数和列数
    tableWidget = QTableWidget(3, 2)
    # 创建表格项目,并插入到指定单元
    tableWidgetItem =  QTableWidgetItem("qt")
    tableWidget.setItem(1, 1, tableWidgetItem)
    # 创建表格项目,并将它们作为标头
    headerV =  QTableWidgetItem("first")
    tableWidget.setVerticalHeaderItem(0,headerV)
    headerH =  QTableWidgetItem("ID")
    tableWidget.setHorizontalHeaderItem(0,headerH)
    tableWidget.show()


    #/************ 4.  为listWidget启用拖放 *************/
    # 设置选择模式为单选
    listWidget.setSelectionMode(QAbstractItemView.SingleSelection)
    # 启用拖动
    listWidget.setDragEnabled(True)
    # 设置接受拖放
    listWidget.viewport().setAcceptDrops(True)
    # 设置显示将要被放置的位置
    listWidget.setDropIndicatorShown(True)
    # 设置拖放模式为移动项目,如果不设置,默认为复制项目
    listWidget.setDragDropMode(QAbstractItemView.InternalMove)

    app.exec_()


16.6.2 在模型/视图类中启用拖放

#!/usr/bin/env python
# coding=utf-8

from PyQt4.QtGui import  QApplication, QAbstractItemView, QListView,QTableView
from PyQt4.QtCore import  QStringList
import sys
from stringlistmodel import StringListModel

if __name__ == "__main__":
    app = QApplication(sys.argv)

    slist = QStringList()
    slist.append("a")
    slist.append("b")
    slist.append("c")
    slist.append("d")

    listView = QListView()
    '''
    PyQt使用Model时,如果Model创建时未设置parent,则运行完退出时会报错:
    QObject::startTimer: QTimer can only be used with threads started with QThread
    '''
    # model = StringListModel(slist)
    model = StringListModel(slist,listView)

    listView.setModel(model)
    listView.show()

    tableView = QTableView ()
    tableView.setModel(model)
    tableView.show()

    # 插入删除行
    model.insertRows(3, 2)
    model.removeRows(0, 1)

    # 启用拖放功能
    listView.setSelectionMode(QAbstractItemView.ExtendedSelection)  # 设置拖放为移动, 默认为复制
    listView.setDragEnabled(True)  # 启用拖动
    listView.setAcceptDrops(True)  # 设置接受拖放
    listView.setDropIndicatorShown(True) # 设置显示将要被放置的位置

    app.exec_()

16.7 其他内容

16.7.1 代理模型

#!/usr/bin/env python
# coding=utf-8

from PyQt4.QtCore import QString,  QStringList, QRegExp
from PyQt4.QtGui import  QMainWindow, QStringListModel,  QSortFilterProxyModel
from PyQt4 import uic

try:
    _fromUtf8 = QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

class MainWindow (QMainWindow):
    def __init__(self,parent = None):
        super(MainWindow, self).__init__(parent)
        uic.loadUi("mainwindow.ui", self)
        strlist = QStringList()
        strlist.append("yafei")
        strlist.append("yafeilinux")
        strlist.append("Qt")
        strlist.append("Qt Creator")

        listModel =  QStringListModel(strlist, self)
        self.filterModel =  QSortFilterProxyModel(self)
        # 为代理模型添加源模型
        self.filterModel.setSourceModel(listModel)
        # 在视图中使用代理模型
        self.listView.setModel(self.filterModel)

    def on_pushButton_clicked(self):
        rx = QRegExp(self.lineEdit.text())
        self.filterModel.setFilterRegExp(rx)

#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtGui import QApplication
import sys
from mainwindow import MainWindow
if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exec_()

16.7.2 数据-窗口映射器

#!/usr/bin/env python
# coding=utf-8

from PyQt4.QtCore import QString
from PyQt4.QtGui import  QMainWindow, QStandardItemModel, QStandardItem, \
    QDataWidgetMapper, QTableView
from PyQt4 import uic

try:
    _fromUtf8 = QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

class MainWindow (QMainWindow):
    def __init__(self,parent = None):
        super(MainWindow, self).__init__(parent)
        uic.loadUi("mainwindow.ui", self)

        model =  QStandardItemModel(3, 2, self)
        model.setItem(0, 0, QStandardItem(_fromUtf8("xiaoming")))
        model.setItem(0, 1, QStandardItem(_fromUtf8("0")))
        model.setItem(1, 0, QStandardItem(_fromUtf8("xiaogang")))
        model.setItem(1, 1, QStandardItem(_fromUtf8("5")))
        model.setItem(2, 0, QStandardItem(_fromUtf8("xiaohong")))
        model.setItem(2, 1, QStandardItem(_fromUtf8("0")))
        model.setItem(3, 0, QStandardItem(_fromUtf8("赵六")))
        model.setItem(3, 1, QStandardItem(_fromUtf8("8")))

        self.mapper =  QDataWidgetMapper(self)
        # 设置模型
        self.mapper.setModel(model)
        # 设置窗口部件和模型中的列的映射
        self.mapper.addMapping(self.lineEdit, 0)
        self.mapper.addMapping(self.lineEdit_2, 1)
        # 显示模型中的第一行
        self.mapper.toFirst()

        #----------------------------------------------------------
        tableview  =  QTableView()
        tableview.setModel(model)
        tableview.show()

    # 上一条按钮
    def on_pushButton_clicked(self):
        self.mapper.toPrevious()

    # 下一条按钮
    def on_pushButton_2_clicked(self):
        self.mapper.toNext()

#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtGui import QApplication
import sys
from mainwindow import MainWindow
if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exec_()

16.8 小结