CuraApplication.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. # Copyright (c) 2015 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3. import platform
  4. from UM.Qt.QtApplication import QtApplication
  5. from UM.Scene.SceneNode import SceneNode
  6. from UM.Scene.Camera import Camera
  7. from UM.Scene.Platform import Platform
  8. from UM.Math.Vector import Vector
  9. from UM.Math.Matrix import Matrix
  10. from UM.Math.Quaternion import Quaternion
  11. from UM.Math.AxisAlignedBox import AxisAlignedBox
  12. from UM.Resources import Resources
  13. from UM.Scene.ToolHandle import ToolHandle
  14. from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
  15. from UM.Mesh.WriteMeshJob import WriteMeshJob
  16. from UM.Mesh.ReadMeshJob import ReadMeshJob
  17. from UM.Logger import Logger
  18. from UM.Preferences import Preferences
  19. from UM.Message import Message
  20. from UM.PluginRegistry import PluginRegistry
  21. from UM.JobQueue import JobQueue
  22. from UM.Math.Polygon import Polygon
  23. from UM.Scene.BoxRenderer import BoxRenderer
  24. from UM.Scene.Selection import Selection
  25. from UM.Scene.GroupDecorator import GroupDecorator
  26. from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
  27. from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
  28. from UM.Operations.GroupedOperation import GroupedOperation
  29. from UM.Operations.SetTransformOperation import SetTransformOperation
  30. from UM.i18n import i18nCatalog
  31. from . import PlatformPhysics
  32. from . import BuildVolume
  33. from . import CameraAnimation
  34. from . import PrintInformation
  35. from . import CuraActions
  36. from . import MultiMaterialDecorator
  37. from . import ZOffsetDecorator
  38. from . import CuraSplashScreen
  39. from PyQt5.QtCore import pyqtSlot, QUrl, Qt, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
  40. from PyQt5.QtGui import QColor, QIcon
  41. from PyQt5.QtQml import qmlRegisterUncreatableType
  42. import platform
  43. import sys
  44. import os
  45. import os.path
  46. import numpy
  47. import copy
  48. numpy.seterr(all="ignore")
  49. if platform.system() == "Linux": # Needed for platform.linux_distribution, which is not available on Windows and OSX
  50. # For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
  51. if platform.linux_distribution()[0] in ("Ubuntu", ): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
  52. import ctypes
  53. from ctypes.util import find_library
  54. ctypes.CDLL(find_library('GL'), ctypes.RTLD_GLOBAL)
  55. try:
  56. from cura.CuraVersion import CuraVersion
  57. except ImportError:
  58. CuraVersion = "master" # [CodeStyle: Reflecting imported value]
  59. class CuraApplication(QtApplication):
  60. class ResourceTypes:
  61. QmlFiles = Resources.UserType + 1
  62. Firmware = Resources.UserType + 2
  63. Q_ENUMS(ResourceTypes)
  64. def __init__(self):
  65. Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura"))
  66. if not hasattr(sys, "frozen"):
  67. Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."))
  68. self._open_file_queue = [] #Files to open when plug-ins are loaded.
  69. super().__init__(name = "cura", version = CuraVersion)
  70. self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
  71. self.setRequiredPlugins([
  72. "CuraEngineBackend",
  73. "MeshView",
  74. "LayerView",
  75. "STLReader",
  76. "SelectionTool",
  77. "CameraTool",
  78. "GCodeWriter",
  79. "LocalFileOutputDevice"
  80. ])
  81. self._physics = None
  82. self._volume = None
  83. self._platform = None
  84. self._output_devices = {}
  85. self._print_information = None
  86. self._i18n_catalog = None
  87. self._previous_active_tool = None
  88. self._platform_activity = False
  89. self._scene_boundingbox = AxisAlignedBox()
  90. self._job_name = None
  91. self._center_after_select = False
  92. self.getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineChanged)
  93. self.getMachineManager().addMachineRequested.connect(self._onAddMachineRequested)
  94. self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity)
  95. self.getController().toolOperationStopped.connect(self._onToolOperationStopped)
  96. Resources.addType(self.ResourceTypes.QmlFiles, "qml")
  97. Resources.addType(self.ResourceTypes.Firmware, "firmware")
  98. Preferences.getInstance().addPreference("cura/active_machine", "")
  99. Preferences.getInstance().addPreference("cura/active_mode", "simple")
  100. Preferences.getInstance().addPreference("cura/recent_files", "")
  101. Preferences.getInstance().addPreference("cura/categories_expanded", "")
  102. Preferences.getInstance().addPreference("view/center_on_select", True)
  103. Preferences.getInstance().addPreference("mesh/scale_to_fit", True)
  104. Preferences.getInstance().setDefault("local_file/last_used_type", "text/x-gcode")
  105. JobQueue.getInstance().jobFinished.connect(self._onJobFinished)
  106. self._recent_files = []
  107. files = Preferences.getInstance().getValue("cura/recent_files").split(";")
  108. for f in files:
  109. if not os.path.isfile(f):
  110. continue
  111. self._recent_files.append(QUrl.fromLocalFile(f))
  112. @pyqtSlot(result = QUrl)
  113. def getDefaultPath(self):
  114. return QUrl.fromLocalFile(os.path.expanduser("~/"))
  115. ## Handle loading of all plugin types (and the backend explicitly)
  116. # \sa PluginRegistery
  117. def _loadPlugins(self):
  118. self._plugin_registry.addPluginLocation(os.path.join(QtApplication.getInstallPrefix(), "lib", "cura"))
  119. if not hasattr(sys, "frozen"):
  120. self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins"))
  121. self._plugin_registry.loadPlugin("ConsoleLogger")
  122. self._plugin_registry.loadPlugin("CuraEngineBackend")
  123. self._plugin_registry.loadPlugins()
  124. if self.getBackend() == None:
  125. raise RuntimeError("Could not load the backend plugin!")
  126. self._plugins_loaded = True
  127. def addCommandLineOptions(self, parser):
  128. super().addCommandLineOptions(parser)
  129. parser.add_argument("file", nargs="*", help="Files to load after starting the application.")
  130. parser.add_argument("--debug", dest="debug-mode", action="store_true", default=False, help="Enable detailed crash reports.")
  131. def run(self):
  132. self._i18n_catalog = i18nCatalog("cura");
  133. i18nCatalog.setTagReplacements({
  134. "filename": "font color=\"black\"",
  135. "message": "font color=UM.Theme.colors.message_text;",
  136. })
  137. self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
  138. controller = self.getController()
  139. controller.setActiveView("SolidView")
  140. controller.setCameraTool("CameraTool")
  141. controller.setSelectionTool("SelectionTool")
  142. t = controller.getTool("TranslateTool")
  143. if t:
  144. t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis,ToolHandle.ZAxis])
  145. Selection.selectionChanged.connect(self.onSelectionChanged)
  146. root = controller.getScene().getRoot()
  147. self._platform = Platform(root)
  148. self._volume = BuildVolume.BuildVolume(root)
  149. self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
  150. self._physics = PlatformPhysics.PlatformPhysics(controller, self._volume)
  151. camera = Camera("3d", root)
  152. camera.setPosition(Vector(-80, 250, 700))
  153. camera.setPerspective(True)
  154. camera.lookAt(Vector(0, 0, 0))
  155. controller.getScene().setActiveCamera("3d")
  156. self.getController().getTool("CameraTool").setOrigin(Vector(0, 100, 0))
  157. self._camera_animation = CameraAnimation.CameraAnimation()
  158. self._camera_animation.setCameraTool(self.getController().getTool("CameraTool"))
  159. self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
  160. self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
  161. self.initializeEngine()
  162. if self._engine.rootObjects:
  163. self.closeSplash()
  164. for file in self.getCommandLineOption("file", []):
  165. self._openFile(file)
  166. for file_name in self._open_file_queue: #Open all the files that were queued up while plug-ins were loading.
  167. self._openFile(file_name)
  168. self.exec_()
  169. # Handle Qt events
  170. def event(self, event):
  171. if event.type() == QEvent.FileOpen:
  172. if self._plugins_loaded:
  173. self._openFile(event.file())
  174. else:
  175. self._open_file_queue.append(event.file())
  176. return super().event(event)
  177. def getPrintInformation(self):
  178. return self._print_information
  179. def registerObjects(self, engine):
  180. engine.rootContext().setContextProperty("Printer", self)
  181. self._print_information = PrintInformation.PrintInformation()
  182. engine.rootContext().setContextProperty("PrintInformation", self._print_information)
  183. self._cura_actions = CuraActions.CuraActions(self)
  184. engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
  185. qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
  186. def onSelectionChanged(self):
  187. if Selection.hasSelection():
  188. if not self.getController().getActiveTool():
  189. if self._previous_active_tool:
  190. self.getController().setActiveTool(self._previous_active_tool)
  191. self._previous_active_tool = None
  192. else:
  193. self.getController().setActiveTool("TranslateTool")
  194. if Preferences.getInstance().getValue("view/center_on_select"):
  195. self._center_after_select = True
  196. else:
  197. if self.getController().getActiveTool():
  198. self._previous_active_tool = self.getController().getActiveTool().getPluginId()
  199. self.getController().setActiveTool(None)
  200. else:
  201. self._previous_active_tool = None
  202. def _onToolOperationStopped(self, event):
  203. if self._center_after_select:
  204. self._center_after_select = False
  205. self._camera_animation.setStart(self.getController().getTool("CameraTool").getOrigin())
  206. self._camera_animation.setTarget(Selection.getSelectedObject(0).getWorldPosition())
  207. self._camera_animation.start()
  208. requestAddPrinter = pyqtSignal()
  209. activityChanged = pyqtSignal()
  210. sceneBoundingBoxChanged = pyqtSignal()
  211. @pyqtProperty(bool, notify = activityChanged)
  212. def getPlatformActivity(self):
  213. return self._platform_activity
  214. @pyqtProperty(str, notify = sceneBoundingBoxChanged)
  215. def getSceneBoundingBoxString(self):
  216. return self._i18n_catalog.i18nc("@info", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_boundingbox.width.item(), 'depth': self._scene_boundingbox.depth.item(), 'height' : self._scene_boundingbox.height.item()}
  217. def updatePlatformActivity(self, node = None):
  218. count = 0
  219. scene_boundingbox = None
  220. for node in DepthFirstIterator(self.getController().getScene().getRoot()):
  221. if type(node) is not SceneNode or not node.getMeshData():
  222. continue
  223. count += 1
  224. if not scene_boundingbox:
  225. scene_boundingbox = copy.deepcopy(node.getBoundingBox())
  226. else:
  227. scene_boundingbox += node.getBoundingBox()
  228. if not scene_boundingbox:
  229. scene_boundingbox = AxisAlignedBox()
  230. if repr(self._scene_boundingbox) != repr(scene_boundingbox):
  231. self._scene_boundingbox = scene_boundingbox
  232. self.sceneBoundingBoxChanged.emit()
  233. self._platform_activity = True if count > 0 else False
  234. self.activityChanged.emit()
  235. @pyqtSlot(str)
  236. def setJobName(self, name):
  237. name = os.path.splitext(name)[0] #when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its extension. This cuts the extension off if nescessary.
  238. if self._job_name != name:
  239. self._job_name = name
  240. self.jobNameChanged.emit()
  241. jobNameChanged = pyqtSignal()
  242. @pyqtProperty(str, notify = jobNameChanged)
  243. def jobName(self):
  244. return self._job_name
  245. # Remove all selected objects from the scene.
  246. @pyqtSlot()
  247. def deleteSelection(self):
  248. if not self.getController().getToolsEnabled():
  249. return
  250. op = GroupedOperation()
  251. nodes = Selection.getAllSelectedObjects()
  252. for node in nodes:
  253. op.addOperation(RemoveSceneNodeOperation(node))
  254. op.push()
  255. pass
  256. ## Remove an object from the scene.
  257. # Note that this only removes an object if it is selected.
  258. @pyqtSlot("quint64")
  259. def deleteObject(self, object_id):
  260. if not self.getController().getToolsEnabled():
  261. return
  262. node = self.getController().getScene().findObject(object_id)
  263. if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
  264. node = Selection.getSelectedObject(0)
  265. if node:
  266. if node.getParent():
  267. group_node = node.getParent()
  268. if not group_node.callDecoration("isGroup"):
  269. op = RemoveSceneNodeOperation(node)
  270. else:
  271. while group_node.getParent().callDecoration("isGroup"):
  272. group_node = group_node.getParent()
  273. op = RemoveSceneNodeOperation(group_node)
  274. op.push()
  275. ## Create a number of copies of existing object.
  276. @pyqtSlot("quint64", int)
  277. def multiplyObject(self, object_id, count):
  278. node = self.getController().getScene().findObject(object_id)
  279. if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
  280. node = Selection.getSelectedObject(0)
  281. if node:
  282. op = GroupedOperation()
  283. for i in range(count):
  284. if node.getParent() and node.getParent().callDecoration("isGroup"):
  285. new_node = copy.deepcopy(node.getParent()) #Copy the group node.
  286. new_node.callDecoration("setConvexHull",None)
  287. op.addOperation(AddSceneNodeOperation(new_node,node.getParent().getParent()))
  288. else:
  289. new_node = copy.deepcopy(node)
  290. new_node.callDecoration("setConvexHull", None)
  291. op.addOperation(AddSceneNodeOperation(new_node, node.getParent()))
  292. op.push()
  293. ## Center object on platform.
  294. @pyqtSlot("quint64")
  295. def centerObject(self, object_id):
  296. node = self.getController().getScene().findObject(object_id)
  297. if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
  298. node = Selection.getSelectedObject(0)
  299. if not node:
  300. return
  301. if node.getParent() and node.getParent().callDecoration("isGroup"):
  302. node = node.getParent()
  303. if node:
  304. op = SetTransformOperation(node, Vector())
  305. op.push()
  306. ## Delete all mesh data on the scene.
  307. @pyqtSlot()
  308. def deleteAll(self):
  309. if not self.getController().getToolsEnabled():
  310. return
  311. nodes = []
  312. for node in DepthFirstIterator(self.getController().getScene().getRoot()):
  313. if type(node) is not SceneNode:
  314. continue
  315. if not node.getMeshData() and not node.callDecoration("isGroup"):
  316. continue #Node that doesnt have a mesh and is not a group.
  317. if node.getParent() and node.getParent().callDecoration("isGroup"):
  318. continue #Grouped nodes don't need resetting as their parent (the group) is resetted)
  319. nodes.append(node)
  320. if nodes:
  321. op = GroupedOperation()
  322. for node in nodes:
  323. op.addOperation(RemoveSceneNodeOperation(node))
  324. op.push()
  325. ## Reset all translation on nodes with mesh data.
  326. @pyqtSlot()
  327. def resetAllTranslation(self):
  328. nodes = []
  329. for node in DepthFirstIterator(self.getController().getScene().getRoot()):
  330. if type(node) is not SceneNode:
  331. continue
  332. if not node.getMeshData() and not node.callDecoration("isGroup"):
  333. continue #Node that doesnt have a mesh and is not a group.
  334. if node.getParent() and node.getParent().callDecoration("isGroup"):
  335. continue #Grouped nodes don't need resetting as their parent (the group) is resetted)
  336. nodes.append(node)
  337. if nodes:
  338. op = GroupedOperation()
  339. for node in nodes:
  340. node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
  341. op.addOperation(SetTransformOperation(node, Vector(0,0,0)))
  342. op.push()
  343. ## Reset all transformations on nodes with mesh data.
  344. @pyqtSlot()
  345. def resetAll(self):
  346. nodes = []
  347. for node in DepthFirstIterator(self.getController().getScene().getRoot()):
  348. if type(node) is not SceneNode:
  349. continue
  350. if not node.getMeshData() and not node.callDecoration("isGroup"):
  351. continue #Node that doesnt have a mesh and is not a group.
  352. if node.getParent() and node.getParent().callDecoration("isGroup"):
  353. continue #Grouped nodes don't need resetting as their parent (the group) is resetted)
  354. nodes.append(node)
  355. if nodes:
  356. op = GroupedOperation()
  357. for node in nodes:
  358. # Ensure that the object is above the build platform
  359. node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
  360. op.addOperation(SetTransformOperation(node, Vector(0,0,0), Quaternion(), Vector(1, 1, 1)))
  361. op.push()
  362. ## Reload all mesh data on the screen from file.
  363. @pyqtSlot()
  364. def reloadAll(self):
  365. nodes = []
  366. for node in DepthFirstIterator(self.getController().getScene().getRoot()):
  367. if type(node) is not SceneNode or not node.getMeshData():
  368. continue
  369. nodes.append(node)
  370. if not nodes:
  371. return
  372. for node in nodes:
  373. if not node.getMeshData():
  374. continue
  375. file_name = node.getMeshData().getFileName()
  376. if file_name:
  377. job = ReadMeshJob(file_name)
  378. job._node = node
  379. job.finished.connect(self._reloadMeshFinished)
  380. job.start()
  381. ## Get logging data of the backend engine
  382. # \returns \type{string} Logging data
  383. @pyqtSlot(result=str)
  384. def getEngineLog(self):
  385. log = ""
  386. for entry in self.getBackend().getLog():
  387. log += entry.decode()
  388. return log
  389. recentFilesChanged = pyqtSignal()
  390. @pyqtProperty("QVariantList", notify = recentFilesChanged)
  391. def recentFiles(self):
  392. return self._recent_files
  393. @pyqtSlot("QStringList")
  394. def setExpandedCategories(self, categories):
  395. categories = list(set(categories))
  396. categories.sort()
  397. joined = ";".join(categories)
  398. if joined != Preferences.getInstance().getValue("cura/categories_expanded"):
  399. Preferences.getInstance().setValue("cura/categories_expanded", joined)
  400. self.expandedCategoriesChanged.emit()
  401. expandedCategoriesChanged = pyqtSignal()
  402. @pyqtProperty("QStringList", notify = expandedCategoriesChanged)
  403. def expandedCategories(self):
  404. return Preferences.getInstance().getValue("cura/categories_expanded").split(";")
  405. @pyqtSlot(str, result = "QVariant")
  406. def getSettingValue(self, key):
  407. if not self.getMachineManager().getWorkingProfile():
  408. return None
  409. return self.getMachineManager().getWorkingProfile().getSettingValue(key)
  410. #return self.getActiveMachine().getSettingValueByKey(key)
  411. ## Change setting by key value pair
  412. @pyqtSlot(str, "QVariant")
  413. def setSettingValue(self, key, value):
  414. if not self.getMachineManager().getWorkingProfile():
  415. return
  416. self.getMachineManager().getWorkingProfile().setSettingValue(key, value)
  417. @pyqtSlot()
  418. def mergeSelected(self):
  419. self.groupSelected()
  420. try:
  421. group_node = Selection.getAllSelectedObjects()[0]
  422. except Exception as e:
  423. return
  424. multi_material_decorator = MultiMaterialDecorator.MultiMaterialDecorator()
  425. group_node.addDecorator(multi_material_decorator)
  426. # Reset the position of each node
  427. for node in group_node.getChildren():
  428. new_position = node.getMeshData().getCenterPosition()
  429. new_position = new_position.scale(node.getScale())
  430. node.setPosition(new_position)
  431. # Use the previously found center of the group bounding box as the new location of the group
  432. group_node.setPosition(group_node.getBoundingBox().center)
  433. @pyqtSlot()
  434. def groupSelected(self):
  435. group_node = SceneNode()
  436. group_decorator = GroupDecorator()
  437. group_node.addDecorator(group_decorator)
  438. group_node.setParent(self.getController().getScene().getRoot())
  439. group_node.setSelectable(True)
  440. center = Selection.getSelectionCenter()
  441. group_node.setPosition(center)
  442. group_node.setCenterPosition(center)
  443. for node in Selection.getAllSelectedObjects():
  444. world = node.getWorldPosition()
  445. node.setParent(group_node)
  446. node.setPosition(world - center)
  447. for node in group_node.getChildren():
  448. Selection.remove(node)
  449. Selection.add(group_node)
  450. @pyqtSlot()
  451. def ungroupSelected(self):
  452. ungrouped_nodes = []
  453. selected_objects = Selection.getAllSelectedObjects()[:] #clone the list
  454. for node in selected_objects:
  455. if node.callDecoration("isGroup" ):
  456. children_to_move = []
  457. for child in node.getChildren():
  458. if type(child) is SceneNode:
  459. children_to_move.append(child)
  460. for child in children_to_move:
  461. position = child.getWorldPosition()
  462. child.setParent(node.getParent())
  463. child.setPosition(position - node.getParent().getWorldPosition())
  464. child.scale(node.getScale())
  465. child.rotate(node.getOrientation())
  466. Selection.add(child)
  467. child.callDecoration("setConvexHull",None)
  468. node.setParent(None)
  469. ungrouped_nodes.append(node)
  470. for node in ungrouped_nodes:
  471. Selection.remove(node)
  472. def _createSplashScreen(self):
  473. return CuraSplashScreen.CuraSplashScreen()
  474. def _onActiveMachineChanged(self):
  475. machine = self.getMachineManager().getActiveMachineInstance()
  476. if machine:
  477. pass
  478. #Preferences.getInstance().setValue("cura/active_machine", machine.getName())
  479. #self._volume.setWidth(machine.getSettingValueByKey("machine_width"))
  480. #self._volume.setHeight(machine.getSettingValueByKey("machine_height"))
  481. #self._volume.setDepth(machine.getSettingValueByKey("machine_depth"))
  482. #disallowed_areas = machine.getSettingValueByKey("machine_disallowed_areas")
  483. #areas = []
  484. #if disallowed_areas:
  485. #for area in disallowed_areas:
  486. #areas.append(Polygon(numpy.array(area, numpy.float32)))
  487. #self._volume.setDisallowedAreas(areas)
  488. #self._volume.rebuild()
  489. #offset = machine.getSettingValueByKey("machine_platform_offset")
  490. #if offset:
  491. #self._platform.setPosition(Vector(offset[0], offset[1], offset[2]))
  492. #else:
  493. #self._platform.setPosition(Vector(0.0, 0.0, 0.0))
  494. def _onFileLoaded(self, job):
  495. node = job.getResult()
  496. if node != None:
  497. self.setJobName(os.path.basename(job.getFileName()))
  498. node.setSelectable(True)
  499. node.setName(os.path.basename(job.getFileName()))
  500. op = AddSceneNodeOperation(node, self.getController().getScene().getRoot())
  501. op.push()
  502. self.getController().getScene().sceneChanged.emit(node) #Force scene change.
  503. def _onJobFinished(self, job):
  504. if type(job) is not ReadMeshJob or not job.getResult():
  505. return
  506. f = QUrl.fromLocalFile(job.getFileName())
  507. if f in self._recent_files:
  508. self._recent_files.remove(f)
  509. self._recent_files.insert(0, f)
  510. if len(self._recent_files) > 10:
  511. del self._recent_files[10]
  512. pref = ""
  513. for path in self._recent_files:
  514. pref += path.toLocalFile() + ";"
  515. Preferences.getInstance().setValue("cura/recent_files", pref)
  516. self.recentFilesChanged.emit()
  517. def _reloadMeshFinished(self, job):
  518. # TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh!
  519. job._node.setMeshData(job.getResult().getMeshData())
  520. #job.getResult().setParent(self.getController().getScene().getRoot())
  521. #job._node.setParent(self.getController().getScene().getRoot())
  522. #job._node.meshDataChanged.emit(job._node)
  523. def _openFile(self, file):
  524. job = ReadMeshJob(os.path.abspath(file))
  525. job.finished.connect(self._onFileLoaded)
  526. job.start()
  527. def _onAddMachineRequested(self):
  528. self.requestAddPrinter.emit()