from PyQt5.QtCore import Qt, pyqtSignal, QPoint from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import (QFileDialog, QHBoxLayout, QMdiSubWindow, QMenu, QPushButton, QSizePolicy, QSplitter, QWidget, QStyle) from . import dialogs from .graphics import customView from .canvas import canvas from .tabs import customTabWidget from .undo import resizeCommand from .app import dump, loads, JSON_Typer class fileWindow(QMdiSubWindow): """ This defines a single file, inside the application, consisting of multiple tabs that contain canvases. Pre-Defined so that a file can be instantly created without defining the structure again. """ fileCloseEvent = pyqtSignal(int) tabChangeEvent = pyqtSignal() def __init__(self, parent = None, title = 'New Project', size = 'A4', ppi = '72'): super(fileWindow, self).__init__(parent) self._sideViewTab = None self.index = None self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) #Uses a custom QTabWidget that houses a custom new Tab Button, used to house the seperate # diagrams inside a file self.tabber = customTabWidget(self) self.tabber.setObjectName(title) #store title as object name for pickling self.tabber.tabCloseRequested.connect(self.closeTab) # Show save alert on tab close self.tabber.currentChanged.connect(self.changeTab) # placeholder just to detect tab change self.tabber.plusClicked.connect(self.newDiagram) #connect the new tab button to add a new tab #assign layout to widget self.mainWidget = QWidget(self) layout = QHBoxLayout(self.mainWidget) self.createSideViewArea() #create the side view objects layout.addWidget(self.tabber) layout.addWidget(self.splitter) layout.addWidget(self.sideView) self.mainWidget.setLayout(layout) self.setWidget(self.mainWidget) self.setWindowTitle(title) #This is done so that a right click menu is shown on right click self.tabber.setContextMenuPolicy(Qt.CustomContextMenu) self.tabber.customContextMenuRequested.connect(self.contextMenu) # self.setAttribute(Qt.WA_DeleteOnClose, True) self.setWindowFlag(Qt.CustomizeWindowHint, True) self.setWindowFlag(Qt.WindowMinimizeButtonHint, False) self.setWindowFlag(Qt.WindowMaximizeButtonHint, False) def createSideViewArea(self): #creates the side view widgets and sets them to invisible self.splitter = QSplitter(Qt.Vertical ,self) self.sideView = customView(parent = self) self.sideView.setInteractive(False) self.sideViewCloseButton = QPushButton('×', self.sideView) self.sideViewCloseButton.setFlat(True) self.sideViewCloseButton.setStyleSheet("""QPushButton{ background: rgba(214, 54, 40, 50%); border: 1px groove white; border-radius: 2px; font-size: 18px; font-weight: Bold; padding: 1px 2px 3px 3px; color: rgba(255, 255, 255, 50%); } QPushButton:Hover{ background: rgba(214, 54, 40, 90%); color: rgba(255, 255, 255, 90%); } """) self.sideViewCloseButton.setFixedSize(20, 20) self.moveSideViewCloseButton() self.sideViewCloseButton.clicked.connect(lambda: setattr(self, 'sideViewTab', None)) self.splitter.setVisible(False) self.sideView.setVisible(False) self.sideView.setContextMenuPolicy(Qt.CustomContextMenu) self.sideView.customContextMenuRequested.connect(self.sideViewContextMenu) def resizeHandler(self): # resize Handler to handle resize cases. parentRect = self.mdiArea().size() current = self.tabber.currentWidget() width, height = current.dimensions # if side view is visible, set width to maximum possible, else use minimum requirement if self.sideViewTab: width = parentRect.width() height = parentRect.height() self.moveSideViewCloseButton() else: width = min(parentRect.width(), width + 100) height = min(parentRect.height(), height + 200) if len(self.parent().parent().subWindowList()) > 1: height -= 20 # set element dimensions self.setFixedSize(width, height) self.tabber.resize(width, height) self.tabber.currentWidget().adjustView() def contextMenu(self, point): #function to display the right click menu at point of right click menu = QMenu("Context Menu", self) menu.addAction("Adjust Canvas", self.adjustCanvasDialog) menu.addAction("Remove Side View" if self.sideViewTab == self.tabber.currentWidget() else "View Side-By-Side", self.sideViewMode) menu.addAction("Reset Zoom", lambda : setattr(self.tabber.currentWidget().view, 'zoom', 1)) menu.exec_(self.tabber.mapToGlobal(point)) def sideViewMode(self): #helper context menu function to toggle side view self.sideViewTab = self.tabber.currentWidget() def adjustCanvasDialog(self): #helper context menu function to the context menu dialog box currentTab = self.tabber.currentWidget() result = dialogs.paperDims(self, currentTab._canvasSize, currentTab._ppi, currentTab.objectName()).exec_() if result is not None: currentTab.painter.undoStack.push(resizeCommand(result, currentTab, self)) def sideViewToggle(self): #Function checks if current side view tab is set, and toggles view as required if self.sideViewTab: self.splitter.setVisible(True) self.sideView.setVisible(True) self.sideView.setScene(self.tabber.currentWidget().painter) self.moveSideViewCloseButton() self.resizeHandler() return True else: self.splitter.setVisible(False) self.sideView.setVisible(False) self.resizeHandler() return False def moveSideViewCloseButton(self): # used to place side view close button at appropriate position self.sideViewCloseButton.move(5, 5) def sideViewContextMenu(self, point): # context menu for side view menu = QMenu("Context Menu", self.sideView) menu.addAction("Reset Zoom", lambda : setattr(self.sideView, 'zoom', 1)) menu.addSection('Change Side View Tab') if self.tabCount > 5: # just show switch dialog box, if there are 6 or more tabs open menu.addAction("Show switch menu", self.sideViewSwitchTab) else: # enumerate all tabs from side view. for i in range(self.tabCount): j = self.tabber.widget(i) if j == self.sideViewTab: continue # evaluate i as index, weird lambda behaviour #see https://stackoverflow.com/a/33984811/7799568 menu.addAction(f'{i}. {j.objectName()}', lambda index=i: self.sideViewSwitchCMenu(index)) menu.addAction("Remove side view", lambda : setattr(self, 'sideViewTab', None)) menu.exec_(self.sideView.mapToGlobal(point)) def sideViewSwitchCMenu(self, index): self.sideViewTab = self.tabber.widget(index) def sideViewSwitchTab(self): # displays a side view switch dialog box tabList = [f'{i}. {j.objectName()}' for i, j in enumerate(self.tabList)] #names and index of all tabs initial = self.tabList.index(self.sideViewTab) # current side view tab result = dialogs.sideViewSwitchDialog(self.tabber, tabList, initial).exec_() #call dialog box if result != initial: self.sideViewTab = self.tabber.widget(result) if result dict: return { "_classname_": self.__class__.__name__, "ObjectName": self.objectName(), "tabs": [i.__getstate__() for i in self.tabList] } def __setstate__(self, dict): self.setObjectName = dict['ObjectName'] self.setWindowTitle = dict['ObjectName'] for i in dict['tabs']: diagram = self.newDiagram(i['ObjectName']) diagram.__setstate__(i)