173
CHANGES
Normal file
@ -0,0 +1,173 @@
|
||||
Cura 15.06 Beta
|
||||
===============
|
||||
|
||||
This is the *Beta* version of Cura 15.06.
|
||||
|
||||
Cura 15.06 is a new release built from the ground up on a completely new
|
||||
framework called Uranium. This framework has been designed to make it easier to
|
||||
extend Cura with additional functionality as well as provide a cleaner UI.
|
||||
|
||||
Changes since 15.05.95
|
||||
----------------------
|
||||
|
||||
* Fixed: Selection ghost remains visible after deleting an object
|
||||
* Fixed: Window does not show up immediately after starting application on OSX
|
||||
* Fixed: Added display of rotation angle during rotation
|
||||
* Fixed: Object changes position while rotating/scaling
|
||||
* Fixed: Loading improvements in the layer view
|
||||
* Fixed: Added application icons
|
||||
* Fixed: Improved feedback when loading models
|
||||
* Fixed: Eject device on MacOSX now provides proper feedback
|
||||
* Fixed: Make it possible to show retraction settings for UM2
|
||||
* Fixed: Opening the machine preferences page will switch to the first available machine
|
||||
* Fixed: Improved tool handle hit area size
|
||||
* Fixed: Render lines with a thickness based on screen DPI
|
||||
|
||||
Changes since 15.05.94
|
||||
----------------------
|
||||
|
||||
* Added Russian translations
|
||||
* Fixed: Infill not displayed in layer view
|
||||
* Fixed: Cannot select/scale/rotate when first activating the tool and then trying to select a model.
|
||||
* Fixed: Improved font rendering on Windows
|
||||
* Fixed: Help > Show Documentation crashes Cura on Windows
|
||||
* Fixed: "There is no disk in the drive" repeating messages on Windows
|
||||
* Fixed: Retraction settings not visible for Ultimaker2
|
||||
* Fixed: Display rotation angle when rotating an object
|
||||
* Fixed: Time/Quality slider values are properly rounded
|
||||
* Fixed: Improved clarity of buttons and text
|
||||
* Fixed: No indication that anything is happening when loading a model
|
||||
* Fixed: Eject device now works on Windows
|
||||
|
||||
Changes since 15.05.93
|
||||
----------------------
|
||||
|
||||
* Fixed: No shortcuts for moving up/down layers in layer view.
|
||||
* Fixed: Last view layers could not be scrolled through in layer view.
|
||||
* Fixed: Files provided on command line would not actually show up on the build
|
||||
platform.
|
||||
* Fixed: Render a ghost of the selection in Layer view to make the actual object
|
||||
position clear.
|
||||
* Fixed: Showing a menu would clear the selection.
|
||||
* Fixed: Size and scaling factor display for scale tool.
|
||||
* Fixed: Missing background for additional tool controls.
|
||||
* Fixed: Loading message times out when loading large files.
|
||||
* Fixed: Show recent files in the file menu.
|
||||
* Fixed: Windows installer will now install MSVC 2010 redistributable, to
|
||||
prevent issues with missing DLL's.
|
||||
* Fixed: Collapsed/expanded state of setting categories not stored.
|
||||
|
||||
Changes since 15.05.91
|
||||
----------------------
|
||||
|
||||
* There is now a working MacOSX version. Currently it supports OSX 10.7 and
|
||||
higher.
|
||||
* Fixed: Need to deselect before selecting a different object.
|
||||
* Fixed: Object can be moved on Z axis.
|
||||
* Fixed: Error values should be considered invalid values and will not trigger a
|
||||
slice.
|
||||
* Fixed: Text fields used a locale-aware validator while the underlying code did
|
||||
not.
|
||||
* Fixed: Text fields will trigger a slice on text change, not only after focus
|
||||
change/enter press.
|
||||
* Fixed: Rotate Tool snaps to incorrect value.
|
||||
* Fixed: Object Collision would only moved objects to the right.
|
||||
* Fixed: Object Collision would move the selected object when it should not.
|
||||
* Fixed: Camera panning now works correctly instead of doing nothing.
|
||||
* Fixed: Camera would flip around center point at maximum rotation.
|
||||
* Fixed: Build platform grid blocked view from below objects.
|
||||
* Fixed: Viewport on MacOSX with high-DPI screens was only taking 1/4th of the
|
||||
window
|
||||
|
||||
Changes since 15.05.90
|
||||
----------------------
|
||||
|
||||
* Fixed: Additional UI elements for tools and views not loading.
|
||||
* Fixed: Double click needed to change setting dialog page.
|
||||
* Fixed: Context menu entries (reload, center object, etc.) not working.
|
||||
* Fixed: "Open With" or passing files from command line not working.
|
||||
* Fixed: "Reload All" would not reload files.
|
||||
|
||||
In addition, a lot of work has gone into getting a usable Mac OSX version.
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
||||
* Plugin based system
|
||||
The Uranium framework provides us with a plugin-based system
|
||||
that provides additional flexibility when extending Cura. Think
|
||||
of new views, tools, file formats, etc. This is probably the
|
||||
biggest new feature.
|
||||
* Improved UI
|
||||
The UI has received a complete overhaul.
|
||||
* Time-Quality Slider
|
||||
The 4 static quick print profiles have been replaced with
|
||||
a slider that should make it easier to find the right spot
|
||||
between print time and print quality.
|
||||
* More Settings
|
||||
The Advanced mode is now configurable and can show many
|
||||
additional settings that were previously not available, while at
|
||||
the same time not overwhelming new users with too many settings.
|
||||
Custom set of visible settings can be created by the user.
|
||||
* Support for high-DPI screens
|
||||
The refreshed UI has been designed with high-DPI screens in
|
||||
mind which should improve the experience of Cura on such
|
||||
devices.
|
||||
* Improved language support
|
||||
(Not yet available for the Beta release.)
|
||||
* Improved support structure generation
|
||||
The new version of the CuraEngine now features improved
|
||||
support generation algorithms and additional options for support
|
||||
structure generation.
|
||||
* Experimental Feature: Wire Printing
|
||||
Wire Printing has been added as an experimental new feature. It
|
||||
will print objects as a structure of lines. It can be enabled by
|
||||
from Advanced Mode -> Fixes -> Wire Printing.
|
||||
* Undo/Redo
|
||||
It is now possible to undo and redo most scene operations, like
|
||||
moving or rotating objects.
|
||||
|
||||
Features from earlier versions not (yet) in this release
|
||||
--------------------------------------------------------
|
||||
|
||||
* The All-at-once/One-at-a-time toggle is not available.
|
||||
We are working on an improved implementation of this mechanism
|
||||
but it will not be available for this release.
|
||||
* No dual extrusion features are available yet.
|
||||
We are working on a completely new workflow for this but this
|
||||
needs additional time.
|
||||
* “Lay Flat” has been removed.
|
||||
The existing implementation was unfortunately not salvageable.
|
||||
We will be looking into an improved implementation for this
|
||||
feature.
|
||||
* "Split Object Into Parts" has been removed.
|
||||
Due to the same reason as Lay Flat.
|
||||
* Support for AMF and DAE file formats has been removed.
|
||||
Both of these will be implemented as plugins in the future.
|
||||
* Support for directly loading a GCode file is not yet available.
|
||||
This will be implemented as a plugin in the future.
|
||||
* Support for PNG, JPG and other image formats has been removed.
|
||||
These can be supported by a plugin with an improved UI.
|
||||
* Support for loading Minecraft levels has been removed.
|
||||
This can be implemented as a plugin.
|
||||
* Windows XP support has been dropped.
|
||||
Microsoft is no longer supporting xp, so they no longer back
|
||||
port certain features that we require.
|
||||
* X-Ray view is missing.
|
||||
Will be implemented as a (you might have guessed it) plugin.
|
||||
* Fixes: Follow Mesh Surface
|
||||
Has been removed from the engine, the same result can be
|
||||
achieved using no infill or top/bottom layers.
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
For an up to date list of all known issues, please see
|
||||
https://github.com/Ultimaker/Cura/issues and
|
||||
https://github.com/Ultimaker/Uranium/issues .
|
||||
|
||||
* Some OBJ files are rendered as black objects due to missing
|
||||
normals.
|
||||
* Disabling plugins does not work correctly yet.
|
||||
* Unicorn occasionally still requires feeding. Do not feed it
|
||||
after midnight.
|
@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
set(URANIUM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/../uranium/scripts" CACHE DIRECTORY "The location of the scripts directory of the Uranium repository")
|
||||
|
||||
if(${URANIUM_SCRIPTS_DIR})
|
||||
if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "")
|
||||
# Extract Strings
|
||||
add_custom_target(extract-messages ${URANIUM_SCRIPTS_DIR}/extract-messages ${CMAKE_SOURCE_DIR} cura)
|
||||
|
||||
@ -27,6 +27,12 @@ if(${URANIUM_SCRIPTS_DIR})
|
||||
set(languages
|
||||
en
|
||||
x-test
|
||||
ru
|
||||
fr
|
||||
de
|
||||
it
|
||||
es
|
||||
fi
|
||||
)
|
||||
foreach(lang ${languages})
|
||||
file(GLOB po_files resources/i18n/${lang}/*.po)
|
||||
@ -55,7 +61,6 @@ find_package(PythonInterp 3.4.0 REQUIRED)
|
||||
set(PYTHON_SITE_PACKAGES_DIR ${CMAKE_INSTALL_LIBDIR}/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages CACHE PATH "Install location of Python package")
|
||||
install(DIRECTORY resources DESTINATION ${CMAKE_INSTALL_DATADIR}/cura)
|
||||
install(DIRECTORY plugins DESTINATION ${CMAKE_INSTALL_LIBDIR}/cura)
|
||||
file(GLOB cura_SRCS cura/*)
|
||||
install(FILES ${cura_SRCS} DESTINATION ${PYTHON_SITE_PACKAGES_DIR}/cura)
|
||||
install(DIRECTORY cura DESTINATION ${PYTHON_SITE_PACKAGES_DIR})
|
||||
install(FILES cura_app.py DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
|
16
README.md
@ -1,7 +1,9 @@
|
||||
Cura
|
||||
====
|
||||
|
||||
This is the source code of Cura.
|
||||
This is the new, shiny, unreleased frontend for Cura. [daid/Cura](https://github.com/daid/Cura.git) is the old legacy Cura that everyone knows and loves/hates.
|
||||
|
||||
We re-worked the whole GUI code at Ultimaker, because my old code started to become an unmaintainable ball of poo.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
@ -12,3 +14,15 @@ Dependencies
|
||||
This will be needed at runtime to perform the actual slicing.
|
||||
* PySerial
|
||||
Only required for USB printing support.
|
||||
|
||||
Configuring Cura
|
||||
----------------
|
||||
* link your CuraEngine backend by inserting the following line in home/.config/cura/config.cfg :
|
||||
[backend]
|
||||
location = /[path_to_the..]/CuraEngine/build/CuraEngine
|
||||
|
||||
|
||||
Build scripts
|
||||
-------------
|
||||
|
||||
Please checkout [cura-build](https://github.com/Ultimaker/cura-build)
|
||||
|
@ -42,6 +42,9 @@ class BuildVolume(SceneNode):
|
||||
def setDepth(self, depth):
|
||||
self._depth = depth
|
||||
|
||||
def getDisallowedAreas(self):
|
||||
return self._disallowed_areas
|
||||
|
||||
def setDisallowedAreas(self, areas):
|
||||
self._disallowed_areas = areas
|
||||
|
||||
@ -62,7 +65,7 @@ class BuildVolume(SceneNode):
|
||||
self._grid_material.setUniformValue("u_gridColor1", Color(205, 202, 201, 255))
|
||||
|
||||
renderer.queueNode(self, material = self._material, mode = Renderer.RenderLines)
|
||||
renderer.queueNode(self, mesh = self._grid_mesh, material = self._grid_material)
|
||||
renderer.queueNode(self, mesh = self._grid_mesh, material = self._grid_material, force_single_sided = True)
|
||||
if self._disallowed_area_mesh:
|
||||
renderer.queueNode(self, mesh = self._disallowed_area_mesh, material = self._material)
|
||||
return True
|
||||
@ -99,29 +102,53 @@ class BuildVolume(SceneNode):
|
||||
|
||||
mb = MeshBuilder()
|
||||
mb.addQuad(
|
||||
Vector(minW, minH, maxD),
|
||||
Vector(maxW, minH, maxD),
|
||||
Vector(minW, minH, minD),
|
||||
Vector(maxW, minH, minD),
|
||||
Vector(minW, minH, minD)
|
||||
Vector(maxW, minH, maxD),
|
||||
Vector(minW, minH, maxD)
|
||||
)
|
||||
self._grid_mesh = mb.getData()
|
||||
for n in range(0, 6):
|
||||
v = self._grid_mesh.getVertex(n)
|
||||
self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2])
|
||||
|
||||
disallowed_area_size = 0
|
||||
if self._disallowed_areas:
|
||||
mb = MeshBuilder()
|
||||
for area in self._disallowed_areas:
|
||||
for polygon in self._disallowed_areas:
|
||||
points = polygon.getPoints()
|
||||
mb.addQuad(
|
||||
area[0],
|
||||
area[1],
|
||||
area[2],
|
||||
area[3],
|
||||
Vector(points[0, 0], 0.1, points[0, 1]),
|
||||
Vector(points[1, 0], 0.1, points[1, 1]),
|
||||
Vector(points[2, 0], 0.1, points[2, 1]),
|
||||
Vector(points[3, 0], 0.1, points[3, 1]),
|
||||
color = Color(174, 174, 174, 255)
|
||||
)
|
||||
# Find the largest disallowed area to exclude it from the maximum scale bounds
|
||||
size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1]))
|
||||
disallowed_area_size = max(size, disallowed_area_size)
|
||||
|
||||
self._disallowed_area_mesh = mb.getData()
|
||||
else:
|
||||
self._disallowed_area_mesh = None
|
||||
|
||||
self._aabb = AxisAlignedBox(minimum = Vector(minW, minH - 1.0, minD), maximum = Vector(maxW, maxH, maxD))
|
||||
|
||||
settings = Application.getInstance().getActiveMachine()
|
||||
|
||||
skirt_size = 0.0
|
||||
if settings.getSettingValueByKey("adhesion_type") == "None":
|
||||
skirt_size = settings.getSettingValueByKey("skirt_line_count") * settings.getSettingValueByKey("skirt_line_width") + settings.getSettingValueByKey("skirt_gap")
|
||||
elif settings.getSettingValueByKey("adhesion_type") == "Brim":
|
||||
skirt_size = settings.getSettingValueByKey("brim_line_count") * settings.getSettingValueByKey("skirt_line_width")
|
||||
else:
|
||||
skirt_size = settings.getSettingValueByKey("skirt_line_width")
|
||||
|
||||
skirt_size += settings.getSettingValueByKey("skirt_line_width")
|
||||
|
||||
scale_to_max_bounds = AxisAlignedBox(
|
||||
minimum = Vector(minW + skirt_size, minH, minD + skirt_size + disallowed_area_size),
|
||||
maximum = Vector(maxW - skirt_size, maxH, maxD - skirt_size - disallowed_area_size)
|
||||
)
|
||||
|
||||
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
|
||||
|
@ -48,6 +48,9 @@ class ConvexHullNode(SceneNode):
|
||||
|
||||
self.setMeshData(mesh)
|
||||
|
||||
def getWatchedNode(self):
|
||||
return self._node
|
||||
|
||||
def render(self, renderer):
|
||||
if not self._material:
|
||||
self._material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "color.frag"))
|
||||
|
27
cura/CuraActions.py
Normal file
@ -0,0 +1,27 @@
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty, QUrl
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
|
||||
from UM.Event import CallFunctionEvent
|
||||
from UM.Application import Application
|
||||
|
||||
import webbrowser
|
||||
|
||||
class CuraActions(QObject):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
@pyqtSlot()
|
||||
def openDocumentation(self):
|
||||
# Starting a web browser from a signal handler connected to a menu will crash on windows.
|
||||
# So instead, defer the call to the next run of the event loop, since that does work.
|
||||
# Note that weirdly enough, only signal handlers that open a web browser fail like that.
|
||||
event = CallFunctionEvent(self._openUrl, [QUrl("http://ultimaker.com/en/support/software")], {})
|
||||
Application.getInstance().functionEvent(event)
|
||||
|
||||
@pyqtSlot()
|
||||
def openBugReportPage(self):
|
||||
event = CallFunctionEvent(self._openUrl, [QUrl("http://github.com/Ultimaker/Cura/issues")], {})
|
||||
Application.getInstance().functionEvent(event)
|
||||
|
||||
def _openUrl(self, url):
|
||||
QDesktopServices.openUrl(url)
|
@ -17,6 +17,8 @@ from UM.Logger import Logger
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Message import Message
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.JobQueue import JobQueue
|
||||
from UM.Math.Polygon import Polygon
|
||||
|
||||
from UM.Scene.BoxRenderer import BoxRenderer
|
||||
from UM.Scene.Selection import Selection
|
||||
@ -32,9 +34,10 @@ from . import PlatformPhysics
|
||||
from . import BuildVolume
|
||||
from . import CameraAnimation
|
||||
from . import PrintInformation
|
||||
from . import CuraActions
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, QUrl, Qt, pyqtSignal, pyqtProperty
|
||||
from PyQt5.QtGui import QColor
|
||||
from PyQt5.QtGui import QColor, QIcon
|
||||
|
||||
import platform
|
||||
import sys
|
||||
@ -50,6 +53,8 @@ class CuraApplication(QtApplication):
|
||||
|
||||
super().__init__(name = "cura", version = "master")
|
||||
|
||||
self.setWindowIcon(QIcon(Resources.getPath(Resources.ImagesLocation, "cura-icon.png")))
|
||||
|
||||
self.setRequiredPlugins([
|
||||
"CuraEngineBackend",
|
||||
"MeshView",
|
||||
@ -66,11 +71,24 @@ class CuraApplication(QtApplication):
|
||||
self._output_devices = {}
|
||||
self._print_information = None
|
||||
self._i18n_catalog = None
|
||||
self._previous_active_tool = None
|
||||
|
||||
self.activeMachineChanged.connect(self._onActiveMachineChanged)
|
||||
|
||||
Preferences.getInstance().addPreference("cura/active_machine", "")
|
||||
Preferences.getInstance().addPreference("cura/active_mode", "simple")
|
||||
Preferences.getInstance().addPreference("cura/recent_files", "")
|
||||
Preferences.getInstance().addPreference("cura/categories_expanded", "")
|
||||
|
||||
JobQueue.getInstance().jobFinished.connect(self._onJobFinished)
|
||||
|
||||
self._recent_files = []
|
||||
files = Preferences.getInstance().getValue("cura/recent_files").split(";")
|
||||
for f in files:
|
||||
if not os.path.isfile(f):
|
||||
continue
|
||||
|
||||
self._recent_files.append(QUrl.fromLocalFile(f))
|
||||
|
||||
## Handle loading of all plugin types (and the backend explicitly)
|
||||
# \sa PluginRegistery
|
||||
@ -162,6 +180,7 @@ class CuraApplication(QtApplication):
|
||||
|
||||
for file in self.getCommandLineOption("file", []):
|
||||
job = ReadMeshJob(os.path.abspath(file))
|
||||
job.finished.connect(self._onFileLoaded)
|
||||
job.start()
|
||||
|
||||
self.exec_()
|
||||
@ -170,18 +189,27 @@ class CuraApplication(QtApplication):
|
||||
engine.rootContext().setContextProperty("Printer", self)
|
||||
self._print_information = PrintInformation.PrintInformation()
|
||||
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
|
||||
self._cura_actions = CuraActions.CuraActions(self)
|
||||
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
|
||||
|
||||
def onSelectionChanged(self):
|
||||
if Selection.hasSelection():
|
||||
if not self.getController().getActiveTool():
|
||||
self.getController().setActiveTool("TranslateTool")
|
||||
if self._previous_active_tool:
|
||||
self.getController().setActiveTool(self._previous_active_tool)
|
||||
self._previous_active_tool = None
|
||||
else:
|
||||
self.getController().setActiveTool("TranslateTool")
|
||||
|
||||
self._camera_animation.setStart(self.getController().getTool("CameraTool").getOrigin())
|
||||
self._camera_animation.setTarget(Selection.getSelectedObject(0).getWorldPosition())
|
||||
self._camera_animation.start()
|
||||
else:
|
||||
if self.getController().getActiveTool():
|
||||
self._previous_active_tool = self.getController().getActiveTool().getPluginId()
|
||||
self.getController().setActiveTool(None)
|
||||
else:
|
||||
self._previous_active_tool = None
|
||||
|
||||
requestAddPrinter = pyqtSignal()
|
||||
|
||||
@ -190,6 +218,9 @@ class CuraApplication(QtApplication):
|
||||
def deleteObject(self, object_id):
|
||||
object = self.getController().getScene().findObject(object_id)
|
||||
|
||||
if not object and object_id != 0: #Workaround for tool handles overlapping the selected object
|
||||
object = Selection.getSelectedObject(0)
|
||||
|
||||
if object:
|
||||
op = RemoveSceneNodeOperation(object)
|
||||
op.push()
|
||||
@ -199,6 +230,9 @@ class CuraApplication(QtApplication):
|
||||
def multiplyObject(self, object_id, count):
|
||||
node = self.getController().getScene().findObject(object_id)
|
||||
|
||||
if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
|
||||
node = Selection.getSelectedObject(0)
|
||||
|
||||
if node:
|
||||
op = GroupedOperation()
|
||||
for i in range(count):
|
||||
@ -215,6 +249,9 @@ class CuraApplication(QtApplication):
|
||||
def centerObject(self, object_id):
|
||||
node = self.getController().getScene().findObject(object_id)
|
||||
|
||||
if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
|
||||
node = Selection.getSelectedObject(0)
|
||||
|
||||
if node:
|
||||
op = SetTransformOperation(node, Vector())
|
||||
op.push()
|
||||
@ -304,6 +341,25 @@ class CuraApplication(QtApplication):
|
||||
|
||||
return log
|
||||
|
||||
recentFilesChanged = pyqtSignal()
|
||||
@pyqtProperty("QVariantList", notify = recentFilesChanged)
|
||||
def recentFiles(self):
|
||||
return self._recent_files
|
||||
|
||||
@pyqtSlot("QStringList")
|
||||
def setExpandedCategories(self, categories):
|
||||
categories = list(set(categories))
|
||||
categories.sort()
|
||||
joined = ";".join(categories)
|
||||
if joined != Preferences.getInstance().getValue("cura/categories_expanded"):
|
||||
Preferences.getInstance().setValue("cura/categories_expanded", joined)
|
||||
self.expandedCategoriesChanged.emit()
|
||||
|
||||
expandedCategoriesChanged = pyqtSignal()
|
||||
@pyqtProperty("QStringList", notify = expandedCategoriesChanged)
|
||||
def expandedCategories(self):
|
||||
return Preferences.getInstance().getValue("cura/categories_expanded").split(";")
|
||||
|
||||
outputDevicesChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty("QVariantMap", notify = outputDevicesChanged)
|
||||
@ -385,7 +441,7 @@ class CuraApplication(QtApplication):
|
||||
self.addOutputDevice(drive, {
|
||||
"id": drive,
|
||||
"function": self._writeToSD,
|
||||
"description": self._i18n_catalog.i18nc("Save button tooltip. {0} is sd card name", "Save to SD Card {0}".format(drive)),
|
||||
"description": self._i18n_catalog.i18nc("Save button tooltip. {0} is sd card name", "Save to SD Card {0}").format(drive),
|
||||
"icon": "save_sd",
|
||||
"priority": 1
|
||||
})
|
||||
@ -411,21 +467,13 @@ class CuraApplication(QtApplication):
|
||||
disallowed_areas = machine.getSettingValueByKey("machine_disallowed_areas")
|
||||
areas = []
|
||||
if disallowed_areas:
|
||||
|
||||
for area in disallowed_areas:
|
||||
polygon = []
|
||||
polygon.append(Vector(area[0][0], 0.2, area[0][1]))
|
||||
polygon.append(Vector(area[1][0], 0.2, area[1][1]))
|
||||
polygon.append(Vector(area[2][0], 0.2, area[2][1]))
|
||||
polygon.append(Vector(area[3][0], 0.2, area[3][1]))
|
||||
areas.append(polygon)
|
||||
areas.append(Polygon(numpy.array(area, numpy.float32)))
|
||||
|
||||
self._volume.setDisallowedAreas(areas)
|
||||
|
||||
self._volume.rebuild()
|
||||
|
||||
if self.getController().getTool("ScaleTool"):
|
||||
self.getController().getTool("ScaleTool").setMaximumBounds(self._volume.getBoundingBox())
|
||||
|
||||
offset = machine.getSettingValueByKey("machine_platform_offset")
|
||||
if offset:
|
||||
self._platform.setPosition(Vector(offset[0], offset[1], offset[2]))
|
||||
@ -438,7 +486,7 @@ class CuraApplication(QtApplication):
|
||||
"eject",
|
||||
self._i18n_catalog.i18nc("Message action", "Eject"),
|
||||
"eject",
|
||||
self._i18n_catalog.i18nc("Message action tooltip, {0} is sdcard", "Eject SD Card {0}".format(job._sdcard))
|
||||
self._i18n_catalog.i18nc("Message action tooltip, {0} is sdcard", "Eject SD Card {0}").format(job._sdcard)
|
||||
)
|
||||
message._sdcard = job._sdcard
|
||||
message.actionTriggered.connect(self._onMessageActionTriggered)
|
||||
@ -447,3 +495,34 @@ class CuraApplication(QtApplication):
|
||||
def _onMessageActionTriggered(self, message, action):
|
||||
if action == "eject":
|
||||
self.getStorageDevice("LocalFileStorage").ejectRemovableDrive(message._sdcard)
|
||||
|
||||
def _onFileLoaded(self, job):
|
||||
mesh = job.getResult()
|
||||
if mesh != None:
|
||||
node = SceneNode()
|
||||
|
||||
node.setSelectable(True)
|
||||
node.setMeshData(mesh)
|
||||
node.setName(os.path.basename(job.getFileName()))
|
||||
|
||||
op = AddSceneNodeOperation(node, self.getController().getScene().getRoot())
|
||||
op.push()
|
||||
|
||||
def _onJobFinished(self, job):
|
||||
if type(job) is not ReadMeshJob:
|
||||
return
|
||||
|
||||
f = QUrl.fromLocalFile(job.getFileName())
|
||||
if f in self._recent_files:
|
||||
self._recent_files.remove(f)
|
||||
|
||||
self._recent_files.insert(0, f)
|
||||
if len(self._recent_files) > 10:
|
||||
del self._recent_files[10]
|
||||
|
||||
pref = ""
|
||||
for path in self._recent_files:
|
||||
pref += path.toLocalFile() + ";"
|
||||
|
||||
Preferences.getInstance().setValue("cura/recent_files", pref)
|
||||
self.recentFilesChanged.emit()
|
||||
|
@ -11,6 +11,7 @@ from UM.Math.Float import Float
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
from UM.Application import Application
|
||||
from UM.Scene.Selection import Selection
|
||||
|
||||
from . import PlatformPhysicsOperation
|
||||
from . import ConvexHullJob
|
||||
@ -23,8 +24,12 @@ class PlatformPhysics:
|
||||
super().__init__()
|
||||
self._controller = controller
|
||||
self._controller.getScene().sceneChanged.connect(self._onSceneChanged)
|
||||
self._controller.toolOperationStarted.connect(self._onToolOperationStarted)
|
||||
self._controller.toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
self._build_volume = volume
|
||||
|
||||
self._enabled = True
|
||||
|
||||
self._change_timer = QTimer()
|
||||
self._change_timer.setInterval(100)
|
||||
self._change_timer.setSingleShot(True)
|
||||
@ -34,6 +39,9 @@ class PlatformPhysics:
|
||||
self._change_timer.start()
|
||||
|
||||
def _onChangeTimerFinished(self):
|
||||
if not self._enabled:
|
||||
return
|
||||
|
||||
root = self._controller.getScene().getRoot()
|
||||
for node in BreadthFirstIterator(root):
|
||||
if node is root or type(node) is not SceneNode:
|
||||
@ -41,6 +49,7 @@ class PlatformPhysics:
|
||||
|
||||
bbox = node.getBoundingBox()
|
||||
if not bbox or not bbox.isValid():
|
||||
self._change_timer.start()
|
||||
continue
|
||||
|
||||
# Mark the node as outside the build volume if the bounding box test fails.
|
||||
@ -60,6 +69,8 @@ class PlatformPhysics:
|
||||
job = ConvexHullJob.ConvexHullJob(node)
|
||||
job.start()
|
||||
node._convex_hull_job = job
|
||||
elif Selection.isSelected(node):
|
||||
pass
|
||||
else:
|
||||
# Check for collisions between convex hulls
|
||||
for other_node in BreadthFirstIterator(root):
|
||||
@ -80,13 +91,25 @@ class PlatformPhysics:
|
||||
if overlap is None:
|
||||
continue
|
||||
|
||||
move_vector.setX(-overlap[0])
|
||||
move_vector.setZ(-overlap[1])
|
||||
move_vector.setX(overlap[0] * 1.1)
|
||||
move_vector.setZ(overlap[1] * 1.1)
|
||||
|
||||
if hasattr(node, "_convex_hull"):
|
||||
# Check for collisions between disallowed areas and the object
|
||||
for area in self._build_volume.getDisallowedAreas():
|
||||
overlap = node._convex_hull.intersectsPolygon(area)
|
||||
if overlap is None:
|
||||
continue
|
||||
|
||||
node._outside_buildarea = True
|
||||
|
||||
if move_vector != Vector():
|
||||
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
|
||||
op.push()
|
||||
|
||||
if node.getBoundingBox().intersectsBox(self._build_volume.getBoundingBox()) == AxisAlignedBox.IntersectionResult.FullIntersection:
|
||||
op = ScaleToBoundsOperation(node, self._build_volume.getBoundingBox())
|
||||
op.push()
|
||||
def _onToolOperationStarted(self, tool):
|
||||
self._enabled = False
|
||||
|
||||
def _onToolOperationStopped(self, tool):
|
||||
self._enabled = True
|
||||
self._onChangeTimerFinished()
|
||||
|
@ -38,6 +38,8 @@ class PrintInformation(QObject):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._enabled = False
|
||||
|
||||
self._minimum_print_time = Duration(None, self)
|
||||
self._current_print_time = Duration(None, self)
|
||||
self._maximum_print_time = Duration(None, self)
|
||||
@ -51,8 +53,8 @@ class PrintInformation(QObject):
|
||||
self._time_quality_changed_timer.timeout.connect(self._updateTimeQualitySettings)
|
||||
|
||||
self._interpolation_settings = {
|
||||
"layer_height": { "minimum": "low", "maximum": "high", "curve": "linear" },
|
||||
"fill_sparse_density": { "minimum": "low", "maximum": "high", "curve": "linear" }
|
||||
"layer_height": { "minimum": "low", "maximum": "high", "curve": "linear", "precision": 2 },
|
||||
"fill_sparse_density": { "minimum": "low", "maximum": "high", "curve": "linear", "precision": 0 }
|
||||
}
|
||||
|
||||
self._low_quality_settings = None
|
||||
@ -103,6 +105,21 @@ class PrintInformation(QObject):
|
||||
def timeQualityValue(self):
|
||||
return self._time_quality_value
|
||||
|
||||
def setEnabled(self, enabled):
|
||||
if enabled != self._enabled:
|
||||
self._enabled = enabled
|
||||
|
||||
if self._enabled:
|
||||
self._updateTimeQualitySettings()
|
||||
self._onSlicingStarted()
|
||||
|
||||
self.enabledChanged.emit()
|
||||
|
||||
enabledChanged = pyqtSignal()
|
||||
@pyqtProperty(bool, fset = setEnabled, notify = enabledChanged)
|
||||
def enabled(self):
|
||||
return self._enabled
|
||||
|
||||
@pyqtSlot(int)
|
||||
def setTimeQualityValue(self, value):
|
||||
if value != self._time_quality_value:
|
||||
@ -132,7 +149,10 @@ class PrintInformation(QObject):
|
||||
self._material_amount = round(amount / 10) / 100
|
||||
self.materialAmountChanged.emit()
|
||||
|
||||
if self._slice_reason != self.SliceReason.SettingChanged:
|
||||
if not self._enabled:
|
||||
return
|
||||
|
||||
if self._slice_reason != self.SliceReason.SettingChanged or not self._minimum_print_time.valid or not self._maximum_print_time.valid:
|
||||
self._slice_pass = self.SlicePass.LowQualitySettings
|
||||
self._backend.slice(settings = self._low_quality_settings, save_gcode = False, save_polygons = False, force_restart = False, report_progress = False)
|
||||
else:
|
||||
@ -166,7 +186,7 @@ class PrintInformation(QObject):
|
||||
self._slice_reason = self.SliceReason.ActiveMachineChanged
|
||||
|
||||
def _updateTimeQualitySettings(self):
|
||||
if not self._current_settings:
|
||||
if not self._current_settings or not self._enabled:
|
||||
return
|
||||
|
||||
if not self._low_quality_settings:
|
||||
@ -196,7 +216,7 @@ class PrintInformation(QObject):
|
||||
else:
|
||||
continue
|
||||
|
||||
setting_value = minimum_value + (maximum_value - minimum_value) * (self._time_quality_value / 100)
|
||||
setting_value = round(minimum_value + (maximum_value - minimum_value) * (self._time_quality_value / 100), options["precision"])
|
||||
self._current_settings.setSettingValueByKey(key, setting_value)
|
||||
|
||||
def _onSceneChanged(self, source):
|
||||
|
0
cura_app.py
Normal file → Executable file
BIN
icons/cura-128.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
icons/cura-32.png
Normal file
After Width: | Height: | Size: 625 B |
BIN
icons/cura-48.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
icons/cura-64.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
icons/cura.icns
Normal file
BIN
icons/cura.ico
Normal file
After Width: | Height: | Size: 30 KiB |
@ -1,5 +1,5 @@
|
||||
!ifndef VERSION
|
||||
!define VERSION 'BETA'
|
||||
!define VERSION '15.05.96'
|
||||
!endif
|
||||
|
||||
; The name of the installer
|
||||
@ -44,6 +44,9 @@ SetCompressor /SOLID lzma
|
||||
!define MUI_FINISHPAGE_RUN_TEXT "Start Cura ${VERSION}"
|
||||
!define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink"
|
||||
|
||||
;Add an option to show release notes
|
||||
!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\release_notes.txt"
|
||||
|
||||
; Pages
|
||||
;!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
@ -89,7 +92,7 @@ Section "Cura ${VERSION}"
|
||||
|
||||
CreateDirectory "$SMPROGRAMS\Cura ${VERSION}"
|
||||
CreateShortCut "$SMPROGRAMS\Cura ${VERSION}\Uninstall Cura ${VERSION}.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
|
||||
CreateShortCut "$SMPROGRAMS\Cura ${VERSION}\Cura ${VERSION}.lnk" "$INSTDIR\Cura.exe" '' "$INSTDIR\resources\cura.ico" 0
|
||||
CreateShortCut "$SMPROGRAMS\Cura ${VERSION}\Cura ${VERSION}.lnk" "$INSTDIR\Cura.exe" '' "$INSTDIR\Cura.exe" 0
|
||||
|
||||
SectionEnd
|
||||
|
||||
@ -99,6 +102,15 @@ Function LaunchLink
|
||||
Exec '"$WINDIR\explorer.exe" "$SMPROGRAMS\Cura ${VERSION}\Cura ${VERSION}.lnk"'
|
||||
FunctionEnd
|
||||
|
||||
Section "Install Visual Studio 2010 Redistributable"
|
||||
SetOutPath "$INSTDIR"
|
||||
File "vcredist_2010_20110908_x86.exe"
|
||||
|
||||
IfSilent +2
|
||||
ExecWait '"$INSTDIR\vcredist_2010_20110908_x86.exe" /q /norestart'
|
||||
|
||||
SectionEnd
|
||||
|
||||
;Section "Install Arduino Drivers"
|
||||
; ; Set output path to the driver directory.
|
||||
; SetOutPath "$INSTDIR\drivers\"
|
||||
|
@ -59,6 +59,8 @@ class CuraEngineBackend(Backend):
|
||||
self._save_polygons = True
|
||||
self._report_progress = True
|
||||
|
||||
self._enabled = True
|
||||
|
||||
self.backendConnected.connect(self._onBackendConnected)
|
||||
|
||||
def getEngineCommand(self):
|
||||
@ -86,6 +88,9 @@ class CuraEngineBackend(Backend):
|
||||
# If False, this method will do nothing when already slicing. True by default.
|
||||
# - report_progress: True if the slicing progress should be reported, False if not. Default is True.
|
||||
def slice(self, **kwargs):
|
||||
if not self._enabled:
|
||||
return
|
||||
|
||||
if self._slicing:
|
||||
if not kwargs.get("force_restart", True):
|
||||
return
|
||||
@ -110,6 +115,9 @@ class CuraEngineBackend(Backend):
|
||||
if not objects:
|
||||
return #No point in slicing an empty build plate
|
||||
|
||||
if kwargs.get("settings", self._settings).hasErrorValue():
|
||||
return #No slicing if we have error values since those are by definition illegal values.
|
||||
|
||||
self._slicing = True
|
||||
self.slicingStarted.emit()
|
||||
|
||||
@ -232,3 +240,10 @@ class CuraEngineBackend(Backend):
|
||||
if self._restart:
|
||||
self._onChanged()
|
||||
self._restart = False
|
||||
|
||||
def _onToolOperationStarted(self, tool):
|
||||
self._enabled = False
|
||||
|
||||
def _onToolOperationStopped(self, tool):
|
||||
self._enabled = True
|
||||
self._onChanged()
|
||||
|
@ -1,6 +1,3 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: Cura.proto
|
||||
|
||||
@ -21,7 +18,7 @@ _sym_db = _symbol_database.Default()
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='Cura.proto',
|
||||
package='Cura',
|
||||
serialized_pb=_b('\n\nCura.proto\x12\x04\x43ura\"+\n\nObjectList\x12\x1d\n\x07objects\x18\x01 \x03(\x0b\x32\x0c.Cura.Object\"i\n\x06Object\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08vertices\x18\x02 \x01(\x0c\x12\x0f\n\x07normals\x18\x03 \x01(\x0c\x12\x0f\n\x07indices\x18\x04 \x01(\x0c\x12\x1f\n\x08settings\x18\x05 \x03(\x0b\x32\r.Cura.Setting\"\x1a\n\x08Progress\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x02\"7\n\x10SlicedObjectList\x12#\n\x07objects\x18\x01 \x03(\x0b\x32\x12.Cura.SlicedObject\"7\n\x0cSlicedObject\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x1b\n\x06layers\x18\x02 \x03(\x0b\x32\x0b.Cura.Layer\"4\n\x05Layer\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x1f\n\x08polygons\x18\x02 \x03(\x0b\x32\r.Cura.Polygon\"\x9f\x01\n\x07Polygon\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.Cura.Polygon.Type\x12\x0e\n\x06points\x18\x02 \x01(\x0c\"b\n\x04Type\x12\x0c\n\x08NoneType\x10\x00\x12\x0e\n\nInset0Type\x10\x01\x12\x0e\n\nInsetXType\x10\x02\x12\x0c\n\x08SkinType\x10\x03\x12\x0f\n\x0bSupportType\x10\x04\x12\r\n\tSkirtType\x10\x05\"&\n\nGCodeLayer\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"D\n\x0fObjectPrintTime\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04time\x18\x02 \x01(\x02\x12\x17\n\x0fmaterial_amount\x18\x03 \x01(\x02\".\n\x0bSettingList\x12\x1f\n\x08settings\x18\x01 \x03(\x0b\x32\r.Cura.Setting\"&\n\x07Setting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\"\x1b\n\x0bGCodePrefix\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x62\x06proto3')
|
||||
serialized_pb=_b('\n\nCura.proto\x12\x04\x43ura\"+\n\nObjectList\x12\x1d\n\x07objects\x18\x01 \x03(\x0b\x32\x0c.Cura.Object\"i\n\x06Object\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08vertices\x18\x02 \x01(\x0c\x12\x0f\n\x07normals\x18\x03 \x01(\x0c\x12\x0f\n\x07indices\x18\x04 \x01(\x0c\x12\x1f\n\x08settings\x18\x05 \x03(\x0b\x32\r.Cura.Setting\"\x1a\n\x08Progress\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x02\"7\n\x10SlicedObjectList\x12#\n\x07objects\x18\x01 \x03(\x0b\x32\x12.Cura.SlicedObject\"7\n\x0cSlicedObject\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x1b\n\x06layers\x18\x02 \x03(\x0b\x32\x0b.Cura.Layer\"W\n\x05Layer\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x02\x12\x11\n\tthickness\x18\x03 \x01(\x02\x12\x1f\n\x08polygons\x18\x04 \x03(\x0b\x32\r.Cura.Polygon\"\xdb\x01\n\x07Polygon\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.Cura.Polygon.Type\x12\x0e\n\x06points\x18\x02 \x01(\x0c\x12\x12\n\nline_width\x18\x03 \x01(\x02\"\x89\x01\n\x04Type\x12\x0c\n\x08NoneType\x10\x00\x12\x0e\n\nInset0Type\x10\x01\x12\x0e\n\nInsetXType\x10\x02\x12\x0c\n\x08SkinType\x10\x03\x12\x0f\n\x0bSupportType\x10\x04\x12\r\n\tSkirtType\x10\x05\x12\x0e\n\nInfillType\x10\x06\x12\x15\n\x11SupportInfillType\x10\x07\"&\n\nGCodeLayer\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"D\n\x0fObjectPrintTime\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04time\x18\x02 \x01(\x02\x12\x17\n\x0fmaterial_amount\x18\x03 \x01(\x02\".\n\x0bSettingList\x12\x1f\n\x08settings\x18\x01 \x03(\x0b\x32\r.Cura.Setting\"&\n\x07Setting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\"\x1b\n\x0bGCodePrefix\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x62\x06proto3')
|
||||
)
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
@ -57,11 +54,19 @@ _POLYGON_TYPE = _descriptor.EnumDescriptor(
|
||||
name='SkirtType', index=5, number=5,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='InfillType', index=6, number=6,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='SupportInfillType', index=7, number=7,
|
||||
options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
options=None,
|
||||
serialized_start=430,
|
||||
serialized_end=528,
|
||||
serialized_start=486,
|
||||
serialized_end=623,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_POLYGON_TYPE)
|
||||
|
||||
@ -266,8 +271,22 @@ _LAYER = _descriptor.Descriptor(
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='polygons', full_name='Cura.Layer.polygons', index=1,
|
||||
number=2, type=11, cpp_type=10, label=3,
|
||||
name='height', full_name='Cura.Layer.height', index=1,
|
||||
number=2, type=2, cpp_type=6, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='thickness', full_name='Cura.Layer.thickness', index=2,
|
||||
number=3, type=2, cpp_type=6, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='polygons', full_name='Cura.Layer.polygons', index=3,
|
||||
number=4, type=11, cpp_type=10, label=3,
|
||||
has_default_value=False, default_value=[],
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
@ -284,7 +303,7 @@ _LAYER = _descriptor.Descriptor(
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=314,
|
||||
serialized_end=366,
|
||||
serialized_end=401,
|
||||
)
|
||||
|
||||
|
||||
@ -309,6 +328,13 @@ _POLYGON = _descriptor.Descriptor(
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='line_width', full_name='Cura.Polygon.line_width', index=2,
|
||||
number=3, type=2, cpp_type=6, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
@ -321,8 +347,8 @@ _POLYGON = _descriptor.Descriptor(
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=369,
|
||||
serialized_end=528,
|
||||
serialized_start=404,
|
||||
serialized_end=623,
|
||||
)
|
||||
|
||||
|
||||
@ -358,8 +384,8 @@ _GCODELAYER = _descriptor.Descriptor(
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=530,
|
||||
serialized_end=568,
|
||||
serialized_start=625,
|
||||
serialized_end=663,
|
||||
)
|
||||
|
||||
|
||||
@ -402,8 +428,8 @@ _OBJECTPRINTTIME = _descriptor.Descriptor(
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=570,
|
||||
serialized_end=638,
|
||||
serialized_start=665,
|
||||
serialized_end=733,
|
||||
)
|
||||
|
||||
|
||||
@ -432,8 +458,8 @@ _SETTINGLIST = _descriptor.Descriptor(
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=640,
|
||||
serialized_end=686,
|
||||
serialized_start=735,
|
||||
serialized_end=781,
|
||||
)
|
||||
|
||||
|
||||
@ -469,8 +495,8 @@ _SETTING = _descriptor.Descriptor(
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=688,
|
||||
serialized_end=726,
|
||||
serialized_start=783,
|
||||
serialized_end=821,
|
||||
)
|
||||
|
||||
|
||||
@ -499,8 +525,8 @@ _GCODEPREFIX = _descriptor.Descriptor(
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=728,
|
||||
serialized_end=755,
|
||||
serialized_start=823,
|
||||
serialized_end=850,
|
||||
)
|
||||
|
||||
_OBJECTLIST.fields_by_name['objects'].message_type = _OBJECT
|
||||
|
@ -2,10 +2,13 @@
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Math.Color import Color
|
||||
from UM.Math.Vector import Vector
|
||||
|
||||
import numpy
|
||||
import math
|
||||
import copy
|
||||
|
||||
class LayerData(MeshData):
|
||||
def __init__(self):
|
||||
@ -13,12 +16,19 @@ class LayerData(MeshData):
|
||||
self._layers = {}
|
||||
self._element_counts = {}
|
||||
|
||||
def addPolygon(self, layer, type, data):
|
||||
def addLayer(self, layer):
|
||||
if layer not in self._layers:
|
||||
self._layers[layer] = []
|
||||
self._layers[layer] = Layer(layer)
|
||||
|
||||
p = Polygon(self, type, data)
|
||||
self._layers[layer].append(p)
|
||||
def addPolygon(self, layer, type, data, line_width):
|
||||
if layer not in self._layers:
|
||||
self.addLayer(layer)
|
||||
|
||||
p = Polygon(self, type, data, line_width)
|
||||
self._layers[layer].polygons.append(p)
|
||||
|
||||
def getLayer(self, layer):
|
||||
return self._layers[layer]
|
||||
|
||||
def getLayers(self):
|
||||
return self._layers
|
||||
@ -26,14 +36,134 @@ class LayerData(MeshData):
|
||||
def getElementCounts(self):
|
||||
return self._element_counts
|
||||
|
||||
def build(self):
|
||||
for layer, data in self._layers.items():
|
||||
if layer not in self._element_counts:
|
||||
self._element_counts[layer] = []
|
||||
def setLayerHeight(self, layer, height):
|
||||
if layer not in self._layers:
|
||||
self.addLayer(layer)
|
||||
|
||||
for polygon in data:
|
||||
polygon.build()
|
||||
self._element_counts[layer].append(polygon.elementCount)
|
||||
self._layers[layer].setHeight(height)
|
||||
|
||||
def setLayerThickness(self, layer, thickness):
|
||||
if layer not in self._layers:
|
||||
self.addLayer(layer)
|
||||
|
||||
self._layers[layer].setThickness(thickness)
|
||||
|
||||
def build(self):
|
||||
vertex_count = 0
|
||||
for layer, data in self._layers.items():
|
||||
vertex_count += data.vertexCount()
|
||||
|
||||
vertices = numpy.empty((vertex_count, 3), numpy.float32)
|
||||
colors = numpy.empty((vertex_count, 4), numpy.float32)
|
||||
indices = numpy.empty((vertex_count, 2), numpy.int32)
|
||||
|
||||
offset = 0
|
||||
for layer, data in self._layers.items():
|
||||
offset = data.build(offset, vertices, colors, indices)
|
||||
self._element_counts[layer] = data.elementCount
|
||||
|
||||
self.addVertices(vertices)
|
||||
self.addColors(colors)
|
||||
self.addIndices(indices.flatten())
|
||||
|
||||
class Layer():
|
||||
def __init__(self, id):
|
||||
self._id = id
|
||||
self._height = 0.0
|
||||
self._thickness = 0.0
|
||||
self._polygons = []
|
||||
self._element_count = 0
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
return self._height
|
||||
|
||||
@property
|
||||
def thickness(self):
|
||||
return self._thickness
|
||||
|
||||
@property
|
||||
def polygons(self):
|
||||
return self._polygons
|
||||
|
||||
@property
|
||||
def elementCount(self):
|
||||
return self._element_count
|
||||
|
||||
def setHeight(self, height):
|
||||
self._height = height
|
||||
|
||||
def setThickness(self, thickness):
|
||||
self._thickness = thickness
|
||||
|
||||
def vertexCount(self):
|
||||
result = 0
|
||||
for polygon in self._polygons:
|
||||
result += polygon.vertexCount()
|
||||
|
||||
return result
|
||||
|
||||
def build(self, offset, vertices, colors, indices):
|
||||
result = offset
|
||||
for polygon in self._polygons:
|
||||
if polygon._type == Polygon.InfillType or polygon._type == Polygon.SupportInfillType:
|
||||
continue
|
||||
|
||||
polygon.build(result, vertices, colors, indices)
|
||||
result += polygon.vertexCount()
|
||||
self._element_count += polygon.elementCount
|
||||
|
||||
return result
|
||||
|
||||
def createMesh(self):
|
||||
builder = MeshBuilder()
|
||||
|
||||
for polygon in self._polygons:
|
||||
poly_color = polygon.getColor()
|
||||
|
||||
points = numpy.copy(polygon.data)
|
||||
if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType:
|
||||
points[:,1] -= 0.01
|
||||
|
||||
# Calculate normals for the entire polygon using numpy.
|
||||
normals = numpy.copy(points)
|
||||
normals[:,1] = 0.0 # We are only interested in 2D normals
|
||||
|
||||
# Calculate the edges between points.
|
||||
# The call to numpy.roll shifts the entire array by one so that
|
||||
# we end up subtracting each next point from the current, wrapping
|
||||
# around. This gives us the edges from the next point to the current
|
||||
# point.
|
||||
normals[:] = normals[:] - numpy.roll(normals, -1, axis = 0)
|
||||
# Calculate the length of each edge using standard Pythagoras
|
||||
lengths = numpy.sqrt(normals[:,0] ** 2 + normals[:,2] ** 2)
|
||||
# The normal of a 2D vector is equal to its x and y coordinates swapped
|
||||
# and then x inverted. This code does that.
|
||||
normals[:,[0, 2]] = normals[:,[2, 0]]
|
||||
normals[:,0] *= -1
|
||||
|
||||
# Normalize the normals.
|
||||
normals[:,0] /= lengths
|
||||
normals[:,2] /= lengths
|
||||
|
||||
# Scale all by the line width of the polygon so we can easily offset.
|
||||
normals *= (polygon.lineWidth / 2)
|
||||
|
||||
#TODO: Use numpy magic to perform the vertex creation to speed up things.
|
||||
for i in range(len(points)):
|
||||
start = points[i - 1]
|
||||
end = points[i]
|
||||
|
||||
normal = normals[i - 1]
|
||||
|
||||
point1 = Vector(data = start - normal)
|
||||
point2 = Vector(data = start + normal)
|
||||
point3 = Vector(data = end + normal)
|
||||
point4 = Vector(data = end - normal)
|
||||
|
||||
builder.addQuad(point1, point2, point3, point4, color = poly_color)
|
||||
|
||||
return builder.getData()
|
||||
|
||||
class Polygon():
|
||||
NoneType = 0
|
||||
@ -42,43 +172,58 @@ class Polygon():
|
||||
SkinType = 3
|
||||
SupportType = 4
|
||||
SkirtType = 5
|
||||
InfillType = 6
|
||||
SupportInfillType = 7
|
||||
|
||||
def __init__(self, mesh, type, data):
|
||||
def __init__(self, mesh, type, data, line_width):
|
||||
super().__init__()
|
||||
self._mesh = mesh
|
||||
self._type = type
|
||||
self._data = data
|
||||
self._line_width = line_width / 1000
|
||||
|
||||
def build(self, offset, vertices, colors, indices):
|
||||
self._begin = offset
|
||||
|
||||
color = self.getColor()
|
||||
color.setValues(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a)
|
||||
|
||||
for i in range(len(self._data)):
|
||||
vertices[offset + i, :] = self._data[i, :]
|
||||
colors[offset + i, 0] = color.r
|
||||
colors[offset + i, 1] = color.g
|
||||
colors[offset + i, 2] = color.b
|
||||
colors[offset + i, 3] = color.a
|
||||
|
||||
def build(self):
|
||||
self._begin = self._mesh._vertex_count
|
||||
self._mesh.addVertices(self._data)
|
||||
self._end = self._begin + len(self._data) - 1
|
||||
|
||||
color = None
|
||||
if self._type == self.Inset0Type:
|
||||
color = [1, 0, 0, 1]
|
||||
elif self._type == self.InsetXType:
|
||||
color = [0, 1, 0, 1]
|
||||
elif self._type == self.SkinType:
|
||||
color = [1, 1, 0, 1]
|
||||
elif self._type == self.SupportType:
|
||||
color = [0, 1, 1, 1]
|
||||
elif self._type == self.SkirtType:
|
||||
color = [0, 1, 1, 1]
|
||||
else:
|
||||
color = [1, 1, 1, 1]
|
||||
|
||||
colors = [color for i in range(len(self._data))]
|
||||
self._mesh.addColors(numpy.array(colors, dtype=numpy.float32))
|
||||
|
||||
indices = []
|
||||
for i in range(self._begin, self._end):
|
||||
indices.append(i)
|
||||
indices.append(i + 1)
|
||||
indices[i, 0] = i
|
||||
indices[i, 1] = i + 1
|
||||
|
||||
indices.append(self._end)
|
||||
indices.append(self._begin)
|
||||
self._mesh.addIndices(numpy.array(indices, dtype=numpy.int32))
|
||||
indices[self._end, 0] = self._end
|
||||
indices[self._end, 1] = self._begin
|
||||
|
||||
def getColor(self):
|
||||
if self._type == self.Inset0Type:
|
||||
return Color(1.0, 0.0, 0.0, 1.0)
|
||||
elif self._type == self.InsetXType:
|
||||
return Color(0.0, 1.0, 0.0, 1.0)
|
||||
elif self._type == self.SkinType:
|
||||
return Color(1.0, 1.0, 0.0, 1.0)
|
||||
elif self._type == self.SupportType:
|
||||
return Color(0.0, 1.0, 1.0, 1.0)
|
||||
elif self._type == self.SkirtType:
|
||||
return Color(0.0, 1.0, 1.0, 1.0)
|
||||
elif self._type == self.InfillType:
|
||||
return Color(1.0, 1.0, 0.0, 1.0)
|
||||
elif self._type == self.SupportInfillType:
|
||||
return Color(0.0, 1.0, 1.0, 1.0)
|
||||
else:
|
||||
return Color(1.0, 1.0, 1.0, 1.0)
|
||||
|
||||
def vertexCount(self):
|
||||
return len(self._data)
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
@ -90,4 +235,8 @@ class Polygon():
|
||||
|
||||
@property
|
||||
def elementCount(self):
|
||||
return (self._end - self._begin) * 2 #The range of vertices multiplied by 2 since each vertex is used twice
|
||||
return ((self._end - self._begin) + 1) * 2 #The range of vertices multiplied by 2 since each vertex is used twice
|
||||
|
||||
@property
|
||||
def lineWidth(self):
|
||||
return self._line_width
|
||||
|
@ -7,18 +7,30 @@ from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Application import Application
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
|
||||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from . import LayerData
|
||||
|
||||
import numpy
|
||||
import struct
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
class ProcessSlicedObjectListJob(Job):
|
||||
def __init__(self, message):
|
||||
super().__init__()
|
||||
self._message = message
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
|
||||
self._progress = None
|
||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
def run(self):
|
||||
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
||||
self._progress = Message(catalog.i18nc("Layers View mode", "Layers"), 0, False, 0)
|
||||
self._progress.show()
|
||||
|
||||
objectIdMap = {}
|
||||
new_node = SceneNode()
|
||||
## Put all nodes in a dict identified by ID
|
||||
@ -32,39 +44,67 @@ class ProcessSlicedObjectListJob(Job):
|
||||
settings = Application.getInstance().getActiveMachine()
|
||||
layerHeight = settings.getSettingValueByKey("layer_height")
|
||||
|
||||
center = None
|
||||
if not settings.getSettingValueByKey("machine_center_is_zero"):
|
||||
center = numpy.array([settings.getSettingValueByKey("machine_width") / 2, 0.0, -settings.getSettingValueByKey("machine_depth") / 2])
|
||||
else:
|
||||
center = numpy.array([0.0, 0.0, 0.0])
|
||||
|
||||
if self._progress:
|
||||
self._progress.setProgress(2)
|
||||
|
||||
mesh = MeshData()
|
||||
for object in self._message.objects:
|
||||
try:
|
||||
try:
|
||||
node = objectIdMap[object.id]
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
mesh = MeshData()
|
||||
|
||||
layerData = LayerData.LayerData()
|
||||
for layer in object.layers:
|
||||
layerData.addLayer(layer.id)
|
||||
layerData.setLayerHeight(layer.id, layer.height)
|
||||
layerData.setLayerThickness(layer.id, layer.thickness)
|
||||
for polygon in layer.polygons:
|
||||
points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array
|
||||
points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
|
||||
points = numpy.asarray(points, dtype=numpy.float32)
|
||||
points /= 1000
|
||||
points = numpy.insert(points, 1, layer.id * layerHeight, axis = 1)
|
||||
points = numpy.insert(points, 1, (layer.height / 1000), axis = 1)
|
||||
|
||||
points[:,2] *= -1
|
||||
|
||||
if not settings.getSettingValueByKey("machine_center_is_zero"):
|
||||
center = [settings.getSettingValueByKey("machine_width") / 2, 0.0, -settings.getSettingValueByKey("machine_depth") / 2]
|
||||
points -= numpy.array(center)
|
||||
points -= numpy.array(center)
|
||||
|
||||
#points = numpy.pad(points, ((0,0), (0,1)), "constant", constant_values=(0.0, 1.0))
|
||||
#inverse = node.getWorldTransformation().getInverse().getData()
|
||||
#points = points.dot(inverse)
|
||||
#points = points[:,0:3]
|
||||
layerData.addPolygon(layer.id, polygon.type, points, polygon.line_width)
|
||||
|
||||
layerData.addPolygon(layer.id, polygon.type, points)
|
||||
if self._progress:
|
||||
self._progress.setProgress(50)
|
||||
|
||||
# We are done processing all the layers we got from the engine, now create a mesh out of the data
|
||||
layerData.build()
|
||||
mesh.layerData = layerData
|
||||
|
||||
if self._progress:
|
||||
self._progress.setProgress(100)
|
||||
|
||||
# We are done processing all the layers we got from the engine, now create a mesh out of the data
|
||||
layerData.build()
|
||||
mesh.layerData = layerData
|
||||
|
||||
new_node.setMeshData(mesh)
|
||||
new_node.setParent(self._scene.getRoot())
|
||||
|
||||
view = Application.getInstance().getController().getActiveView()
|
||||
if view.getPluginId() == "LayerView":
|
||||
view.resetLayerData()
|
||||
|
||||
if self._progress:
|
||||
self._progress.hide()
|
||||
|
||||
def _onActiveViewChanged(self):
|
||||
if self.isRunning():
|
||||
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
||||
if not self._progress:
|
||||
self._progress = Message(catalog.i18nc("Layers View mode", "Layers"), 0, False, 0)
|
||||
self._progress.show()
|
||||
else:
|
||||
if self._progress:
|
||||
self._progress.hide()
|
||||
|
||||
|
@ -5,6 +5,15 @@ from UM.View.View import View
|
||||
from UM.View.Renderer import Renderer
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Resources import Resources
|
||||
from UM.Event import Event, KeyEvent
|
||||
from UM.Signal import Signal
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Math.Color import Color
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
|
||||
from cura.ConvexHullNode import ConvexHullNode
|
||||
|
||||
from . import LayerViewProxy
|
||||
|
||||
## View used to display g-code paths.
|
||||
class LayerView(View):
|
||||
@ -13,6 +22,25 @@ class LayerView(View):
|
||||
self._material = None
|
||||
self._num_layers = 0
|
||||
self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100)
|
||||
self._proxy = LayerViewProxy.LayerViewProxy()
|
||||
self._controller.getScene().sceneChanged.connect(self._onSceneChanged)
|
||||
self._max_layers = 10
|
||||
self._current_layer_num = 10
|
||||
self._current_layer_mesh = None
|
||||
|
||||
self._solid_layers = 5
|
||||
|
||||
def getCurrentLayer(self):
|
||||
return self._current_layer_num
|
||||
|
||||
def _onSceneChanged(self, node):
|
||||
self.calculateMaxLayers()
|
||||
|
||||
def getMaxLayers(self):
|
||||
return self._max_layers
|
||||
|
||||
def resetLayerData(self):
|
||||
self._current_layer_mesh = None
|
||||
|
||||
def beginRendering(self):
|
||||
scene = self.getController().getScene()
|
||||
@ -23,32 +51,110 @@ class LayerView(View):
|
||||
self._material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "vertexcolor.frag"))
|
||||
self._material.setUniformValue("u_color", [1.0, 0.0, 0.0, 1.0])
|
||||
|
||||
self._selection_material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "color.frag"))
|
||||
self._selection_material.setUniformValue("u_color", Color(35, 35, 35, 128))
|
||||
|
||||
for node in DepthFirstIterator(scene.getRoot()):
|
||||
# We do not want to render ConvexHullNode as it conflicts with the bottom layers.
|
||||
# However, it is somewhat relevant when the node is selected, so do render it then.
|
||||
if type(node) is ConvexHullNode and not Selection.isSelected(node.getWatchedNode()):
|
||||
continue
|
||||
|
||||
if not node.render(renderer):
|
||||
if node.getMeshData() and node.isVisible():
|
||||
if Selection.isSelected(node):
|
||||
renderer.queueNode(node, material = self._selection_material, transparent = True)
|
||||
|
||||
try:
|
||||
layer_data = node.getMeshData().layerData
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
if self._layer_percentage < 100:
|
||||
# Render all layers below a certain number as line mesh instead of vertices.
|
||||
if self._current_layer_num - self._solid_layers > -1:
|
||||
start = 0
|
||||
end_layer = round(len(layer_data.getLayers()) * (self._layer_percentage / 100))
|
||||
end = 0
|
||||
|
||||
element_counts = layer_data.getElementCounts()
|
||||
for layer, counts in element_counts.items():
|
||||
end += sum(counts)
|
||||
|
||||
if layer >= end_layer:
|
||||
if layer + self._solid_layers > self._current_layer_num:
|
||||
break
|
||||
end += counts
|
||||
|
||||
# This uses glDrawRangeElements internally to only draw a certain range of lines.
|
||||
renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end)
|
||||
else:
|
||||
renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines)
|
||||
|
||||
|
||||
# We currently recreate the current "solid" layers every time a
|
||||
if not self._current_layer_mesh:
|
||||
self._current_layer_mesh = MeshData()
|
||||
for i in range(self._solid_layers):
|
||||
layer = self._current_layer_num - i
|
||||
if layer < 0:
|
||||
continue
|
||||
|
||||
layer_mesh = layer_data.getLayer(layer).createMesh()
|
||||
if not layer_mesh or layer_mesh.getVertices() is None:
|
||||
continue
|
||||
|
||||
self._current_layer_mesh.addVertices(layer_mesh.getVertices())
|
||||
|
||||
# Scale layer color by a brightness factor based on the current layer number
|
||||
# This will result in a range of 0.5 - 1.0 to multiply colors by.
|
||||
brightness = (2.0 - (i / self._solid_layers)) / 2.0
|
||||
self._current_layer_mesh.addColors(layer_mesh.getColors() * brightness)
|
||||
|
||||
renderer.queueNode(node, mesh = self._current_layer_mesh, material = self._material)
|
||||
|
||||
def setLayer(self, value):
|
||||
self._layer_percentage = value
|
||||
|
||||
if self._current_layer_num != value:
|
||||
self._current_layer_num = value
|
||||
if self._current_layer_num < 0:
|
||||
self._current_layer_num = 0
|
||||
if self._current_layer_num > self._max_layers:
|
||||
self._current_layer_num = self._max_layers
|
||||
|
||||
self._current_layer_mesh = None
|
||||
self.currentLayerNumChanged.emit()
|
||||
|
||||
currentLayerNumChanged = Signal()
|
||||
|
||||
def calculateMaxLayers(self):
|
||||
scene = self.getController().getScene()
|
||||
renderer = self.getRenderer()
|
||||
if renderer and self._material:
|
||||
renderer.setRenderSelection(False)
|
||||
self._old_max_layers = self._max_layers
|
||||
## Recalculate num max layers
|
||||
new_max_layers = 0
|
||||
for node in DepthFirstIterator(scene.getRoot()):
|
||||
if not node.render(renderer):
|
||||
if node.getMeshData() and node.isVisible():
|
||||
try:
|
||||
layer_data = node.getMeshData().layerData
|
||||
except AttributeError:
|
||||
continue
|
||||
if new_max_layers < len(layer_data.getLayers()):
|
||||
new_max_layers = len(layer_data.getLayers()) - 1
|
||||
|
||||
if new_max_layers > 0 and new_max_layers != self._old_max_layers:
|
||||
self._max_layers = new_max_layers
|
||||
self.maxLayersChanged.emit()
|
||||
|
||||
# This makes sure we update the current layer
|
||||
self.setLayer(int(self._max_layers * (self._current_layer_num / self._old_max_layers)))
|
||||
|
||||
maxLayersChanged = Signal()
|
||||
|
||||
## Hackish way to ensure the proxy is already created, which ensures that the layerview.qml is already created
|
||||
# as this caused some issues.
|
||||
def getProxy(self, engine, script_engine):
|
||||
return self._proxy
|
||||
|
||||
def endRendering(self):
|
||||
pass
|
||||
|
||||
def event(self, event):
|
||||
if event.type == Event.KeyPressEvent:
|
||||
if event.key == KeyEvent.UpKey:
|
||||
self.setLayer(self._current_layer_num + 1)
|
||||
if event.key == KeyEvent.DownKey:
|
||||
self.setLayer(self._current_layer_num - 1)
|
||||
|
@ -20,10 +20,11 @@ Item
|
||||
anchors.right : parent.right
|
||||
orientation: Qt.Vertical
|
||||
minimumValue: 0;
|
||||
maximumValue: 100;
|
||||
maximumValue: UM.LayerView.numLayers;
|
||||
stepSize: 1
|
||||
|
||||
value: 100;
|
||||
onValueChanged: UM.ActiveView.triggerAction("setLayer", value)
|
||||
value: UM.LayerView.currentLayer
|
||||
onValueChanged: UM.LayerView.setCurrentLayer(value)
|
||||
|
||||
style: UM.Theme.styles.slider;
|
||||
}
|
||||
|
44
plugins/LayerView/LayerViewProxy.py
Normal file
@ -0,0 +1,44 @@
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty
|
||||
from UM.Application import Application
|
||||
import LayerView
|
||||
class LayerViewProxy(QObject):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
self._current_layer = 0
|
||||
self._controller = Application.getInstance().getController()
|
||||
self._controller.activeViewChanged.connect(self._onActiveViewChanged)
|
||||
self._onActiveViewChanged()
|
||||
|
||||
currentLayerChanged = pyqtSignal()
|
||||
maxLayersChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(int, notify = maxLayersChanged)
|
||||
def numLayers(self):
|
||||
active_view = self._controller.getActiveView()
|
||||
#print("num max layers " , active_view.getMaxLayers())
|
||||
return active_view.getMaxLayers()
|
||||
#return 100
|
||||
|
||||
@pyqtProperty(int, notify = currentLayerChanged)
|
||||
def currentLayer(self):
|
||||
active_view = self._controller.getActiveView()
|
||||
if type(active_view) == LayerView.LayerView.LayerView:
|
||||
return active_view.getCurrentLayer()
|
||||
|
||||
@pyqtSlot(int)
|
||||
def setCurrentLayer(self, layer_num):
|
||||
active_view = self._controller.getActiveView()
|
||||
if type(active_view) == LayerView.LayerView.LayerView:
|
||||
active_view.setLayer(layer_num)
|
||||
|
||||
def _onLayerChanged(self):
|
||||
self.currentLayerChanged.emit()
|
||||
|
||||
def _onMaxLayersChanged(self):
|
||||
self.maxLayersChanged.emit()
|
||||
|
||||
def _onActiveViewChanged(self):
|
||||
active_view = self._controller.getActiveView()
|
||||
if type(active_view) == LayerView.LayerView.LayerView:
|
||||
active_view.currentLayerNumChanged.connect(self._onLayerChanged)
|
||||
active_view.maxLayersChanged.connect(self._onMaxLayersChanged)
|
@ -1,7 +1,8 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import LayerView
|
||||
from . import LayerView, LayerViewProxy
|
||||
from PyQt5.QtQml import qmlRegisterType, qmlRegisterSingletonType
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
@ -21,6 +22,10 @@ def getMetaData():
|
||||
}
|
||||
}
|
||||
|
||||
def createLayerViewProxy(engine, script_engine):
|
||||
return LayerViewProxy.LayerViewProxy()
|
||||
|
||||
def register(app):
|
||||
layer_view = LayerView.LayerView()
|
||||
qmlRegisterSingletonType(LayerViewProxy.LayerViewProxy, "UM", 1, 0, "LayerView", layer_view.getProxy)
|
||||
return { "view": LayerView.LayerView() }
|
||||
|
@ -173,6 +173,10 @@ class PrinterConnection(SignalEmitter):
|
||||
Logger.log("i", "Could not establish connection on %s: %s. Device is not arduino based." %(self._serial_port,str(e)))
|
||||
except Exception as e:
|
||||
Logger.log("i", "Could not establish connection on %s, unknown reasons. Device is not arduino based." % self._serial_port)
|
||||
|
||||
if not self._serial or not programmer.serial:
|
||||
self._is_connecting = False
|
||||
return
|
||||
|
||||
# If the programmer connected, we know its an atmega based version. Not all that usefull, but it does give some debugging information.
|
||||
for baud_rate in self._getBaudrateList(): # Cycle all baud rates (auto detect)
|
||||
@ -241,10 +245,17 @@ class PrinterConnection(SignalEmitter):
|
||||
## Close the printer connection
|
||||
def close(self):
|
||||
if self._connect_thread.isAlive():
|
||||
self._connect_thread.join()
|
||||
try:
|
||||
self._connect_thread.join()
|
||||
except Exception as e:
|
||||
pass # This should work, but it does fail sometimes for some reason
|
||||
|
||||
if self._serial is not None:
|
||||
self.setIsConnected(False)
|
||||
self._listen_thread.join()
|
||||
try:
|
||||
self._listen_thread.join()
|
||||
except:
|
||||
pass
|
||||
self._serial.close()
|
||||
|
||||
self._serial = None
|
||||
|
@ -19,8 +19,7 @@ from PyQt5.QtQuick import QQuickView
|
||||
from PyQt5.QtCore import QUrl, QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("uranium")
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
class USBPrinterManager(QObject, SignalEmitter, Extension):
|
||||
def __init__(self, parent = None):
|
||||
@ -40,6 +39,7 @@ class USBPrinterManager(QObject, SignalEmitter, Extension):
|
||||
self._error_message = ""
|
||||
|
||||
## Add menu item to top menu of the application.
|
||||
self.setMenuName("Firmware")
|
||||
self.addMenuItem(i18n_catalog.i18n("Update Firmware"), self.updateAllFirmware)
|
||||
|
||||
pyqtError = pyqtSignal(str, arguments = ["amount"])
|
||||
|
91
resources/i18n/ru/cura.po
Normal file
@ -0,0 +1,91 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-05-07 16:35+0200\n"
|
||||
"PO-Revision-Date: 2015-06-01 18:29+0300\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 1.8.1\n"
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:91
|
||||
msgctxt "Save button tooltip"
|
||||
msgid "Save to Disk"
|
||||
msgstr "Сохранить на диск"
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:96
|
||||
msgctxt "Splash screen message"
|
||||
msgid "Setting up scene..."
|
||||
msgstr "Настройка сцены ..."
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:130
|
||||
msgctxt "Splash screen message"
|
||||
msgid "Loading interface..."
|
||||
msgstr "Загрузка интерфейса ..."
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:373
|
||||
#, python-brace-format
|
||||
msgctxt "Save button tooltip. {0} is sd card name"
|
||||
msgid "Save to SD Card {0}"
|
||||
msgstr "Сохранить на SD карту {0}"
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:421
|
||||
#, python-brace-format
|
||||
msgctxt "Saved to SD message, {0} is sdcard, {1} is filename"
|
||||
msgid "Saved to SD Card {0} as {1}"
|
||||
msgstr "Сохранено на SD карту {0} как {1}"
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:424
|
||||
msgctxt "Message action"
|
||||
msgid "Eject"
|
||||
msgstr "Извлечь"
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:426
|
||||
#, python-brace-format
|
||||
msgctxt "Message action tooltip, {0} is sdcard"
|
||||
msgid "Eject SD Card {0}"
|
||||
msgstr "Извлечь SD карту {0}"
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/CuraEngineBackend/__init__.py:13
|
||||
msgctxt "CuraEngine backend plugin description"
|
||||
msgid "Provides the link to the CuraEngine slicing backend"
|
||||
msgstr "Обеспечивает связь с Кура слайсинг сервером"
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/USBPrinting/USBPrinterManager.py:40
|
||||
msgid "Update Firmware"
|
||||
msgstr "Обновление прошивки"
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/USBPrinting/__init__.py:13
|
||||
msgctxt "USB Printing plugin description"
|
||||
msgid "Accepts G-Code and sends them to a printer. Plugin can also update firmware"
|
||||
msgstr "Принимает G-коды и отправляет их на принтер. Плагин также может обновить прошивку."
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/GCodeWriter/__init__.py:13
|
||||
msgctxt "GCode Writer Plugin Description"
|
||||
msgid "Writes GCode to a file"
|
||||
msgstr "Записывает G-код в файл "
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/GCodeWriter/__init__.py:18
|
||||
msgctxt "GCode Writer File Description"
|
||||
msgid "GCode File"
|
||||
msgstr "G-код"
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/LayerView/__init__.py:13
|
||||
msgctxt "Layer View plugin description"
|
||||
msgid "Provides the Layer view."
|
||||
msgstr "Обеспечивает просмотр слоев."
|
||||
|
||||
#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/LayerView/__init__.py:16
|
||||
msgctxt "Layers View mode"
|
||||
msgid "Layers"
|
||||
msgstr "Слой"
|
430
resources/i18n/ru/cura_qt.po
Normal file
@ -0,0 +1,430 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Qt-Contexts: true\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"Project-Id-Version: Cura\n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: ru\n"
|
||||
"X-Generator: Poedit 1.8.1\n"
|
||||
|
||||
#. About dialog title
|
||||
#: ../resources/qml/AboutDialog.qml:12
|
||||
msgctxt "AboutDialog|"
|
||||
msgid "About Cura"
|
||||
msgstr "О Кура"
|
||||
|
||||
#. About dialog application description
|
||||
#: ../resources/qml/AboutDialog.qml:42
|
||||
msgctxt "AboutDialog|"
|
||||
msgid "End-to-end solution for fused filament 3D printing."
|
||||
msgstr "Комплексное решение для 3D печати по технологии послойного накладывание пластика."
|
||||
|
||||
#. About dialog application author note
|
||||
#: ../resources/qml/AboutDialog.qml:47
|
||||
msgctxt "AboutDialog|"
|
||||
msgid "Cura has been developed by Ultimaker B.V. in cooperation with the community."
|
||||
msgstr "Кура была разработана компании Ultimaker BV в сотрудничестве с сообществом."
|
||||
|
||||
#. Close about dialog button
|
||||
#: ../resources/qml/AboutDialog.qml:58
|
||||
msgctxt "AboutDialog|"
|
||||
msgid "Close"
|
||||
msgstr "Закрыть окно"
|
||||
|
||||
#. Undo action
|
||||
#: ../resources/qml/Actions.qml:37
|
||||
msgctxt "Actions|"
|
||||
msgid "&Undo"
|
||||
msgstr "&Отмена"
|
||||
|
||||
#. Redo action
|
||||
#: ../resources/qml/Actions.qml:45
|
||||
msgctxt "Actions|"
|
||||
msgid "&Redo"
|
||||
msgstr "&Повтор"
|
||||
|
||||
#. Quit action
|
||||
#: ../resources/qml/Actions.qml:53
|
||||
msgctxt "Actions|"
|
||||
msgid "&Quit"
|
||||
msgstr "&Выход"
|
||||
|
||||
#. Preferences action
|
||||
#: ../resources/qml/Actions.qml:61
|
||||
msgctxt "Actions|"
|
||||
msgid "&Preferences..."
|
||||
msgstr "&Настройки программы..."
|
||||
|
||||
#. Add Printer action
|
||||
#: ../resources/qml/Actions.qml:68
|
||||
msgctxt "Actions|"
|
||||
msgid "&Add Printer..."
|
||||
msgstr "&Добавит новый принтер"
|
||||
|
||||
#. Configure Printers action
|
||||
#: ../resources/qml/Actions.qml:74
|
||||
msgctxt "Actions|"
|
||||
msgid "&Configure Printers"
|
||||
msgstr "&Настройки принтера"
|
||||
|
||||
#. Show Online Documentation action
|
||||
#: ../resources/qml/Actions.qml:81
|
||||
msgctxt "Actions|"
|
||||
msgid "Show Online &Documentation"
|
||||
msgstr "Показать онлайн &справку"
|
||||
|
||||
#. Report a Bug Action
|
||||
#: ../resources/qml/Actions.qml:89
|
||||
msgctxt "Actions|"
|
||||
msgid "Report a &Bug"
|
||||
msgstr "Сообщить о &проблеме"
|
||||
|
||||
#. About action
|
||||
#: ../resources/qml/Actions.qml:96
|
||||
msgctxt "Actions|"
|
||||
msgid "&About..."
|
||||
msgstr "&О Кура"
|
||||
|
||||
#. Delete selection action
|
||||
#: ../resources/qml/Actions.qml:103
|
||||
msgctxt "Actions|"
|
||||
msgid "Delete Selection"
|
||||
msgstr "Удалить выбранное"
|
||||
|
||||
#. Delete object action
|
||||
#: ../resources/qml/Actions.qml:111
|
||||
msgctxt "Actions|"
|
||||
msgid "Delete Object"
|
||||
msgstr "Удалить объект"
|
||||
|
||||
#. Center object action
|
||||
#: ../resources/qml/Actions.qml:118
|
||||
msgctxt "Actions|"
|
||||
msgid "Center Object on Platform"
|
||||
msgstr "Расположить объект по центру"
|
||||
|
||||
#. Duplicate object action
|
||||
#: ../resources/qml/Actions.qml:124
|
||||
msgctxt "Actions|"
|
||||
msgid "Duplicate Object"
|
||||
msgstr "Умножить объект"
|
||||
|
||||
#. Split object action
|
||||
#: ../resources/qml/Actions.qml:130
|
||||
msgctxt "Actions|"
|
||||
msgid "Split Object into Parts"
|
||||
msgstr "Разбить объект на частей"
|
||||
|
||||
#. Clear build platform action
|
||||
#: ../resources/qml/Actions.qml:137
|
||||
msgctxt "Actions|"
|
||||
msgid "Clear Build Platform"
|
||||
msgstr "Очистить печатающую платформу"
|
||||
|
||||
#. Reload all objects action
|
||||
#: ../resources/qml/Actions.qml:144
|
||||
msgctxt "Actions|"
|
||||
msgid "Reload All Objects"
|
||||
msgstr "Перезагрузить все объекты"
|
||||
|
||||
#. Reset all positions action
|
||||
#: ../resources/qml/Actions.qml:150
|
||||
msgctxt "Actions|"
|
||||
msgid "Reset All Object Positions"
|
||||
msgstr "Сбросить все позиции объектов"
|
||||
|
||||
#. Reset all positions action
|
||||
#: ../resources/qml/Actions.qml:156
|
||||
msgctxt "Actions|"
|
||||
msgid "Reset All Object Transformations"
|
||||
msgstr "Сбросить все преобразования объектов"
|
||||
|
||||
#. Open file action
|
||||
#: ../resources/qml/Actions.qml:162
|
||||
msgctxt "Actions|"
|
||||
msgid "&Open..."
|
||||
msgstr "&Открыть..."
|
||||
|
||||
#. Save file action
|
||||
#: ../resources/qml/Actions.qml:170
|
||||
msgctxt "Actions|"
|
||||
msgid "&Save..."
|
||||
msgstr "&Сохранить..."
|
||||
|
||||
#. Show engine log action
|
||||
#: ../resources/qml/Actions.qml:178
|
||||
msgctxt "Actions|"
|
||||
msgid "Show engine &log..."
|
||||
msgstr "Показать журнал &слайсера"
|
||||
|
||||
#. Add Printer dialog title
|
||||
#. ----------
|
||||
#. Add Printer wizard page title
|
||||
#: ../resources/qml/AddMachineWizard.qml:12
|
||||
#: ../resources/qml/AddMachineWizard.qml:19
|
||||
msgctxt "AddMachineWizard|"
|
||||
msgid "Add Printer"
|
||||
msgstr "Добавить новый принтер"
|
||||
|
||||
#. Add Printer wizard page description
|
||||
#: ../resources/qml/AddMachineWizard.qml:25
|
||||
msgctxt "AddMachineWizard|"
|
||||
msgid "Please select the type of printer:"
|
||||
msgstr "Выберите тип принтера:"
|
||||
|
||||
#. Add Printer wizard field label
|
||||
#: ../resources/qml/AddMachineWizard.qml:40
|
||||
msgctxt "AddMachineWizard|"
|
||||
msgid "Printer Name:"
|
||||
msgstr "Название принтера:"
|
||||
|
||||
#. Add Printer wizarad button
|
||||
#: ../resources/qml/AddMachineWizard.qml:53
|
||||
msgctxt "AddMachineWizard|"
|
||||
msgid "Next"
|
||||
msgstr "Дальше"
|
||||
|
||||
#. Add Printer wizarad button
|
||||
#: ../resources/qml/AddMachineWizard.qml:63
|
||||
msgctxt "AddMachineWizard|"
|
||||
msgid "Cancel"
|
||||
msgstr "Отменить"
|
||||
|
||||
#. USB Printing dialog label, %1 is head temperature
|
||||
#: ../plugins/USBPrinting/ControlWindow.qml:15
|
||||
#, qt-format
|
||||
msgctxt "ControlWindow|"
|
||||
msgid "Extruder Temperature %1"
|
||||
msgstr "Температура экструдера %1"
|
||||
|
||||
#. USB Printing dialog label, %1 is bed temperature
|
||||
#: ../plugins/USBPrinting/ControlWindow.qml:20
|
||||
#, qt-format
|
||||
msgctxt "ControlWindow|"
|
||||
msgid "Bed Temperature %1"
|
||||
msgstr "Температура платформы %1"
|
||||
|
||||
#. USB Printing dialog start print button
|
||||
#: ../plugins/USBPrinting/ControlWindow.qml:33
|
||||
msgctxt "ControlWindow|"
|
||||
msgid "Print"
|
||||
msgstr "Печать"
|
||||
|
||||
#. USB Printing dialog cancel print button
|
||||
#: ../plugins/USBPrinting/ControlWindow.qml:40
|
||||
msgctxt "ControlWindow|"
|
||||
msgid "Cancel"
|
||||
msgstr "Отменить"
|
||||
|
||||
#. Cura application window title
|
||||
#: ../resources/qml/Cura.qml:14
|
||||
msgctxt "Cura|"
|
||||
msgid "Cura"
|
||||
msgstr "Кура"
|
||||
|
||||
#. File menu
|
||||
#: ../resources/qml/Cura.qml:26
|
||||
msgctxt "Cura|"
|
||||
msgid "&File"
|
||||
msgstr "&Файл"
|
||||
|
||||
#. Edit menu
|
||||
#: ../resources/qml/Cura.qml:38
|
||||
msgctxt "Cura|"
|
||||
msgid "&Edit"
|
||||
msgstr "&Изменить"
|
||||
|
||||
#. Machine menu
|
||||
#: ../resources/qml/Cura.qml:50
|
||||
msgctxt "Cura|"
|
||||
msgid "&Machine"
|
||||
msgstr "&Принтер"
|
||||
|
||||
#. Extensions menu
|
||||
#: ../resources/qml/Cura.qml:76
|
||||
msgctxt "Cura|"
|
||||
msgid "E&xtensions"
|
||||
msgstr "&Расширения"
|
||||
|
||||
#. Settings menu
|
||||
#: ../resources/qml/Cura.qml:107
|
||||
msgctxt "Cura|"
|
||||
msgid "&Settings"
|
||||
msgstr "&Настройки"
|
||||
|
||||
#. Help menu
|
||||
#: ../resources/qml/Cura.qml:114
|
||||
msgctxt "Cura|"
|
||||
msgid "&Help"
|
||||
msgstr "&Помощь"
|
||||
|
||||
#. View Mode toolbar button
|
||||
#: ../resources/qml/Cura.qml:231
|
||||
msgctxt "Cura|"
|
||||
msgid "View Mode"
|
||||
msgstr "Режим просмотра"
|
||||
|
||||
#. View preferences page title
|
||||
#: ../resources/qml/Cura.qml:273
|
||||
msgctxt "Cura|"
|
||||
msgid "View"
|
||||
msgstr "Просмотр"
|
||||
|
||||
#. File open dialog title
|
||||
#: ../resources/qml/Cura.qml:370
|
||||
msgctxt "Cura|"
|
||||
msgid "Open File"
|
||||
msgstr "Открыть файл"
|
||||
|
||||
#. File save dialog title
|
||||
#: ../resources/qml/Cura.qml:386
|
||||
msgctxt "Cura|"
|
||||
msgid "Save File"
|
||||
msgstr "Сохранить файл"
|
||||
|
||||
#. Engine Log dialog title
|
||||
#: ../resources/qml/EngineLog.qml:11
|
||||
msgctxt "EngineLog|"
|
||||
msgid "Engine Log"
|
||||
msgstr "Журнал слайсера"
|
||||
|
||||
#. Close engine log button
|
||||
#: ../resources/qml/EngineLog.qml:30
|
||||
msgctxt "EngineLog|"
|
||||
msgid "Close"
|
||||
msgstr "Закрыть"
|
||||
|
||||
#. Firmware update status label
|
||||
#: ../plugins/USBPrinting/FirmwareUpdateWindow.qml:16
|
||||
msgctxt "FirmwareUpdateWindow|"
|
||||
msgid "Starting firmware update, this may take a while."
|
||||
msgstr "Начинается обновление прошивки, это может занять некоторое время."
|
||||
|
||||
#. Firmware update status label
|
||||
#: ../plugins/USBPrinting/FirmwareUpdateWindow.qml:21
|
||||
msgctxt "FirmwareUpdateWindow|"
|
||||
msgid "Firmware update completed."
|
||||
msgstr "Обновление прошивки завершено."
|
||||
|
||||
#. Firmware update status label
|
||||
#: ../plugins/USBPrinting/FirmwareUpdateWindow.qml:26
|
||||
msgctxt "FirmwareUpdateWindow|"
|
||||
msgid "Updating firmware."
|
||||
msgstr "Обновление прошивки."
|
||||
|
||||
#. Print material amount save button label
|
||||
#: ../resources/qml/SaveButton.qml:149
|
||||
#, qt-format
|
||||
msgctxt "SaveButton|"
|
||||
msgid "%1m material"
|
||||
msgstr "%1m материал"
|
||||
|
||||
#. Save button label
|
||||
#: ../resources/qml/SaveButton.qml:191
|
||||
msgctxt "SaveButton|"
|
||||
msgid "Please load a 3D model"
|
||||
msgstr "Пожалуйста, загрузите 3D модель"
|
||||
|
||||
#. Save button label
|
||||
#: ../resources/qml/SaveButton.qml:194
|
||||
msgctxt "SaveButton|"
|
||||
msgid "Calculating Print-time"
|
||||
msgstr "Вычисляется время печати"
|
||||
|
||||
#. Save button label
|
||||
#: ../resources/qml/SaveButton.qml:197
|
||||
msgctxt "SaveButton|"
|
||||
msgid "Estimated Print-time"
|
||||
msgstr "Расчетное время печати"
|
||||
|
||||
#. Simple configuration mode option
|
||||
#: ../resources/qml/Sidebar.qml:105
|
||||
msgctxt "Sidebar|"
|
||||
msgid "Simple"
|
||||
msgstr "Простой"
|
||||
|
||||
#. Advanced configuration mode option
|
||||
#: ../resources/qml/Sidebar.qml:107
|
||||
msgctxt "Sidebar|"
|
||||
msgid "Advanced"
|
||||
msgstr "Продвинутый"
|
||||
|
||||
#. Configuration mode label
|
||||
#: ../resources/qml/SidebarHeader.qml:26
|
||||
msgctxt "SidebarHeader|"
|
||||
msgid "Mode:"
|
||||
msgstr "Режим:"
|
||||
|
||||
#. Machine selection label
|
||||
#: ../resources/qml/SidebarHeader.qml:70
|
||||
msgctxt "SidebarHeader|"
|
||||
msgid "Machine:"
|
||||
msgstr "Принтер:"
|
||||
|
||||
#. Sidebar header label
|
||||
#: ../resources/qml/SidebarHeader.qml:117
|
||||
msgctxt "SidebarHeader|"
|
||||
msgid "Print Setup"
|
||||
msgstr "Настройка Печати"
|
||||
|
||||
#. Sidebar configuration label
|
||||
#: ../resources/qml/SidebarSimple.qml:40
|
||||
msgctxt "SidebarSimple|"
|
||||
msgid "No Model Loaded"
|
||||
msgstr "Модель не загружена"
|
||||
|
||||
#. Sidebar configuration label
|
||||
#: ../resources/qml/SidebarSimple.qml:45
|
||||
msgctxt "SidebarSimple|"
|
||||
msgid "Calculating..."
|
||||
msgstr "Высчитывается..."
|
||||
|
||||
#. Sidebar configuration label
|
||||
#: ../resources/qml/SidebarSimple.qml:50
|
||||
msgctxt "SidebarSimple|"
|
||||
msgid "Estimated Print Time"
|
||||
msgstr "Расчетное время печати"
|
||||
|
||||
#. Quality slider label
|
||||
#: ../resources/qml/SidebarSimple.qml:87
|
||||
msgctxt "SidebarSimple|"
|
||||
msgid ""
|
||||
"Minimum\n"
|
||||
"Draft"
|
||||
msgstr ""
|
||||
"Минимальная \n"
|
||||
"тяга"
|
||||
|
||||
#. Quality slider label
|
||||
#: ../resources/qml/SidebarSimple.qml:97
|
||||
msgctxt "SidebarSimple|"
|
||||
msgid ""
|
||||
"Maximum\n"
|
||||
"Quality"
|
||||
msgstr ""
|
||||
"Максимальное\n"
|
||||
"Качество"
|
||||
|
||||
#. Setting checkbox
|
||||
#: ../resources/qml/SidebarSimple.qml:109
|
||||
msgctxt "SidebarSimple|"
|
||||
msgid "Enable Support"
|
||||
msgstr "Включить Поддержку"
|
||||
|
||||
#. View configuration page title
|
||||
#: ../resources/qml/ViewPage.qml:9
|
||||
msgctxt "ViewPage|"
|
||||
msgid "View"
|
||||
msgstr "Просмотр"
|
||||
|
||||
#. Display Overhang preference checkbox
|
||||
#: ../resources/qml/ViewPage.qml:24
|
||||
msgctxt "ViewPage|"
|
||||
msgid "Display Overhang"
|
||||
msgstr "Показать где есть нависающие части"
|
1843
resources/i18n/ru/fdmprinter.json.po
Normal file
BIN
resources/images/cura-icon.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
@ -3,7 +3,6 @@
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.0 as UM
|
||||
@ -12,48 +11,53 @@ UM.Dialog {
|
||||
id: base
|
||||
|
||||
//: About dialog title
|
||||
title: qsTr("About Cura");
|
||||
title: qsTr("About Cura")
|
||||
minimumWidth: 400
|
||||
minimumHeight: 300
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent;
|
||||
Image {
|
||||
id: logo
|
||||
width: parent.width * 0.75
|
||||
height: width * (1/4.25)
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true;
|
||||
Layout.fillHeight: true;
|
||||
}
|
||||
source: UM.Theme.images.logo
|
||||
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignHCenter;
|
||||
Layout.preferredWidth: parent.width * 0.75;
|
||||
Layout.preferredHeight: width * (1/4.25);
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset : -(height * 0.5)
|
||||
}
|
||||
|
||||
source: UM.Theme.images.logo;
|
||||
Label {
|
||||
id: version
|
||||
|
||||
sourceSize.width: width;
|
||||
sourceSize.height: height;
|
||||
}
|
||||
text: "Cura 15.06 Beta"
|
||||
font: UM.Theme.fonts.large
|
||||
anchors.horizontalCenter : logo.horizontalCenter
|
||||
anchors.horizontalCenterOffset : (logo.width * 0.25)
|
||||
anchors.top: logo.bottom
|
||||
anchors.topMargin : 5
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignHCenter;
|
||||
Label {
|
||||
id: description
|
||||
width: parent.width
|
||||
|
||||
text: "Cura 15.06 Beta";
|
||||
font: UM.Theme.fonts.large;
|
||||
}
|
||||
//: About dialog application description
|
||||
text: qsTr("End-to-end solution for fused filament 3D printing.")
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.top: version.bottom
|
||||
anchors.topMargin : 10
|
||||
}
|
||||
|
||||
Label {
|
||||
//: About dialog application description
|
||||
text: qsTr("End-to-end solution for fused filament 3D printing.")
|
||||
}
|
||||
Label {
|
||||
id: author_note
|
||||
width: parent.width
|
||||
|
||||
Label {
|
||||
//: About dialog application author note
|
||||
text: qsTr("Cura has been developed by Ultimaker B.V. in cooperation with the community.")
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true;
|
||||
Layout.fillHeight: true;
|
||||
}
|
||||
//: About dialog application author note
|
||||
text: qsTr("Cura has been developed by Ultimaker B.V. in cooperation with the community.")
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.top: description.bottom
|
||||
}
|
||||
|
||||
rightButtons: Button {
|
||||
|
@ -25,6 +25,7 @@ UM.MainWindow {
|
||||
window: base
|
||||
|
||||
Menu {
|
||||
id: fileMenu
|
||||
//: File menu
|
||||
title: qsTr("&File");
|
||||
|
||||
@ -33,6 +34,21 @@ UM.MainWindow {
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
Instantiator {
|
||||
model: Printer.recentFiles
|
||||
MenuItem {
|
||||
text: {
|
||||
var path = modelData.toString()
|
||||
return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
onTriggered: UM.MeshFileHandler.readLocalFile(modelData);
|
||||
}
|
||||
onObjectAdded: fileMenu.insertItem(index, object)
|
||||
onObjectRemoved: fileMenu.removeItem(object)
|
||||
}
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
MenuItem { action: actions.quit; }
|
||||
}
|
||||
|
||||
@ -144,23 +160,6 @@ UM.MainWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Sidebar {
|
||||
id: sidebar;
|
||||
|
||||
anchors {
|
||||
top: parent.top;
|
||||
bottom: parent.bottom;
|
||||
right: parent.right;
|
||||
rightMargin: UM.Theme.sizes.window_margin.width;
|
||||
}
|
||||
|
||||
width: UM.Theme.sizes.panel.width;
|
||||
|
||||
addMachineAction: actions.addMachine;
|
||||
configureMachinesAction: actions.configureMachines;
|
||||
saveAction: actions.save;
|
||||
}
|
||||
|
||||
UM.MessageStack {
|
||||
anchors {
|
||||
left: toolbar.right;
|
||||
@ -195,8 +194,8 @@ UM.MainWindow {
|
||||
id: openFileButton;
|
||||
|
||||
iconSource: UM.Theme.icons.open;
|
||||
style: UM.Theme.styles.tool_button;
|
||||
|
||||
style: UM.Backend.progress < 0 ? UM.Theme.styles.open_file_button : UM.Theme.styles.tool_button;
|
||||
tooltip: '';
|
||||
anchors {
|
||||
top: parent.top;
|
||||
topMargin: UM.Theme.sizes.window_margin.height;
|
||||
@ -235,7 +234,7 @@ UM.MainWindow {
|
||||
iconSource: UM.Theme.icons.viewmode;
|
||||
|
||||
style: UM.Theme.styles.tool_button;
|
||||
|
||||
tooltip: '';
|
||||
menu: Menu {
|
||||
id: viewMenu;
|
||||
Instantiator {
|
||||
@ -265,6 +264,35 @@ UM.MainWindow {
|
||||
bottomMargin: UM.Theme.sizes.window_margin.height;
|
||||
}
|
||||
}
|
||||
|
||||
Sidebar {
|
||||
id: sidebar;
|
||||
|
||||
anchors {
|
||||
top: parent.top;
|
||||
bottom: parent.bottom;
|
||||
right: parent.right;
|
||||
}
|
||||
|
||||
width: UM.Theme.sizes.panel.width;
|
||||
|
||||
addMachineAction: actions.addMachine;
|
||||
configureMachinesAction: actions.configureMachines;
|
||||
saveAction: actions.save;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
x: base.mouseX + UM.Theme.sizes.default_margin.width;
|
||||
y: base.mouseY + UM.Theme.sizes.default_margin.height;
|
||||
|
||||
width: childrenRect.width;
|
||||
height: childrenRect.height;
|
||||
Label {
|
||||
text: UM.ActiveTool.properties.Rotation != undefined ? "%1°".arg(UM.ActiveTool.properties.Rotation) : "";
|
||||
}
|
||||
|
||||
visible: UM.ActiveTool.valid && UM.ActiveTool.properties.Rotation != undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,8 +351,8 @@ UM.MainWindow {
|
||||
preferences.onTriggered: preferences.visible = true;
|
||||
configureMachines.onTriggered: { preferences.visible = true; preferences.setPage(2); }
|
||||
|
||||
documentation.onTriggered: Qt.openUrlExternally("https://ultimaker.com/en/support");
|
||||
reportBug.onTriggered: Qt.openUrlExternally("https://github.com/Ultimaker/Cura/issues");
|
||||
documentation.onTriggered: CuraActions.openDocumentation();
|
||||
reportBug.onTriggered: CuraActions.openBugReportPage();
|
||||
showEngineLog.onTriggered: engineLog.visible = true;
|
||||
about.onTriggered: aboutDialog.visible = true;
|
||||
}
|
||||
@ -419,3 +447,4 @@ UM.MainWindow {
|
||||
|
||||
Component.onCompleted: UM.Theme.load(UM.Resources.getPath(UM.Resources.ThemesLocation, "cura"))
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import QtQuick.Layouts 1.1
|
||||
|
||||
import UM 1.0 as UM
|
||||
|
||||
Button {
|
||||
Rectangle {
|
||||
id: base;
|
||||
|
||||
property Action saveAction;
|
||||
@ -16,8 +16,6 @@ Button {
|
||||
property real progress: UM.Backend.progress;
|
||||
Behavior on progress { NumberAnimation { duration: 250; } }
|
||||
|
||||
enabled: progress >= 0.95;
|
||||
|
||||
property string currentDevice: "local_file"
|
||||
property bool defaultOverride: false;
|
||||
property bool defaultAmbiguous: false;
|
||||
@ -25,9 +23,6 @@ Button {
|
||||
property variant printDuration: PrintInformation.currentPrintTime;
|
||||
property real printMaterialAmount: PrintInformation.materialAmount;
|
||||
|
||||
iconSource: UM.Theme.icons[Printer.outputDevices[base.currentDevice].icon];
|
||||
tooltip: Printer.outputDevices[base.currentDevice].description;
|
||||
|
||||
Connections {
|
||||
target: Printer;
|
||||
onOutputDevicesChanged: {
|
||||
@ -51,161 +46,183 @@ Button {
|
||||
}
|
||||
}
|
||||
|
||||
style: ButtonStyle {
|
||||
background: UM.AngledCornerRectangle {
|
||||
implicitWidth: control.width;
|
||||
implicitHeight: control.height;
|
||||
Rectangle{
|
||||
id: background
|
||||
implicitWidth: base.width;
|
||||
implicitHeight: parent.height;
|
||||
color: UM.Theme.colors.save_button_background;
|
||||
border.width: UM.Theme.sizes.save_button_border.width
|
||||
border.color: UM.Theme.colors.save_button_border
|
||||
|
||||
color: UM.Theme.colors.save_button_border;
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
Rectangle {
|
||||
id: infoBox
|
||||
width: parent.width - UM.Theme.sizes.default_margin.width * 2;
|
||||
height: UM.Theme.sizes.save_button_slicing_bar.height
|
||||
|
||||
UM.AngledCornerRectangle {
|
||||
anchors.fill: parent;
|
||||
anchors.margins: UM.Theme.sizes.save_button_border.width;
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
color: UM.Theme.colors.save_button;
|
||||
}
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: UM.Theme.sizes.default_margin.height;
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.sizes.default_margin.width;
|
||||
|
||||
UM.AngledCornerRectangle {
|
||||
anchors {
|
||||
left: parent.left;
|
||||
top: parent.top;
|
||||
bottom: parent.bottom;
|
||||
}
|
||||
|
||||
width: Math.max(parent.height + (parent.width - parent.height) * control.progress, parent.height);
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
|
||||
color: !control.enabled ? UM.Theme.colors.save_button_inactive : control.hovered ? UM.Theme.colors.save_button_active_hover : UM.Theme.colors.save_button_active;
|
||||
Behavior on color { ColorAnimation { duration: 50; } }
|
||||
}
|
||||
|
||||
UM.AngledCornerRectangle {
|
||||
anchors.left: parent.left;
|
||||
width: parent.height + UM.Theme.sizes.save_button_border.width;
|
||||
height: parent.height;
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
color: UM.Theme.colors.save_button;
|
||||
}
|
||||
|
||||
UM.AngledCornerRectangle {
|
||||
anchors.left: parent.left;
|
||||
width: parent.height + UM.Theme.sizes.save_button_border.width;
|
||||
height: parent.height;
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
|
||||
color: UM.Theme.colors.save_button;
|
||||
}
|
||||
|
||||
UM.AngledCornerRectangle {
|
||||
id: icon;
|
||||
|
||||
anchors.left: parent.left;
|
||||
width: parent.height;
|
||||
height: parent.height;
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
color: !control.enabled ? UM.Theme.colors.save_button_inactive : control.hovered ? UM.Theme.colors.save_button_active_hover : UM.Theme.colors.save_button_active;
|
||||
Behavior on color { ColorAnimation { duration: 50; } }
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent;
|
||||
|
||||
width: UM.Theme.sizes.button_icon.width;
|
||||
height: UM.Theme.sizes.button_icon.height;
|
||||
|
||||
sourceSize.width: width;
|
||||
sourceSize.height: height;
|
||||
|
||||
source: control.iconSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label: Column {
|
||||
border.width: UM.Theme.sizes.save_button_border.width
|
||||
border.color: UM.Theme.colors.save_button_border
|
||||
color: UM.Theme.colors.save_button_estimated_text_background;
|
||||
Label {
|
||||
id: label;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: control.height + UM.Theme.sizes.save_button_label_margin.width;
|
||||
|
||||
color: UM.Theme.colors.save_button_text;
|
||||
font: UM.Theme.fonts.default;
|
||||
|
||||
text: control.text;
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width;
|
||||
visible: base.progress >= 0 && base.progress < 0.99 ? false : true
|
||||
color: UM.Theme.colors.save_button_estimated_text;
|
||||
font: UM.Theme.fonts.small;
|
||||
text:
|
||||
if(base.progress < 0) {
|
||||
//: Save button label
|
||||
return qsTr("Please load a 3D model");
|
||||
} else if (base.progress < 0.99) {
|
||||
//: Save button label
|
||||
return qsTr("Calculating Print-time");
|
||||
} else if (base.progress > 0.99){
|
||||
//: Save button label
|
||||
return qsTr("Estimated Print-time");
|
||||
} else if (base.progress == null){
|
||||
return qsTr("");
|
||||
}
|
||||
}
|
||||
Label {
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: control.height + UM.Theme.sizes.save_button_label_margin.width;
|
||||
|
||||
color: UM.Theme.colors.save_button_text;
|
||||
font: UM.Theme.fonts.default;
|
||||
|
||||
text: (!control.printDuration || !control.printDuration.valid) ? "" : control.printDuration.getDisplayString(UM.DurationFormat.Long)
|
||||
id: printDurationLabel
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: label.right;
|
||||
anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width;
|
||||
color: UM.Theme.colors.save_button_printtime_text;
|
||||
font: UM.Theme.fonts.small;
|
||||
visible: base.progress < 0.99 ? false : true
|
||||
text: (!base.printDuration || !base.printDuration.valid) ? "" : base.printDuration.getDisplayString(UM.DurationFormat.Long);
|
||||
}
|
||||
Label {
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: control.height + UM.Theme.sizes.save_button_label_margin.width;
|
||||
|
||||
color: UM.Theme.colors.save_button_text;
|
||||
font: UM.Theme.fonts.default;
|
||||
|
||||
id: printMaterialLabel
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: printDurationLabel.right;
|
||||
anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width;
|
||||
color: UM.Theme.colors.save_button_printtime_text;
|
||||
font: UM.Theme.fonts.small;
|
||||
visible: base.progress < 0.99 ? false : true
|
||||
//: Print material amount save button label
|
||||
text: control.printMaterialAmount < 0 ? "" : qsTr("%1m material").arg(control.printMaterialAmount);
|
||||
text: base.printMaterialAmount < 0 ? "" : qsTr("%1m material").arg(base.printMaterialAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: infoBoxOverlay
|
||||
anchors {
|
||||
left: infoBox.left;
|
||||
top: infoBox.top;
|
||||
bottom: infoBox.bottom;
|
||||
}
|
||||
width: Math.max(infoBox.width * base.progress);
|
||||
color: UM.Theme.colors.save_button_active
|
||||
visible: base.progress > 0.99 ? false : true
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
Button {
|
||||
id: saveToButton
|
||||
anchors.top: infoBox.bottom
|
||||
anchors.topMargin: UM.Theme.sizes.save_button_text_margin.height;
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.sizes.default_margin.width;
|
||||
tooltip: ''
|
||||
enabled: progress >= 0.99;
|
||||
|
||||
acceptedButtons: Qt.RightButton;
|
||||
|
||||
onClicked: devicesMenu.popup();
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: devicesMenu;
|
||||
|
||||
Instantiator {
|
||||
model: Printer.outputDeviceNames;
|
||||
MenuItem {
|
||||
text: Printer.outputDevices[modelData].description;
|
||||
checkable: true;
|
||||
checked: base.defaultAmbiguous ? false : modelData == base.currentDevice;
|
||||
exclusiveGroup: devicesMenuGroup;
|
||||
onTriggered: {
|
||||
base.defaultOverride = true;
|
||||
base.currentDevice = modelData;
|
||||
if(base.defaultAmbiguous) {
|
||||
base.defaultAmbiguous = false;
|
||||
Printer.writeToOutputDevice(modelData);
|
||||
width: infoBox.width/6*4.5
|
||||
height: UM.Theme.sizes.save_button_save_to_button.height
|
||||
style: ButtonStyle {
|
||||
background: Rectangle {
|
||||
color: !control.enabled ? UM.Theme.colors.save_button_inactive : control.hovered ? UM.Theme.colors.save_button_active_hover : UM.Theme.colors.save_button_active;
|
||||
Label {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: UM.Theme.colors.save_button_safe_to_text;
|
||||
font: UM.Theme.fonts.sidebar_save_to;
|
||||
text: Printer.outputDevices[base.currentDevice].description;
|
||||
}
|
||||
}
|
||||
}
|
||||
onObjectAdded: devicesMenu.insertItem(index, object)
|
||||
onObjectRemoved: devicesMenu.removeItem(object)
|
||||
onClicked:
|
||||
if(base.defaultAmbiguous) {
|
||||
devicesMenu.popup();
|
||||
} else {
|
||||
Printer.writeToOutputDevice(base.currentDevice);
|
||||
}
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: devicesMenuGroup; }
|
||||
}
|
||||
Button {
|
||||
id: deviceSelectionMenu;
|
||||
anchors.top: infoBox.bottom
|
||||
anchors.topMargin: UM.Theme.sizes.save_button_text_margin.height
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.sizes.default_margin.width;
|
||||
tooltip: ''
|
||||
|
||||
text: {
|
||||
if(base.progress < 0) {
|
||||
//: Save button label
|
||||
return qsTr("Please load a 3D model");
|
||||
} else if (base.progress < 0.95) {
|
||||
//: Save button label
|
||||
return qsTr("Calculating Print-time");
|
||||
} else {
|
||||
//: Save button label
|
||||
return qsTr("Estimated Print-time");
|
||||
width: infoBox.width/6*1.3 - UM.Theme.sizes.save_button_text_margin.height;
|
||||
height: UM.Theme.sizes.save_button_save_to_button.height
|
||||
|
||||
style: ButtonStyle {
|
||||
background: Rectangle {
|
||||
color: UM.Theme.colors.save_button_background;
|
||||
border.width: control.hovered ? UM.Theme.sizes.save_button_border.width : 0
|
||||
border.color: UM.Theme.colors.save_button_border
|
||||
Rectangle {
|
||||
id: deviceSelectionIcon
|
||||
color: UM.Theme.colors.save_button_background;
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width / 2;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: parent.height - UM.Theme.sizes.save_button_text_margin.width ;
|
||||
height: parent.height - UM.Theme.sizes.save_button_text_margin.width;
|
||||
UM.RecolorImage {
|
||||
anchors.centerIn: parent;
|
||||
width: parent.width;
|
||||
height: parent.height;
|
||||
sourceSize.width: width;
|
||||
sourceSize.height: height;
|
||||
color: UM.Theme.colors.save_button_active
|
||||
source: UM.Theme.icons[Printer.outputDevices[base.currentDevice].icon];
|
||||
}
|
||||
}
|
||||
Label {
|
||||
id: deviceSelectionArrow
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: UM.Theme.sizes.save_button_text_margin.height
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
text: "▼";
|
||||
font: UM.Theme.fonts.tiny;
|
||||
color: UM.Theme.colors.save_button_active;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu: Menu {
|
||||
id: devicesMenu;
|
||||
Instantiator {
|
||||
model: Printer.outputDeviceNames;
|
||||
MenuItem {
|
||||
text: Printer.outputDevices[modelData].description;
|
||||
checkable: true;
|
||||
checked: base.defaultAmbiguous ? false : modelData == base.currentDevice;
|
||||
exclusiveGroup: devicesMenuGroup;
|
||||
onTriggered: {
|
||||
base.defaultOverride = true;
|
||||
base.currentDevice = modelData;
|
||||
if(base.defaultAmbiguous) {
|
||||
base.defaultAmbiguous = false;
|
||||
Printer.writeToOutputDevice(modelData);
|
||||
}
|
||||
}
|
||||
}
|
||||
onObjectAdded: devicesMenu.insertItem(index, object)
|
||||
onObjectRemoved: devicesMenu.removeItem(object)
|
||||
}
|
||||
ExclusiveGroup { id: devicesMenuGroup; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if(base.defaultAmbiguous) {
|
||||
devicesMenu.popup();
|
||||
} else {
|
||||
Printer.writeToOutputDevice(base.currentDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,18 +8,18 @@ import QtQuick.Layouts 1.1
|
||||
|
||||
import UM 1.0 as UM
|
||||
|
||||
UM.AngledCornerRectangle {
|
||||
Rectangle {
|
||||
id: base;
|
||||
|
||||
property Action addMachineAction;
|
||||
property Action configureMachinesAction;
|
||||
property alias saveAction: saveButton.saveAction;
|
||||
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
|
||||
color: UM.Theme.colors.sidebar;
|
||||
|
||||
function showTooltip(item, position, text) {
|
||||
tooltip.text = text;
|
||||
position = item.mapToItem(base, position.x, position.y);
|
||||
position = item.mapToItem(base, position.x, position.y / 2);
|
||||
tooltip.show(position);
|
||||
}
|
||||
|
||||
@ -39,7 +39,6 @@ UM.AngledCornerRectangle {
|
||||
ColumnLayout {
|
||||
anchors.fill: parent;
|
||||
anchors.topMargin: UM.Theme.sizes.default_margin.height;
|
||||
anchors.bottomMargin: UM.Theme.sizes.window_margin.height;
|
||||
|
||||
spacing: UM.Theme.sizes.default_margin.height;
|
||||
|
||||
@ -73,7 +72,7 @@ UM.AngledCornerRectangle {
|
||||
property Item sidebar: base;
|
||||
|
||||
onLoaded:
|
||||
{
|
||||
{
|
||||
if(item)
|
||||
{
|
||||
item.configureSettings = base.configureMachinesAction;
|
||||
@ -91,13 +90,11 @@ UM.AngledCornerRectangle {
|
||||
|
||||
SaveButton {
|
||||
id: saveButton;
|
||||
Layout.preferredWidth: base.width - UM.Theme.sizes.default_margin.width * 2;
|
||||
Layout.preferredHeight: UM.Theme.sizes.button.height;
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter;
|
||||
implicitWidth: base.width
|
||||
implicitHeight: UM.Theme.sizes.save_button_text_margin.height * 2 + UM.Theme.sizes.save_button_slicing_bar.height + UM.Theme.sizes.save_button_save_to_button.height + UM.Theme.sizes.default_margin.height
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
SidebarTooltip {
|
||||
id: tooltip;
|
||||
}
|
||||
@ -109,4 +106,11 @@ UM.AngledCornerRectangle {
|
||||
//: Advanced configuration mode option
|
||||
ListElement { text: QT_TR_NOOP("Advanced"); file: "SidebarAdvanced.qml" }
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
for(var i = 0; i < modesListModel.count; ++i)
|
||||
{
|
||||
modesListModel.setProperty(i, "text", qsTr(modesListModel.get(i).text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,13 @@
|
||||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
import UM 1.0 as UM
|
||||
|
||||
UM.SettingView { }
|
||||
UM.SettingView {
|
||||
expandedCategories: Printer.expandedCategories;
|
||||
onExpandedCategoriesChanged: Printer.setExpandedCategories(expandedCategories);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ Column {
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
text: base.modesModel ? qsTr(base.modesModel.get(modeMenu.currentIndex).text) : "";
|
||||
text: base.modesModel ? base.modesModel.get(modeMenu.currentIndex).text : "";
|
||||
|
||||
style: UM.Theme.styles.sidebar_header_button;
|
||||
|
||||
@ -48,7 +48,7 @@ Column {
|
||||
model: base.modesModel;
|
||||
|
||||
MenuItem {
|
||||
text: qsTr(model.text);
|
||||
text: model.text;
|
||||
checkable: true;
|
||||
checked: modeMenu.currentIndex == index;
|
||||
exclusiveGroup: modeMenuGroup;
|
||||
|
@ -20,6 +20,9 @@ Item {
|
||||
property variant minimumPrintTime: PrintInformation.minimumPrintTime;
|
||||
property variant maximumPrintTime: PrintInformation.maximumPrintTime;
|
||||
|
||||
Component.onCompleted: PrintInformation.enabled = true
|
||||
Component.onDestruction: PrintInformation.enabled = false
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent;
|
||||
|
||||
|
@ -22,10 +22,10 @@ Rectangle {
|
||||
|
||||
function show(position) {
|
||||
if(position.y + base.height > parent.height) {
|
||||
x = position.x;
|
||||
x = position.x - base.width;
|
||||
y = parent.height - base.height;
|
||||
} else {
|
||||
x = position.x;
|
||||
x = position.x - base.width;
|
||||
y = position.y;
|
||||
}
|
||||
base.opacity = 1;
|
||||
|
@ -14,6 +14,24 @@ Item {
|
||||
width: buttons.width;
|
||||
height: buttons.height + panel.height;
|
||||
|
||||
Rectangle {
|
||||
id: activeItemBackground;
|
||||
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: UM.Theme.sizes.default_margin.height;
|
||||
|
||||
width: UM.Theme.sizes.button.width;
|
||||
height: UM.Theme.sizes.button.height * 2;
|
||||
|
||||
opacity: panelBackground.opacity;
|
||||
|
||||
color: UM.Theme.colors.tool_panel_background
|
||||
|
||||
function setActive(new_x) {
|
||||
x = new_x;
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: buttons;
|
||||
|
||||
@ -30,10 +48,10 @@ Item {
|
||||
Button {
|
||||
text: model.name;
|
||||
iconSource: UM.Theme.icons[model.icon];
|
||||
tooltip: model.description;
|
||||
|
||||
checkable: true;
|
||||
checked: model.active;
|
||||
onCheckedChanged: if (checked) activeItemBackground.setActive(x);
|
||||
|
||||
style: UM.Theme.styles.tool_button;
|
||||
|
||||
@ -43,20 +61,33 @@ Item {
|
||||
anchors.fill: parent;
|
||||
onClicked: parent.checked ? UM.Controller.setActiveTool(null) : UM.Controller.setActiveTool(model.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: panel
|
||||
UM.AngledCornerRectangle {
|
||||
id: panelBackground;
|
||||
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: buttons.top;
|
||||
anchors.bottomMargin: UM.Theme.sizes.default_margin.height;
|
||||
|
||||
height: childrenRect.height;
|
||||
width: panel.item ? Math.max(panel.width + 2 * UM.Theme.sizes.default_margin.width, activeItemBackground.x + activeItemBackground.width) : 0;
|
||||
height: panel.item ? panel.height + 2 * UM.Theme.sizes.default_margin.height : 0;
|
||||
|
||||
source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : "";
|
||||
opacity: panel.item ? 1 : 0
|
||||
Behavior on opacity { NumberAnimation { duration: 100 } }
|
||||
|
||||
color: UM.Theme.colors.tool_panel_background;
|
||||
cornerSize: width > 0 ? UM.Theme.sizes.default_margin.width : 0;
|
||||
|
||||
Loader {
|
||||
id: panel
|
||||
|
||||
x: UM.Theme.sizes.default_margin.width;
|
||||
y: UM.Theme.sizes.default_margin.height;
|
||||
|
||||
source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,20 @@
|
||||
|
||||
"categories": {
|
||||
"material": {
|
||||
"visible": false
|
||||
"settings": {
|
||||
"material_print_temperature": {
|
||||
"visible": false
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"visible": false
|
||||
},
|
||||
"material_diameter": {
|
||||
"visible": false
|
||||
},
|
||||
"material_flow": {
|
||||
"visible": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,23 @@
|
||||
"machine_nozzle_gantry_distance": { "default": 55 },
|
||||
"machine_nozzle_offset_x_1": { "default": 18.0 },
|
||||
"machine_nozzle_offset_y_1": { "default": 0.0 },
|
||||
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" }
|
||||
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
|
||||
|
||||
"machine_start_gcode": {
|
||||
"default": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||
}
|
||||
},
|
||||
|
||||
"categories": {
|
||||
"material": {
|
||||
"settings": {
|
||||
"material_bed_temperature": {
|
||||
"visible": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "ultimaker_original_plus",
|
||||
"name": "Ultimaker Original Plus",
|
||||
"name": "Ultimaker Original+",
|
||||
"icon": "icon_ultimaker.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "UltimakerPlusbackplate.png",
|
||||
|
149
resources/themes/cura/icons/category_blackmagic.svg
Normal file
@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:i="&#38;#38;ns_ai;"
|
||||
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:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="30px"
|
||||
height="30px"
|
||||
viewBox="0 0 30 30"
|
||||
enable-background="new 0 0 30 30"
|
||||
xml:space="preserve"
|
||||
id="svg3445"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="category_blackmagic.svg"><metadata
|
||||
id="metadata3464"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs3462" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1104"
|
||||
id="namedview3460"
|
||||
showgrid="false"
|
||||
inkscape:zoom="15.733333"
|
||||
inkscape:cx="17.360656"
|
||||
inkscape:cy="-1.829103"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g3449" /><switch
|
||||
id="switch3447"
|
||||
style="fill:#000000"><foreignObject
|
||||
requiredExtensions="http://ns.adobe.com/AdobeIllustrator/10.0/"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"><i:pgfRef
|
||||
xlink:href="#adobe_illustrator_pgf" /></foreignObject><g
|
||||
i:extraneous="self"
|
||||
id="g3449"
|
||||
style="fill:#000000"><g
|
||||
id="frame_30x30"
|
||||
display="none"
|
||||
style="fill:#000000"><rect
|
||||
display="inline"
|
||||
width="30"
|
||||
height="30"
|
||||
id="rect3452"
|
||||
style="fill:#000000" /></g><g
|
||||
id="frame_24x24"
|
||||
display="none"
|
||||
style="fill:#000000"><rect
|
||||
x="3"
|
||||
y="3"
|
||||
display="inline"
|
||||
fill="#C6C7C8"
|
||||
width="24"
|
||||
height="24"
|
||||
id="rect3455"
|
||||
style="fill:#000000" /></g><g
|
||||
id="icon"
|
||||
style="fill:#ffffff"><path
|
||||
d="M 21,11 24,3 6,3 9,11 3,27 27,27 Z M 9.937,10.648 7.443,4 22.556,4 20.063,10.648 19.932,11 l 0.132,0.352 5.495386,14.648398 c -21.1116911,-0.0086 0,0 -21.1116911,-0.0086 L 9.938,11.352 10.068,11 Z"
|
||||
id="path3458"
|
||||
style="fill:#ffffff"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccccccccc" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:18.84262657px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="8.4277077"
|
||||
y="19.917229"
|
||||
id="text3477"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="scale(1.0563883,0.94662161)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3479"
|
||||
x="8.4277077"
|
||||
y="19.917229"
|
||||
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:gargi;-inkscape-font-specification:'gargi Medium';fill:#ffffff">?</tspan></text>
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffffff"
|
||||
id="path3481"
|
||||
sodipodi:sides="4"
|
||||
sodipodi:cx="9.3481913"
|
||||
sodipodi:cy="22.314709"
|
||||
sodipodi:r1="2.4681976"
|
||||
sodipodi:r2="0.83969672"
|
||||
sodipodi:arg1="-0.57790194"
|
||||
sodipodi:arg2="0.14505566"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 11.41558,20.966412 -1.236511,1.469673 0.517419,1.946012 -1.4696728,-1.23651 -1.9460124,0.517419 1.2365104,-1.469673 -0.5174188,-1.946013 1.4696729,1.236511 z"
|
||||
inkscape:transform-center-x="1.0389927"
|
||||
inkscape:transform-center-y="0.029898609"
|
||||
transform="matrix(-0.22728467,0.67950815,-0.70858638,-0.2354929,27.791673,20.846699)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffffff"
|
||||
id="path3483"
|
||||
sodipodi:sides="4"
|
||||
sodipodi:cx="19.64019"
|
||||
sodipodi:cy="21.10124"
|
||||
sodipodi:r1="3.1779661"
|
||||
sodipodi:r2="1.3224357"
|
||||
sodipodi:arg1="1.7126934"
|
||||
sodipodi:arg2="2.4768523"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 19.190758,24.247266 -0.591427,-2.330275 -2.105167,-1.265183 2.330275,-0.591427 1.265183,-2.105167 0.591427,2.330275 2.105167,1.265183 -2.330275,0.591427 z"
|
||||
inkscape:transform-center-x="-0.16053709"
|
||||
inkscape:transform-center-y="0.22160086"
|
||||
transform="matrix(0.6277977,0.94579165,-0.90860936,0.60311684,26.861228,-9.3621234)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffffff"
|
||||
id="path3487"
|
||||
sodipodi:sides="4"
|
||||
sodipodi:cx="14.741379"
|
||||
sodipodi:cy="18.045101"
|
||||
sodipodi:r1="2.2819479"
|
||||
sodipodi:r2="0.93399796"
|
||||
sodipodi:arg1="-1.0101093"
|
||||
sodipodi:arg2="-0.21584978"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 15.954846,16.112542 -0.301143,1.732518 1.020235,1.413508 -1.732518,-0.301143 -1.413508,1.020235 0.301142,-1.732517 -1.020234,-1.413509 1.732517,0.301143 z"
|
||||
inkscape:transform-center-x="-0.17795886"
|
||||
inkscape:transform-center-y="-0.0146708"
|
||||
transform="matrix(0.18679438,-0.98239903,0.98239903,0.18679438,-5.8318713,29.017206)" /></g></g></switch><i:pgf
|
||||
id="adobe_illustrator_pgf" /></svg>
|
After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 246 KiB |
3384
resources/themes/cura/icons/category_shell.svg
Normal file
After Width: | Height: | Size: 248 KiB |
3422
resources/themes/cura/icons/category_travel.svg
Normal file
After Width: | Height: | Size: 249 KiB |
@ -35,46 +35,42 @@ QtObject {
|
||||
}
|
||||
}
|
||||
|
||||
property Component tool_button: Component {
|
||||
property Component open_file_button: Component {
|
||||
ButtonStyle {
|
||||
background: UM.AngledCornerRectangle {
|
||||
property bool down: control.pressed || (control.checkable && control.checked);
|
||||
|
||||
background: Item {
|
||||
implicitWidth: UM.Theme.sizes.button.width;
|
||||
implicitHeight: UM.Theme.sizes.button.height;
|
||||
color: {
|
||||
if(!control.enabled) {
|
||||
return UM.Theme.colors.button_disabled;
|
||||
} else if(control.checkable && control.checked && control.hovered) {
|
||||
return UM.Theme.colors.button_active_hover;
|
||||
} else if(control.pressed || (control.checkable && control.checked)) {
|
||||
return UM.Theme.colors.button_active;
|
||||
} else if(control.hovered) {
|
||||
return UM.Theme.colors.button_hover;
|
||||
} else {
|
||||
return UM.Theme.colors.button;
|
||||
}
|
||||
}
|
||||
Behavior on color { ColorAnimation { duration: 50; } }
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
|
||||
Rectangle {
|
||||
anchors.bottom: parent.top;
|
||||
|
||||
anchors.bottom: parent.verticalCenter;
|
||||
width: parent.width;
|
||||
height: control.hovered ? label.height : 0;
|
||||
Behavior on height { NumberAnimation { duration: 75; } }
|
||||
height: control.hovered ? parent.height / 2 + label.height : 0;
|
||||
Behavior on height { NumberAnimation { duration: 100; } }
|
||||
|
||||
opacity: control.hovered ? 1.0 : 0.0;
|
||||
Behavior on opacity { NumberAnimation { duration: 75; } }
|
||||
Behavior on opacity { NumberAnimation { duration: 100; } }
|
||||
|
||||
Label {
|
||||
id: label
|
||||
id: label;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
text: control.text;
|
||||
text: control.text.replace("&", "");
|
||||
font: UM.Theme.fonts.button_tooltip;
|
||||
color: UM.Theme.colors.button_tooltip_text;
|
||||
}
|
||||
}
|
||||
|
||||
UM.AngledCornerRectangle {
|
||||
anchors.fill: parent;
|
||||
color: {
|
||||
if(control.hovered) {
|
||||
return UM.Theme.colors.button_active_hover;
|
||||
} else {
|
||||
return UM.Theme.colors.button_active;
|
||||
}
|
||||
}
|
||||
Behavior on color { ColorAnimation { duration: 50; } }
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
}
|
||||
}
|
||||
|
||||
label: Item {
|
||||
@ -91,6 +87,110 @@ QtObject {
|
||||
}
|
||||
}
|
||||
|
||||
property Component tool_button: Component {
|
||||
ButtonStyle {
|
||||
background: Item {
|
||||
implicitWidth: UM.Theme.sizes.button.width;
|
||||
implicitHeight: UM.Theme.sizes.button.height;
|
||||
|
||||
Rectangle {
|
||||
anchors.bottom: parent.verticalCenter;
|
||||
|
||||
width: parent.width;
|
||||
height: control.hovered ? parent.height / 2 + label.height : 0;
|
||||
Behavior on height { NumberAnimation { duration: 100; } }
|
||||
|
||||
opacity: control.hovered ? 1.0 : 0.0;
|
||||
Behavior on opacity { NumberAnimation { duration: 100; } }
|
||||
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
width: childrenRect.width;
|
||||
height: childrenRect.height;
|
||||
|
||||
Label {
|
||||
id: label
|
||||
text: control.text.replace("&", "");
|
||||
font: UM.Theme.fonts.button_tooltip;
|
||||
color: UM.Theme.colors.button_tooltip_text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UM.AngledCornerRectangle {
|
||||
id: buttonFace;
|
||||
|
||||
anchors.fill: parent;
|
||||
|
||||
property bool down: control.pressed || (control.checkable && control.checked);
|
||||
|
||||
color: {
|
||||
if(!control.enabled) {
|
||||
return UM.Theme.colors.button_disabled;
|
||||
} else if(control.checkable && control.checked && control.hovered) {
|
||||
return UM.Theme.colors.button_active_hover;
|
||||
} else if(control.pressed || (control.checkable && control.checked)) {
|
||||
return UM.Theme.colors.button_active;
|
||||
} else if(control.hovered) {
|
||||
return UM.Theme.colors.button_hover;
|
||||
} else {
|
||||
return UM.Theme.colors.button;
|
||||
}
|
||||
}
|
||||
Behavior on color { ColorAnimation { duration: 50; } }
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
}
|
||||
}
|
||||
|
||||
label: Item {
|
||||
Image {
|
||||
anchors.centerIn: parent;
|
||||
|
||||
source: control.iconSource;
|
||||
width: UM.Theme.sizes.button_icon.width;
|
||||
height: UM.Theme.sizes.button_icon.height;
|
||||
|
||||
sourceSize: UM.Theme.sizes.button_icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
property Component progressbar: Component{
|
||||
ProgressBarStyle {
|
||||
background: UM.AngledCornerRectangle {
|
||||
cornerSize: UM.Theme.sizes.progressbar_control.height
|
||||
implicitWidth: UM.Theme.sizes.progressbar.width
|
||||
implicitHeight: UM.Theme.sizes.progressbar.height
|
||||
color: UM.Theme.colors.progressbar_background
|
||||
}
|
||||
progress: UM.AngledCornerRectangle {
|
||||
cornerSize: UM.Theme.sizes.progressbar_control.height
|
||||
color: control.indeterminate ? "transparent" : UM.Theme.colors.progressbar_control
|
||||
|
||||
UM.AngledCornerRectangle {
|
||||
cornerSize: UM.Theme.sizes.progressbar_control.height
|
||||
color: UM.Theme.colors.progressbar_control
|
||||
width: UM.Theme.sizes.progressbar_control.width
|
||||
height: UM.Theme.sizes.progressbar_control.height
|
||||
visible: control.indeterminate
|
||||
|
||||
SequentialAnimation on x {
|
||||
id: xAnim
|
||||
property int animEndPoint: UM.Theme.sizes.progressbar.width - UM.Theme.sizes.progressbar_control.width
|
||||
running: control.indeterminate
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { from: 0; to: xAnim.animEndPoint; duration: 2000;}
|
||||
NumberAnimation { from: xAnim.animEndPoint; to: 0; duration: 2000;}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
property Component sidebar_category: Component {
|
||||
ButtonStyle {
|
||||
background: UM.AngledCornerRectangle {
|
||||
@ -121,6 +221,8 @@ QtObject {
|
||||
Image {
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
source: control.iconSource;
|
||||
width: UM.Theme.sizes.section_icon.width;
|
||||
height: UM.Theme.sizes.section_icon.height;
|
||||
}
|
||||
|
||||
Label {
|
||||
@ -243,4 +345,32 @@ QtObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Component text_field: Component {
|
||||
TextFieldStyle {
|
||||
textColor: UM.Theme.colors.setting_control_text;
|
||||
font: UM.Theme.fonts.default;
|
||||
|
||||
background: Rectangle
|
||||
{
|
||||
implicitHeight: control.height;
|
||||
implicitWidth: control.width;
|
||||
|
||||
border.width: 1;
|
||||
border.color: UM.Theme.colors.setting_control_border;
|
||||
|
||||
color: UM.Theme.colors.setting_validation_ok;
|
||||
|
||||
Label {
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: UM.Theme.sizes.setting_unit_margin.width;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
||||
text: control.unit ? control.unit : ""
|
||||
color: UM.Theme.colors.setting_unit;
|
||||
font: UM.Theme.fonts.default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,10 @@
|
||||
"capitalize": true,
|
||||
"family": "Roboto"
|
||||
},
|
||||
"sidebar_save_to": {
|
||||
"size": 1.0,
|
||||
"family": "Roboto"
|
||||
},
|
||||
"timeslider_time": {
|
||||
"size": 1.0,
|
||||
"bold": true,
|
||||
@ -44,23 +48,26 @@
|
||||
},
|
||||
|
||||
"colors": {
|
||||
"sidebar": [255, 255, 255, 255],
|
||||
|
||||
"primary": [12, 169, 227, 255],
|
||||
"primary_hover": [34, 150, 190, 255],
|
||||
"primary_text": [255, 255, 255, 255],
|
||||
"border": [205, 202, 201, 255],
|
||||
"secondary": [205, 202, 201, 255],
|
||||
|
||||
"text": [174, 174, 174, 255],
|
||||
"text_inactive": [205, 202, 201, 255],
|
||||
"text": [140, 144, 154, 255],
|
||||
"text_inactive": [174, 174, 174, 255],
|
||||
"text_hover": [35, 35, 35, 255],
|
||||
"text_pressed": [12, 169, 227, 255],
|
||||
|
||||
"button": [205, 202, 201, 255],
|
||||
"button_hover": [174, 174, 174, 255],
|
||||
"button": [160, 163, 171, 255],
|
||||
"button_hover": [140, 144, 154, 255],
|
||||
"button_active": [12, 169, 227, 255],
|
||||
"button_active_hover": [34, 150, 190, 255],
|
||||
"button_active_hover": [34, 150, 199, 255],
|
||||
"button_text": [255, 255, 255, 255],
|
||||
"button_disabled": [245, 245, 245, 255],
|
||||
"button_tooltip_text": [35, 35, 35, 255],
|
||||
|
||||
"scrollbar_background": [245, 245, 245, 255],
|
||||
"scrollbar_handle": [205, 202, 201, 255],
|
||||
@ -74,7 +81,7 @@
|
||||
"setting_category_active_hover": [34, 150, 190, 255],
|
||||
"setting_category_text": [255, 255, 255, 255],
|
||||
|
||||
"setting_label": [174, 174, 174, 255],
|
||||
"setting_label": [140, 144, 154, 255],
|
||||
"setting_control": [255, 255, 255, 255],
|
||||
"setting_control_highlight": [245, 245, 245, 255],
|
||||
"setting_control_border": [174, 174, 174, 255],
|
||||
@ -85,6 +92,9 @@
|
||||
"setting_validation_warning": [255, 186, 15, 255],
|
||||
"setting_validation_ok": [255, 255, 255, 255],
|
||||
|
||||
"progressbar_background": [245, 245, 245, 255],
|
||||
"progressbar_control": [12, 169, 227, 255],
|
||||
|
||||
"slider_groove": [245, 245, 245, 255],
|
||||
"slider_groove_border": [205, 202, 201, 255],
|
||||
"slider_groove_fill": [205, 202, 201, 255],
|
||||
@ -95,19 +105,24 @@
|
||||
"checkbox_hover": [245, 245, 245, 255],
|
||||
"checkbox_border": [174, 174, 174, 255],
|
||||
"checkbox_mark": [35, 35, 35, 255],
|
||||
"checkbox_text": [174, 174, 174, 255],
|
||||
"checkbox_text": [140, 144, 154, 255],
|
||||
|
||||
"tooltip": [255, 225, 146, 255],
|
||||
|
||||
"save_button": [255, 255, 255, 255],
|
||||
"save_button_border": [205, 202, 201, 255],
|
||||
"save_button_inactive": [205, 202, 201, 255],
|
||||
"save_button_active": [12, 159, 227, 255],
|
||||
"save_button_active_hover": [34, 150, 190, 255],
|
||||
"save_button_text": [35, 35, 35, 255],
|
||||
"save_button_safe_to_text": [255, 255, 255, 255],
|
||||
"save_button_estimated_text": [140, 144, 154, 255],
|
||||
"save_button_estimated_text_background": [255, 255, 255, 255],
|
||||
"save_button_printtime_text": [12, 169, 227, 255],
|
||||
"save_button_background": [249, 249, 249, 255],
|
||||
|
||||
"message": [205, 202, 201, 255],
|
||||
"message_text": [35, 35, 35, 255]
|
||||
"message_text": [35, 35, 35, 255],
|
||||
|
||||
"tool_panel_background": [255, 255, 255, 255]
|
||||
},
|
||||
|
||||
"sizes": {
|
||||
@ -128,7 +143,11 @@
|
||||
"setting_unit_margin": [0.5, 0.5],
|
||||
|
||||
"button": [4.25, 4.25],
|
||||
"button_icon": [3.57, 3.57],
|
||||
"button_icon": [2.9, 2.9],
|
||||
|
||||
"progressbar": [26.0, 0.5],
|
||||
"progressbar_control": [8.0, 0.5],
|
||||
"progressbar_padding": [0.0, 1.0],
|
||||
|
||||
"scrollbar": [0.5, 0.5],
|
||||
|
||||
@ -141,7 +160,10 @@
|
||||
"tooltip_margins": [1.0, 1.0],
|
||||
|
||||
"save_button_border": [0.06, 0.06],
|
||||
"save_button_text_margin": [0.6, 0.6],
|
||||
"save_button_slicing_bar": [0.0, 2.2],
|
||||
"save_button_label_margin": [0.5, 0.5],
|
||||
"save_button_save_to_button": [0.3, 2.7],
|
||||
|
||||
"message": [30.0, 5.0],
|
||||
"message_close": [1.25, 1.25]
|
||||
|
16
setup.py
@ -22,7 +22,7 @@ def copytree(src, dst, symlinks=False, ignore=None):
|
||||
else:
|
||||
shutil.copy2(s, d)
|
||||
|
||||
includes = ["sip", "ctypes", "UM", "PyQt5.QtNetwork", "PyQt5._QOpenGLFunctions_2_0", "serial", "Arcus", "google", "google.protobuf", "google.protobuf.descriptor", "xml.etree", "xml.etree.ElementTree", "src"]
|
||||
includes = ["sip", "ctypes", "UM", "PyQt5.QtNetwork", "PyQt5._QOpenGLFunctions_2_0", "serial", "Arcus", "google", "google.protobuf", "google.protobuf.descriptor", "xml.etree", "xml.etree.ElementTree", "cura"]
|
||||
# Include all the UM modules in the includes. As py2exe fails to properly find all the dependencies due to the plugin architecture.
|
||||
for dirpath, dirnames, filenames in os.walk(os.path.dirname(UM.__file__)):
|
||||
if "__" in dirpath:
|
||||
@ -41,20 +41,20 @@ print("Removing previous distribution package")
|
||||
shutil.rmtree("dist", True)
|
||||
|
||||
setup(name="Cura",
|
||||
version="2.0",
|
||||
version="15.05.95",
|
||||
author="Ultimaker",
|
||||
author_email="d.braam@ultimaker.com",
|
||||
url="http://software.ultimaker.com/",
|
||||
license="GNU AFFERO GENERAL PUBLIC LICENSE (AGPL)",
|
||||
scripts=["cura_app.py"],
|
||||
#windows=[{"script": "printer.py", "dest_name": "Cura"}],
|
||||
console=[{"script": "cura_app.py"}],
|
||||
windows=[{"script": "cura_app.py", "dest_name": "Cura", "icon_resources": [(1, "icons/cura.ico")]}],
|
||||
#console=[{"script": "cura_app.py"}],
|
||||
options={"py2exe": {"skip_archive": False, "includes": includes}})
|
||||
|
||||
print("Coping Cura plugins.")
|
||||
shutil.copytree(os.path.dirname(UM.__file__) + "/../plugins", "dist/plugins")
|
||||
shutil.copytree(os.path.dirname(UM.__file__) + "/../plugins", "dist/plugins", ignore = shutil.ignore_patterns("ConsoleLogger", "OBJWriter", "MLPWriter", "MLPReader"))
|
||||
for path in os.listdir("plugins"):
|
||||
shutil.copytree("plugins/" + path, "dist/plugins/" + path)
|
||||
shutil.copytree("plugins/" + path, "dist/plugins/" + path)
|
||||
print("Coping resources.")
|
||||
shutil.copytree(os.path.dirname(UM.__file__) + "/../resources", "dist/resources")
|
||||
copytree("resources", "dist/resources")
|
||||
@ -70,3 +70,7 @@ for site_package in site.getsitepackages():
|
||||
shutil.copytree(os.path.join(qt_origin_path, "qml/QtQuick.2"), "dist/qml/QtQuick.2")
|
||||
print("Coping PyQt5 svg library from: %s" % qt_origin_path)
|
||||
shutil.copy(os.path.join(qt_origin_path, "Qt5Svg.dll"), "dist/Qt5Svg.dll")
|
||||
print("Copying Angle libraries from %s" % qt_origin_path)
|
||||
shutil.copy(os.path.join(qt_origin_path, "libEGL.dll"), "dist/libEGL.dll")
|
||||
shutil.copy(os.path.join(qt_origin_path, "libGLESv2.dll"), "dist/libGLESv2.dll")
|
||||
os.rename("dist/cura_app.exe", "dist/Cura.exe")
|