adding option of opening model as UCP or normal project file

CURA-11403
This commit is contained in:
Saumya Jain 2024-02-29 15:45:13 +01:00
parent c817f11c02
commit f3c49e494e
6 changed files with 187 additions and 35 deletions

View file

@ -1,6 +1,6 @@
# Copyright (c) 2023 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
import zipfile
from typing import List, cast
from PyQt6.QtCore import QObject, QUrl, pyqtSignal, pyqtProperty
@ -33,6 +33,7 @@ from cura.Operations.SetBuildPlateNumberOperation import SetBuildPlateNumberOper
from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode
USER_SETTINGS_PATH = "Cura/user-settings.json"
class CuraActions(QObject):
def __init__(self, parent: QObject = None) -> None:
@ -195,6 +196,13 @@ class CuraActions(QObject):
operation.addOperation(SetObjectExtruderOperation(node, extruder_id))
operation.push()
@pyqtSlot(str, result = bool)
def isProjectUcp(self, file_url) -> bool:
file_name = QUrl(file_url).toLocalFile()
archive = zipfile.ZipFile(file_name, "r")
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
return USER_SETTINGS_PATH in cura_file_names
@pyqtSlot(int)
def setBuildPlateForSelection(self, build_plate_nr: int) -> None:
Logger.log("d", "Setting build plate number... %d" % build_plate_nr)

View file

@ -1979,6 +1979,18 @@ class CuraApplication(QtApplication):
openProjectFile = pyqtSignal(QUrl, bool, arguments = ["project_file", "add_to_recent_files"]) # Emitted when a project file is about to open.
@pyqtSlot(QUrl, bool)
def readLocalUcpFile(self, file: QUrl, add_to_recent_files: bool = True):
file_name = QUrl(file).toLocalFile()
workspace_reader = self.getWorkspaceFileHandler()
if workspace_reader is None:
Logger.log("w", "Workspace reader not found")
return
workspace_reader.getReaderForFile(file_name).setOpenAsUcp(True)
workspace_reader.readLocalFile(file, add_to_recent_files)
@pyqtSlot(QUrl, str, bool)
@pyqtSlot(QUrl, str)
@pyqtSlot(QUrl)

View file

@ -117,6 +117,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._supported_extensions = [".3mf"]
self._dialog = WorkspaceDialog()
self._3mf_mesh_reader = None
self._is_ucp = False
self._container_registry = ContainerRegistry.getInstance()
# suffixes registered with the MimeTypes don't start with a dot '.'
@ -153,6 +154,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._load_profile = False
self._user_settings = {}
def setOpenAsUcp(self, openAsUcp: bool):
self._is_ucp = openAsUcp
def getNewId(self, old_id: str):
"""Get a unique name based on the old_id. This is different from directly calling the registry in that it caches results.
@ -242,7 +246,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Read definition containers
#
machine_definition_id = None
updatable_machines = None if is_ucp else []
updatable_machines = None if self._is_ucp else []
machine_definition_container_count = 0
extruder_definition_container_count = 0
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
@ -609,7 +613,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Load the user specifically exported settings
self._dialog.exportedSettingModel.clear()
if is_ucp:
if self._is_ucp:
try:
self._user_settings = json.loads(archive.open("Cura/user-settings.json").read().decode("utf-8"))
any_extruder_stack = ExtruderManager.getInstance().getExtruderStack(0)
@ -658,8 +662,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._dialog.setVariantType(variant_type_name)
self._dialog.setHasObjectsOnPlate(Application.getInstance().platformActivity)
self._dialog.setMissingPackagesMetadata(missing_package_metadata)
self._dialog.setHasVisibleSelectSameProfileChanged(is_ucp)
self._dialog.setAllowCreatemachine(not is_ucp)
self._dialog.setHasVisibleSelectSameProfileChanged(self._is_ucp)
self._dialog.setAllowCreatemachine(not self._is_ucp)
self._dialog.show()
@ -701,7 +705,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if self._dialog.getResult() == {}:
return WorkspaceReader.PreReadResult.cancelled
self._load_profile = not is_ucp or (self._dialog.selectSameProfileChecked and self._dialog.isCompatibleMachine)
self._load_profile = not self._is_ucp
self._resolve_strategies = self._dialog.getResult()
#
@ -717,7 +721,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if key not in containers_found_dict or strategy is not None:
continue
self._resolve_strategies[key] = "override" if containers_found_dict[key] else "new"
self._is_ucp = False
return WorkspaceReader.PreReadResult.accepted
@call_on_qt_thread

View file

@ -135,16 +135,13 @@ class ThreeMFWriter(MeshWriter):
stack = um_node.callDecoration("getStack")
if stack is not None:
changed_setting_keys = stack.getTop().getAllKeys()
if exported_settings is None:
# Ensure that we save the extruder used for this object in a multi-extrusion setup
if stack.getProperty("machine_extruder_count", "value") > 1:
changed_setting_keys.add("extruder_nr")
# Get values for all changed settings & save them.
for key in changed_setting_keys:
savitar_node.setSetting("cura:" + key, str(stack.getProperty(key, "value")))
else:
# Ensure that we save the extruder used for this object in a multi-extrusion setup
if stack.getProperty("machine_extruder_count", "value") > 1:
changed_setting_keys.add("extruder_nr")
# Get values for all changed settings & save them.
for key in changed_setting_keys:
savitar_node.setSetting("cura:" + key, str(stack.getProperty(key, "value")))
if exported_settings is not None:
# We want to export only the specified settings
if um_node.getName() in exported_settings:
model_exported_settings = exported_settings[um_node.getName()]

View file

@ -701,24 +701,34 @@ UM.MainWindow
if (hasProjectFile)
{
var projectFile = projectFileUrlList[0];
// check preference
var choice = UM.Preferences.getValue("cura/choice_on_open_project");
if (choice == "open_as_project")
var projectFile = projectFileUrlList[0]
var is_ucp = CuraActions.isProjectUcp(projectFile);
print("this file is ucp", is_ucp);
if (is_ucp)
{
openFilesIncludingProjectsDialog.loadProjectFile(projectFile);
askOpenAsProjectOrUcpOrImportModelsDialog.fileUrl = projectFile;
askOpenAsProjectOrUcpOrImportModelsDialog.addToRecent = true;
askOpenAsProjectOrUcpOrImportModelsDialog.show();
}
else if (choice == "open_as_model")
else
{
openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice());
}
else // always ask
{
// ask whether to open as project or as models
askOpenAsProjectOrModelsDialog.fileUrl = projectFile;
askOpenAsProjectOrModelsDialog.addToRecent = true;
askOpenAsProjectOrModelsDialog.show();
// check preference
var choice = UM.Preferences.getValue("cura/choice_on_open_project");
if (choice == "open_as_project")
{
openFilesIncludingProjectsDialog.loadProjectFile(projectFile);
}
else if (choice == "open_as_model")
{
openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice());
}
else // always ask
{
// ask whether to open as project or as models
askOpenAsProjectOrModelsDialog.fileUrl = projectFile;
askOpenAsProjectOrModelsDialog.addToRecent = true;
askOpenAsProjectOrModelsDialog.show();
}
}
}
else
@ -769,14 +779,31 @@ UM.MainWindow
id: askOpenAsProjectOrModelsDialog
}
AskOpenAsProjectOrUcpOrImportModel
{
id: askOpenAsProjectOrUcpOrImportModelsDialog
}
Connections
{
target: CuraApplication
function onOpenProjectFile(project_file, add_to_recent_files)
{
askOpenAsProjectOrModelsDialog.fileUrl = project_file;
askOpenAsProjectOrModelsDialog.addToRecent = add_to_recent_files;
askOpenAsProjectOrModelsDialog.show();
var is_ucp = CuraActions.isProjectUcp(project_file);
print("this file is ucp", is_ucp);
if (is_ucp)
{
askOpenAsProjectOrUcpOrImportModelsDialog.fileUrl = project_file;
askOpenAsProjectOrUcpOrImportModelsDialog.addToRecent = add_to_recent_files;
askOpenAsProjectOrUcpOrImportModelsDialog.show();
}
else
{
askOpenAsProjectOrModelsDialog.fileUrl = project_file;
askOpenAsProjectOrModelsDialog.addToRecent = add_to_recent_files;
askOpenAsProjectOrModelsDialog.show();
}
}
}

View file

@ -0,0 +1,104 @@
// Copyright (c) 2022 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import UM 1.5 as UM
import Cura 1.0 as Cura
UM.Dialog
{
// This dialog asks the user whether he/she wants to open a project file as a project or import models.
id: base
title: catalog.i18nc("@title:window", "Open Universal Cura Project (UCP) file")
width: UM.Theme.getSize("small_popup_dialog").width
height: UM.Theme.getSize("small_popup_dialog").height
backgroundColor: UM.Theme.getColor("main_background")
maximumHeight: height
maximumWidth: width
minimumHeight: maximumHeight
minimumWidth: maximumWidth
modality: Qt.WindowModal
property var fileUrl
property var addToRecent: true //Whether to add this file to the recent files list after reading it.
// load the entire project
function loadProjectFile() {
UM.WorkspaceFileHandler.readLocalFile(base.fileUrl, base.addToRecent);
base.hide()
}
// load the project file as separated models
function loadModelFiles() {
CuraApplication.readLocalFile(base.fileUrl, "open_as_model", base.addToRecent)
base.hide()
}
// load the project file as Universal cura project
function loadUcpFiles() {
CuraApplication.readLocalUcpFile(base.fileUrl, base.addToRecent)
base.hide()
}
// override UM.Dialog accept
function accept () {
// when hitting 'enter', we always open as project unless open_as_model was explicitly stored as preference
if (openAsPreference == "open_as_model") {
loadModelFiles()
} else if (openAsPreference == "open_as_ucp"){
loadUcpFiles()
}else {
loadProjectFile()
}
}
Column
{
anchors.fill: parent
spacing: UM.Theme.getSize("default_margin").height
UM.Label
{
id: questionText
width: parent.width
text: catalog.i18nc("@text:window", "This is a Cura Universal project file. Would you like to open it as a Cura project or Cura Universal Project or import the models from it?")
wrapMode: Text.WordWrap
}
}
onAccepted: loadProjectFile()
onRejected: loadModelFiles()
buttonSpacing: UM.Theme.getSize("thin_margin").width
rightButtons:
[
Cura.SecondaryButton
{
text: catalog.i18nc("@action:button", "Open as project")
onClicked: loadProjectFile()
},
Cura.PrimaryButton
{
text: catalog.i18nc("@action:button", "Open as UCP")
onClicked: loadUcpFiles()
},
Cura.SecondaryButton
{
text: catalog.i18nc("@action:button", "Import models")
onClicked: loadModelFiles()
}
]
}