python

超轻量级php框架startmvc

python3+PyQt5自定义视图详解

更新时间:2020-05-30 10:06:01 作者:startmvc
pyqt提供的几个视图类都可以较好工作,包括QLisView,QTableView和QTreeView。但是对于一些难以

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


#!/usr/bin/env python3

import gzip
import os
import platform
import sys
from 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 8


class 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 视图