QTabWidgetに追加ボタンを作る!!part1

PySide(Qt)でタブを使って、好きなだけタブを増やせるようなツールを作っている時に、、、

追加ボタン欲しい
(゚A゚;)ゴクリ

なんて思いませんか?

メニューからタブの追加とか、別途離れた場所に追加ボタンを作るなら簡単ですが、Tabと一体感がなくてイマイチ、、、

できれば、MayaのScript Editorとかにある感じで、Tabを追加できるボタンを作りたい!

そんな方法をご紹介したいと思います∠( ゚д゚)/

クラスの作成

まずは、QTabWidgetを継承した「TabWidget」のクラスから用意したいと思います!

class TabWidget(QtGui.QTabWidget):
	def __init__(self, *args, **kwargs):
		super(TabWidget, self).__init__(*args, **kwargs)

インスタンス作成時の引数は色々あるので「*args, **kwargs」で受け取って、そのまま基底クラスにぶん投げてしまいます。

タブの追加

次は、初期のタブと、タブ追加の「+」のタブを設定します。サンプルのコードですので、QWidgetのインスタンスをそのまま入れています。必要に応じて変更してください。

from PySide import QtGui

class TabWidget(QtGui.QTabWidget):
	def __init__(self, *args, **kwargs):
		super(TabWidget, self).__init__(*args, **kwargs)
		
		self.insertTab(0, QtGui.QWidget(), 'Tab1')
		self.insertTab(1, QtGui.QWidget(), '+')

タブ追加の実装

タブの追加処理は、外部から干渉されないようにプライベートにしました。こちらはお好みで変更してください。
from PySide import QtGui

class TabWidget(QtGui.QTabWidget):
	def __init__(self, *args, **kwargs):
		super(TabWidget, self).__init__(*args, **kwargs)
		
		self.insertTab(0, QtGui.QWidget(), 'Tab1')
		self.insertTab(1, QtGui.QWidget(), '+')
	
	def __addTab(self, index):
		if index != self.count()-1:
			return
			
		newTabName, status = QtGui.QInputDialog.getText(
				self,
				'Add New Tab',
				'Specify new tab name',
				QtGui.QLineEdit.Normal,
				'Tab%d' % (index+1)
				)
		
		if not status:
			return
		
		self.insertTab(index, QtGui.QWidget(), newTabName)
		self.setCurrentIndex(index)

どの位置にタブを追加するか受け取れるように引数「index」を設けました。次のステップで、Signal「currentChanged」と接続することを想定しており、変更したタブのIndexを引数で渡してきます。

まず、タブの「+」は常に最後尾にありますので、タブの総数を調べて、Indexと比較します。最後尾のタブ出ない場合は、表示の切り替えなのでreturnして終了します(゚∀゚)v

次に、「QtGui.QInputDialog.getText」を使い、ユーザーに新しいタブの名前を決めてもらいます。このメソッドは、新しい名前と、キャンセルされたかどうか同時に返します。もし、変数「status」がnotの場合は、ユーザーがキャンセルした場合なので、新しいタブは作成せずにreturnして終了します(*´ω`*)b

新しい名前が決まったところで、「insertTab」を使ってタブを追加します。この時、「+」のタブは後ろにずれて上書きされるわけではありません。

タブを作成したら、ユーザーが作業しやすいように「作成したタブ」に切り替えます∠( ゚д゚)/

シグナルの設定

このままでは、「+」のタブをクリックしても反応しませんので、Signal & Slotの設定を「__init__」に追加します。

from PySide import QtGui

class TabWidget(QtGui.QTabWidget):
	def __init__(self, *args, **kwargs):
		super(TabWidget, self).__init__(*args, **kwargs)
		
		self.insertTab(0, QtGui.QWidget(), 'Tab1')
		self.insertTab(1, QtGui.QWidget(), '+')
		self.currentChanged.connect(self.__addTab)
	
	def __addTab(self, index):
		if index != self.count()-1:
			return
			
		newTabName, status = QtGui.QInputDialog.getText(
				self,
				'Add New Tab',
				'Specify new tab name',
				QtGui.QLineEdit.Normal,
				'Tab%d' % (index+1)
				)
		
		if not status:
			self.setCurrentIndex(self.__preIndex)
			return
		
		self.insertTab(index, QtGui.QWidget(), newTabName)
		self.setCurrentIndex(index)

タブを変更したら、どのタブに変更したかにかかわらず実行されます。その為に「__addTab」の冒頭でどのタブに変更されたか調べて処理をするようにいたしました。

キャンセルの対応

この状態で実行してみると、タブ追加の際にキャンセルすると「+」のタブに切り替わったまま放置されて、ユーザーがバグったように感じてしまいます。

タブ追加をキャンセルしたら、タブの変更をなかったことにするために、事前のタブの位置を記憶させておく必要があります。

インスタンス変数「__preIndex」を追加して対応していきます。

from PySide import QtGui

class TabWidget(QtGui.QTabWidget):
	def __init__(self, *args, **kwargs):
		super(TabWidget, self).__init__(*args, **kwargs)
		
		self.insertTab(0, QtGui.QWidget(), 'Tab1')
		self.insertTab(1, QtGui.QWidget(), '+')
		self.currentChanged.connect(self.__addTab)
		self.__preIndex = 0
	
	def __addTab(self, index):
		if index != self.count()-1:
			self.__preIndex = index
			return
			
		newTabName, status = QtGui.QInputDialog.getText(
				self,
				'Add New Tab',
				'Specify new tab name',
				QtGui.QLineEdit.Normal,
				'Tab%d' % (index+1)
				)
		
		if not status:
			self.setCurrentIndex(self.__preIndex)
			return
		
		self.insertTab(index, QtGui.QWidget(), newTabName)
		self.setCurrentIndex(index)

「__init__」で、まずインスタンス変数を初期化します。

「__addTab」では、「+」出なかった時に、タブのIndexを記憶します。もしタブ追加がキャンセルされた場合は、「setCurrentIndex」を使い、記憶しておいたIndexのタブに変更します!

これで、MayaライクなTabWidgetの出来上がりです!

しかし、、、

QTabWidgetにある機能と、相性が悪いです。。。

「setTabsClosable」を設定して、タブを閉じれるようにしてみると、、、

「+」のタブに「X」が表示されてしまいます、、、これじゃ、追加なのか削除なのか意味不、、、(;´∀`)

「setMovable」を設定すると、タブをドラッグして並び変えられるようにしてみると、、、

常に右にあるはずの「+」のタブが移動してしまい、挙動が完全にバグ状態、、、(;´∀`)

いずれも、MayaのScript Editorでは「タブの削除ボタン」「ドラッグでタブの並び替え」はできない機能ですので、よしとします!(ぁ