From 623fcea1c90fff6c7d0e9c52c47bc8ef63577259 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Thu, 9 Mar 2017 15:51:45 -0300 Subject: [PATCH] Web: Added sketchfab exporter --- src/Mod/Web/CMakeLists.txt | 8 + src/Mod/Web/Gui/Command.cpp | 30 +++ src/Mod/Web/Gui/Resources/Web.qrc | 3 + src/Mod/Web/Gui/Resources/icons/Sketchfab.svg | 133 ++++++++++ .../Resources/icons/actions/web-sketchfab.svg | 77 ++++++ .../Web/Gui/Resources/ui/TaskDlgSketchfab.ui | 228 ++++++++++++++++++ src/Mod/Web/Gui/Workbench.cpp | 4 +- src/Mod/Web/Webscripts/Sketchfab.py | 194 +++++++++++++++ src/Mod/Web/Webscripts/__init__.py | 0 9 files changed, 676 insertions(+), 1 deletion(-) create mode 100644 src/Mod/Web/Gui/Resources/icons/Sketchfab.svg create mode 100644 src/Mod/Web/Gui/Resources/icons/actions/web-sketchfab.svg create mode 100644 src/Mod/Web/Gui/Resources/ui/TaskDlgSketchfab.ui create mode 100644 src/Mod/Web/Webscripts/Sketchfab.py create mode 100644 src/Mod/Web/Webscripts/__init__.py diff --git a/src/Mod/Web/CMakeLists.txt b/src/Mod/Web/CMakeLists.txt index e0c11d989c..d9db505a29 100644 --- a/src/Mod/Web/CMakeLists.txt +++ b/src/Mod/Web/CMakeLists.txt @@ -13,3 +13,11 @@ INSTALL( DESTINATION Mod/Web ) + +INSTALL( + FILES + Webscripts/__init__.py + Webscripts/Sketchfab.py + DESTINATION + Mod/Web/Webscripts + ) diff --git a/src/Mod/Web/Gui/Command.cpp b/src/Mod/Web/Gui/Command.cpp index 06e0f9fd7d..b1a0af47ec 100644 --- a/src/Mod/Web/Gui/Command.cpp +++ b/src/Mod/Web/Gui/Command.cpp @@ -237,6 +237,35 @@ bool CmdWebBrowserZoomOut::isActive(void) return getGuiApplication()->sendHasMsgToActiveView("ZoomOut"); } +//=========================================================================== +// CmdWebSketchfab +//=========================================================================== + +DEF_STD_CMD_A(CmdWebSketchfab); + +CmdWebSketchfab::CmdWebSketchfab() + : Command("Web_Sketchfab") +{ + sAppModule = "Web"; + sGroup = QT_TR_NOOP("Web"); + sMenuText = QT_TR_NOOP("Sketchfab"); + sToolTipText = QT_TR_NOOP("Uploads a model to your sketchfab account"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "actions/web-sketchfab"; +} + +void CmdWebSketchfab::activated(int iMsg) +{ + Q_UNUSED(iMsg); + doCommand(Command::Gui,"from Webscripts import Sketchfab"); + doCommand(Command::Gui,"FreeCADGui.Control.showDialog(Sketchfab.SketchfabTaskPanel())"); +} + +bool CmdWebSketchfab::isActive(void) +{ + return hasActiveDocument(); +} void CreateWebCommands(void) { @@ -249,4 +278,5 @@ void CreateWebCommands(void) rcCmdMgr.addCommand(new CmdWebBrowserStop()); rcCmdMgr.addCommand(new CmdWebBrowserZoomIn()); rcCmdMgr.addCommand(new CmdWebBrowserZoomOut()); + rcCmdMgr.addCommand(new CmdWebSketchfab()); } diff --git a/src/Mod/Web/Gui/Resources/Web.qrc b/src/Mod/Web/Gui/Resources/Web.qrc index 184d90ec1f..cd6420d580 100644 --- a/src/Mod/Web/Gui/Resources/Web.qrc +++ b/src/Mod/Web/Gui/Resources/Web.qrc @@ -8,7 +8,10 @@ icons/actions/web-stop.svg icons/actions/web-zoom-in.svg icons/actions/web-zoom-out.svg + icons/actions/web-sketchfab.svg icons/WebWorkbench.svg + icons/Sketchfab.svg + ui/TaskDlgSketchfab.ui translations/Web_de.qm translations/Web_af.qm translations/Web_zh-CN.qm diff --git a/src/Mod/Web/Gui/Resources/icons/Sketchfab.svg b/src/Mod/Web/Gui/Resources/icons/Sketchfab.svg new file mode 100644 index 0000000000..52ec0b2508 --- /dev/null +++ b/src/Mod/Web/Gui/Resources/icons/Sketchfab.svg @@ -0,0 +1,133 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/Mod/Web/Gui/Resources/icons/actions/web-sketchfab.svg b/src/Mod/Web/Gui/Resources/icons/actions/web-sketchfab.svg new file mode 100644 index 0000000000..8049efa606 --- /dev/null +++ b/src/Mod/Web/Gui/Resources/icons/actions/web-sketchfab.svg @@ -0,0 +1,77 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/Mod/Web/Gui/Resources/ui/TaskDlgSketchfab.ui b/src/Mod/Web/Gui/Resources/ui/TaskDlgSketchfab.ui new file mode 100644 index 0000000000..5153bc1cc5 --- /dev/null +++ b/src/Mod/Web/Gui/Resources/ui/TaskDlgSketchfab.ui @@ -0,0 +1,228 @@ + + + Form + + + + 0 + 0 + 218 + 548 + + + + Sketchfab exporter + + + + + + + 200 + 43 + + + + + + + :/icons/Sketchfab.svg + + + true + + + + + + + What to upload? + + + + + + + Selection + + + true + + + + + + + All visible objects + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + Model name + + + + + + + Private + + + + + + + + + + + + Description + + + + + + + + + + Tags (separated by commas) + + + + + + + + + freecad, + + + + + + + + + + + + + + + + Format + + + + + + + + OBJ + MTL (Arch exporter) + + + + + OBJ (standard Mesh exporter) + + + + + DAE (Collada) + + + + + STL + + + + + IGES (shapes only) + + + + + IV (buggy) + + + + + + + + Sketchfab API token + + + + + + + + + QLineEdit::Password + + + + + + + Obtain + + + + + + + + + Upload + + + + + + + 25 + + + uploading + + + + + + + Success! View uploaded online + + + + + + + + + + diff --git a/src/Mod/Web/Gui/Workbench.cpp b/src/Mod/Web/Gui/Workbench.cpp index 163322b571..9526963c34 100644 --- a/src/Mod/Web/Gui/Workbench.cpp +++ b/src/Mod/Web/Gui/Workbench.cpp @@ -333,7 +333,9 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "Web_BrowserStop" << "Separator" << "Web_BrowserZoomIn" - << "Web_BrowserZoomOut"; + << "Web_BrowserZoomOut" + << "Separator" + << "Web_Sketchfab"; return root; diff --git a/src/Mod/Web/Webscripts/Sketchfab.py b/src/Mod/Web/Webscripts/Sketchfab.py new file mode 100644 index 0000000000..3662fb9057 --- /dev/null +++ b/src/Mod/Web/Webscripts/Sketchfab.py @@ -0,0 +1,194 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2017 - Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +from __future__ import print_function + +__title__ = "Sketchfab uploader" +__author__ = "Yorik van Havre" +__url__ = "http://www.freecadweb.org" + +import FreeCAD, FreeCADGui, WebGui, os, zipfile, requests, tempfile +from PySide import QtCore, QtGui + +# \cond +try: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, QtGui.QApplication.UnicodeUTF8) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) +# \endcond + +SKETCHFAB_UPLOAD_URL = "https://api.sketchfab.com/v1/models" +SKETCHFAB_TOKEN_URL = "https://sketchfab.com/settings/password" +SKETCHFAB_MODEL_URL = "https://sketchfab.com/show/" + + +class SketchfabTaskPanel: + + '''The TaskPanel for Sketchfab upload''' + + def __init__(self): + + self.url = None + self.form = FreeCADGui.PySideUic.loadUi(":/ui/TaskDlgSketchfab.ui") + self.form.ProgressBar.hide() + self.form.Button_View.hide() + QtCore.QObject.connect(self.form.Button_Token,QtCore.SIGNAL("pressed()"),self.getToken) + QtCore.QObject.connect(self.form.Button_Upload,QtCore.SIGNAL("pressed()"),self.upload) + QtCore.QObject.connect(self.form.Button_View,QtCore.SIGNAL("pressed()"),self.viewModel) + self.form.Text_Name.setText(FreeCAD.ActiveDocument.Label) + self.form.Text_Token.setText(FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Web").GetString("SketchfabToken","")) + + def isAllowedAlterSelection(self): + + return True + + def isAllowedAlterView(self): + + return True + + def getStandardButtons(self): + + return int(QtGui.QDialogButtonBox.Close) + + def accept(self): + + FreeCADGui.ActiveDocument.resetEdit() + return True + + def getToken(self): + + QtGui.QDesktopServices.openUrl(SKETCHFAB_TOKEN_URL) + + def saveFile(self): + + import FreeCADGui + if self.form.Radio_Selection.isChecked(): + objects = FreeCADGui.Selection.getSelection() + else: + objects = [obj for obj in FreeCAD.ActiveDocument.Objects if obj.ViewObject.isVisible()] + if not objects: + QtGui.QMessageBox.critical(None,translate("Web","Nothing to upload"),translate("The selection of the document contains no object to upload")) + return None + filename = os.path.join(tempfile._get_default_tempdir(),next(tempfile._get_candidate_names())) + filetype = self.form.Combo_Filetype.currentIndex() + # 0 = obj + mtl, 1 = obj, 2 = dae, 3 = stl, 4 = IGES, 5 = iv (currently not working) + if filetype == 0: # OBJ + MTL + import importOBJ + importOBJ.export(objects,filename+".obj") + return self.packFiles(filename,[filename+".obj",filename+".mtl"]) + elif filetype == 1: # OBJ (mesh exporter) + import Mesh + Mesh.export(objects,filename+".obj") + return self.packFiles(filename,[filename+".obj"]) + elif filetype == 2: # DAE + import importDAE + importDAE.export(objects,filename+".dae") + return self.packFiles(filename,[filename+".dae"]) + elif filetype == 3: # STL + import Mesh + Mesh.export(objects,filename+".stl") + return self.packFiles(filename,[filename+".stl"]) + elif filetype == 4: # IGES + import Part + Part.export(objects,filename+".iges") + return self.packFiles(filename,[filename+".iges"]) + elif filetype == 5: # STL + import FreeCADGui + FreeCADGui.export(objects,filename+".iv") + return self.packFiles(filename,[filename+".iv"]) + + def packFiles(self,filename,fileslist): + + originalname = os.path.basename(fileslist[0]) + for f in fileslist: + if not os.path.exists(f): + return None + z = zipfile.ZipFile(filename+".zip","w") + for f in fileslist: + z.write(f) + z.close() + for f in fileslist: + os.remove(f) + s = os.path.getsize(filename+".zip") + if s > 1048576: + size = str(s >> 20)+" MB" + else: + size = str(s >> 10)+" KB" + return (filename+".zip",originalname,size) + + def upload(self): + + if not self.form.Text_Name.text(): + QtGui.QMessageBox.critical(None,translate("Web","Model name is empty"),translate("You must provide a name for your model")) + return + if not self.form.Text_Token.text(): + QtGui.QMessageBox.critical(None,translate("Web","No token provided"),translate("The token is empty. Please press the Obtain button to get your user API token from Sketchfab, then copy / paste the API token to the field below")) + return + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Web").SetString("SketchfabToken",self.form.Text_Token.text()) + pack = self.saveFile() + if not pack: + QtGui.QMessageBox.critical(None,translate("Web","File packing error"),translate("Unable to save and zip a file for upload")) + return + data = { + "title": self.form.Text_Name.text(), + "description": self.form.Text_Description.text(), + "filename": pack[1], + "tags": "freecad,"+self.form.Text_Tags.text(), + "private": self.form.Check_Private.isChecked(), + "token": self.form.Text_Token.text(), + "source": "freecad", + } + files = { + "fileModel": open(pack[0], 'rb'), + } + self.form.Button_Upload.hide() + # for now this is a fake progress bar, it won't move, just to show the user that the upload is in progress + self.form.ProgressBar.setFormat(translate("Web","Uploading")+" "+pack[2]+"...") + self.form.ProgressBar.show() + try: + r = requests.post(SKETCHFAB_UPLOAD_URL, data=data, files=files, verify=False) + except requests.exceptions.RequestException as e: + QtGui.QMessageBox.critical(None,translate("Web","Upload error"),translate("Upload failed:")+" "+str(e)) + self.form.ProgressBar.hide() + self.form.Button_Upload.show() + return + result = r.json() + if r.status_code != requests.codes.ok: + QtGui.QMessageBox.critical(None,translate("Web","Upload error"),translate("Upload failed:")+" "+result["error"]) + self.form.ProgressBar.hide() + self.form.Button_Upload.show() + return + self.url = SKETCHFAB_MODEL_URL + result["result"]["id"] + self.form.ProgressBar.hide() + self.form.Button_View.show() + + def viewModel(self): + + if self.url: + QtGui.QDesktopServices.openUrl(self.url) + + + + + diff --git a/src/Mod/Web/Webscripts/__init__.py b/src/Mod/Web/Webscripts/__init__.py new file mode 100644 index 0000000000..e69de29bb2