An example of finding unique characters in a string using set and re
testStr = 'AAA 7 BBBB C 1 DDD d Y ZZ 3'
''.join( set(re.findall(r'[a-zA-Z]',testStr)) )
Output:
'ACBDYZd'
Monday, April 30, 2012
Wednesday, February 1, 2012
Qt License Wizard Example
The following is based on the Qt License Wizard Example which creates a complex (non-linear) wizard.
Modified the original example to position the Print button between the Back and Finish buttons rather than the default layout which would place it to the right of the Back button.
Modified the original example to position the Print button between the Back and Finish buttons rather than the default layout which would place it to the right of the Back button.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
PyQt4 conversion of Qt License Wizard Example
A complex Wizard example where the path through the wizard depends
on user choices.
NOTES:
=====
Field Validation
----------------
Fields are only validated if they are registered as mandatory
(denoted by an asterisk after the name) even if they have
been assigned a QRegExValidator.
i.e.
registerField("evaluate.email*", emailEdit) - validates
registerField("evaluate.email", emailEdit) - no validation
Button Positioning
------------------
If you want to change the order in which the buttons appear
you can set their layout; however, the layout ignores the visibility
of the custom buttons so need to remove them from the layout to hide them.
(see ConclusionPage._configWizBtns())
Refactored
----------
Refactored _showHelp() - extracted messages to _createHelpMsgs(),
solely as an exercise in using Python dictionaries.
last modified: 2012-01-25 jg
ref:
http://developer.qt.nokia.com/doc/qt-4.8/dialogs-licensewizard.html
'''
from PyQt4.QtGui import (QApplication, QWizard, QWizardPage, QPixmap, QLabel,
QRadioButton, QVBoxLayout, QLineEdit, QGridLayout,
QRegExpValidator, QCheckBox, QPrinter, QPrintDialog,
QMessageBox)
from PyQt4.QtCore import (pyqtSlot, QRegExp)
import qrc_licwiz
class LicenseWizard(QWizard):
NUM_PAGES = 5
(PageIntro, PageEvaluate, PageRegister, PageDetails,
PageConclusion) = range(NUM_PAGES)
def __init__(self, parent=None):
super(LicenseWizard, self).__init__(parent)
self.setPage(self.PageIntro, IntroPage(self))
self.setPage(self.PageEvaluate, EvaluatePage())
self.setPage(self.PageRegister, RegisterPage())
self.setPage(self.PageDetails, DetailsPage())
self.setPage(self.PageConclusion, ConclusionPage())
self.setStartId(self.PageIntro)
# images won't show in Windows 7 if style not set
self.setWizardStyle(self.ModernStyle)
self.setOption(self.HaveHelpButton, True)
self.setPixmap(QWizard.LogoPixmap, QPixmap(":/images/logo.png"))
# set up help messages
self._lastHelpMsg = ''
self._helpMsgs = self._createHelpMsgs()
self.helpRequested.connect(self._showHelp)
self.setWindowTitle(self.tr("License Wizard"))
def _createHelpMsgs(self):
msgs = {}
msgs[self.PageIntro] = self.tr(
"The decision you make here will affect which page you "
"get to see next.")
msgs[self.PageEvaluate] = self.tr(
"Make sure to provide a valid email address, such as "
"toni.buddenbrook@example.de.")
msgs[self.PageRegister] = self.tr(
"If you don't provide an upgrade key, you will be "
"asked to fill in your details.")
msgs[self.PageDetails] = self.tr(
"Make sure to provide a valid email address, such as "
"thomas.gradgrind@example.co.uk.")
msgs[self.PageConclusion] = self.tr(
"You must accept the terms and conditions of the "
"license to proceed.")
msgs[self.NUM_PAGES + 1] = self.tr("Sorry, I already gave what help I could. "
"\nMaybe you should try asking a human?")
return msgs
@pyqtSlot()
def _showHelp(self):
# get the help message for the current page
msg = self._helpMsgs[self.currentId()]
# if same as last message, display alternate message
if msg == self._lastHelpMsg:
msg = self._helpMsgs[self.NUM_PAGES + 1]
QMessageBox.information(self,
self.tr("License Wizard Help"),
msg)
self._lastHelpMsg = msg
class IntroPage(QWizardPage):
def __init__(self, parent=None):
super(IntroPage, self).__init__(parent)
self.setTitle(self.tr("Introduction"))
self.setPixmap(QWizard.WatermarkPixmap, QPixmap(":/images/watermark.png"))
topLabel = QLabel(self.tr("This Wizard will help you register your copy of "
"Super Product One™ or start "
"evaluating the product."))
topLabel.setWordWrap(True)
regRBtn = QRadioButton(self.tr("&Register your copy"))
self.evalRBtn = QRadioButton(self.tr("&Evaluate the product for 30 days"))
regRBtn.setChecked(True)
layout = QVBoxLayout()
layout.addWidget(topLabel)
layout.addWidget(regRBtn)
layout.addWidget(self.evalRBtn)
self.setLayout(layout)
def nextId(self):
if self.evalRBtn.isChecked():
return LicenseWizard.PageEvaluate
else:
return LicenseWizard.PageRegister
class EvaluatePage(QWizardPage):
def __init__(self, parent=None):
super(EvaluatePage, self).__init__(parent)
self.setTitle(self.tr("Evaluate Super Product One™"))
self.setSubTitle(self.tr("Please fill both fields. \nMake sure to provide "
"a valid email address (e.g. john.smith@example.com)"))
nameLabel = QLabel("Name: ")
nameEdit = QLineEdit()
nameLabel.setBuddy(nameEdit)
emailLabel = QLabel(self.tr("&Email address: "))
emailEdit = QLineEdit()
emailEdit.setValidator(QRegExpValidator(
QRegExp(".*@.*"), self))
emailLabel.setBuddy(emailEdit)
self.registerField("evaluate.name*", nameEdit)
self.registerField("evaluate.email*", emailEdit)
grid = QGridLayout()
grid.addWidget(nameLabel, 0, 0)
grid.addWidget(nameEdit, 0, 1)
grid.addWidget(emailLabel, 1, 0)
grid.addWidget(emailEdit, 1, 1)
self.setLayout(grid)
def nextId(self):
return LicenseWizard.PageConclusion
class RegisterPage(QWizardPage):
def __init__(self, parent=None):
super(RegisterPage, self).__init__(parent)
self.setTitle(self.tr("Register Your Copy of Super Product One™"))
self.setSubTitle(self.tr("If you have an upgrade key, please fill in "
"the appropriate field."))
nameLabel = QLabel(self.tr("N&ame"))
nameEdit = QLineEdit()
nameLabel.setBuddy(nameEdit)
upgradeKeyLabel = QLabel(self.tr("&Upgrade key"))
self.upgradeKeyEdit = QLineEdit()
upgradeKeyLabel.setBuddy(self.upgradeKeyEdit)
self.registerField("register.name*", nameEdit)
self.registerField("register.upgradeKey", self.upgradeKeyEdit)
grid = QGridLayout()
grid.addWidget(nameLabel, 0, 0)
grid.addWidget(nameEdit, 0, 1)
grid.addWidget(upgradeKeyLabel, 1, 0)
grid.addWidget(self.upgradeKeyEdit, 1, 1)
self.setLayout(grid)
def nextId(self):
if len(self.upgradeKeyEdit.text()) > 0 :
return LicenseWizard.PageConclusion
else:
return LicenseWizard.PageDetails
class DetailsPage(QWizardPage):
def __init__(self, parent=None):
super(DetailsPage, self).__init__(parent)
self.setTitle(self.tr("Fill in Your Details"))
self.setSubTitle(self.tr("Please fill all three fields. /n"
"Make sure to provide a valid "
"email address (e.g., tanaka.aya@example.com)."))
coLabel = QLabel(self.tr("&Company name: "))
coEdit = QLineEdit()
coLabel.setBuddy(coEdit)
emailLabel = QLabel(self.tr("&Email address: "))
emailEdit = QLineEdit()
emailEdit.setValidator(QRegExpValidator(QRegExp(".*@.*"), self))
emailLabel.setBuddy(emailEdit)
postLabel = QLabel(self.tr("&Postal address: "))
postEdit = QLineEdit()
postLabel.setBuddy(postEdit)
self.registerField("details.company*", coEdit)
self.registerField("details.email*", emailEdit)
self.registerField("details.postal*", postEdit)
grid = QGridLayout()
grid.addWidget(coLabel, 0, 0)
grid.addWidget(coEdit, 0, 1)
grid.addWidget(emailLabel, 1, 0)
grid.addWidget(emailEdit, 1, 1)
grid.addWidget(postLabel, 2, 0)
grid.addWidget(postEdit, 2, 1)
self.setLayout(grid)
def nextId(self):
return LicenseWizard.PageConclusion
class ConclusionPage(QWizardPage):
def __init__(self, parent=None):
super(ConclusionPage, self).__init__(parent)
self.setTitle(self.tr("Complete Your Registration"))
self.setPixmap(QWizard.WatermarkPixmap, QPixmap(":/images/watermark.png"))
self.bottomLabel = QLabel()
self.bottomLabel.setWordWrap(True)
agreeBox = QCheckBox(self.tr("I agree to the terms of the license."))
self.registerField("conclusion.agree*", agreeBox)
vbox = QVBoxLayout()
vbox.addWidget(self.bottomLabel)
vbox.addWidget(agreeBox)
self.setLayout(vbox)
def nextId(self):
return -1
def initializePage(self):
licenseText = ''
if self.wizard().hasVisitedPage(LicenseWizard.PageEvaluate):
licenseText = self.tr("Evaluation License Agreement: "
"You can use this software for 30 days and make one "
"backup, but you are not allowed to distribute it.")
elif self.wizard().hasVisitedPage(LicenseWizard.PageDetails):
licenseText = self.tr("First-Time License Agreement: "
"You can use this software subject to the license "
"you will receive by email.")
else:
licenseText = self.tr("Upgrade License Agreement: "
"This software is licensed under the terms of your "
"current license.")
self.bottomLabel.setText(licenseText)
def setVisible(self, visible):
# only show the 'Print' button on the last page
QWizardPage.setVisible(self, visible)
if visible:
self.wizard().setButtonText(QWizard.CustomButton1, self.tr("&Print"))
self.wizard().setOption(QWizard.HaveCustomButton1, True)
self.wizard().customButtonClicked.connect(self._printButtonClicked)
self._configWizBtns(True)
else:
# only disconnect if button has been assigned and connected
btn = self.wizard().button(QWizard.CustomButton1)
if len(btn.text()) > 0:
self.wizard().customButtonClicked.disconnect(self._printButtonClicked)
self.wizard().setOption(QWizard.HaveCustomButton1, False)
self._configWizBtns(False)
def _configWizBtns(self, state):
# position the Print button (CustomButton1) before the Finish button
if state:
btnList = [QWizard.Stretch, QWizard.BackButton, QWizard.NextButton,
QWizard.CustomButton1, QWizard.FinishButton,
QWizard.CancelButton, QWizard.HelpButton]
self.wizard().setButtonLayout(btnList)
else:
# remove it if it's not visible
btnList = [QWizard.Stretch, QWizard.BackButton, QWizard.NextButton,
QWizard.FinishButton,
QWizard.CancelButton, QWizard.HelpButton]
self.wizard().setButtonLayout(btnList)
@pyqtSlot()
def _printButtonClicked(self):
printer = QPrinter()
dialog = QPrintDialog(printer, self)
if dialog.exec():
QMessageBox.warning(self,
self.tr("Print License"),
self.tr("As an environment friendly measure, the "
"license text will not actually be printed."))
# main ========================================================================
def main():
import sys
app = QApplication(sys.argv)
wiz = LicenseWizard()
wiz.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Labels:
Qt Wizards
Tuesday, January 31, 2012
Qt 4.8 Basic Sort/Filter Model Example - Part 1
The following code is based on the Qt 4.8 Basic Sort/Filter Model Example; listed under Qt Item Views Examples. The original example has one class, Window, and is split over two files: main.cpp and window.cpp with associated header files. Here they've been combined in one module file built up in three stages.
The example demonstrates the use of QSortProxyModel, QStandardItemModel and QTreeView. One standard item model is used to create two views containing the same data. The second view uses a proxy model to allow sorting and filtering. Changes in the second view are not reflected in the first view even though the underlying models are the same.
Part 1 creates the basic GUI.
The example demonstrates the use of QSortProxyModel, QStandardItemModel and QTreeView. One standard item model is used to create two views containing the same data. The second view uses a proxy model to allow sorting and filtering. Changes in the second view are not reflected in the first view even though the underlying models are the same.
Part 1 creates the basic GUI.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
PyQt4 conversion of Qt Basic Sort/Filter Example
The Basic Sort/Filter Model example illustrates how to use
QSortFilterProxyModel to perform basic sorting and filtering.
BEHAVIOUR:
=========
GUI is laid out. The initial conditions for each widget
are in place. Both the case sentivity checkboxes are checked.
The Filter Column combobox is set to 'Sender'.
NOTES:
=====
Create Window class and implement methods to build the GUI
(refactored the original C++ to split the creation of the
various screen areas up for easier reading.)
Create signal/slot connections, adding stub methods for the slots.
last modified: 2012-01-30 jg
ref:
http://developer.qt.nokia.com/doc/qt-4.8/itemviews-basicsortfiltermodel.html
'''
from PyQt4.QtGui import (QApplication, QWidget, QGroupBox, QHBoxLayout, QLabel,
QVBoxLayout, QGridLayout, QTreeView, QSortFilterProxyModel,
QCheckBox, QLineEdit, QComboBox, QStandardItemModel)
from PyQt4.QtCore import (Qt, pyqtSlot, QRegExp)
class Window(QWidget):
def __init__(self, parent=None): # initialise base class
super(Window, self).__init__(parent)
layout = QVBoxLayout()
layout.addWidget(self._createSourcePanel())
layout.addWidget(self._createProxyPanel())
self.setLayout(layout)
# private methods ---------------------------------------------------------
def _createSourcePanel(self):
sourceGroupBox = QGroupBox(self.tr("Original Model"))
sourceView = QTreeView()
sourceView.setRootIsDecorated(False)
sourceView.setAlternatingRowColors(True)
sourceLayout = QHBoxLayout()
sourceLayout.addWidget(sourceView)
sourceGroupBox.setLayout(sourceLayout)
return sourceGroupBox
def _createProxyPanel(self):
proxyGroupBox = QGroupBox(self.tr("Sorted/Filter Model"))
proxyView = QTreeView()
proxyView.setRootIsDecorated(False)
proxyView.setAlternatingRowColors(True)
proxyView.sortByColumn(1, Qt.AscendingOrder)
proxyModel = QSortFilterProxyModel()
proxyModel.setDynamicSortFilter(True)
proxyView.setModel(proxyModel)
proxyView.setSortingEnabled(True) # click col hdr to sort
proxyLayout = QVBoxLayout()
proxyLayout.addWidget(proxyView)
proxyLayout.addLayout(self._createProxyFilterPanel())
proxyGroupBox.setLayout(proxyLayout)
return proxyGroupBox
def _createProxyFilterPanel(self):
sortCaseSensitivityCheckBox = QCheckBox(self.tr("Case sensitive sorting"));
sortCaseSensitivityCheckBox.setChecked(True)
filterCaseSensitivityCheckBox = QCheckBox(self.tr("Case sensitive filter"));
filterCaseSensitivityCheckBox.setChecked(True)
filterPatternLineEdit = QLineEdit();
filterPatternLabel = QLabel(self.tr("&Filter pattern:"));
filterPatternLabel.setBuddy(filterPatternLineEdit);
filterSyntaxComboBox = QComboBox();
filterSyntaxComboBox.addItem(self.tr("Regular expression"), QRegExp.RegExp);
filterSyntaxComboBox.addItem(self.tr("Wildcard"), QRegExp.Wildcard);
filterSyntaxComboBox.addItem(self.tr("Fixed string"), QRegExp.FixedString);
filterSyntaxLabel = QLabel(self.tr("Filter &syntax:"));
filterSyntaxLabel.setBuddy(filterSyntaxComboBox);
filterColumnComboBox = QComboBox();
filterColumnComboBox.addItem(self.tr("Subject"));
filterColumnComboBox.addItem(self.tr("Sender"));
filterColumnComboBox.addItem(self.tr("Date"));
filterColumnComboBox.setCurrentIndex(1)
filterColumnLabel = QLabel(self.tr("Filter &column:"));
filterColumnLabel.setBuddy(filterColumnComboBox);
# connect signals/slots for event handling
filterPatternLineEdit.textChanged.connect(self._filterRegExpChanged)
filterSyntaxComboBox.currentIndexChanged.connect(self._filterRegExpChanged)
filterColumnComboBox.currentIndexChanged.connect(self._filterColumnChanged)
filterCaseSensitivityCheckBox.toggled.connect(self._filterRegExpChanged)
sortCaseSensitivityCheckBox.toggled.connect(self._sortChanged)
grid = QGridLayout()
grid.addWidget(filterPatternLabel, 0, 0)
grid.addWidget(filterPatternLineEdit, 0, 1)
grid.addWidget(filterSyntaxLabel, 1, 0)
grid.addWidget(filterSyntaxComboBox, 1, 1)
grid.addWidget(filterColumnLabel, 2, 0)
grid.addWidget(filterColumnComboBox, 2, 1)
grid.addWidget(filterCaseSensitivityCheckBox, 3, 0)
grid.addWidget(sortCaseSensitivityCheckBox, 3, 1, Qt.AlignRight)
return grid
# private slots -----------------------------------------------------------
@pyqtSlot()
def _filterRegExpChanged(self):
pass
@pyqtSlot()
def _filterColumnChanged(self):
pass
@pyqtSlot()
def _sortChanged(self):
pass
# main ========================================================================
def main():
import sys
app = QApplication(sys.argv)
mw = Window()
mw.setWindowTitle("Basic Sort/Filter Model - Part 1")
mw.resize(500, 450)
mw.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Labels:
Qt Basic Sort/Filter
Qt 4.8 Basic Sort/Filter Model Example - Part 2
In Part 2 the underlying model is created and populated with demo data. The proxy view is initially set to sort alphabetically on the Sender column.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
PyQt4 conversion of Qt Basic Sort/Filter Example
The Basic Sort/Filter Model example illustrates how to use
QSortFilterProxyModel to perform basic sorting and filtering.
BEHAVIOUR:
=========
Data has been added, with both panels using the same
data model.
The data in the Sort/Filter panel is sorted by 'Sender'.
Clicking the column headers re-sorts the data.
NOTES:
=====
Changes to Window class:
- added public method setSourceModel()
- re-defined proxyView and sourceView as attributes
Module:
- added createMailModel()
- added addMail()
- modified main() to set the windows source model
last modified: 2012-01-30 jg
ref:
http://developer.qt.nokia.com/doc/qt-4.8/itemviews-basicsortfiltermodel.html
'''
from PyQt4.QtGui import (QApplication, QWidget, QGroupBox, QHBoxLayout, QLabel,
QVBoxLayout, QGridLayout, QTreeView, QSortFilterProxyModel,
QCheckBox, QLineEdit, QComboBox, QStandardItemModel)
from PyQt4.QtCore import (Qt, pyqtSlot, QRegExp, QDateTime, QDate, QTime)
class Window(QWidget):
def __init__(self, parent=None): # initialise base class
super(Window, self).__init__(parent)
layout = QVBoxLayout()
layout.addWidget(self._createSourcePanel())
layout.addWidget(self._createProxyPanel())
self.setLayout(layout)
# public methods ----------------------------------------------------------
def setSourceModel(self, model):
self._proxyModel.setSourceModel(model)
self._sourceView.setModel(model)
# private methods ---------------------------------------------------------
def _createSourcePanel(self):
sourceGroupBox = QGroupBox(self.tr("Original Model"))
self._sourceView = QTreeView()
self._sourceView.setRootIsDecorated(False)
self._sourceView.setAlternatingRowColors(True)
sourceLayout = QHBoxLayout()
sourceLayout.addWidget(self._sourceView)
sourceGroupBox.setLayout(sourceLayout)
return sourceGroupBox
def _createProxyPanel(self):
proxyGroupBox = QGroupBox(self.tr("Sorted/Filter Model"))
proxyView = QTreeView()
proxyView.setRootIsDecorated(False)
proxyView.setAlternatingRowColors(True)
proxyView.sortByColumn(1, Qt.AscendingOrder)
self._proxyModel = QSortFilterProxyModel()
self._proxyModel.setDynamicSortFilter(True)
proxyView.setModel(self._proxyModel)
proxyView.setSortingEnabled(True) # click col hdr to sort
proxyLayout = QVBoxLayout()
proxyLayout.addWidget(proxyView)
proxyLayout.addLayout(self._createProxyFilterPanel())
proxyGroupBox.setLayout(proxyLayout)
return proxyGroupBox
def _createProxyFilterPanel(self):
sortCaseSensitivityCheckBox = QCheckBox(self.tr("Case sensitive sorting"));
sortCaseSensitivityCheckBox.setChecked(True)
filterCaseSensitivityCheckBox = QCheckBox(self.tr("Case sensitive filter"));
filterCaseSensitivityCheckBox.setChecked(True)
filterPatternLineEdit = QLineEdit();
filterPatternLabel = QLabel(self.tr("&Filter pattern:"));
filterPatternLabel.setBuddy(filterPatternLineEdit);
filterSyntaxComboBox = QComboBox();
filterSyntaxComboBox.addItem(self.tr("Regular expression"), QRegExp.RegExp);
filterSyntaxComboBox.addItem(self.tr("Wildcard"), QRegExp.Wildcard);
filterSyntaxComboBox.addItem(self.tr("Fixed string"), QRegExp.FixedString);
filterSyntaxLabel = QLabel(self.tr("Filter &syntax:"));
filterSyntaxLabel.setBuddy(filterSyntaxComboBox);
filterColumnComboBox = QComboBox();
filterColumnComboBox.addItem(self.tr("Subject"));
filterColumnComboBox.addItem(self.tr("Sender"));
filterColumnComboBox.addItem(self.tr("Date"));
filterColumnComboBox.setCurrentIndex(1)
filterColumnLabel = QLabel(self.tr("Filter &column:"));
filterColumnLabel.setBuddy(filterColumnComboBox);
# connect signals/slots for event handling
filterPatternLineEdit.textChanged.connect(self._filterRegExpChanged)
filterSyntaxComboBox.currentIndexChanged.connect(self._filterRegExpChanged)
filterColumnComboBox.currentIndexChanged.connect(self._filterColumnChanged)
filterCaseSensitivityCheckBox.toggled.connect(self._filterRegExpChanged)
sortCaseSensitivityCheckBox.toggled.connect(self._sortChanged)
grid = QGridLayout()
grid.addWidget(filterPatternLabel, 0, 0)
grid.addWidget(filterPatternLineEdit, 0, 1)
grid.addWidget(filterSyntaxLabel, 1, 0)
grid.addWidget(filterSyntaxComboBox, 1, 1)
grid.addWidget(filterColumnLabel, 2, 0)
grid.addWidget(filterColumnComboBox, 2, 1)
grid.addWidget(filterCaseSensitivityCheckBox, 3, 0)
grid.addWidget(sortCaseSensitivityCheckBox, 3, 1, Qt.AlignRight)
return grid
# private slots -----------------------------------------------------------
@pyqtSlot()
def _filterRegExpChanged(self):
pass
@pyqtSlot()
def _filterColumnChanged(self):
pass
@pyqtSlot()
def _sortChanged(self):
pass
# createMailModel() ==========================================================
def createMailModel(parent):
# create, populate and return a 'QStandardItemModel' object
model = QStandardItemModel(0, 3, parent)
model.setHeaderData(0, Qt.Horizontal, parent.tr("Subject"))
model.setHeaderData(1, Qt.Horizontal, parent.tr("Sender"))
model.setHeaderData(2, Qt.Horizontal, parent.tr("Date"))
addMail(model, "Happy New Year!", "Grace K. ",
QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
addMail(model, "Radically new concept", "Grace K. ",
QDateTime(QDate(2006, 12, 22), QTime(9, 44)))
addMail(model, "Accounts", "pascale@nospam.com",
QDateTime(QDate(2006, 12, 31), QTime(12, 50)))
addMail(model, "Expenses", "Joe Bloggs ",
QDateTime(QDate(2006, 12, 25), QTime(11, 39)))
addMail(model, "Re: Expenses", "Andy ",
QDateTime(QDate(2007, 1, 2), QTime(16, 5)))
addMail(model, "Re: Accounts", "Joe Bloggs ",
QDateTime(QDate(2007, 1, 3), QTime(14, 18)))
addMail(model, "Re: Accounts", "Andy ",
QDateTime(QDate(2007, 1, 3), QTime(14, 26)))
addMail(model, "Sports", "Linda Smith ",
QDateTime(QDate(2007, 1, 5), QTime(11, 33)))
addMail(model, "AW: Sports", "Rolf Newschweinstein ",
QDateTime(QDate(2007, 1, 5), QTime(12, 0)))
addMail(model, "RE: Sports", "Petra Schmidt ",
QDateTime(QDate(2007, 1, 5), QTime(12, 1)))
return model
# addMail() ==================================================================
def addMail(model, subject, sender, date):
# insert a row of data into the given model
model.insertRow(0)
model.setData(model.index(0, 0), subject)
model.setData(model.index(0, 1), sender)
model.setData(model.index(0, 2), date)
# main ========================================================================
def main():
import sys
app = QApplication(sys.argv)
mw = Window()
mw.setSourceModel(createMailModel(mw))
mw.setWindowTitle("Basic Sort/Filter Model - Part 2")
mw.resize(500, 450)
mw.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Labels:
Qt Basic Sort/Filter
Qt 4.8 Basic Sort/Filter Model Example - Part 3
In Part 3 the slots to handle user events are implemented and an initial Regular Expression filter is setup.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
PyQt4 conversion of Qt Basic Sort/Filter Example
The Basic Sort/Filter Model example illustrates how to use
QSortFilterProxyModel to perform basic sorting and filtering.
BEHAVIOUR:
=========
Filter options are implemented. Try different regular expressions,
change the filter column or the sort column.
NOTES:
=====
Implemented private slots:
_filterRegExpChanged()
_filterColumnChanged()
_sortChanged()
Redefined filterSyntaxComboBox, filterCaseSensitivityCheckBox,
sortCaseSensitivityCheckBox, filterSyntaxComboBox and
filterPatternLineEdit as attributes.
last modified: 2012-01-30 jg
ref:
http://developer.qt.nokia.com/doc/qt-4.8/itemviews-basicsortfiltermodel.html
'''
from PyQt4.QtGui import (QApplication, QWidget, QGroupBox, QHBoxLayout, QLabel,
QVBoxLayout, QGridLayout, QTreeView, QSortFilterProxyModel,
QCheckBox, QLineEdit, QComboBox, QStandardItemModel)
from PyQt4.QtCore import (Qt, pyqtSlot, QRegExp, QDateTime, QDate, QTime)
class Window(QWidget):
def __init__(self, parent=None): # initialise base class
super(Window, self).__init__(parent)
layout = QVBoxLayout()
layout.addWidget(self._createSourcePanel())
layout.addWidget(self._createProxyPanel())
self.setLayout(layout)
# set initial filter column and regexp
self._filterColumnComboBox.setCurrentIndex(1)
self._filterPatternLineEdit.setText("Grace|Andy")
# public methods ----------------------------------------------------------
def setSourceModel(self, model):
self._proxyModel.setSourceModel(model)
self._sourceView.setModel(model)
# private methods ---------------------------------------------------------
def _createSourcePanel(self):
sourceGroupBox = QGroupBox(self.tr("Original Model"))
self._sourceView = QTreeView()
self._sourceView.setRootIsDecorated(False)
self._sourceView.setAlternatingRowColors(True)
sourceLayout = QHBoxLayout()
sourceLayout.addWidget(self._sourceView)
sourceGroupBox.setLayout(sourceLayout)
return sourceGroupBox
def _createProxyPanel(self):
proxyGroupBox = QGroupBox(self.tr("Sorted/Filter Model"))
proxyView = QTreeView()
proxyView.setRootIsDecorated(False)
proxyView.setAlternatingRowColors(True)
proxyView.sortByColumn(1, Qt.AscendingOrder)
self._proxyModel = QSortFilterProxyModel()
self._proxyModel.setDynamicSortFilter(True)
proxyView.setModel(self._proxyModel)
proxyView.setSortingEnabled(True) # click col hdr to sort
proxyLayout = QVBoxLayout()
proxyLayout.addWidget(proxyView)
proxyLayout.addLayout(self._createProxyFilterPanel())
proxyGroupBox.setLayout(proxyLayout)
return proxyGroupBox
def _createProxyFilterPanel(self):
self._sortCaseSensitivityCheckBox = QCheckBox(self.tr("Case sensitive sorting"));
self._filterCaseSensitivityCheckBox = QCheckBox(self.tr("Case sensitive filter"));
# default for case sensitivity is true so check boxes
self._sortCaseSensitivityCheckBox.setChecked(True)
self._filterCaseSensitivityCheckBox.setChecked(True)
self._filterPatternLineEdit = QLineEdit();
filterPatternLabel = QLabel(self.tr("&Filter pattern:"));
filterPatternLabel.setBuddy(self._filterPatternLineEdit);
self._filterSyntaxComboBox = QComboBox();
self._filterSyntaxComboBox.addItem(self.tr("Regular expression"), QRegExp.RegExp);
self._filterSyntaxComboBox.addItem(self.tr("Wildcard"), QRegExp.Wildcard);
self._filterSyntaxComboBox.addItem(self.tr("Fixed string"), QRegExp.FixedString);
filterSyntaxLabel = QLabel(self.tr("Filter &syntax:"));
filterSyntaxLabel.setBuddy(self._filterSyntaxComboBox);
self._filterColumnComboBox = QComboBox();
self._filterColumnComboBox.addItem(self.tr("Subject"));
self._filterColumnComboBox.addItem(self.tr("Sender"));
self._filterColumnComboBox.addItem(self.tr("Date"));
filterColumnLabel = QLabel(self.tr("Filter &column:"));
filterColumnLabel.setBuddy(self._filterColumnComboBox);
# connect signals/slots for event handling
self._filterPatternLineEdit.textChanged.connect(self._filterRegExpChanged)
self._filterSyntaxComboBox.currentIndexChanged.connect(self._filterRegExpChanged)
self._filterColumnComboBox.currentIndexChanged.connect(self._filterColumnChanged)
self._filterCaseSensitivityCheckBox.toggled.connect(self._filterRegExpChanged)
self._sortCaseSensitivityCheckBox.toggled.connect(self._sortChanged)
grid = QGridLayout()
grid.addWidget(filterPatternLabel, 0, 0)
grid.addWidget(self._filterPatternLineEdit, 0, 1)
grid.addWidget(filterSyntaxLabel, 1, 0)
grid.addWidget(self._filterSyntaxComboBox, 1, 1)
grid.addWidget(filterColumnLabel, 2, 0)
grid.addWidget(self._filterColumnComboBox, 2, 1)
grid.addWidget(self._filterCaseSensitivityCheckBox, 3, 0)
grid.addWidget(self._sortCaseSensitivityCheckBox, 3, 1, Qt.AlignRight)
return grid
# private slots -----------------------------------------------------------
@pyqtSlot()
def _filterRegExpChanged(self):
# get the QRegEx.PatternSyntax enum value
# 0 - Regular Expression
# 1 - Wildcard
# 2 - Fixed String
syntax = self._filterSyntaxComboBox.itemData(
self._filterSyntaxComboBox.currentIndex())
# get case sensitivity
cs = Qt.CaseInsensitive
if self._filterCaseSensitivityCheckBox.isChecked():
cs = Qt.CaseSensitive
# get user regex pattern
pattern = self._filterPatternLineEdit.text()
# build filter and update proxy model
regExp = QRegExp(pattern, cs, syntax)
self._proxyModel.setFilterRegExp(regExp)
@pyqtSlot()
def _filterColumnChanged(self):
self._proxyModel.setFilterKeyColumn(
self._filterColumnComboBox.currentIndex())
@pyqtSlot()
def _sortChanged(self):
cs = Qt.CaseInsensitive
if self._sortCaseSensitivityCheckBox.isChecked():
cs = Qt.CaseSensitive
self._proxyModel.setSortCaseSensitivity(cs)
# createMailModel() ==========================================================
def createMailModel(parent):
# create, populate and return a 'QStandardItemModel' object
model = QStandardItemModel(0, 3, parent)
model.setHeaderData(0, Qt.Horizontal, parent.tr("Subject"))
model.setHeaderData(1, Qt.Horizontal, parent.tr("Sender"))
model.setHeaderData(2, Qt.Horizontal, parent.tr("Date"))
addMail(model, "Happy New Year!", "Grace K. ",
QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
addMail(model, "Radically new concept", "Grace K. ",
QDateTime(QDate(2006, 12, 22), QTime(9, 44)))
addMail(model, "Accounts", "pascale@nospam.com",
QDateTime(QDate(2006, 12, 31), QTime(12, 50)))
addMail(model, "Expenses", "Joe Bloggs ",
QDateTime(QDate(2006, 12, 25), QTime(11, 39)))
addMail(model, "Re: Expenses", "Andy ",
QDateTime(QDate(2007, 1, 2), QTime(16, 5)))
addMail(model, "Re: Accounts", "Joe Bloggs ",
QDateTime(QDate(2007, 1, 3), QTime(14, 18)))
addMail(model, "Re: Accounts", "Andy ",
QDateTime(QDate(2007, 1, 3), QTime(14, 26)))
addMail(model, "Sports", "Linda Smith ",
QDateTime(QDate(2007, 1, 5), QTime(11, 33)))
addMail(model, "AW: Sports", "Rolf Newschweinstein ",
QDateTime(QDate(2007, 1, 5), QTime(12, 0)))
addMail(model, "RE: Sports", "Petra Schmidt ",
QDateTime(QDate(2007, 1, 5), QTime(12, 1)))
return model
# addMail() ==================================================================
def addMail(model, subject, sender, date):
# insert a row of data into the given model
model.insertRow(0)
model.setData(model.index(0, 0), subject)
model.setData(model.index(0, 1), sender)
model.setData(model.index(0, 2), date)
# main ========================================================================
def main():
import sys
app = QApplication(sys.argv)
mw = Window()
mw.setSourceModel(createMailModel(mw))
mw.setWindowTitle("Basic Sort/Filter Model - Part 3")
mw.resize(500, 450)
mw.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Labels:
Qt Basic Sort/Filter
Monday, January 30, 2012
Qt 4.8 Address Book Example - Part 1
The following code is based on the Qt Address Book Example, one of their ItemViews Examples. The example uses QTableView, QTabWidget and QSortFilterProxyModel to display entries grouped alphabetically on tabbed pages.
The conversion from C++ to PyQt4 is given here in 5 parts; the last includes two modifications to the original code.
The example consists of six C++ classes: Main, MainWindow, AddressWidget, TableModel, NewAddressTab, AddDialog and their corresponding header files.
Part 1 implements the Main and MainWindow classes with stubs for the AddressWidget class. Note that Main is not implemented as a separate class in Python and that all classes are defined in one module file.
The conversion from C++ to PyQt4 is given here in 5 parts; the last includes two modifications to the original code.
The example consists of six C++ classes: Main, MainWindow, AddressWidget, TableModel, NewAddressTab, AddDialog and their corresponding header files.
Part 1 implements the Main and MainWindow classes with stubs for the AddressWidget class. Note that Main is not implemented as a separate class in Python and that all classes are defined in one module file.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
PyQt4 conversion of Qt Address Book (Tabbed) Example
The address book example shows how to use proxy models to display
different views onto data from a single model.
This example provides an address book that allows contacts to be
grouped alphabetically into 9 groups:
ABC, DEF, GHI, ... , VW, ..., XYZ.
This is achieved by using multiple views on the same model, each of
which is filtered using an instance of the QSortFilterProxyModel class.
BEHAVIOUR:
=========
Basic application shell and menus created. Menu items, other than
Exit, are not fully functional.
NOTES:
=====
The application is based on five classes:
Mainwindow
AddressWidget
TabelModel
NewAddressTab
AddDialog
The original C++ is an example rather than a tutorial; each class
occupies its own file with associated include files.
Here the application is built up, starting with MainWindow
stubs for the other classes are defined as encountered, to be
implemented in later steps.
last modified: 2012-01-29 jg
ref:
http://developer.qt.nokia.com/doc/qt-4.8/itemviews-addressbook.html
'''
from PyQt4.QtGui import (QApplication, QMainWindow, QWidget, QTabWidget, QAction,
QFileDialog, QItemSelection)
from PyQt4.QtCore import (pyqtSlot, pyqtSignal)
class AddressWidget(QWidget):
def __init__(self, parent=None): # initialise base class
super(AddressWidget, self).__init__(parent)
# signals --------------------------------------------------------------
selectionChanged = pyqtSignal(QItemSelection, name="selectionChanged")
# public slots ---------------------------------------------------------
@pyqtSlot(str, str)
def addEntry(self, name='', address=''):
pass
@pyqtSlot()
def editEntry(self):
pass
@pyqtSlot()
def removeEntry(self):
pass
# public methods ---------------------------------------------------------
def readFromFile(self, fname):
pass
def writeToFile(self, fname):
pass
class MainWindow(QMainWindow):
def __init__(self, parent=None): # initialise base class
super(MainWindow, self).__init__(parent)
self._addrWidget = AddressWidget()
self.setCentralWidget(self._addrWidget)
self._createFileMenu() # extracted from createMenus()
self._createToolsMenu() # extracted from createMenus()
self._addrWidget.selectionChanged.connect(self._updateActions)
self.setWindowTitle(self.tr("Address Book - Part 1"))
# private methods ---------------------------------------------------------
def _createFileMenu(self):
# create actions
openAct = QAction(self.tr("&Open..."), self)
saveAct = QAction(self.tr("&Save..."), self)
exitAct = QAction(self.tr("E&xit"), self)
# connect signals/slots for event handling
openAct.triggered.connect(self._openFile)
saveAct.triggered.connect(self._saveFile)
exitAct.triggered.connect(QApplication.instance().exit)
# create menu
fileMenu = self.menuBar().addMenu(self.tr("&File"))
fileMenu.addAction(openAct)
fileMenu.addAction(saveAct)
fileMenu.addSeparator()
fileMenu.addAction(exitAct)
def _createToolsMenu(self):
# create actions
addAct = QAction(self.tr("&Add Entry..."), self)
self._editAct = QAction(self.tr("&EditEntry..."), self)
self._removeAct = QAction(self.tr("&Remove Entry"), self)
# connect signals/slots for event handling
addAct.triggered.connect(self._addrWidget.addEntry)
self._editAct.triggered.connect(self._addrWidget.editEntry)
self._removeAct.triggered.connect(self._addrWidget.removeEntry)
# create menu
toolMenu = self.menuBar().addMenu(self.tr("&Tools"))
toolMenu.addAction(addAct)
toolMenu.addAction(self._editAct)
toolMenu.addSeparator()
toolMenu.addAction(self._removeAct)
# set menu item visibility
self._editAct.setEnabled(False)
self._removeAct.setEnabled(False)
# private slots ---------------------------------------------------------
@pyqtSlot()
def _openFile(self):
fname = QFileDialog.getOpenFileName(self)
if fname:
self._addrWidget.readFromFile(fname)
@pyqtSlot()
def _saveFile(self):
fname = QFileDialog.getSaveFileName(self)
if fname:
self._addrWidget.writeToFile(fname)
@pyqtSlot(QItemSelection)
def _updateActions(self, sel):
indexes = sel.indexes()
if indexes:
self._removeAct.setEnabled(True)
self._editAct.setEnabled(True)
else:
self._removeAct.setEnabled(False)
self._editAct.setEnabled(False)
# main ========================================================================
def main():
import sys
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Labels:
Qt Address Book Example
Qt 4.8 Address Book Example - Part 2
Part 2 implements the methods and classes needed to initialize the AddressWidget; this includes the full implementation of the TableModel and NewAddressTab classes.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
PyQt4 conversion of Qt Address Book (Tabbed) Example
The address book example shows how to use proxy models to display
different views onto data from a single model.
This example provides an address book that allows contacts to be
grouped alphabetically into 9 groups:
ABC, DEF, GHI, ... , VW, ..., XYZ.
This is achieved by using multiple views on the same model, each of
which is filtered using an instance of the QSortFilterProxyModel class.
BEHAVIOUR:
=========
Address tabs are visible and can be selected.
NOTES:
=====
Implemented initialisation methods in AddressWidget
which required the implementation of the TableModel
and NewAddressTab classes.
The Qt version of TableModel uses a list of QPair
objects; QPair is not implemented in PyQt4; a 'list
of lists' in the form:
[ [str, str], [str,str], etc.]
is used instead.
last modified: 2012-01-29 jg
ref:
http://developer.qt.nokia.com/doc/qt-4.8/itemviews-addressbook.html
'''
from PyQt4.QtGui import (QApplication, QMainWindow, QWidget, QAction, QFileDialog,
QLabel, QPushButton, QVBoxLayout, QSortFilterProxyModel,
QTableView, QAbstractItemView, QTabWidget, QItemSelection)
from PyQt4.QtCore import (pyqtSlot, pyqtSignal, Qt, QAbstractTableModel, QModelIndex,
QRegExp)
class NewAddressTab(QWidget):
def __init__(self, parent=None): # initialise base class
super(NewAddressTab, self).__init__(parent)
descripLabel = QLabel(self.tr(
"There are currently no contacts in your Address Book"
"\nClick Add to add new contacts."))
addBtn = QPushButton(self.tr("Add"))
addBtn.clicked.connect(self._addEntry)
layout = QVBoxLayout()
layout.addWidget(descripLabel)
layout.addWidget(addBtn)
self.setLayout(layout)
def _addEntry(self):
pass
class TableModel(QAbstractTableModel):
def __init__(self, parent=None, pairs=[['', '']]): # initialise base class
super(TableModel, self).__init__(parent)
self._listOfPairs = pairs
# overridden methods ------------------------------------------------------
def rowCount(self, parent=QModelIndex()):
return len(self._listOfPairs)
def columnCount(self, parent=QModelIndex()):
return 2
def data(self, index, role):
if not index.isValid():
return None
if (index.row() >= len(self._listOfPairs) or
index.row() < 0):
return None
if role == Qt.DisplayRole:
row = index.row()
pair = self._listOfPairs[row]
if index.column() == 0:
return pair[0]
elif index.column == 1:
return pair[1]
return None
def headerData(self, section, orientation, role):
if role != Qt.DisplayRole:
return None
if orientation == Qt.Horizontal:
if section == 0: return self.tr("Name")
elif section == 1: return self.tr("Address")
else: return None
return None
def insertRows(self, position, rows, index=QModelIndex()):
self.beginInsertRows(QModelIndex(), position, position + rows - 1)
for r in range(rows):
pair = [" ", " "]
self._listOfPairs.insert(position, pair)
self.endInsertRows()
return True
def removeRows(self, position, rows, index=QModelIndex()):
self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
for r in range(rows):
# use list slicing to remove rows
self._listOfPairs = (self._listOfPairs[:position] +
self._listOfPairs[position + rows:])
self.endRemoveRows()
return True
def setData(self, index, value, role=Qt.EditRole):
if index.isValid() and role == Qt.EditRole:
row = index.row()
pair = self._listOfPairs(row)
if index.column() == 0:
pair[0] = value
elif index.column() == 1:
pair[1] = value
else:
return False
self._listOfPairs[row] = pair
return True
return False
def flags(self, index):
if not index.isValid():
return Qt.ItemIsEnabled
return Qt.ItemFlags(QAbstractTableModel.flags(self, index) |
Qt.ItemIsEditable)
# public methods ----------------------------------------------------------
def getList(self):
return self._listOfPairs
class AddressWidget(QTabWidget):
def __init__(self, parent=None): # initialise base class
super(AddressWidget, self).__init__(parent)
self._table = TableModel(self)
newAddressTab = NewAddressTab(self)
self.addTab(newAddressTab, "Address Book")
self._setUpTabs()
# signals --------------------------------------------------------------
selectionChanged = pyqtSignal(QItemSelection, name="selectionChanged")
# public slots ---------------------------------------------------------
@pyqtSlot(str, str)
def addEntry(self, name='', address=''):
pass
@pyqtSlot()
def editEntry(self):
pass
@pyqtSlot()
def removeEntry(self):
pass
# public methods ---------------------------------------------------------
def readFromFile(self, fname):
pass
def writeToFile(self, fname):
pass
# private methods ---------------------------------------------------------
def _setUpTabs(self):
groups = ["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ"]
for i in range(len(groups)):
tabName = groups[i] # 'str' is a reserved word in Python
proxyModel = QSortFilterProxyModel(self)
proxyModel.setSourceModel(self._table)
proxyModel.setDynamicSortFilter(True)
tableView = QTableView()
tableView.setModel(proxyModel)
tableView.setSortingEnabled(True)
tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
tableView.horizontalHeader().setStretchLastSection(True)
tableView.verticalHeader().hide()
tableView.setEditTriggers(QAbstractItemView.NoEditTriggers)
tableView.setSelectionMode(QAbstractItemView.SingleSelection)
newStr = "^{0}.*".format(tabName)
proxyModel.setFilterRegExp(QRegExp(newStr, Qt.CaseInsensitive))
proxyModel.setFilterKeyColumn(0)
proxyModel.sort(0, Qt.AscendingOrder)
tableView.selectionModel().selectionChanged.connect(self.selectionChanged)
self.addTab(tableView, tabName)
class MainWindow(QMainWindow):
def __init__(self, parent=None): # initialise base class
super(MainWindow, self).__init__(parent)
self._addrWidget = AddressWidget()
self.setCentralWidget(self._addrWidget)
self._createFileMenu() # extracted from createMenus()
self._createToolsMenu() # extracted from createMenus()
self._addrWidget.selectionChanged.connect(self._updateActions)
self.setWindowTitle(self.tr("Address Book - Part 2"))
# private methods ---------------------------------------------------------
def _createFileMenu(self):
# create actions
openAct = QAction(self.tr("&Open..."), self)
saveAct = QAction(self.tr("&Save..."), self)
exitAct = QAction(self.tr("E&xit"), self)
# connect signals/slots for event handling
openAct.triggered.connect(self._openFile)
saveAct.triggered.connect(self._saveFile)
exitAct.triggered.connect(QApplication.instance().exit)
# create menu
fileMenu = self.menuBar().addMenu(self.tr("&File"))
fileMenu.addAction(openAct)
fileMenu.addAction(saveAct)
fileMenu.addSeparator()
fileMenu.addAction(exitAct)
def _createToolsMenu(self):
# create actions
addAct = QAction(self.tr("&Add Entry..."), self)
self._editAct = QAction(self.tr("&EditEntry..."), self)
self._removeAct = QAction(self.tr("&Remove Entry"), self)
# connect signals/slots for event handling
addAct.triggered.connect(self._addrWidget.addEntry)
self._editAct.triggered.connect(self._addrWidget.editEntry)
self._removeAct.triggered.connect(self._addrWidget.removeEntry)
# create menu
toolMenu = self.menuBar().addMenu(self.tr("&Tools"))
toolMenu.addAction(addAct)
toolMenu.addAction(self._editAct)
toolMenu.addSeparator()
toolMenu.addAction(self._removeAct)
# set menu item visibility
self._editAct.setEnabled(False)
self._removeAct.setEnabled(False)
# private slots ---------------------------------------------------------
@pyqtSlot()
def _openFile(self):
fname = QFileDialog.getOpenFileName(self)
if fname:
self._addrWidget.readFromFile(fname)
@pyqtSlot()
def _saveFile(self):
fname = QFileDialog.getSaveFileName(self)
if fname:
self._addrWidget.writeToFile(fname)
@pyqtSlot(QItemSelection)
def _updateActions(self, sel):
indexes = sel.indexes()
if indexes:
self._removeAct.setEnabled(True)
self._editAct.setEnabled(True)
else:
self._removeAct.setEnabled(False)
self._editAct.setEnabled(False)
# main ========================================================================
def main():
import sys
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Labels:
Qt Address Book Example
Subscribe to:
Posts (Atom)



