Windowを重複させない方法!!




takkun
どうも!たっくんです!

PySideでツールを作っていると、Windowを1つだけしか出したくないときありませんか!?そんな方法を、模索してみたいと思います!!

どうやるか考えてみる!

Windowを1つしか出したくないっと思った時、グローバルの変数を使う、クラス変数を使うなどなど色々思いつくのですが、、、

なるべく汎用性が
あるようにしたい!

Windowを1つしか出したくないということは、「クラスのインスタンスを1つしか作らせない!」とも考えることができます(*´∀`)

ということは!

以前紹介した「シングルトン」という考え方を導入すると、簡単にWindowを1つしか作れない状態ができそうです(゚∀゚)

Pythonでシングルトン!!

2017.04.13

シングルトンを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を解決する方法!!」のようにコードを以下のように修正します。

metaclass conflictを解決する方法!!

2017.11.07
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