1#!/usr/bin/env python 2 3 4############################################################################# 5## 6## Copyright (C) 2013 Riverbank Computing Limited. 7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 8## All rights reserved. 9## 10## This file is part of the examples of PyQt. 11## 12## $QT_BEGIN_LICENSE:BSD$ 13## You may use this file under the terms of the BSD license as follows: 14## 15## "Redistribution and use in source and binary forms, with or without 16## modification, are permitted provided that the following conditions are 17## met: 18## * Redistributions of source code must retain the above copyright 19## notice, this list of conditions and the following disclaimer. 20## * Redistributions in binary form must reproduce the above copyright 21## notice, this list of conditions and the following disclaimer in 22## the documentation and/or other materials provided with the 23## distribution. 24## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 25## the names of its contributors may be used to endorse or promote 26## products derived from this software without specific prior written 27## permission. 28## 29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 40## $QT_END_LICENSE$ 41## 42############################################################################# 43 44 45import sys 46 47from PyQt5.QtCore import (QByteArray, QDate, QDateTime, QEvent, QPoint, QRect, 48 QRegExp, QSettings, QSize, Qt, QTime, QTimer) 49from PyQt5.QtGui import QColor, QIcon, QRegExpValidator, QValidator 50from PyQt5.QtWidgets import (QAbstractItemView, QAction, QApplication, 51 QComboBox, QDialog, QDialogButtonBox, QFileDialog, QGridLayout, 52 QGroupBox, QHeaderView, QInputDialog, QItemDelegate, QLabel, QLineEdit, 53 QMainWindow, QMessageBox, QStyle, QStyleOptionViewItem, QTableWidget, 54 QTableWidgetItem, QTreeWidget, QTreeWidgetItem, QVBoxLayout) 55 56 57class MainWindow(QMainWindow): 58 def __init__(self, parent=None): 59 super(MainWindow, self).__init__(parent) 60 61 self.settingsTree = SettingsTree() 62 self.setCentralWidget(self.settingsTree) 63 64 self.locationDialog = None 65 66 self.createActions() 67 self.createMenus() 68 69 self.autoRefreshAct.setChecked(True) 70 self.fallbacksAct.setChecked(True) 71 72 self.setWindowTitle("Settings Editor") 73 self.resize(500, 600) 74 75 def openSettings(self): 76 if self.locationDialog is None: 77 self.locationDialog = LocationDialog(self) 78 79 if self.locationDialog.exec_(): 80 settings = QSettings(self.locationDialog.format(), 81 self.locationDialog.scope(), 82 self.locationDialog.organization(), 83 self.locationDialog.application()) 84 self.setSettingsObject(settings) 85 self.fallbacksAct.setEnabled(True) 86 87 def openIniFile(self): 88 fileName, _ = QFileDialog.getOpenFileName(self, "Open INI File", '', 89 "INI Files (*.ini *.conf)") 90 91 if fileName: 92 settings = QSettings(fileName, QSettings.IniFormat) 93 self.setSettingsObject(settings) 94 self.fallbacksAct.setEnabled(False) 95 96 def openPropertyList(self): 97 fileName, _ = QFileDialog.getOpenFileName(self, "Open Property List", 98 '', "Property List Files (*.plist)") 99 100 if fileName: 101 settings = QSettings(fileName, QSettings.NativeFormat) 102 self.setSettingsObject(settings) 103 self.fallbacksAct.setEnabled(False) 104 105 def openRegistryPath(self): 106 path, ok = QInputDialog.getText(self, "Open Registry Path", 107 "Enter the path in the Windows registry:", QLineEdit.Normal, 108 'HKEY_CURRENT_USER\\') 109 110 if ok and path != '': 111 settings = QSettings(path, QSettings.NativeFormat) 112 self.setSettingsObject(settings) 113 self.fallbacksAct.setEnabled(False) 114 115 def about(self): 116 QMessageBox.about(self, "About Settings Editor", 117 "The <b>Settings Editor</b> example shows how to access " 118 "application settings using Qt.") 119 120 def createActions(self): 121 self.openSettingsAct = QAction("&Open Application Settings...", self, 122 shortcut="Ctrl+O", triggered=self.openSettings) 123 124 self.openIniFileAct = QAction("Open I&NI File...", self, 125 shortcut="Ctrl+N", triggered=self.openIniFile) 126 127 self.openPropertyListAct = QAction("Open Mac &Property List...", self, 128 shortcut="Ctrl+P", triggered=self.openPropertyList) 129 if sys.platform != 'darwin': 130 self.openPropertyListAct.setEnabled(False) 131 132 self.openRegistryPathAct = QAction("Open Windows &Registry Path...", 133 self, shortcut="Ctrl+G", triggered=self.openRegistryPath) 134 if sys.platform != 'win32': 135 self.openRegistryPathAct.setEnabled(False) 136 137 self.refreshAct = QAction("&Refresh", self, shortcut="Ctrl+R", 138 enabled=False, triggered=self.settingsTree.refresh) 139 140 self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q", 141 triggered=self.close) 142 143 self.autoRefreshAct = QAction("&Auto-Refresh", self, shortcut="Ctrl+A", 144 checkable=True, enabled=False) 145 self.autoRefreshAct.triggered.connect(self.settingsTree.setAutoRefresh) 146 self.autoRefreshAct.triggered.connect(self.refreshAct.setDisabled) 147 148 self.fallbacksAct = QAction("&Fallbacks", self, shortcut="Ctrl+F", 149 checkable=True, enabled=False, 150 triggered=self.settingsTree.setFallbacksEnabled) 151 152 self.aboutAct = QAction("&About", self, triggered=self.about) 153 154 self.aboutQtAct = QAction("About &Qt", self, 155 triggered=QApplication.instance().aboutQt) 156 157 def createMenus(self): 158 self.fileMenu = self.menuBar().addMenu("&File") 159 self.fileMenu.addAction(self.openSettingsAct) 160 self.fileMenu.addAction(self.openIniFileAct) 161 self.fileMenu.addAction(self.openPropertyListAct) 162 self.fileMenu.addAction(self.openRegistryPathAct) 163 self.fileMenu.addSeparator() 164 self.fileMenu.addAction(self.refreshAct) 165 self.fileMenu.addSeparator() 166 self.fileMenu.addAction(self.exitAct) 167 168 self.optionsMenu = self.menuBar().addMenu("&Options") 169 self.optionsMenu.addAction(self.autoRefreshAct) 170 self.optionsMenu.addAction(self.fallbacksAct) 171 172 self.menuBar().addSeparator() 173 174 self.helpMenu = self.menuBar().addMenu("&Help") 175 self.helpMenu.addAction(self.aboutAct) 176 self.helpMenu.addAction(self.aboutQtAct) 177 178 def setSettingsObject(self, settings): 179 settings.setFallbacksEnabled(self.fallbacksAct.isChecked()) 180 self.settingsTree.setSettingsObject(settings) 181 182 self.refreshAct.setEnabled(True) 183 self.autoRefreshAct.setEnabled(True) 184 185 niceName = settings.fileName() 186 niceName.replace('\\', '/') 187 niceName = niceName.split('/')[-1] 188 189 if not settings.isWritable(): 190 niceName += " (read only)" 191 192 self.setWindowTitle("%s - Settings Editor" % niceName) 193 194 195class LocationDialog(QDialog): 196 def __init__(self, parent=None): 197 super(LocationDialog, self).__init__(parent) 198 199 self.formatComboBox = QComboBox() 200 self.formatComboBox.addItem("Native") 201 self.formatComboBox.addItem("INI") 202 203 self.scopeComboBox = QComboBox() 204 self.scopeComboBox.addItem("User") 205 self.scopeComboBox.addItem("System") 206 207 self.organizationComboBox = QComboBox() 208 self.organizationComboBox.addItem("Trolltech") 209 self.organizationComboBox.setEditable(True) 210 211 self.applicationComboBox = QComboBox() 212 self.applicationComboBox.addItem("Any") 213 self.applicationComboBox.addItem("Application Example") 214 self.applicationComboBox.addItem("Assistant") 215 self.applicationComboBox.addItem("Designer") 216 self.applicationComboBox.addItem("Linguist") 217 self.applicationComboBox.setEditable(True) 218 self.applicationComboBox.setCurrentIndex(3) 219 220 formatLabel = QLabel("&Format:") 221 formatLabel.setBuddy(self.formatComboBox) 222 223 scopeLabel = QLabel("&Scope:") 224 scopeLabel.setBuddy(self.scopeComboBox) 225 226 organizationLabel = QLabel("&Organization:") 227 organizationLabel.setBuddy(self.organizationComboBox) 228 229 applicationLabel = QLabel("&Application:") 230 applicationLabel.setBuddy(self.applicationComboBox) 231 232 self.locationsGroupBox = QGroupBox("Setting Locations") 233 234 self.locationsTable = QTableWidget() 235 self.locationsTable.setSelectionMode(QAbstractItemView.SingleSelection) 236 self.locationsTable.setSelectionBehavior(QAbstractItemView.SelectRows) 237 self.locationsTable.setEditTriggers(QAbstractItemView.NoEditTriggers) 238 self.locationsTable.setColumnCount(2) 239 self.locationsTable.setHorizontalHeaderLabels(("Location", "Access")) 240 self.locationsTable.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) 241 self.locationsTable.horizontalHeader().resizeSection(1, 180) 242 243 self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 244 245 self.formatComboBox.activated.connect(self.updateLocationsTable) 246 self.scopeComboBox.activated.connect(self.updateLocationsTable) 247 self.organizationComboBox.lineEdit().editingFinished.connect(self.updateLocationsTable) 248 self.applicationComboBox.lineEdit().editingFinished.connect(self.updateLocationsTable) 249 self.buttonBox.accepted.connect(self.accept) 250 self.buttonBox.rejected.connect(self.reject) 251 252 locationsLayout = QVBoxLayout() 253 locationsLayout.addWidget(self.locationsTable) 254 self.locationsGroupBox.setLayout(locationsLayout) 255 256 mainLayout = QGridLayout() 257 mainLayout.addWidget(formatLabel, 0, 0) 258 mainLayout.addWidget(self.formatComboBox, 0, 1) 259 mainLayout.addWidget(scopeLabel, 1, 0) 260 mainLayout.addWidget(self.scopeComboBox, 1, 1) 261 mainLayout.addWidget(organizationLabel, 2, 0) 262 mainLayout.addWidget(self.organizationComboBox, 2, 1) 263 mainLayout.addWidget(applicationLabel, 3, 0) 264 mainLayout.addWidget(self.applicationComboBox, 3, 1) 265 mainLayout.addWidget(self.locationsGroupBox, 4, 0, 1, 2) 266 mainLayout.addWidget(self.buttonBox, 5, 0, 1, 2) 267 self.setLayout(mainLayout) 268 269 self.updateLocationsTable() 270 271 self.setWindowTitle("Open Application Settings") 272 self.resize(650, 400) 273 274 def format(self): 275 if self.formatComboBox.currentIndex() == 0: 276 return QSettings.NativeFormat 277 else: 278 return QSettings.IniFormat 279 280 def scope(self): 281 if self.scopeComboBox.currentIndex() == 0: 282 return QSettings.UserScope 283 else: 284 return QSettings.SystemScope 285 286 def organization(self): 287 return self.organizationComboBox.currentText() 288 289 def application(self): 290 if self.applicationComboBox.currentText() == "Any": 291 return '' 292 293 return self.applicationComboBox.currentText() 294 295 def updateLocationsTable(self): 296 self.locationsTable.setUpdatesEnabled(False) 297 self.locationsTable.setRowCount(0) 298 299 for i in range(2): 300 if i == 0: 301 if self.scope() == QSettings.SystemScope: 302 continue 303 304 actualScope = QSettings.UserScope 305 else: 306 actualScope = QSettings.SystemScope 307 308 for j in range(2): 309 if j == 0: 310 if not self.application(): 311 continue 312 313 actualApplication = self.application() 314 else: 315 actualApplication = '' 316 317 settings = QSettings(self.format(), actualScope, 318 self.organization(), actualApplication) 319 320 row = self.locationsTable.rowCount() 321 self.locationsTable.setRowCount(row + 1) 322 323 item0 = QTableWidgetItem() 324 item0.setText(settings.fileName()) 325 326 item1 = QTableWidgetItem() 327 disable = not (settings.childKeys() or settings.childGroups()) 328 329 if row == 0: 330 if settings.isWritable(): 331 item1.setText("Read-write") 332 disable = False 333 else: 334 item1.setText("Read-only") 335 self.buttonBox.button(QDialogButtonBox.Ok).setDisabled(disable) 336 else: 337 item1.setText("Read-only fallback") 338 339 if disable: 340 item0.setFlags(item0.flags() & ~Qt.ItemIsEnabled) 341 item1.setFlags(item1.flags() & ~Qt.ItemIsEnabled) 342 343 self.locationsTable.setItem(row, 0, item0) 344 self.locationsTable.setItem(row, 1, item1) 345 346 self.locationsTable.setUpdatesEnabled(True) 347 348 349class SettingsTree(QTreeWidget): 350 def __init__(self, parent=None): 351 super(SettingsTree, self).__init__(parent) 352 353 self.setItemDelegate(VariantDelegate(self)) 354 355 self.setHeaderLabels(("Setting", "Type", "Value")) 356 self.header().setSectionResizeMode(0, QHeaderView.Stretch) 357 self.header().setSectionResizeMode(2, QHeaderView.Stretch) 358 359 self.settings = None 360 self.refreshTimer = QTimer() 361 self.refreshTimer.setInterval(2000) 362 self.autoRefresh = False 363 364 self.groupIcon = QIcon() 365 self.groupIcon.addPixmap(self.style().standardPixmap(QStyle.SP_DirClosedIcon), 366 QIcon.Normal, QIcon.Off) 367 self.groupIcon.addPixmap(self.style().standardPixmap(QStyle.SP_DirOpenIcon), 368 QIcon.Normal, QIcon.On) 369 self.keyIcon = QIcon() 370 self.keyIcon.addPixmap(self.style().standardPixmap(QStyle.SP_FileIcon)) 371 372 self.refreshTimer.timeout.connect(self.maybeRefresh) 373 374 def setSettingsObject(self, settings): 375 self.settings = settings 376 self.clear() 377 378 if self.settings is not None: 379 self.settings.setParent(self) 380 self.refresh() 381 if self.autoRefresh: 382 self.refreshTimer.start() 383 else: 384 self.refreshTimer.stop() 385 386 def sizeHint(self): 387 return QSize(800, 600) 388 389 def setAutoRefresh(self, autoRefresh): 390 self.autoRefresh = autoRefresh 391 392 if self.settings is not None: 393 if self.autoRefresh: 394 self.maybeRefresh() 395 self.refreshTimer.start() 396 else: 397 self.refreshTimer.stop() 398 399 def setFallbacksEnabled(self, enabled): 400 if self.settings is not None: 401 self.settings.setFallbacksEnabled(enabled) 402 self.refresh() 403 404 def maybeRefresh(self): 405 if self.state() != QAbstractItemView.EditingState: 406 self.refresh() 407 408 def refresh(self): 409 if self.settings is None: 410 return 411 412 # The signal might not be connected. 413 try: 414 self.itemChanged.disconnect(self.updateSetting) 415 except: 416 pass 417 418 self.settings.sync() 419 self.updateChildItems(None) 420 421 self.itemChanged.connect(self.updateSetting) 422 423 def event(self, event): 424 if event.type() == QEvent.WindowActivate: 425 if self.isActiveWindow() and self.autoRefresh: 426 self.maybeRefresh() 427 428 return super(SettingsTree, self).event(event) 429 430 def updateSetting(self, item): 431 key = item.text(0) 432 ancestor = item.parent() 433 434 while ancestor: 435 key = ancestor.text(0) + '/' + key 436 ancestor = ancestor.parent() 437 438 d = item.data(2, Qt.UserRole) 439 self.settings.setValue(key, item.data(2, Qt.UserRole)) 440 441 if self.autoRefresh: 442 self.refresh() 443 444 def updateChildItems(self, parent): 445 dividerIndex = 0 446 447 for group in self.settings.childGroups(): 448 childIndex = self.findChild(parent, group, dividerIndex) 449 if childIndex != -1: 450 child = self.childAt(parent, childIndex) 451 child.setText(1, '') 452 child.setText(2, '') 453 child.setData(2, Qt.UserRole, None) 454 self.moveItemForward(parent, childIndex, dividerIndex) 455 else: 456 child = self.createItem(group, parent, dividerIndex) 457 458 child.setIcon(0, self.groupIcon) 459 dividerIndex += 1 460 461 self.settings.beginGroup(group) 462 self.updateChildItems(child) 463 self.settings.endGroup() 464 465 for key in self.settings.childKeys(): 466 childIndex = self.findChild(parent, key, 0) 467 if childIndex == -1 or childIndex >= dividerIndex: 468 if childIndex != -1: 469 child = self.childAt(parent, childIndex) 470 for i in range(child.childCount()): 471 self.deleteItem(child, i) 472 self.moveItemForward(parent, childIndex, dividerIndex) 473 else: 474 child = self.createItem(key, parent, dividerIndex) 475 child.setIcon(0, self.keyIcon) 476 dividerIndex += 1 477 else: 478 child = self.childAt(parent, childIndex) 479 480 value = self.settings.value(key) 481 if value is None: 482 child.setText(1, 'Invalid') 483 else: 484 child.setText(1, value.__class__.__name__) 485 child.setText(2, VariantDelegate.displayText(value)) 486 child.setData(2, Qt.UserRole, value) 487 488 while dividerIndex < self.childCount(parent): 489 self.deleteItem(parent, dividerIndex) 490 491 def createItem(self, text, parent, index): 492 after = None 493 494 if index != 0: 495 after = self.childAt(parent, index - 1) 496 497 if parent is not None: 498 item = QTreeWidgetItem(parent, after) 499 else: 500 item = QTreeWidgetItem(self, after) 501 502 item.setText(0, text) 503 item.setFlags(item.flags() | Qt.ItemIsEditable) 504 return item 505 506 def deleteItem(self, parent, index): 507 if parent is not None: 508 item = parent.takeChild(index) 509 else: 510 item = self.takeTopLevelItem(index) 511 del item 512 513 def childAt(self, parent, index): 514 if parent is not None: 515 return parent.child(index) 516 else: 517 return self.topLevelItem(index) 518 519 def childCount(self, parent): 520 if parent is not None: 521 return parent.childCount() 522 else: 523 return self.topLevelItemCount() 524 525 def findChild(self, parent, text, startIndex): 526 for i in range(self.childCount(parent)): 527 if self.childAt(parent, i).text(0) == text: 528 return i 529 return -1 530 531 def moveItemForward(self, parent, oldIndex, newIndex): 532 for int in range(oldIndex - newIndex): 533 self.deleteItem(parent, newIndex) 534 535 536class VariantDelegate(QItemDelegate): 537 def __init__(self, parent=None): 538 super(VariantDelegate, self).__init__(parent) 539 540 self.boolExp = QRegExp() 541 self.boolExp.setPattern('true|false') 542 self.boolExp.setCaseSensitivity(Qt.CaseInsensitive) 543 544 self.byteArrayExp = QRegExp() 545 self.byteArrayExp.setPattern('[\\x00-\\xff]*') 546 547 self.charExp = QRegExp() 548 self.charExp.setPattern('.') 549 550 self.colorExp = QRegExp() 551 self.colorExp.setPattern('\\(([0-9]*),([0-9]*),([0-9]*),([0-9]*)\\)') 552 553 self.doubleExp = QRegExp() 554 self.doubleExp.setPattern('') 555 556 self.pointExp = QRegExp() 557 self.pointExp.setPattern('\\((-?[0-9]*),(-?[0-9]*)\\)') 558 559 self.rectExp = QRegExp() 560 self.rectExp.setPattern('\\((-?[0-9]*),(-?[0-9]*),(-?[0-9]*),(-?[0-9]*)\\)') 561 562 self.signedIntegerExp = QRegExp() 563 self.signedIntegerExp.setPattern('-?[0-9]*') 564 565 self.sizeExp = QRegExp(self.pointExp) 566 567 self.unsignedIntegerExp = QRegExp() 568 self.unsignedIntegerExp.setPattern('[0-9]*') 569 570 self.dateExp = QRegExp() 571 self.dateExp.setPattern('([0-9]{,4})-([0-9]{,2})-([0-9]{,2})') 572 573 self.timeExp = QRegExp() 574 self.timeExp.setPattern('([0-9]{,2}):([0-9]{,2}):([0-9]{,2})') 575 576 self.dateTimeExp = QRegExp() 577 self.dateTimeExp.setPattern(self.dateExp.pattern() + 'T' + self.timeExp.pattern()) 578 579 def paint(self, painter, option, index): 580 if index.column() == 2: 581 value = index.model().data(index, Qt.UserRole) 582 if not self.isSupportedType(value): 583 myOption = QStyleOptionViewItem(option) 584 myOption.state &= ~QStyle.State_Enabled 585 super(VariantDelegate, self).paint(painter, myOption, index) 586 return 587 588 super(VariantDelegate, self).paint(painter, option, index) 589 590 def createEditor(self, parent, option, index): 591 if index.column() != 2: 592 return None 593 594 originalValue = index.model().data(index, Qt.UserRole) 595 if not self.isSupportedType(originalValue): 596 return None 597 598 lineEdit = QLineEdit(parent) 599 lineEdit.setFrame(False) 600 601 if isinstance(originalValue, bool): 602 regExp = self.boolExp 603 elif isinstance(originalValue, float): 604 regExp = self.doubleExp 605 elif isinstance(originalValue, int): 606 regExp = self.signedIntegerExp 607 elif isinstance(originalValue, QByteArray): 608 regExp = self.byteArrayExp 609 elif isinstance(originalValue, QColor): 610 regExp = self.colorExp 611 elif isinstance(originalValue, QDate): 612 regExp = self.dateExp 613 elif isinstance(originalValue, QDateTime): 614 regExp = self.dateTimeExp 615 elif isinstance(originalValue, QTime): 616 regExp = self.timeExp 617 elif isinstance(originalValue, QPoint): 618 regExp = self.pointExp 619 elif isinstance(originalValue, QRect): 620 regExp = self.rectExp 621 elif isinstance(originalValue, QSize): 622 regExp = self.sizeExp 623 else: 624 regExp = QRegExp() 625 626 if not regExp.isEmpty(): 627 validator = QRegExpValidator(regExp, lineEdit) 628 lineEdit.setValidator(validator) 629 630 return lineEdit 631 632 def setEditorData(self, editor, index): 633 value = index.model().data(index, Qt.UserRole) 634 if editor is not None: 635 editor.setText(self.displayText(value)) 636 637 def setModelData(self, editor, model, index): 638 if not editor.isModified(): 639 return 640 641 text = editor.text() 642 validator = editor.validator() 643 if validator is not None: 644 state, text, _ = validator.validate(text, 0) 645 if state != QValidator.Acceptable: 646 return 647 648 originalValue = index.model().data(index, Qt.UserRole) 649 650 if isinstance(originalValue, QColor): 651 self.colorExp.exactMatch(text) 652 value = QColor(min(int(self.colorExp.cap(1)), 255), 653 min(int(self.colorExp.cap(2)), 255), 654 min(int(self.colorExp.cap(3)), 255), 655 min(int(self.colorExp.cap(4)), 255)) 656 elif isinstance(originalValue, QDate): 657 value = QDate.fromString(text, Qt.ISODate) 658 if not value.isValid(): 659 return 660 elif isinstance(originalValue, QDateTime): 661 value = QDateTime.fromString(text, Qt.ISODate) 662 if not value.isValid(): 663 return 664 elif isinstance(originalValue, QTime): 665 value = QTime.fromString(text, Qt.ISODate) 666 if not value.isValid(): 667 return 668 elif isinstance(originalValue, QPoint): 669 self.pointExp.exactMatch(text) 670 value = QPoint(int(self.pointExp.cap(1)), 671 int(self.pointExp.cap(2))) 672 elif isinstance(originalValue, QRect): 673 self.rectExp.exactMatch(text) 674 value = QRect(int(self.rectExp.cap(1)), 675 int(self.rectExp.cap(2)), 676 int(self.rectExp.cap(3)), 677 int(self.rectExp.cap(4))) 678 elif isinstance(originalValue, QSize): 679 self.sizeExp.exactMatch(text) 680 value = QSize(int(self.sizeExp.cap(1)), 681 int(self.sizeExp.cap(2))) 682 elif isinstance(originalValue, list): 683 value = text.split(',') 684 else: 685 value = type(originalValue)(text) 686 687 model.setData(index, self.displayText(value), Qt.DisplayRole) 688 model.setData(index, value, Qt.UserRole) 689 690 @staticmethod 691 def isSupportedType(value): 692 return isinstance(value, (bool, float, int, QByteArray, str, QColor, 693 QDate, QDateTime, QTime, QPoint, QRect, QSize, list)) 694 695 @staticmethod 696 def displayText(value): 697 if isinstance(value, (bool, int, QByteArray)): 698 return str(value) 699 if isinstance(value, str): 700 return value 701 elif isinstance(value, float): 702 return '%g' % value 703 elif isinstance(value, QColor): 704 return '(%u,%u,%u,%u)' % (value.red(), value.green(), value.blue(), value.alpha()) 705 elif isinstance(value, (QDate, QDateTime, QTime)): 706 return value.toString(Qt.ISODate) 707 elif isinstance(value, QPoint): 708 return '(%d,%d)' % (value.x(), value.y()) 709 elif isinstance(value, QRect): 710 return '(%d,%d,%d,%d)' % (value.x(), value.y(), value.width(), value.height()) 711 elif isinstance(value, QSize): 712 return '(%d,%d)' % (value.width(), value.height()) 713 elif isinstance(value, list): 714 return ','.join(value) 715 elif value is None: 716 return '<Invalid>' 717 718 return '<%s>' % value 719 720 721if __name__ == '__main__': 722 app = QApplication(sys.argv) 723 mainWin = MainWindow() 724 mainWin.show() 725 sys.exit(app.exec_()) 726