「C MAGAZINE - Qt GUI プログラミング」のコード移植 第2章

昨日の続きのネタが、仮想マシンのCent OSを起動するのが面倒になったので、ホストOSのWindows 7PythonとPyQt4をインストールした。

動作環境

OS Windows 7 Home Premium SP1 (x64)
Python 2.7.4 download
PyQt4 4.10.3 download

第2章のサンプル

FindDialog

良くある文字列検索ダイアログ。

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class FindDialog(QDialog):
    findNext = pyqtSignal(QString, bool)
    findPrev = pyqtSignal(QString, bool)

    def __init__(self, parent=None):
        super(FindDialog, self).__init__(parent)

        self.setWindowTitle("Find")
        self.label = QLabel("Find &what:", self)
        self.lineEdit = QLineEdit(self)
        self.label.setBuddy(self.lineEdit)

        self.caseCheckBox = QCheckBox("Match &case", self)
        self.backwardCheckBox = QCheckBox("Search &backward", self)

        self.findButton = QPushButton("&Find", self)
        self.findButton.setDefault(True)
        self.findButton.setEnabled(False)

        self.closeButton = QPushButton("Close", self)

        self.lineEdit.textChanged.connect(self.enableFindButton)
        self.findButton.clicked.connect(self.findClicked)
        self.closeButton.clicked.connect(self.close)

        topLeftLayout = QHBoxLayout()
        topLeftLayout.addWidget(self.label)
        topLeftLayout.addWidget(self.lineEdit)

        leftLayout = QVBoxLayout()
        leftLayout.addLayout(topLeftLayout)
        leftLayout.addWidget(self.caseCheckBox)
        leftLayout.addWidget(self.backwardCheckBox)

        rightLayout = QVBoxLayout()
        rightLayout.addWidget(self.findButton)
        rightLayout.addWidget(self.closeButton)
        rightLayout.addStretch(1)

        mainLayout = QHBoxLayout(self)
        mainLayout.setMargin(11)
        mainLayout.setSpacing(6)
        mainLayout.addLayout(leftLayout)
        mainLayout.addLayout(rightLayout)

    def findClicked(self):
        text = self.lineEdit.text()
        caseSensitive = self.caseCheckBox.isChecked()

        if self.backwardCheckBox.isChecked():
            self.findPrev.emit(text, caseSensitive)
        else:
            self.findNext.emit(text, caseSensitive)

    def enableFindButton(self, text):
        self.findButton.setEnabled(text != "")

if __name__ == '__main__':
    def prevSearch(text, caseSensitive):
        print "prevSearch: %s, %d" % (text, caseSensitive)

    def nextSearch(text, caseSensitive):
        print "nextSearch: %s, %d" % (text, caseSensitive)

    import sys
    app = QApplication(sys.argv)
    dialog = FindDialog()
    dialog.findPrev.connect(prevSearch)
    dialog.findNext.connect(nextSearch)
    dialog.show()
    app.exec_()
Employee

これも良く見る社員管理クラス。GUIには関係なくシグナル/スロットが使える。

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class Employee(QObject):
    salaryChanged = pyqtSignal(int)

    def __init__(self):
        super(Employee, self).__init__()
        self.mySalary = 0

    def salary(self):
        return self.mySalary

    def setSalary(self, newSalary):
        if newSalary != self.mySalary:
            self.mySalary = newSalary
            self.salaryChanged.emit(self.mySalary)

if __name__ == '__main__':
    def print_salary(salary):
        print "salary: %d\n" % salary

    import sys
    app = QApplication(sys.argv)
    emp = Employee()
    emp.salaryChanged.connect(print_salary)
    emp.setSalary(100)
    emp.setSalary(100)
GotoCellDialogTmpl

Qt Designerで作った初めてのUIテンプレート。

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>GotoCellDialogTmpl</class>
 <widget class="QDialog" name="GotoCellDialogTmpl">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>260</width>
    <height>84</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Go to Cell</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item>
      <widget class="QLabel" name="label">
       <property name="text">
        <string>&amp;Call Location</string>
       </property>
       <property name="buddy">
        <cstring>lineEdit</cstring>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QLineEdit" name="lineEdit"/>
     </item>
    </layout>
   </item>
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout_2">
     <item>
      <spacer name="horizontalSpacer">
       <property name="orientation">
        <enum>Qt::Horizontal</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
         <width>58</width>
         <height>20</height>
        </size>
       </property>
      </spacer>
     </item>
     <item>
      <widget class="QPushButton" name="okButton">
       <property name="enabled">
        <bool>false</bool>
       </property>
       <property name="text">
        <string>OK</string>
       </property>
       <property name="default">
        <bool>true</bool>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="cancelButton">
       <property name="text">
        <string>Cancel</string>
       </property>
      </widget>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <tabstops>
  <tabstop>lineEdit</tabstop>
  <tabstop>okButton</tabstop>
  <tabstop>cancelButton</tabstop>
 </tabstops>
 <resources/>
 <connections>
  <connection>
   <sender>okButton</sender>
   <signal>clicked()</signal>
   <receiver>GotoCellDialogTmpl</receiver>
   <slot>accept()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>116</x>
     <y>59</y>
    </hint>
    <hint type="destinationlabel">
     <x>129</x>
     <y>41</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>cancelButton</sender>
   <signal>clicked()</signal>
   <receiver>GotoCellDialogTmpl</receiver>
   <slot>reject()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>207</x>
     <y>59</y>
    </hint>
    <hint type="destinationlabel">
     <x>129</x>
     <y>41</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>
GotoCellDialog

任意セルへ移動するためのダイアログ。

先ずはUIテンプレートをPythonコンパイル

pyuic4 -o gotocelldialogtmpl.py gotocelldialogtmpl.ui


コンパイルしたコードを使用したダイアログ。

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from gotocelldialogtmpl import *

class GotoCellDialog(QDialog, Ui_GotoCellDialogTmpl):
    def __init__(self, parent=None):
        super(GotoCellDialog, self).__init__(parent)
        
        self.setupUi(self)
        self.lineEdit.textChanged.connect(self.enableOkButton)

        regExp = QRegExp("[A-Za-z][1-9][0-9]{0,2}")
        self.lineEdit.setValidator(QRegExpValidator(regExp, self))

    def enableOkButton(self):
        self.okButton.setEnabled(self.lineEdit.hasAcceptableInput())

if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    dialog = GotoCellDialog()
    dialog.show()
    app.exec_()
参考
http://pyqt.sourceforge.net/Docs/PyQt4/designer.html
SortDialogTmpl

並べ替えダイアログのUIテンプレート。

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>SortDialog</class>
 <widget class="QDialog" name="SortDialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>283</width>
    <height>340</height>
   </rect>
  </property>
  <property name="mouseTracking">
   <bool>false</bool>
  </property>
  <property name="windowTitle">
   <string>Sort</string>
  </property>
  <layout class="QGridLayout" name="gridLayout_4">
   <property name="sizeConstraint">
    <enum>QLayout::SetFixedSize</enum>
   </property>
   <item row="0" column="0">
    <widget class="QGroupBox" name="primaryGroupBox">
     <property name="title">
      <string>&amp;Primary Key</string>
     </property>
     <layout class="QGridLayout" name="gridLayout">
      <item row="0" column="0">
       <widget class="QLabel" name="primaryColumLabel">
        <property name="text">
         <string>Column</string>
        </property>
       </widget>
      </item>
      <item row="0" column="1" rowspan="2">
       <widget class="QComboBox" name="primaryColumnCombo">
        <item>
         <property name="text">
          <string>None</string>
         </property>
        </item>
       </widget>
      </item>
      <item row="3" column="0">
       <widget class="QLabel" name="primaryOrderLabel">
        <property name="text">
         <string>Order</string>
        </property>
       </widget>
      </item>
      <item row="0" column="2">
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>33</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item row="3" column="1" colspan="2">
       <widget class="QComboBox" name="primaryOrderCombo">
        <item>
         <property name="text">
          <string>Ascending</string>
         </property>
        </item>
        <item>
         <property name="text">
          <string>Descending</string>
         </property>
        </item>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
   <item row="0" column="1" rowspan="2">
    <layout class="QVBoxLayout" name="verticalLayout">
     <item>
      <widget class="QPushButton" name="okButton">
       <property name="text">
        <string>OK</string>
       </property>
       <property name="default">
        <bool>true</bool>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="cancelButton">
       <property name="text">
        <string>Cancel</string>
       </property>
      </widget>
     </item>
     <item>
      <spacer name="verticalSpacer_2">
       <property name="orientation">
        <enum>Qt::Vertical</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
         <width>20</width>
         <height>18</height>
        </size>
       </property>
      </spacer>
     </item>
     <item>
      <widget class="QPushButton" name="moreButton">
       <property name="text">
        <string>&amp;More</string>
       </property>
       <property name="checkable">
        <bool>true</bool>
       </property>
      </widget>
     </item>
    </layout>
   </item>
   <item row="1" column="0">
    <spacer name="verticalSpacer">
     <property name="orientation">
      <enum>Qt::Vertical</enum>
     </property>
     <property name="sizeType">
      <enum>QSizePolicy::Expanding</enum>
     </property>
     <property name="sizeHint" stdset="0">
      <size>
       <width>20</width>
       <height>7</height>
      </size>
     </property>
    </spacer>
   </item>
   <item row="2" column="0">
    <widget class="QGroupBox" name="secondaryGroupBox">
     <property name="title">
      <string>&amp;Secondary Key</string>
     </property>
     <layout class="QGridLayout" name="gridLayout_2">
      <item row="0" column="0">
       <widget class="QLabel" name="secondaryColumnLabel">
        <property name="text">
         <string>Column</string>
        </property>
       </widget>
      </item>
      <item row="0" column="1" rowspan="2">
       <widget class="QComboBox" name="secondaryColumnCombo">
        <item>
         <property name="text">
          <string>None</string>
         </property>
        </item>
       </widget>
      </item>
      <item row="3" column="0">
       <widget class="QLabel" name="sconaryOrderLabel">
        <property name="text">
         <string>Order</string>
        </property>
       </widget>
      </item>
      <item row="0" column="2">
       <spacer name="horizontalSpacer_2">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>33</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item row="3" column="1" colspan="2">
       <widget class="QComboBox" name="secondaryOrderCombo">
        <item>
         <property name="text">
          <string>Ascending</string>
         </property>
        </item>
        <item>
         <property name="text">
          <string>Descending</string>
         </property>
        </item>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
   <item row="3" column="0">
    <widget class="QGroupBox" name="tertiaryGroupBox">
     <property name="title">
      <string>&amp;Tertiary Key</string>
     </property>
     <layout class="QGridLayout" name="gridLayout_3">
      <item row="0" column="0">
       <widget class="QLabel" name="tertiaryComunLabel">
        <property name="text">
         <string>Column</string>
        </property>
       </widget>
      </item>
      <item row="0" column="1" rowspan="2">
       <widget class="QComboBox" name="tertiaryColumnCombo">
        <item>
         <property name="text">
          <string>None</string>
         </property>
        </item>
       </widget>
      </item>
      <item row="3" column="0">
       <widget class="QLabel" name="tertiaryOrderLabel">
        <property name="text">
         <string>Order</string>
        </property>
       </widget>
      </item>
      <item row="0" column="2">
       <spacer name="horizontalSpacer_3">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>33</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item row="3" column="1" colspan="2">
       <widget class="QComboBox" name="tertiaryOrderCombo">
        <item>
         <property name="text">
          <string>Ascending</string>
         </property>
        </item>
        <item>
         <property name="text">
          <string>Descending</string>
         </property>
        </item>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
  </layout>
 </widget>
 <tabstops>
  <tabstop>primaryColumnCombo</tabstop>
  <tabstop>primaryOrderCombo</tabstop>
  <tabstop>secondaryColumnCombo</tabstop>
  <tabstop>secondaryOrderCombo</tabstop>
  <tabstop>tertiaryColumnCombo</tabstop>
  <tabstop>tertiaryOrderCombo</tabstop>
  <tabstop>okButton</tabstop>
  <tabstop>cancelButton</tabstop>
  <tabstop>moreButton</tabstop>
 </tabstops>
 <resources/>
 <connections>
  <connection>
   <sender>okButton</sender>
   <signal>clicked()</signal>
   <receiver>SortDialog</receiver>
   <slot>accept()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>230</x>
     <y>23</y>
    </hint>
    <hint type="destinationlabel">
     <x>141</x>
     <y>170</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>cancelButton</sender>
   <signal>clicked()</signal>
   <receiver>SortDialog</receiver>
   <slot>reject()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>230</x>
     <y>57</y>
    </hint>
    <hint type="destinationlabel">
     <x>141</x>
     <y>170</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>moreButton</sender>
   <signal>toggled(bool)</signal>
   <receiver>secondaryGroupBox</receiver>
   <slot>setVisible(bool)</slot>
   <hints>
    <hint type="sourcelabel">
     <x>230</x>
     <y>108</y>
    </hint>
    <hint type="destinationlabel">
     <x>94</x>
     <y>178</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>moreButton</sender>
   <signal>toggled(bool)</signal>
   <receiver>tertiaryGroupBox</receiver>
   <slot>setVisible(bool)</slot>
   <hints>
    <hint type="sourcelabel">
     <x>230</x>
     <y>108</y>
    </hint>
    <hint type="destinationlabel">
     <x>94</x>
     <y>282</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>
SortDialog

拡張機能付き検索ダイアログ。

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from sortdialogtmpl import *

class SortDialog(QDialog, Ui_SortDialog):
    def __init__(self, parent=None):
        super(SortDialog, self).__init__(parent)

        self.setupUi(self)
        self.secondaryGroupBox.hide()
        self.tertiaryGroupBox.hide()
        self.setColumnRange('A', 'Z')

    def setColumnRange(self, first, last):
        self.primaryColumnCombo.clear()
        self.secondaryColumnCombo.clear()
        self.tertiaryColumnCombo.clear()

        self.secondaryColumnCombo.addItem("None")
        self.tertiaryColumnCombo.addItem("None")

        self.primaryColumnCombo.setMinimumSize(self.secondaryColumnCombo.sizeHint())
        ch = ord(first)
        ch_last = ord(last)
        while ch <= ch_last:
            s = chr(ch)
            self.primaryColumnCombo.addItem(s)
            self.secondaryColumnCombo.addItem(s)
            self.tertiaryColumnCombo.addItem(s)
            ch = ch + 1

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    dialog = SortDialog()
    dialog.setColumnRange('C', 'F')
    dialog.show()
    app.exec_()