侯体宗的博客
  • 首页
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

python3+PyQt5自定义视图详解

Python  /  管理员 发布于 7年前   256

pyqt提供的几个视图类都可以较好工作,包括QLisView,QTableView和QTreeView。但是对于一些难以用现有的方式来呈现数据,这时,可以创建我们自己的视图子类并将其用做模型数据的可视化来解决这一问题。本文通过Python3+pyqt5实现了python Qt GUI 快速编程的16章的例子。

#!/usr/bin/env python3import gzipimport osimport platformimport sysfrom PyQt5.QtCore import (QAbstractTableModel, QDateTime, QModelIndex,    QSize, QTimer, QVariant, Qt,pyqtSignal)from PyQt5.QtGui import ( QColor, QCursor, QFont,    QFontDatabase, QFontMetrics, QPainter, QPalette, QPixmap)from PyQt5.QtWidgets import QApplication,QDialog,QHBoxLayout, QLabel, QMessageBox,QScrollArea, QSplitter, QTableView,QWidget(TIMESTAMP, TEMPERATURE, INLETFLOW, TURBIDITY, CONDUCTIVITY, COAGULATION, RAWPH, FLOCCULATEDPH) = range(8)TIMESTAMPFORMAT = "yyyy-MM-dd hh:mm"class WaterQualityModel(QAbstractTableModel):  def __init__(self, filename):    super(WaterQualityModel, self).__init__()    self.filename = filename    self.results = []  def load(self):    self.beginResetModel()    exception = None    fh = None    try:      if not self.filename:        raise IOError("no filename specified for loading")      self.results = []      line_data = gzip.open(self.filename).read()      for line in line_data.decode("utf8").splitlines():        parts = line.rstrip().split(",")        date = QDateTime.fromString(parts[0] + ":00",          Qt.ISODate)        result = [date]        for part in parts[1:]:          result.append(float(part))        self.results.append(result)    except (IOError, ValueError) as e:      exception = e    finally:      if fh is not None:        fh.close()      self.endResetModel()      if exception is not None:        raise exception  def data(self, index, role=Qt.DisplayRole):    if (not index.isValid() or      not (0 <= index.row() < len(self.results))):      return QVariant()    column = index.column()    result = self.results[index.row()]    if role == Qt.DisplayRole:      item = result[column]      if column == TIMESTAMP:        #item = item.toString(TIMESTAMPFORMAT)        item=item      else:        #item = QString("%1").arg(item, 0, "f", 2)        item = "{0:.2f}".format(item)      return item    elif role == Qt.TextAlignmentRole:      if column != TIMESTAMP:        return QVariant(int(Qt.AlignRight|Qt.AlignVCenter))      return QVariant(int(Qt.AlignLeft|Qt.AlignVCenter))    elif role == Qt.TextColorRole and column == INLETFLOW:      if result[column] < 0:        return QVariant(QColor(Qt.red))    elif (role == Qt.TextColorRole and       column in (RAWPH, FLOCCULATEDPH)):      ph = result[column]      if ph < 7:        return QVariant(QColor(Qt.red))      elif ph >= 8:        return QVariant(QColor(Qt.blue))      else:        return QVariant(QColor(Qt.darkGreen))    return QVariant()  def headerData(self, section, orientation, role=Qt.DisplayRole):    if role == Qt.TextAlignmentRole:      if orientation == Qt.Horizontal:        return QVariant(int(Qt.AlignCenter))      return QVariant(int(Qt.AlignRight|Qt.AlignVCenter))    if role != Qt.DisplayRole:      return QVariant()    if orientation == Qt.Horizontal:      if section == TIMESTAMP:        return "Timestamp"      elif section == TEMPERATURE:        return "\u00B0" +"C"      elif section == INLETFLOW:        return "Inflow"      elif section == TURBIDITY:        return "NTU"      elif section == CONDUCTIVITY:        return "\u03BCS/cm"      elif section == COAGULATION:        return "mg/L"      elif section == RAWPH:        return "Raw Ph"      elif section == FLOCCULATEDPH:        return "Floc Ph"    return int(section + 1)  def rowCount(self, index=QModelIndex()):    return len(self.results)  def columnCount(self, index=QModelIndex()):    return 8class WaterQualityView(QWidget):  clicked = pyqtSignal(QModelIndex)  FLOWCHARS = (chr(0x21DC), chr(0x21DD), chr(0x21C9))  def __init__(self, parent=None):    super(WaterQualityView, self).__init__(parent)    self.scrollarea = None    self.model = None    self.setFocusPolicy(Qt.StrongFocus)    self.selectedRow = -1    self.flowfont = self.font()    size = self.font().pointSize()    if platform.system() == "Windows":      fontDb = QFontDatabase()      for face in [face.toLower() for face in fontDb.families()]:        if face.contains("unicode"):          self.flowfont = QFont(face, size)          break      else:        self.flowfont = QFont("symbol", size)        WaterQualityView.FLOWCHARS = (chr(0xAC), chr(0xAE),           chr(0xDE))  def setModel(self, model):    self.model = model    #self.connect(self.model,    #    SIGNAL("dataChanged(QModelIndex,QModelIndex)"),    #    self.setNewSize)    self.model.dataChanged.connect(self.setNewSize)    #self.connect(self.model, SIGNAL("modelReset()"), self.setNewSize)    self.model.modelReset.connect(self.setNewSize)    self.setNewSize()  def setNewSize(self):    self.resize(self.sizeHint())    self.update()    self.updateGeometry()  def minimumSizeHint(self):    size = self.sizeHint()    fm = QFontMetrics(self.font())    size.setHeight(fm.height() * 3)    return size  def sizeHint(self):    fm = QFontMetrics(self.font())    size = fm.height()    return QSize(fm.width("9999-99-99 99:99 ") + (size * 4),           (size / 4) + (size * self.model.rowCount()))  def paintEvent(self, event):    if self.model is None:      return    fm = QFontMetrics(self.font())    timestampWidth = fm.width("9999-99-99 99:99 ")    size = fm.height()    indicatorSize = int(size * 0.8)    offset = int(1.5 * (size - indicatorSize))    minY = event.rect().y()    maxY = minY + event.rect().height() + size    minY -= size    painter = QPainter(self)    painter.setRenderHint(QPainter.Antialiasing)    painter.setRenderHint(QPainter.TextAntialiasing)    y = 0    for row in range(self.model.rowCount()):      x = 0      if minY <= y <= maxY:        painter.save()        painter.setPen(self.palette().color(QPalette.Text))        if row == self.selectedRow:          painter.fillRect(x, y + (offset * 0.8),  self.width(), size, self.palette().highlight())          painter.setPen(self.palette().color(  QPalette.HighlightedText))        #timestamp = self.model.data(#self.model.index(row, TIMESTAMP)).toDateTime()        timestamp = self.model.data(self.model.index(row, TIMESTAMP))painter.drawText(x, y + size,timestamp.toString(TIMESTAMPFORMAT))        #print(timestamp.toString(TIMESTAMPFORMAT))        x += timestampWidth        temperature = self.model.data(self.model.index(row, TEMPERATURE))        #temperature = temperature.toDouble()[0]        temperature = float(temperature)        if temperature < 20:          color = QColor(0, 0,  int(255 * (20 - temperature) / 20))        elif temperature > 25:          color = QColor(int(255 * temperature / 100), 0, 0)        else:          color = QColor(0, int(255 * temperature / 100), 0)        painter.setPen(Qt.NoPen)        painter.setBrush(color)        painter.drawEllipse(x, y + offset, indicatorSize,      indicatorSize)        x += size        rawPh = self.model.data(self.model.index(row, RAWPH))        #rawPh = rawPh.toDouble()[0]        rawPh = float(rawPh)        if rawPh < 7:          color = QColor(int(255 * rawPh / 10), 0, 0)        elif rawPh >= 8:          color = QColor(0, 0, int(255 * rawPh / 10))        else:          color = QColor(0, int(255 * rawPh / 10), 0)        painter.setBrush(color)        painter.drawEllipse(x, y + offset, indicatorSize,      indicatorSize)        x += size        flocPh = self.model.data(self.model.index(row, FLOCCULATEDPH))        #flocPh = flocPh.toDouble()[0]        flocPh = float(flocPh)        if flocPh < 7:          color = QColor(int(255 * flocPh / 10), 0, 0)        elif flocPh >= 8:          color = QColor(0, 0, int(255 * flocPh / 10))        else:          color = QColor(0, int(255 * flocPh / 10), 0)        painter.setBrush(color)        painter.drawEllipse(x, y + offset, indicatorSize,      indicatorSize)        painter.restore()        painter.save()        x += size        flow = self.model.data(self.model.index(row, INLETFLOW))        #flow = flow.toDouble()[0]        flow = float(flow)        char = None        if flow <= 0:          char = WaterQualityView.FLOWCHARS[0]        elif flow < 3.6:          char = WaterQualityView.FLOWCHARS[1]        elif flow > 4.7:          char = WaterQualityView.FLOWCHARS[2]        if char is not None:          painter.setFont(self.flowfont)          painter.drawText(x, y + size, char)        painter.restore()      y += size      if y > maxY:        break  def mousePressEvent(self, event):    fm = QFontMetrics(self.font())    self.selectedRow = event.y() // fm.height()    self.update()    #self.emit(SIGNAL("clicked(QModelIndex)"),    #     self.model.index(self.selectedRow, 0))    self.clicked.emit(self.model.index(self.selectedRow, 0))  def keyPressEvent(self, event):    if self.model is None:      return    row = -1    if event.key() == Qt.Key_Up:      row = max(0, self.selectedRow - 1)    elif event.key() == Qt.Key_Down:      row = min(self.selectedRow + 1, self.model.rowCount() - 1)    if row != -1 and row != self.selectedRow:      self.selectedRow = row      if self.scrollarea is not None:        fm = QFontMetrics(self.font())        y = fm.height() * self.selectedRow        print(y)        self.scrollarea.ensureVisible(0, y)      self.update()      #self.emit(SIGNAL("clicked(QModelIndex)"),      #     self.model.index(self.selectedRow, 0))      self.clicked.emit(self.model.index(self.selectedRow, 0))    else:      QWidget.keyPressEvent(self, event)class MainForm(QDialog):  def __init__(self, parent=None):    super(MainForm, self).__init__(parent)    self.model = WaterQualityModel(os.path.join(        os.path.dirname(__file__), "waterdata.csv.gz"))    self.tableView = QTableView()    self.tableView.setAlternatingRowColors(True)    self.tableView.setModel(self.model)    self.waterView = WaterQualityView()    self.waterView.setModel(self.model)    scrollArea = QScrollArea()    scrollArea.setBackgroundRole(QPalette.Light)    scrollArea.setWidget(self.waterView)    self.waterView.scrollarea = scrollArea    splitter = QSplitter(Qt.Horizontal)    splitter.addWidget(self.tableView)    splitter.addWidget(scrollArea)    splitter.setSizes([600, 250])    layout = QHBoxLayout()    layout.addWidget(splitter)    self.setLayout(layout)    self.setWindowTitle("Water Quality Data")    QTimer.singleShot(0, self.initialLoad)  def initialLoad(self):    QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))    splash = QLabel(self)    pixmap = QPixmap(os.path.join(os.path.dirname(__file__),        "iss013-e-14802.jpg"))    #print(os.path.join(os.path.dirname(__file__),    #    "iss013-e-14802.jpg"))    splash.setPixmap(pixmap)    splash.setWindowFlags(Qt.SplashScreen)    splash.move(self.x() + ((self.width() - pixmap.width()) / 2),          self.y() + ((self.height() - pixmap.height()) / 2))    splash.show()    QApplication.processEvents()    try:      self.model.load()    except IOError as e:      QMessageBox.warning(self, "Water Quality - Error", e)    else:      self.tableView.resizeColumnsToContents()    splash.close()    QApplication.processEvents()    QApplication.restoreOverrideCursor()app = QApplication(sys.argv)form = MainForm()form.resize(850, 620)form.show()app.exec_()

运行结果:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


  • 上一条:
    python3+PyQt5实现柱状图
    下一条:
    python自动重试第三方包retrying模块的方法
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在windows10中升级go版本至1.24后LiteIDE的Ctrl+左击无法跳转问题解决方案(0个评论)
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(95个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客