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