Web: Added sketchfab exporter

This commit is contained in:
Yorik van Havre
2017-03-09 15:51:45 -03:00
parent 78b986a265
commit 623fcea1c9
9 changed files with 676 additions and 1 deletions

View File

@@ -13,3 +13,11 @@ INSTALL(
DESTINATION
Mod/Web
)
INSTALL(
FILES
Webscripts/__init__.py
Webscripts/Sketchfab.py
DESTINATION
Mod/Web/Webscripts
)

View File

@@ -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());
}

View File

@@ -8,7 +8,10 @@
<file>icons/actions/web-stop.svg</file>
<file>icons/actions/web-zoom-in.svg</file>
<file>icons/actions/web-zoom-out.svg</file>
<file>icons/actions/web-sketchfab.svg</file>
<file>icons/WebWorkbench.svg</file>
<file>icons/Sketchfab.svg</file>
<file>ui/TaskDlgSketchfab.ui</file>
<file>translations/Web_de.qm</file>
<file>translations/Web_af.qm</file>
<file>translations/Web_zh-CN.qm</file>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64"
height="64"
viewBox="0 0 16.933329 16.933333"
version="1.1"
id="svg4526"
inkscape:version="0.92.1 r15371"
sodipodi:docname="web-sketchfab.svg">
<defs
id="defs4520" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="62.910611"
inkscape:cy="2.0041616"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
width="49px"
inkscape:window-width="1360"
inkscape:window-height="739"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata4523">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-280.06667)">
<g
id="g56"
transform="matrix(0.03906056,0,0,-0.03906056,8.4531576,281.04755)">
<path
d="m 0,0 c -106.512,0 -192.826,-86.347 -192.826,-192.843 0,-106.496 86.314,-192.843 192.826,-192.843 106.512,0 192.843,86.347 192.843,192.843 C 192.843,-86.347 106.512,0 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path58"
inkscape:connector-curvature="0" />
</g>
<g
id="g60"
transform="matrix(0.03906056,0,0,-0.03906056,12.491566,291.36405)">
<path
d="M 0,0 -88.744,-51.24 V 55.405 L 0,103.355 Z m -105.471,80.231 -105.388,55.868 105.388,60.86 105.405,-60.86 z m -15.868,-131.454 -89.074,51.421 v 103.736 l 89.074,-48.116 z m 17.951,329.587 C -217.504,278.364 -310,185.851 -310,71.736 c 0,-114.116 92.496,-206.629 206.612,-206.629 114.115,0 206.611,92.513 206.611,206.629 0.017,114.115 -92.479,206.628 -206.611,206.628"
style="fill:#1caad9;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path62"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>218</width>
<height>548</height>
</rect>
</property>
<property name="windowTitle">
<string>Sketchfab exporter</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="maximumSize">
<size>
<width>200</width>
<height>43</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../Web.qrc">:/icons/Sketchfab.svg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>What to upload?</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="Radio_Selection">
<property name="text">
<string>Selection</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="Radio_Document">
<property name="text">
<string>All visible objects</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Model name</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignRight">
<widget class="QCheckBox" name="Check_Private">
<property name="text">
<string>Private</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLineEdit" name="Text_Name"/>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="Text_Description"/>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Tags (separated by commas)</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>freecad,</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="Text_Tags">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Format</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="Combo_Filetype">
<item>
<property name="text">
<string>OBJ + MTL (Arch exporter)</string>
</property>
</item>
<item>
<property name="text">
<string>OBJ (standard Mesh exporter)</string>
</property>
</item>
<item>
<property name="text">
<string>DAE (Collada)</string>
</property>
</item>
<item>
<property name="text">
<string>STL</string>
</property>
</item>
<item>
<property name="text">
<string>IGES (shapes only)</string>
</property>
</item>
<item>
<property name="text">
<string>IV (buggy)</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Sketchfab API token</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="Text_Token">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="Button_Token">
<property name="text">
<string>Obtain</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="Button_Upload">
<property name="text">
<string>Upload</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="ProgressBar">
<property name="value">
<number>25</number>
</property>
<property name="format">
<string>uploading</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="Button_View">
<property name="text">
<string>Success! View uploaded online</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../Web.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -333,7 +333,9 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "Web_BrowserStop"
<< "Separator"
<< "Web_BrowserZoomIn"
<< "Web_BrowserZoomOut";
<< "Web_BrowserZoomOut"
<< "Separator"
<< "Web_Sketchfab";
return root;

View File

@@ -0,0 +1,194 @@
#***************************************************************************
#* *
#* Copyright (c) 2017 - Yorik van Havre <yorik@uncreated.net> *
#* *
#* 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)

View File