1#!/usr/bin/env python
2
3
4#############################################################################
5##
6## Copyright (C) 2014 Riverbank Computing Limited.
7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
8## All rights reserved.
9##
10## This file is part of the examples of PyQt.
11##
12## $QT_BEGIN_LICENSE:BSD$
13## You may use this file under the terms of the BSD license as follows:
14##
15## "Redistribution and use in source and binary forms, with or without
16## modification, are permitted provided that the following conditions are
17## met:
18##   * Redistributions of source code must retain the above copyright
19##     notice, this list of conditions and the following disclaimer.
20##   * Redistributions in binary form must reproduce the above copyright
21##     notice, this list of conditions and the following disclaimer in
22##     the documentation and/or other materials provided with the
23##     distribution.
24##   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
25##     the names of its contributors may be used to endorse or promote
26##     products derived from this software without specific prior written
27##     permission.
28##
29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
40## $QT_END_LICENSE$
41##
42#############################################################################
43
44
45from PyQt5.QtCore import (QFile, QFileInfo, QPoint, QRect, QSettings, QSize,
46        Qt, QTextStream)
47from PyQt5.QtGui import QIcon, QKeySequence
48from PyQt5.QtWidgets import (QAction, QApplication, QFileDialog, QMainWindow,
49        QMessageBox, QTextEdit)
50
51
52class MainWindow(QMainWindow):
53    def __init__(self):
54        super(MainWindow, self).__init__()
55
56        self.curFile = ''
57
58        self.textEdit = QTextEdit()
59        self.setCentralWidget(self.textEdit)
60
61        self.createActions()
62        self.createMenus()
63        self.createToolBars()
64        self.createStatusBar()
65
66        self.readSettings()
67
68        self.textEdit.document().contentsChanged.connect(self.documentWasModified)
69
70        self.setCurrentFile('')
71
72    def closeEvent(self, event):
73        if self.maybeSave():
74            self.writeSettings()
75            event.accept()
76        else:
77            event.ignore()
78
79    def newFile(self):
80        if self.maybeSave():
81            self.textEdit.clear()
82            self.setCurrentFile('')
83
84    def open(self):
85        if self.maybeSave():
86            fileName, _ = QFileDialog.getOpenFileName(self)
87            if fileName:
88                self.loadFile(fileName)
89
90    def save(self):
91        if self.curFile:
92            return self.saveFile(self.curFile)
93
94        return self.saveAs()
95
96    def saveAs(self):
97        fileName, _ = QFileDialog.getSaveFileName(self)
98        if fileName:
99            return self.saveFile(fileName)
100
101        return False
102
103    def about(self):
104        QMessageBox.about(self, "About Application",
105                "The <b>Application</b> example demonstrates how to write "
106                "modern GUI applications using Qt, with a menu bar, "
107                "toolbars, and a status bar.")
108
109    def documentWasModified(self):
110        self.setWindowModified(self.textEdit.document().isModified())
111
112    def createActions(self):
113        root = QFileInfo(__file__).absolutePath()
114
115        self.newAct = QAction(QIcon(root + '/images/new.png'), "&New", self,
116                shortcut=QKeySequence.New, statusTip="Create a new file",
117                triggered=self.newFile)
118
119        self.openAct = QAction(QIcon(root + '/images/open.png'), "&Open...",
120                self, shortcut=QKeySequence.Open,
121                statusTip="Open an existing file", triggered=self.open)
122
123        self.saveAct = QAction(QIcon(root + '/images/save.png'), "&Save", self,
124                shortcut=QKeySequence.Save,
125                statusTip="Save the document to disk", triggered=self.save)
126
127        self.saveAsAct = QAction("Save &As...", self,
128                shortcut=QKeySequence.SaveAs,
129                statusTip="Save the document under a new name",
130                triggered=self.saveAs)
131
132        self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q",
133                statusTip="Exit the application", triggered=self.close)
134
135        self.cutAct = QAction(QIcon(root + '/images/cut.png'), "Cu&t", self,
136                shortcut=QKeySequence.Cut,
137                statusTip="Cut the current selection's contents to the clipboard",
138                triggered=self.textEdit.cut)
139
140        self.copyAct = QAction(QIcon(root + '/images/copy.png'), "&Copy", self,
141                shortcut=QKeySequence.Copy,
142                statusTip="Copy the current selection's contents to the clipboard",
143                triggered=self.textEdit.copy)
144
145        self.pasteAct = QAction(QIcon(root + '/images/paste.png'), "&Paste",
146                self, shortcut=QKeySequence.Paste,
147                statusTip="Paste the clipboard's contents into the current selection",
148                triggered=self.textEdit.paste)
149
150        self.aboutAct = QAction("&About", self,
151                statusTip="Show the application's About box",
152                triggered=self.about)
153
154        self.aboutQtAct = QAction("About &Qt", self,
155                statusTip="Show the Qt library's About box",
156                triggered=QApplication.instance().aboutQt)
157
158        self.cutAct.setEnabled(False)
159        self.copyAct.setEnabled(False)
160        self.textEdit.copyAvailable.connect(self.cutAct.setEnabled)
161        self.textEdit.copyAvailable.connect(self.copyAct.setEnabled)
162
163    def createMenus(self):
164        self.fileMenu = self.menuBar().addMenu("&File")
165        self.fileMenu.addAction(self.newAct)
166        self.fileMenu.addAction(self.openAct)
167        self.fileMenu.addAction(self.saveAct)
168        self.fileMenu.addAction(self.saveAsAct)
169        self.fileMenu.addSeparator();
170        self.fileMenu.addAction(self.exitAct)
171
172        self.editMenu = self.menuBar().addMenu("&Edit")
173        self.editMenu.addAction(self.cutAct)
174        self.editMenu.addAction(self.copyAct)
175        self.editMenu.addAction(self.pasteAct)
176
177        self.menuBar().addSeparator()
178
179        self.helpMenu = self.menuBar().addMenu("&Help")
180        self.helpMenu.addAction(self.aboutAct)
181        self.helpMenu.addAction(self.aboutQtAct)
182
183    def createToolBars(self):
184        self.fileToolBar = self.addToolBar("File")
185        self.fileToolBar.addAction(self.newAct)
186        self.fileToolBar.addAction(self.openAct)
187        self.fileToolBar.addAction(self.saveAct)
188
189        self.editToolBar = self.addToolBar("Edit")
190        self.editToolBar.addAction(self.cutAct)
191        self.editToolBar.addAction(self.copyAct)
192        self.editToolBar.addAction(self.pasteAct)
193
194    def createStatusBar(self):
195        self.statusBar().showMessage("Ready")
196
197    def readSettings(self):
198        settings = QSettings("Trolltech", "Application Example")
199        pos = settings.value("pos", QPoint(200, 200))
200        size = settings.value("size", QSize(400, 400))
201        self.resize(size)
202        self.move(pos)
203
204    def writeSettings(self):
205        settings = QSettings("Trolltech", "Application Example")
206        settings.setValue("pos", self.pos())
207        settings.setValue("size", self.size())
208
209    def maybeSave(self):
210        if self.textEdit.document().isModified():
211            ret = QMessageBox.warning(self, "Application",
212                    "The document has been modified.\nDo you want to save "
213                    "your changes?",
214                    QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
215
216            if ret == QMessageBox.Save:
217                return self.save()
218
219            if ret == QMessageBox.Cancel:
220                return False
221
222        return True
223
224    def loadFile(self, fileName):
225        file = QFile(fileName)
226        if not file.open(QFile.ReadOnly | QFile.Text):
227            QMessageBox.warning(self, "Application",
228                    "Cannot read file %s:\n%s." % (fileName, file.errorString()))
229            return
230
231        inf = QTextStream(file)
232        QApplication.setOverrideCursor(Qt.WaitCursor)
233        self.textEdit.setPlainText(inf.readAll())
234        QApplication.restoreOverrideCursor()
235
236        self.setCurrentFile(fileName)
237        self.statusBar().showMessage("File loaded", 2000)
238
239    def saveFile(self, fileName):
240        file = QFile(fileName)
241        if not file.open(QFile.WriteOnly | QFile.Text):
242            QMessageBox.warning(self, "Application",
243                    "Cannot write file %s:\n%s." % (fileName, file.errorString()))
244            return False
245
246        outf = QTextStream(file)
247        QApplication.setOverrideCursor(Qt.WaitCursor)
248        outf << self.textEdit.toPlainText()
249        QApplication.restoreOverrideCursor()
250
251        self.setCurrentFile(fileName);
252        self.statusBar().showMessage("File saved", 2000)
253        return True
254
255    def setCurrentFile(self, fileName):
256        self.curFile = fileName
257        self.textEdit.document().setModified(False)
258        self.setWindowModified(False)
259
260        if self.curFile:
261            shownName = self.strippedName(self.curFile)
262        else:
263            shownName = 'untitled.txt'
264
265        self.setWindowTitle("%s[*] - Application" % shownName)
266
267    def strippedName(self, fullFileName):
268        return QFileInfo(fullFileName).fileName()
269
270
271if __name__ == '__main__':
272
273    import sys
274
275    app = QApplication(sys.argv)
276    mainWin = MainWindow()
277    mainWin.show()
278    sys.exit(app.exec_())
279