PySideでツールを作っていると、Windowを1つだけしか出したくないときありませんか!?そんな方法を、模索してみたいと思います!!
どうやるか考えてみる!
Windowを1つしか出したくないっと思った時、グローバルの変数を使う、クラス変数を使うなどなど色々思いつくのですが、、、
なるべく汎用性が
あるようにしたい!
Windowを1つしか出したくないということは、「クラスのインスタンスを1つしか作らせない!」とも考えることができます(*´∀`)
ということは!
以前紹介した「シングルトン」という考え方を導入すると、簡単にWindowを1つしか作れない状態ができそうです(゚∀゚)
シングルトンをQMainWindowに適用する
以前紹介したシングルトンのコードは以下の通りです。
class SingletonType(type): _instance = None def __call__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super(SingletonType, cls).__call__(*args, **kwargs) return cls._instance
このクラスを、QMainWindowなどを継承したクラスのメタクラスに適用します。
from PySide import QtGui class SingletonType(type): _instance = None def __call__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super(SingletonType, cls).__call__(*args, **kwargs) return cls._instance class MainWindow(QtGui.QMainWindow): __metaclass__ = SingletonType def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setWindowTitle('Singleton Window') self.resize(640, 405)
すると、MainWindowはインスタンスが1つしか作れないクラスになります!以下の様に、インスタンスを何個つくってもWindowがたくさん出てくることはありません(`・ω・´)ゞ
metaclass conflict
コードを実行しても、以下の様にエラーしてしまうと思います。
# TypeError: Error when calling the metaclass bases # metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases #
この問題は、先日ご紹介した「metaclass conflictを解決する方法!!」のようにコードを以下のように修正します。
from PySide import QtGui from noconflict import classmaker class SingletonType(type): _instance = None def __call__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super(SingletonType, cls).__call__(*args, **kwargs) return cls._instance class Singleton(object): __metaclass__ = SingletonType class MainWindow(Singleton, QtGui.QMainWindow): __metaclass__ = classmaker() def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setWindowTitle('Singleton Window') self.resize(640, 405)
Metaclassを設定したクラス「Singleton」を用意して、クラス「MainWindow」で多重継承する状態にします。直接記述していた「metaclass」に、モジュールの「classmaker()」を指定することで「metaclass conflict」の問題を解決します!
windowA = MainWindow() windowA.show() windowB = MainWindow() windowB.show()
show()の改善
このままだと、Windowが縮小・最大化されている場合そのままなので、表示をリセットするように「show()」をオーバーライドしてみます。
class MainWindow(Singleton, QtGui.QMainWindow): __metaclass__ = classmaker() def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setWindowTitle('Singleton Window') self.resize(640, 405) def show(self): if self.isMinimized() or self.isMaximized(): self.showNormal() else: super(MainWindow, self).show()
Windowが縮小・最大化されている状態を調べるために「isMinimized()」と「isMaximized()」を使って調べます。どちらかが「True」の場合は「showNormal()」を使って縮小・最大化をキャンセルし、通常の状態に戻します。
縮小・最大化されていない場合は、基底クラスの「show()」を呼び出します。
この状態で下記のコードを実行すると、windowAで縮小されても、windowBでリセットされ通常の表示状態になることが確認できます(`・ω・´)ゞ
windowA = MainWindow() windowA.show() windowA.showMinimized()# 縮小表示にする windowB = MainWindow() windowB.show()# オーバーライドしたshowが実行されリセットされる
ライブラリに「1つしか作れないWindowのクラス」を定義しておけば、いつでも簡単に作れますね!しかも、基底クラスを差し替えるだけでも対応できるケースが多そうです(*´∀`)b