mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-13 01:37:51 -06:00
Merge branch 'master' of github.com:Ultimaker/Cura into cura_connect_UI_rework
This commit is contained in:
commit
53d083e232
27 changed files with 59138 additions and 123 deletions
|
@ -85,14 +85,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
MimeTypeDatabase.addMimeType(
|
||||
MimeType(
|
||||
name="application/x-curaproject+xml",
|
||||
comment="Cura Project File",
|
||||
suffixes=["curaproject.3mf"]
|
||||
)
|
||||
)
|
||||
|
||||
self._supported_extensions = [".3mf"]
|
||||
self._dialog = WorkspaceDialog()
|
||||
self._3mf_mesh_reader = None
|
||||
|
@ -726,8 +718,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
nodes = []
|
||||
|
||||
base_file_name = os.path.basename(file_name)
|
||||
if base_file_name.endswith(".curaproject.3mf"):
|
||||
base_file_name = base_file_name[:base_file_name.rfind(".curaproject.3mf")]
|
||||
self.setWorkspaceName(base_file_name)
|
||||
return nodes
|
||||
|
||||
|
|
|
@ -18,11 +18,7 @@ catalog = i18nCatalog("cura")
|
|||
|
||||
|
||||
def getMetaData() -> Dict:
|
||||
# Workaround for osx not supporting double file extensions correctly.
|
||||
if Platform.isOSX():
|
||||
workspace_extension = "3mf"
|
||||
else:
|
||||
workspace_extension = "curaproject.3mf"
|
||||
workspace_extension = "3mf"
|
||||
|
||||
metaData = {}
|
||||
if "3MFReader.ThreeMFReader" in sys.modules:
|
||||
|
|
|
@ -15,11 +15,7 @@ from UM.Platform import Platform
|
|||
i18n_catalog = i18nCatalog("uranium")
|
||||
|
||||
def getMetaData():
|
||||
# Workarround for osx not supporting double file extensions correctly.
|
||||
if Platform.isOSX():
|
||||
workspace_extension = "3mf"
|
||||
else:
|
||||
workspace_extension = "curaproject.3mf"
|
||||
workspace_extension = "3mf"
|
||||
|
||||
metaData = {}
|
||||
|
||||
|
@ -36,7 +32,7 @@ def getMetaData():
|
|||
"output": [{
|
||||
"extension": workspace_extension,
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "Cura Project 3MF file"),
|
||||
"mime_type": "application/x-curaproject+xml",
|
||||
"mime_type": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml",
|
||||
"mode": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.BinaryMode
|
||||
}]
|
||||
}
|
||||
|
|
|
@ -44,12 +44,11 @@ UM.PointingRectangle {
|
|||
id: valueLabel
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2)
|
||||
verticalCenter: parent.verticalCenter
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
width: maximumValue.toString().length * 12 * screenScaleFactor
|
||||
width: (maximumValue.toString().length + 1) * 10 * screenScaleFactor
|
||||
text: sliderLabelRoot.value + startFrom // the current handle value, add 1 because layers is an array
|
||||
horizontalAlignment: TextInput.AlignRight
|
||||
|
||||
|
|
|
@ -82,9 +82,16 @@ Item
|
|||
}
|
||||
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||
width: childrenRect.width
|
||||
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Contact") + ":"
|
||||
text: catalog.i18nc("@label", "Website") + ":"
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Email") + ":"
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
}
|
||||
|
@ -100,18 +107,32 @@ Item
|
|||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||
|
||||
Label
|
||||
{
|
||||
text:
|
||||
{
|
||||
if (details.website)
|
||||
{
|
||||
return "<a href=\"" + details.website + "\">" + details.website + "</a>"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
linkColor: UM.Theme.getColor("text_link")
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
text:
|
||||
{
|
||||
if (details.email)
|
||||
{
|
||||
return "<a href=\"mailto:"+details.email+"\">"+details.name+"</a>"
|
||||
}
|
||||
else
|
||||
{
|
||||
return "<a href=\""+details.website+"\">"+details.name+"</a>"
|
||||
return "<a href=\"mailto:" + details.email + "\">" + details.email + "</a>"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
|
|
|
@ -8,7 +8,18 @@ import UM 1.1 as UM
|
|||
|
||||
Item
|
||||
{
|
||||
id: base
|
||||
|
||||
property var packageData
|
||||
property var technicalDataSheetUrl: {
|
||||
var link = undefined
|
||||
if ("Technical Data Sheet" in packageData.links)
|
||||
{
|
||||
link = packageData.links["Technical Data Sheet"]
|
||||
}
|
||||
return link
|
||||
}
|
||||
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
height: visible ? childrenRect.height : 0
|
||||
visible: packageData.type == "material" && packageData.has_configs
|
||||
|
@ -132,4 +143,25 @@ Item
|
|||
width: Math.floor(table.width * 0.1)
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: technical_data_sheet
|
||||
anchors.top: table.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height / 2
|
||||
visible: base.technicalDataSheetUrl !== undefined
|
||||
text:
|
||||
{
|
||||
if (base.technicalDataSheetUrl !== undefined)
|
||||
{
|
||||
return "<a href='%1'>%2</a>".arg(base.technicalDataSheetUrl).arg("Technical Data Sheet")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
linkColor: UM.Theme.getColor("text_link")
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import UM 1.1 as UM
|
|||
|
||||
Item
|
||||
{
|
||||
property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfPackagesByAuthor(model.id) : 1
|
||||
property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1
|
||||
property int installedPackages: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
|
||||
height: childrenRect.height
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
|
|
|
@ -30,7 +30,7 @@ ScrollView
|
|||
id: allPlugins
|
||||
width: parent.width
|
||||
heading: toolbox.viewCategory == "material" ? catalog.i18nc("@label", "Community Contributions") : catalog.i18nc("@label", "Community Plugins")
|
||||
model: toolbox.viewCategory == "material" ? toolbox.authorsModel : toolbox.packagesModel
|
||||
model: toolbox.viewCategory == "material" ? toolbox.materialsAvailableModel : toolbox.pluginsAvailableModel
|
||||
}
|
||||
|
||||
ToolboxDownloadsGrid
|
||||
|
|
|
@ -9,7 +9,7 @@ import UM 1.1 as UM
|
|||
|
||||
Rectangle
|
||||
{
|
||||
property int packageCount: toolbox.viewCategory == "material" ? toolbox.getTotalNumberOfPackagesByAuthor(model.id) : 1
|
||||
property int packageCount: toolbox.viewCategory == "material" ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1
|
||||
property int installedPackages: toolbox.viewCategory == "material" ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
|
||||
id: tileBase
|
||||
width: UM.Theme.getSize("toolbox_thumbnail_large").width + (2 * UM.Theme.getSize("default_lining").width)
|
||||
|
|
|
@ -33,6 +33,9 @@ class AuthorsModel(ListModel):
|
|||
|
||||
def _update(self):
|
||||
items = []
|
||||
if not self._metadata:
|
||||
self.setItems([])
|
||||
return
|
||||
|
||||
for author in self._metadata:
|
||||
items.append({
|
||||
|
|
|
@ -5,9 +5,13 @@ import re
|
|||
from typing import Dict
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtProperty
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Qt.ListModel import ListModel
|
||||
|
||||
from .ConfigsModel import ConfigsModel
|
||||
|
||||
|
||||
## Model that holds cura packages. By setting the filter property the instances held by this model can be changed.
|
||||
class PackagesModel(ListModel):
|
||||
def __init__(self, parent = None):
|
||||
|
@ -34,6 +38,8 @@ class PackagesModel(ListModel):
|
|||
self.addRoleName(Qt.UserRole + 17, "supported_configs")
|
||||
self.addRoleName(Qt.UserRole + 18, "download_count")
|
||||
self.addRoleName(Qt.UserRole + 19, "tags")
|
||||
self.addRoleName(Qt.UserRole + 20, "links")
|
||||
self.addRoleName(Qt.UserRole + 21, "website")
|
||||
|
||||
# List of filters for queries. The result is the union of the each list of results.
|
||||
self._filter = {} # type: Dict[str, str]
|
||||
|
@ -45,10 +51,16 @@ class PackagesModel(ListModel):
|
|||
def _update(self):
|
||||
items = []
|
||||
|
||||
for package in self._metadata:
|
||||
if self._metadata is None:
|
||||
Logger.logException("w", "Failed to load packages for Toolbox")
|
||||
self.setItems(items)
|
||||
return
|
||||
|
||||
for package in self._metadata:
|
||||
has_configs = False
|
||||
configs_model = None
|
||||
|
||||
links_dict = {}
|
||||
if "data" in package:
|
||||
if "supported_configs" in package["data"]:
|
||||
if len(package["data"]["supported_configs"]) > 0:
|
||||
|
@ -56,41 +68,48 @@ class PackagesModel(ListModel):
|
|||
configs_model = ConfigsModel()
|
||||
configs_model.setConfigs(package["data"]["supported_configs"])
|
||||
|
||||
# Links is a list of dictionaries with "title" and "url". Convert this list into a dict so it's easier
|
||||
# to process.
|
||||
link_list = package['data']['links'] if 'links' in package['data'] else []
|
||||
links_dict = {d["title"]: d["url"] for d in link_list}
|
||||
|
||||
if "author_id" not in package["author"] or "display_name" not in package["author"]:
|
||||
package["author"]["author_id"] = ""
|
||||
package["author"]["display_name"] = ""
|
||||
# raise Exception("Detected a package with malformed author data.")
|
||||
|
||||
items.append({
|
||||
"id": package["package_id"],
|
||||
"type": package["package_type"],
|
||||
"name": package["display_name"],
|
||||
"version": package["package_version"],
|
||||
"author_id": package["author"]["author_id"],
|
||||
"author_name": package["author"]["display_name"],
|
||||
"author_email": package["author"]["email"] if "email" in package["author"] else None,
|
||||
"description": package["description"] if "description" in package else None,
|
||||
"icon_url": package["icon_url"] if "icon_url" in package else None,
|
||||
"image_urls": package["image_urls"] if "image_urls" in package else None,
|
||||
"download_url": package["download_url"] if "download_url" in package else None,
|
||||
"last_updated": package["last_updated"] if "last_updated" in package else None,
|
||||
"is_bundled": package["is_bundled"] if "is_bundled" in package else False,
|
||||
"is_active": package["is_active"] if "is_active" in package else False,
|
||||
"is_installed": package["is_installed"] if "is_installed" in package else False,
|
||||
"has_configs": has_configs,
|
||||
"supported_configs": configs_model,
|
||||
"download_count": package["download_count"] if "download_count" in package else 0,
|
||||
"tags": package["tags"] if "tags" in package else []
|
||||
"id": package["package_id"],
|
||||
"type": package["package_type"],
|
||||
"name": package["display_name"],
|
||||
"version": package["package_version"],
|
||||
"author_id": package["author"]["author_id"],
|
||||
"author_name": package["author"]["display_name"],
|
||||
"author_email": package["author"]["email"] if "email" in package["author"] else None,
|
||||
"description": package["description"] if "description" in package else None,
|
||||
"icon_url": package["icon_url"] if "icon_url" in package else None,
|
||||
"image_urls": package["image_urls"] if "image_urls" in package else None,
|
||||
"download_url": package["download_url"] if "download_url" in package else None,
|
||||
"last_updated": package["last_updated"] if "last_updated" in package else None,
|
||||
"is_bundled": package["is_bundled"] if "is_bundled" in package else False,
|
||||
"is_active": package["is_active"] if "is_active" in package else False,
|
||||
"is_installed": package["is_installed"] if "is_installed" in package else False,
|
||||
"has_configs": has_configs,
|
||||
"supported_configs": configs_model,
|
||||
"download_count": package["download_count"] if "download_count" in package else 0,
|
||||
"tags": package["tags"] if "tags" in package else [],
|
||||
"links": links_dict,
|
||||
"website": package["website"] if "website" in package else None,
|
||||
})
|
||||
|
||||
# Filter on all the key-word arguments.
|
||||
for key, value in self._filter.items():
|
||||
if key is "tags":
|
||||
key_filter = lambda item, value = value: value in item["tags"]
|
||||
key_filter = lambda item, v = value: v in item["tags"]
|
||||
elif "*" in value:
|
||||
key_filter = lambda candidate, key = key, value = value: self._matchRegExp(candidate, key, value)
|
||||
key_filter = lambda candidate, k = key, v = value: self._matchRegExp(candidate, k, v)
|
||||
else:
|
||||
key_filter = lambda candidate, key = key, value = value: self._matchString(candidate, key, value)
|
||||
key_filter = lambda candidate, k = key, v = value: self._matchString(candidate, k, v)
|
||||
items = filter(key_filter, items)
|
||||
|
||||
# Execute all filters.
|
||||
|
|
|
@ -83,7 +83,7 @@ class Toolbox(QObject, Extension):
|
|||
"plugins_available": PackagesModel(self),
|
||||
"plugins_installed": PackagesModel(self),
|
||||
"materials_showcase": AuthorsModel(self),
|
||||
"materials_available": PackagesModel(self),
|
||||
"materials_available": AuthorsModel(self),
|
||||
"materials_installed": PackagesModel(self),
|
||||
"materials_generic": PackagesModel(self)
|
||||
} # type: Dict[str, ListModel]
|
||||
|
@ -514,12 +514,14 @@ class Toolbox(QObject, Extension):
|
|||
count += 1
|
||||
return count
|
||||
|
||||
# This slot is only used to get the number of material packages by author, not any other type of packages.
|
||||
@pyqtSlot(str, result = int)
|
||||
def getTotalNumberOfPackagesByAuthor(self, author_id: str) -> int:
|
||||
def getTotalNumberOfMaterialPackagesByAuthor(self, author_id: str) -> int:
|
||||
count = 0
|
||||
for package in self._metadata["materials_available"]:
|
||||
if package["author"]["author_id"] == author_id:
|
||||
count += 1
|
||||
for package in self._metadata["packages"]:
|
||||
if package["package_type"] == "material":
|
||||
if package["author"]["author_id"] == author_id:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
@pyqtSlot(str, result = bool)
|
||||
|
@ -606,8 +608,21 @@ class Toolbox(QObject, Extension):
|
|||
self.resetDownload()
|
||||
return
|
||||
|
||||
# HACK: These request are not handled independently at this moment, but together from the "packages" call
|
||||
do_not_handle = [
|
||||
"materials_available",
|
||||
"materials_showcase",
|
||||
"plugins_available",
|
||||
"plugins_showcase",
|
||||
]
|
||||
|
||||
if reply.operation() == QNetworkAccessManager.GetOperation:
|
||||
for type, url in self._request_urls.items():
|
||||
|
||||
# HACK: Do nothing because we'll handle these from the "packages" call
|
||||
if type in do_not_handle:
|
||||
return
|
||||
|
||||
if reply.url() == url:
|
||||
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200:
|
||||
try:
|
||||
|
@ -623,25 +638,16 @@ class Toolbox(QObject, Extension):
|
|||
if not self._models[type]:
|
||||
Logger.log("e", "Could not find the %s model.", type)
|
||||
break
|
||||
|
||||
# HACK: Eventually get rid of the code from here...
|
||||
if type is "plugins_showcase" or type is "materials_showcase":
|
||||
self._metadata["plugins_showcase"] = json_data["data"]["plugin"]["packages"]
|
||||
self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
|
||||
self._metadata["materials_showcase"] = json_data["data"]["material"]["authors"]
|
||||
self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
|
||||
else:
|
||||
# ...until here.
|
||||
# This hack arises for multiple reasons but the main
|
||||
# one is because there are not separate API calls
|
||||
# for different kinds of showcases.
|
||||
self._metadata[type] = json_data["data"]
|
||||
self._models[type].setMetadata(self._metadata[type])
|
||||
|
||||
self._metadata[type] = json_data["data"]
|
||||
self._models[type].setMetadata(self._metadata[type])
|
||||
|
||||
# Do some auto filtering
|
||||
# TODO: Make multiple API calls in the future to handle this
|
||||
if type is "packages":
|
||||
self._models[type].setFilter({"type": "plugin"})
|
||||
self.buildMaterialsModels()
|
||||
self.buildPluginsModels()
|
||||
if type is "authors":
|
||||
self._models[type].setFilter({"package_types": "material"})
|
||||
if type is "materials_generic":
|
||||
|
@ -755,6 +761,10 @@ class Toolbox(QObject, Extension):
|
|||
def pluginsShowcaseModel(self) -> PackagesModel:
|
||||
return cast(PackagesModel, self._models["plugins_showcase"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def pluginsAvailableModel(self) -> PackagesModel:
|
||||
return cast(PackagesModel, self._models["plugins_available"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def pluginsInstalledModel(self) -> PackagesModel:
|
||||
return cast(PackagesModel, self._models["plugins_installed"])
|
||||
|
@ -763,6 +773,10 @@ class Toolbox(QObject, Extension):
|
|||
def materialsShowcaseModel(self) -> AuthorsModel:
|
||||
return cast(AuthorsModel, self._models["materials_showcase"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def materialsAvailableModel(self) -> AuthorsModel:
|
||||
return cast(AuthorsModel, self._models["materials_available"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def materialsInstalledModel(self) -> PackagesModel:
|
||||
return cast(PackagesModel, self._models["materials_installed"])
|
||||
|
@ -798,3 +812,46 @@ class Toolbox(QObject, Extension):
|
|||
return
|
||||
self._models[model_type].setFilter({})
|
||||
self.filterChanged.emit()
|
||||
|
||||
|
||||
# HACK(S):
|
||||
# --------------------------------------------------------------------------
|
||||
def buildMaterialsModels(self) -> None:
|
||||
|
||||
self._metadata["materials_showcase"] = []
|
||||
self._metadata["materials_available"] = []
|
||||
|
||||
processed_authors = [] # type: List[str]
|
||||
|
||||
for item in self._metadata["packages"]:
|
||||
if item["package_type"] == "material":
|
||||
|
||||
author = item["author"]
|
||||
if author["author_id"] in processed_authors:
|
||||
continue
|
||||
|
||||
if "showcase" in item["tags"]:
|
||||
self._metadata["materials_showcase"].append(author)
|
||||
else:
|
||||
self._metadata["materials_available"].append(author)
|
||||
|
||||
processed_authors.append(author["author_id"])
|
||||
|
||||
self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
|
||||
self._models["materials_available"].setMetadata(self._metadata["materials_available"])
|
||||
|
||||
def buildPluginsModels(self) -> None:
|
||||
|
||||
self._metadata["plugins_showcase"] = []
|
||||
self._metadata["plugins_available"] = []
|
||||
|
||||
for item in self._metadata["packages"]:
|
||||
if item["package_type"] == "plugin":
|
||||
|
||||
if "showcase" in item["tags"]:
|
||||
self._metadata["plugins_showcase"].append(item)
|
||||
else:
|
||||
self._metadata["plugins_available"].append(item)
|
||||
|
||||
self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
|
||||
self._models["plugins_available"].setMetadata(self._metadata["plugins_available"])
|
||||
|
|
|
@ -326,7 +326,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
if self._firmware_name is None:
|
||||
self.sendCommand("M115")
|
||||
|
||||
if (b"ok " in line and b"T:" in line) or b"ok T:" in line or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed
|
||||
if (b"ok " in line and b"T:" in line) or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed
|
||||
extruder_temperature_matches = re.findall(b"T(\d*): ?([\d\.]+) ?\/?([\d\.]+)?", line)
|
||||
# Update all temperature values
|
||||
matched_extruder_nrs = []
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue