import sys, re import codecs from qt import QBrush, QCheckBox, QColor, QColorGroup, QFontMetrics, \ QHBox, QHBoxLayout, QHButtonGroup, QLineEdit, QPushButton, QRadioButton, \ QSizePolicy, QScrollView, SIGNAL, QString, Qt, QVButtonGroup, \ QVBoxLayout, QVGroupBox, QWidget from qttable import QTable import codecs Encode,Decode,Reader,Writer=codecs.lookup("utf-8") class Paragraph: def __init__(self): self.tokenBoxes=[] self.previous=None self.next=None def toPrettyText(self,dir): if dir=="Left-to-Right": self.tokenBoxes.sort(lambda a,b:cmp(a.xPos,b.xPos)) else: self.tokenBoxes.sort(lambda a,b:cmp(b.xPos,a.xPos)) return " ".join(b.getText() for b in self.tokenBoxes) class TokenText(QScrollView): def __init__(self, displayParent, master, config, masterEvent): QScrollView.__init__(self, displayParent) self.master=master self.config=config self.masterEvent=masterEvent self.viewport().setBackgroundMode(Qt.PaletteBase) self.setBackgroundMode(Qt.PaletteBase) self.enableClipper(1) self.setFocusPolicy(QWidget.NoFocus) self.baseLineSpacing = self.config.valueOf("Display/Spacing/Lines") self.paragraphSpacing= self.config.valueOf("Display/Spacing/Paragraphs") self.wordSpacing=self.config.valueOf("Display/Spacing/Words") self.paragraphs=[] self.currentToken=None self.token2Box={} self.token2Paragraph={} self.font=None def toPrettyText(self): dir=self.config.valueOf("Display/TextDirection") return "\n\n".join(p.toPrettyText(dir) for p in self.paragraphs) def resizeEvent(self, e): self.redrawWidget() def setFont(self, f): for b in self.token2Box.values(): b.setFont(f) self.font=f def clear(self): tokens=self.token2Box.keys() for t in tokens: self.removeToken(t) self.currentX=0 self.currentY=0 self.paragraphs=[] self.token2Box={} self.token2Paragraph={} def selectToken(self, token): if self.currentToken and self.token2Box.has_key(self.currentToken): b=self.token2Box[self.currentToken] b.unselect() self.currentToken=token if self.currentToken: b=self.token2Box[token] b.select() self.ensureVisible(b.xPos,b.yPos) def load(self, tokenList): self.tokenList=tokenList self.showTokens() def selectedText(self): return "No text selected!" def putText(self, text): pass def putPlainText(self, text): pass def tokenEvent(self, e): e["type"]="token" self.masterEvent(e) def removeToken(self, token): b=self.token2Box[token] p=self.token2Paragraph[token] if p.previous: p.previous.next=p.next if p.next: p.next.previous=p.previous p.tokenBoxes.remove(b) if not p.tokenBoxes: self.paragraphs.remove(p) del self.token2Box[token] del self.token2Paragraph[token] self.removeChild(b) b.deleteLater() ## Update x and y pos of all following tokens def refreshDisplay(self, token=None): self.baseLineSpacing = self.config.valueOf("Display/Spacing/Lines") self.paragraphSpacing= self.config.valueOf("Display/Spacing/Paragraphs") self.wordSpacing=self.config.valueOf("Display/Spacing/Words") if token: b=self.token2Box[token] b.refreshDisplay() else: self.redrawWidget() def insertParagraph(self, tokenList, previousParagraph=None): previous=None p=Paragraph() for t in tokenList: b=self.insertToken(t, previous, p) if previous: previous.next=b previous=t self.paragraphs.append(p) def insertToken(self, t, previous=None, paragraph=None): b=TokenBox(self.viewport(), self, config=self.config,masterEvent=self.tokenEvent, token=t) if self.font: b.setFont(self.font) if previous and not(paragraph): paragraph=self.token2Paragraph[previous] self.token2Box[t]=b if paragraph: self.token2Paragraph[t]=paragraph if b not in paragraph.tokenBoxes: paragraph.tokenBoxes.append(b) if previous: previous.next=b b.previous=previous else: b.previous=None return b def shiftParagraph(self, p,delta=0): for b in p: y=b.yPos self.addChild(b,b.xPos.b.yPos+delta) def redrawParagraph(self, p, startY=0): dir=self.config.valueOf("Display/TextDirection") if dir == "Left-to-Right": startX=self.wordSpacing elif dir == "Right-to-Left": startX=self.width() h=0 for b in p.tokenBoxes: b.refreshDisplay() if dir == "Left-to-Right": if startX <> 0 and startX + b.width() > self.width(): startX=0 startY += self.baseLineSpacing + b.height() elif dir == "Right-to-Left": if startX <> self.width() and startX - b.width() < 0: startX=self.width() startY += self.baseLineSpacing + b.height() b.xPos=startX b.yPos = startY self.addChild(b,b.xPos,b.yPos) if dir == "Left-to-Right": startX += b.width() + self.wordSpacing elif dir == "Right-to-Left": startX -= (b.width() + self.wordSpacing) h=b.height() return startY + h def redrawWidget(self): startY=0 for p in self.paragraphs: startY=self.redrawParagraph(p,startY=startY) + self.paragraphSpacing self.resizeContents(self.width(), startY) def showTokens(self, paragraphList, append=0): if not(append): self.clear() for tokenList in paragraphList: self.insertParagraph(tokenList) self.redrawWidget() class TokenBox(QPushButton): def __init__(self, displayParent, master, masterEvent=None, token=None, config=None): QPushButton.__init__(self, displayParent) self.setFlat(False) self.token=token self.config=config self.masterEvent=masterEvent self.isSelected=False self.color=None self.xPos=0 self.yPos=0 self.token=token self.nextBox=None self.previousBox=None self.refreshDisplay() self.connect(self,SIGNAL("clicked()"),self.selected) self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,True)) self.show() def select(self): self.isSelected=True self.setColor() def unselect(self): self.isSelected=False self.setColor() def selected(self): e={"name":"selected","token":self.token} self.masterEvent(e) def adjustSize(self): """ This will need to be way, way smarter at some point. Just test for now. """ s=QFontMetrics(self.font()).size(0,self.text()) h=s.height()+4 w=s.width()+4 s.setWidth(w) s.setHeight(h) self.setFixedSize(s) def setColor(self, color=None): if not(color): color=self.color brush = QBrush(Qt.SolidPattern) brush.setColor(color) blackBrush=QBrush(Qt.SolidPattern) blackBrush.setColor(QColor(Qt.black)) if self.isSelected: hlBrush=blackBrush else: hlBrush=brush palette = self.palette() palette.setBrush(QColorGroup.Button, brush) palette.setBrush(QColorGroup.Base, brush) palette.setBrush(QColorGroup.Light, hlBrush) palette.setBrush(QColorGroup.Midlight, brush) palette.setBrush(QColorGroup.Dark, brush) palette.setBrush(QColorGroup.Mid, brush) palette.setBrush(QColorGroup.Shadow, hlBrush) self.setPalette(palette) self.repaint() def getText(self): if self.token: text=self.token.getText() if self.token.isSelected(): morph=self.token.currentParse.getUnderlyingForm() gloss=self.token.currentParse.getGloss() else: morph=gloss=" " else: text=morph=gloss=" " if self.config.valueOf("Display/ShowMorphemes"): text += "\n%s" % morph if self.config.valueOf("Display/ShowGlosses"): text += "\n%s" % gloss return text def refreshDisplay(self): if self.config.valueOf("Display/ShowParses"): if self.config and self.token: if self.token.isSelected(): c="Selected" elif not(self.token.isParsing()): c="NonParsing" elif self.token.isAmbiguous(): c="Ambiguous" elif self.token.isUnambiguous(): c="Unambiguous" else: c="Unparsed" colorName=self.config.valueOf("Colors/Highlight/%s" % c) else: colorName="lightGrey" else: colorName="lightGrey" if not(self.color) or colorName <> self.color.name: color=QColor(colorName) self.setColor(color) self.color=color text=self.getText() self.setText(text) self.adjustSize() self.show() class TokenDetailBox(QVGroupBox): def __init__(self, displayParent, master, masterEvent, token=None): QVGroupBox.__init__(self, displayParent) self.master=master self.masterEvent=masterEvent self.changed=False self.tokenText=QLineEdit(self) box=QHBox(self) self.parsable=QCheckBox("Parsable",box) self.parseButton=QPushButton("Parse now",box) buttons=QHButtonGroup(self) self.splitButton=QPushButton("Split",buttons) self.mergeNextButton=QPushButton("Merge with next",buttons) self.mergePreviousButton=QPushButton("Merge with previous",buttons) self.deleteButton=QPushButton("Delete",buttons) self.parseList=ParseList(self, self, self.masterEvent) self.noteList=NoteList(self,self,self.masterEvent) self.font=None self.tokenText.setMaximumWidth(300) self.connect(self.tokenText,SIGNAL("lostFocus()"),self.checkChanges) self.connect(self.tokenText,SIGNAL("textChanged(const QString &)"),self.textChanged) self.connect(self.parsable,SIGNAL("clicked()"),self.parsableChanged) self.connect(self.splitButton,SIGNAL("clicked()"),self.splitToken) self.connect(self.parseButton,SIGNAL("clicked()"),self.parseNow) self.connect(self.mergeNextButton,SIGNAL("clicked()"),self.mergeNext) self.connect(self.mergePreviousButton,SIGNAL("clicked()"),self.mergePrevious) self.connect(self.deleteButton,SIGNAL("clicked()"),self.delete) self.showToken(token) def mergeNext(self): e={"type":"token","name":"mergeNext"} self.masterEvent(e) def mergePrevious(self): e={"type":"token","name":"mergePrevious"} self.masterEvent(e) def delete(self): e={"type":"token","name":"delete"} self.masterEvent(e) def parseNow(self): e={"type":"token","name":"parseCurrent"} self.masterEvent(e) def splitToken(self): e={"type":"token","name":"splitRequested", "splitPoint":self.tokenText.cursorPosition()} self.masterEvent(e) def setFont(self,f): self.font=f self.tokenText.setFont(f) self.parseList.setFont(f) def parsableChanged(self): b=self.parsable.isChecked() self.parseButton.setEnabled(b) e={"type":"token","name":"parsableChanged","value":b} self.masterEvent(e) def checkChanges(self): if self.changed and self.tokenText.isEnabled(): e={"type":"token","name":"textChanged","text":str(self.tokenText.text())} self.masterEvent(e) self.changed=False def textChanged(self, t): if self.tokenText.hasFocus(): self.changed=True def clear(self): self.tokenText.setEnabled(False) self.tokenText.setText("") self.changed=False self.parsable.setEnabled(False) self.splitButton.setEnabled(False) self.parseButton.setEnabled(False) self.mergeNextButton.setEnabled(False) self.mergePreviousButton.setEnabled(False) self.deleteButton.setEnabled(False) self.parseList.clear() self.noteList.clear() self.parseList.setEnabled(False) self.noteList.setEnabled(False) def showToken(self, token, isFirst=False,isLast=False): self.checkChanges() self.clear() if token: self.tokenText.setEnabled(True) self.deleteButton.setEnabled(True) self.parseList.setEnabled(True) self.noteList.setEnabled(True) self.tokenText.setText(token.getText()) self.changed=False self.parsable.setEnabled(True) self.parsable.setChecked(token.isParsing()) self.splitButton.setEnabled(True) self.parseButton.setEnabled(token.isParsing()) self.mergeNextButton.setEnabled(not(isLast)) self.mergePreviousButton.setEnabled(not(isFirst)) for p in token.getParses(): self.parseList.addParse(p) if token.getCurrentParse(): self.parseList.selectParse(token.getCurrentParse()) for n in token.getProblemNotes(): self.noteList.addNote(n) class ParseList(QVButtonGroup): def __init__(self, displayParent, master, masterEvent): QVButtonGroup.__init__(self, "Parses", displayParent) self.master=master self.masterEvent=masterEvent self.button2Parse={} self.parse2Button={} self.connect(self,SIGNAL("clicked(int)"),self.changed) self.font=None def changed(self, i): b=self.find(i) p=self.button2Parse[b] e={"type":"token","name":"parseChanged","parse":p} self.masterEvent(e) def setFont(self, f): self.font=f for b in self.button2Parse: b.setFont(f) def addParse(self, parse): self.hide() uf=parse.getUnderlyingForm() gloss=parse.getGloss() button=QRadioButton("%s\n%s" % (gloss,uf),self) if self.font: button.setFont(self.font) self.button2Parse[button]=parse self.parse2Button[parse]=button self.show() def selectParse(self, parse): button=self.parse2Button[parse] id=self.id(button) self.setButton(id) def clear(self): buttons=self.button2Parse.keys() for b in [x for x in buttons]: self.removeChild(b) b.deleteLater() self.parse2Button={} self.button2Parse={} class NoteWidget(QWidget): def __init__(self, parent,masterEvent,text=""): QWidget.__init__(self, parent) self.parent=parent self.text=text self.note=None self.masterEvent=masterEvent QHBoxLayout(self) self.layout().setAutoAdd(1) self.button=QCheckBox(text,self) self.noteText=QLineEdit(self) self.connect(self.button,SIGNAL("clicked()"),self.toggle) self.connect(self.noteText,SIGNAL("lostFocus()"),self.textChanged) self.clear() def toggle(self): v=self.button.isChecked() self.noteText.setEnabled(v) if v: ## Create a new note with my type pType=self.text text=str(self.noteText.text()) e={"name":"newNote","type":pType,"text":text} self.note=self.masterEvent(e) else: e={"name":"delNote","note":self.note} self.masterEvent(e) self.note=None def textChanged(self): if self.note and self.noteText.isEnabled(): t=str(self.noteText.text()) self.note.setText(t) def loadNote(self, n): self.button.setChecked(True) self.noteText.setText(n.getText()) self.note=n self.noteText.setEnabled(True) def clear(self): self.textChanged() self.button.setChecked(False) self.noteText.setEnabled(False) self.noteText.clear() self.note=None def setFont(self, f): #self.button.setFont(f) self.noteText.setFont(f) class NoteList(QWidget): def __init__(self, displayParent, master, masterEvent): QWidget.__init__(self, displayParent) self.master=master self.masterEvent=masterEvent QVBoxLayout(self) self.layout().setAutoAdd(True) self.type2Widget={} self.font=None def widgetEvent(self, e): if e["name"]=="newNote": return self.addNote(pType=e["type"],text=e["text"]) elif e["name"]=="delNote": note=e["note"] note.delete() def setTypes(self, typeList): self.hide() self.empty() for t in typeList: w=NoteWidget(self,self.widgetEvent, t) self.type2Widget[t]=w self.show() def setFont(self, f): self.font=f for w in self.type2Widget.itervalues(): w.setFont(f) def addNote(self, note=None, pType=None, text=None): if not(note): e={"type":"token","name":"newProblemNote","text":text,"pType":pType} note=self.masterEvent(e) return note else: pType=note.getType() if pType not in self.type2Widget: e={"type":"note","name":"missingType","text":pType} correct=self.masterEvent(e) if correct: note.setType(correct) else: note.delete() note=None if note: self.type2Widget[pType].loadNote(note) def empty(self): keys=[x for x in self.type2Widget.keys()] for t in keys: w=self.type2Widget[t] del self.type2Widget[t] w.deleteLater() def clear(self): for w in self.type2Widget.itervalues(): w.clear()