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