前言

在开发Qt界面时,经常会用到一些嵌套式控件,例如列表控件QlistWidget、树形控件QTreeWidget、表格控件QTableWidget。

同时Qt中也有QListView、QTreeView、QtableView,区别是前者是Item-Based,使用起来更简单,后者是Model-Based,功能更强大。

因此接下来用几篇文章就整理一下常用的这三种控件的使用方法,今天先来说一下QListWidget和QListView。

 

QListWidget的使用

在使用QListWidget时,经常会和复选框QCheckBox嵌套使用,例如下面的例子。

这个简单的例子有基本的增删查功能,并且有复选框和图标,能够对单击和双击事件进行响应,代码如下:

from Qt import QtCore, QtWidgets, QtGui
from Qt.QtCompat import load_ui, QFileDialog

class testPanel(QtWidgets.QWidget):

    def __init__(self):
        super(testPanel, self).__init__()
        DIR, file_name = os.path.split(os.path.abspath(__file__))
        load_ui(os.path.join(DIR, "ListWidget.ui"), self)

        self.listWidget.setIconSize(QtCore.QSize(15,10))
        self.bindEvent()

    def bindEvent(self):
        self.pushButton.clicked.connect(self.add)
        self.pushButton_2.clicked.connect(self.remove)
        self.pushButton_3.clicked.connect(self.all)
        self.pushButton_4.clicked.connect(self.clear)
        self.listWidget.itemDoubleClicked.connect(self.dbclick)

    # add Item
    def add(self):
        text = self.lineEdit.text().strip()
        if text:
            item = QtWidgets.QListWidgetItem(text)
            icon = QtGui.QIcon("D:/img538.jpg")
            item.setIcon(icon)
            item.setCheckState(QtCore.Qt.Checked)
            self.listWidget.addItem(item)

    # remove Item
    def remove(self):
        selectedRow = self.listWidget.currentRow()
        if selectedRow != -1:
            self.listWidget.takeItem(selectedRow)

    # select all Items
    def all(self):
        for i in range(self.listWidget.count()):
            item = self.listWidget.item(i)
            item.setCheckState(QtCore.Qt.Checked)

    # unselect all Items
    def clear(self):
        for i in range(self.listWidget.count()):
            item = self.listWidget.item(i)
            item.setCheckState(QtCore.Qt.Unchecked)
            
    # get all selected Items
    def get_data(self):
        allData = []
        for i in range(self.listWidget.count()):
            item = self.listWidget.item(i)
            if item.checkState():
                allData.append(item.text())
        return allData
     
    # Item DoubleClicked
    def dbclick(self):
        self.label.setText("ListWidgetItem been dbClicked!")

app = QtWidgets.QApplication(sys.argv)
mt = testPanel()
mt.show()
app.exec_()

常用的样式如下:

QListWidget
{
    background: #222222;
}
QListWidget::item
{
    background: #bbbbbb;
}
QListWidget::item:hover
{
    background: #ffffff;
   
}
QListWidget::item:selected
{
    background: #cccccc;
}
复选框样式
QListWidget::indicator::checked{
    image: url(D:/ico/selected.png); 
}

上面的案例里面的子项比较简单,只有复选框、图标、文字,是通过item方式来添加子项的,而如果子项比较复杂,例如歌曲列表,每项都有独立的歌名、作者、专辑、图片、按钮、进度条等等,这种时候可以使用itemWidget方式添加Widget子项。

例如下面的例子。

如上图,每一行都是一个小的Widget,实现也很简单,代码如下:

import os
from Qt import QtCore, QtWidgets, QtGui
from Qt.QtCompat import load_ui, QFileDialog

# 行控件
class songItem(QtWidgets.QWidget):
    def __init__(self,count,name,artist,CD,time):
        super(songItem, self).__init__()
        DIR, file_name = os.path.split(os.path.abspath(__file__))
        load_ui(os.path.join(DIR, "song.ui"), self)

        self.count = count
        self.name = name
        self.artist = artist
        self.CD = CD
        self.time = time

        self.initUI()
        self.initData()

    def initUI(self):
        favIcon = QtGui.QIcon("fav.png")
        self.pushButton_Fav.setIcon(favIcon)
        downIcon = QtGui.QIcon("down.png")
        self.pushButton_Down.setIcon(downIcon)
        qss = """QPushButton{
            background:transparent;
            border:none;
        }
        """
        self.pushButton_Fav.setStyleSheet(qss)
        self.pushButton_Down.setStyleSheet(qss)

    def initData(self):
        self.label_Num.setText(str(self.count))
        self.label_Name.setText(self.name)
        self.label_Artist.setText(self.artist)
        self.label_CD.setText(self.CD)
        self.label_Time.setText(self.time)

# 主体窗口
class testPanel(QtWidgets.QWidget):

    def __init__(self):
        super(testPanel, self).__init__()
        DIR, file_name = os.path.split(os.path.abspath(__file__))
        load_ui(os.path.join(DIR, "ListWidget.ui"), self)

        self.initui()
        self.datas = [["いかないで","鹿乃","two","03:02"],["フラレガイガール","さユり","フラレガイガール","06:07"],["ワタシノテンシ","雨宫天","好きすぎてやばい","04:22"]]
        self.bindEvent()
        self.add()

    def initui(self):
        qss = """
QListWidget
{
    outline: 0px;
}
QListWidget::item
{
    height:40px;
    padding: 0px;
}
        """
        self.listWidget.setStyleSheet(qss)

    def bindEvent(self):
        self.pushButton.clicked.connect(self.add)
        self.pushButton_2.clicked.connect(self.remove)
        # self.pushButton_3.clicked.connect(self.all)
        # self.pushButton_4.clicked.connect(self.clear)
        self.listWidget.itemClicked.connect(self.click)
        self.listWidget.itemDoubleClicked.connect(self.dbclick)


    def add(self):
        for i in range(len(self.datas)):
            itemWidget = songItem(i+1,self.datas[i][0],self.datas[i][1],self.datas[i][2],self.datas[i][3])
            item = QtWidgets.QListWidgetItem()
            self.listWidget.addItem(item)
            self.listWidget.setItemWidget(item, itemWidget)
            self.listWidget.addItem(item)

    def remove(self):
        selectedItems = self.listWidget.selectedItems()
        if selectedItems:
            self.listWidget.takeItem(self.listWidget.row(selectedItems[0]))

    def get_data(self):
        allData = []
        for i in range(self.listWidget.count()):
            item = self.listWidget.itemWidget(self.listWidget.item(i))
            allData.append(item.label_Name.text())
        return allData

    def click(self):
        selectedItems = self.listWidget.selectedItems()
        if selectedItems:
            item = self.listWidget.itemWidget(selectedItems[0])
            self.label.setText(item.label_Name.text() + " been Clicked!")

    def dbclick(self):
        selectedItems = self.listWidget.selectedItems()
        if selectedItems:
            item = self.listWidget.itemWidget(selectedItems[0])
            self.label.setText(item.label_Name.text() + " been DoubleClicked!")

app = QtWidgets.QApplication(sys.argv)
mt = testPanel()
mt.show()
app.exec_()

 

QListView的使用

相比于QListWidget,QListView是使用Model/View结构来管理数据与视图的关系, model负责数据的存取。

QT提供了一些现成的models用于处理数据项:

  • QStringListModel 用于存储简单的QString列表。
  • QStandardItemModel 管理复杂的树型结构数据项,每项都可以包含任意数据。
  • QDirModel 提供本地文件系统中的文件与目录信息。
  • QSqlQueryModel, QSqlTableModel,QSqlRelationTableModel用来访问数据库。

这里先以QStringListModel为例,看下面的例子。

这个简单的例子有基本的增删改查功能,也能正常触发单击、双击事件,代码如下:

import os

from Qt import QtCore, QtWidgets, QtGui
from Qt.QtCompat import load_ui, QFileDialog

class testPanel(QtWidgets.QWidget):

    def __init__(self):
        super(testPanel, self).__init__()
        DIR, file_name = os.path.split(os.path.abspath(__file__))
        load_ui(os.path.join(DIR, "ListView.ui"), self)

        self.qList = ["Item1", "Item2", "Item3", "Item4"]

        self.bindEvent()
        self.initData()

    def bindEvent(self):
        self.pushButton.clicked.connect(self.add)
        self.pushButton_2.clicked.connect(self.remove)
        self.listView.clicked.connect(self.click)
        self.listView.doubleClicked.connect(self.dbclick)

    def initData(self):
        self.model = QtCore.QStringListModel()
        self.model.setStringList(self.qList)
        self.listView.setModel(self.model)

    def add(self):
        text = self.lineEdit.text().strip()
        if text:
            row = self.model.rowCount()
            self.model.insertRow(row)
            self.model.setData(self.model.index(row), text)

    def remove(self):
        selectedIndex = self.listView.currentIndex()
        if selectedIndex:
            self.model.removeRow(selectedIndex.row())

    def get_data(self):
        return self.model.stringList()

    def click(self, qModelIndex):
        self.label.setText(self.model.stringList()[qModelIndex.row()] + " been Clicked!")

    def dbclick(self,qModelIndex):
        self.label.setText(self.model.stringList()[qModelIndex.row()] + " been DoubleClicked!")
        

app = QtWidgets.QApplication(sys.argv)
mt = testPanel()
mt.show()
app.exec_()

常用的样式与QListWidget相同,如下:

QListView {
	background: red;
}
QListView::item {
    height: 30px;
	background: blue;
}
QListView::item:selected {
	border:2px solid #6a6ea9;
}
QListView::item::hover {
	background:qlineargradient(x1:0, y1:0,x2:1,y2:0, stop:0 #000000, stop:1 #ffffff);
}
QListView::indicator::checked{
    image: url(D:/img538.jpg); 
}

接下来是比较常用的QStandardItemModel,案例如下:

与QListWidget的第一个案例几乎没有区别,只是多了双击可以编辑的功能,代码如下:

import os

from Qt import QtCore, QtWidgets, QtGui
from Qt.QtCompat import load_ui, QFileDialog

class testPanel(QtWidgets.QWidget):

    def __init__(self):
        super(testPanel, self).__init__()
        DIR, file_name = os.path.split(os.path.abspath(__file__))
        load_ui(os.path.join(DIR, "ListView.ui"), self)

        self.bindEvent()
        self.initData()

    def bindEvent(self):
        self.pushButton.clicked.connect(self.add)
        self.pushButton_2.clicked.connect(self.remove)
        self.pushButton_3.clicked.connect(self.all)
        self.pushButton_4.clicked.connect(self.clear)
        self.listView.clicked.connect(self.click)
        self.listView.doubleClicked.connect(self.dbclick)


    def initData(self):
        self.model = QtGui.QStandardItemModel()
        self.listView.setModel(self.model)

    def add(self):
        text = self.lineEdit.text().strip()
        if text:
            item = QtGui.QStandardItem(text)
            item.setCheckable(True)
            item.setCheckState(QtCore.Qt.Checked)
            item.setData(QtGui.QIcon("D:/img538.jpg"), QtCore.Qt.DecorationRole)
            self.model.appendRow(item)

    def remove(self):
        selectedIndex = self.listView.currentIndex()
        if selectedIndex:
            self.model.removeRow(selectedIndex.row())

    def all(self):
        i = 0
        while self.model.item(i):
            item = self.model.item(i)
            if not item.checkState():
                item.setCheckState(QtCore.Qt.Checked)
            i += 1

    def clear(self):
        i = 0
        while self.model.item(i):
            item = self.model.item(i)
            if item.checkState():
                item.setCheckState(QtCore.Qt.Unchecked)
            i += 1

    def get_data(self):
        choices = []
        i = 0
        while self.model.item(i):
            if self.model.item(i).checkState():
                choices.append(self.model.item(i).text())
            i += 1
        return choices

    def click(self, qModelIndex):
        print(qModelIndex.row())
        self.label.setText(self.model.item(qModelIndex.row()).text() + " been Clicked!")

    def dbclick(self,qModelIndex):
        self.label.setText(self.model.item(qModelIndex.row()).text() + " been DoubleClicked!")


app = QtWidgets.QApplication(sys.argv)
mt = testPanel()
mt.show()
app.exec_()

不要因为事与愿违而感到惊讶,
因为这个宇宙比你大的多。

——阿兰·德波顿