温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

python3+PyQt5泛型委托的示例分析

发布时间:2021-08-13 13:36:07 来源:亿速云 阅读:169 作者:小新 栏目:开发技术

这篇文章给大家分享的是有关python3+PyQt5泛型委托的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

自定义委托可以让我们对视图中出现的数据项的外观和行为进行完全控制。如果有很多模型,可能会希望不是全部的大多数模型能够仅用一个自定义委托,如果不能这么做,那么对于这些自定义委托,将很有可能存在大量重复代码。为了使得维护工作变得轻松,更好的方法为不要为每个模型创建一个自定义委托,而是用一系列的通用组件来共同构成一个委托。本文通过Python3+pyqt5实现了python Qt GUI 快速编程的16章的泛型委托例子。

/home/yrd/eric_workspace/chap16/richtextlineedit.py

#!/usr/bin/env python3 import platform import sys import html from PyQt5.QtCore import QSize, Qt,pyqtSignal from PyQt5.QtGui import QColor, QFont,QFontMetrics, QIcon, QKeySequence, QPixmap,QTextCharFormat from PyQt5.QtWidgets import QAction,QApplication,QMenu,QTextEdit class RichTextLineEdit(QTextEdit):  returnPressed=pyqtSignal()  (Bold, Italic, Underline, StrikeOut, Monospaced, Sans, Serif,   NoSuperOrSubscript, Subscript, Superscript) = range(10)  def __init__(self, parent=None):   super(RichTextLineEdit, self).__init__(parent)   self.monofamily = "courier"   self.sansfamily = "helvetica"   self.seriffamily = "times"   self.setLineWrapMode(QTextEdit.NoWrap)   self.setTabChangesFocus(True)   self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)   self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)   fm = QFontMetrics(self.font())   h = int(fm.height() * (1.4 if platform.system() == "Windows"          else 1.2))   self.setMinimumHeight(h)   self.setMaximumHeight(int(h * 1.2))   self.setToolTip("Press <b>Ctrl+M</b> for the text effects "     "menu and <b>Ctrl+K</b> for the color menu")  def toggleItalic(self):   self.setFontItalic(not self.fontItalic())  def toggleUnderline(self):   self.setFontUnderline(not self.fontUnderline())  def toggleBold(self):   self.setFontWeight(QFont.Normal     if self.fontWeight() > QFont.Normal else QFont.Bold)  def sizeHint(self):   return QSize(self.document().idealWidth() + 5,       self.maximumHeight())  def minimumSizeHint(self):   fm = QFontMetrics(self.font())   return QSize(fm.width("WWWW"), self.minimumHeight())  def contextMenuEvent(self, event):   self.textEffectMenu()  def keyPressEvent(self, event):   if event.modifiers() & Qt.ControlModifier:    handled = False    if event.key() == Qt.Key_B:     self.toggleBold()     handled = True    elif event.key() == Qt.Key_I:     self.toggleItalic()     handled = True    elif event.key() == Qt.Key_K:     self.colorMenu()     handled = True    elif event.key() == Qt.Key_M:     self.textEffectMenu()     handled = True    elif event.key() == Qt.Key_U:     self.toggleUnderline()     handled = True    if handled:     event.accept()     return   if event.key() in (Qt.Key_Enter, Qt.Key_Return):    self.returnPressed.emit()    event.accept()   else:    QTextEdit.keyPressEvent(self, event)  def colorMenu(self):   pixmap = QPixmap(22, 22)   menu = QMenu("Colour")   for text, color in (     ("&Black", Qt.black),     ("B&lue", Qt.blue),     ("Dark Bl&ue", Qt.darkBlue),     ("&Cyan", Qt.cyan),     ("Dar&k Cyan", Qt.darkCyan),     ("&Green", Qt.green),     ("Dark Gr&een", Qt.darkGreen),     ("M&agenta", Qt.magenta),     ("Dark Mage&nta", Qt.darkMagenta),     ("&Red", Qt.red),     ("&Dark Red", Qt.darkRed)):    color = QColor(color)    pixmap.fill(color)    action = menu.addAction(QIcon(pixmap), text, self.setColor)    action.setData(color)   self.ensureCursorVisible()   menu.exec_(self.viewport().mapToGlobal(      self.cursorRect().center()))  def setColor(self):   action = self.sender()   if action is not None and isinstance(action, QAction):    color = QColor(action.data())    if color.isValid():     self.setTextColor(color)  def textEffectMenu(self):   format = self.currentCharFormat()   menu = QMenu("Text Effect")   for text, shortcut, data, checked in (     ("&Bold", "Ctrl+B", RichTextLineEdit.Bold,      self.fontWeight() > QFont.Normal),     ("&Italic", "Ctrl+I", RichTextLineEdit.Italic,      self.fontItalic()),     ("Strike &out", None, RichTextLineEdit.StrikeOut,      format.fontStrikeOut()),     ("&Underline", "Ctrl+U", RichTextLineEdit.Underline,      self.fontUnderline()),     ("&Monospaced", None, RichTextLineEdit.Monospaced,      format.fontFamily() == self.monofamily),     ("&Serifed", None, RichTextLineEdit.Serif,      format.fontFamily() == self.seriffamily),     ("S&ans Serif", None, RichTextLineEdit.Sans,      format.fontFamily() == self.sansfamily),     ("&No super or subscript", None,      RichTextLineEdit.NoSuperOrSubscript,      format.verticalAlignment() ==      QTextCharFormat.AlignNormal),     ("Su&perscript", None, RichTextLineEdit.Superscript,      format.verticalAlignment() ==      QTextCharFormat.AlignSuperScript),     ("Subs&cript", None, RichTextLineEdit.Subscript,      format.verticalAlignment() ==      QTextCharFormat.AlignSubScript)):    action = menu.addAction(text, self.setTextEffect)    if shortcut is not None:     action.setShortcut(QKeySequence(shortcut))    action.setData(data)    action.setCheckable(True)    action.setChecked(checked)   self.ensureCursorVisible()   menu.exec_(self.viewport().mapToGlobal(      self.cursorRect().center()))  def setTextEffect(self):   action = self.sender()   if action is not None and isinstance(action, QAction):    what = action.data()    if what == RichTextLineEdit.Bold:     self.toggleBold()     return    if what == RichTextLineEdit.Italic:     self.toggleItalic()     return    if what == RichTextLineEdit.Underline:     self.toggleUnderline()     return    format = self.currentCharFormat()    if what == RichTextLineEdit.Monospaced:     format.setFontFamily(self.monofamily)    elif what == RichTextLineEdit.Serif:     format.setFontFamily(self.seriffamily)    elif what == RichTextLineEdit.Sans:     format.setFontFamily(self.sansfamily)    if what == RichTextLineEdit.StrikeOut:     format.setFontStrikeOut(not format.fontStrikeOut())    if what == RichTextLineEdit.NoSuperOrSubscript:     format.setVerticalAlignment(       QTextCharFormat.AlignNormal)    elif what == RichTextLineEdit.Superscript:     format.setVerticalAlignment(       QTextCharFormat.AlignSuperScript)    elif what == RichTextLineEdit.Subscript:     format.setVerticalAlignment(       QTextCharFormat.AlignSubScript)    self.mergeCurrentCharFormat(format)  def toSimpleHtml(self):   htmltext = ""   black = QColor(Qt.black)   block = self.document().begin()   while block.isValid():    iterator = block.begin()    while iterator != block.end():     fragment = iterator.fragment()     if fragment.isValid():      format = fragment.charFormat()      family = format.fontFamily()      color = format.foreground().color()           text=html.escape(fragment.text())      if (format.verticalAlignment() ==       QTextCharFormat.AlignSubScript):       text = "<sub>{0}</sub>".format(text)      elif (format.verticalAlignment() ==        QTextCharFormat.AlignSuperScript):       text = "<sup>{0}</sup>".format(text)      if format.fontUnderline():       text = "<u>{0}</u>".format(text)      if format.fontItalic():       text = "<i>{0}</i>".format(text)      if format.fontWeight() > QFont.Normal:       text = "<b>{0}</b>".format(text)      if format.fontStrikeOut():       text = "<s>{0}</s>".format(text)      if color != black or family:       attribs = ""       if color != black:        attribs += ' color="{0}"'.format(color.name())       if family:        attribs += ' face="{0}"'.format(family)       text = "<font{0}>{1}</font>".format(attribs,text)      htmltext += text     iterator += 1    block = block.next()   return htmltext if __name__ == "__main__":  def printout(lineedit):   print(str(lineedit.toHtml()))   print(str(lineedit.toPlainText()))   print(str(lineedit.toSimpleHtml()))      app = QApplication(sys.argv)  lineedit = RichTextLineEdit()  lineedit.returnPressed.connect(lambda:printout(lineedit))  lineedit.show()  lineedit.setWindowTitle("RichTextEdit")  app.exec_()

/home/yrd/eric_workspace/chap16/genericdelegates.py

#!/usr/bin/env python3 from PyQt5.QtCore import (QDate, QSize, Qt) from PyQt5.QtWidgets import (QApplication, QDateEdit, QLineEdit,   QSpinBox, QStyledItemDelegate,QStyle) from PyQt5.QtGui import QColor,QTextDocument import richtextlineedit class GenericDelegate(QStyledItemDelegate):  def __init__(self, parent=None):   super(GenericDelegate, self).__init__(parent)   self.delegates = {}  def insertColumnDelegate(self, column, delegate):   delegate.setParent(self)   self.delegates[column] = delegate  def removeColumnDelegate(self, column):   if column in self.delegates:    del self.delegates[column]  def paint(self, painter, option, index):   delegate = self.delegates.get(index.column())   if delegate is not None:    delegate.paint(painter, option, index)   else:    QStyledItemDelegate.paint(self, painter, option, index)  def createEditor(self, parent, option, index):   delegate = self.delegates.get(index.column())   if delegate is not None:    return delegate.createEditor(parent, option, index)   else:    return QStyledItemDelegate.createEditor(self, parent, option,              index)  def setEditorData(self, editor, index):   delegate = self.delegates.get(index.column())   if delegate is not None:    delegate.setEditorData(editor, index)   else:    QStyledItemDelegate.setEditorData(self, editor, index)  def setModelData(self, editor, model, index):   delegate = self.delegates.get(index.column())   if delegate is not None:    delegate.setModelData(editor, model, index)   else:    QStyledItemDelegate.setModelData(self, editor, model, index) class IntegerColumnDelegate(QStyledItemDelegate):  def __init__(self, minimum=0, maximum=100, parent=None):   super(IntegerColumnDelegate, self).__init__(parent)   self.minimum = minimum   self.maximum = maximum  def createEditor(self, parent, option, index):   spinbox = QSpinBox(parent)   spinbox.setRange(self.minimum, self.maximum)   spinbox.setAlignment(Qt.AlignRight|Qt.AlignVCenter)   return spinbox  def setEditorData(self, editor, index):   value = index.model().data(index, Qt.DisplayRole)   editor.setValue(value)  def setModelData(self, editor, model, index):   editor.interpretText()   model.setData(index, editor.value()) class DateColumnDelegate(QStyledItemDelegate):  def __init__(self, minimum=QDate(),      maximum=QDate.currentDate(),      format="yyyy-MM-dd", parent=None):   super(DateColumnDelegate, self).__init__(parent)   self.minimum = minimum   self.maximum = maximum   self.format = format  def createEditor(self, parent, option, index):   dateedit = QDateEdit(parent)   #dateedit=QDateTimeEdit(parent)   dateedit.setDateRange(self.minimum, self.maximum)   dateedit.setAlignment(Qt.AlignRight|Qt.AlignVCenter)   dateedit.setDisplayFormat(self.format)   dateedit.setCalendarPopup(True)   return dateedit  def setEditorData(self, editor, index):   value = index.model().data(index, Qt.DisplayRole)   #if value.isNull:   editor.setDate(value)   #editor.setDisplayFormat(self.format)  def setModelData(self, editor, model, index):   model.setData(index, editor.date())  def paint(self, painter, option, index):   text = index.model().data(index, Qt.DisplayRole).toString(self.format)   palette = QApplication.palette()   document = QTextDocument()   document.setDefaultFont(option.font)   if option.state & QStyle.State_Selected:    document.setHtml("<font color={0}>{1}</font>".format(palette.highlightedText().color().name(),text))   else:    document.setHtml(text)   painter.save()   color = (palette.highlight().color()      if option.state & QStyle.State_Selected      else QColor(index.model().data(index,         Qt.BackgroundColorRole)))   painter.fillRect(option.rect, color)   painter.translate(option.rect.x(), option.rect.y())   document.drawContents(painter)   painter.restore()   class PlainTextColumnDelegate(QStyledItemDelegate):  def __init__(self, parent=None):   super(PlainTextColumnDelegate, self).__init__(parent)  def createEditor(self, parent, option, index):   lineedit = QLineEdit(parent)   return lineedit  def setEditorData(self, editor, index):   value = index.model().data(index, Qt.DisplayRole)   editor.setText(value)  def setModelData(self, editor, model, index):   model.setData(index, editor.text()) class RichTextColumnDelegate(QStyledItemDelegate):  def __init__(self, parent=None):   super(RichTextColumnDelegate, self).__init__(parent)  def paint(self, painter, option, index):   text = index.model().data(index, Qt.DisplayRole)   palette = QApplication.palette()   document = QTextDocument()   document.setDefaultFont(option.font)   if option.state & QStyle.State_Selected:    document.setHtml("<font color={0}>{1}</font>".format(palette.highlightedText().color().name(),text))   else:    document.setHtml(text)   painter.save()   color = (palette.highlight().color()      if option.state & QStyle.State_Selected      else QColor(index.model().data(index,         Qt.BackgroundColorRole)))   painter.fillRect(option.rect, color)   painter.translate(option.rect.x(), option.rect.y())   document.drawContents(painter)   painter.restore()  def sizeHint(self, option, index):   text = index.model().data(index).toString()   document = QTextDocument()   document.setDefaultFont(option.font)   document.setHtml(text)   return QSize(document.idealWidth() + 5,       option.fontMetrics.height())  def createEditor(self, parent, option, index):   lineedit = richtextlineedit.RichTextLineEdit(parent)   return lineedit  def setEditorData(self, editor, index):   value = index.model().data(index, Qt.DisplayRole)   editor.setHtml(value)  def setModelData(self, editor, model, index):   model.setData(index, editor.toSimpleHtml())

/home/yrd/eric_workspace/chap16/carhirelog.pyw

#!/usr/bin/env python3 import bisect import os import platform import sys from PyQt5.QtCore import (QAbstractTableModel, QDate, QModelIndex,   QVariant, Qt,pyqtSignal) from PyQt5.QtWidgets import (QApplication, QMainWindow,   QShortcut, QTableView) from PyQt5.QtGui import QKeySequence import genericdelegates (LICENSE, CUSTOMER, HIRED, MILEAGEOUT, RETURNED, MILEAGEBACK,  NOTES, MILEAGE, DAYS) = range(9) class CarHireLog(object):  def __init__(self, license, customer, hired, mileageout,      returned=QDate(), mileageback=0, notes=""):   self.license = license  # plain text   self.customer = customer # plain text   self.hired = hired     # QDate   self.mileageout = mileageout   # int   self.returned = returned    # QDate   self.mileageback = mileageback  # int   self.notes = notes   # HTML  def field(self, column):   if column == LICENSE:    return self.license   elif column == CUSTOMER:    return self.customer   elif column == HIRED:    return self.hired   elif column == MILEAGEOUT:    return self.mileageout   elif column == RETURNED:    return self.returned   elif column == MILEAGEBACK:    return self.mileageback   elif column == NOTES:    return self.notes   elif column == MILEAGE:    return self.mileage()   elif column == DAYS:    return self.days()   assert False  def mileage(self):   return (0 if self.mileageback == 0      else self.mileageback - self.mileageout)  def days(self):   return (0 if not self.returned.isValid()      else self.hired.daysTo(self.returned))  def __hash__(self):   return super(CarHireLog, self).__hash__()  def __eq__(self, other):   if self.hired != other.hired:    return False   if self.customer != other.customer:    return False   if self.license != other.license:    return False   return id(self) == id(other)  def __lt__(self, other):   if self.hired < other.hired:    return True   if self.customer < other.customer:    return True   if self.license < other.license:    return True   return id(self) < id(other) class CarHireModel(QAbstractTableModel):  dataChanged = pyqtSignal(QModelIndex,QModelIndex)  def __init__(self, parent=None):   super(CarHireModel, self).__init__(parent)   self.logs = []   # Generate fake data   import gzip   import random   import string   surname_data = gzip.open(os.path.join(     os.path.dirname(__file__), "surnames.txt.gz")).read()   surnames = surname_data.decode("utf8").splitlines()   years = ("06 ", "56 ", "07 ", "57 ", "08 ", "58 ")   titles = ("Ms ", "Mr ", "Ms ", "Mr ", "Ms ", "Mr ", "Dr ")   notetexts = ("Returned <font color=red><b>damaged</b></font>",     "Returned with <i>empty fuel tank</i>",     "Customer <b>complained</b> about the <u>engine</u>",     "Customer <b>complained</b> about the <u>gears</u>",     "Customer <b>complained</b> about the <u>clutch</u>",     "Returned <font color=darkred><b>dirty</b></font>",)   today = QDate.currentDate()   for i in range(250):    license = []    for c in range(5):     license.append(random.choice(string.ascii_uppercase))    license = ("".join(license[:2]) + random.choice(years) +       "".join(license[2:]))    customer = random.choice(titles) + random.choice(surnames)    hired = today.addDays(-random.randint(0, 365))    mileageout = random.randint(10000, 30000)    notes = ""    if random.random() >= 0.2:     days = random.randint(1, 21)     returned = hired.addDays(days)     mileageback = (mileageout +         (days * random.randint(30, 300)))     if random.random() > 0.75:      notes = random.choice(notetexts)    else:     returned = QDate()     mileageback = 0    log = CarHireLog(license, customer, hired, mileageout,         returned, mileageback, notes)    bisect.insort(self.logs, log)  def rowCount(self, index=QModelIndex()):   return len(self.logs)  def columnCount(self, index=QModelIndex()):   return 9  def data(self, index, role):   if not index.isValid():    return QVariant()   if role == Qt.DisplayRole:    log = self.logs[index.row()]    value = log.field(index.column())    if (index.column() in (MILEAGEBACK, MILEAGE, DAYS) and     value == 0):     return 0    return value   if (role == Qt.TextAlignmentRole and    index.column() not in (LICENSE, CUSTOMER, NOTES)):    return QVariant(int(Qt.AlignRight|Qt.AlignVCenter))   if role == Qt.BackgroundColorRole:    palette = QApplication.palette()    if index.column() in (LICENSE, MILEAGE, DAYS):     return QVariant(palette.alternateBase())    else:     return QVariant(palette.base())   return QVariant()  def setData(self, index, value, role=Qt.EditRole):   if (index.isValid() and role == Qt.EditRole and    index.column() not in (LICENSE, MILEAGE, DAYS)):    log = self.logs[index.row()]    column = index.column()    if column == CUSTOMER:     log.customer = value    elif column == HIRED:     #log.hired = value.toDate()     log.hired = value    elif column == MILEAGEOUT:     log.mileageout = value    elif column == RETURNED:     #log.returned = value.toDate()     log.returned = value    elif column == MILEAGEBACK:     log.mileageback = value    elif column == NOTES:     log.notes = value    self.dataChanged[QModelIndex,QModelIndex].emit(index,index)    return True   return False  def headerData(self, section, orientation, role):   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 == LICENSE:     return "License"    elif section == CUSTOMER:     return "Customer"    elif section == HIRED:     return "Hired"    elif section == MILEAGEOUT:     return "Mileage #1"    elif section == RETURNED:     return "Returned"    elif section == MILEAGEBACK:     return "Mileage #2"    elif section == DAYS:     return "Days"    elif section == MILEAGE:     return "Miles"    elif section == NOTES:     return "Notes"   return section + 1  def flags(self, index):   flag = QAbstractTableModel.flags(self, index)   if index.column() not in (LICENSE, MILEAGE, DAYS):    flag |= Qt.ItemIsEditable   return flag class HireDateColumnDelegate(genericdelegates.DateColumnDelegate):  def createEditor(self, parent, option, index):   i = index.sibling(index.row(), RETURNED)   self.maximum = i.model().data(i, Qt.DisplayRole).addDays(-1)   return genericdelegates.DateColumnDelegate.createEditor(     self, parent, option, index) class ReturnDateColumnDelegate(genericdelegates.DateColumnDelegate):  def createEditor(self, parent, option, index):   i = index.sibling(index.row(), HIRED)   self.minimum = i.model().data(i, Qt.DisplayRole).addDays(1)   return genericdelegates.DateColumnDelegate.createEditor(     self, parent, option, index) class MileageOutColumnDelegate(genericdelegates.IntegerColumnDelegate):  def createEditor(self, parent, option, index):   i = index.sibling(index.row(), MILEAGEBACK)   maximum = i.model().data(i, Qt.DisplayRole)   self.maximum = 1000000 if maximum == 0 else maximum - 1   return genericdelegates.IntegerColumnDelegate.createEditor(     self, parent, option, index) class MileageBackColumnDelegate(genericdelegates.IntegerColumnDelegate):  def createEditor(self, parent, option, index):   i = index.sibling(index.row(), MILEAGEOUT)   self.minimum = i.model().data(i, Qt.DisplayRole) + 1   return genericdelegates.IntegerColumnDelegate.createEditor(     self, parent, option, index) class MainForm(QMainWindow):  def __init__(self, parent=None):   super(MainForm, self).__init__(parent)   model = CarHireModel(self)   self.view = QTableView()   self.view.setModel(model)   self.view.resizeColumnsToContents()   delegate = genericdelegates.GenericDelegate(self)   delegate.insertColumnDelegate(CUSTOMER,     genericdelegates.PlainTextColumnDelegate())   earliest = QDate.currentDate().addYears(-3)   delegate.insertColumnDelegate(HIRED,     HireDateColumnDelegate(earliest))   delegate.insertColumnDelegate(MILEAGEOUT,     MileageOutColumnDelegate(0, 1000000))   delegate.insertColumnDelegate(RETURNED,     ReturnDateColumnDelegate(earliest))   delegate.insertColumnDelegate(MILEAGEBACK,     MileageBackColumnDelegate(0, 1000000))   delegate.insertColumnDelegate(NOTES,     genericdelegates.RichTextColumnDelegate())   self.view.setItemDelegate(delegate)   self.setCentralWidget(self.view)   QShortcut(QKeySequence("Escape"), self, self.close)   QShortcut(QKeySequence("Ctrl+Q"), self, self.close)   self.setWindowTitle("Car Hire Logs") app = QApplication(sys.argv) form = MainForm() rect = QApplication.desktop().availableGeometry() form.resize(int(rect.width() * 0.7), int(rect.height() * 0.8)) form.move(0, 0) form.show() app.exec_()

运行结果:

python3+PyQt5泛型委托的示例分析

感谢各位的阅读!关于“python3+PyQt5泛型委托的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI