[Main Page] [Edit #1] [Edit #2] [Edit #3] [Edit #4] [Edit #5]
An editor isn't much use unless you can actually load and save text files. To achieve this is actually surprisingly easy.
#!/usr/bin/env python import sys from kdeui import * from kdecore import * from qt import * # Constants TRUE=1 FALSE=0 # Generic command codes ID_OPEN=100 ID_NEW=101 ID_SAVE=102 ID_SAVEAS=103 ID_CLOSE=104 ID_NEWWINDOW=105 ID_CLOSEWINDOW=106 ID_COPY=107 ID_CUT=108 ID_PASTE=109 ID_OPTIONS=110 ID_EXIT=111 ID_HELPCONTENTS=112 ID_ABOUT=113 ID_HINTTEXT=300 # Edit object class Edit(KTMainWindow): def __init__ (self): KTMainWindow.__init__(self) self.filename_ = None self.initMenuBar() self.initToolBar() self.initStatusBar() self.view=QMultiLineEdit(self, "Main View") self.setView(self.view) self.show() def commandCallback (self, id): if id == ID_NEW: self.view.clear() self.filename_=None elif id == ID_OPEN: name=QFileDialog.getOpenFileName() if name is not None: self.load(name) elif id == ID_SAVE: if self.filename_ is not None: self.saveAs(self.filename_) else: name=QFileDialog.getSaveFileName() if name is not None: self.saveAs(name) elif id == ID_SAVEAS: name=QFileDialog.getSaveFileName() if name is not None: self.saveAs(name) elif id == ID_ABOUT: QMessageBox.about(self , "About Edit" , "This is a simple text editor" ) elif id == ID_EXIT: self.close() def initMenuBar(self): self.file=QPopupMenu() self.file.insertItem(i18n("&Open..."), ID_OPEN) self.file.insertItem(i18n("&New..."), ID_NEW) self.file.insertItem(i18n("&Save"), ID_SAVE) self.file.insertItem(i18n("Save &As..."), ID_SAVEAS) self.file.insertSeparator() self.file.insertItem(i18n("&Exit"), ID_EXIT) self.help=QPopupMenu() self.help.insertItem(i18n("&Contents..."), ID_HELPCONTENTS) self.help.insertItem(i18n("&About..."), ID_ABOUT) self.menu=KMenuBar(self) self.menu.insertItem(i18n("&File"), self.file) self.menu.insertItem(i18n("&Help"), self.help) self.menu.show() self.setMenu(self.menu) self.connect(self.file ,SIGNAL("activated(int)") ,self.commandCallback) self.connect(self.help ,SIGNAL("activated(int)") ,self.commandCallback) def initToolBar(self): self.toolbar=KToolBar(self) self.toolbar.insertButton(Icon("filenew.xpm") ,ID_NEW, TRUE ,i18n("Create a new file") ) self.toolbar.insertButton(Icon("fileopen.xpm") ,ID_OPEN, FALSE ,i18n("Open a file") ) self.toolbar.insertButton(Icon("filefloppy.xpm") ,ID_SAVE, FALSE ,i18n("Save the current file") ) self.addToolBar(self.toolbar) self.toolbar.show() self.connect(self.toolbar ,SIGNAL("clicked(int)") ,self.commandCallback ) def initStatusBar(self): self.statusbar=KStatusBar(self) self.statusbar.insertItem("Welcome to Edit",ID_HINTTEXT) self.statusbar.show() self.setStatusBar(self.statusbar) def closeEvent(self, QCloseEvent): QCloseEvent.accept() def saveAs(self, filename): f=open(filename, "w+") f.write(self.view.text()) self.filename_=filename def load(self, filename): f=open(filename) s=f.read() self.view.setText(s) self.filename_=filename # Main clause app=KApplication(sys.argv,"EditApp") toplevel=Edit() app.setMainWidget(toplevel) toplevel.show() app.exec_loop()
In this application, there's a central method, commandCallback, that handles the actions the user demanded from the menu or the butotonbar. Depending upon the command given, some action is performed. The signal-slot mechanics should be clear by now.
The actions performed in commandCallback are:
The Edit widget now has a state variable, namely self.filename_, that keeps the name of the file opened. The Open, Save and Save As commands ultimately lead to the saveAs and load methods. Both are quite simple.
The saveAs method opens a Python file object for writing and dumps the entire contents of the QMultiLineEdit widget into that file. Instant gratification:
f.write(self.view.text())
The load method is as simple. A file is opened, read from begin to end and sucked into the QMultiLineEdit widget:
self.view.setText(s)
Compared to the exertions of the C++ contortionists, this is almost embarrassingly simple:
void Edit::load(const char *filename) { QFile f( filename ); if ( !f.open( IO_ReadOnly ) ) return; view->setAutoUpdate( FALSE ); view->clear(); QTextStream t(&f); while ( !t.eof() ) { QString s = t.readLine(); view->append( s ); } f.close(); view->setAutoUpdate( TRUE ); view->repaint(); filename_= filename; }
And of course, the following is good Python too:
def load(self, filename): file=open(filename) text=file.read() self.view.setText(text) self.filename_=filename
Another nice possiblity is to use KFileDialog.getOpenFileURL instead of the QFileDialog.getOpenFileName. It will return an url and you can use the urllib.urlopen function instead of a normal file function you have instant network transparancy. In fact, you should think twice, or preferably trice, before implementing just a local file dialog, instead of a network transparant one. Indeed, you shouldn't confine your users to their own desktop.
It is true that the dialog has a few problems - you can't browse with it yet, but it won't be long before that's solved, too.
Changes