QTabWidget's tab bar behavior?
Matic Kukovec <[email protected]>
| Newsgroups | gmane.comp.python.pyqt-pykde |
|---|---|
| Message-ID | <VI1PR01MB5344312DF836261DBDBF43CDD7030@VI1PR01MB5344.eurprd01.prod.exchangelabs.com> |
Hi guys, In a standard QTabWidget when moving the tabs, the behavior is like this: [cid:7d8c1ac9-04b1-4350-b2d7-fdabff771b69] I made a QTabWidget with a custom tabBar in which I manually add a QGroupBox with QLabel that acts as a close button with self.setTabButton(index, QTabBar.RightSide, groupbox). This works great except that it changes the behavior of the mouse-dragging of tabs like so: [cid:9c0ec5b9-3a3f-4f1c-a3eb-5166abe45a55] I don't exactly know what caused this? I wish to get the standard behaviour back, can anyone help? The code is in the attachment. Thanks, Matic _______________________________________________ PyQt mailing list [email protected] https://www.riverbankcomputing.com/mailman/listinfo/pyqt
2019-05-25_20-05-15.gif
(image/gif, 52.9 KB) - not displayed
2019-05-25_20-02-47.gif
(image/gif, 83.4 KB) - not displayed
qtabwidget_test.py
(text/plain, 15.1 KB)
import os
import sys
from PyQt5.Qsci import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtTest import *
def create_pixmap(pixmap_name):
return QPixmap(pixmap_name)
class PictureButton(QLabel):
off_image = None
on_image = None
hover_image = None
function = None
def __init__(self,
off_image,
on_image,
hover_image,
function,
size,
parent=None):
super().__init__(parent)
self.setScaledContents(True)
self.off_image = off_image
self.on_image = on_image
self.hover_image = hover_image
self.function = function
# self.resize(size)
self.setFixedSize(size)
self.setPixmap(self.off_image)
def mousePressEvent(self, event):
super().mousePressEvent(event)
self.setPixmap(self.on_image)
event.accept()
def mouseReleaseEvent(self, event):
super().mouseReleaseEvent(event)
self.setPixmap(self.hover_image)
QTest.qWait(5)
if callable(self.function) == True:
self.function()
event.accept()
def enterEvent(self, event):
super().enterEvent(event)
self.setPixmap(self.hover_image)
def leaveEvent(self, event):
super().leaveEvent(event)
self.setPixmap(self.off_image)
class MyTabWidget(QTabWidget):
"""
Basic widget used for holding QScintilla/QTextEdit objects
"""
class CustomTabBar(QTabBar):
class TabGroupBox(QGroupBox):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._index = None
@property
def index(self):
return self._index
@index.setter
def index(self, value):
self._index = value
"""
Custom tab bar used to capture tab clicks, ...
"""
# Layout constants
SPACING = 3
MARGINS = (5, 0, 5, 0)
# Reference to the parent widget
parent = None
# Reference to the main form
main_form = None
# Reference to the tab menu
tab_menu = None
def __init__(self, parent):
"""Initialize the tab bar object"""
# Initialize superclass
super().__init__(parent)
# Store the parent reference
self.parent = parent
# Store the main form reference
self.main_form = self.parent.parent
# Connect the signals
self.tabMoved.connect(self._tab_moved_slot)
self.currentChanged.connect(self._current_tab_changed)
# Enable mouse tracking move events
self.setMouseTracking(True)
self.mouse_hover_index = None
def _update_tab_indexes(self):
# Readdress all tabs
for i in range(self.count()):
groupbox = self.tabButton(i, QTabBar.RightSide)
_data = self.tabData(i)
if _data is not None:
stored_groupbox, close_box = _data
if groupbox == None:
groupbox = stored_groupbox
if close_box:
close_box.index = i
groupbox.index = i
self.changeEvent(QEvent(QEvent.FontChange))
def tabInserted(self, index):
groupbox = self.TabGroupBox()
layout = QHBoxLayout()
layout.setSpacing(self.SPACING)
layout.setContentsMargins(*self.MARGINS)
groupbox.setLayout(layout)
groupbox.setStyleSheet("QGroupBox{border: 0px;}")
close_box = None
widget = self.parent.widget(index)
def close_function():
close_index = groupbox.index
self.parent.tabCloseRequested.emit(close_index)
close_button = PictureButton(
create_pixmap("alt_close.png"),
create_pixmap("alt_close_press.png"),
create_pixmap("alt_close_hover.png"),
close_function,
QSize(int(20), int(20)),
self
)
close_button.closes = True
layout.addWidget(close_button)
close_box = self.TabGroupBox()
def copy_close_function():
close_index = close_box.index
self.parent.tabCloseRequested.emit(close_index)
copy_close_button = PictureButton(
create_pixmap("alt_close.png"),
create_pixmap("alt_close_press.png"),
create_pixmap("alt_close_hover.png"),
copy_close_function,
QSize(int(20), int(20)),
self
)
copy_close_button.closes = True
copy_close_button.setParent(close_box)
copy_layout = QHBoxLayout()
copy_layout.setSpacing(self.SPACING)
copy_layout.setContentsMargins(*self.MARGINS)
copy_layout.addWidget(copy_close_button)
close_box.setLayout(copy_layout)
close_box.setStyleSheet("QGroupBox{border: 0px;}")
# Store the groupbox
self.setTabData(index, (groupbox, close_box))
self.setTabButton(index, QTabBar.RightSide, groupbox)
self.resize_tab_buttons(index)
self._update_tab_indexes()
def tabRemoved(self, index):
self._update_tab_indexes()
@pyqtSlot(int, int)
def _tab_moved_slot(self, t_from, t_to):
self._update_tab_indexes()
@pyqtSlot(int)
def _current_tab_changed(self, current_index):
self.refresh_tab_buttons(current_index)
def refresh_tab_buttons(self, index):
for i in range(self.count()):
groupbox = self.tabButton(i, QTabBar.RightSide)
if i != index:
if groupbox == None:
continue
if isinstance(groupbox, self.TabGroupBox) == False:
continue
groupbox.hide()
close_box = self.tabData(i)[1]
self.setTabButton(i, QTabBar.RightSide, close_box)
if close_box:
close_box.show()
else:
_data = self.tabData(i)
if _data == None:
continue
groupbox, close_box = _data
if isinstance(groupbox, self.TabGroupBox) == False or \
groupbox.layout().count() == 0:
continue
self.setTabButton(i, QTabBar.RightSide, groupbox)
groupbox.show()
def add_tab_button(self,
index,
icon_path_off,
icon_path_on,
icon_path_hover,
tooltip,
func,
visible=True):
groupbox = self.tabButton(index, QTabBar.RightSide)
layout = groupbox.layout()
items = []
for i in range(layout.count()):
items.append(layout.itemAt(i).widget())
button = PictureButton(
create_pixmap(icon_path_off),
create_pixmap(icon_path_on),
create_pixmap(icon_path_hover),
func,
QSize(int(20), int(20)),
self
)
button.setVisible(visible)
button.setToolTip(tooltip)
button.closes = False
groupbox.index = index
close_box = self.tabData(index)[1]
if close_box:
close_box.index = index
layout.setSpacing(self.SPACING)
layout.setContentsMargins(*self.MARGINS)
groupbox.setStyleSheet("QGroupBox{border: 0px;}")
# Clear old layout
for i in reversed(range(layout.count())):
layout.itemAt(i).widget().setParent(None)
# Add new items
for i in items[:-1]:
layout.addWidget(i)
layout.addWidget(button)
if len(items) > 0:
layout.addWidget(items[-1])
self.setTabButton(index, QTabBar.RightSide, groupbox)
self.resize_tab_buttons(index)
self._update_tab_indexes()
groupbox.show()
def _set_tab_button_visibility(self, tab_index, button_index, visible):
groupbox = self.tabButton(tab_index, QTabBar.RightSide)
if groupbox is None:
return
layout = groupbox.layout()
button_item = layout.itemAt(button_index)
if button_item is None:
return
if visible == True:
button_item.widget().show()
else:
button_item.widget().hide()
self.resize_tab_buttons(tab_index)
groupbox.adjustSize()
e = QEvent(QEvent.Resize)
QCoreApplication.sendEvent(self, e)
self.adjustSize()
QCoreApplication.processEvents()
def hide_tab_button(self, tab_index, button_index):
self._set_tab_button_visibility(tab_index, button_index, False)
def show_tab_button(self, tab_index, button_index):
self._set_tab_button_visibility(tab_index, button_index, True)
def scale_tab_buttons(self):
for i in range(self.count()):
groupbox = self.tabButton(i, QTabBar.RightSide)
if groupbox == None:
groupbox = self.tabData(i)
if groupbox == None:
continue
if isinstance(groupbox, self.TabGroupBox):
layout = groupbox.layout()
for j in range(layout.count()):
w = layout.itemAt(j).widget()
w.setFixedSize(
int(20), int(20)
)
if isinstance(w, QToolButton):
w.setIconSize(
QSize(
int(20),
int(20)
)
)
self.resize_tab_buttons(i)
elif isinstance(groupbox, tuple):
groupbox, close_box = groupbox
layout = groupbox.layout()
for j in range(layout.count()):
w = layout.itemAt(j).widget()
w.setFixedSize(
int(20), int(20)
)
if isinstance(w, QToolButton):
w.setIconSize(
QSize(
int(20),
int(20)
)
)
if close_box:
close_box.layout().itemAt(0).widget().setFixedSize(
int(20), int(20)
)
self.resize_tab_buttons(i)
def resize_tab_buttons(self, index):
# Resize groupbox
close_box = None
groupbox = self.tabButton(index, QTabBar.RightSide)
_data = self.tabData(index)
if groupbox == None:
if _data == None:
return
groupbox = _data[0]
close_box = _data[1]
layout = groupbox.layout()
count = 0
for i in range(layout.count()):
if layout.itemAt(i).widget().isVisible():
count += 1
if count > 0:
button_size = int(20) * count
spacing_size = self.SPACING * (count - 1)
margin_space_horizontal = self.MARGINS[0] + self.MARGINS[2]
margin_space_vertical = self.MARGINS[1] + self.MARGINS[3]
groupbox.setFixedSize(
QSize(
button_size + spacing_size + margin_space_horizontal,
int(20) + margin_space_vertical
)
)
if close_box == None and _data != None:
close_box = _data[1]
if close_box:
layout = close_box.layout()
count = 0
for i in range(layout.count()):
if layout.itemAt(i).widget().isVisible():
count += 1
if count > 0:
button_size = int(20) * count
spacing_size = self.SPACING * (count - 1)
margin_space_horizontal = self.MARGINS[0] + self.MARGINS[2]
margin_space_vertical = self.MARGINS[1] + self.MARGINS[3]
close_box.setFixedSize(
QSize(
button_size + spacing_size + margin_space_horizontal,
int(20) + margin_space_vertical
)
)
def __init__(self, parent):
super().__init__(parent)
self.custom_tab_bar = self.CustomTabBar(self)
self.setTabBar(self.custom_tab_bar)
class Window(QWidget):
def __init__(self):
super().__init__()
# Custom tab widget
tab_widget = MyTabWidget(self)
# Standard tab widget
# tab_widget = QTabWidget(self)
# tab_widget.setTabsClosable(True)
tab_widget.setMovable(True)
tab_widget.addTab(QWidget(tab_widget), 'Tab One')
tab_widget.addTab(QWidget(tab_widget), 'Tab Two')
tab_widget.addTab(QWidget(tab_widget), 'Tab Three')
tab_widget.addTab(QWidget(tab_widget), 'Tab Four')
layout = QHBoxLayout()
layout.addWidget(tab_widget)
def button_func(state):
tab_widget.addTab(QWidget(tab_widget), 'Tab Five')
tab_widget.tabBar().add_tab_button(
index=4,
icon_path_off="alt_settings.png",
icon_path_on="alt_settings_press.png",
icon_path_hover="alt_settings_hover.png",
tooltip="settings",
func=None
)
tab_widget.setCurrentIndex(tab_widget.count()-1)
button = QPushButton("TEST")
button.clicked.connect(button_func)
layout.addWidget(button)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())