https://github.com/minorua/Qgis2threejs
Revision 0058a0738f45d58f694f3f5f2d97c75d843c9f32 authored by Minoru Akagi on 09 April 2018, 04:37:43 UTC, committed by Minoru Akagi on 09 April 2018, 04:37:43 UTC
1 parent 046139a
Raw File
Tip revision: 0058a0738f45d58f694f3f5f2d97c75d843c9f32 authored by Minoru Akagi on 09 April 2018, 04:37:43 UTC
JS: move app.addEventListeners() into app.init()
Tip revision: 0058a07
q3dview.py
# -*- coding: utf-8 -*-
"""
/***************************************************************************
 Q3DView

                              -------------------
        begin                : 2016-02-10
        copyright            : (C) 2016 Minoru Akagi
        email                : akaginch@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""
from datetime import datetime
import os

#from PyQt5.Qt import *
from PyQt5.QtCore import Qt, QByteArray, QBuffer, QDir, QIODevice, QObject, QUrl, pyqtSignal, pyqtSlot, qDebug
from PyQt5.QtGui import QImage, QPainter, QPalette
from PyQt5.QtWebKitWidgets import QWebPage, QWebView
from PyQt5.QtWidgets import QFileDialog, QMessageBox

from .conf import debug_mode
from .qgis2threejstools import logMessage, pluginDir

def base64image(image):
  ba = QByteArray()
  buffer = QBuffer(ba)
  buffer.open(QIODevice.WriteOnly)
  image.save(buffer, "PNG")
  return "data:image/png;base64," + ba.toBase64().data().decode("ascii")


class Bridge(QObject):

  # Python to JS
  sendData = pyqtSignal("QVariant")

  # Python to Python
  modelDataReceived = pyqtSignal("QByteArray", str)
  imageReceived = pyqtSignal(int, int, "QImage")

  def __init__(self, parent=None):
    QObject.__init__(self, parent)
    self._parent = parent

  @pyqtSlot(int, int, result=str)
  def mouseUpMessage(self, x, y):
    return "Clicked at ({0}, {1})".format(x, y)
    # JS side: console.log(pyObj.mouseUpMessage(e.clientX, e.clientY));

  @pyqtSlot("QByteArray", str)
  def saveBytes(self, data, filename):
    self.modelDataReceived.emit(data, filename)

  @pyqtSlot(str, str)
  def saveString(self, text, filename):
    self.modelDataReceived.emit(text.encode("UTF-8"), filename)

  @pyqtSlot(int, int, str)
  def saveImage(self, width, height, dataUrl):
    image = None
    if dataUrl:
      ba = QByteArray.fromBase64(dataUrl[22:].encode("ascii"))
      image = QImage()
      image.loadFromData(ba)
    self.imageReceived.emit(width, height, image)


class Q3DWebPage(QWebPage):

  consoleMessage = pyqtSignal(str, int, str)

  def __init__(self, parent=None):
    QWebPage.__init__(self, parent)

    if debug_mode == 2:
      # open log file
      self.logfile = open(pluginDir("q3dview.log"), "w")

  def javaScriptConsoleMessage(self, message, lineNumber, sourceID):
    self.consoleMessage.emit(message, lineNumber, sourceID)

    if debug_mode == 2:
      now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
      self.logfile.write("{} {} ({}: {})\n".format(now, message, sourceID, lineNumber))
      self.logfile.flush()


class Q3DView(QWebView):

  def __init__(self, parent=None):
    QWebView.__init__(self, parent)

    self.requestQueue = []
    self.isProcessingExclusively = False

  def setup(self, wnd, iface, isViewer=True, enabled=True):
    self.wnd = wnd
    self.iface = iface
    self.isViewer = isViewer
    self._enabled = enabled
    self.bridge = Bridge(self)
    self.bridge.modelDataReceived.connect(self.saveModelData)
    self.bridge.imageReceived.connect(self.saveImage)

    self.loadFinished.connect(self.pageLoaded)

    self._page = Q3DWebPage(self)
    self._page.consoleMessage.connect(wnd.printConsoleMessage)
    self._page.mainFrame().javaScriptWindowObjectCleared.connect(self.addJSObject)
    self.setPage(self._page)

    if not isViewer:
      # transparent background
      palette = self._page.palette()
      palette.setBrush(QPalette.Base, Qt.transparent)
      self._page.setPalette(palette)
      self.setAttribute(Qt.WA_OpaquePaintEvent, False)

    self.renderId = None    # for renderer

    filetitle = "viewer" if isViewer else "layer"
    url = os.path.join(os.path.abspath(os.path.dirname(__file__)), "viewer", filetitle + ".html").replace("\\", "/")
    self.setUrl(QUrl.fromLocalFile(url))

  def reloadPage(self):
    self.wnd.clearConsole()
    self.setUrl(self.url())
    #self.reload()

  def addJSObject(self):
    self._page.mainFrame().addToJavaScriptWindowObject("pyObj", self.bridge)
    if debug_mode:
      self.wnd.printConsoleMessage("pyObj added", sourceID="q3dview.py")

  def pageLoaded(self, ok):
    self.runString("pyObj.sendData.connect(this, dataReceived);")

    if self.iface.controller.settings.isOrthoCamera:
      self.runString("switchCamera(true);")

    # start application - enable controls
    self.iface.startApplication()

    if self._enabled:
      # create scene and layers
      self.iface.exportScene()

      for layer in self.iface.controller.settings.getLayerList():
        if layer.visible:
          self.iface.exportLayer(layer)
    else:
      self.iface.setPreviewEnabled(False)

  def showStatusMessage(self, msg):
    self.wnd.ui.statusbar.showMessage(msg)

  def reload(self):
    pass

  def resetCameraPosition(self):
    self.runString("app.controls.reset();")

  def runString(self, string):
    if debug_mode:
      self.wnd.printConsoleMessage(string, sourceID="runString")
      qDebug("runString: {}\n".format(string))

      if debug_mode == 2:
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self._page.logfile.write("{} runString: {}\n".format(now, string))
        self._page.logfile.flush()

    return self._page.mainFrame().evaluateJavaScript(string)

  def runJavaScriptFile(self, filename):
    with open(filename, "r") as f:
      text = f.read()
    return self._page.mainFrame().evaluateJavaScript(text)

  def saveModelData(self, data, filename):
    try:
      with open(filename, "wb") as f:
        f.write(data)

    except Exception as e:
      QMessageBox.warning(self, "Failed to save model data.", str(e))
      return

    logMessage("Successfully saved model data: " + filename)

  def saveImage(self, width, height, image):
    if image is None:
      image = QImage(width, height, QImage.Format_ARGB32_Premultiplied)
      painter = QPainter(image)
      self._page.mainFrame().render(painter)
      painter.end()

    filename, _ = QFileDialog.getSaveFileName(self, self.tr("Save As"), QDir.homePath(), "PNG files (*.png)")
    if filename:
      image.save(filename)
back to top