1#!/usr/bin/env python 2 3 4############################################################################# 5## 6## Copyright (C) 2013 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, QRegExp, QTextCodec, QTextStream 46from PyQt5.QtWidgets import (QAction, QApplication, QComboBox, QDialog, 47 QDialogButtonBox, QFileDialog, QGridLayout, QLabel, QMainWindow, QMenu, 48 QMessageBox, QTextEdit) 49 50 51def codec_name(codec): 52 try: 53 # Python v3. 54 name = str(codec.name(), encoding='ascii') 55 except TypeError: 56 # Python v2. 57 name = str(codec.name()) 58 59 return name 60 61 62class MainWindow(QMainWindow): 63 def __init__(self): 64 super(MainWindow, self).__init__() 65 66 self.textEdit = QTextEdit() 67 self.textEdit.setLineWrapMode(QTextEdit.NoWrap) 68 self.setCentralWidget(self.textEdit) 69 70 self.codecs = [] 71 self.findCodecs() 72 73 self.previewForm = PreviewForm(self) 74 self.previewForm.setCodecList(self.codecs) 75 76 self.saveAsActs = [] 77 self.createActions() 78 self.createMenus() 79 80 self.setWindowTitle("Codecs") 81 self.resize(500, 400) 82 83 def open(self): 84 fileName, _ = QFileDialog.getOpenFileName(self) 85 if fileName: 86 inFile = QFile(fileName) 87 if not inFile.open(QFile.ReadOnly): 88 QMessageBox.warning(self, "Codecs", 89 "Cannot read file %s:\n%s" % (fileName, inFile.errorString())) 90 return 91 92 data = inFile.readAll() 93 94 self.previewForm.setEncodedData(data) 95 if self.previewForm.exec_(): 96 self.textEdit.setPlainText(self.previewForm.decodedString()) 97 98 def save(self): 99 fileName, _ = QFileDialog.getSaveFileName(self) 100 if fileName: 101 outFile = QFile(fileName) 102 if not outFile.open(QFile.WriteOnly|QFile.Text): 103 QMessageBox.warning(self, "Codecs", 104 "Cannot write file %s:\n%s" % (fileName, outFile.errorString())) 105 return 106 107 action = self.sender() 108 codecName = action.data() 109 110 out = QTextStream(outFile) 111 out.setCodec(codecName) 112 out << self.textEdit.toPlainText() 113 114 def about(self): 115 QMessageBox.about(self, "About Codecs", 116 "The <b>Codecs</b> example demonstrates how to read and " 117 "write files using various encodings.") 118 119 def aboutToShowSaveAsMenu(self): 120 currentText = self.textEdit.toPlainText() 121 122 for action in self.saveAsActs: 123 codecName = action.data() 124 codec = QTextCodec.codecForName(codecName) 125 action.setVisible(codec and codec.canEncode(currentText)) 126 127 def findCodecs(self): 128 codecMap = [] 129 iso8859RegExp = QRegExp('ISO[- ]8859-([0-9]+).*') 130 131 for mib in QTextCodec.availableMibs(): 132 codec = QTextCodec.codecForMib(mib) 133 sortKey = codec_name(codec).upper() 134 rank = 0 135 136 if sortKey.startswith('UTF-8'): 137 rank = 1 138 elif sortKey.startswith('UTF-16'): 139 rank = 2 140 elif iso8859RegExp.exactMatch(sortKey): 141 if len(iso8859RegExp.cap(1)) == 1: 142 rank = 3 143 else: 144 rank = 4 145 else: 146 rank = 5 147 148 codecMap.append((str(rank) + sortKey, codec)) 149 150 codecMap.sort() 151 self.codecs = [item[-1] for item in codecMap] 152 153 def createActions(self): 154 self.openAct = QAction("&Open...", self, shortcut="Ctrl+O", 155 triggered=self.open) 156 157 for codec in self.codecs: 158 name = codec_name(codec) 159 160 action = QAction(name + '...', self, triggered=self.save) 161 action.setData(name) 162 self.saveAsActs.append(action) 163 164 self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q", 165 triggered=self.close) 166 167 self.aboutAct = QAction("&About", self, triggered=self.about) 168 169 self.aboutQtAct = QAction("About &Qt", self, 170 triggered=QApplication.instance().aboutQt) 171 172 def createMenus(self): 173 self.saveAsMenu = QMenu("&Save As", self) 174 for action in self.saveAsActs: 175 self.saveAsMenu.addAction(action) 176 177 self.saveAsMenu.aboutToShow.connect(self.aboutToShowSaveAsMenu) 178 179 self.fileMenu = QMenu("&File", self) 180 self.fileMenu.addAction(self.openAct) 181 self.fileMenu.addMenu(self.saveAsMenu) 182 self.fileMenu.addSeparator() 183 self.fileMenu.addAction(self.exitAct) 184 185 self.helpMenu = QMenu("&Help", self) 186 self.helpMenu.addAction(self.aboutAct) 187 self.helpMenu.addAction(self.aboutQtAct) 188 189 self.menuBar().addMenu(self.fileMenu) 190 self.menuBar().addSeparator() 191 self.menuBar().addMenu(self.helpMenu) 192 193 194class PreviewForm(QDialog): 195 def __init__(self, parent): 196 super(PreviewForm, self).__init__(parent) 197 198 self.encodingComboBox = QComboBox() 199 encodingLabel = QLabel("&Encoding:") 200 encodingLabel.setBuddy(self.encodingComboBox) 201 202 self.textEdit = QTextEdit() 203 self.textEdit.setLineWrapMode(QTextEdit.NoWrap) 204 self.textEdit.setReadOnly(True) 205 206 buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 207 208 self.encodingComboBox.activated.connect(self.updateTextEdit) 209 buttonBox.accepted.connect(self.accept) 210 buttonBox.rejected.connect(self.reject) 211 212 mainLayout = QGridLayout() 213 mainLayout.addWidget(encodingLabel, 0, 0) 214 mainLayout.addWidget(self.encodingComboBox, 0, 1) 215 mainLayout.addWidget(self.textEdit, 1, 0, 1, 2) 216 mainLayout.addWidget(buttonBox, 2, 0, 1, 2) 217 self.setLayout(mainLayout) 218 219 self.setWindowTitle("Choose Encoding") 220 self.resize(400, 300) 221 222 def setCodecList(self, codecs): 223 self.encodingComboBox.clear() 224 for codec in codecs: 225 self.encodingComboBox.addItem(codec_name(codec), codec.mibEnum()) 226 227 def setEncodedData(self, data): 228 self.encodedData = data 229 self.updateTextEdit() 230 231 def decodedString(self): 232 return self.decodedStr 233 234 def updateTextEdit(self): 235 mib = self.encodingComboBox.itemData(self.encodingComboBox.currentIndex()) 236 codec = QTextCodec.codecForMib(mib) 237 238 data = QTextStream(self.encodedData) 239 data.setAutoDetectUnicode(False) 240 data.setCodec(codec) 241 242 self.decodedStr = data.readAll() 243 self.textEdit.setPlainText(self.decodedStr) 244 245 246if __name__ == '__main__': 247 248 import sys 249 250 app = QApplication(sys.argv) 251 mainWin = MainWindow() 252 mainWin.show() 253 sys.exit(app.exec_()) 254