PHP前端开发

python3+PyQt5自定义视图详解

百变鹏仔 3小时前 #Python
文章标签 自定义

这篇文章主要为大家详细介绍了python3+pyqt5自定义视图的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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 = 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  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 = 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 = 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  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_()

运行结果: