Jump to content
Ultimaker Community of 3D Printing Experts
Anteino

Plugin to import fdm_material and inst.cfg profiles

Recommended Posts

Hi all,

 

I would like to make a plugin to import files with the extensions .fdm_material and .inst.cfg to import material- and printing profiles as native in Cura. I basically copied the ImageReader plugin and changed the extensions. The plugin is found but it's still saying that the cfg extension is not supported. What am I missing here?

 

__init__.py:

# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

from . import ProfileImporter

from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura")

def getMetaData():
    return {
        "p_reader": [
            {
                "extension": "fdm_material",
                "description": i18n_catalog.i18nc("@item:inlistbox", "Material profile")
            },
            {
                "extension": "cfg",
                "description": i18n_catalog.i18nc("@item:inlistbox", "Printing profile")
            }
        ]
    }

def register(app):
    return { "p_reader": ProfileImporter.ProfileImporter() }

ConfigUI.qml:

// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.

import QtQuick 2.1
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1

import UM 1.1 as UM

UM.Dialog
{
    width: minimumWidth;
    minimumWidth: 350 * screenScaleFactor;

    height: minimumHeight;
    minimumHeight: 250 * screenScaleFactor;

    title: catalog.i18nc("@title:window", "Convert Image...")

    GridLayout
    {
        UM.I18nCatalog{id: catalog; name:"cura"}
        anchors.fill: parent;
        Layout.fillWidth: true
        columnSpacing: 16 * screenScaleFactor
        rowSpacing: 4 * screenScaleFactor
        columns: 1

        UM.TooltipArea {
            Layout.fillWidth:true
            height: childrenRect.height
            text: catalog.i18nc("@info:tooltip","The maximum distance of each pixel from \"Base.\"")
            Row {
                width: parent.width

                Label {
                    text: catalog.i18nc("@action:label","Height (mm)")
                    width: 150 * screenScaleFactor
                    anchors.verticalCenter: parent.verticalCenter
                }

                TextField {
                    id: peak_height
                    objectName: "Peak_Height"
                    validator: RegExpValidator {regExp: /^-?\d{1,3}([\,|\.]\d*)?$/}
                    width: 180 * screenScaleFactor
                    onTextChanged: { manager.onPeakHeightChanged(text) }
                }
            }
        }

        UM.TooltipArea {
            Layout.fillWidth:true
            height: childrenRect.height
            text: catalog.i18nc("@info:tooltip","The base height from the build plate in millimeters.")
            Row {
                width: parent.width

                Label {
                    text: catalog.i18nc("@action:label","Base (mm)")
                    width: 150 * screenScaleFactor
                    anchors.verticalCenter: parent.verticalCenter
                }

                TextField {
                    id: base_height
                    objectName: "Base_Height"
                    validator: RegExpValidator {regExp: /^\d{1,3}([\,|\.]\d*)?$/}
                    width: 180 * screenScaleFactor
                    onTextChanged: { manager.onBaseHeightChanged(text) }
                }
            }
        }

        UM.TooltipArea {
            Layout.fillWidth:true
            height: childrenRect.height
            text: catalog.i18nc("@info:tooltip","The width in millimeters on the build plate.")
            Row {
                width: parent.width

                Label {
                    text: catalog.i18nc("@action:label","Width (mm)")
                    width: 150 * screenScaleFactor
                    anchors.verticalCenter: parent.verticalCenter
                }

                TextField {
                    id: width
                    objectName: "Width"
                    focus: true
                    validator: RegExpValidator {regExp: /^[1-9]\d{0,2}([\,|\.]\d*)?$/}
                    width: 180 * screenScaleFactor
                    onTextChanged: { manager.onWidthChanged(text) }
                }
            }
        }

        UM.TooltipArea {
            Layout.fillWidth:true
            height: childrenRect.height
            text: catalog.i18nc("@info:tooltip","The depth in millimeters on the build plate")
            Row {
                width: parent.width

                Label {
                    text: catalog.i18nc("@action:label","Depth (mm)")
                    width: 150 * screenScaleFactor
                    anchors.verticalCenter: parent.verticalCenter
                }
                TextField {
                    id: depth
                    objectName: "Depth"
                    focus: true
                    validator: RegExpValidator {regExp: /^[1-9]\d{0,2}([\,|\.]\d*)?$/}
                    width: 180 * screenScaleFactor
                    onTextChanged: { manager.onDepthChanged(text) }
                }
            }
        }

        UM.TooltipArea {
            Layout.fillWidth:true
            height: childrenRect.height
            text: catalog.i18nc("@info:tooltip","By default, white pixels represent high points on the mesh and black pixels represent low points on the mesh. Change this option to reverse the behavior such that black pixels represent high points on the mesh and white pixels represent low points on the mesh.")
            Row {
                width: parent.width

                //Empty label so 2 column layout works.
                Label {
                    text: ""
                    width: 150 * screenScaleFactor
                    anchors.verticalCenter: parent.verticalCenter
                }
                ComboBox {
                    id: image_color_invert
                    objectName: "Image_Color_Invert"
                    model: [ catalog.i18nc("@item:inlistbox","Lighter is higher"), catalog.i18nc("@item:inlistbox","Darker is higher") ]
                    width: 180 * screenScaleFactor
                    onCurrentIndexChanged: { manager.onImageColorInvertChanged(currentIndex) }
                }
            }
        }

        UM.TooltipArea {
            Layout.fillWidth:true
            height: childrenRect.height
            text: catalog.i18nc("@info:tooltip","The amount of smoothing to apply to the image.")
            Row {
                width: parent.width

                Label {
                    text: catalog.i18nc("@action:label","Smoothing")
                    width: 150 * screenScaleFactor
                    anchors.verticalCenter: parent.verticalCenter
                }

                Item {
                    width: 180 * screenScaleFactor
                    height: 20 * screenScaleFactor
                    Layout.fillWidth: true

                    Slider {
                        id: smoothing
                        objectName: "Smoothing"
                        maximumValue: 100.0
                        stepSize: 1.0
                        width: 180
                        onValueChanged: { manager.onSmoothingChanged(value) }
                    }
                }
            }
        }
    }

    rightButtons: [
        Button
        {
            id:ok_button
            text: catalog.i18nc("@action:button","OK");
            onClicked: { manager.onOkButtonClicked() }
            enabled: true
        },
        Button
        {
            id:cancel_button
            text: catalog.i18nc("@action:button","Cancel");
            onClicked: { manager.onCancelButtonClicked() }
            enabled: true
        }
    ]
}

plugin.json:

{
    "name": "Profile importer",
    "author": "Anteino",
    "version": "1",
    "description": "Import .xml.fdm_material material profiles and .inst.cfg printer profiles as native in Cura.",
    "api": 4,
    "i18n-catalog": "cura"
}

ProfileImporter.py:

# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

import numpy

from PyQt5.QtGui import QImage, qRed, qGreen, qBlue
from PyQt5.QtCore import Qt

from UM.Mesh.MeshReader import MeshReader
from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Math.Vector import Vector
from UM.Job import Job
from UM.Logger import Logger
from .ProfileImporterUI import ProfileImporterUI

from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode


class ProfileImporter(MeshReader):
    def __init__(self):
        super(ProfileImporter, self).__init__()
        self._supported_extensions = [".cfg", ".fdm_material"]
        self._ui = ProfileImporterUI(self)

    def preRead(self, file_name, *args, **kwargs):
        img = QImage(file_name)

        if img.isNull():
            Logger.log("e", "Image is corrupt.")
            return MeshReader.PreReadResult.failed

        width = img.width()
        depth = img.height()

        largest = max(width, depth)
        width = width / largest * self._ui.default_width
        depth = depth / largest * self._ui.default_depth

        self._ui.setWidthAndDepth(width, depth)
        self._ui.showConfigUI()
        self._ui.waitForUIToClose()

        if self._ui.getCancelled():
            return MeshReader.PreReadResult.cancelled
        return MeshReader.PreReadResult.accepted

    def read(self, file_name):
        size = max(self._ui.getWidth(), self._ui.getDepth())
        return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.image_color_invert)

    def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, image_color_invert):
        scene_node = SceneNode()

        mesh = MeshBuilder()

        img = QImage(file_name)

        if img.isNull():
            Logger.log("e", "Image is corrupt.")
            return None

        width = max(img.width(), 2)
        height = max(img.height(), 2)
        aspect = height / width

        if img.width() < 2 or img.height() < 2:
            img = img.scaled(width, height, Qt.IgnoreAspectRatio)

        base_height = max(base_height, 0)
        peak_height = max(peak_height, -base_height)

        xz_size = max(xz_size, 1)
        scale_vector = Vector(xz_size, peak_height, xz_size)

        if width > height:
            scale_vector = scale_vector.set(z=scale_vector.z * aspect)
        elif height > width:
            scale_vector = scale_vector.set(x=scale_vector.x / aspect)

        if width > max_size or height > max_size:
            scale_factor = max_size / width
            if height > width:
                scale_factor = max_size / height

            width = int(max(round(width * scale_factor), 2))
            height = int(max(round(height * scale_factor), 2))
            img = img.scaled(width, height, Qt.IgnoreAspectRatio)

        width_minus_one = width - 1
        height_minus_one = height - 1

        Job.yieldThread()

        texel_width = 1.0 / (width_minus_one) * scale_vector.x
        texel_height = 1.0 / (height_minus_one) * scale_vector.z

        height_data = numpy.zeros((height, width), dtype=numpy.float32)

        for x in range(0, width):
            for y in range(0, height):
                qrgb = img.pixel(x, y)
                avg = float(qRed(qrgb) + qGreen(qrgb) + qBlue(qrgb)) / (3 * 255)
                height_data[y, x] = avg

        Job.yieldThread()

        if image_color_invert:
            height_data = 1 - height_data

        for _ in range(0, blur_iterations):
            copy = numpy.pad(height_data, ((1, 1), (1, 1)), mode= "edge")

            height_data += copy[1:-1, 2:]
            height_data += copy[1:-1, :-2]
            height_data += copy[2:, 1:-1]
            height_data += copy[:-2, 1:-1]

            height_data += copy[2:, 2:]
            height_data += copy[:-2, 2:]
            height_data += copy[2:, :-2]
            height_data += copy[:-2, :-2]

            height_data /= 9

            Job.yieldThread()

        height_data *= scale_vector.y
        height_data += base_height

        heightmap_face_count = 2 * height_minus_one * width_minus_one
        total_face_count = heightmap_face_count + (width_minus_one * 2) * (height_minus_one * 2) + 2

        mesh.reserveFaceCount(total_face_count)

        # initialize to texel space vertex offsets.
        # 6 is for 6 vertices for each texel quad.
        heightmap_vertices = numpy.zeros((width_minus_one * height_minus_one, 6, 3), dtype = numpy.float32)
        heightmap_vertices = heightmap_vertices + numpy.array([[
            [0, base_height, 0],
            [0, base_height, texel_height],
            [texel_width, base_height, texel_height],
            [texel_width, base_height, texel_height],
            [texel_width, base_height, 0],
            [0, base_height, 0]
        ]], dtype = numpy.float32)

        offsetsz, offsetsx = numpy.mgrid[0: height_minus_one, 0: width - 1]
        offsetsx = numpy.array(offsetsx, numpy.float32).reshape(-1, 1) * texel_width
        offsetsz = numpy.array(offsetsz, numpy.float32).reshape(-1, 1) * texel_height

        # offsets for each texel quad
        heightmap_vertex_offsets = numpy.concatenate([offsetsx, numpy.zeros((offsetsx.shape[0], offsetsx.shape[1]), dtype=numpy.float32), offsetsz], 1)
        heightmap_vertices += heightmap_vertex_offsets.repeat(6, 0).reshape(-1, 6, 3)

        # apply height data to y values
        heightmap_vertices[:, 0, 1] = heightmap_vertices[:, 5, 1] = height_data[:-1, :-1].reshape(-1)
        heightmap_vertices[:, 1, 1] = height_data[1:, :-1].reshape(-1)
        heightmap_vertices[:, 2, 1] = heightmap_vertices[:, 3, 1] = height_data[1:, 1:].reshape(-1)
        heightmap_vertices[:, 4, 1] = height_data[:-1, 1:].reshape(-1)

        heightmap_indices = numpy.array(numpy.mgrid[0:heightmap_face_count * 3], dtype=numpy.int32).reshape(-1, 3)

        mesh._vertices[0:(heightmap_vertices.size // 3), :] = heightmap_vertices.reshape(-1, 3)
        mesh._indices[0:(heightmap_indices.size // 3), :] = heightmap_indices

        mesh._vertex_count = heightmap_vertices.size // 3
        mesh._face_count = heightmap_indices.size // 3

        geo_width = width_minus_one * texel_width
        geo_height = height_minus_one * texel_height

        # bottom
        mesh.addFaceByPoints(0, 0, 0, 0, 0, geo_height, geo_width, 0, geo_height)
        mesh.addFaceByPoints(geo_width, 0, geo_height, geo_width, 0, 0, 0, 0, 0)

        # north and south walls
        for n in range(0, width_minus_one):
            x = n * texel_width
            nx = (n + 1) * texel_width

            hn0 = height_data[0, n]
            hn1 = height_data[0, n + 1]

            hs0 = height_data[height_minus_one, n]
            hs1 = height_data[height_minus_one, n + 1]

            mesh.addFaceByPoints(x, 0, 0, nx, 0, 0, nx, hn1, 0)
            mesh.addFaceByPoints(nx, hn1, 0, x, hn0, 0, x, 0, 0)

            mesh.addFaceByPoints(x, 0, geo_height, nx, 0, geo_height, nx, hs1, geo_height)
            mesh.addFaceByPoints(nx, hs1, geo_height, x, hs0, geo_height, x, 0, geo_height)

        # west and east walls
        for n in range(0, height_minus_one):
            y = n * texel_height
            ny = (n + 1) * texel_height

            hw0 = height_data[n, 0]
            hw1 = height_data[n + 1, 0]

            he0 = height_data[n, width_minus_one]
            he1 = height_data[n + 1, width_minus_one]

            mesh.addFaceByPoints(0, 0, y, 0, 0, ny, 0, hw1, ny)
            mesh.addFaceByPoints(0, hw1, ny, 0, hw0, y, 0, 0, y)

            mesh.addFaceByPoints(geo_width, 0, y, geo_width, 0, ny, geo_width, he1, ny)
            mesh.addFaceByPoints(geo_width, he1, ny, geo_width, he0, y, geo_width, 0, y)

        mesh.calculateNormals(fast=True)

        scene_node.setMeshData(mesh.build())

        return scene_node

ProfileImporterUI.py:

# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

import os
import threading

from PyQt5.QtCore import Qt, pyqtSignal, QObject
from UM.FlameProfiler import pyqtSlot
from UM.Application import Application
from UM.PluginRegistry import PluginRegistry
from UM.Logger import Logger

from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")


class ProfileImporterUI(QObject):
    show_config_ui_trigger = pyqtSignal()

    def __init__(self, image_reader):
        super(ProfileImporterUI, self).__init__()
        self.image_reader = image_reader
        self._ui_view = None
        self.show_config_ui_trigger.connect(self._actualShowConfigUI)

        self.default_width = 120
        self.default_depth = 120

        self._aspect = 1
        self._width = self.default_width
        self._depth = self.default_depth

        self.base_height = 1
        self.peak_height = 10
        self.smoothing = 1
        self.image_color_invert = False;

        self._ui_lock = threading.Lock()
        self._cancelled = False
        self._disable_size_callbacks = False

    def setWidthAndDepth(self, width, depth):
        self._aspect = width / depth
        self._width = width
        self._depth = depth

    def getWidth(self):
        return self._width

    def getDepth(self):
        return self._depth

    def getCancelled(self):
        return self._cancelled

    def waitForUIToClose(self):
        self._ui_lock.acquire()
        self._ui_lock.release()

    def showConfigUI(self):
        self._ui_lock.acquire()
        self._cancelled = False
        self.show_config_ui_trigger.emit()

    def _actualShowConfigUI(self):
        self._disable_size_callbacks = True

        if self._ui_view is None:
            self._createConfigUI()
        self._ui_view.show()

        self._ui_view.findChild(QObject, "Width").setProperty("text", str(self._width))
        self._ui_view.findChild(QObject, "Depth").setProperty("text", str(self._depth))
        self._disable_size_callbacks = False

        self._ui_view.findChild(QObject, "Base_Height").setProperty("text", str(self.base_height))
        self._ui_view.findChild(QObject, "Peak_Height").setProperty("text", str(self.peak_height))
        self._ui_view.findChild(QObject, "Smoothing").setProperty("value", self.smoothing)

    def _createConfigUI(self):
        if self._ui_view is None:
            Logger.log("d", "Creating ProfileImporter config UI")
            path = os.path.join(PluginRegistry.getInstance().getPluginPath("ProfileImporter"), "ConfigUI.qml")
            self._ui_view = Application.getInstance().createQmlComponent(path, {"manager": self})
            self._ui_view.setFlags(self._ui_view.flags() & ~Qt.WindowCloseButtonHint & ~Qt.WindowMinimizeButtonHint & ~Qt.WindowMaximizeButtonHint);
            self._disable_size_callbacks = False

    @pyqtSlot()
    def onOkButtonClicked(self):
        self._cancelled = False
        self._ui_view.close()
        self._ui_lock.release()

    @pyqtSlot()
    def onCancelButtonClicked(self):
        self._cancelled = True
        self._ui_view.close()
        self._ui_lock.release()

    @pyqtSlot(str)
    def onWidthChanged(self, value):
        if self._ui_view and not self._disable_size_callbacks:
            if len(value) > 0:
                self._width = float(value.replace(",", "."))
            else:
                self._width = 0

            self._depth = self._width / self._aspect
            self._disable_size_callbacks = True
            self._ui_view.findChild(QObject, "Depth").setProperty("text", str(self._depth))
            self._disable_size_callbacks = False

    @pyqtSlot(str)
    def onDepthChanged(self, value):
        if self._ui_view and not self._disable_size_callbacks:
            if len(value) > 0:
                self._depth = float(value.replace(",", "."))
            else:
                self._depth = 0

            self._width = self._depth * self._aspect
            self._disable_size_callbacks = True
            self._ui_view.findChild(QObject, "Width").setProperty("text", str(self._width))
            self._disable_size_callbacks = False

    @pyqtSlot(str)
    def onBaseHeightChanged(self, value):
        if (len(value) > 0):
            self.base_height = float(value.replace(",", "."))
        else:
            self.base_height = 0

    @pyqtSlot(str)
    def onPeakHeightChanged(self, value):
        if (len(value) > 0):
            self.peak_height = float(value.replace(",", "."))
        else:
            self.peak_height = 0

    @pyqtSlot(float)
    def onSmoothingChanged(self, value):
        self.smoothing = int(value)

    @pyqtSlot(int)
    def onImageColorInvertChanged(self, value):
        self.image_color_invert = (value == 1)

 

Of course you can't interpret a .cfg file as an image. But even when removing the rudimentary code from ImageReader the file is not recognized.. :s

 

Any help would be greatly appreciated ?

Share this post


Link to post
Share on other sites

There are a few mistakes that I see on first glance;

The "register" function in the __init__ must return a dict. The keys of the dict must match plugin types that Cura knows about. In this case, you return an unknown type ("p_reader").
You're also basing it on a meshreader, which is, as the name implies, a plugin that's made to read meshes. It would be better to base it on the profile_reader plugin type. An example of it can be found in the LegacyProfileReader plugin.
 

Share this post


Link to post
Share on other sites

I did some research and indeed you're right. I'm not making the global profiles. I understand this is done by making a generic_material.xml.fdm_material file. However, when I do this I still keep getting errors like this:

 

Exception: When trying to deserialize fdmextruder #2, we received an unknown ID (um2p_plaht_0.8_draft) for container

 

How would I go about properly implementing a new printing material? Could you give me some advice maybe, because I have been tinkering with this for hours now.

Share this post


Link to post
Share on other sites

Right, I see what you're missing. The UM2+ has so called material based qualities. So if you only add a new material type, you're not there yet, you also need the matching quality profile. As we also have "variants" (aka; nozzle sizes), you need to make multiple new quality profiles for each of the nozzle sizes it supports (a single material will need num_qualities * num_variants) of quality profiles to be added.

Adding new materials / qualities like that should not be done by means of a plugin. You don't even need to, as you can just put the files in the resources folder, which ensures that they are loaded on cura startup.

Share this post


Link to post
Share on other sites
4 minutes ago, nallath said:

Right, I see what you're missing. The UM2+ has so called material based qualities. So if you only add a new material type, you're not there yet, you also need the matching quality profile. As we also have "variants" (aka; nozzle sizes), you need to make multiple new quality profiles for each of the nozzle sizes it supports (a single material will need num_qualities * num_variants) of quality profiles to be added.

 

Yes, thank you. I did that actually. But the cura.log says my HT-PLA doesn't have a fallback material, which is obviously true because Ultimaker doesn't have an HT-PLA material. The thing that comes closest is Tough PLA I'd say but that is not supported on the Ultimaker 2+.

 

So I added 3 material based qualities (.inst.cfg files) for the UM2E+ so far. It's for the 0.8 mm variant with the qualities draft (0.2 mm ), verydraft (0.3 mm) and superdraft (0.4 mm). I'd like to maintain Ultimaker standards as much as possible to not get confused. When I select 0.8 mm with the draft quality and HT-PLA all is fine and dandy. When I select 0.3 mm layerthickness or higher Cura immediately crashes. Even though I wrote profiles for those exact layerheights. I think it must have something to do with 0.2 mm already being supported natively, but 0.3 mm and above are not.

Share this post


Link to post
Share on other sites
1 hour ago, nallath said:

Did you add a generic HT-PLA? That's what it always tries to use as a fallback material.

 

Yea I kinda figured that is how to add global profiles. So I just copied my makerpoint_plaht.xml.fdm_material to generic_plaht.xml.fdm_material. In the generic file I changed the brand and color to Generic. Still crashes though at 0.8 mm nozzle when I add the material based qualities for 0.3 mm and 0.4 mm to the quality folder of the ultimaker2_plus.

Share this post


Link to post
Share on other sites

So the fallback error is actually gone but I still get the uncaught error:

 

2018-06-22 16:07:43,416 - INFO - [MainThread] cura.Machines.ContainerNode.getContainer [39]: Lazy-loading container [makerpoint_plaht_ultimaker2_extended_plus_0.8_mm]
2018-06-22 16:07:43,474 - INFO - [MainThread] cura.Settings.MachineManager._updateQualityWithMaterial [1128]: Updating quality/quality_changes due to material change
2018-06-22 16:07:43,483 - DEBUG - [MainThread] cura.Settings.MachineManager._updateQualityWithMaterial [1135]: Current quality type = [fast]
2018-06-22 16:07:43,490 - INFO - [MainThread] cura.Settings.MachineManager._updateQualityWithMaterial [1161]: The current quality type [fast] is not available, switching to [draft] instead
2018-06-22 16:07:43,498 - INFO - [MainThread] cura.Machines.ContainerNode.getContainer [39]: Lazy-loading container [um2p_plaht_0.8_draft]
2018-06-22 16:07:43,503 - DEBUG - [MainThread] LocalContainerProvider.LocalContainerProvider.loadContainer [53]: Loading container um2p_plaht_0.8_draft
2018-06-22 16:07:43,531 - DEBUG - [MainThread] UM.Settings.ContainerRegistry.addContainer [355]: Container [um2p_plaht_0.8_draft] added.
2018-06-22 16:07:43,542 - DEBUG - [MainThread] cura.Machines.Models.CustomQualityProfilesDropDownMenuModel._update [15]: Updating CustomQualityProfilesDropDownMenuModel.
2018-06-22 16:07:43,549 - INFO - [MainThread] cura.Machines.QualityManager.getQualityChangesGroups [186]: Cannot find node for machine def [ultimaker2_plus] in QualityChanges lookup table
2018-06-22 16:07:43,557 - DEBUG - [MainThread] cura.Machines.Models.QualityProfilesDropDownMenuModel._update [50]: Updating QualityProfilesDropDownMenuModel.
2018-06-22 16:07:43,609 - CRITICAL - [MainThread] cura.CrashHandler.__init__ [66]: An uncaught error has occurred!
2018-06-22 16:07:43,618 - CRITICAL - [MainThread] cura.CrashHandler.__init__ [69]: Traceback (most recent call last):
2018-06-22 16:07:43,625 - CRITICAL - [MainThread] cura.CrashHandler.__init__ [69]:   File "X:\3.3\build\inst\lib\python3.5\site-packages\cura\Machines\Models\QualityProfilesDropDownMenuModel.py", line 70, in _update
2018-06-22 16:07:43,632 - CRITICAL - [MainThread] cura.CrashHandler.__init__ [69]:   File "X:\3.3\build\inst\lib\python3.5\site-packages\cura\Machines\Models\QualityProfilesDropDownMenuModel.py", line 97, in _fetchLayerHeight
2018-06-22 16:07:43,640 - CRITICAL - [MainThread] cura.CrashHandler.__init__ [69]: AttributeError: 'NoneType' object has no attribute 'getContainer'
2018-06-22 16:07:43,802 - DEBUG - [MainThread] LocalContainerProvider.LocalContainerProvider.loadContainer [53]: Loading container ultimaker2_plus
2018-06-22 16:07:44,134 - DEBUG - [MainThread] UM.Settings.ContainerRegistry.addContainer [355]: Container [ultimaker2_plus] added.

Cura works fine (without crashes at least) untill I add the .inst.cfg profiles. For example the draft profile that is loaded by default:

 

[general]
version = 3
name = Fast
definition = ultimaker2_plus

[metadata]
setting_version = 4
type = quality
quality_type = draft
weight = -1
material = makerpoint_plaht
variant = 0.8 mm

[values]
cool_fan_full_at_height = =layer_height_0 + 2 * layer_height
cool_min_speed = 2
infill_sparse_density = 100
infill_line_width = =round(line_width * 0.75 / 0.75, 2)
infill_pattern = cubic
line_width = =machine_nozzle_size * 0.9375
machine_nozzle_cool_down_speed = 0.75
machine_nozzle_heat_up_speed = 1.6
material_final_print_temperature = =max(-273.15, material_print_temperature - 5)
material_initial_print_temperature = =max(-273.15, material_print_temperature)
material_print_temperature = =default_material_print_temperature + 10
speed_print = 45
top_bottom_thickness = =layer_height * 6
wall_line_width = =round(line_width * 0.75 / 0.75, 2)
wall_thickness = =wall_line_width_0 + wall_line_width_x
cool_fan_speed_max = =100
layer_height = 0.2
prime_tower_enable = False
wall_line_width_x = =round(wall_line_width * 0.75 / 0.75, 2)
gradual_infill_step_height = =3 * layer_height
retract_at_layer_change = False
support_angle = 70
support_line_width = =line_width * 0.75
support_xy_distance = =wall_line_width_0 * 1.5

 

Edited by Anteino

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Our picks

    • How to 3D print with reinforced engineering materials
      Ultimaker is hosting a webinar where we explain how you can achieve and maintain a high print success rate using these new reinforced engineering materials. Learn from Ultimaker's Product Manager of Materials and top chemical engineer Bart van As how you can take your 3D printing to that next level.
      • 2 replies
    • "Back To The Future" using Generative Design & Investment Casting
      Designing for light-weight parts is becoming more important, and I’m a firm believer in the need to produce lighter weight, less over-engineered parts for the future. This is for sustainability reasons because we need to be using less raw materials and, in things like transportation, it impacts the energy usage of the product during it’s service life.
        • Like
      • 12 replies
×

Important Information

Welcome to the Ultimaker Community of 3D printing experts. Visit the following links to read more about our Terms of Use or our Privacy Policy. Thank you!