Mercurial > projects > dil
view trunk/src/translator/translator.py @ 477:3b5421d40f9b
Translator: major changes and additions.
Moved Close Project menu item to Project menu. Added Save, Save All, Close
and Close All menu items.
Added class Document which all forms inherit from. Documents can indicated
that they have been modified, they can be saved and closed. They are displayed
as pages in the main tab widget.
Fix: signals from changing the text in text controls programatically are
ignored now.
Fix: text taken from text controls must be passed to unicode()
Catching errors from yaml.load() in classes LangFile and Project.
author | Aziz K?ksal <aziz.koeksal@gmail.com> |
---|---|
date | Sat, 10 Nov 2007 23:50:09 +0100 |
parents | 773ddddb583d |
children | 044b5393f8bc |
line wrap: on
line source
#! /usr/bin/python # -*- coding: utf-8 -*- # Author: Aziz Köksal # License: GPL2 import sys, os import yaml from PyQt4 import QtCore, QtGui # User interface modules from ui_translator import Ui_MainWindow from ui_about import Ui_AboutDialog from ui_new_project import Ui_NewProjectDialog from ui_project_properties import Ui_ProjectProperties from ui_msg_form import Ui_MsgForm from project import Project, newProjectData g_scriptDir = sys.path[0] g_CWD = os.getcwd() g_projectExt = ".tproj" g_catExt = ".cat" g_settingsFile = os.path.join(g_scriptDir, "settings.yaml") g_settings = {} Qt = QtCore.Qt Qt.connect = QtCore.QObject.connect Qt.disconnect = QtCore.QObject.disconnect Qt.SIGNAL = QtCore.SIGNAL Qt.SLOT = QtCore.SLOT def QTabWidgetCloseAll(self): for i in range(0, self.count()): widget = self.widget(0) self.removeTab(0) widget.close() QtGui.QTabWidget.closeAll = QTabWidgetCloseAll class MainWindow(QtGui.QMainWindow, Ui_MainWindow): def __init__(self): QtGui.QMainWindow.__init__(self) self.setupUi(self) self.project = None # Modifications self.pages = QtGui.QTabWidget() self.setCentralWidget(self.pages) self.disableMenuItems() self.projectDock = QtGui.QDockWidget("Project", self) self.projectTree = ProjectTree(self.projectDock) self.projectDock.setWidget(self.projectTree) self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.projectDock) # Custom connections Qt.connect(self.action_About, Qt.SIGNAL("triggered()"), self.showAboutDialog) Qt.connect(self.action_New_Project, Qt.SIGNAL("triggered()"), self.createNewProject) Qt.connect(self.action_Open_Project, Qt.SIGNAL("triggered()"), self.openProjectAction) Qt.connect(self.action_Close_Project, Qt.SIGNAL("triggered()"), self.closeProject) Qt.connect(self.action_Save, Qt.SIGNAL("triggered()"), self.saveForm) Qt.connect(self.action_Save_All, Qt.SIGNAL("triggered()"), self.saveAllForms) Qt.connect(self.action_Close, Qt.SIGNAL("triggered()"), self.closeForm) Qt.connect(self.action_Close_All, Qt.SIGNAL("triggered()"), self.closeAllForms) Qt.connect(self.action_Properties, Qt.SIGNAL("triggered()"), self.showProjectProperties) Qt.connect(self.action_Add_Catalogue, Qt.SIGNAL("triggered()"), self.addCatalogue) Qt.connect(self.action_Add_New_Catalogue, QtCore.SIGNAL("triggered()"), self.addNewCatalogue) Qt.connect(self.projectTree, Qt.SIGNAL("itemDoubleClicked(QTreeWidgetItem*,int)"), self.projectTreeItemDblClicked) Qt.connect(self.projectTree, Qt.SIGNAL("onKeyEnter"), self.projectTreeItemActivated) Qt.connect(self.projectTree, Qt.SIGNAL("onKeyDelete"), self.projectTreeItemDeleted) self.readSettings() def showAboutDialog(self): about = QtGui.QDialog() Ui_AboutDialog().setupUi(about) about.exec_() def showProjectProperties(self): dialog = QtGui.QDialog() Ui_ProjectProperties().setupUi(dialog) dialog.exec_() def createNewProject(self): if self.cantCloseProjectIfOpen(): return dialog = NewProjectDialog() code = dialog.exec_() if code == QtGui.QDialog.Accepted: self.openProject(str(dialog.projectFilePath.text())) def openProjectAction(self): if self.cantCloseProjectIfOpen(): return filePath = QtGui.QFileDialog.getOpenFileName(self, "Select Project File", g_CWD, "Translator Project (*%s)" % g_projectExt); filePath = str(filePath) if filePath: self.openProject(filePath) def openProject(self, filePath): from errors import LoadingError try: self.project = Project(filePath) except LoadingError, e: QtGui.QMessageBox.critical(self, "Error", u"Couldn't load project file:\n\n"+str(e)) return self.enableMenuItems() self.projectTree.setProject(self.project) def addCatalogue(self): filePath = QtGui.QFileDialog.getOpenFileName(self, "Select Project File", g_CWD, "Catalogue (*%s)" % g_catExt); filePath = str(filePath) def addNewCatalogue(self): pass def cantCloseProjectIfOpen(self): if self.project == None: return False return self.closeProject() def closeProject(self): if self.project == None: return True MB = QtGui.QMessageBox button = MB.question(self, "Closing", "Close the current project?", MB.Ok | MB.Cancel, MB.Cancel) if button == MB.Cancel: return False del self.project self.project = None self.disableMenuItems() self.projectTree.clear() self.pages.closeAll() return True def enableMenuItems(self): #self.action_Close_Project.setEnabled(True) for action in [ self.action_Save, self.action_Save_All, self.action_Close, self.action_Close_All ]: action.setEnabled(True) self.menubar.insertMenu(self.menu_Help.menuAction(), self.menu_Project) def disableMenuItems(self): #self.action_Close_Project.setEnabled(False) for action in [ self.action_Save, self.action_Save_All, self.action_Close, self.action_Close_All ]: action.setEnabled(False) self.menubar.removeAction(self.menu_Project.menuAction()) def projectTreeItemDblClicked(self, item, int): self.projectTreeItemActivated(item) def projectTreeItemActivated(self, item): if item == None: return if isinstance(item, LangFileItem): msgForm = None if not item.isDocOpen(): msgForm = item.openDoc() msgForm.setModifiedCallback(self.formModified) index = self.pages.indexOf(msgForm) if index == -1: index = self.pages.addTab(msgForm, msgForm.getDocumentTitle()) self.pages.setCurrentIndex(index) msgForm.updateData() def projectTreeItemDeleted(self, item): pass def formModified(self, form): # Append an asterisk to the tab label index = self.pages.indexOf(form) text = form.getDocumentTitle() + "*" self.pages.setTabText(index, text) def saveForm(self): self.saveDocument(self.pages.currentWidget()) def saveAllForms(self): for i in range(0, self.pages.count()): self.saveDocument(self.pages.widget(i)) def saveDocument(self, form): if form.isModified: index = self.pages.indexOf(form) text = form.getDocumentTitle() self.pages.setTabText(index, text) form.save() def closeForm(self): if self.pages.currentWidget(): self.closeDocument(self.pages.currentWidget()) def closeAllForms(self): for i in range(0, self.pages.count()): self.closeDocument(self.pages.widget(i)) def closeDocument(self, form): if form.isModified: MB = QtGui.QMessageBox button = MB.question(self, "Closing Document", "The document '%s' has been modified.\nDo you want to save the changes?" % form.getDocumentFullPath(), MB.Save | MB.Discard | MB.Cancel, MB.Cancel) if button == MB.Cancel: return False if button == MB.Save: self.saveDocument(form) index = self.pages.indexOf(form) self.pages.removeTab(index) form.close() return True def closeEvent(self, event): if self.closeProject() == False: event.ignore() return self.writeSettings() # Closing application def moveToCenterOfDesktop(self): rect = QtGui.QApplication.desktop().geometry() self.move(rect.center() - self.rect().center()) def readSettings(self): # Set default size self.resize(QtCore.QSize(500, 400)) doc = {} try: doc = yaml.load(open(g_settingsFile, "r")) except: self.moveToCenterOfDesktop() return g_settings = doc if not isinstance(doc, dict): g_settings = {} try: coord = doc["Window"] size = QtCore.QSize(coord["Size"][0], coord["Size"][1]) point = QtCore.QPoint(coord["Pos"][0], coord["Pos"][1]) self.resize(size) self.move(point) except: self.moveToCenterOfDesktop() def writeSettings(self): # Save window coordinates g_settings["Window"] = { "Pos" : [self.pos().x(), self.pos().y()], "Size" : [self.size().width(), self.size().height()] } yaml.dump(g_settings, open(g_settingsFile, "w")) #default_flow_style=False class MessageItem(QtGui.QTreeWidgetItem): def __init__(self, msg): QtGui.QTreeWidgetItem.__init__(self, [str(msg["ID"]), msg["Text"]]) self.msg = msg def getID(self): return self.msg["ID"] def setMsgText(self, text): self.msg["Text"] = text def setMsgAnnot(self, text): self.msg["Annot"] = text class Document(QtGui.QWidget): def __init__(self): QtGui.QWidget.__init__(self) self.isModified = False self.modifiedCallback = None self.documentTitle = "" self.documentFullPath = "" def modified(self): if not self.isModified: self.isModified = True self.modifiedCallback(self) def setModifiedCallback(self, func): self.modifiedCallback = func def save(self): self.isModified = False def close(self): self.emit(Qt.SIGNAL("closed()")) QtGui.QWidget.close(self) def getDocumentTitle(self): return self.documentTitle def getDocumentFullPath(self): return self.documentFullPath class MsgForm(Document, Ui_MsgForm): def __init__(self, langFile): Document.__init__(self) self.documentTitle = langFile.getFileName() self.documentFullPath = langFile.getFilePath() self.setupUi(self) self.vboxlayout.setMargin(0) self.langFile = langFile self.currentItem = None self.colID = 0 self.colText = 1 self.treeWidget.setColumnCount(2) self.treeWidget.setHeaderLabels(["ID", "Text"]) for msg in self.langFile.messages: self.treeWidget.addTopLevelItem(MessageItem(msg)) Qt.connect(self.treeWidget, Qt.SIGNAL("currentItemChanged (QTreeWidgetItem *,QTreeWidgetItem *)"), self.treeItemChanged) Qt.connect(self.translEdit, Qt.SIGNAL("textChanged()"), self.translEditTextChanged) Qt.connect(self.translAnnotEdit, Qt.SIGNAL("textChanged()"), self.translAnnotEditTextChanged) #self.translEdit.focusOutEvent = self.translEditFocusOut def treeItemChanged(self, current, previous): if current == None: self.setTranslMsg("") self.setSourceMsg("") return ID = current.getID() # Set the text controls. # The slots receiving text changed signals do nothing if self.currentItem is None. self.currentItem = None self.setTranslMsg(self.langFile.getMsg(ID)) self.setSourceMsg(self.langFile.source.getMsg(ID)) self.currentItem = current def setTranslMsg(self, msg): self.translEdit.setText(msg["Text"]) self.translAnnotEdit.setText(msg["Annot"]) def setSourceMsg(self, msg): self.sourceEdit.setText(msg["Text"]) self.sourceAnnotEdit.setText(msg["Annot"]) #def translEditFocusOut(self, event): #if self.currentItem: #print self.currentItem.text(self.colText) #if self.translEdit.document().isModified(): #self.translEdit.document().setModified(False) #print "translEdit was modified" #QtGui.QTextEdit.focusOutEvent(self.translEdit, event) def translEditTextChanged(self): if self.currentItem: text = unicode(self.translEdit.toPlainText()) self.currentItem.setText(self.colText, text) self.currentItem.setMsgText(text) self.modified() def translAnnotEditTextChanged(self): if self.currentItem: text = unicode(self.translAnnotEdit.toPlainText()) self.currentItem.setMsgAnnot(text) self.modified() def updateData(self): if self.currentItem == None: return ID = self.currentItem.getID() msg = self.langFile.source.getMsg(ID) text = self.sourceEdit.toPlainText() if text != msg["Text"]: self.sourceEdit.setText(msg["Text"]) text = self.sourceAnnotEdit.toPlainText() if text != msg["Annot"]: self.sourceAnnotEdit.setText(msg["Annot"]) def save(self): Document.save(self) self.langFile.save() class MsgFormSource(MsgForm): def __init__(self, langFile): MsgForm.__init__(self, langFile) for x in [self.translEdit, self.translAnnotEdit, self.label_4, self.label_5]: x.close() Qt.connect(self.sourceEdit, Qt.SIGNAL("textChanged()"), self.sourceEditTextChanged) Qt.connect(self.sourceAnnotEdit, Qt.SIGNAL("textChanged()"), self.sourceAnnotEditTextChanged) def treeItemChanged(self, current, previous): if current == None: self.setSourceMsg("") return ID = current.getID() self.currentItem = None self.setSourceMsg(self.langFile.getMsg(ID)) self.currentItem = current def sourceEditTextChanged(self): if self.currentItem: text = unicode(self.sourceEdit.toPlainText()) self.currentItem.setText(self.colText, text) self.currentItem.setMsgText(text) self.modified() def sourceAnnotEditTextChanged(self): if self.currentItem: text = unicode(self.sourceAnnotEdit.toPlainText()) #self.currentItem.setText(self.colAnnot, text) self.currentItem.setMsgAnnot(text) self.modified() class MsgIDItem(QtGui.QTreeWidgetItem): def __init__(self, parent, text): QtGui.QTreeWidgetItem.__init__(self, parent, [text]) class LangFileItem(QtGui.QTreeWidgetItem): def __init__(self, parent, langFile): QtGui.QTreeWidgetItem.__init__(self, parent, [langFile.langCode]) self.langFile = langFile self.msgForm = None def isDocOpen(self): return self.msgForm != None def openDoc(self): if self.msgForm == None: if self.langFile.isSource: self.msgForm = MsgFormSource(self.langFile) else: self.msgForm = MsgForm(self.langFile) Qt.connect(self.msgForm, Qt.SIGNAL("closed()"), self.docClosed) return self.msgForm def docClosed(self): print "docClosed()" self.msgForm = None class ProjectItem(QtGui.QTreeWidgetItem): def __init__(self, text): QtGui.QTreeWidgetItem.__init__(self, [text]) class ProjectTree(QtGui.QTreeWidget): def __init__(self, parent): QtGui.QTreeWidget.__init__(self, parent) self.topItem = None self.msgIDsItem = None self.headerItem().setHidden(True) def keyReleaseEvent(self, event): Qt = QtCore.Qt key = event.key() if key in [Qt.Key_Enter, Qt.Key_Return]: self.emit(Qt.SIGNAL("onKeyEnter"), self.currentItem()) elif key == Qt.Key_Delete: self.emit(Qt.SIGNAL("onKeyDelete"), self.currentItem()) def setProject(self, project): self.project = project self.topItem = ProjectItem(self.project.name) self.addTopLevelItem(self.topItem) for langFile in self.project.langFiles: langFileItem = LangFileItem(self.topItem, langFile) self.msgIDsItem = QtGui.QTreeWidgetItem(self.topItem, ["Message IDs"]) for msgID in self.project.msgIDs: MsgIDItem(self.msgIDsItem, msgID["Name"]) for x in [self.topItem, self.msgIDsItem]: x.setExpanded(True) def contextMenuEvent(self, event): item = self.itemAt(event.pos()) func_map = { None : lambda item: None, QtGui.QTreeWidgetItem : lambda item: None, ProjectItem : self.showMenuProjectItem, LangFileItem : self.showMenuLangFileItem, MsgIDItem : self.showMenuMsgIDItem } func_map[type(item)](item) def showMenuProjectItem(self, item): print "ProjectItem" def showMenuLangFileItem(self, item): print "LangFileItem" def showMenuMsgIDItem(self, item): print "MsgIDItem" def clear(self): self.topItem = None self.msgIDsItem = None QtGui.QTreeWidget.clear(self) class NewProjectDialog(QtGui.QDialog, Ui_NewProjectDialog): def __init__(self): QtGui.QDialog.__init__(self) self.setupUi(self) Qt.connect(self.pickFileButton, Qt.SIGNAL("clicked()"), self.pickFilePath) def pickFilePath(self): filePath = QtGui.QFileDialog.getSaveFileName(self, "New Project File", g_CWD, "Translator Project (*%s)" % g_projectExt); if not filePath: return filePath = str(filePath) # Convert QString if os.path.splitext(filePath)[1] != g_projectExt: filePath += g_projectExt self.projectFilePath.setText(filePath) def accept(self): projectName = str(self.projectName.text()) filePath = str(self.projectFilePath.text()) if projectName == "": QtGui.QMessageBox.warning(self, "Warning", "Please, enter a name for the project.") return if filePath == "": QtGui.QMessageBox.warning(self, "Warning", "Please, choose or enter a path for the project file.") return projectData = newProjectData(projectName) if os.path.splitext(filePath)[1] != g_projectExt: filePath += g_projectExt try: yaml.dump(projectData, open(filePath, "w"), default_flow_style=False) except Exception, e: QtGui.QMessageBox.critical(self, "Error", str(e)) return # Accept and close dialog. QtGui.QDialog.accept(self) if __name__ == "__main__": app = QtGui.QApplication(sys.argv) main = MainWindow() main.show() sys.exit(app.exec_())