mirror of
https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System.git
synced 2024-11-15 13:15:43 +08:00
Update Python Bindings (#348)
* Update Python bindings * Add X11Extras to setup.py for Linux builds * Update Python Bindings * Update Python bindings * Update Python Bindings
This commit is contained in:
parent
0c44accb44
commit
b5b251dffb
@ -215,6 +215,17 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
dock_widget = self.create_calendar_dock_widget()
|
dock_widget = self.create_calendar_dock_widget()
|
||||||
dock_widget.setTabToolTip("Tab ToolTip\nHodie est dies magna")
|
dock_widget.setTabToolTip("Tab ToolTip\nHodie est dies magna")
|
||||||
dock_area = self.dock_manager.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, top_dock_area)
|
dock_area = self.dock_manager.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, top_dock_area)
|
||||||
|
# Now we create a action to test resizing of DockArea widget
|
||||||
|
action = self.menuTests.addAction("Resize {}".format(dock_widget.windowTitle()))
|
||||||
|
def action_triggered():
|
||||||
|
splitter = QtAds.internal.findParent(QtAds.CDockSplitter, dock_area)
|
||||||
|
if not splitter:
|
||||||
|
return
|
||||||
|
# We change the sizes of the splitter that contains the Calendar 1 widget
|
||||||
|
# to resize the dock widget
|
||||||
|
width = splitter.width()
|
||||||
|
splitter.setSizes([width * 2/3, width * 1/3])
|
||||||
|
action.triggered.connect(action_triggered)
|
||||||
|
|
||||||
# Now we add a custom button to the dock area title bar that will create
|
# Now we add a custom button to the dock area title bar that will create
|
||||||
# new editor widgets when clicked
|
# new editor widgets when clicked
|
@ -13,18 +13,6 @@ from PyQtAds import QtAds
|
|||||||
UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
|
UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
|
||||||
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
|
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
|
||||||
|
|
||||||
import demo_rc # pyrcc5 demo\demo.qrc -o examples\centralWidget\demo_rc.py
|
|
||||||
|
|
||||||
|
|
||||||
def svg_icon(filename: str):
|
|
||||||
'''Helper function to create an SVG icon'''
|
|
||||||
# This is a workaround, because because in item views SVG icons are not
|
|
||||||
# properly scaled and look blurry or pixelate
|
|
||||||
icon = QIcon(filename)
|
|
||||||
icon.addPixmap(icon.pixmap(92))
|
|
||||||
return icon
|
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(MainWindowUI, MainWindowBase):
|
class MainWindow(MainWindowUI, MainWindowBase):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
@ -46,27 +34,26 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
central_dock_area.setAllowedAreas(QtAds.DockWidgetArea.OuterDockAreas)
|
central_dock_area.setAllowedAreas(QtAds.DockWidgetArea.OuterDockAreas)
|
||||||
|
|
||||||
# create other dock widgets
|
# create other dock widgets
|
||||||
file_tree = QTreeView()
|
|
||||||
file_tree.setFrameShape(QFrame.NoFrame)
|
|
||||||
file_model = QFileSystemModel(file_tree)
|
|
||||||
file_model.setRootPath(QDir.currentPath())
|
|
||||||
file_tree.setModel(file_model)
|
|
||||||
data_dock_widget = QtAds.CDockWidget("File system")
|
|
||||||
data_dock_widget.setWidget(file_tree)
|
|
||||||
data_dock_widget.resize(150, 250)
|
|
||||||
data_dock_widget.setMinimumSize(100, 250)
|
|
||||||
file_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, data_dock_widget, central_dock_area)
|
|
||||||
self.menuView.addAction(data_dock_widget.toggleViewAction())
|
|
||||||
|
|
||||||
table = QTableWidget()
|
table = QTableWidget()
|
||||||
table.setColumnCount(3)
|
table.setColumnCount(3)
|
||||||
table.setRowCount(10)
|
table.setRowCount(10)
|
||||||
table_dock_widget = QtAds.CDockWidget("Table")
|
table_dock_widget = QtAds.CDockWidget("Table 1")
|
||||||
table_dock_widget.setWidget(table)
|
table_dock_widget.setWidget(table)
|
||||||
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
table_dock_widget.resize(250, 150)
|
table_dock_widget.resize(250, 150)
|
||||||
table_dock_widget.setMinimumSize(200, 150)
|
table_dock_widget.setMinimumSize(200, 150)
|
||||||
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, file_area)
|
table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, table_dock_widget)
|
||||||
|
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
|
table = QTableWidget()
|
||||||
|
table.setColumnCount(5)
|
||||||
|
table.setRowCount(1020)
|
||||||
|
table_dock_widget = QtAds.CDockWidget("Table 2")
|
||||||
|
table_dock_widget.setWidget(table)
|
||||||
|
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
|
table_dock_widget.resize(250, 150)
|
||||||
|
table_dock_widget.setMinimumSize(200, 150)
|
||||||
|
table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, table_area)
|
||||||
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
properties_table = QTableWidget()
|
properties_table = QTableWidget()
|
||||||
@ -76,7 +63,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
properties_dock_widget.setWidget(properties_table)
|
properties_dock_widget.setWidget(properties_table)
|
||||||
properties_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
properties_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
properties_dock_widget.resize(250, 150)
|
properties_dock_widget.resize(250, 150)
|
||||||
properties_dock_widget.setMinimumSize(200,150)
|
properties_dock_widget.setMinimumSize(200, 150)
|
||||||
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.RightDockWidgetArea, properties_dock_widget, central_dock_area)
|
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.RightDockWidgetArea, properties_dock_widget, central_dock_area)
|
||||||
self.menuView.addAction(properties_dock_widget.toggleViewAction())
|
self.menuView.addAction(properties_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
@ -84,7 +71,6 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
|
|
||||||
def create_perspective_ui(self):
|
def create_perspective_ui(self):
|
||||||
save_perspective_action = QAction("Create Perspective", self)
|
save_perspective_action = QAction("Create Perspective", self)
|
||||||
save_perspective_action.setIcon(svg_icon(":/adsdemo/images/picture_in_picture.svg"))
|
|
||||||
save_perspective_action.triggered.connect(self.save_perspective)
|
save_perspective_action.triggered.connect(self.save_perspective)
|
||||||
perspective_list_action = QWidgetAction(self)
|
perspective_list_action = QWidgetAction(self)
|
||||||
self.perspective_combobox = QComboBox(self)
|
self.perspective_combobox = QComboBox(self)
|
203
examples/dockindock/dockindock.py
Normal file
203
examples/dockindock/dockindock.py
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QMessageBox,
|
||||||
|
QInputDialog, QMenu, QLineEdit)
|
||||||
|
from PyQt5.QtGui import QIcon
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
from dockindockmanager import DockInDockManager
|
||||||
|
from perspectiveactions import LoadPerspectiveAction, RemovePerspectiveAction
|
||||||
|
|
||||||
|
|
||||||
|
class DockInDockWidget(QWidget):
|
||||||
|
def __init__(self, parent, perspectives_manager: 'PerspectivesManager', can_create_new_groups: bool = False, top_level_widget = None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
if top_level_widget is not None:
|
||||||
|
self.__can_create_new_groups = top_level_widget.can_create_new_groups
|
||||||
|
else:
|
||||||
|
self.__can_create_new_groups = can_create_new_groups
|
||||||
|
self.__top_level_dock_widget = top_level_widget if top_level_widget else self
|
||||||
|
self.__perspectives_manager = perspectives_manager
|
||||||
|
self.__new_perspective_default_name: str = ''
|
||||||
|
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setContentsMargins(0,0,0,0)
|
||||||
|
self.__mgr = DockInDockManager(self)
|
||||||
|
layout.addWidget(self.__mgr)
|
||||||
|
|
||||||
|
def getManager(self) -> 'DockInDockManager':
|
||||||
|
return self.__mgr
|
||||||
|
|
||||||
|
def getTopLevelDockWidget(self) -> 'DockInDockWidget':
|
||||||
|
return self.__top_level_dock_widget
|
||||||
|
|
||||||
|
def canCreateNewGroups(self) -> bool:
|
||||||
|
return self.__can_create_new_groups
|
||||||
|
|
||||||
|
def getPerspectivesManager(self) -> 'PerspectivesManager':
|
||||||
|
return self.__perspectives_manager
|
||||||
|
|
||||||
|
def addTabWidget(self, widget: QWidget, name: str, after: QtAds.CDockAreaWidget, icon = QIcon()) -> QtAds.CDockAreaWidget:
|
||||||
|
for existing in self.getTopLevelDockWidget().getManager().allDockWidgets(True, True):
|
||||||
|
if existing[1].objectName() == name:
|
||||||
|
QMessageBox.critical(self, "Error", "Name '" + name + "' already in use")
|
||||||
|
return
|
||||||
|
|
||||||
|
dock_widget = QtAds.CDockWidget(name)
|
||||||
|
dock_widget.setWidget(widget)
|
||||||
|
dock_widget.setIcon(icon)
|
||||||
|
|
||||||
|
# Add the dock widget to the top dock widget area
|
||||||
|
return self.__mgr.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, after)
|
||||||
|
|
||||||
|
def isTopLevel(self) -> bool:
|
||||||
|
return not self.objectName()
|
||||||
|
|
||||||
|
def getGroupNameError(self, group_name: str) -> str:
|
||||||
|
if not group_name:
|
||||||
|
return "Group must have a non-empty name"
|
||||||
|
|
||||||
|
dock_managers = self.__mgr.allManagers(True, True)
|
||||||
|
for mgr in dock_managers:
|
||||||
|
if mgr.getGroupName() == group_name:
|
||||||
|
return "Group name '" + group_name + "' already used"
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def createGroup(self, group_name: str, insert_pos: QtAds.CDockAreaWidget, icon = QIcon()) -> 'DockInDockWidget':
|
||||||
|
error = self.getGroupNameError(group_name)
|
||||||
|
if error:
|
||||||
|
QMessageBox.critical(None, "Error", error)
|
||||||
|
return
|
||||||
|
|
||||||
|
child = DockInDockWidget(self, self.__top_level_dock_widget, self.__perspectives_manager)
|
||||||
|
child.setObjectName(group_name)
|
||||||
|
|
||||||
|
dock_widget = QtAds.CDockWidget(group_name)
|
||||||
|
dock_widget.setWidget(child)
|
||||||
|
dock_widget.setIcon(icon)
|
||||||
|
|
||||||
|
insert_pos = self.__mgr.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, insert_pos)
|
||||||
|
|
||||||
|
return child, insert_pos
|
||||||
|
|
||||||
|
def destroyGroup(self, widget_to_remove: 'DockInDockWidget') -> None:
|
||||||
|
top_level_widget = widget_to_remove.getTopLevelDockWidget()
|
||||||
|
|
||||||
|
if top_level_widget and top_level_widget != widget_to_remove:
|
||||||
|
for dock_widget in widget_to_remove.getManager().getWidgetsInGUIOrder(): #don't use allDockWidgets to preserve sub-groups
|
||||||
|
MoveDockWidgetAction.move(dock_widget, top_level_widget.getManager())
|
||||||
|
assert not widget_to_remove.getManager().allDockWidgets(True, True)
|
||||||
|
|
||||||
|
# find widget's parent:
|
||||||
|
for dock_widget in top_level_widget.getManager().allDockWidgets(True, True):
|
||||||
|
if dockwidget[1].widget() == widget_to_remove:
|
||||||
|
dockwidget[0].removeDockWidget(dockwidget[1])
|
||||||
|
del dockwidget[1]
|
||||||
|
# delete widgetToRemove; automatically deleted when dockWidget is deleted
|
||||||
|
widget_to_remove = None
|
||||||
|
break
|
||||||
|
|
||||||
|
assert widget_to_remove == None
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def attachViewMenu(self, menu: QMenu) -> None:
|
||||||
|
menu.aboutToShow.connect(self.autoFillAttachedViewMenu)
|
||||||
|
|
||||||
|
def autoFillAttachedViewMenu(self) -> None:
|
||||||
|
menu = self.sender()
|
||||||
|
|
||||||
|
if menu:
|
||||||
|
menu.clear()
|
||||||
|
self.setupViewMenu(menu)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def setupViewMenu(self, menu):
|
||||||
|
dock_managers = self.__mgr.allManagers(True, True)
|
||||||
|
|
||||||
|
has_perspectives_menu = False
|
||||||
|
if self.getTopLevelDockWidget() == self:
|
||||||
|
has_perspectives_menu = (self.__perspectives_manager != None)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
organize = menu
|
||||||
|
if has_perspectives_menu:
|
||||||
|
organize = menu.addMenu("Organize")
|
||||||
|
|
||||||
|
self.setupMenu(organize, dock_managers)
|
||||||
|
|
||||||
|
if has_perspectives_menu:
|
||||||
|
perspectives = menu.addMenu("Perspectives")
|
||||||
|
self.fillPerspectivesMenu(perspectives)
|
||||||
|
|
||||||
|
def setupMenu(self, menu: QMenu, move_to: 'list[DockInDockManager]') -> None:
|
||||||
|
self.__mgr.fillViewMenu(menu, move_to)
|
||||||
|
menu.addSeparator()
|
||||||
|
move_menu = menu.addMenu("Move")
|
||||||
|
self.__mgr.fillMoveMenu(move_menu, move_to)
|
||||||
|
|
||||||
|
def fillPerspectivesMenu(self, menu: QMenu):
|
||||||
|
menu.addAction("Create perspective...", self.createPerspective)
|
||||||
|
perspectives_names = []
|
||||||
|
if self.__perspectives_manager:
|
||||||
|
perspectives_names = self.__perspectives_manager.perspectiveNames()
|
||||||
|
|
||||||
|
if perspectives_names:
|
||||||
|
load = menu.addMenu("Load perspective")
|
||||||
|
for name in perspectives_names:
|
||||||
|
load.addAction(LoadPerspectiveAction(load, name, self))
|
||||||
|
remove = menu.addMenu("Remove perspective")
|
||||||
|
for name in perspectives_names:
|
||||||
|
remove.addAction(RemovePerspectiveAction(remove, name, self))
|
||||||
|
|
||||||
|
def setNewPerspectiveDefaultName(default_name: str) -> None:
|
||||||
|
self.__new_perspective_default_name = default_name
|
||||||
|
|
||||||
|
def createPerspective(self) -> None:
|
||||||
|
if not self.__perspectives_manager:
|
||||||
|
return
|
||||||
|
|
||||||
|
name = self.__new_perspective_default_name
|
||||||
|
if self.__new_perspective_default_name:
|
||||||
|
index = 2
|
||||||
|
while name in self.__perspectives_manager.perspectiveNames():
|
||||||
|
name = f"{self.__new_perspective_default_name}({index})"
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
while True:
|
||||||
|
name, ok = QInputDialog.getText(None, "Create perspective", "Enter perspective name", QLineEdit.Normal, name)
|
||||||
|
if ok:
|
||||||
|
if not name:
|
||||||
|
QMessageBox.critical(None, "Error", "Perspective name cannot be empty")
|
||||||
|
continue
|
||||||
|
elif name in self.__perspectives_manager.perspectiveNames():
|
||||||
|
if QMessageBox.critical(None, "Error", f"Perspective '{name}' already exists, overwrite it?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.No:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.__perspectives_manager.addPerspective(name, self)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
def dumpStatus(self, echo: callable = print, widget: QtAds.CDockWidget = None, tab: str = '', suffix: str = '') -> str:
|
||||||
|
if widget is not None:
|
||||||
|
as_mgr = DockInDockManager.dockInAManager(widget)
|
||||||
|
if as_mgr:
|
||||||
|
as_mgr.parent().dumpStatus(tab=tab)
|
||||||
|
else:
|
||||||
|
echo(tab + widget.objectName() + suffix)
|
||||||
|
else:
|
||||||
|
echo(tab + "Group: " + self.getManager().getGroupName())
|
||||||
|
tab += " "
|
||||||
|
visible_widgets = set()
|
||||||
|
for widget in self.getManager().getWidgetsInGUIOrder():
|
||||||
|
visible_widgets.add(widget)
|
||||||
|
self.dumpStatus(widget=widget, tab=tab)
|
||||||
|
|
||||||
|
for closed in self.getManager().dockWidgetsMap().values():
|
||||||
|
if not closed in visible_widgets:
|
||||||
|
self.dumpStatus(widget=closed, tab=tab, suffix=" (closed)")
|
214
examples/dockindock/dockindockmanager.py
Normal file
214
examples/dockindock/dockindockmanager.py
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
from PyQt5.QtWidgets import QAction, QMenu, QInputDialog, QLineEdit
|
||||||
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
CHILD_PREFIX = "Child-"
|
||||||
|
|
||||||
|
class DockInDockManager(QtAds.CDockManager):
|
||||||
|
def __init__(self, parent: 'DockInDockWidget'):
|
||||||
|
super().__init__()
|
||||||
|
self.__parent = parent
|
||||||
|
|
||||||
|
def parent(self) -> 'DockInDockWidget':
|
||||||
|
return self.__parent
|
||||||
|
|
||||||
|
def fillViewMenu(self, menu: QMenu, move_to: 'dict[DockInDockManager]') -> None:
|
||||||
|
from dockindock import DockInDockWidget # Prevent cyclic import
|
||||||
|
|
||||||
|
widgets_map = self.dockWidgetsMap()
|
||||||
|
for key, value in widgets_map.items():
|
||||||
|
widget = value.widget()
|
||||||
|
action = value.toggleViewAction()
|
||||||
|
|
||||||
|
if isinstance(widget, DockInDockWidget):
|
||||||
|
sub_menu = menu.addMenu(key)
|
||||||
|
|
||||||
|
sub_menu.addAction(action)
|
||||||
|
sub_menu.addSeparator()
|
||||||
|
|
||||||
|
widget.setupMenu(sub_menu, move_to)
|
||||||
|
else:
|
||||||
|
menu.addAction(action)
|
||||||
|
|
||||||
|
if self.parent().canCreateNewGroups():
|
||||||
|
# see how this works, to create it in the right place,
|
||||||
|
# and also to have load perspective work when some groups are missing
|
||||||
|
menu.addSeparator()
|
||||||
|
menu.addAction(CreateChildDockAction(self.__parent, menu))
|
||||||
|
|
||||||
|
if self.parent().getTopLevelDockWidget().getManager() != self:
|
||||||
|
menu.addAction(DestroyGroupAction( self.parent, menu))
|
||||||
|
|
||||||
|
def fillMoveMenu(self, menu: QMenu, move_to: 'list[DockInDockManager]') -> None:
|
||||||
|
widgets_map = self.dockWidgetsMap()
|
||||||
|
for key, value in widgets_map.items():
|
||||||
|
sub_menu = menu.addMenu(key)
|
||||||
|
|
||||||
|
for mgr in move_to:
|
||||||
|
# iterate over all possible target managers
|
||||||
|
if mgr == self:
|
||||||
|
pass # if dock is already in mgr, no reason to move it there
|
||||||
|
elif mgr == DockInDockManager.dockInAManager(value):
|
||||||
|
pass # if target is the group itself, can't move it there, would make no sense
|
||||||
|
else:
|
||||||
|
sub_menu.addAction(MoveDockWidgetAction(value, mgr, sub_menu))
|
||||||
|
|
||||||
|
def addPerspectiveRec(self, name: str) -> None:
|
||||||
|
managers = self.allManagers(True, True)
|
||||||
|
|
||||||
|
for child in managers:
|
||||||
|
child.addPerspective(name)
|
||||||
|
|
||||||
|
def openPerspectiveRec(self, name: str) -> None:
|
||||||
|
managers = self.allManagers(True, True)
|
||||||
|
|
||||||
|
for child in managers:
|
||||||
|
child.openPerspective(name)
|
||||||
|
|
||||||
|
def getGroupName(self) -> str:
|
||||||
|
return self.parent().objectName()
|
||||||
|
|
||||||
|
def getPersistGroupName(self) -> str:
|
||||||
|
group = "Top"
|
||||||
|
if self.getGroupName():
|
||||||
|
group = CHILD_PREFIX + self.getGroupName()
|
||||||
|
return group
|
||||||
|
|
||||||
|
def getGroupNameFromPersistGroupName(self, persist_group_name) -> str:
|
||||||
|
if persist_group_name.startswith(CHILD_PREFIX):
|
||||||
|
persist_group_name = persist_group_name[len(CHILD_PREFIX):]
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
return persist_group_name
|
||||||
|
|
||||||
|
def loadPerspectivesRec(self, settings: QSettings) -> None:
|
||||||
|
children = self.allManagers(True, True)
|
||||||
|
|
||||||
|
for mgr in children:
|
||||||
|
settings.beginGroup(mgr.getPersistGroupName())
|
||||||
|
mgr.loadPerspectives(settings)
|
||||||
|
settings.endGroup()
|
||||||
|
|
||||||
|
def savePerspectivesRec(self, settings: QSettings) -> None:
|
||||||
|
children = self.allManagers(True, True)
|
||||||
|
|
||||||
|
for mgr in children:
|
||||||
|
settings.beginGroup(mgr.getPersistGroupName())
|
||||||
|
mgr.savePerspectives(settings)
|
||||||
|
settings.endGroup()
|
||||||
|
|
||||||
|
def removePerspectivesRec(self, settings: QSettings) -> None:
|
||||||
|
children = self.allManagers(True, True)
|
||||||
|
|
||||||
|
for mgr in children:
|
||||||
|
child.removePerspectives(child.perspectiveNames())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def dockInAManager(widget) -> 'DockInDockManager':
|
||||||
|
from dockindock import DockInDockWidget # Prevent cyclic import
|
||||||
|
|
||||||
|
dock_widget = widget.widget() if widget else None
|
||||||
|
return dock_widget.getManager() if isinstance(dock_widget, DockInDockWidget) else None
|
||||||
|
|
||||||
|
def childManagers(self, managers: 'list[DockInDockManager]', rec: bool) -> None:
|
||||||
|
widgets = self.getWidgetsInGUIOrder()
|
||||||
|
for widget in widgets:
|
||||||
|
as_mgr = DockInDockManager.dockInAManager(widget)
|
||||||
|
if as_mgr:
|
||||||
|
managers.append(as_mgr)
|
||||||
|
if rec:
|
||||||
|
as_mgr.childManagers(managers, rec)
|
||||||
|
|
||||||
|
def allManagers(self, include_self: bool, rec: bool) -> 'list[DockInDockManager]':
|
||||||
|
managers = []
|
||||||
|
if include_self:
|
||||||
|
managers.append(self)
|
||||||
|
self.childManagers(managers, rec)
|
||||||
|
return managers
|
||||||
|
|
||||||
|
def allDockWidgets(self, include_self: bool, rec: bool) -> 'list[tuple[DockInDockManager, QtAds.CDockWidget]]':
|
||||||
|
widgets = []
|
||||||
|
for mgr in self.allManagers(include_self, rec):
|
||||||
|
for widget in mgr.getWidgetsInGUIOrder():
|
||||||
|
widgets.append((mgr, widget))
|
||||||
|
return widgets
|
||||||
|
|
||||||
|
def getGroupContents(self) -> 'dict[str, list[str]]':
|
||||||
|
result = {}
|
||||||
|
managers = self.allManagers(True, True)
|
||||||
|
for mgr in managers:
|
||||||
|
result[mgr.getPersistGroupName()] = mgr.dockWidgetsMap().keys()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def getInsertDefaultPos(self) -> QtAds.CDockAreaWidget:
|
||||||
|
default_pos = None
|
||||||
|
if self.dockAreaCount() != 0:
|
||||||
|
default_pos = self.dockArea(self.dockAreaCount()-1)
|
||||||
|
return default_pos
|
||||||
|
|
||||||
|
def getWidgetsInGUIOrder(self) -> 'list[QtAds.CDockWidget]':
|
||||||
|
result = []
|
||||||
|
for i in range(self.dockAreaCount()):
|
||||||
|
for widget in self.dockArea(i).dockWidgets():
|
||||||
|
result.append(widget)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class CreateChildDockAction(QAction):
|
||||||
|
def __init__(self, dock_in_dock: 'DockInDockWidget', menu: QMenu):
|
||||||
|
super().__init__("New group...", menu)
|
||||||
|
self.__dock_in_dock = dock_in_dock
|
||||||
|
self.triggered.connect(self.createGroup)
|
||||||
|
|
||||||
|
def createGroup(self) -> None:
|
||||||
|
name = ""
|
||||||
|
while True:
|
||||||
|
name, ok = QInputDialog.getText(None, self.text(), "Enter group name", QLineEdit.Normal, name)
|
||||||
|
if ok:
|
||||||
|
error = ""
|
||||||
|
if self.__dock_in_dock.getTopLevelDockWidget():
|
||||||
|
error = self.__dock_in_dock.getTopLevelDockWidget().getGroupNameError(name)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
if not error:
|
||||||
|
self.__dock_in_dock.createGroup(name, None)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
QMessageBox.critical(None, "Error", error)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
class DestroyGroupAction(QAction):
|
||||||
|
def __init__(self, widget: 'DockInDockWidget', menu: QMenu):
|
||||||
|
super().__init__("Destroy" + widget.getManager().getGroupName(), menu)
|
||||||
|
self.__widget = widget
|
||||||
|
self.triggered.connect(self.destroyGroup)
|
||||||
|
|
||||||
|
def destroyGroup(self) -> None:
|
||||||
|
self.__widget.getTopLevelDockWidget().destroyGroup(self.__widget)
|
||||||
|
|
||||||
|
|
||||||
|
class MoveDockWidgetAction(QAction):
|
||||||
|
def __init__(self, widget: 'DockInDockWidget', move_to: DockInDockManager, menu: QMenu):
|
||||||
|
super().__init__(menu)
|
||||||
|
self.__widget = widget
|
||||||
|
self.__move_to = move_to
|
||||||
|
|
||||||
|
if move_to.parent().isTopLevel():
|
||||||
|
self.setText("To top")
|
||||||
|
else:
|
||||||
|
self.setText(f"To {move_to.parent().objectName()}")
|
||||||
|
self.triggered.connect(self._move)
|
||||||
|
|
||||||
|
def _move(self) -> None:
|
||||||
|
self.move(self.__widget, self.__move_to)
|
||||||
|
|
||||||
|
def move(self, widget: QtAds.CDockWidget, move_to: QtAds.CDockManager) -> None:
|
||||||
|
if widget and move_to:
|
||||||
|
widget.dockManager().removeDockWidget(widget)
|
||||||
|
move_to.addDockWidget(QtAds.CenterDockWidgetArea, widget, move_to.getInsertDefaultPos())
|
||||||
|
else:
|
||||||
|
assert False
|
72
examples/dockindock/main.py
Normal file
72
examples/dockindock/main.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import atexit
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
from perspectives import PerspectivesManager
|
||||||
|
from dockindock import DockInDockWidget
|
||||||
|
|
||||||
|
class MainWindow(QMainWindow):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.perspectives_manager = PerspectivesManager("persist")
|
||||||
|
self.resize(400, 400)
|
||||||
|
self.dock_manager = DockInDockWidget(self, self.perspectives_manager, can_create_new_groups=True)
|
||||||
|
self.setCentralWidget(self.dock_manager)
|
||||||
|
self.dock_manager.attachViewMenu(self.menuBar().addMenu("View"))
|
||||||
|
|
||||||
|
previous_dock_widget = None
|
||||||
|
for i in range(3):
|
||||||
|
l = QLabel()
|
||||||
|
l.setWordWrap(True)
|
||||||
|
l.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||||
|
l.setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
|
||||||
|
|
||||||
|
previous_dock_widget = self.dock_manager.addTabWidget(l, f"Top label {i}", previous_dock_widget)
|
||||||
|
|
||||||
|
last_top_level_dock = previous_dock_widget
|
||||||
|
|
||||||
|
for j in range(2):
|
||||||
|
group_manager, _ = self.dock_manager.createGroup(f"Group {j}", last_top_level_dock)
|
||||||
|
|
||||||
|
previous_dock_widget = None
|
||||||
|
|
||||||
|
for i in range(3):
|
||||||
|
# Create example content label - this can be any application specific widget
|
||||||
|
l = QLabel()
|
||||||
|
l.setWordWrap(True)
|
||||||
|
l.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||||
|
l.setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
|
||||||
|
|
||||||
|
previous_dock_widget = group_manager.addTabWidget(l, f"ZInner {j}/{i}", previous_dock_widget)
|
||||||
|
|
||||||
|
# create sub-group
|
||||||
|
sub_group, _ = group_manager.createGroup(f"SubGroup {j}", previous_dock_widget)
|
||||||
|
previous_dock_widget = None
|
||||||
|
for i in range(3):
|
||||||
|
# Create example content label - this can be any application specific widget
|
||||||
|
l = QLabel()
|
||||||
|
l.setWordWrap(True)
|
||||||
|
l.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||||
|
l.setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
|
||||||
|
|
||||||
|
previous_dock_widget = sub_group.addTabWidget(l, f"SubInner {j}/{i}", previous_dock_widget)
|
||||||
|
|
||||||
|
self.perspectives_manager.loadPerspectives()
|
||||||
|
|
||||||
|
atexit.register(self.cleanup)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.perspectives_manager.savePerspectives()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
w = MainWindow()
|
||||||
|
w.show()
|
||||||
|
app.exec_()
|
25
examples/dockindock/perspectiveactions.py
Normal file
25
examples/dockindock/perspectiveactions.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from PyQt5.QtWidgets import QAction, QMenu
|
||||||
|
|
||||||
|
|
||||||
|
class LoadPerspectiveAction(QAction):
|
||||||
|
def __init__(self, parent: QMenu, name: str, dock_manager: 'DockInDockWidget'):
|
||||||
|
super().__init__(name, parent)
|
||||||
|
self.name = name
|
||||||
|
self.dock_manager = dock_manager
|
||||||
|
|
||||||
|
self.triggered.connect(self.load)
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
self.dock_manager.getPerspectivesManager().openPerspective(self.name, self.dock_manager)
|
||||||
|
|
||||||
|
|
||||||
|
class RemovePerspectiveAction(QAction):
|
||||||
|
def __init__(self, parent: QMenu, name: str, dock_manager: 'DockInDockWidget'):
|
||||||
|
super().__init__(name, parent)
|
||||||
|
self.name = name
|
||||||
|
self.dock_manager = dock_manager
|
||||||
|
|
||||||
|
self.triggered.connect(self.remove)
|
||||||
|
|
||||||
|
def remove(self):
|
||||||
|
self.dock_manager.getPerspectivesManager().removePerspective(self.name)
|
203
examples/dockindock/perspectives.py
Normal file
203
examples/dockindock/perspectives.py
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import shutil
|
||||||
|
import atexit
|
||||||
|
|
||||||
|
from PyQt5.QtCore import pyqtSignal, QSettings, QObject
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
from dockindockmanager import DockInDockManager
|
||||||
|
from dockindock import DockInDockWidget
|
||||||
|
|
||||||
|
GROUP_PREFIX = "Group"
|
||||||
|
|
||||||
|
def findWidget(name, managers: 'list[DockInDockManager]') -> QtAds.CDockWidget:
|
||||||
|
for mgr in managers:
|
||||||
|
widget = mgr.findDockWidget(name)
|
||||||
|
if widget:
|
||||||
|
return widget
|
||||||
|
|
||||||
|
|
||||||
|
class PerspectiveInfo:
|
||||||
|
# Partially bypass ADS perspective management, store list here
|
||||||
|
# and then ADS will only have one perspective loaded
|
||||||
|
# this is because all docking widgets must exist when a perspective is loaded
|
||||||
|
# we will guarantee that!
|
||||||
|
|
||||||
|
settings = QSettings()
|
||||||
|
groups: 'dict[str, list[str]]' = {}
|
||||||
|
|
||||||
|
|
||||||
|
class PerspectivesManager(QObject):
|
||||||
|
perspectivesListChanged = pyqtSignal()
|
||||||
|
openingPerspective = pyqtSignal()
|
||||||
|
openedPerspective = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, perspectives_folder):
|
||||||
|
super().__init__()
|
||||||
|
self.__perspectives_folder = perspectives_folder
|
||||||
|
self.__perspectives = {}
|
||||||
|
atexit.register(self.cleanup)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
for perspective in self.__perspectives.values():
|
||||||
|
filename = perspective.settings.fileName()
|
||||||
|
try:
|
||||||
|
os.remove(filename)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def perspectiveNames(self) -> 'list[str]':
|
||||||
|
return self.__perspectives.keys()
|
||||||
|
|
||||||
|
def addPerspective(self, name: str, widget: DockInDockWidget) -> None:
|
||||||
|
if self.__perspectives_folder:
|
||||||
|
self.__perspectives[name] = perspective = PerspectiveInfo()
|
||||||
|
perspective.settings = self.getSettingsObject(self.getSettingsFileName(name, True))
|
||||||
|
perspective.groups = widget.getManager().getGroupContents()
|
||||||
|
|
||||||
|
# save perspective internally
|
||||||
|
widget.getManager().addPerspectiveRec(name)
|
||||||
|
# store it in QSettings object
|
||||||
|
widget.getManager().savePerspectivesRec(perspective.settings)
|
||||||
|
# remove internal perspectives
|
||||||
|
widget.getManager().removePerspectives(widget.getManager().perspectiveNames())
|
||||||
|
|
||||||
|
self.perspectivesListChanged.emit()
|
||||||
|
|
||||||
|
def openPerspective(name: str, widget: DockInDockWidget) -> None:
|
||||||
|
assert widget.getTopLevelDockWidget() == widget
|
||||||
|
|
||||||
|
if self.__perspectives_folder:
|
||||||
|
if name in self.__perspectives:
|
||||||
|
self.openingPerspective.emit()
|
||||||
|
|
||||||
|
if widget.canCreateNewGroups():
|
||||||
|
cur_groups = widget.getManager().allManagers(True, True)
|
||||||
|
for group in self.__perspectives[name].groups.keys():
|
||||||
|
found = False
|
||||||
|
for curgroup in cur_groups:
|
||||||
|
if curgroup.getPerspectiveGroupName() == group:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
group = DockInDockManager.getGroupNameFromPersistGroupName(group)
|
||||||
|
|
||||||
|
# restore group in file but not in GUI yet
|
||||||
|
widget.createGroup(group, None)
|
||||||
|
|
||||||
|
cur_groups = widget.getManager().allManagers(False, True)
|
||||||
|
for curgroup in cur_groups:
|
||||||
|
if curgroup.getPersistGroupName() not in self.__perspectives[name].groups.keys():
|
||||||
|
widget.destroyGroup(curgroup.parent())
|
||||||
|
|
||||||
|
managers = widget.getManager().allManagers(True, True)
|
||||||
|
for group in self.__perspectives[name].groups().keys():
|
||||||
|
for mgr in managers:
|
||||||
|
if mgr.getPersistGroupName() == group:
|
||||||
|
for widget_name in self.__perspectives[name].groups[group]:
|
||||||
|
widget = findWidget(widget_name, [mgr])
|
||||||
|
if widget:
|
||||||
|
pass # OK, widget is already in the good manager!
|
||||||
|
else:
|
||||||
|
widget = findWidget(widget_name, managers)
|
||||||
|
if widget:
|
||||||
|
# move dock widget in the same group as it used to be when perspective was saved
|
||||||
|
# this guarantee load/open perspectives will work smartly
|
||||||
|
MoveDockWidgetAction.move(widget, mgr)
|
||||||
|
|
||||||
|
# internally load perspectives from QSettings
|
||||||
|
widget.getManager().loadPerspectivesRec(self.__perspectives[name].settings)
|
||||||
|
# load perspective (update GUI)
|
||||||
|
widget.getManager().openPerspectiveRec(name)
|
||||||
|
# remove internal perspectives
|
||||||
|
widget.getManager().removePerspectives(widget.getManager().perspectiveNames())
|
||||||
|
|
||||||
|
self.openedPerspective().emit()
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def removePerspectives(self) -> None:
|
||||||
|
self.__perspectives.clear()
|
||||||
|
self.perspectivesListChanged.emit()
|
||||||
|
|
||||||
|
def removePerspective(self, name: str) -> None:
|
||||||
|
del self.__perspectives[name]
|
||||||
|
self.perspectivesListChanged.emit()
|
||||||
|
|
||||||
|
def getSettingsFileName(self, perspective: str, temp: bool) -> str:
|
||||||
|
name = "perspectives.ini" if not perspective else f"perspectives_{perspective + '.tmp' if temp else perspective + '.ini'}"
|
||||||
|
|
||||||
|
return os.path.join(self.__perspectives_folder, name)
|
||||||
|
|
||||||
|
def getSettingsObject(self, file_path: str) -> QSettings:
|
||||||
|
return QSettings(file_path, QSettings.IniFormat)
|
||||||
|
|
||||||
|
def loadPerspectives(self) -> None:
|
||||||
|
if self.__perspectives_folder:
|
||||||
|
tempfile.mktemp(dir=self.__perspectives_folder)
|
||||||
|
|
||||||
|
self.__perspectives.clear()
|
||||||
|
|
||||||
|
main_settings = self.getSettingsObject(self.getSettingsFileName("", False))
|
||||||
|
debug = main_settings.fileName()
|
||||||
|
|
||||||
|
size = main_settings.beginReadArray("Perspectives")
|
||||||
|
|
||||||
|
for i in range(0, size):
|
||||||
|
main_settings.setArrayIndex(i)
|
||||||
|
perspective = main_settings.value("Name")
|
||||||
|
|
||||||
|
if perspective:
|
||||||
|
to_load = self.getSettingsFileName(perspective, False)
|
||||||
|
loaded = self.getSettingsFileName(perspective, True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.remove(loaded)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
if not shutil.copy(to_load, loaded):
|
||||||
|
assert False
|
||||||
|
|
||||||
|
self.__perspectives[perspective] = PerspectiveInfo()
|
||||||
|
self.__perspectives[perspective].settings = self.getSettingsObject(loaded)
|
||||||
|
|
||||||
|
# load group info:
|
||||||
|
main_settings.beginGroup(GROUP_PREFIX)
|
||||||
|
for key in main_settings.allKeys():
|
||||||
|
self.__perspectives[perspective].groups[key] = main_settings.value(key)
|
||||||
|
main_settings.endGroup()
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
main_settings.endArray()
|
||||||
|
|
||||||
|
self.perspectivesListChanged.emit()
|
||||||
|
|
||||||
|
def savePerspectives(self) -> None:
|
||||||
|
if self.__perspectives_folder:
|
||||||
|
main_settings = self.getSettingsObject(self.getSettingsFileName("", False))
|
||||||
|
|
||||||
|
# Save list of perspective and group organization
|
||||||
|
main_settings.beginWriteArray("Perspectives", len(self.__perspectives))
|
||||||
|
for i, perspective in enumerate(self.__perspectives.keys()):
|
||||||
|
main_settings.setArrayIndex(i)
|
||||||
|
main_settings.setValue("Name", perspective)
|
||||||
|
main_settings.beginGroup(GROUP_PREFIX)
|
||||||
|
for group in self.__perspectives[perspective].groups.keys():
|
||||||
|
main_settings.setValue(group, list(self.__perspectives[perspective].groups[group]))
|
||||||
|
main_settings.endGroup()
|
||||||
|
main_settings.endArray()
|
||||||
|
|
||||||
|
# Save perspectives themselves
|
||||||
|
for perspective_name in self.__perspectives.keys():
|
||||||
|
to_save = self.getSettingsFileName(perspective_name, False)
|
||||||
|
settings = self.__perspectives[perspective_name].settings
|
||||||
|
settings.sync()
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.remove(to_save)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
if not shutil.copy(settings.fileName(), to_save):
|
||||||
|
assert False
|
108
examples/emptydockarea/main.py
Normal file
108
examples/emptydockarea/main.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
from PyQt5 import uic
|
||||||
|
from PyQt5.QtCore import Qt, QSignalBlocker
|
||||||
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QComboBox, QTableWidget,
|
||||||
|
QAction, QWidgetAction, QSizePolicy, QInputDialog)
|
||||||
|
from PyQt5.QtGui import QCloseEvent
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
|
||||||
|
UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
|
||||||
|
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
class CMainWindow(MainWindowUI, MainWindowBase):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.OpaqueSplitterResize, True)
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.XmlCompressionEnabled, False)
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FocusHighlighting, True)
|
||||||
|
self.dock_manager = QtAds.CDockManager(self)
|
||||||
|
|
||||||
|
# Set central widget
|
||||||
|
label = QLabel()
|
||||||
|
label.setText("This is a DockArea which is always visible, even if it does not contain any DockWidgets.")
|
||||||
|
label.setAlignment(Qt.AlignCenter)
|
||||||
|
central_dock_widget = QtAds.CDockWidget("CentralWidget")
|
||||||
|
central_dock_widget.setWidget(label)
|
||||||
|
central_dock_widget.setFeature(QtAds.CDockWidget.NoTab, True)
|
||||||
|
central_dock_area = self.dock_manager.setCentralWidget(central_dock_widget)
|
||||||
|
|
||||||
|
# create other dock widgets
|
||||||
|
table = QTableWidget()
|
||||||
|
table.setColumnCount(3)
|
||||||
|
table.setRowCount(10)
|
||||||
|
table_dock_widget = QtAds.CDockWidget("Table 1")
|
||||||
|
table_dock_widget.setWidget(table)
|
||||||
|
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
|
table_dock_widget.resize(250, 150)
|
||||||
|
table_dock_widget.setMinimumSize(200,150)
|
||||||
|
self.dock_manager.addDockWidgetTabToArea(table_dock_widget, central_dock_area)
|
||||||
|
table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, table_dock_widget)
|
||||||
|
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
|
table = QTableWidget()
|
||||||
|
table.setColumnCount(5)
|
||||||
|
table.setRowCount(1020)
|
||||||
|
table_dock_widget = QtAds.CDockWidget("Table 2")
|
||||||
|
table_dock_widget.setWidget(table)
|
||||||
|
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
|
table_dock_widget.resize(250, 150)
|
||||||
|
table_dock_widget.setMinimumSize(200,150)
|
||||||
|
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, table_area)
|
||||||
|
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
|
properties_table = QTableWidget()
|
||||||
|
properties_table.setColumnCount(3)
|
||||||
|
properties_table.setRowCount(10)
|
||||||
|
properties_dock_widget = QtAds.CDockWidget("Properties")
|
||||||
|
properties_dock_widget.setWidget(properties_table)
|
||||||
|
properties_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
|
properties_dock_widget.resize(250, 150)
|
||||||
|
properties_dock_widget.setMinimumSize(200,150)
|
||||||
|
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.RightDockWidgetArea, properties_dock_widget, central_dock_area)
|
||||||
|
self.menuView.addAction(properties_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
|
self.createPerspectiveUi()
|
||||||
|
|
||||||
|
def createPerspectiveUi(self):
|
||||||
|
save_perspective_action = QAction("Create Perspective", self)
|
||||||
|
save_perspective_action.triggered.connect(self.savePerspective)
|
||||||
|
perspective_list_action = QWidgetAction(self)
|
||||||
|
self.perspective_combo_box = QComboBox(self)
|
||||||
|
self.perspective_combo_box.setSizeAdjustPolicy(QComboBox.AdjustToContents)
|
||||||
|
self.perspective_combo_box.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
|
||||||
|
self.perspective_combo_box.activated[str].connect(self.dock_manager.openPerspective)
|
||||||
|
perspective_list_action.setDefaultWidget(self.perspective_combo_box)
|
||||||
|
self.toolBar.addSeparator()
|
||||||
|
self.toolBar.addAction(perspective_list_action)
|
||||||
|
self.toolBar.addAction(save_perspective_action)
|
||||||
|
|
||||||
|
def savePerspective(self):
|
||||||
|
perspective_name, ok = QInputDialog.getText(self, "Save Perspective", "Enter unique name:")
|
||||||
|
if not perspective_name or not ok:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.dock_manager.addPerspective(perspective_name)
|
||||||
|
blocker = QSignalBlocker(self.perspective_combo_box)
|
||||||
|
self.perspective_combo_box.clear()
|
||||||
|
self.perspective_combo_box.addItems(self.dock_manager.perspectiveNames())
|
||||||
|
self.perspective_combo_box.setCurrentText(perspective_name)
|
||||||
|
|
||||||
|
def closeEvent(self, event: QCloseEvent):
|
||||||
|
# Delete dock manager here to delete all floating widgets. This ensures
|
||||||
|
# that all top level windows of the dock manager are properly closed
|
||||||
|
self.dock_manager.deleteLater()
|
||||||
|
super().closeEvent(event)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
w = CMainWindow()
|
||||||
|
w.show()
|
||||||
|
app.exec_()
|
@ -21,6 +21,7 @@ public:
|
|||||||
void notifyWidgetOrAreaRelocation(QWidget* RelocatedWidget);
|
void notifyWidgetOrAreaRelocation(QWidget* RelocatedWidget);
|
||||||
void notifyFloatingWidgetDrop(ads::CFloatingDockContainer* FloatingWidget);
|
void notifyFloatingWidgetDrop(ads::CFloatingDockContainer* FloatingWidget);
|
||||||
ads::CDockWidget* focusedDockWidget() const;
|
ads::CDockWidget* focusedDockWidget() const;
|
||||||
|
void setDockWidgetTabFocused(ads::CDockWidgetTab* Tab);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setDockWidgetFocused(ads::CDockWidget* focusedNow);
|
void setDockWidgetFocused(ads::CDockWidget* focusedNow);
|
||||||
|
@ -33,8 +33,10 @@ public:
|
|||||||
CustomCloseHandling,
|
CustomCloseHandling,
|
||||||
DockWidgetFocusable,
|
DockWidgetFocusable,
|
||||||
DockWidgetForceCloseWithArea,
|
DockWidgetForceCloseWithArea,
|
||||||
|
NoTab,
|
||||||
DefaultDockWidgetFeatures,
|
DefaultDockWidgetFeatures,
|
||||||
AllDockWidgetFeatures,
|
AllDockWidgetFeatures,
|
||||||
|
DockWidgetAlwaysCloseAndDelete,
|
||||||
NoDockWidgetFeatures
|
NoDockWidgetFeatures
|
||||||
};
|
};
|
||||||
typedef QFlags<ads::CDockWidget::DockWidgetFeature> DockWidgetFeatures;
|
typedef QFlags<ads::CDockWidget::DockWidgetFeature> DockWidgetFeatures;
|
||||||
|
@ -2,6 +2,40 @@
|
|||||||
|
|
||||||
%If (Qt_5_0_0 -)
|
%If (Qt_5_0_0 -)
|
||||||
|
|
||||||
|
%ModuleHeaderCode
|
||||||
|
PyObject *qtads_FindParent(PyObject* type, const QWidget *child);
|
||||||
|
%End
|
||||||
|
|
||||||
|
%ModuleCode
|
||||||
|
PyObject *qtads_FindParent(PyObject* type, const QWidget *w)
|
||||||
|
{
|
||||||
|
// Check that the types checking was successful.
|
||||||
|
if (!type)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
QWidget* parentWidget = w->parentWidget();
|
||||||
|
|
||||||
|
while (parentWidget)
|
||||||
|
{
|
||||||
|
PyObject *ParentImpl = sipConvertFromType(parentWidget, sipType_QObject, 0);
|
||||||
|
if (!ParentImpl)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyType_IsSubtype((PyTypeObject *)type, Py_TYPE(ParentImpl)))
|
||||||
|
return ParentImpl;
|
||||||
|
|
||||||
|
Py_DECREF(ParentImpl);
|
||||||
|
|
||||||
|
parentWidget = parentWidget->parentWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
%End
|
||||||
|
|
||||||
namespace ads
|
namespace ads
|
||||||
{
|
{
|
||||||
%TypeHeaderCode
|
%TypeHeaderCode
|
||||||
@ -55,6 +89,50 @@ namespace ads
|
|||||||
BitwiseOr
|
BitwiseOr
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace internal
|
||||||
|
{
|
||||||
|
void replaceSplitterWidget(QSplitter* Splitter, QWidget* From, QWidget* To);
|
||||||
|
void hideEmptyParentSplitters(ads::CDockSplitter* FirstParentSplitter);
|
||||||
|
|
||||||
|
class CDockInsertParam
|
||||||
|
{
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include <ads_globals.h>
|
||||||
|
%End
|
||||||
|
|
||||||
|
public:
|
||||||
|
Qt::Orientation orientation() const;
|
||||||
|
bool append() const;
|
||||||
|
int insertOffset() const;
|
||||||
|
};
|
||||||
|
ads::internal::CDockInsertParam dockAreaInsertParameters(ads::DockWidgetArea Area);
|
||||||
|
|
||||||
|
SIP_PYOBJECT findParent(SIP_PYTYPE type, const QWidget *w) const /TypeHint="QObject"/;
|
||||||
|
%MethodCode
|
||||||
|
sipRes = qtads_FindParent(a0, a1);
|
||||||
|
|
||||||
|
if (!sipRes)
|
||||||
|
{
|
||||||
|
sipIsErr = 1;
|
||||||
|
}
|
||||||
|
%End
|
||||||
|
|
||||||
|
QPixmap createTransparentPixmap(const QPixmap& Source, qreal Opacity);
|
||||||
|
|
||||||
|
QPoint globalPositionOf(QMouseEvent* ev);
|
||||||
|
|
||||||
|
void setButtonIcon(QAbstractButton* Button, QStyle::StandardPixmap StandarPixmap, ads::eIcon CustomIconId);
|
||||||
|
|
||||||
|
enum eRepolishChildOptions
|
||||||
|
{
|
||||||
|
RepolishIgnoreChildren,
|
||||||
|
RepolishDirectChildren,
|
||||||
|
RepolishChildrenRecursively
|
||||||
|
};
|
||||||
|
|
||||||
|
void repolishStyle(QWidget* w, ads::internal::eRepolishChildOptions Options = ads::internal::RepolishIgnoreChildren);
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
%End
|
%End
|
||||||
|
Loading…
Reference in New Issue
Block a user