1############################################################################
2##
3## Copyright (C) 2016 The Qt Company Ltd.
4## Contact: https://www.qt.io/licensing/
5##
6## This file is part of the examples of Qt for Python.
7##
8## $QT_BEGIN_LICENSE:BSD$
9## Commercial License Usage
10## Licensees holding valid commercial Qt licenses may use this file in
11## accordance with the commercial license agreement provided with the
12## Software or, alternatively, in accordance with the terms contained in
13## a written agreement between you and The Qt Company. For licensing terms
14## and conditions see https://www.qt.io/terms-conditions. For further
15## information use the contact form at https://www.qt.io/contact-us.
16##
17## BSD License Usage
18## Alternatively, you may use this file under the terms of the BSD license
19## as follows:
20##
21## "Redistribution and use in source and binary forms, with or without
22## modification, are permitted provided that the following conditions are
23## met:
24##   * Redistributions of source code must retain the above copyright
25##     notice, this list of conditions and the following disclaimer.
26##   * Redistributions in binary form must reproduce the above copyright
27##     notice, this list of conditions and the following disclaimer in
28##     the documentation and/or other materials provided with the
29##     distribution.
30##   * Neither the name of The Qt Company Ltd nor the names of its
31##     contributors may be used to endorse or promote products derived
32##     from this software without specific prior written permission.
33##
34##
35## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46##
47## $QT_END_LICENSE$
48##
49############################################################################
50
51from PySide2.QtGui import *
52
53class QMdiSubWindow(QMainWindow):
54    def __init__(self, parent=None):
55        QMainWindow.__init__(self, parent)
56
57        mdiArea =  QMdiArea()
58        mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
59        mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
60        setCentralWidget(mdiArea)
61        mdiArea.subWindowActivated[QMdiSubWindow].connect(updateMenus)
62        windowMapper =  QSignalMapper(self)
63        windowMapper.mapped[QWidget].connect(setActiveSubWindow)
64
65        self.createActions()
66        self.createMenus()
67        self.createToolBars()
68        self.createStatusBar()
69        self.updateMenus()
70        self.readSettings()
71        self.setWindowTitle(tr("MDI"))
72        self.setUnifiedTitleAndToolBarOnMac(True)
73
74
75    def closeEvent(self, event):
76        mdiArea.closeAllSubWindows()
77        if self.activeMdiChild():
78            event.ignore()
79        else:
80            self.writeSettings()
81            event.accept()
82
83    def File(self):
84        child = self.createMdiChild()
85        child.File()
86        child.show()
87
88
89    def open(self):
90        fileName = QFileDialog.getOpenFileName(self)
91        if not fileName.isEmpty():
92            existing = self.findMdiChild(fileName)
93            if existing:
94                mdiArea.setActiveSubWindow(existing)
95                return
96
97            child = createMdiChild()
98            if child.loadFile(fileName):
99                statusBar().showMessage(tr("File loaded"), 2000)
100                child.show()
101            else:
102                child.close()
103
104    def save(self):
105        if self.activeMdiChild() and self.activeMdiChild().save():
106            self.statusBar().showMessage(tr("File saved"), 2000)
107
108    def saveAs(self):
109        if self.activeMdiChild() and self.activeMdiChild().saveAs():
110            self.statusBar().showMessage(tr("File saved"), 2000)
111
112    def cut(self):
113        if self.activeMdiChild():
114            self.activeMdiChild().cut()
115
116    def copy(self):
117        if self.activeMdiChild():
118            activeMdiChild().copy()
119
120    def paste(self):
121        if self.activeMdiChild():
122            activeMdiChild().paste()
123
124    def about(self):
125       QMessageBox.about(self, tr("About MDI"),
126                               tr("The <b>MDI</b> example demonstrates how to write multiple "
127                                  "document interface applications using Qt."))
128
129    def updateMenus(self):
130        hasMdiChild = (activeMdiChild() != 0)
131        self.saveAct.setEnabled(hasMdiChild)
132        self.saveAsAct.setEnabled(hasMdiChild)
133        self.pasteAct.setEnabled(hasMdiChild)
134        self.closeAct.setEnabled(hasMdiChild)
135        self.closeAllAct.setEnabled(hasMdiChild)
136        self.tileAct.setEnabled(hasMdiChild)
137        self.cascadeAct.setEnabled(hasMdiChild)
138        self.nextAct.setEnabled(hasMdiChild)
139        self.previousAct.setEnabled(hasMdiChild)
140        self.separatorAct.setVisible(hasMdiChild)
141
142        hasSelection = (self.activeMdiChild() and
143                        self.activeMdiChild().textCursor().hasSelection())
144        self.cutAct.setEnabled(hasSelection)
145        self.copyAct.setEnabled(hasSelection)
146
147    def updateWindowMenu(self):
148        self.windowMenu.clear()
149        self.windowMenu.addAction(closeAct)
150        self.windowMenu.addAction(closeAllAct)
151        self.windowMenu.addSeparator()
152        self.windowMenu.addAction(tileAct)
153        self.windowMenu.addAction(cascadeAct)
154        self.windowMenu.addSeparator()
155        self.windowMenu.addAction(nextAct)
156        self.windowMenu.addAction(previousAct)
157        self.windowMenu.addAction(separatorAct)
158
159        windows = mdiArea.subWindowList()
160        separatorAct.setVisible(not windows.isEmpty())
161
162        for i in range(0, windows.size()):
163            child = windows.at(i).widget()
164
165            text = ""
166            if i < 9:
167                text = "{} {}".format(i + 1, child.userFriendlyCurrentFile())
168            else:
169                text = "{} {}".format(i + 1, child.userFriendlyCurrentFile())
170
171            action  = windowMenu.addAction(text)
172            action.setCheckable(True)
173            action.setChecked(child == activeMdiChild())
174            action.triggered.connect(windowMapper.map)
175            windowMapper.setMapping(action, windows.at(i))
176
177            createMdiChild = MdiChild()
178
179            child =  MdiChild()
180            mdiArea.addSubWindow(child)
181
182            child.copyAvailable[bool].connect(cutAct.setEnabled)
183            child.copyAvailable[bool].connect(copyAct.setEnabled)
184
185            return child
186
187
188    def createActions(self):
189
190        Act = QAction(QIcon(":/images/new.png"), tr("&New"), self)
191        Act.setShortcuts(QKeySequence.New)
192        Act.setStatusTip(tr("Create a new file"))
193        Act.triggered.connect(self.newFile)
194
195        openAct = QAction(QIcon(":/images/open.png"), tr("&Open..."), self)
196        openAct.setShortcuts(QKeySequence.Open)
197        openAct.setStatusTip(tr("Open an existing file"))
198        openAct.triggered.connect(self.open)
199
200        saveAct =  QAction(QIcon(":/images/save.png"), tr("&Save"), self)
201        saveAct.setShortcuts(QKeySequence.Save)
202        saveAct.setStatusTip(tr("Save the document to disk"))
203        saveAct.triggered.connect(self.save)
204
205        saveAsAct =  QAction(tr("Save &As..."), self)
206        saveAsAct.setShortcuts(QKeySequence.SaveAs)
207        saveAsAct.setStatusTip(tr("Save the document under a  name"))
208        saveAsAct.triggered.connect(self.saveAs)
209
210//! [0]
211        exitAct = QAction(tr("E&xit"), self)
212        exitAct.setShortcut(tr("Ctrl+Q"))
213        exitAct.setStatusTip(tr("Exit the application"))
214        exitAct.triggered.connect(qApp.closeAllWindows)
215//! [0]
216
217        cutAct =  QAction(QIcon(":/images/cut.png"), tr("Cu&t"), self)
218        cutAct.setShortcuts(QKeySequence.Cut)
219        cutAct.setStatusTip(tr("Cut the current selection's contents to the "
220                                "clipboard"))
221        cutAct.triggered.connect(self.cut)
222
223        copyAct =  QAction(QIcon(":/images/copy.png"), tr("&Copy"), self)
224        copyAct.setShortcuts(QKeySequence.Copy)
225        copyAct.setStatusTip(tr("Copy the current selection's contents to the "
226                                 "clipboard"))
227        copyAct.triggered.connect(self.copy)
228
229        pasteAct =  QAction(QIcon(":/images/paste.png"), tr("&Paste"), self)
230        pasteAct.setShortcuts(QKeySequence.Paste)
231        pasteAct.setStatusTip(tr("Paste the clipboard's contents into the current "
232                                  "selection"))
233        pasteAct.triggered.connect(self.paste)
234
235        closeAct =  QAction(tr("Cl&ose"), self)
236        closeAct.setShortcut(tr("Ctrl+F4"))
237        closeAct.setStatusTip(tr("Close the active window"))
238        closeAct.triggered.connect(mdiArea.closeActiveSubWindow)
239
240        closeAllAct =  QAction(tr("Close &All"), self)
241        closeAllAct.setStatusTip(tr("Close all the windows"))
242        closeAllAct.triggered.connect(mdiArea.closeAllSubWindows)
243
244        tileAct =  QAction(tr("&Tile"), self)
245        tileAct.setStatusTip(tr("Tile the windows"))
246        tileAct.triggered.connect(mdiArea.tileSubWindows)
247
248        cascadeAct =  QAction(tr("&Cascade"), self)
249        cascadeAct.setStatusTip(tr("Cascade the windows"))
250        cascadeAct.triggered.connect(mdiArea.cascadeSubWindows)
251
252        nextAct =  QAction(tr("Ne&xt"), self)
253        nextAct.setShortcuts(QKeySequence.NextChild)
254        nextAct.setStatusTip(tr("Move the focus to the next window"))
255        nextAct.triggered.connect(mdiArea.activateNextSubWindow)
256
257        previousAct =  QAction(tr("Pre&vious"), self)
258        previousAct.setShortcuts(QKeySequence.PreviousChild)
259        previousAct.setStatusTip(tr("Move the focus to the previous "
260                                     "window"))
261        previousAct.triggered.connect(mdiArea.activatePreviousSubWindow)
262
263        separatorAct =  QAction(self)
264        separatorAct.setSeparator(True)
265
266        aboutAct =  QAction(tr("&About"), self)
267        aboutAct.setStatusTip(tr("Show the application's About box"))
268        aboutAct.triggered.connect(self.about)
269
270        aboutQtAct =  QAction(tr("About &Qt"), self)
271        aboutQtAct.setStatusTip(tr("Show the Qt library's About box"))
272        aboutQtAct.triggered.connect(qApp.aboutQt)
273
274
275    def createMenus(self):
276
277        fileMenu = menuBar().addMenu(tr("&File"))
278        fileMenu.addAction(Act)
279        fileMenu.addAction(openAct)
280        fileMenu.addAction(saveAct)
281        fileMenu.addAction(saveAsAct)
282        fileMenu.addSeparator()
283        action = fileMenu.addAction(tr("Switch layout direction"))
284        action.triggered.connect(self.switchLayoutDirection)
285        fileMenu.addAction(exitAct)
286
287        editMenu = menuBar().addMenu(tr("&Edit"))
288        editMenu.addAction(cutAct)
289        editMenu.addAction(copyAct)
290        editMenu.addAction(pasteAct)
291
292        windowMenu = menuBar().addMenu(tr("&Window"))
293        updateWindowMenu()
294        windowMenu.aboutToShow.connect(self.updateWindowMenu)
295
296        menuBar().addSeparator()
297
298        helpMenu = menuBar().addMenu(tr("&Help"))
299        helpMenu.addAction(aboutAct)
300        helpMenu.addAction(aboutQtAct)
301
302
303    def createToolBars(self):
304        fileToolBar = addToolBar(tr("File"))
305        fileToolBar.addAction(Act)
306        fileToolBar.addAction(openAct)
307        fileToolBar.addAction(saveAct)
308
309        editToolBar = addToolBar(tr("Edit"))
310        editToolBar.addAction(cutAct)
311        editToolBar.addAction(copyAct)
312        editToolBar.addAction(pasteAct)
313
314
315    def createStatusBar(self):
316        statusBar().showMessage(tr("Ready"))
317
318
319    def readSettings(self):
320        settings = QSettings("Trolltech", "MDI Example")
321        QPoint pos = settings.value("pos", QPoint(200, 200)").toPoint()
322        QSize size = settings.value("size", QSize(400, 400)").toSize()
323        move(pos)
324        resize(size)
325
326    def writeSettings(self):
327        QSettings settings("Trolltech", "MDI Example")
328        settings.setValue("pos", pos()")
329        settings.setValue("size", size()")
330
331
332        activeMdiChild = MdiChild()
333        activeSubWindow = mdiArea.activeSubWindow()
334        if activeSubWindow:
335            return activeSubWindow.widget()
336        return 0
337
338
339    def findMdiChild(self, fileName):
340
341        canonicalFilePath = QFileInfo(fileName).canonicalFilePath()
342
343        for window in mdiArea.subWindowList():
344            mdiChild = window.widget()
345            if mdiChild.currentFile() == canonicalFilePath:
346                return window
347        return 0
348
349
350    def switchLayoutDirection(self)
351        if layoutDirection() == Qt.LeftToRight:
352            qApp.setLayoutDirection(Qt.RightToLeft)
353        else:
354            qApp.setLayoutDirection(Qt.LeftToRight)
355
356
357    def setActiveSubWindow(self, window):
358        if not window:
359            return
360        mdiArea.setActiveSubWindow(window)
361