使用Python编写RSS阅读器(四)

使用Python编写RSS阅读器(四)


  前面,我们已经完成了RSS阅读器的界面。

  这一节,让我们继续为RSS阅读器添加事件处理代码。




  一、信号和槽(SIGNALS AND SLOTS)


  Qt和PyQt提供了两种通信机制:底层事件处理,及“信号和槽”。

  由于篇幅所限,这里重点讲讲“信号和槽”。


  所有的PyQt组件,由于它们都是从QWidget(QObject的子类)派生的,所以它们都支持信号和槽机制。


  •   所有的PyQt组件都有一组预先定义的“信号”(SIGNAL),当组件的状态发生变化时(如按钮被点击,或复选框被选中),都会发射相应的信号。
  •   信号的处理,是由“槽”(SLOT)来完成的。
  •   当一个信号发射时,PyQt只是抛出它,要处理信号,必须将信号“连接”(connect)到槽。
  •   C++编写Qt程序时,槽是使用特殊语法声明的方法,PyQt对其做了简化,槽可以是一个普通的函数。
  •   一个信号可以关联到多个槽。一个槽也可以处理多个信号。

  如果用JavaScript来类比的话,信号就相当于“事件”,点击按钮会产生click事件。
  槽就相当于“事件处理的回调函数”。
  需要注册事件处理程序,用于关联“事件”和“回调函数”,例如:btn1.onclick="btn1_click()";


  二、QTreeView编程


  在实际编程时,一定要多去看API文档:http://pyqt.sourceforge.net/Docs/PyQt4/classes.html

  每个类都有详细的说明,可以使用Chrome浏览器的“翻成中文”功能将API文档变成中文。

  每个类的说明文档大体分为简单的使用说明、方法列表、信号列表等几个部分。


  1. QTreeView选中信号


  当点击QTreeView中的一项时,会发射选中信号。


  (1) 选中信号


  QTreeView的选中,由一个封装好的QItemSelectionModel来管理,通过QTreeView的selectionModel()方法可以取得该Model。


selectionModel = treeView.selectionModel()


  这个Model有预先设定的几个信号:selectionChanged、currentChanged、currentRowChanged、currentColumnChanged、modelChanged。通常我们可以选择currentChanged信号来使用。

  (2) 槽函数的编写


def slotCurrentChanged(self, index):
    # 取得url
    url = index.data().toString()
    # 加载url
    self.webView.load(QUrl(url))

  (3) 将信号连接到槽函数


self.treeView.selectionModel().currentChanged.connect(self.slotCurrentChanged)


  将信号连接到槽函数,有多种不同的写法。这里我们采用的是比较简单的写法,格式如下:

  信号.connect(槽函数)


  为了编程的方便,我们需要将编程会用到的组件(如QTreeView、QWebView等)都定义为成员变量,即以“self.成员变量”的方式使用它们,这样在类的内部,特别是在槽函数中,可以方便地访问到它们。


  2. QTreeView选中项和兄弟项


  我们怎么在槽函数中得到选中项的信息呢?

  槽函数的格式是这样的:def slotCurrentChanged(self, index)

  槽函数的第一个参数通常都是self,可以不管它,第二个参数传递的是当前选中项的index,这个index是什么呢?


  原来index是QModelIndex类型的变量,QModelIndex可以看做是Model的索引,通过它可以访问选中项的QStandardItem。
  通过index.data()方法可以获得QStandardItem的数据。
  通过index.row()方法可以获得QStandardItem所在的行。
  通过index.column()方法可以获得QStandardItem所在的列。

  由于我们的QTreeView有两列,你可能会点在第0列,也可能会点在第1列,第0列是RSS订阅源的名称,第1列才是URL,实际应用中我们需要的是第1列的值。
  为了让程序不管点QTreeView的哪一列都能浏览网页,可以使用index.sibling()方法得到同级的兄弟项。
  语法如下:

  index.sibling(row, column)

  最后,我们把槽函数改写成这样:

    def slotCurrentChanged(self, index):
        # 如果点的是第0列,则取第1列的数据
        # 如果点的是第1列,直接取它的数据
        if index.column() == 0:
            url = index.sibling(index.row(), 1).data().toString()
        else:
            url = index.data().toString()
        self.webView.load(QUrl(url))


  三、完整代码


#!/usr/bin/python
# -*- coding: UTF-8 -*-
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
import sys


class TreeDemo(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        # 使用字典保存所有小图标,字典数据由键、值对构成
        iconMap = {}
        iconMap[QString('x1')] = QIcon(QString('icons/x1.ico'))
        iconMap[QString('x2')] = QIcon(QString('icons/x2.ico'))
        iconMap[QString('x3')] = QIcon(QString('icons/x3.ico'))
        iconMap[QString('x4')] = QIcon(QString('icons/x4.ico'))
        iconMap[QString('x5')] = QIcon(QString('icons/x5.ico'))

        gridLayout = QGridLayout()
        gridLayout.setHorizontalSpacing(10)
        gridLayout.setVerticalSpacing(10)
        gridLayout.setContentsMargins(10, 10, 10, 10)

        # 左边是QTreeView
        self.treeView = QTreeView()
        # QTreeView使用固定宽度
        self.treeView.setFixedWidth(500)

        # QStandardItemModel是树状视图的数据模型
        # 模型与视图是分离的,我们只需维护模型
        # 使用treeView作为model的parent,这样treeView释放时会销毁model
        model = QStandardItemModel(self.treeView)

        # 添加树状视图的表头
        headers = QStringList()
        headers.append(QString(u'RSS订阅源'))
        headers.append(QString(u'URL'))
        model.setHorizontalHeaderLabels(headers)

        # 为视状视图添加5个数据项,其中item1~3是平级的,item4是挂在item3之下,item5是挂在item4之下
        item1 = QStandardItem(iconMap[QString('x1')], QString(u'微软亚洲研究院'))
        model.appendRow(item1)
        # 使用model.setItem可以添加第2列的信息
        # 0, 1表示第0行,第1列
        # 为了编程方便,也可以写成:
        # model.setItem(model.indexFromItem(item1).row(), 1, ...)
        model.setItem(0, 1, QStandardItem(
            QString('http://blog.sina.com.cn/rss/1286528122.xml')))

        item2 = QStandardItem(iconMap[QString('x2')], QString(u'知乎每日精选'))
        model.appendRow(item2)
        model.setItem(1, 1, QStandardItem(
            QString('http://www.zhihu.com/rss')))

        item3 = QStandardItem(iconMap[QString('x3')], QString(u'爱范儿'))
        model.appendRow(item3)
        model.setItem(2, 1, QStandardItem(
            QString('http://www.ifanr.com/feed')))

        item4 = QStandardItem(iconMap[QString('x4')], QString(u'柴静'))
        item3.appendRow(item4)
        # item.setChild作用与model.setItem相近
        # 0, 1表示第0行,第1列
        item3.setChild(0, 1, QStandardItem(
            QString('http://blog.sina.com.cn/rss/1219548027.xml')))

        item5 = QStandardItem(iconMap[QString('x5')], QString(u'小众软件'))
        item4.appendRow(item5)
        item4.setChild(0, 1, QStandardItem(
            QString('http://www.appinn.com/feed/')))

        # 为树状视图设置模型,这里将模型与视图关联起来
        self.treeView.setModel(model)

        # 关联信号和槽函数
        self.treeView.selectionModel().currentChanged.connect(self.slotCurrentChanged)

        gridLayout.addWidget(self.treeView, 0, 0, 1, 1)

        # 右边是QWebView,QWebView可用于显示网页
        self.webView = QWebView()
        self.webView.load(QUrl('https://www.baidu.com'))

        gridLayout.addWidget(self.webView, 0, 1, 1, 1)

        self.setLayout(gridLayout)

        self.setGeometry(100, 100, 1200, 600)
        self.setWindowTitle(u'RSS阅读器')

    def slotCurrentChanged(self, index):
        # 如果点的是第0列,则取第1列的数据
        # 如果点的是第1列,直接取它的数据
        if index.column() == 0:
            url = index.sibling(index.row(), 1).data().toString()
        else:
            url = index.data().toString()
        self.webView.load(QUrl(url))


app = QApplication(sys.argv)
treeDemo = TreeDemo()
treeDemo.show()
sys.exit(app.exec_())

猜你喜欢

转载自blog.csdn.net/hanhf/article/details/80414484