No we will add two features to our editor. This will train our docs searching skills :nice:.
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.
If Designer can do this so does PyQT4. Used textEdit widget has "
textChanged()" signal so this part is easy. In the
pushButton docs there is nothing about "enabled", but notice this line (before methods list):
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_())
I've added the slot with signal connection:
QtCore.QObject.connect(self.ui.editor_window,QtCore.SIGNAL("textChanged()"), self.enable_save)
def enable_save(self):
self.ui.button_save.setEnabled(True)
In the
file_dialog slot when we add text to the textEdit from file the "textChanged()" signal will be emitted so we have disable the "Save" button after it:
self.ui.editor_window.setPlainText(s)
# inserting text emits textChanged() so we disable the button :)
self.ui.button_save.setEnabled(False)
An the "Save" button will work as planned.
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_()
The window needs to be configured. We have to add buttons and some text. But how do we use it? We need to edit
file_dialog method and if there are unsaved changes show the message. And how we will know if there are unsaved changes ? If the "Save" button is active (
self.ui.button_save.isEnabled()) then we have unsaved changes. So here is
start.py:
# -*- 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_()))
The new part is:
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:
We create a QtGui.QMessageBox and then we set the message text (
setText), window title (
setWindowTitle), icon (
setIcon, values are in the
docs), and then we add 3 buttons ("Save", "Discard" i "Cancel"). As a second argument we add "role" of the button. Values are in the docs and they are responsible for the order of those buttons.
setDetailedText sets detailed message, and after that we run the QMessageBox with
exec_().
message.clickedButton() will return a pushButton object of the clicked button. To figure out which one was clicked we can compare buttons text. The Message Box looks like this:
Download sources
Note: to get English names on the buttons regenerate "edytor.py" class using "edytorEN.ui" and run the application using "startEN.py"
- Added: 14.07.2008 by riklaunim