| 9 | | from PyQt4.QtGui import QApplication, QWidget, QColor, QPainter, QMenuBar, QMenu, QKeySequence, QTextEdit, QSplitter, QGridLayout, QDialog, QScrollArea, QFont, QTextCharFormat, QFileDialog, QTextCursor, QPixmap, QPrinter, QImage, QMainWindow |
| 10 | | from PyQt4.QtCore import Qt, SIGNAL, SLOT, pyqtSignature, QTimer, QRectF, QSize, QCoreApplication, QSettings, QVariant |
| | 9 | from PyQt4.QtGui import * |
| | 10 | from PyQt4.QtCore import * |
| 12 | | |
| 13 | | from nodebox.gui.qt.editor import PythonHighlighter, CodeEdit, loadConfig |
| 14 | | |
| 15 | | MAGICVAR = "__magic_var__" |
| | 12 | from PyQt4.QtOpenGL import QGLWidget, QGLFormat, QGL |
| | 13 | |
| | 14 | from nodebox.gui.qt.ValueLadder import MAGICVAR |
| | 15 | from nodebox.gui.qt.pytextview import PythonHighlighter, PyTextView, loadConfig, Config |
| | 16 | from nodebox.gui.qt.dashboard import DashboardController |
| 37 | | QWidget.__init__(self, parent) |
| 38 | | self.setMinimumSize(300, 300) |
| | 37 | QGraphicsScene.__init__(self, parent) |
| | 38 | self.num = 0 |
| | 39 | |
| | 40 | def addItem(self, item): |
| | 41 | item.setZValue(self.num) |
| | 42 | QGraphicsScene.addItem(self, item) |
| | 43 | self.num += 1 |
| | 44 | |
| | 45 | def removeItem(self, item): |
| | 46 | QGraphicsScene.removeItem(self, item) |
| | 47 | self.num -= 1 |
| | 48 | |
| | 49 | def clear(self): |
| | 50 | try: |
| | 51 | QGraphicsScene.clear(self) |
| | 52 | except AttributeError: |
| | 53 | for item in self.items(): |
| | 54 | QGraphicsScene.removeItem(self, item) |
| | 55 | self.num = 0 |
| | 56 | |
| | 57 | def drawItems(self, painter, items, options, widget): |
| | 58 | painter.setClipRect(self.sceneRect()) |
| | 59 | QGraphicsScene.drawItems(self, painter, items, options, widget) |
| | 60 | |
| | 61 | class NodeBoxGraphicsView(QGraphicsView): |
| | 62 | zoomLevels = [0.1, 0.25, 0.5, 0.75] |
| | 63 | zoom = 1.0 |
| | 64 | while zoom <= 20.0: |
| | 65 | zoomLevels.append(zoom) |
| | 66 | zoom += 1.0 |
| | 67 | |
| | 68 | def __init__(self, parent=None): |
| | 69 | QGraphicsView.__init__(self) |
| | 70 | self.setAlignment(Qt.AlignLeft | Qt.AlignTop) |
| | 71 | self._scene = GraphicsScene(self) |
| | 72 | self.setScene(self._scene) |
| | 73 | self._scene.setSceneRect(0,0,1000,1000) |
| | 74 | self._scene.setItemIndexMethod(QGraphicsScene.NoIndex) |
| 42 | | |
| | 78 | self._zoom = 1.0 |
| | 79 | self._dx = 0 |
| | 80 | self._dy = 0 |
| | 81 | self._mouseDown = False |
| | 82 | |
| | 83 | def scrollContentsBy(self, dx, dy): |
| | 84 | self._dx -= dx |
| | 85 | self._dy -= dy |
| | 86 | QGraphicsView.scrollContentsBy(self, dx, dy) |
| | 87 | |
| | 88 | def mousePressEvent(self, event): |
| | 89 | if event.button() == Qt.LeftButton: |
| | 90 | self._mouseDown = True |
| | 91 | |
| | 92 | def mouseReleaseEvent(self, event): |
| | 93 | if event.button() == Qt.LeftButton: |
| | 94 | self._mouseDown = False |
| | 95 | |
| | 96 | def mousePosition(self): |
| | 97 | pos = self.mapFromGlobal(QCursor.pos()) |
| | 98 | return QPoint(pos.x() + self._dx, pos.y() + self._dy) |
| | 99 | |
| 52 | | |
| | 106 | |
| | 107 | def _get_zoom(self): |
| | 108 | return self._zoom |
| | 109 | def _set_zoom(self, zoom): |
| | 110 | self._zoom = zoom |
| | 111 | self.document.zoomLevel.setText("%i%%" % (self._zoom * 100.0)) |
| | 112 | self.document.zoomSlider.setValue(self._zoom * 100.0) |
| | 113 | transform = QTransform() |
| | 114 | transform.scale(self.zoom, self.zoom) |
| | 115 | self.setTransform( transform) |
| | 116 | zoom = property(_get_zoom, _set_zoom) |
| | 117 | |
| | 118 | def findNearestZoomIndex(self, zoom): |
| | 119 | """Returns the nearest zoom level, and whether we found a direct, exact |
| | 120 | match or a fuzzy match.""" |
| | 121 | try: # Search for a direct hit first. |
| | 122 | idx = self.zoomLevels.index(zoom) |
| | 123 | return idx, True |
| | 124 | except ValueError: # Can't find the zoom level, try looking at the indexes. |
| | 125 | idx = 0 |
| | 126 | try: |
| | 127 | while self.zoomLevels[idx] < zoom: |
| | 128 | idx += 1 |
| | 129 | except KeyError: # End of the list |
| | 130 | idx = len(self.zoomLevels) - 1 # Just return the last index. |
| | 131 | return idx, False |
| | 132 | |
| | 133 | def zoomIn_(self): |
| | 134 | idx, direct = self.findNearestZoomIndex(self.zoom) |
| | 135 | # Direct hits are perfect, but indirect hits require a bit of help. |
| | 136 | # Because of the way indirect hits are calculated, they are already |
| | 137 | # rounded up to the upper zoom level; this means we don't need to add 1. |
| | 138 | if direct: |
| | 139 | idx += 1 |
| | 140 | idx = max(min(idx, len(self.zoomLevels)-1), 0) |
| | 141 | self.zoom = self.zoomLevels[idx] |
| | 142 | |
| | 143 | def zoomOut_(self): |
| | 144 | idx, direct = self.findNearestZoomIndex(self.zoom) |
| | 145 | idx -= 1 |
| | 146 | idx = max(min(idx, len(self.zoomLevels)-1), 0) |
| | 147 | self.zoom = self.zoomLevels[idx] |
| | 148 | |
| | 149 | def zoomTo_(self, value): |
| | 150 | self.zoom = value |
| | 151 | |
| | 152 | def zoomToFit_(self): |
| | 153 | w, h = self.canvas.size |
| | 154 | viewport = self.viewport() |
| | 155 | fw = viewport.width() |
| | 156 | fh = viewport.height() |
| | 157 | factor = min(fw / float(w), fh / float(h)) |
| | 158 | self.zoom = factor |
| | 159 | |
| | 160 | def dragZoom_(self, value): |
| | 161 | self.zoom = value / 100.0 |
| | 162 | |
| | 235 | |
| | 236 | self.viewMenu = QMenu(self.tr("&View"), self) |
| | 237 | self.viewMenu.addAction(self.tr("Zoom &In"), self, SLOT("zoomIn_()"), QKeySequence("Ctrl++")) |
| | 238 | self.viewMenu.addAction(self.tr("Zoom &Out"), self, SLOT("zoomOut_()"), QKeySequence("Ctrl+-")) |
| | 239 | self.zoomToMenu = QMenu(self.tr("Zoom to"), self) |
| | 240 | self.zoomToMenu.addAction(self.tr("To &Fit"), self, SLOT("zoomToFit_()"), QKeySequence("Ctrl+0")) |
| | 241 | self.zoomToMenu.addAction(self.tr("Actual &Size"), self, SLOT("zoomTo100_()"), QKeySequence("Ctrl+1")) |
| | 242 | self.zoomToMenu.addAction(self.tr("200%"), self, SLOT("zoomTo200_()"), QKeySequence("Ctrl+2")) |
| | 243 | self.zoomToMenu.addAction(self.tr("300%"), self, SLOT("zoomTo300_()"), QKeySequence("Ctrl+3")) |
| | 244 | self.zoomToMenu.addAction(self.tr("400%"), self, SLOT("zoomTo400_()"), QKeySequence("Ctrl+4")) |
| | 245 | self.zoomToMenu.addAction(self.tr("50%"), self, SLOT("zoomTo50_()"), QKeySequence("Ctrl+5")) |
| | 246 | self.viewMenu.addMenu(self.zoomToMenu) |
| | 247 | self.menuBar().addMenu(self.viewMenu) |
| 168 | | self.graphicsView.resize(1000, 1000) |
| 169 | | self.graphicsScroll = QScrollArea() |
| 170 | | self.graphicsScroll.setWidget(self.graphicsView) |
| 171 | | self.graphicsScroll.setMinimumSize(300, 300) |
| 172 | | self.codeView = CodeEdit() |
| | 280 | self.graphicsView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) |
| | 281 | self.graphicsView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) |
| | 282 | if app._startgl: |
| | 283 | self.graphicsView.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers | QGL.AlphaChannel))) |
| | 284 | self.graphicsView.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) |
| | 285 | self._oldViewport = "qwidget" |
| | 286 | else: |
| | 287 | self.graphicsView.setViewport(QWidget()) |
| | 288 | self.graphicsView.setViewportUpdateMode(QGraphicsView.SmartViewportUpdate) |
| | 289 | self._oldViewport = "qglwidget" |
| | 290 | self.graphicsView.setOptimizationFlags(QGraphicsView.DontClipPainter | QGraphicsView.DontSavePainterState) |
| | 291 | |
| | 292 | self.codeView = PyTextView() |
| | 293 | self.codeView._document = self |
| | 315 | self.zoomLevel = QLabel("100%") |
| | 316 | ft = self.zoomLevel.font() |
| | 317 | ft.setPointSizeF(10) |
| | 318 | self.zoomLevel.setFont(ft) |
| | 319 | self.zoomSlider = QSlider(Qt.Horizontal) |
| | 320 | self.zoomSlider.setMinimumWidth(130) |
| | 321 | self.zoomSlider.setMinimum(1) |
| | 322 | self.zoomSlider.setMaximum(1000) |
| | 323 | self.zoomSlider.setValue(100) |
| | 324 | self.zoom_small = QPushButton() |
| | 325 | self.zoom_small.setIcon(QIcon(os.path.join(app.dir_gui, "zoomsmall.png"))) |
| | 326 | self.zoom_small.setStyleSheet("border: 0;") |
| | 327 | self.zoom_big = QPushButton() |
| | 328 | self.zoom_big.setIcon(QIcon(os.path.join(app.dir_gui, "zoombig.png"))) |
| | 329 | self.zoom_big.setStyleSheet("border: 0;") |
| | 330 | self.connect(self.zoom_small, SIGNAL("clicked()"), self, SLOT("zoomOut_()")) |
| | 331 | self.connect(self.zoom_big, SIGNAL("clicked()"), self, SLOT("zoomIn_()")) |
| | 332 | self.connect(self.zoomSlider, SIGNAL("valueChanged(int)"), self, SLOT("dragZoom_(int)")) |
| | 333 | |
| | 334 | lh = QHBoxLayout() |
| | 335 | lh.addWidget(self.zoomLevel) |
| | 336 | lh.addWidget(self.zoom_small) |
| | 337 | lh.addWidget(self.zoomSlider) |
| | 338 | lh.addWidget(self.zoom_big) |
| | 339 | lh.setContentsMargins(0,0,0,0) |
| | 340 | lh.setSpacing(8) |
| | 341 | |
| | 342 | self.zoom = QWidget(self) |
| | 343 | self.zoom.setLayout(lh) |
| | 344 | |
| | 345 | lv = QVBoxLayout() |
| | 346 | lv.addWidget(self.graphicsView) |
| | 347 | lv.addWidget(self.zoom, 0, Qt.AlignRight) |
| | 348 | lv.setContentsMargins(0, 0, 0, 0) |
| | 349 | |
| | 350 | self.graphics_zoom = QWidget(self) |
| | 351 | self.graphics_zoom.setLayout(lv) |
| | 352 | |
| 211 | | |
| | 369 | |
| | 370 | @pyqtSignature("") |
| | 371 | def switchViewport(self): |
| | 372 | self.outputView.setTextColor(QColor(0,0,0)) |
| | 373 | if self._oldViewport == "qwidget": |
| | 374 | self.outputView.insertPlainText("switch to normal view\n") |
| | 375 | self.graphicsView.setViewport(QWidget()) |
| | 376 | self.graphicsView.setViewportUpdateMode(QGraphicsView.SmartViewportUpdate) |
| | 377 | self._oldViewport = "qglwidget" |
| | 378 | else: |
| | 379 | self.outputView.insertPlainText("switch to OpenGL view\n") |
| | 380 | self.graphicsView.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers | QGL.AlphaChannel))) |
| | 381 | self.graphicsView.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) |
| | 382 | self._oldViewport = "qwidget" |
| | 383 | |
| | 384 | @pyqtSignature("") |
| | 385 | def zoomIn_(self): |
| | 386 | if self.fullScreen is not None: return |
| | 387 | self.graphicsView.zoomIn_() |
| | 388 | |
| | 389 | @pyqtSignature("") |
| | 390 | def zoomOut_(self): |
| | 391 | if self.fullScreen is not None: return |
| | 392 | self.graphicsView.zoomOut_() |
| | 393 | |
| | 394 | @pyqtSignature("int") |
| | 395 | def dragZoom_(self, value): |
| | 396 | if self.fullScreen is not None: return |
| | 397 | self.graphicsView.dragZoom_(value) |
| | 398 | |
| | 399 | @pyqtSignature("") |
| | 400 | def zoomTo100_(self): |
| | 401 | if self.fullScreen is not None: return |
| | 402 | self.graphicsView.zoomTo_(1.0) |
| | 403 | |
| | 404 | @pyqtSignature("") |
| | 405 | def zoomTo200_(self): |
| | 406 | if self.fullScreen is not None: return |
| | 407 | self.graphicsView.zoomTo_(2.0) |
| | 408 | |
| | 409 | @pyqtSignature("") |
| | 410 | def zoomTo300_(self): |
| | 411 | if self.fullScreen is not None: return |
| | 412 | self.graphicsView.zoomTo_(3.0) |
| | 413 | |
| | 414 | @pyqtSignature("") |
| | 415 | def zoomTo400_(self): |
| | 416 | if self.fullScreen is not None: return |
| | 417 | self.graphicsView.zoomTo_(4.0) |
| | 418 | |
| | 419 | @pyqtSignature("") |
| | 420 | def zoomTo50_(self): |
| | 421 | if self.fullScreen is not None: return |
| | 422 | self.graphicsView.zoomTo_(0.5) |
| | 423 | |
| | 424 | @pyqtSignature("") |
| | 425 | def zoomToFit_(self): |
| | 426 | if self.fullScreen is not None: return |
| | 427 | self.graphicsView.zoomToFit_() |
| | 428 | |
| 408 | | # TODO: Get correct mouse position |
| 409 | | #window = self.currentView.window() |
| 410 | | pt = 0, 0 # window.mouseLocationOutsideOfEventStream() |
| 411 | | mx, my = 0, 0 # window.contentView().convertPoint_toView_(pt, self.currentView) |
| 412 | | # Hack: mouse coordinates are flipped vertically in FullscreenView. |
| 413 | | # This flips them back. |
| 414 | | if isinstance(self.currentView, FullscreenView): |
| 415 | | my = self.currentView.bounds()[1][1] - my |
| | 638 | pos = self.currentView.mousePosition() |
| | 639 | mx, my = pos.x(), pos.y() |
| | 640 | |
| | 641 | # if isinstance(self.currentView, FullscreenView): |
| | 642 | # my = self.currentView.bounds()[1][1] - my |
| | 643 | |
| | 644 | if self.fullScreen is None: |
| | 645 | mx /= self.currentView.zoom |
| | 646 | my /= self.currentView.zoom |
| | 796 | |
| | 797 | def overrideConfig(app): |
| | 798 | settings = app.settings |
| | 799 | Config["fontfamily"] = settings.value("fontfamily", |
| | 800 | QVariant("Monaco")).toString() |
| | 801 | Config["fontsize"] = settings.value("fontsize", |
| | 802 | QVariant(11)).toInt()[0] |
| | 803 | for name, color, bold, italic in ( |
| | 804 | ("normal", "#000000", False, False), |
| | 805 | ("keyword", "#0000FF", False, False), |
| | 806 | ("builtin", "#000000", False, False), |
| | 807 | ("constant", "#0000FF", False, False), |
| | 808 | ("decorator", "#000000", False, False), |
| | 809 | ("comment", "#808080", False, False), |
| | 810 | ("string", "#FF00FF", False, False), |
| | 811 | ("number", "#000000", False, False), |
| | 812 | ("error", "#FF0000", False, False), |
| | 813 | ("pyqt", "#000000", False, False)): |
| | 814 | Config["%sfontcolor" % name] = settings.value( |
| | 815 | "%sfontcolor" % name, QVariant(color)).toString() |
| | 816 | Config["%sfontbold" % name] = settings.value( |
| | 817 | "%sfontbold" % name, QVariant(bold)).toBool() |
| | 818 | Config["%sfontitalic" % name] = settings.value( |
| | 819 | "%sfontitalic" % name, QVariant(italic)).toBool() |