Prosty edytor tekstowy w PyQT4

Naszym celem będzie wykonanie prostego edytora tekstowego (na początek samo przeglądanie). W QTDesigner stworzyłem interfejs (na bazie "Main Window") aplikacji składający się z dwóch PushButton i jednego TextEdit:
qt4text_edit1
Przycisk "Zamknij" połączyłem sygnałem z widżetem przypisując mu slot "close()" zamykający aplikację. Dla przycisku "Otwórz Plik" zmieniłem objectName na "button_open", a dla okna TextEdit na "editor_window". Nazwę okna zmieniłem z "MainWindow" na "notatnik". Nazwy te zmienia się w oknie "Property Editor":
qt4text_edit2
Zapisałem interfejs, a następnie wygenerowałem kod pythona:
pyuic4 edytor.ui > edytor.py
Otrzymując plik z klasą "Ui_notatnik". By uruchomić aplikację użyłem pliku start.py o kodzie:
import sys
from PyQt4 import QtCore, QtGui

# import klasy
from edytor import Ui_notatnik

class StartQT4(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        # nazwa klasy
        self.ui = Ui_notatnik()
        self.ui.setupUi(self)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = StartQT4()
    myapp.show()
    sys.exit(app.exec_())
Wykonanie start.py wyświetli okno aplikacji, a kliknięcie na "Zamknij" zamknie je.
qt4text_edit3
Teraz przejdziemy do ważnej kwestii - własnych slotów. W QT3 QTDesigner mógł tworzyć własne sloty a my mogliśmy od razu wszystko łączyć. W QT4 takiej opcji już nie ma. Musimy kod slotu umieścić w start.py, w klasie wywołującej aplikację (nie żeby to było ładnie opisane dla PyQT4, tak napisali dla samego QT :evil:). Oto zmodyfikowany kod start.py:
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)
		# tutaj dajemy wlasne polaczenia slotow
		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_())
Teraz gdy klikniemy na "Otwórz plik" w oknie edytora pojawi się "aaaaaaaaaa". Powyższy kod zawiera własny slot i łączy go z sygnałem z przycisku.
QtCore.QObject.connect(self.ui.button_open,QtCore.SIGNAL("clicked()"), self.file_dialog)
Gdzie self.ui to obiekt naszego okna. Poprzez niego możemy dostać się do elementów interfejsu, czyli self.ui.button_open oznacza przycisk "Otwórz Plik". self.file_dialog oznacza nazwę metody, która zostanie wykonana gdy sygnał zostanie wyemitowany. Klasę tą definiuję obok. Ważne jest by własny kod dodawać po self.ui.setupUi(self). Odnośnie slotu:
def file_dialog(self):
	self.ui.editor_window.setText('aaaaaaaaaa')
self.ui.editor_window to nasze okno TextEdit, które zgodnie z dokumentacją ma metodę setText ustawiającą tekst wyświetlany w oknie. Ot cała filozofia :cool:
Teraz trzeba użyć QFileDialog by wybrać jakiś plik do otwarcia. Jest to proste:
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)
		# tutaj dajemy wlasne polaczenia slotow
		QtCore.QObject.connect(self.ui.button_open,QtCore.SIGNAL("clicked()"), self.file_dialog)
	def file_dialog(self):
		# stworzenie obiektu QFileDialog
		fd = QtGui.QFileDialog(self)
		# pobranie nazwy wybranego pliku
		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() w naszym przypadku wyświetli okno dialogowe QFileDialog i umożliwi wybór pliku. Gdy wybierzemy zwróci ścieżkę do niego umożliwiając wykonanie reszty kodu. Powyższy kod zadziała, ale nie do końca. Jeżeli nie wybierzemy pliku (anulowanie w oknie dialogowym) to fd.getOpenFileName() nie zwróci ścieżki do pliku i dostaniemy błąd typu:
IOError: [Errno 2] Nie ma takiego pliku ani katalogu:
qt4text_edit4
PyQT4 oferuje QFile do obsługi plików, najpierw jednak wersja z czystym pythonem:
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)
		# 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_())
I nasza obecnie przeglądarka plików tekstowych działa. Nie posiada jeszcze opcji zapisu ale z tym sobie poradzimy. Otwieramy plik interfejsu (edytor.ui) w QTDesignerze i dodajemy trzeci pushButton o nazwie "button_save" i napisie "Zapisz". Regenerujemy klasę interfejsu:
pyuic4 edytor.ui > edytor.py
I możemy rozbudowywać nasz edytor, który teraz wygląda tak:
qt4text_edit5
Dodajemy slot zapisujący plik i łączymy go z sygnałem kliknięcia w przycisk "Zapisz":
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)
		# tutaj dajemy wlasne polaczenia slotow
		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_())
Edytor prawie gotowy, prawie bo działa poprawnie jedynie z plikami zawierającymi znaki ASCII. Nie obsługuje prawidłowo plików UTF-8 zawierających polskie znaki. Rozwiązanie to zastosowanie modułu codecs i unicode():
# -*- 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)
	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_())
I edytor gotowy.

Pobierz źródła

Pobierz źródła
RkBlog

PyQt, 14 July 2008

Comment article
Comment article RkBlog main page Search RSS Contact