244 lines
9.5 KiB
Python
244 lines
9.5 KiB
Python
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QMessageBox
|
|
from PyQt5.QtCore import QObject, QEvent, QRect, QMargins, Qt, QMimeData
|
|
from PyQt5.QtGui import QPainter, QColor, QDragEnterEvent, QDropEvent
|
|
from enum import Enum
|
|
import re
|
|
|
|
|
|
class PlaceArea(Enum):
|
|
LeftArea = 0,
|
|
RightArea = 1,
|
|
TopArea = 2,
|
|
BottomArea = 3,
|
|
CenterArea = 4,
|
|
UndefineArea = 5,
|
|
|
|
|
|
class AcceptPanel(QWidget):
|
|
|
|
def __init__(self, mgr):
|
|
super(AcceptPanel, self).__init__(None)
|
|
self.left_rect = QRect()
|
|
self.right_rect = QRect()
|
|
self.top_rect = QRect()
|
|
self.bottom_rect = QRect()
|
|
self.center_rect = QRect()
|
|
self.hover_rect = QRect()
|
|
self.setMouseTracking(True)
|
|
self.setAcceptDrops(True)
|
|
|
|
self.mgr_inst = mgr
|
|
self.target_anchor :QWidget = None
|
|
self.target_area = PlaceArea.UndefineArea
|
|
|
|
def resizeEvent(self, a0):
|
|
total_rect = self.rect()
|
|
total_rect = total_rect - QMargins(5, 5, 5, 5)
|
|
anchor_width = 30
|
|
self.target_area = PlaceArea.UndefineArea
|
|
|
|
self.left_rect = QRect(int(total_rect.left()), int(total_rect.center().y() - anchor_width / 2), int(anchor_width), int(anchor_width))
|
|
self.right_rect = QRect(int(total_rect.right() - anchor_width), int(total_rect.center().y() - anchor_width / 2), int(anchor_width), int(anchor_width))
|
|
self.top_rect = QRect(int(total_rect.center().x() - anchor_width / 2), int(total_rect.top()), int(anchor_width), int(anchor_width))
|
|
self.bottom_rect = QRect(int(total_rect.center().x() - anchor_width / 2), int(total_rect.bottom() - anchor_width), int(anchor_width), int(anchor_width))
|
|
self.center_rect = QRect(int(total_rect.center().x() - anchor_width / 2), int(total_rect.center().y() - anchor_width / 2), int(anchor_width), int(anchor_width))
|
|
|
|
def async_with(self, target_view: QWidget):
|
|
self.resize(target_view.size())
|
|
self.target_anchor = target_view
|
|
|
|
def paintEvent(self, a0):
|
|
super(AcceptPanel, self).paintEvent(a0)
|
|
painter = QPainter(self)
|
|
painter.fillRect(self.rect(), Qt.lightGray)
|
|
painter.fillRect(self.hover_rect, Qt.gray)
|
|
|
|
painter.fillRect(self.top_rect, Qt.green)
|
|
painter.fillRect(self.bottom_rect, Qt.green)
|
|
painter.fillRect(self.left_rect, Qt.green)
|
|
painter.fillRect(self.right_rect, Qt.green)
|
|
painter.fillRect(self.center_rect, Qt.green)
|
|
|
|
def dragMoveEvent(self, a0):
|
|
self.hover_rect = self.rect()
|
|
|
|
if self.left_rect.contains(a0.pos()):
|
|
self.hover_rect.setWidth(int(self.hover_rect.width() / 3))
|
|
self.target_area = PlaceArea.LeftArea
|
|
elif self.right_rect.contains(a0.pos()):
|
|
self.target_area = PlaceArea.RightArea
|
|
self.hover_rect.setWidth(int(self.hover_rect.width() / 3))
|
|
self.hover_rect.moveLeft(int(self.rect().right() - self.hover_rect.width()))
|
|
elif self.top_rect.contains(a0.pos()):
|
|
self.target_area = PlaceArea.TopArea
|
|
self.hover_rect.setHeight(int(self.hover_rect.height() / 3))
|
|
elif self.center_rect.contains(a0.pos()):
|
|
self.target_area = PlaceArea.CenterArea
|
|
pass
|
|
elif self.bottom_rect.contains(a0.pos()):
|
|
self.target_area = PlaceArea.BottomArea
|
|
self.hover_rect.setHeight(int(self.hover_rect.height() / 3))
|
|
self.hover_rect.moveTop(int(self.rect().bottom() - self.hover_rect.height()))
|
|
else:
|
|
self.hover_rect = QRect()
|
|
self.target_area = PlaceArea.UndefineArea
|
|
|
|
self.update()
|
|
|
|
def dragEnterEvent(self, a0: QDragEnterEvent):
|
|
if a0.mimeData().hasFormat("text/plain"):
|
|
content = a0.mimeData().text()
|
|
regex = re.compile("view-drags\\(([^\\(\\)]+)\\)")
|
|
if regex.match(content):
|
|
a0.acceptProposedAction()
|
|
|
|
def dropEvent(self, a0: QDropEvent):
|
|
regex = re.compile("view-drags\\(([^\\(\\)]+)\\)")
|
|
result = regex.match(a0.mimeData().text())
|
|
if result:
|
|
from SplitPanel import SplitPanel, SplitType
|
|
from DockPanel import DockPanel
|
|
view_id = result.group(1)
|
|
adjust_view: DockPanel = self.mgr_inst.get_dockpanel(view_id)
|
|
abandon_frame = adjust_view.parent_res
|
|
if abandon_frame is None:
|
|
QMessageBox.critical(self, "操作无效", "视图不能与自身进行替换和拼接操作!");
|
|
self.setVisible(False)
|
|
self.setParent(self.__peak_window(self.target_anchor))
|
|
return
|
|
|
|
remains_attach_frame = abandon_frame.parent_res
|
|
target_view: DockPanel = self.target_anchor
|
|
|
|
if self.target_area == PlaceArea.CenterArea and not target_view.can_replace:
|
|
self.setVisible(False)
|
|
return
|
|
if self.target_area == PlaceArea.UndefineArea:
|
|
self.setVisible(False)
|
|
return
|
|
|
|
# 移除源视图
|
|
self_siblings = abandon_frame.child()
|
|
if remains_attach_frame is None:
|
|
main_window:QMainWindow = abandon_frame.parent()
|
|
views = [self_siblings[0], self_siblings[1]]
|
|
views.pop(views.index(adjust_view))
|
|
main_window.setCentralWidget(views[0])
|
|
views[0].setParent(main_window)
|
|
views[0].parent_res = None
|
|
else:
|
|
if self_siblings[0] == adjust_view:
|
|
remains_attach_frame.replace_view(self_siblings[1], abandon_frame)
|
|
else:
|
|
remains_attach_frame.replace_view(self_siblings[0], abandon_frame)
|
|
|
|
abandon_frame.setParent(None)
|
|
abandon_frame.parent_res = None
|
|
del abandon_frame
|
|
|
|
place_frame = target_view.parent_res
|
|
split_group:QWidget = None # 声明类型
|
|
if self.target_area == PlaceArea.LeftArea:
|
|
split_group = SplitPanel(adjust_view, target_view, SplitType.SPLIT_H)
|
|
split_group.set_split_info(SplitType.SPLIT_H, 1/3)
|
|
elif self.target_area == PlaceArea.RightArea:
|
|
split_group = SplitPanel(target_view, adjust_view, SplitType.SPLIT_H)
|
|
split_group.set_split_info(SplitType.SPLIT_H, 2/3)
|
|
elif self.target_area == PlaceArea.TopArea:
|
|
split_group = SplitPanel(adjust_view, target_view, SplitType.SPLIT_V)
|
|
split_group.set_split_info(SplitType.SPLIT_V, 1/3)
|
|
elif self.target_area == PlaceArea.BottomArea:
|
|
split_group = SplitPanel(target_view, adjust_view, SplitType.SPLIT_V)
|
|
split_group.set_split_info(SplitType.SPLIT_V, 2/3)
|
|
elif self.target_area == PlaceArea.CenterArea:
|
|
split_group = adjust_view
|
|
|
|
if place_frame is None:
|
|
main_window = target_view.parent()
|
|
main_window.setCentralWidget(split_group)
|
|
split_group.parent_res = None
|
|
target_view.setVisible(False)
|
|
self.mgr_inst.remove_dockpanel(target_view)
|
|
del target_view
|
|
else:
|
|
place_frame.replace_view(split_group, target_view)
|
|
|
|
self.setVisible(False)
|
|
self.setParent(self.__peak_window(self.target_anchor))
|
|
|
|
def __peak_window(self, obj: QWidget):
|
|
if obj is None:
|
|
return None
|
|
|
|
if obj.__class__.__name__ == "QMainWindow":
|
|
return obj
|
|
|
|
return self.__peak_window(obj.parent())
|
|
|
|
|
|
class DragManager(QObject):
|
|
__unique_inst: 'DragManager' = None
|
|
|
|
def __init__(self):
|
|
super(DragManager, self).__init__()
|
|
self.__dock_map = {}
|
|
self.__accept_panel = AcceptPanel(self)
|
|
self.__accept_panel.setVisible(False)
|
|
|
|
@classmethod
|
|
def instance(cls):
|
|
if cls.__unique_inst is None:
|
|
cls.__unique_inst = DragManager()
|
|
return cls.__unique_inst
|
|
|
|
def __peak_window(self, obj: QWidget):
|
|
if obj is None:
|
|
return None
|
|
|
|
if obj.__class__.__name__ == "QMainWindow":
|
|
return obj
|
|
|
|
return self.__peak_window(obj.parent())
|
|
|
|
def eventFilter(self, sender: QObject | None, event: QEvent | None):
|
|
if event.type() == QEvent.Type.DragMove:
|
|
if sender.isWindowType():
|
|
sender.requestActivate()
|
|
|
|
for ip in self.__dock_map:
|
|
view_inst: QWidget = self.get_dockpanel(ip)
|
|
if view_inst is None:
|
|
return False
|
|
|
|
gpos = sender.mapToGlobal(event.pos())
|
|
point = view_inst.mapFromGlobal(gpos)
|
|
if (view_inst.rect().contains(point)) and (self.__peak_window(view_inst).isActiveWindow()):
|
|
self.__accept_panel.setParent(view_inst)
|
|
self.__accept_panel.raise_()
|
|
self.__accept_panel.async_with(view_inst)
|
|
self.__accept_panel.setVisible(True)
|
|
pass
|
|
pass
|
|
pass
|
|
|
|
return super(DragManager, self).eventFilter(sender, event)
|
|
|
|
def register_dockpanel(self, view):
|
|
self.__dock_map[str(view.__hash__())] = view
|
|
|
|
def remove_dockpanel(self, view):
|
|
if str(view.__hash__()) in self.__dock_map:
|
|
self.__dock_map.pop(str(view.__hash__()))
|
|
|
|
def get_dockpanel(self, address: str):
|
|
if address in self.__dock_map:
|
|
return self.__dock_map[address]
|
|
return None
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app = QApplication([])
|
|
accept = AcceptPanel(None)
|
|
accept.show()
|
|
app.exec()
|