
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_())