CuraApplication.py 25 KB

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