Simple text editor in PyQT4

Check out the new site at https://rkblog.dev.

We want to make a simple text editor (viewer at start). So in QTDesigner we create a GUI based on "Main Window". I used two PushButtons and one TextEdit:
qt4text_edit1
The "Close" button I've connected with the window assigning "close()" slot which will close the GUI. For the "Open File" button I've changed it objectName to "button_open", and for the TextEdit window to "editor_window", and the window name from "MainWindow" to "notepad". You change them in the "Property Editor" when you select a widget.
qt4text_edit2
I've saved the GUI and made a Python class out of it:
pyuic4 edytor.ui > edytor.py
And I got a file with a "Ui_notepad" class. To start an application using it we need a "special" code. Create a file start.py with:
import sys
from PyQt4 import QtCore, QtGui
from edytor import Ui_notepad

class StartQT4(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_notepad()
        self.ui.setupUi(self)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = StartQT4()
    myapp.show()
    sys.exit(app.exec_())
Execute start.py to show the application. Click on "Close" to close it.
qt4text_edit3
Now the main part - our own slots. In QT3 we could do that in QTDesigner, but here it looks different. We will edit start.py and add there our code not touching edytor.py code. Here is the modified start.py:
import sys
from PyQt4 import QtCore, QtGui
from edytor import Ui_notepad

class StartQT4(QtGui.QMainWindow):
	def __init__(self, parent=None):
		QtGui.QWidget.__init__(self, parent)
		self.ui = Ui_notepad()
		self.ui.setupUi(self)
		# here we connect signals with our slots
		QtCore.QObject.connect(self.ui.button_open,QtCore.SIGNAL("clicked()"), self.file_dialog)
	def file_dialog(self):
		self.ui.editor_window.setText('aaaaaaaaaa')

if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	myapp = StartQT4()
	myapp.show()
	sys.exit(app.exec_())
Now when you click on "Open File" you'll see "aaaaaaaaaa" in the editor window. It happens because we connect "Open File" button click signal with our slot:
QtCore.QObject.connect(self.ui.button_open,QtCore.SIGNAL("clicked()"), self.file_dialog)
Where self.ui is our window object through which we can access the widgets, so self.ui.button_open is the "Open File" button. self.file_dialog is the method name which will be executed when the signal will be emitted. It is important to add our code after self.ui.setupUi(self). As for the custom slot:
def file_dialog(self):
	self.ui.editor_window.setText('aaaaaaaaaa')
self.ui.editor_window this is our TextEdit, which has setText method, as described in the docs. That's how you add custom slots to a PyQT4 application :cool:
Now we have to use QFileDialog to select a file. Here is the code:
import sys
from PyQt4 import QtCore, QtGui
from edytor import Ui_notepad

class StartQT4(QtGui.QMainWindow):
	def __init__(self, parent=None):
		QtGui.QWidget.__init__(self, parent)
		self.ui = Ui_notepad()
		self.ui.setupUi(self)
		QtCore.QObject.connect(self.ui.button_open,QtCore.SIGNAL("clicked()"), self.file_dialog)
	def file_dialog(self):
		fd = QtGui.QFileDialog(self)
		plik = open(fd.getOpenFileName()).read()
		self.ui.editor_window.setText(plik)

if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	myapp = StartQT4()
	myapp.show()
	sys.exit(app.exec_())
fd.getOpenFileName() will show a File Selection window, which will close after selecting a file (returns path to file), and allowing the rest of the code to execute. It works but not that if we cancel file selection fd.getOpenFileName() will not return a path to a existing file, we will get a bug like:
IOError: [Errno 2] Nie ma takiego pliku ani katalogu: < PyQt4.QtCore.QString object at 0x2b6465569738 >
qt4text_edit4
So we have to improve our code:
import sys
from PyQt4 import QtCore, QtGui
from edytor import Ui_notepad

class StartQT4(QtGui.QMainWindow):
	def __init__(self, parent=None):
		QtGui.QWidget.__init__(self, parent)
		self.ui = Ui_notepad()
		self.ui.setupUi(self)
		# tutaj dajemy wlasne polaczenia slotow
		QtCore.QObject.connect(self.ui.button_open,QtCore.SIGNAL("clicked()"), self.file_dialog)
	def file_dialog(self):
		fd = QtGui.QFileDialog(self)
		self.filename = fd.getOpenFileName()
		from os.path import isfile
		if isfile(self.filename):
			text = open(self.filename).read()
			self.ui.editor_window.setText(text)

if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	myapp = StartQT4()
	myapp.show()
	sys.exit(app.exec_())
So we can view our files, but we can't save changes. I needed a "Save" button so I edited the ui file in QTDesigner and added a pushButton with "button_save" name. After editing the *ui file we have to recreate the gui class:
pyuic4 edytor.ui > edytor.py
And now our application looks like this:
qt4text_edit5
We add a slot that saves the file and connect him with a "clicked()" signal of "Save" pushButton:
import sys
from PyQt4 import QtCore, QtGui
from edytor import Ui_notepad

class StartQT4(QtGui.QMainWindow):
	def __init__(self, parent=None):
		QtGui.QWidget.__init__(self, parent)
		self.ui = Ui_notepad()
		self.ui.setupUi(self)
		QtCore.QObject.connect(self.ui.button_open,QtCore.SIGNAL("clicked()"), self.file_dialog)
		QtCore.QObject.connect(self.ui.button_save,QtCore.SIGNAL("clicked()"), self.file_save)
	def file_dialog(self):
		fd = QtGui.QFileDialog(self)
		self.filename = fd.getOpenFileName()
		from os.path import isfile
		if isfile(self.filename):
			text = open(self.filename).read()
			self.ui.editor_window.setText(text)
	def file_save(self):
		from os.path import isfile
		if isfile(self.filename):
			file = open(self.filename, 'w')
			file.write(self.ui.editor_window.toPlainText())
			file.close()

if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	myapp = StartQT4()
	myapp.show()
	sys.exit(app.exec_())
It works but some of you may detect a but - It doesn't support non-ASCII files/characters. So we need to use some UTF-8 help with Python codecs and unicode():
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
from edytor import Ui_notepad

class StartQT4(QtGui.QMainWindow):
	def __init__(self, parent=None):
		QtGui.QWidget.__init__(self, parent)
		self.ui = Ui_notepad()
		self.ui.setupUi(self)
		QtCore.QObject.connect(self.ui.button_open,QtCore.SIGNAL("clicked()"), self.file_dialog)
		QtCore.QObject.connect(self.ui.button_save,QtCore.SIGNAL("clicked()"), self.file_save)
	def file_dialog(self):
		fd = QtGui.QFileDialog(self)
		self.filename = fd.getOpenFileName()
		from os.path import isfile
		if isfile(self.filename):
			import codecs
			s = codecs.open(self.filename,'r','utf-8').read()
			self.ui.editor_window.setPlainText(s)
	def file_save(self):
		from os.path import isfile
		if isfile(self.filename):
			import codecs
			s = codecs.open(self.filename,'w','utf-8')
			s.write(unicode(self.ui.editor_window.toPlainText()))
			s.close()

if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	myapp = StartQT4()
	myapp.show()
	sys.exit(app.exec_())
And the text editor is UTF-8 friendly. In the next tutorials I'll show you how to add more features to this editor thus learning more of PyQT4.

Download

Download sources
Note: to get English names on the buttons regenerate "edytor.py" class using "edytorEN.ui"
RkBlog

Python programming, 14 July 2008


Check out the new site at https://rkblog.dev.
Comment article
Comment article RkBlog main page Search RSS Contact