Extending PyQT4 text editor
Check out the new site at https://rkblog.dev.
14 July 2008
Comments
No we will add two features to our editor. This will train our docs searching skills :nice:.Disabled "Save" button
When there is no open file, or no changes the "Save" button should be disabled. In QTDesigner in the Property Editor we can set "enabled" attribute to "False" to disable the button.
Inherits QAbstractButton.
pushButton class inherits QAbstractButton and has its methods. When we go to QAbstractButton docs we wont see any method related to "enabled", but QAbstractButton inherits QWidget, and QWidget has setEnabled() method. So here is the start.py file:
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
from edytor import Ui_notatnik
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_notatnik()
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)
QtCore.QObject.connect(self.ui.editor_window,QtCore.SIGNAL("textChanged()"), self.enable_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)
# inserting text emits textChanged() so we disable the button :)
self.ui.button_save.setEnabled(False)
def enable_save(self):
self.ui.button_save.setEnabled(True)
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()
self.ui.button_save.setEnabled(False)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
QtCore.QObject.connect(self.ui.editor_window,QtCore.SIGNAL("textChanged()"), self.enable_save)
def enable_save(self):
self.ui.button_save.setEnabled(True)
self.ui.editor_window.setPlainText(s)
# inserting text emits textChanged() so we disable the button :)
self.ui.button_save.setEnabled(False)
Save od Discard changes
When we want to open a file and we didn't saved changes to the current open file a message box should appear asking what to do about those changes - Save, Discard, Cancel. We will use QMessageBox. Go show it we need only:message = QtGui.QMessageBox(self)
message.exec_()

# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
from edytor import Ui_notatnik
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_notatnik()
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)
QtCore.QObject.connect(self.ui.editor_window,QtCore.SIGNAL("textChanged()"), self.enable_save)
def file_dialog(self):
response = False
# buttons texts
SAVE = 'Save'
DISCARD = 'Discard'
CANCEL = 'Cancel'
# if we have changes then ask about them
if self.ui.button_save.isEnabled() and self.filename:
message = QtGui.QMessageBox(self)
message.setText('What to do about unsaved changes ?')
message.setWindowTitle('Notepad')
message.setIcon(QtGui.QMessageBox.Question)
message.addButton(SAVE, QtGui.QMessageBox.AcceptRole)
message.addButton(DISCARD, QtGui.QMessageBox.DestructiveRole)
message.addButton(CANCEL, QtGui.QMessageBox.RejectRole)
message.setDetailedText('Unsaved changes in file: ' + str(self.filename))
message.exec_()
response = message.clickedButton().text()
# save file
if response == SAVE:
self.file_save()
self.ui.button_save.setEnabled(False)
# discard changes
elif response == DISCARD:
self.ui.button_save.setEnabled(False)
# if we didn't cancelled show the file dialogue
if response != CANCEL:
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)
self.ui.button_save.setEnabled(False)
def enable_save(self):
self.ui.button_save.setEnabled(True)
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()
self.ui.button_save.setEnabled(False)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_()))
response = False
# buttons texts
SAVE = 'Save'
DISCARD = 'Discard'
CANCEL = 'Cancel'
# if we have changes then ask about them
if self.ui.button_save.isEnabled() and self.filename:
message = QtGui.QMessageBox(self)
message.setText('What to do about unsaved changes ?')
message.setWindowTitle('Notepad')
message.setIcon(QtGui.QMessageBox.Question)
message.addButton(SAVE, QtGui.QMessageBox.AcceptRole)
message.addButton(DISCARD, QtGui.QMessageBox.DestructiveRole)
message.addButton(CANCEL, QtGui.QMessageBox.RejectRole)
message.setDetailedText('Unsaved changes in file: ' + str(self.filename))
message.exec_()
response = message.clickedButton().text()
# save file
if response == SAVE:
self.file_save()
self.ui.button_save.setEnabled(False)
# discard changes
elif response == DISCARD:
self.ui.button_save.setEnabled(False)
# if we didn't cancelled show the file dialogue
if response != CANCEL:

Download
Download sourcesNote: to get English names on the buttons regenerate "edytor.py" class using "edytorEN.ui" and run the application using "startEN.py"
RkBlog
Check out the new site at https://rkblog.dev.
Comment article