""" qtgui/TextEdit.py: CCP4MG Molecular Graphics Program Copyright (C) 2001-2008 University of York, CCLRC Copyright (C) 2009-2011 University of York This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, modified in accordance with the provisions of the license to address the requirements of UK law. You should have received a copy of the modified GNU Lesser General Public License along with this library. If not, copies may be downloaded from http://www.ccp4.ac.uk/ccp4license.php This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. """ from PyQt4 import QtGui, QtCore import FontedPositionedStrings import sys import os import functools from MGSimpleWidgets import InteractiveLabel unicodeRanges = ( #Latin {'name':"Basic Latin",'range':['0020','007E']}, {'name':"Latin-1 Supplement",'range':['0080','00FF']}, {'name':"Latin Extended-A",'range':['0100','017F']}, {'name':"Latin Extended-B",'range':['0180','024F']}, #{'name':"IPA Extensions",'range':['0250','02AF']}, #{'name':"Phonetic Extensions",'range':['1D00','1D7F']}, #{'name':"Phonetic Extensions Supplement",'range':['1D80','1DBF']}, {'name':"Latin Extended Additional",'range':['1E00','1EFF']}, {'name':"Letterlike Symbols",'range':['2100','214F']}, #{'name':"Enclosed Alphanumerics",'range':['2460','24FF']}, {'name':"Latin Extended-C",'range':['2C60','2C7F']}, {'name':"Latin Extended-D",'range':['A720','A7FF']}, {'name':"Alphabetic Presentation Forms (Latin ligatures)",'range':['FB00','FB4F']}, #{'name':"Halfwidth and Fullwidth Forms (fullwidth Latin letters)",'range':['FF00','FFEF']}, #{'name':"Mathematical Alphanumeric Symbols",'range':['1D400','1D7FF']}, #Cyrillic {'name':"Cyrillic",'range':['0400','052F']}, #Hebrew {'name':"Hebrew",'range':(['0590','05ff'],['fb1d','fb40'])}, #Greek {'name':"Greek and Coptic",'range':['0370','03ff']}, {'name':"Greek Extended",'range':['1f00','1fff']}, ) default_colours = [ ['black',0,0,0], ['red',255,0,0], ['green',0,255,0], ['blue',0,0,255], ['yellow',255,255,0], ['magenta',255,0,255], ['cyan',0,255,255], ['white',255,255,255]] class UnicodePicker(QtGui.QWidget): charPicked = QtCore.pyqtSignal(int) def charPressed(self,char): self.charPicked.emit(char) def __init__(self,parent=None): QtGui.QWidget.__init__(self,parent) self.setWindowTitle(self.tr("Pick unicode character")) layout = QtGui.QVBoxLayout() pageCombo = QtGui.QComboBox() for r in unicodeRanges: pageCombo.addItem(r['name']) layout.addWidget(pageCombo) stack = QtGui.QStackedWidget() iwidget = 0 max_ii = 0 for r in unicodeRanges: table = QtGui.QGridLayout() tableWidget = QtGui.QWidget() ii = 0 if type(r["range"]) == tuple: for r2 in r["range"]: start = int(r2[0],16) end = int(r2[1],16) for i in range(start,end+1): label = QtGui.QLabel() label.setText(""+'&#'+str(i)+';'+"") self.connect(label,QtCore.SIGNAL("linkActivated ( const QString & )"),functools.partial(self.charPressed,i)) table.addWidget(label,ii/16,ii%16) ii = ii + 1 else: start = int(r["range"][0],16) end = int(r["range"][1],16) for i in range(start,end+1): label = QtGui.QLabel() label.setText(""+'&#'+str(i)+';'+"") self.connect(label,QtCore.SIGNAL("linkActivated ( const QString & )"),functools.partial(self.charPressed,i)) table.addWidget(label,ii/16,ii%16) ii = ii + 1 if ii > max_ii: max_ii = ii tableWidget.setLayout(table) stack.addWidget(tableWidget) iwidget = iwidget + 1 for i in range(len(unicodeRanges)): if stack.widget(i).layout().rowCount()0: self.fontSizeComboBox.setCurrentIndex(idx) else: self.fontSizeComboBox.setEditText(str(font.pointSize())) # This should be poplutated with MG's colours, but # absolutely not depend on MG in any way. self.colourComboBox = QtGui.QComboBox() self.colourComboBox.setEditable(True) #self.colourComboBox.addItem('default') for colour in default_colours: name = colour[0] col = QtGui.QColor(colour[1],colour[2],colour[3]) pixmap = QtGui.QPixmap(20,10) pixmap.fill(col) icon = QtGui.QIcon(pixmap) self.colourComboBox.addItem(icon,name) self.colourComboBox.addItem("Colour browser") #self.colourComboBox.setCurrentIndex(self.colourComboBox.findText('default')) layout.addWidget(self.boldLabel) layout.addWidget(self.italicLabel) layout.addWidget(self.underlineLabel) layout.addWidget(self.superLabel) layout.addWidget(self.subLabel) layout.addWidget(self.symbolLabel) layout.addWidget(self.fontComboBox) layout.addWidget(self.fontSizeComboBox) layout.addWidget(self.colourComboBox) self.setBoldLabel() self.setItalicLabel() self.setUnderlineLabel() self.setSubLabel() self.setSuperLabel() self.connect(self.boldLabel,QtCore.SIGNAL("MousePressed"),self.toggleBold) self.connect(self.italicLabel,QtCore.SIGNAL("MousePressed"),self.toggleItalic) self.connect(self.underlineLabel,QtCore.SIGNAL("MousePressed"),self.toggleUnderline) self.connect(self.subLabel,QtCore.SIGNAL("MousePressed"),self.toggleSubscript) self.connect(self.symbolLabel,QtCore.SIGNAL("clicked(bool)"),self.showSymbols) self.connect(self.superLabel,QtCore.SIGNAL("MousePressed"),self.toggleSuperscript) self.connect(self.fontComboBox,QtCore.SIGNAL("currentFontChanged(const QFont&)"),self.FontComboBoxChanged) self.connect(self.fontSizeComboBox,QtCore.SIGNAL("currentIndexChanged(const QString&)"),self.FontSizeComboBoxChanged) self.connect(self.colourComboBox,QtCore.SIGNAL("activated(const QString&)"),self.ColourComboBoxChanged) self.symbolsGui.charPicked.connect(self.charPicked) self.setLayout(layout) def setCurrentCharFormat(self,format): #print "new format",format,self.currentCharFormat self.currentCharFormat = QtGui.QTextCharFormat(format) def set_browser_colour(self,text=None): if not text: return index = -1 for colour,i in zip(default_colours,range(len(default_colours))): if colour[0] == str(text): index = i break if index>=0: col = QtGui.QColor(default_colours[index][1],default_colours[index][2],default_colours[index][3]) else: if len(text.split('#'))==2: rgb = text.split('#')[1] r,g,b = int(rgb[:2],16),int(rgb[2:4],16),int(rgb[4:],16) col = QtGui.QColor(r,g,b) else: if hasattr(self,'colour_browser_gui') and hasattr(self.colour_browser_gui,'standard_col'): theCol = self.colour_browser_gui.standard_col.get_rgb(name=text) col = QtGui.QColor(theCol[0],theCol[1],theCol[2]) else: col = QtGui.QColor(0,0,0) self.emit(QtCore.SIGNAL("ColourChanged"),col) def ColourComboBoxChanged(self,text): if str(text) == "Colour browser": print text if not hasattr(self,'colour_browser_gui'): import mgWidgets self.colour_browser_gui = mgWidgets.mgColourBrowser() self.connect(self.colour_browser_gui,QtCore.SIGNAL("ColourSelected"),self.set_browser_colour) else: self.colour_browser_gui.UnClose() return index = -1 for colour,i in zip(default_colours,range(len(default_colours))): if colour[0] == text: index = i break if index>=0: col = QtGui.QColor(default_colours[index][1],default_colours[index][2],default_colours[index][3]) else: #Hmm, how do we do "default"? col = QtGui.QColor(0,0,0) self.emit(QtCore.SIGNAL("ColourChanged"),col) def fontChanged(self): #print "Font Changed",self.myfont.family(),self.myfont.pointSize(),self.myfont.bold(),self.myfont.italic(),self.myfont.underline() self.emit(QtCore.SIGNAL("FontChanged"),self.myfont) def changeCharFormat(self,format,col): self.fontComboBox.blockSignals(True) self.fontSizeComboBox.blockSignals(True) self.colourComboBox.blockSignals(True) r,g,b = col.red(),col.green(),col.blue() index = -1 for colour,i in zip(default_colours,range(len(default_colours))): if colour[1] == r and colour[2] == g and colour[3] == b: index = i name = colour[0] cbindex = self.colourComboBox.findText(name) if cbindex>-1: self.colourComboBox.setCurrentIndex(cbindex) else: self.colourComboBox.setCurrentIndex(0) break if index<0: cbindex = self.colourComboBox.findText("Colour browser") if cbindex>-1: self.colourComboBox.setCurrentIndex(cbindex) else: self.colourComboBox.setCurrentIndex(0) font = format.font() comboFont = QtGui.QFont(font) comboFont.setPointSize(14) self.fontComboBox.setCurrentFont(comboFont) idx = self.fontSizeComboBox.findText(str(font.pointSize())) if idx>0: self.fontSizeComboBox.setCurrentIndex(idx) else: self.fontSizeComboBox.setEditText(str(font.pointSize())) self.myfont = QtGui.QFont(font) self.setCurrentCharFormat(format) self.setBoldLabel() self.setItalicLabel() self.setUnderlineLabel() self.setSubLabel() self.setSuperLabel() self.fontComboBox.blockSignals(False) self.fontSizeComboBox.blockSignals(False) self.colourComboBox.blockSignals(False) def FontSizeComboBoxChanged(self,size): self.myfont.setPointSize(int(size)) self.fontChanged() def FontComboBoxChanged(self,font): self.myfont.setFamily(font.family()) self.fontChanged() def setSubLabel(self): isSubscript = self.currentCharFormat.verticalAlignment()==QtGui.QTextCharFormat.AlignSubScript if isSubscript: self.subLabel.setFrameShadow(QtGui.QFrame.Sunken) else: self.subLabel.setFrameShadow(QtGui.QFrame.Raised) def setSuperLabel(self): isSuperscript = self.currentCharFormat.verticalAlignment()==QtGui.QTextCharFormat.AlignSuperScript if isSuperscript: self.superLabel.setFrameShadow(QtGui.QFrame.Sunken) else: self.superLabel.setFrameShadow(QtGui.QFrame.Raised) def toggleSubscript(self): isSubscript = self.currentCharFormat.verticalAlignment()==QtGui.QTextCharFormat.AlignSubScript if isSubscript: self.currentCharFormat.setVerticalAlignment(QtGui.QTextCharFormat.AlignNormal) else: self.currentCharFormat.setVerticalAlignment(QtGui.QTextCharFormat.AlignSubScript) self.setSubLabel() self.setSuperLabel() self.emit(QtCore.SIGNAL("AlignmentChanged"),self.currentCharFormat.verticalAlignment()) def toggleSuperscript(self): isSuperscript = self.currentCharFormat.verticalAlignment()==QtGui.QTextCharFormat.AlignSuperScript if isSuperscript: self.currentCharFormat.setVerticalAlignment(QtGui.QTextCharFormat.AlignNormal) else: self.currentCharFormat.setVerticalAlignment(QtGui.QTextCharFormat.AlignSuperScript) self.setSubLabel() self.setSuperLabel() self.emit(QtCore.SIGNAL("AlignmentChanged"),self.currentCharFormat.verticalAlignment()) def toggleItalic(self): isItalic = self.myfont.italic() italic = 1-isItalic self.myfont.setItalic(italic) self.setItalicLabel() self.fontChanged() def setItalicLabel(self): italic = self.myfont.italic() if italic: self.italicLabel.setFrameShadow(QtGui.QFrame.Sunken) else: self.italicLabel.setFrameShadow(QtGui.QFrame.Raised) def toggleUnderline(self): isUnderline = self.myfont.underline() underline = 1-isUnderline self.myfont.setUnderline(underline) self.setUnderlineLabel() self.fontChanged() def setUnderlineLabel(self): underline = self.myfont.underline() if underline: self.underlineLabel.setFrameShadow(QtGui.QFrame.Sunken) else: self.underlineLabel.setFrameShadow(QtGui.QFrame.Raised) def toggleBold(self): isBold = self.myfont.bold() bold = 1-isBold self.myfont.setBold(bold) self.setBoldLabel() self.fontChanged() def setBoldLabel(self): bold = self.myfont.bold() if bold: self.boldLabel.setFrameShadow(QtGui.QFrame.Sunken) else: self.boldLabel.setFrameShadow(QtGui.QFrame.Raised) class OutputWidget(QtGui.QWidget): def __init__(self,parent=None): QtGui.QWidget.__init__(self,parent) self.myfont = QtGui.QFont("Times",20) self.text = '' def setText(self,text): self.text = text def draw(self,painter): if len(self.text)<1: return; fontedPositionedStrings = FontedPositionedStrings.FontedPositionedStringsFromText(str(self.text),self.myfont) rect = fontedPositionedStrings.getBoundingRect() #self.resize(rect.width(),rect.height()) #hmm, doesn't work. FontedPositionedStrings.drawFontedPositionedStrings(fontedPositionedStrings.getStrings(),painter,-rect.left(),-rect.top(),self.myfont) def paintEvent(self,paintEvent): painter = QtGui.QPainter(self) self.draw(painter) class ClickableTextEdit(QtGui.QTextEdit): def __init__(self,parent=None): QtGui.QTextEdit.__init__(self,parent) self.pendingFont = None self.pendingColor = None def keyPressEvent(self,e): if self.pendingFont: self.setCurrentFont(self.pendingFont) self.pendingFont = None if self.pendingColor: self.setTextColor(self.pendingColor) self.pendingColor = None QtGui.QTextEdit.keyPressEvent(self,e) def setPendingColor(self,color): self.pendingColor = color def setPendingFont(self,font): self.pendingFont = font class TextEditWidget(QtGui.QWidget): def insertUnicodeChar(self,char): self.insertHtml('&#'+str(char)+';') def plainText(self): text = unicode(self.textEdit.toPlainText()) return text def text(self): text = unicode(self.textEdit.toHtml()).encode('ascii', 'xmlcharrefreplace') return text def setText(self,text): self.textEdit.setText(text) def insertHtml(self,text): self.textEdit.insertHtml(text) def setHtml(self,text): self.textEdit.setHtml(text) def __init__(self,parent=None,debug=False): QtGui.QWidget.__init__(self,parent) layout = QtGui.QVBoxLayout() self.textEdit = ClickableTextEdit() font = QtGui.QFont("Times",28) self.setCurrentFont(font) self.output = OutputWidget() self.splitter = QtGui.QSplitter(QtCore.Qt.Vertical,self) self.splitter.addWidget(self.textEdit) if debug: self.splitter.addWidget(self.output) self.fontWidget = SimpleFontWidget(font) boldShortcut = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+B")),self) underlineShortcut = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+U")),self) italicShortcut = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+I")),self) self.connect(boldShortcut,QtCore.SIGNAL("activated()"),self.fontWidget.toggleBold) self.connect(italicShortcut,QtCore.SIGNAL("activated()"),self.fontWidget.toggleItalic) self.connect(underlineShortcut,QtCore.SIGNAL("activated()"),self.fontWidget.toggleUnderline) htmlShortcut = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+G")),self) self.connect(htmlShortcut,QtCore.SIGNAL("activated()"),self.showHTML) layout.addWidget(self.fontWidget) layout.addWidget(self.splitter) self.setLayout(layout) self.connect(self.fontWidget,QtCore.SIGNAL("AlignmentChanged"),self.setCurrentAlignment) self.connect(self.fontWidget,QtCore.SIGNAL("ColourChanged"),self.setCurrentColour) self.connect(self.fontWidget,QtCore.SIGNAL("FontChanged"),self.setCurrentFont) if debug: self.connect(self.textEdit,QtCore.SIGNAL("textChanged()"),self.setOutput) if debug: self.connect(self.textEdit,QtCore.SIGNAL("undoAvailable()"),self.setOutput) self.connect(self.textEdit,QtCore.SIGNAL("currentCharFormatChanged(const QTextCharFormat &)"),self.charFormatChanged) self.fontWidget.setCurrentCharFormat(self.textEdit.currentCharFormat()) if debug: self.setOutput() self.fontWidget.charPicked.connect(self.insertUnicodeChar) def setCurrentAlignment(self,alignment): format = self.textEdit.currentCharFormat() format.setVerticalAlignment(alignment) self.textEdit.setCurrentCharFormat(format) def setCurrentFont(self,font=None,definition={},setGuiFont=False): if definition: font = QtGui.QFont() font.setFamily(definition['family']) if definition['slant'] == 'i': font.setItalic(1) if definition['weight'] == 'bold': font.setBold(1) if definition['underline'] > 0 : font.setUnderline(1) font.setPointSize(int(definition['size'])) if setGuiFont: self.fontWidget.fontComboBox.blockSignals(True) self.fontWidget.fontSizeComboBox.blockSignals(True) idx = self.fontWidget.fontSizeComboBox.findText(str(font.pointSize())) if idx>0: self.fontWidget.fontSizeComboBox.setCurrentIndex(idx) else: self.fontWidget.fontSizeComboBox.setCurrentIndex(idx) self.fontWidget.fontSizeComboBox.setEditText(str(font.pointSize())) self.fontWidget.fontComboBox.blockSignals(False) self.fontWidget.fontSizeComboBox.blockSignals(False) self.myfont = font self.textEdit.setCurrentFont(font) self.textEdit.setPendingFont(font) #print "font changed to",self.textEdit.fontFamily() def setCurrentColour(self,col): self.textEdit.setTextColor(col) self.textEdit.setPendingColor(col) def charFormatChanged(self,format): #print "charFormatChanged" col = self.textEdit.textColor() #print self.textEdit.currentCharFormat().verticalAlignment() self.fontWidget.changeCharFormat(format,col) def sizeHint(self): return QtCore.QSize(400,200) def save(self): text = unicode(self.textEdit.toHtml()) fontedPositionedStrings = FontedPositionedStrings.FontedPositionedStringsFromText(str(text),self.myfont) rect = fontedPositionedStrings.getBoundingRect() pixmap = QtGui.QPixmap(rect.width(),rect.height()) pixmap.fill(QtGui.QColor(0,0,0,0)) painter = QtGui.QPainter() painter.begin(pixmap) FontedPositionedStrings.drawFontedPositionedStrings(fontedPositionedStrings.getStrings(),painter,-rect.left(),-rect.top(),self.myfont) painter.end() file = QtGui.QFileDialog.getSaveFileName(self, self.tr("Save File"),"untitled.png",self.tr("Images (*.png *.bmp *.jpg)")); if file: pixmap.toImage().save(file) def setOutput(self): text = unicode(self.textEdit.toHtml()) self.output.setText(text) self.output.repaint() def showHTML(self): text = unicode(self.textEdit.toHtml()).encode('ascii', 'xmlcharrefreplace') print text if __name__ == "__main__": if "-quit" in sys.argv: sys.exit() app = QtGui.QApplication(sys.argv) win = TextEditWidget() """ for r in unicodeRanges: print r if type(r["range"]) == tuple: win.insertHtml('

'+r['name']+'

') for r2 in r["range"]: start = int(r2[0],16) end = int(r2[1],16) for i in range(start,end+1): win.insertHtml('&#'+str(i)+';') else: start = int(r["range"][0],16) end = int(r["range"][1],16) win.insertHtml('

'+r['name']+'

') for i in range(start,end+1): win.insertHtml('&#'+str(i)+';') win.insertHtml('

') """ win.show() win.raise_() sys.exit(app.exec_())