#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Copyright (C) 2014 Technische Universität Berlin,
Fakultät IV - Elektrotechnik und Informatik,
Fachgebiet Regelungssysteme,
Einsteinufer 17, D-10587 Berlin, Germany
This file is part of PaPI.
PaPI is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PaPI is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PaPI. If not, see <http://www.gnu.org/licenses/>.
Contributors:
<Stefan Ruppin
"""
from PyQt5.QtCore import Qt, QObject
from PyQt5 import QtCore, QtGui
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtWidgets import QDialog, QLineEdit, QCheckBox , QTabWidget, QMdiArea, \
QMessageBox, QMenu, QAction, QInputDialog, QFileDialog, QWidget, \
QMainWindow, QHBoxLayout, QVBoxLayout, QTabWidget
from papi.gui.default.custom import FileLineEdit
from papi.constants import PLUGIN_IOP_IDENTIFIER, PLUGIN_VIP_IDENTIFIER, PLUGIN_DPP_IDENTIFIER, \
GUI_TABWIDGET_IDENTIFIER
[docs]class PaPITabManger(QObject):
def __init__(self, tabWigdet = None, dgui = None,gui_api = None, parent=None, centralWidget=None):
super(PaPITabManger, self).__init__(parent)
self.tabWidget = tabWigdet
self.gui_api = gui_api
self.tabWidget.setContextMenuPolicy(Qt.CustomContextMenu)
self.tabWidget.customContextMenuRequested.connect(self.show_context_menu)
self.cmenu = self.create_context_menu()
self.tabWidget.tabCloseRequested.connect(self.closeTab_by_ind)
#self.tabWidget.setTabShape(QtGui.QTabWidget.Triangular)
#self.tabWidget.setTabPosition(QtGui.QTabWidget.North)
self.dGui = dgui
# make tabs movable
self.tabWidget.setMovable(True)
self.tabWidget.setTabsClosable(True)
# create dict for saving tabs
self.tab_dict_uname = {}
self.windowTabData = {}
self.centralWidget = centralWidget
self.contextMenusEnabled = True
[docs] def setTabsMoveable(self, moveable):
if isinstance(moveable,bool):
self.tabWidget.setMovable(moveable)
[docs] def setTabsCloseable(self, closeable):
if isinstance(closeable,bool):
self.tabWidget.setTabsClosable(closeable)
[docs] def get_tabs_by_uname(self):
return self.tab_dict_uname
[docs] def add_tab(self, name):
if name in self.tab_dict_uname:
print('Tab with name already exists')
else:
newTab = TabObject( name)
newTab.index = self.tabWidget.addTab(newTab,newTab.name)
self.tab_dict_uname[newTab.name] = newTab
return newTab
[docs] def remove_tab(self,tabObject):
self.tab_dict_uname.pop(tabObject.name)
ind = self.tabWidget.indexOf(tabObject)
self.tabWidget.removeTab(ind)
tabObject.destroy()
[docs] def rename_tab(self, tabObject, new_name):
if new_name not in self.tab_dict_uname:
# rename it
old_name = tabObject.name
self.tab_dict_uname.pop(tabObject.name)
tabObject.name = new_name
self.tab_dict_uname[tabObject.name] = tabObject
ind = self.tabWidget.indexOf(tabObject)
self.tabWidget.setTabText(ind,tabObject.name)
allPlugins = self.dGui.get_all_plugins()
for pluign_ind in allPlugins:
dplugin = allPlugins[pluign_ind]
if dplugin.plugin._get_type() == PLUGIN_VIP_IDENTIFIER:
tabOfPlugin = dplugin.plugin._config['tab']['value']
if tabOfPlugin == old_name:
dplugin.plugin._config['tab']['value'] = new_name
[docs] def rename_window(self, window, new_name):
if new_name not in self.tab_dict_uname:
# rename it
old_name = window.windowName
self.tab_dict_uname.pop(old_name)
window.setNewWindowName(new_name)
self.tab_dict_uname[new_name] = window
allPlugins = self.dGui.get_all_plugins()
for pluign_ind in allPlugins:
dplugin = allPlugins[pluign_ind]
if dplugin.plugin._get_type() == PLUGIN_VIP_IDENTIFIER:
tabOfPlugin = dplugin.plugin._config['tab']['value']
if tabOfPlugin == old_name:
dplugin.plugin._config['tab']['value'] = new_name
[docs] def get_default_tab(self, NotThisIndex):
if NotThisIndex == 0:
if self.tabWidget.count() > 1:
return self.tabWidget.widget(1)
else:
raise Exception('no tab open')
else:
return self.tabWidget.widget(0)
[docs] def get_currently_active_tab(self):
return self.tabWidget.currentIndex()
[docs] def set_tab_active_by_index(self, index):
self.tabWidget.setCurrentIndex(index)
[docs] def select_next_tab(self):
cur_ind = self.get_currently_active_tab()
self.set_tab_active_by_index(cur_ind+1)
[docs] def select_prev_tab(self):
cur_ind = self.get_currently_active_tab()
self.set_tab_active_by_index(cur_ind-1)
[docs] def set_tab_active_by_name(self, tabName):
if tabName in self.tab_dict_uname:
tabObj = self.tab_dict_uname[tabName]
ind = self.tabWidget.indexOf(tabObj)
self.set_tab_active_by_index(ind)
[docs] def moveFromTo(self, start, dest, subWindow, posX=0, posY=0):
if start in self.tab_dict_uname and dest in self.tab_dict_uname:
startTab = self.tab_dict_uname[start]
destTab = self.tab_dict_uname[dest]
startTab.removeSubWindow(subWindow)
destTab.addSubWindow(subWindow)
subWindow.show()
subWindow.move(posX, posY)
subWindow.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowMinMaxButtonsHint | Qt.WindowTitleHint )
return True
else:
return False
[docs] def set_background_for_tab_with_name(self, name, bg):
if bg is not None:
if name in self.tab_dict_uname:
if isinstance(self.tab_dict_uname[name], TabObject):
widgetArea = self.tab_dict_uname[name]
elif isinstance(self.tab_dict_uname[name], PaPIWindow):
widgetArea = self.tab_dict_uname[name].tabWidget
widgetArea.setBackground_image(bg)
# pixmap = QtGui.QPixmap(bg).scaled(widgetArea.size())
# qbrush_bg = QtGui.QBrush(QtGui.QColor() ,pixmap)
#
# widgetArea.setBackground(qbrush_bg)
# widgetArea.background = bg
# widgetArea.pixmap = QtGui.QPixmap(bg)
[docs] def set_all_tabs_to_close_when_empty(self, state):
for tabName in self.tab_dict_uname:
tabO = self.tab_dict_uname[tabName]
tabO.closeIfempty = state
[docs] def closeTab_by_ind(self, ind):
tabOb = self.tabWidget.widget(ind)
tab_name = tabOb.name
if self.tabWidget.count() > 1 or len(tabOb.subWindowList()) == 0: #tab_name != 'Default':
# not the default tab, just close and move plugins to default tab
plugins = self.dGui.get_all_plugins()
for pl_id in plugins:
plugin = plugins[pl_id]
if plugin.type == PLUGIN_VIP_IDENTIFIER:
if plugin.plugin._config['tab']['value'] == tab_name:
self.moveFromTo(tab_name,self.get_default_tab(ind).name, plugin.plugin._get_sub_window())
plugin.plugin._config['tab']['value'] = self.get_default_tab(ind).name
self.remove_tab(tabOb)
else:
# default tab: ask if really want to close
diag = DefaultCloseBox()#QtGui.QDialog()
ret = diag.exec_()
if ret == QtGui.QMessageBox.Ok:
allPlugins = self.dGui.get_all_plugins()
for pl_id in allPlugins:
dplugin = allPlugins[pl_id]
if dplugin.own_process is False:
self.gui_api.do_delete_plugin(dplugin.id)
self.remove_tab(tabOb)
[docs] def closeTab_by_name(self,name):
if name in self.tab_dict_uname:
tabO = self.tab_dict_uname[name]
ind = self.tabWidget.indexOf(tabO)
self.closeTab_by_ind(ind)
[docs] def close_all_empty_tabs(self):
tabs = self.tab_dict_uname
tabs_to_close = []
for tab in tabs:
tabO = tabs[tab]
if tabO.isEmpty():
tabs_to_close.append(tab)
for tab in tabs_to_close:
self.closeTab_by_name(tab)
[docs] def getTabPosition_by_name(self, tabName):
if tabName in self.tab_dict_uname:
tabObj = self.tab_dict_uname[tabName]
ind = self.tabWidget.indexOf(tabObj)
return ind
[docs] def setTabs_movable_closable(self, move, close):
if isinstance(move, bool):
self.tabWidget.setMovable(move)
if isinstance(close,bool):
self.tabWidget.setTabsClosable(close)
[docs] def add_wind(self, name):
if name in self.tab_dict_uname:
print('Tab with name already exists')
else:
# create a new window with name = name
newWin = PaPIWindow(name, conextMenu=self.show_context_menu_window, parent=self.centralWidget)
# connect new close slot:
newWin.closeEvent = lambda ev, handler= newWin.closeEvent, window=newWin : self.new_wind_close_event(ev,handler, window)
# add new window to data structure
self.tab_dict_uname[newWin.windowName] = newWin
return newWin
[docs] def new_wind_close_event(self, event, handler, window):
if window.alreadyDocked is True:
pass
else:
self.redock_window(window)
[docs] def remove_window(self,window, rm_from_data=True):
if rm_from_data is True:
if window.windowName in self.tab_dict_uname:
self.tab_dict_uname.pop(window.windowName)
window.close()
window.destroy()
else:
window.close()
window.destroy()
[docs] def redock_window(self, window):
if window.windowName in self.tab_dict_uname:
winName = window.windowName
destTab = self.add_tab('DOCK'+winName+'DOCK')
# reuse previous background (the one before dock)
destTab.setBackground_image(window.tabWidget.getBackground_image())
if not window.tabWidget.isEmpty():
# create new Tab with name with affix and prefix
plugins = self.dGui.get_all_plugins()
for pl_id in plugins:
plugin = plugins[pl_id]
if plugin.type == PLUGIN_VIP_IDENTIFIER:
if plugin.plugin._config['tab']['value'] == window.windowName:
subwin = plugin.plugin._get_sub_window()
posX = subwin.pos().x()
posY = subwin.pos().y()
self.moveFromTo(window.windowName,destTab.name, subwin,posX=posX,posY=posY)
plugin.plugin._config['tab']['value'] = destTab.name
window.alreadyDocked = True
self.remove_window(window)
# rename new Tab to real name
self.rename_tab(destTab,winName)
[docs] def detach_tab(self):
tabOb = self.tabWidget.currentWidget()
tabName = tabOb.name
neWin = self.add_wind('DETACH'+tabName + 'DETACH')
# reuse previous background (the one before detach)
neWin.tabWidget.setBackground_image(tabOb.getBackground_image())
if len(tabOb.subWindowList()) > 0:
plugins = self.dGui.get_all_plugins()
for pl_id in plugins:
plugin = plugins[pl_id]
if plugin.type == PLUGIN_VIP_IDENTIFIER:
if plugin.plugin._config['tab']['value'] == tabOb.name:
subwin = plugin.plugin._get_sub_window()
posX = subwin.pos().x()
posY = subwin.pos().y()
self.moveFromTo(tabOb.name,neWin.windowName, subwin,posX=posX,posY=posY)
plugin.plugin._config['tab']['value'] = neWin.windowName
self.remove_tab(tabOb)
self.rename_window(neWin,tabName)
[docs]class PaPIWindow(QMainWindow):
def __init__(self, windowName, conextMenu=None, parent = None):
super(QMainWindow, self).__init__(parent)
self.windowName = windowName
self.tabList = {}
self.tabWidget = TabObject(windowName)
self.setCentralWidget(self.tabWidget)
self.closeIfempty = False
self.alreadyDocked = False
self.setWindowTitle(self.windowName)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(lambda pos, wind = self: conextMenu(pos,wind))
self.resize(693, 600)
self.show()
[docs] def addSubWindow(self, subwindow):
self.tabWidget.addSubWindow(subwindow)
[docs] def removeSubWindow(self, subwindow):
if subwindow in self.tabWidget.subWindowList():
self.tabWidget.removeSubWindow(subwindow)
[docs] def setNewWindowName(self, newName):
self.setWindowTitle(newName)
self.windowName = newName
[docs] def getBackground(self):
return self.tabWidget.background
[docs] def setBackground(self, bg):
self.tabWidget.background = bg
background = property(fget=getBackground, fset=setBackground)
[docs] def subWindowList(self):
return self.tabWidget.subWindowList()
[docs] def getName(self):
return self.windowName
[docs] def setName(self, name):
self.windowName = name
name = property(fget=getName, fset=setName)
[docs] def isEmpty(self):
return self.tabWidget.isEmpty()
[docs]class TabObject(QMdiArea):
def __init__(self, name, windowName=0, parent=None):
super(TabObject, self).__init__(parent)
self.index = None
self.name = name
self.background = 'default'
self.closeIfempty = False
self.windowName = windowName
self.bg_pixmap = None
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
[docs] def isEmpty(self):
return len(self.subWindowList()) == 0
[docs] def resizeEvent(self, QResizeEvent):
QMdiArea.resizeEvent(self,QResizeEvent)
if self.bg_pixmap is not None:
qbrush_bg = QtGui.QBrush(QtGui.QColor() ,self.bg_pixmap.scaled(QResizeEvent.size()))
self.setBackground(qbrush_bg)
[docs] def setBackground_image(self,image):
if image is not None and image != '' and image != 'default':
self.bg_pixmap = QtGui.QPixmap(image)
qbrush_bg = QtGui.QBrush(QtGui.QColor() ,self.bg_pixmap.scaled(self.size()))
self.setBackground(qbrush_bg)
self.background = image
[docs] def getBackground_image(self):
return self.background
[docs]class DefaultCloseBox(QMessageBox):
def __init__(self, parent=None):
super(DefaultCloseBox, self).__init__(parent)
self.setWindowTitle('Close last tab?')
self.setText('All plugins in this tab will be closed. Are you sure?')
self.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)