Jump to content
Ultimaker Community of 3D Printing Experts

Plugin to import fdm_material and inst.cfg profiles


Recommended Posts

Posted · Plugin to import fdm_material and inst.cfg profiles

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 ?

  • Link to post
    Share on other sites
    Posted · Plugin to import fdm_material and inst.cfg profiles

    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.
     

  • Link to post
    Share on other sites
    Posted · Plugin to import fdm_material and inst.cfg profiles

    Alright, yea, cool. That explains a lot. So if I want to make a completely "new" plugin I should dig deeper in Cura. For now I'll stick to your advice and base it on the LegacyProfileReader plugin.

     

    That's good advice, thanks ?

  • Link to post
    Share on other sites
    Posted · Plugin to import fdm_material and inst.cfg profiles

    But what are you trying to do? Importing a single quality changes will cause issues, as you always need one for the extruder and the global profile.

  • Link to post
    Share on other sites
    Posted · Plugin to import fdm_material and inst.cfg profiles

    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.

  • Link to post
    Share on other sites
    Posted · Plugin to import fdm_material and inst.cfg profiles

    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.

  • Link to post
    Share on other sites
    Posted · Plugin to import fdm_material and inst.cfg profiles
    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.

  • Link to post
    Share on other sites
    Posted · Plugin to import fdm_material and inst.cfg profiles

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

  • Link to post
    Share on other sites
    Posted · Plugin to import fdm_material and inst.cfg profiles
    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.

  • Link to post
    Share on other sites
    Posted (edited) · Plugin to import fdm_material and inst.cfg profiles

    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
  • Link to post
    Share on other sites
    Posted · Plugin to import fdm_material and inst.cfg profiles

    Bump?

  • Link to post
    Share on other sites
    Posted · Plugin to import fdm_material and inst.cfg profiles

    Bump

  • 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

      • Talking additive | The 3D printing podcast
        Why should we be the only ones asking questions? Join us and ask Jabil all your questions on September 22nd 5pm CET
        • 3 replies
      • Ultimaker masterclass: Optimizing your Ultimaker Cura workflow
        Save your seat for either broadcast on September 23.
        What will you learn?
        · Best practices for iterative print preparation and every lesson you should learn from each print
        · The right way to use per-object settings and when they are most useful
        · Easy-to-use resources for anyone who wants to develop their own printer definitions, plugins, or print profiles
        · How to optimize print profile settings and whether to “keep” or “discard” changes
        · When is the right time to export your drawing from CAD? (Based on Ultimaker Cura’s surprising power as 3D control software)
        · And a whole lot more tips and tricks!
         
        How can I join?
        This free masterclass will take place twice:
        1. 11am CEST (5pm SGT, 5am EDT)
        2. 5pm CEST (11am EDT, 8am PDT, 11pm SGT)
        • 0 replies
      • Do you use 3D printing at work? Let us know
        It doesn't matter if you are using 1 Ultimaker or 10, there is inspiration in everything. We're looking for ...
          • Like
        • 1 reply
    ×
    ×
    • Create New...