def on_addLexerButton_clicked(self): """ Private slot to add the lexer association displayed to the list. """ ext = self.editorFileExtEdit.text() if ext.startsWith(self.extsep): ext.replace(self.extsep, "") lexer = self.editorLexerCombo.currentText() if lexer in self.extras: pygmentsLexer = self.pygmentsLexerCombo.currentText() if pygmentsLexer.isEmpty(): lexer = pygmentsLexer else: lexer = QString("Pygments|%1").arg(pygmentsLexer) if not ext.isEmpty() and not lexer.isEmpty(): itmList = self.editorLexerList.findItems(\ ext, Qt.MatchFlags(Qt.MatchExactly), 0) if itmList: index = self.editorLexerList.indexOfTopLevelItem(itmList[0]) itm = self.editorLexerList.takeTopLevelItem(index) del itm itm = QTreeWidgetItem(self.editorLexerList, QStringList() << ext << lexer) self.editorFileExtEdit.clear() self.editorLexerCombo.setCurrentIndex(0) self.pygmentsLexerCombo.setCurrentIndex(0) self.editorLexerList.sortItems(self.editorLexerList.sortColumn(), self.editorLexerList.header().sortIndicatorOrder())
def on_mEndMarkerToolButton_clicked(self): if self.timeStart != 0: self.timeEnd = time.time() print self.timeEnd if self.timeEnd - self.timeStart <= 2: self.timeStart = 0 return s = QSettings() openDir = QString() if (not self.mEndMarkerLineEdit.text().isEmpty()): fi = QFileInfo(self.mEndMarkerLineEdit.text()) openDir = fi.dir().absolutePath() if (openDir.isEmpty()): openDir = s.value("/UI/lastComposerMarkerDir", QDir.homePath()).toString() svgFileName = QFileDialog.getOpenFileName( self, QString("End marker svg file"), openDir) if (not svgFileName.isNull()): fileInfo = QFileInfo(svgFileName) s.setValue("/UI/lastComposerMarkerDir", fileInfo.absolutePath()) self.mArrow.beginCommand(QString("Arrow end marker")) self.mEndMarkerLineEdit.setText(svgFileName) self.mArrow.endCommand() self.timeStart = time.time()
def __init__(self, values, target, parent=None): super(KeyButton, self).__init__(parent) # Save the target QLineEdit self.target = target # save the dictionary of values for each modifier state, and set up # parallel dicts of keytop display QStrings and tooltip QStrings. # Where a value is None, convert it to an empty QString. self.values = values self.glyphs = {} self.tooltips = {} for mode in self.values: py_string = self.values[mode] if py_string is not None: self.glyphs[mode] = QString(py_string) tip = QString() # A value can have multiple chars, put all in tooltip for uc in py_string: if not tip.isEmpty(): tip.append(u'+') tip.append(unicodedata.name(uc)) self.tooltips[mode] = tip else: # value is None, use empty strings self.glyphs[mode] = QString() self.tooltips[mode] = QString() # set constant properties self.setAlignment(Qt.AlignCenter) # both horizontal and vertical self.setTextInteractionFlags(Qt.NoTextInteraction) self.setScaledContents(True) # Make our look square, at least 20px, but able to grow self.setMinimumSize(QSize(20, 20)) self.setSizePolicy(SPOLICY) self.setFrameStyle(QFrame.Panel) self.setFrameShadow(QFrame.Raised) self.setLineWidth(3) # initialize with a glyph self.shift(0)
def formatAssignValue(self, fieldName, value, upper): if self.type() == ("", None) or fieldName.isEmpty(): return "1 = 1" isText = False if value.type() == "string" or value.type() == "stringlist": isText = True formatV = QString() if value.type() == "int" or value.type() == "uint" or value.type( ) == "double": formatV = value.toString() else: formatV = "'" + value.toString() + "'" #print("FORMATV es %s, %s y value era %s" % (formatV, type(formatV), value.toString())) if formatV.isEmpty(): return "1 = 1" if upper and isText: fName = QString("upper(" + fieldName + ")") else: fName = QString(fieldName) return QString(fName + " = " + formatV)
def __init__(self, values, target, parent=None): super(KeyButton, self).__init__(parent) # Save the target QLineEdit self.target = target # save the dictionary of values for each modifier state, and set up # parallel dicts of keytop display QStrings and tooltip QStrings. # Where a value is None, convert it to an empty QString. self.values = values self.glyphs = {} self.tooltips = {} for mode in self.values : py_string = self.values[mode] if py_string is not None: self.glyphs[mode] = QString(py_string) tip = QString() # A value can have multiple chars, put all in tooltip for uc in py_string : if not tip.isEmpty(): tip.append(u'+') tip.append(unicodedata.name(uc)) self.tooltips[mode] = tip else : # value is None, use empty strings self.glyphs[mode] = QString() self.tooltips[mode] = QString() # set constant properties self.setAlignment(Qt.AlignCenter) # both horizontal and vertical self.setTextInteractionFlags(Qt.NoTextInteraction) self.setScaledContents(True) # Make our look square, at least 20px, but able to grow self.setMinimumSize(QSize(20,20)) self.setSizePolicy(SPOLICY) self.setFrameStyle(QFrame.Panel) self.setFrameShadow(QFrame.Raised) self.setLineWidth(3) # initialize with a glyph self.shift(0)
def setFilter(self, f): if not self.cursor_: return previousF = QString(self.cursor_.mainFilter()) newF = QString(None) if previousF.isEmpty(): newF = f elif previousF.contains(f): return else: newF = previousF + " AND " + f self.cursor_.setMainFilter(newF)
def __workingDirectory(path_): """ Private function to determine working directory for the file dialog. @param path_ path of the intended working directory (string or QString) @return calculated working directory (QString) """ path = QString(path_) if not path.isEmpty(): info = QFileInfo(path) if info.exists() and info.isDir(): return info.absoluteFilePath() return info.absolutePath() return QDir.currentPath()
class E4LineEdit(QLineEdit): """ Class implementing a line edit widget showing some inactive text. """ def __init__(self, parent = None, inactiveText = QString()): """ Constructor @param parent reference to the parent widget (QWidget) @param inactiveText text to be shown on inactivity (string or QString) """ QLineEdit.__init__(self, parent) self.__inactiveText = QString(inactiveText) def inactiveText(self): """ Public method to get the inactive text. return inactive text (QString) """ return QString(self.__inactiveText) def setInactiveText(self, inactiveText): """ Public method to set the inactive text. @param inactiveText text to be shown on inactivity (string or QString) """ self.__inactiveText = QString(inactiveText) self.update() def paintEvent(self, evt): """ Protected method handling a paint event. @param evt reference to the paint event (QPaintEvent) """ QLineEdit.paintEvent(self, evt) if self.text().isEmpty() and \ not self.__inactiveText.isEmpty() and \ not self.hasFocus(): panel = QStyleOptionFrameV2() self.initStyleOption(panel) textRect = \ self.style().subElementRect(QStyle.SE_LineEditContents, panel, self) textRect.adjust(2, 0, 0, 0) painter = QPainter(self) painter.setPen(self.palette().brush(QPalette.Disabled, QPalette.Text).color()) painter.drawText(textRect, Qt.AlignLeft | Qt.AlignVCenter, self.__inactiveText)
def parameterValues(self): self.widget.selectAll() items = self.widget.selectedItems() first = True var = QString() for item in items: if first: var.append(item.text()) first = False else: var.append("%s%s" % (self.sep, item.text())) if var.isEmpty(): raise Exception("Error: Insufficient number of input variables") params = {self.id:var} return params
def insertMarkers(self): # Copy the text and if it is empty, complain and exit. qi = QString(self.insertText.text()) if qi.isEmpty(): pqMsgs.warningMsg("No insert text specified") return # See how many pages are involved: all the ones that aren't marked skip n = 0 for i in range(IMC.pageTable.size()): if IMC.pageTable.getAction(i) != IMC.FolioRuleSkip: n += 1 if n == 0: # page table empty or all rows marked skip pqMsgs.warningMsg("No pages to give folios to") return m = "Insert this string at the top of {0} pages?".format(n) b = pqMsgs.okCancelMsg(QString(m), pqMsgs.trunc(qi, 35)) if b: # Convert any '\n' in the text to the QT line delimiter char # we do this in the copy so the lineEdit text doesn't change qi.replace(QString(u'\\n'), QString(IMC.QtLineDelim)) # get a cursor on the edit document tc = QTextCursor(IMC.editWidget.textCursor()) tc.beginEditBlock() # start single undoable operation # Working from the end of the document backward, go to the # top of each page and insert the string for i in reversed(range(IMC.pageTable.size())): if IMC.pageTable.getAction(i) != IMC.FolioRuleSkip: # Note the page's start position and set our work cursor to it pos = IMC.pageTable.getCursor(i).position() tc.setPosition(pos) # Make a copy of the insert string replacing %f with this folio f = IMC.pageTable.getDisplay(i) qf = QString(qi) qf.replace(QString(u'%f'), f, Qt.CaseInsensitive) tc.insertText(qf) # The insertion goes in ahead of the saved cursor position so now # it points after the inserted string. Put it back where it was. IMC.pageTable.setPosition(i, pos) tc.endEditBlock() # wrap up the undo op
def insertMarkers(self): # Copy the text and if it is empty, complain and exit. qi = QString(self.insertText.text()) if qi.isEmpty() : pqMsgs.warningMsg("No insert text specified") return # See how many pages are involved: all the ones that aren't marked skip n = 0 for i in range(IMC.pageTable.size()): if IMC.pageTable.getAction(i) != IMC.FolioRuleSkip : n += 1 if n == 0 : # page table empty or all rows marked skip pqMsgs.warningMsg("No pages to give folios to") return m = "Insert this string at the top of {0} pages?".format(n) b = pqMsgs.okCancelMsg(QString(m),pqMsgs.trunc(qi,35)) if b : # Convert any '\n' in the text to the QT line delimiter char # we do this in the copy so the lineEdit text doesn't change qi.replace(QString(u'\\n'),QString(IMC.QtLineDelim)) # get a cursor on the edit document tc = QTextCursor(IMC.editWidget.textCursor()) tc.beginEditBlock() # start single undoable operation # Working from the end of the document backward, go to the # top of each page and insert the string for i in reversed( range( IMC.pageTable.size() ) ) : if IMC.pageTable.getAction(i) != IMC.FolioRuleSkip : # Note the page's start position and set our work cursor to it pos = IMC.pageTable.getCursor(i).position() tc.setPosition(pos) # Make a copy of the insert string replacing %f with this folio f = IMC.pageTable.getDisplay(i) qf = QString(qi) qf.replace(QString(u'%f'),f,Qt.CaseInsensitive) tc.insertText(qf) # The insertion goes in ahead of the saved cursor position so now # it points after the inserted string. Put it back where it was. IMC.pageTable.setPosition(i, pos) tc.endEditBlock() # wrap up the undo op
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setupUi(self) self.fontDatabase = QFontDatabase() self.path = QString() self.connect(self.exitAction, SIGNAL("triggered()"), qApp, SLOT("quit()")) self.updateStylesCombo(self.fontComboBox.currentFont().family()) self.setWindowModified(False) @pyqtSignature("") def on_newAction_triggered(self): self.effectWidget.reset() self.setWindowModified(False) self.saveAsAction.setEnabled(False) self.saveAction.setEnabled(False) def on_fontComboBox_currentFontChanged(self, font): self.updateStylesCombo(font.family()) def updateStylesCombo(self, family): self.styleComboBox.clear() styles = self.fontDatabase.styles(family) for style in styles: self.styleComboBox.addItem(style) @pyqtSignature("") def on_saveAsAction_triggered(self): filters = self.tr("Supported formats (%1)").arg(" ".join( map(lambda x: "*." + str(x), QImageWriter.supportedImageFormats()))) path = QFileDialog.getSaveFileName(self, self.tr("Save Image"), self.path, filters) if path.isEmpty(): return self.save(path) @pyqtSignature("") def on_saveAction_triggered(self): self.save(self.path) def save(self, path): rect = self.effectWidget.pathRect() image = QImage(rect.size().toSize(), QImage.Format_ARGB32) image.fill(qRgba(0, 0, 0, 0)) self.effectWidget.paint(image, rect) if image.save(path): self.setWindowModified(False) self.saveAction.setEnabled(False) self.path = path def on_effectWidget_contentsChanged(self): self.setWindowModified(True) self.saveAsAction.setEnabled(True) self.saveAction.setEnabled(not self.path.isEmpty())
def enableFindButton(self, text): print type(text), ":", len(text) print(text) t = QString(text) self.findButton.setEnabled(not t.isEmpty())
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent = None): QMainWindow.__init__(self, parent) self.setupUi(self) self.fontDatabase = QFontDatabase() self.path = QString() self.connect(self.exitAction, SIGNAL("triggered()"), qApp, SLOT("quit()")) self.updateStylesCombo(self.fontComboBox.currentFont().family()) self.setWindowModified(False) @pyqtSignature("") def on_newAction_triggered(self): self.effectWidget.reset() self.setWindowModified(False) self.saveAsAction.setEnabled(False) self.saveAction.setEnabled(False) def on_fontComboBox_currentFontChanged(self, font): self.updateStylesCombo(font.family()) def updateStylesCombo(self, family): self.styleComboBox.clear() styles = self.fontDatabase.styles(family) for style in styles: self.styleComboBox.addItem(style) @pyqtSignature("") def on_saveAsAction_triggered(self): filters = self.tr("Supported formats (%1)").arg( " ".join(map(lambda x: "*."+str(x), QImageWriter.supportedImageFormats())) ) path = QFileDialog.getSaveFileName(self, self.tr("Save Image"), self.path, filters) if path.isEmpty(): return self.save(path) @pyqtSignature("") def on_saveAction_triggered(self): self.save(self.path) def save(self, path): rect = self.effectWidget.pathRect() image = QImage(rect.size().toSize(), QImage.Format_ARGB32) image.fill(qRgba(0, 0, 0, 0)) self.effectWidget.paint(image, rect) if image.save(path): self.setWindowModified(False) self.saveAction.setEnabled(False) self.path = path def on_effectWidget_contentsChanged(self): self.setWindowModified(True) self.saveAsAction.setEnabled(True) self.saveAction.setEnabled(not self.path.isEmpty())
class ShipContainer(object): def __init__(self, filename=QString()): self.filename = QString(filename) self.dirty = False self.ships = {} self.owners = set() self.countries = set() def ship(self, identity): return self.ships.get(identity) def addShip(self, ship): self.ships[id(ship)] = ship self.owners.add(unicode(ship.owner)) self.countries.add(unicode(ship.country)) self.dirty = True def removeShip(self, ship): del self.ships[id(ship)] del ship self.dirty = True def __len__(self): return len(self.ships) def __iter__(self): for ship in self.ships.values(): yield ship def inOrder(self): return sorted(self.ships.values()) def inCountryOwnerOrder(self): return sorted(self.ships.values(), key=lambda x: (x.country, x.owner, x.name)) def load(self): exception = None fh = None try: if self.filename.isEmpty(): raise IOError, "no filename specified for loading" fh = QFile(self.filename) if not fh.open(QIODevice.ReadOnly): raise IOError, unicode(fh.errorString()) stream = QDataStream(fh) magic = stream.readInt32() if magic != MAGIC_NUMBER: raise IOError, "unrecognized file type" fileVersion = stream.readInt16() if fileVersion != FILE_VERSION: raise IOError, "unrecognized file type version" self.ships = {} while not stream.atEnd(): name = QString() owner = QString() country = QString() description = QString() stream >> name >> owner >> country >> description teu = stream.readInt32() ship = Ship(name, owner, country, teu, description) self.ships[id(ship)] = ship self.owners.add(unicode(owner)) self.countries.add(unicode(country)) self.dirty = False except IOError, err: exception = err finally:
class helpDisplay(QWebView): def __init__(self, parent=None ): super(helpDisplay, self).__init__(parent) # make page unmodifiable self.page().setContentEditable(False) # initialize settings # Find out the nearest font to Palatino qf = QFont() qf.setStyleStrategy(QFont.PreferAntialias+QFont.PreferMatch) qf.setStyleHint(QFont.Serif) qf.setFamily(QString(u'Palatino')) qfi = QFontInfo(qf) # set the default font to that serif font self.settings().setFontFamily(QWebSettings.StandardFont, qfi.family()) self.settings().setFontSize(QWebSettings.DefaultFontSize, 16) self.settings().setFontSize(QWebSettings.MinimumFontSize, 6) self.settings().setFontSize(QWebSettings.MinimumLogicalFontSize, 6) self.textZoomFactor = 1.0 self.setTextSizeMultiplier(self.textZoomFactor) self.settings().setAttribute(QWebSettings.JavascriptEnabled, False) self.settings().setAttribute(QWebSettings.JavaEnabled, False) self.settings().setAttribute(QWebSettings.PluginsEnabled, False) self.settings().setAttribute(QWebSettings.ZoomTextOnly, True) #self.settings().setAttribute(QWebSettings.SiteSpecificQuirksEnabled, False) self.userFindText = QString() # Look for pqHelp.html in the app folder and copy its text into # a local buffer. If it isn't found, put a message there instead. # We need to keep it in order to implement the "back" function. helpPath = os.path.join(IMC.appBasePath,u'pqHelp.html') helpFile = QFile(helpPath) if not helpFile.exists(): self.HTMLstring = QString('''<p>Unable to locate pqHelp.html.</p> <p>Looking in {0}'''.format(helpPath) ) elif not helpFile.open(QIODevice.ReadOnly) : self.HTMLstring = QString('''<p>Unable to open pqHelp.html.</p> <p>Looking in {0}</p><p>Error code {1}</p>'''.format(helpPath, helpFile.error()) ) else: helpStream = QTextStream(helpFile) helpStream.setCodec('ISO8859-1') self.HTMLstring = helpStream.readAll() self.setHtml(self.HTMLstring) # Re-implement the parent's keyPressEvent in order to provide a simple # find function and font-zoom from ctl-plus/minus. We start the view at # 16 points and textSizeMultiplier of 1.0. Each time the user hits ctl-minus # we deduct 0.0625 from the multiplier, and for each ctl-+ we add 0.0625 # (1/16) to the multiplier. This ought to cause the view to change up or # down by one point. We set a limit of 0.375 (6 points) at the low # end and 4.0 (64 points) at the top. def keyPressEvent(self, event): #pqMsgs.printKeyEvent(event) kkey = int( int(event.modifiers()) & IMC.keypadDeModifier) | int(event.key()) if (kkey == IMC.ctl_F) or (kkey == IMC.ctl_G) : # ctl/cmd f/g event.accept() self.doFind(kkey) elif (kkey in IMC.zoomKeys) : # ctl-plus/minus zfactor = 0.0625 # zoom in if (kkey == IMC.ctl_minus) : zfactor = -zfactor # zoom out zfactor += self.textZoomFactor if (zfactor > 0.374) and (zfactor < 4.0) : self.textZoomFactor = zfactor self.setTextSizeMultiplier(self.textZoomFactor) elif (kkey in IMC.backKeys) : # ctl-B/[/left if self.page().history().currentItemIndex() > 1 : self.page().history().back() else : self.setHtml(self.HTMLstring) self.page().history().clear() else: # not ctl/cmd f or ctl/cmd-plus/minus, so, event.ignore() super(helpDisplay, self).keyPressEvent(event) # Implement a simple Find/Find-Next, same logic as in pqNotes, # but adjusted for our widget being a webview, and it does the # wraparound for us. def doFind(self,kkey): if (kkey == IMC.ctl_F) or (self.userFindText.isEmpty()) : # ctl+F, or ctl+G but no previous find done, show the find dialog # with a COPY of current selection as pqMsgs might truncate it prepText = QString(self.page().selectedText()) (ok, self.userFindText) = pqMsgs.getFindMsg(self,prepText) # We should have some findText now, either left from previous find # on a ctl-G, or entered by user. If none, then user cleared dialog. if not self.userFindText.isEmpty() : if not self.page().findText( self.userFindText, QWebPage.FindWrapsAroundDocument ) : pqMsgs.beep()
class htmlPreview(QWidget): def __init__(self, parent=None): super(htmlPreview, self).__init__(parent) hbox = QHBoxLayout() self.refreshButton = QPushButton(u"Refresh") hbox.addWidget(self.refreshButton, 0) hbox.addStretch(1) self.refreshAndClearButton = QPushButton(u"Refresh + Clear") hbox.addWidget(self.refreshAndClearButton) vbox = QVBoxLayout() vbox.addLayout(hbox, 0) self.preview = QWebView(self) # save a shortcut reference to web page and its history self.webPage = self.preview.page() self.history = self.preview.page().history() vbox.addWidget(self.preview, 1) self.setLayout(vbox) # make the web preview uneditable self.webPage.setContentEditable(False) self.settings = self.preview.settings() # Find out the nearest font to Palatino qf = QFont() qf.setStyleStrategy(QFont.PreferAntialias + QFont.PreferMatch) qf.setStyleHint(QFont.Serif) qf.setFamily(QString(u'Palatino')) qfi = QFontInfo(qf) # set the default font to that serif font self.settings.setFontFamily(QWebSettings.StandardFont, qfi.family()) self.settings.setFontSize(QWebSettings.DefaultFontSize, 16) self.settings.setFontSize(QWebSettings.MinimumFontSize, 6) self.settings.setFontSize(QWebSettings.MinimumLogicalFontSize, 6) self.zoomFactor = 1.0 # Disable everything but bog-standard html, appropriate for PP self.settings.setAttribute(QWebSettings.JavascriptEnabled, False) self.settings.setAttribute(QWebSettings.JavaEnabled, False) self.settings.setAttribute(QWebSettings.PluginsEnabled, False) self.settings.setAttribute(QWebSettings.ZoomTextOnly, False) # the following causes a hard error in linux #self.settings.setAttribute(QWebSettings.SiteSpecificQuirksEnabled, False) # hook up the refresh buttons self.connect(self.refreshButton, SIGNAL("clicked()"), self.refresh1Click) self.connect(self.refreshAndClearButton, SIGNAL("clicked()"), self.refresh2Click) # hook up the load status signals self.connect(self.preview, SIGNAL("loadStarted()"), self.loadStarts) self.connect(self.preview, SIGNAL("loadProgress(int)"), self.loadProgresses) self.connect(self.preview, SIGNAL("loadFinished(bool)"), self.loadEnds) # here we store the scroll position to return to after reloading self.scrollPosition = QPoint(0, 0) # here save the user's find text for ctl-g use self.findText = QString() # here store the base URL for the current book. self.baseURL = QUrl() # we do NOT initialize the preview (e.g. by calling self.refresh) # at construction time. It may be many hours before the user wants # to preview html. So require an explicit refresh click to do it. # Plain Refresh clicked. def refresh1Click(self): self.refresh(False) # Refresh + Clear clicked. def refresh2Click(self): self.refresh(True) # One or the other refresh button clicked. # Get the current scroll position (a QPoint that reflects the position of the # scrollbar "thumb" in the webview) from the QWebFrame that is associated with # the QWebPage that is displayed in our QWebView, and save it for use after # the loadEnds() signal is received, to restore the previous scroll position. # # If Refresh+Clear was clicked, clear the memory cache -- a function that is # strangely located in the web settings object, but whatever -- and then reload # the HTML contents from the editor document, def refresh(self, clearCache=False): # this could be first refresh for this book file, so set the # base URL for its images. sep = QChar(u'/') qsp = QString(IMC.bookDirPath) if not qsp.endsWith(sep): qsp.append(sep) self.baseURL = QUrl.fromLocalFile(qsp) # this might be the second or nth refresh of the book, note the # scroll position so we can restore it in loadEnds below. This # means that when you make a little edit at the end of a book, and # refresh the preview, you won't have to scroll down to the end # for the 500th time to see your changes. self.scrollPosition = self.webPage.mainFrame().scrollPosition() if clearCache: self.settings.clearMemoryCaches() # We are reloading our base page, so clear any history of prior links self.history.clear() self.preview.setHtml(IMC.editWidget.toPlainText(), self.baseURL) # handle the load-in-progress signals by running our main window's # progress bar def loadStarts(self): pqMsgs.startBar(100, "Loading HTML") def loadProgresses(self, amt): pqMsgs.rollBar(amt, refresh=False) def loadEnds(self, bool): pqMsgs.endBar() if bool: # load was ok, reset scroll position now the rendering is finished. self.webPage.mainFrame().setScrollPosition(self.scrollPosition) # our panel is visible (else how was Refresh clicked?) but it may # not have the keyboard focus. Right after refresh one usually wants # to use keys like page-up/dn, so get the focus to our webview # widget (not the page in it because the webview has the scroll # bars and other mechanism.) self.preview.setFocus(Qt.MouseFocusReason) else: pqMsgs.warningMsg("Some problem loading html") # Handle the docWillChange signal: tell ourselves to stop whatever we is doing. # If we are actually loading something, presumably that provokes a call to # loadEnds above. def docWillChange(self): self.preview.stop() # And on the ensuing docHasChanged signal, clear our document. def docHasChanged(self): self.preview.setHtml(QString()) # Re-implement the parent's keyPressEvent in order to provide a simple # find function, font-zoom from ctl-plus/minus, and browser "back". # For the font size, we initialize the view at 16 points and # the textSizeMultiplier at 1.0. Each time the user hits ctl-minus # we deduct 0.0625 from the multiplier, and for each ctl-+ we add 0.0625 # (1/16) to the multiplier. This ought to cause the view to change up or # down by one point. We set a limit of 0.375 (6 points) at the low # end and 4.0 (64 points) at the top. def keyPressEvent(self, event): kkey = int(int(event.modifiers()) & IMC.keypadDeModifier) | int( event.key()) if (kkey == IMC.ctl_F) or (kkey == IMC.ctl_G): # ctl/cmd f/g event.accept() self.doFind(kkey) elif (kkey in IMC.zoomKeys): # ctrl-plus/minus zfactor = 0.0625 # zoom in if (kkey == IMC.ctl_minus): zfactor = -zfactor # zoom out zfactor += self.zoomFactor if (zfactor > 0.374) and (zfactor < 4.0): self.zoomFactor = zfactor self.preview.setZoomFactor(self.zoomFactor) elif (kkey in IMC.backKeys): if self.history.currentItemIndex() > 1: self.webPage.triggerAction(QWebPage.Back) else: # reload the html of the book text, but don't call refresh # because it would capture the scroll position as of now, # and that relates to the linked page we are coming back from. # The scroll position noted the last time Refresh was clicked # will be instantiated in the loadEnds slot. self.history.clear() self.preview.setHtml(IMC.editWidget.toPlainText(), self.baseURL) else: # not a key we support, so, event.ignore() super(htmlPreview, self).keyPressEvent(event) # Implement a simple Find/Find-Next, same logic as in pqNotes, # but adjusted for our widget being a webview, and it does the # wraparound for us. def doFind(self, kkey): if (kkey == IMC.ctl_F) or (self.findText.isEmpty()): # ctl+F, or ctl+G but no previous find done, show the find dialog # with a COPY of current selection as pqMsgs might truncate it prepText = QString(self.webPage.selectedText()) (ok, self.findText) = pqMsgs.getFindMsg(self, prepText) # dialog or no dialog, we should have some findText now if not self.findText.isEmpty(): if not self.webPage.findText(self.findText, QWebPage.FindWrapsAroundDocument): pqMsgs.beep()
class htmlPreview(QWidget): def __init__(self, parent=None ): super(htmlPreview, self).__init__(parent) hbox = QHBoxLayout() self.refreshButton = QPushButton(u"Refresh") hbox.addWidget(self.refreshButton,0) hbox.addStretch(1) self.refreshAndClearButton = QPushButton(u"Refresh + Clear") hbox.addWidget(self.refreshAndClearButton) vbox = QVBoxLayout() vbox.addLayout(hbox,0) self.preview = QWebView(self) # save a shortcut reference to web page and its history self.webPage = self.preview.page() self.history = self.preview.page().history() vbox.addWidget(self.preview,1) self.setLayout(vbox) # make the web preview uneditable self.webPage.setContentEditable(False) self.settings = self.preview.settings() # Find out the nearest font to Palatino qf = QFont() qf.setStyleStrategy(QFont.PreferAntialias+QFont.PreferMatch) qf.setStyleHint(QFont.Serif) qf.setFamily(QString(u'Palatino')) qfi = QFontInfo(qf) # set the default font to that serif font self.settings.setFontFamily(QWebSettings.StandardFont, qfi.family()) self.settings.setFontSize(QWebSettings.DefaultFontSize, 16) self.settings.setFontSize(QWebSettings.MinimumFontSize, 6) self.settings.setFontSize(QWebSettings.MinimumLogicalFontSize, 6) self.zoomFactor = 1.0 # Disable everything but bog-standard html, appropriate for PP self.settings.setAttribute(QWebSettings.JavascriptEnabled, False) self.settings.setAttribute(QWebSettings.JavaEnabled, False) self.settings.setAttribute(QWebSettings.PluginsEnabled, False) self.settings.setAttribute(QWebSettings.ZoomTextOnly, False) # the following causes a hard error in linux #self.settings.setAttribute(QWebSettings.SiteSpecificQuirksEnabled, False) # hook up the refresh buttons self.connect(self.refreshButton, SIGNAL("clicked()"),self.refresh1Click) self.connect(self.refreshAndClearButton, SIGNAL("clicked()"),self.refresh2Click) # hook up the load status signals self.connect(self.preview,SIGNAL("loadStarted()"),self.loadStarts ) self.connect(self.preview,SIGNAL("loadProgress(int)"),self.loadProgresses ) self.connect(self.preview,SIGNAL("loadFinished(bool)"),self.loadEnds ) # here we store the scroll position to return to after reloading self.scrollPosition = QPoint(0,0) # here save the user's find text for ctl-g use self.findText = QString() # here store the base URL for the current book. self.baseURL = QUrl() # we do NOT initialize the preview (e.g. by calling self.refresh) # at construction time. It may be many hours before the user wants # to preview html. So require an explicit refresh click to do it. # Plain Refresh clicked. def refresh1Click(self) : self.refresh(False) # Refresh + Clear clicked. def refresh2Click(self) : self.refresh(True) # One or the other refresh button clicked. # Get the current scroll position (a QPoint that reflects the position of the # scrollbar "thumb" in the webview) from the QWebFrame that is associated with # the QWebPage that is displayed in our QWebView, and save it for use after # the loadEnds() signal is received, to restore the previous scroll position. # # If Refresh+Clear was clicked, clear the memory cache -- a function that is # strangely located in the web settings object, but whatever -- and then reload # the HTML contents from the editor document, def refresh(self, clearCache=False ): # this could be first refresh for this book file, so set the # base URL for its images. sep = QChar(u'/') qsp = QString(IMC.bookDirPath) if not qsp.endsWith(sep): qsp.append(sep) self.baseURL = QUrl.fromLocalFile(qsp) # this might be the second or nth refresh of the book, note the # scroll position so we can restore it in loadEnds below. This # means that when you make a little edit at the end of a book, and # refresh the preview, you won't have to scroll down to the end # for the 500th time to see your changes. self.scrollPosition = self.webPage.mainFrame().scrollPosition() if clearCache : self.settings.clearMemoryCaches() # We are reloading our base page, so clear any history of prior links self.history.clear() self.preview.setHtml(IMC.editWidget.toPlainText(),self.baseURL) # handle the load-in-progress signals by running our main window's # progress bar def loadStarts(self): pqMsgs.startBar(100,"Loading HTML") def loadProgresses(self,amt): pqMsgs.rollBar(amt, refresh=False) def loadEnds(self,bool): pqMsgs.endBar() if bool: # load was ok, reset scroll position now the rendering is finished. self.webPage.mainFrame().setScrollPosition(self.scrollPosition) # our panel is visible (else how was Refresh clicked?) but it may # not have the keyboard focus. Right after refresh one usually wants # to use keys like page-up/dn, so get the focus to our webview # widget (not the page in it because the webview has the scroll # bars and other mechanism.) self.preview.setFocus(Qt.MouseFocusReason) else: pqMsgs.warningMsg("Some problem loading html") # Handle the docWillChange signal: tell ourselves to stop whatever we is doing. # If we are actually loading something, presumably that provokes a call to # loadEnds above. def docWillChange(self) : self.preview.stop() # And on the ensuing docHasChanged signal, clear our document. def docHasChanged(self) : self.preview.setHtml(QString()) # Re-implement the parent's keyPressEvent in order to provide a simple # find function, font-zoom from ctl-plus/minus, and browser "back". # For the font size, we initialize the view at 16 points and # the textSizeMultiplier at 1.0. Each time the user hits ctl-minus # we deduct 0.0625 from the multiplier, and for each ctl-+ we add 0.0625 # (1/16) to the multiplier. This ought to cause the view to change up or # down by one point. We set a limit of 0.375 (6 points) at the low # end and 4.0 (64 points) at the top. def keyPressEvent(self, event): kkey = int( int(event.modifiers()) & IMC.keypadDeModifier) | int(event.key()) if (kkey == IMC.ctl_F) or (kkey == IMC.ctl_G) : # ctl/cmd f/g event.accept() self.doFind(kkey) elif (kkey in IMC.zoomKeys) : # ctrl-plus/minus zfactor = 0.0625 # zoom in if (kkey == IMC.ctl_minus) : zfactor = -zfactor # zoom out zfactor += self.zoomFactor if (zfactor > 0.374) and (zfactor < 4.0) : self.zoomFactor = zfactor self.preview.setZoomFactor(self.zoomFactor) elif (kkey in IMC.backKeys) : if self.history.currentItemIndex() > 1 : self.webPage.triggerAction(QWebPage.Back) else : # reload the html of the book text, but don't call refresh # because it would capture the scroll position as of now, # and that relates to the linked page we are coming back from. # The scroll position noted the last time Refresh was clicked # will be instantiated in the loadEnds slot. self.history.clear() self.preview.setHtml(IMC.editWidget.toPlainText(),self.baseURL) else: # not a key we support, so, event.ignore() super(htmlPreview, self).keyPressEvent(event) # Implement a simple Find/Find-Next, same logic as in pqNotes, # but adjusted for our widget being a webview, and it does the # wraparound for us. def doFind(self,kkey): if (kkey == IMC.ctl_F) or (self.findText.isEmpty()) : # ctl+F, or ctl+G but no previous find done, show the find dialog # with a COPY of current selection as pqMsgs might truncate it prepText = QString(self.webPage.selectedText()) (ok, self.findText) = pqMsgs.getFindMsg(self,prepText) # dialog or no dialog, we should have some findText now if not self.findText.isEmpty() : if not self.webPage.findText( self.findText, QWebPage.FindWrapsAroundDocument ) : pqMsgs.beep()
class Level(QFrame): def __init__(self, parent): QFrame.__init__(self,parent) self.filename = QString() self.copiedItem = QByteArray() self.pasteOffset = 5 self.prevPoint = QPoint() self.addOffset = 5 self.screenSize = (320, 240) self.bgColor = QColor(244,244,244) '''0.Portrait 1.Landscape''' self.orientation = 0 self.currentItem = None self.printer = QPrinter(QPrinter.HighResolution) self.printer.setPageSize(QPrinter.Letter) '''1.Header''' self.levelBar = LevelBar(self) '''3.Tiler''' self.tiler = TileMapGrid(self)#Tiler(self) self.tiler.setMinimumHeight(100) #self.tiler.currentChanged.connect(self.closeDesigner) #self.tiler.setTabsClosable(True) #self.tiler.setTabShape(0) #self.tiler.hide() #self.levelLayout.addWidget(self.levelBar) '''2.view''' viewLayoutWidget = QFrame() viewLayoutWidget.setFrameShape(QFrame.StyledPanel) viewLayout = QHBoxLayout(viewLayoutWidget) #viewLayout.setMargin(10) self.view = LevelView(viewLayoutWidget) '''scene''' self.scene = QGraphicsScene(self) #self.scene.selectionChanged.connect(self.setConnect) #self.view.setStyleSheet("border: 1px solid red;") self.setBackgroundColor(self.bgColor) self.setScreenSize(self.screenSize) self.view.setScene(self.scene) self.view.setAlignment(Qt.AlignCenter) self.scroll_off = 1 self.setScrollBar() viewLayout.setMargin(0) viewLayout.addWidget(self.view) self.wrapped = [] # Needed to keep wrappers alive layout = QVBoxLayout(self) layout.addWidget(self.levelBar) layout.addWidget(viewLayoutWidget) layout.addWidget(self.tiler) layout.setMargin(0) self.setLayout(layout) def setScrollBar(self): if(self.scroll_off): self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scroll_off = 0 else: self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.scroll_off = 1 def setBackgroundColor(self,color): self.bgColor = color self.scene.setBackgroundBrush(QBrush(color)) def setScreenSize(self,size): self.screenSize = size self.setOrientation(self.orientation) def setOrientation(self,idx): self.orientation = idx if(idx == 0): self.view.setMaximumSize(self.screenSize[1], self.screenSize[0]) self.scene.setSceneRect(0, 0, self.screenSize[1], self.screenSize[0]) else: self.view.setMaximumSize(self.screenSize[0], self.screenSize[1]) self.scene.setSceneRect(0, 0, self.screenSize[0], self.screenSize[1]) def offerSave(self): if (Dirty and QMessageBox.question(self, "Designer - Unsaved Changes", "Save unsaved changes?", QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes): self.save() def position(self): point = self.mapFromGlobal(QCursor.pos()) if not self.view.geometry().contains(point): coord = random.randint(36, 144) point = QPoint(coord, coord) else: if point == self.prevPoint: point += QPoint(self.addOffset, self.addOffset) self.addOffset += 5 else: self.addOffset = 5 self.prevPoint = point return self.view.mapToScene(point) def selectedItem(self): items = self.scene.selectedItems() if len(items) == 1: return items[0] return None def current(self,item): self.scene.clearSelection() sceneItems = self.scene.items() for items in sceneItems: if(items != item): item.setSelected(False) if(item.isConnected()): self.propertyBar.disconnectText(item) item.setConnected(False) self.currentItem = item self.currentItem.setConnected(True) self.currentItem.setSelected(True) self.propertyBar.connectText(self.currentItem) self.propertyBar.initText(self.currentItem) def addText(self): item = TextItem("SomeText", self.position()) self.connect(item, SIGNAL("current"),self.current) self.connect(item, SIGNAL("copy"),self.copy) self.connect(item, SIGNAL("cut"),self.cut) self.connect(item, SIGNAL("paste"),self.paste) self.connect(item, SIGNAL("delete"),self.delete) #self.current(item) self.scene.addItem(item) def copy(self): item = self.selectedItem() if item is None: return self.copiedItem.clear() self.pasteOffset = 5 stream = QDataStream(self.copiedItem, QIODevice.WriteOnly) self.writeItemToStream(stream, item) def cut(self): item = self.selectedItem() if item is None: return self.copy() self.scene.removeItem(item) del item def paste(self): if self.copiedItem.isEmpty(): return stream = QDataStream(self.copiedItem, QIODevice.ReadOnly) item = self.readItemFromStream(stream, self.pasteOffset) self.pasteOffset += 5 #self.scene.addItem(item) def delete(self): items = self.scene.selectedItems() if (len(items) and QMessageBox.question(self, "Designer - Delete", "Delete {0} item{1}?".format(len(items), "s" if len(items) != 1 else ""), QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes): while items: item = items.pop() self.scene.removeItem(item) del item def readItemFromStream(self, stream, offset=0): type = QString() position = QPointF() matrix = QMatrix() stream >> type >> position >> matrix if offset: position += QPointF(offset, offset) if type == "Text": text = QString() font = QFont() stream >> text >> font self.scene.addItem(TextItem(text, position, font, matrix)) elif type == "Box": rect = QRectF() stream >> rect style = Qt.PenStyle(stream.readInt16()) self.scene.addItem(BoxItem(position, style, matrix)) elif type == "Pixmap": pixmap = QPixmap() stream >> pixmap self.scene.addItem(self.createPixmapItem(pixmap, position, matrix)) def writeItemToStream(self, stream, item): if isinstance(item, QGraphicsTextItem): stream << QString("Text") << item.pos() \ << item.matrix() << item.toPlainText() << item.font() elif isinstance(item, QGraphicsPixmapItem): stream << QString("Pixmap") << item.pos() \ << item.matrix() << item.pixmap() elif isinstance(item, BoxItem): stream << QString("Box") << item.pos() \ << item.matrix() << item.rect stream.writeInt16(item.style) def rotate(self): for item in self.scene.selectedItems(): item.rotate(30) def print_(self): dialog = QPrintDialog(self.printer) if dialog.exec_(): painter = QPainter(self.printer) painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.TextAntialiasing) self.scene.clearSelection() #self.removeBorders() self.scene.render(painter) #self.addBorders() def open(self): self.offerSave() path = (QFileInfo(self.filename).path() if not self.filename.isEmpty() else ".") fname = QFileDialog.getOpenFileName(self, "Page Designer - Open", path, "Page Designer Files (*.pgd)") if fname.isEmpty(): return self.filename = fname fh = None try: fh = QFile(self.filename) if not fh.open(QIODevice.ReadOnly): raise IOError, unicode(fh.errorString()) items = self.scene.items() while items: item = items.pop() self.scene.removeItem(item) del item self.addBorders() stream = QDataStream(fh) stream.setVersion(QDataStream.Qt_4_2) magic = stream.readInt32() if magic != MagicNumber: raise IOError, "not a valid .pgd file" fileVersion = stream.readInt16() if fileVersion != FileVersion: raise IOError, "unrecognised .pgd file version" while not fh.atEnd(): self.readItemFromStream(stream) except IOError, e: QMessageBox.warning(self, "Page Designer -- Open Error", "Failed to open {0}: {1}".format(self.filename, e)) finally:
class notesEditor(QPlainTextEdit): def __init__(self, parent=None, fontsize=12 ): super(notesEditor, self).__init__(parent) # Do not allow line-wrap; horizontal scrollbar appears when required. self.setLineWrapMode(QPlainTextEdit.NoWrap) # get same font as main editor uses self.setFont(pqMsgs.getMonoFont(12,False)) self.connect(self.document(), SIGNAL("modificationChanged(bool)"),self.eek) self.findText = QString() # called from pqEdit.clear() def clear(self): self.clearing = True self.document().clear() self.document().setModified(False) self.findText = QString() self.clearing = False # slot to receive the modificationChanged(bool) signal from our document. # if it indicates we are modified, set needMetadataSave true if it isn't # already. Should the user undo out of all changes, modval will be false # and we clear our flag, possibly making the flag 0. def eek(self,modval): if not self.clearing : if modval : IMC.needMetadataSave |= IMC.notePanelChanged else : IMC.needMetadataSave &= (0xff ^ IMC.notePanelChanged) IMC.mainWindow.setWinModStatus() # Re-implement the parent's keyPressEvent in order to provide zoom: # ctrl-plus and ctrl-minus step the font size one point. Other keys: # shift-ctl-L inserts the current line number as {nnn} # ctl-L looks for a nearby {nnn} (braces required), selects it, and # tells the main editor to jump to that line. # shift-ctl-P insert the current page (scan) number as [nnn] # ctl-P looks for a nearby [nnn] (brackets required), selects it, and # tells the main editor to jump to that page position. def keyPressEvent(self, event): #pqMsgs.printKeyEvent(event) kkey = int( int(event.modifiers()) & IMC.keypadDeModifier) | int(event.key()) if kkey in IMC.keysOfInterest : # tentatively assume we will process this key event.accept() if kkey in IMC.zoomKeys : n = (-1) if (kkey == IMC.ctl_minus) else 1 p = self.fontInfo().pointSize() + n if (p > 3) and (p < 65): # don't let's get ridiculous, hmm? f = self.font() # so get our font, f.setPointSize(p) # change its point size +/- self.setFont(f) # and put the font back elif (kkey == IMC.ctl_alt_M): # ctrl/cmd-m with shift self.insertLine() elif (kkey == IMC.ctl_M): # ctrl/cmd-m (no shift) self.goToLine() elif (kkey == IMC.ctl_alt_P): # ctl/cmd-p self.insertPage() elif (kkey == IMC.ctl_P): #ctl/cmd-P self.goToPage() elif (kkey == IMC.ctl_F) or (kkey == IMC.ctl_G): # ctl/cmd f/g self.doFind(kkey) else: # one of the keys we support but not in this panel event.ignore() # so clear the accepted flag else: # not one of our keys at all event.ignore() # ensure the accepted flag is off if not event.isAccepted() : # if we didn't handle it, pass it up super(notesEditor, self).keyPressEvent(event) # on ctl-alt-l (mac: opt-cmd-l), insert the current edit line number in # notes as {nnn} def insertLine(self): tc = self.textCursor() bn = IMC.editWidget.textCursor().blockNumber() # line num tc.insertText(u"{{{0}}}".format(bn)) # on ctl-l (mac: cmd-l) look for a {nnn} line number "near" our cursor in # the notes. Strategy: find-backwards for '{', the forward for regex (\d+)\} def goToLine(self): tc = self.document().find(QString(u'{'), self.textCursor(), QTextDocument.FindBackward) if not tc.isNull(): # found it, or one. tc now selects the { re = QRegExp(QString(u'\{\d+\}')) tc.setPosition(tc.selectionStart()) # start looking left of { tc = self.document().find(re,tc) if not tc.isNull(): # found that. self.setTextCursor(tc) # highlight the found string qs = tc.selectedText() # "{nnn}" qs.remove(0,1) # "nnn}" qs.chop(1) # "nnn" # toInt returns a tuple, (int, flag) where flag is false # if the conversion fails. In this case it cannot fail # since we found \d+ in the first place. However, the # number might be invalid as a line number. (bn,flg) = qs.toInt() # line number as int doc = IMC.editWidget.document() # main document tb = doc.findBlockByNumber(bn) # text block number bn if tb.isValid(): # if it exists, tc = IMC.editWidget.textCursor() # cursor on main doc tc.setPosition(tb.position()) # set it on block IMC.editWidget.setTextCursor(tc) # make it visible IMC.editWidget.setFocus(Qt.TabFocusReason) else: # no {ddd} seen pqMsgs.beep() else: # no { preceding the cursor on same line pqMsgs.beep() # Insert current page number as [ppp]. Conveniently, pqPngs saves the # index of the current page in the page table whenever the cursor moves. # (BUG: if there is no pngs folder, that won't happen) def insertPage(self): tc = self.textCursor() if IMC.currentImageNumber is not None: tc.insertText("[{0}]".format(unicode(IMC.currentImageNumber))) # on ^p, look for [ppp] "near" our cursor in notes, and if found, tell # editor to go to that page text. See above for strategy. def goToPage(self): tc = self.document().find(QString(u'['), self.textCursor(), QTextDocument.FindBackward) if not tc.isNull(): # found it, or one. tc now selects the [ re = QRegExp(QString(u'\[\d+\]')) tc.setPosition(tc.selectionStart()) # start looking left of [ tc = self.document().find(re,tc) if not tc.isNull(): # found that. self.setTextCursor(tc) # highlight the found string qs = tc.selectedText() # "[nnn]" qs.remove(0,1) # "nnn]" qs.chop(1) # "nnn" (pn,flg) = qs.toInt() # page number as int pn -= 1 # index to that page in the page table if (pn >= 0) and (pn < IMC.pageTable.size()) : etc = IMC.pageTable.getCursor(pn) # cursor for that page doc = IMC.editWidget.document() # main document IMC.editWidget.setTextCursor(etc) # make it visible IMC.editWidget.setFocus(Qt.TabFocusReason) else: # should not occur pqMsgs.beep() else: # no [ppp] seen pqMsgs.beep() else: # no [ preceding the cursor on same line pqMsgs.beep() # Do a simple find. getFindMsg returns (ok,find-text). This is a VERY # simple find from the present cursor position downward, case-insensitive. # If we get no hit we try once more from the top, thus in effect wrapping. def doFind(self,kkey): if (kkey == IMC.ctl_F) or (self.findText.isEmpty()) : # ctl+F, or ctl+G but no previous find done, show the find dialog # with a COPY of current selection as pqMsgs might truncate it prepText = QString(self.textCursor().selectedText()) (ok, self.findText) = pqMsgs.getFindMsg(self,prepText) # dialog or no dialog, we should have some findText now if not self.findText.isEmpty() : if not self.find(self.findText): # no hits going down self.moveCursor(QTextCursor.Start) # go to top if not self.find(self.findText): # still no hit pqMsgs.beep()
class MainForm(QDialog): def __init__(self, parent=None): super(MainForm, self).__init__(parent) self.filename = QString() self.scene = QGraphicsScene(self) self.view = GraphicsView(self.scene) self.view.setScene(self.scene) buttonLayout = QVBoxLayout() for text, slot in ( ("&Open", self.addPixmap), ("&Quit", self.accept)): button = QPushButton(text) self.connect(button, SIGNAL("clicked()"), slot) if text == "&Quit": buttonLayout.addStretch(1) buttonLayout.addWidget(button) buttonLayout.addStretch() layout = QHBoxLayout() layout.addWidget(self.view, 1) layout.addLayout(buttonLayout) self.setLayout(layout) self.setWindowTitle("Petal Picker") def addPixmap(self): path = (QFileInfo(self.filename).path() if not self.filename.isEmpty() else ".") fname = QFileDialog.getOpenFileName(self, "Add Pixmap", path, "Pixmap Files (*.bmp *.jpg *.png *.xpm)") if fname.isEmpty(): return self.createPixmapItem(QPixmap(fname)) def createPixmapItem(self, pixmap, matrix=QMatrix()): global PIX item = QGraphicsPixmapItem(pixmap) PIX = item # item.setFlags(QGraphicsItem.ItemIsSelectable| # QGraphicsItem.ItemIsMovable) item.setMatrix(matrix) self.scene.clearSelection() for i in self.scene.items(): self.scene.removeItem(i) self.scene.addItem(item) self.view.fitInView(item,Qt.KeepAspectRatio) global DIRTY DIRTY = True def accept(self): global DIRTY if not DIRTY: QDialog.accept(self) if DIRTY: if self.confirm(): QDialog.accept(self) else: return def confirm(self): global DIRTY if (DIRTY and QMessageBox.question(self, "Really Quit?", "Really Quit?", QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes): return True else: return False
class helpDisplay(QWebView): def __init__(self, parent=None): super(helpDisplay, self).__init__(parent) # make page unmodifiable self.page().setContentEditable(False) # initialize settings # Find out the nearest font to Palatino qf = QFont() qf.setStyleStrategy(QFont.PreferAntialias + QFont.PreferMatch) qf.setStyleHint(QFont.Serif) qf.setFamily(QString(u'Palatino')) qfi = QFontInfo(qf) # set the default font to that serif font self.settings().setFontFamily(QWebSettings.StandardFont, qfi.family()) self.settings().setFontSize(QWebSettings.DefaultFontSize, 16) self.settings().setFontSize(QWebSettings.MinimumFontSize, 6) self.settings().setFontSize(QWebSettings.MinimumLogicalFontSize, 6) self.textZoomFactor = 1.0 self.setTextSizeMultiplier(self.textZoomFactor) self.settings().setAttribute(QWebSettings.JavascriptEnabled, False) self.settings().setAttribute(QWebSettings.JavaEnabled, False) self.settings().setAttribute(QWebSettings.PluginsEnabled, False) self.settings().setAttribute(QWebSettings.ZoomTextOnly, True) #self.settings().setAttribute(QWebSettings.SiteSpecificQuirksEnabled, False) self.userFindText = QString() # Look for pqHelp.html in the app folder and copy its text into # a local buffer. If it isn't found, put a message there instead. # We need to keep it in order to implement the "back" function. helpPath = os.path.join(IMC.appBasePath, u'pqHelp.html') helpFile = QFile(helpPath) if not helpFile.exists(): self.HTMLstring = QString('''<p>Unable to locate pqHelp.html.</p> <p>Looking in {0}'''.format(helpPath)) elif not helpFile.open(QIODevice.ReadOnly): self.HTMLstring = QString('''<p>Unable to open pqHelp.html.</p> <p>Looking in {0}</p><p>Error code {1}</p>'''.format( helpPath, helpFile.error())) else: helpStream = QTextStream(helpFile) helpStream.setCodec('ISO8859-1') self.HTMLstring = helpStream.readAll() self.setHtml(self.HTMLstring) # Re-implement the parent's keyPressEvent in order to provide a simple # find function and font-zoom from ctl-plus/minus. We start the view at # 16 points and textSizeMultiplier of 1.0. Each time the user hits ctl-minus # we deduct 0.0625 from the multiplier, and for each ctl-+ we add 0.0625 # (1/16) to the multiplier. This ought to cause the view to change up or # down by one point. We set a limit of 0.375 (6 points) at the low # end and 4.0 (64 points) at the top. def keyPressEvent(self, event): #pqMsgs.printKeyEvent(event) kkey = int(int(event.modifiers()) & IMC.keypadDeModifier) | int( event.key()) if (kkey == IMC.ctl_F) or (kkey == IMC.ctl_G): # ctl/cmd f/g event.accept() self.doFind(kkey) elif (kkey in IMC.zoomKeys): # ctl-plus/minus zfactor = 0.0625 # zoom in if (kkey == IMC.ctl_minus): zfactor = -zfactor # zoom out zfactor += self.textZoomFactor if (zfactor > 0.374) and (zfactor < 4.0): self.textZoomFactor = zfactor self.setTextSizeMultiplier(self.textZoomFactor) elif (kkey in IMC.backKeys): # ctl-B/[/left if self.page().history().currentItemIndex() > 1: self.page().history().back() else: self.setHtml(self.HTMLstring) self.page().history().clear() else: # not ctl/cmd f or ctl/cmd-plus/minus, so, event.ignore() super(helpDisplay, self).keyPressEvent(event) # Implement a simple Find/Find-Next, same logic as in pqNotes, # but adjusted for our widget being a webview, and it does the # wraparound for us. def doFind(self, kkey): if (kkey == IMC.ctl_F) or (self.userFindText.isEmpty()): # ctl+F, or ctl+G but no previous find done, show the find dialog # with a COPY of current selection as pqMsgs might truncate it prepText = QString(self.page().selectedText()) (ok, self.userFindText) = pqMsgs.getFindMsg(self, prepText) # We should have some findText now, either left from previous find # on a ctl-G, or entered by user. If none, then user cleared dialog. if not self.userFindText.isEmpty(): if not self.page().findText(self.userFindText, QWebPage.FindWrapsAroundDocument): pqMsgs.beep()
class Lexer(object): """ Class to implement the lexer mixin class. """ def __init__(self): """ Constructor """ self.commentString = QString('') self.streamCommentString = { 'start' : QString(''), 'end' : QString('') } self.boxCommentString = { 'start' : QString(''), 'middle' : QString(''), 'end' : QString('') } # last indented line wrapper self.lastIndented = -1 self.lastIndentedIndex = -1 # always keep tabs (for languages where tabs are esential self._alwaysKeepTabs = False def initProperties(self): """ Public slot to initialize the properties. """ # default implementation is a do nothing return def commentStr(self): """ Public method to return the comment string. @return comment string (QString) """ return self.commentString def canBlockComment(self): """ Public method to determine, whether the lexer language supports a block comment. @return flag (boolean) """ return not self.commentString.isEmpty() def streamCommentStr(self): """ Public method to return the stream comment strings. @return stream comment strings (dictionary with two QStrings) """ return self.streamCommentString def canStreamComment(self): """ Public method to determine, whether the lexer language supports a stream comment. @return flag (boolean) """ return \ (not self.streamCommentString['start'].isEmpty()) and \ (not self.streamCommentString['end'].isEmpty()) def boxCommentStr(self): """ Public method to return the box comment strings. @return box comment strings (dictionary with three QStrings) """ return self.boxCommentString def canBoxComment(self): """ Public method to determine, whether the lexer language supports a box comment. @return flag (boolean) """ return \ (not self.boxCommentString['start'].isEmpty()) and \ (not self.boxCommentString['middle'].isEmpty()) and \ (not self.boxCommentString['end'].isEmpty()) def alwaysKeepTabs(self): """ Public method to check, if tab conversion is allowed. @return flag indicating to keep tabs (boolean) """ return self._alwaysKeepTabs def hasSmartIndent(self): """ Public method indicating whether lexer can do smart indentation. @return flag indicating availability of smartIndentLine and smartIndentSelection methods (boolean) """ return hasattr(self, 'getIndentationDifference') def smartIndentLine(self, editor): """ Public method to handle smart indentation for a line. @param editor reference to the QScintilla editor object """ cline, cindex = editor.getCursorPosition() # get leading spaces lead_spaces = editor.indentation(cline) # get the indentation difference indentDifference = self.getIndentationDifference(cline, editor) if indentDifference != 0: editor.setIndentation(cline, lead_spaces + indentDifference) editor.setCursorPosition(cline, cindex + indentDifference) self.lastIndented = cline def smartIndentSelection(self, editor): """ Public method to handle smart indentation for a selection of lines. Note: The assumption is, that the first line determines the new indentation level. @param editor reference to the QScintilla editor object """ if not editor.hasSelectedText(): return # get the selection lineFrom, indexFrom, lineTo, indexTo = editor.getSelection() if lineFrom != self.lastIndented: self.lastIndentedIndex = indexFrom if indexTo == 0: endLine = lineTo - 1 else: endLine = lineTo # get the indentation difference indentDifference = self.getIndentationDifference(lineFrom, editor) editor.beginUndoAction() # iterate over the lines for line in range(lineFrom, endLine+1): editor.setIndentation(line, editor.indentation(line) + indentDifference) editor.endUndoAction() if self.lastIndentedIndex != 0: indexStart = indexFrom + indentDifference else: indexStart = 0 if indexStart < 0: indexStart = 0 indexEnd = indexTo != 0 and (indexTo + indentDifference) or 0 if indexEnd < 0: indexEnd = 0 editor.setSelection(lineFrom, indexStart, lineTo, indexEnd) self.lastIndented = lineFrom def autoCompletionWordSeparators(self): """ Public method to return the list of separators for autocompletion. @return list of separators (QStringList) """ return QStringList() def isCommentStyle(self, style): """ Public method to check, if a style is a comment style. @return flag indicating a comment style (boolean) """ return True def isStringStyle(self, style): """ Public method to check, if a style is a string style. @return flag indicating a string style (boolean) """ return True def keywords(self, kwSet): """ Public method to get the keywords. @param kwSet number of the keyword set (integer) @return string giving the keywords (string) or None """ keywords_ = Preferences.getEditorKeywords(self.language()) if not keywords_.isEmpty(): kw = unicode(keywords_[kwSet]) if kw == "": return None else: return kw else: return self.defaultKeywords(kwSet)
def enableFindButton(self,text): print type(text),":",len(text) print(text) t = QString(text) self.findButton.setEnabled(not t.isEmpty())
class notesEditor(QPlainTextEdit): def __init__(self, parent=None, fontsize=12): super(notesEditor, self).__init__(parent) # Do not allow line-wrap; horizontal scrollbar appears when required. self.setLineWrapMode(QPlainTextEdit.NoWrap) # get same font as main editor uses self.setFont(pqMsgs.getMonoFont(12, False)) self.connect(self.document(), SIGNAL("modificationChanged(bool)"), self.eek) self.findText = QString() # called from pqEdit.clear() def clear(self): self.clearing = True self.document().clear() self.document().setModified(False) self.findText = QString() self.clearing = False # slot to receive the modificationChanged(bool) signal from our document. # if it indicates we are modified, set needMetadataSave true if it isn't # already. Should the user undo out of all changes, modval will be false # and we clear our flag, possibly making the flag 0. def eek(self, modval): if not self.clearing: if modval: IMC.needMetadataSave |= IMC.notePanelChanged else: IMC.needMetadataSave &= (0xff ^ IMC.notePanelChanged) IMC.mainWindow.setWinModStatus() # Re-implement the parent's keyPressEvent in order to provide zoom: # ctrl-plus and ctrl-minus step the font size one point. Other keys: # shift-ctl-L inserts the current line number as {nnn} # ctl-L looks for a nearby {nnn} (braces required), selects it, and # tells the main editor to jump to that line. # shift-ctl-P insert the current page (scan) number as [nnn] # ctl-P looks for a nearby [nnn] (brackets required), selects it, and # tells the main editor to jump to that page position. def keyPressEvent(self, event): #pqMsgs.printKeyEvent(event) kkey = int(int(event.modifiers()) & IMC.keypadDeModifier) | int( event.key()) if kkey in IMC.keysOfInterest: # tentatively assume we will process this key event.accept() if kkey in IMC.zoomKeys: n = (-1) if (kkey == IMC.ctl_minus) else 1 p = self.fontInfo().pointSize() + n if (p > 3) and (p < 65): # don't let's get ridiculous, hmm? f = self.font() # so get our font, f.setPointSize(p) # change its point size +/- self.setFont(f) # and put the font back elif (kkey == IMC.ctl_alt_M): # ctrl/cmd-m with shift self.insertLine() elif (kkey == IMC.ctl_M): # ctrl/cmd-m (no shift) self.goToLine() elif (kkey == IMC.ctl_alt_P): # ctl/cmd-p self.insertPage() elif (kkey == IMC.ctl_P): #ctl/cmd-P self.goToPage() elif (kkey == IMC.ctl_F) or (kkey == IMC.ctl_G): # ctl/cmd f/g self.doFind(kkey) else: # one of the keys we support but not in this panel event.ignore() # so clear the accepted flag else: # not one of our keys at all event.ignore() # ensure the accepted flag is off if not event.isAccepted(): # if we didn't handle it, pass it up super(notesEditor, self).keyPressEvent(event) # on ctl-alt-l (mac: opt-cmd-l), insert the current edit line number in # notes as {nnn} def insertLine(self): tc = self.textCursor() bn = IMC.editWidget.textCursor().blockNumber() # line num tc.insertText(u"{{{0}}}".format(bn)) # on ctl-l (mac: cmd-l) look for a {nnn} line number "near" our cursor in # the notes. Strategy: find-backwards for '{', the forward for regex (\d+)\} def goToLine(self): tc = self.document().find(QString(u'{'), self.textCursor(), QTextDocument.FindBackward) if not tc.isNull(): # found it, or one. tc now selects the { re = QRegExp(QString(u'\{\d+\}')) tc.setPosition(tc.selectionStart()) # start looking left of { tc = self.document().find(re, tc) if not tc.isNull(): # found that. self.setTextCursor(tc) # highlight the found string qs = tc.selectedText() # "{nnn}" qs.remove(0, 1) # "nnn}" qs.chop(1) # "nnn" # toInt returns a tuple, (int, flag) where flag is false # if the conversion fails. In this case it cannot fail # since we found \d+ in the first place. However, the # number might be invalid as a line number. (bn, flg) = qs.toInt() # line number as int doc = IMC.editWidget.document() # main document tb = doc.findBlockByNumber(bn) # text block number bn if tb.isValid(): # if it exists, tc = IMC.editWidget.textCursor() # cursor on main doc tc.setPosition(tb.position()) # set it on block IMC.editWidget.setTextCursor(tc) # make it visible IMC.editWidget.setFocus(Qt.TabFocusReason) else: # no {ddd} seen pqMsgs.beep() else: # no { preceding the cursor on same line pqMsgs.beep() # Insert current page number as [ppp]. Conveniently, pqPngs saves the # index of the current page in the page table whenever the cursor moves. # (BUG: if there is no pngs folder, that won't happen) def insertPage(self): tc = self.textCursor() if IMC.currentImageNumber is not None: tc.insertText("[{0}]".format(unicode(IMC.currentImageNumber))) # on ^p, look for [ppp] "near" our cursor in notes, and if found, tell # editor to go to that page text. See above for strategy. def goToPage(self): tc = self.document().find(QString(u'['), self.textCursor(), QTextDocument.FindBackward) if not tc.isNull(): # found it, or one. tc now selects the [ re = QRegExp(QString(u'\[\d+\]')) tc.setPosition(tc.selectionStart()) # start looking left of [ tc = self.document().find(re, tc) if not tc.isNull(): # found that. self.setTextCursor(tc) # highlight the found string qs = tc.selectedText() # "[nnn]" qs.remove(0, 1) # "nnn]" qs.chop(1) # "nnn" (pn, flg) = qs.toInt() # page number as int pn -= 1 # index to that page in the page table if (pn >= 0) and (pn < IMC.pageTable.size()): etc = IMC.pageTable.getCursor(pn) # cursor for that page doc = IMC.editWidget.document() # main document IMC.editWidget.setTextCursor(etc) # make it visible IMC.editWidget.setFocus(Qt.TabFocusReason) else: # should not occur pqMsgs.beep() else: # no [ppp] seen pqMsgs.beep() else: # no [ preceding the cursor on same line pqMsgs.beep() # Do a simple find. getFindMsg returns (ok,find-text). This is a VERY # simple find from the present cursor position downward, case-insensitive. # If we get no hit we try once more from the top, thus in effect wrapping. def doFind(self, kkey): if (kkey == IMC.ctl_F) or (self.findText.isEmpty()): # ctl+F, or ctl+G but no previous find done, show the find dialog # with a COPY of current selection as pqMsgs might truncate it prepText = QString(self.textCursor().selectedText()) (ok, self.findText) = pqMsgs.getFindMsg(self, prepText) # dialog or no dialog, we should have some findText now if not self.findText.isEmpty(): if not self.find(self.findText): # no hits going down self.moveCursor(QTextCursor.Start) # go to top if not self.find(self.findText): # still no hit pqMsgs.beep()