Merge branch 'master' into CURA-4253_psp

This commit is contained in:
ChrisTerBeke 2017-10-16 17:16:00 +02:00
commit 205ac7c50c
18 changed files with 434 additions and 90 deletions

1
.gitignore vendored
View File

@ -42,6 +42,7 @@ plugins/FlatProfileExporter
plugins/ProfileFlattener
plugins/cura-god-mode-plugin
plugins/cura-big-flame-graph
plugins/cura-siemensnx-plugin
#Build stuff
CMakeCache.txt

View File

@ -1,18 +1,27 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import sys
import platform
import traceback
import webbrowser
import faulthandler
import tempfile
import os
import urllib
import os.path
import time
import json
import ssl
import urllib.request
import urllib.error
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, Qt, QCoreApplication
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QHBoxLayout, QVBoxLayout, QLabel, QTextEdit
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QCoreApplication
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox, QPushButton
from UM.Logger import Logger
from UM.View.GL.OpenGL import OpenGL
from UM.i18n import i18nCatalog
from UM.Platform import Platform
catalog = i18nCatalog("cura")
MYPY = False
@ -35,83 +44,238 @@ fatal_exception_types = [
SystemError,
]
def show(exception_type, value, tb):
Logger.log("c", "An uncaught exception has occurred!")
for line in traceback.format_exception(exception_type, value, tb):
for part in line.rstrip("\n").split("\n"):
Logger.log("c", part)
class CrashHandler:
crash_url = "https://stats.ultimaker.com/api/cura"
if not CuraDebugMode and exception_type not in fatal_exception_types:
return
def __init__(self, exception_type, value, tb):
application = QCoreApplication.instance()
if not application:
sys.exit(1)
self.exception_type = exception_type
self.value = value
self.traceback = tb
dialog = QDialog()
dialog.setMinimumWidth(640)
dialog.setMinimumHeight(640)
dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report"))
# While we create the GUI, the information will be stored for sending afterwards
self.data = dict()
self.data["time_stamp"] = time.time()
layout = QVBoxLayout(dialog)
Logger.log("c", "An uncaught exception has occurred!")
for line in traceback.format_exception(exception_type, value, tb):
for part in line.rstrip("\n").split("\n"):
Logger.log("c", part)
#label = QLabel(dialog)
#pixmap = QPixmap()
#try:
# data = urllib.request.urlopen("http://www.randomkittengenerator.com/cats/rotator.php").read()
# pixmap.loadFromData(data)
#except:
# try:
# from UM.Resources import Resources
# path = Resources.getPath(Resources.Images, "kitten.jpg")
# pixmap.load(path)
# except:
# pass
#pixmap = pixmap.scaled(150, 150)
#label.setPixmap(pixmap)
#label.setAlignment(Qt.AlignCenter)
#layout.addWidget(label)
if not CuraDebugMode and exception_type not in fatal_exception_types:
return
label = QLabel(dialog)
layout.addWidget(label)
application = QCoreApplication.instance()
if not application:
sys.exit(1)
#label.setScaledContents(True)
label.setText(catalog.i18nc("@label", """<p>A fatal exception has occurred that we could not recover from!</p>
<p>Please use the information below to post a bug report at <a href=\"http://github.com/Ultimaker/Cura/issues\">http://github.com/Ultimaker/Cura/issues</a></p>
"""))
self._createDialog()
textarea = QTextEdit(dialog)
layout.addWidget(textarea)
## Creates a modal dialog.
def _createDialog(self):
try:
from UM.Application import Application
version = Application.getInstance().getVersion()
except:
version = "Unknown"
self.dialog = QDialog()
self.dialog.setMinimumWidth(640)
self.dialog.setMinimumHeight(640)
self.dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report"))
trace = "".join(traceback.format_exception(exception_type, value, tb))
layout = QVBoxLayout(self.dialog)
crash_info = "Version: {0}\nPlatform: {1}\nQt: {2}\nPyQt: {3}\n\nException:\n{4}"
crash_info = crash_info.format(version, platform.platform(), QT_VERSION_STR, PYQT_VERSION_STR, trace)
layout.addWidget(self._messageWidget())
layout.addWidget(self._informationWidget())
layout.addWidget(self._exceptionInfoWidget())
layout.addWidget(self._logInfoWidget())
layout.addWidget(self._userDescriptionWidget())
layout.addWidget(self._buttonsWidget())
tmp_file_fd, tmp_file_path = tempfile.mkstemp(prefix = "cura-crash", text = True)
os.close(tmp_file_fd)
with open(tmp_file_path, "w") as f:
faulthandler.dump_traceback(f, all_threads=True)
with open(tmp_file_path, "r") as f:
data = f.read()
def _messageWidget(self):
label = QLabel()
label.setText(catalog.i18nc("@label crash message", """<p><b>A fatal exception has occurred. Please send us this Crash Report to fix the problem</p></b>
<p>Please use the "Send report" button to post a bug report automatically to our servers</p>
"""))
msg = "-------------------------\n"
msg += data
crash_info += "\n\n" + msg
return label
textarea.setText(crash_info)
def _informationWidget(self):
group = QGroupBox()
group.setTitle(catalog.i18nc("@title:groupbox", "System information"))
layout = QVBoxLayout()
label = QLabel()
buttons = QDialogButtonBox(QDialogButtonBox.Close, dialog)
layout.addWidget(buttons)
buttons.addButton(catalog.i18nc("@action:button", "Open Web Page"), QDialogButtonBox.HelpRole)
buttons.rejected.connect(dialog.close)
buttons.helpRequested.connect(lambda: webbrowser.open("http://github.com/Ultimaker/Cura/issues"))
try:
from UM.Application import Application
self.cura_version = Application.getInstance().getVersion()
except:
self.cura_version = catalog.i18nc("@label unknown version of Cura", "Unknown")
dialog.exec_()
sys.exit(1)
crash_info = catalog.i18nc("@label Cura version", "<b>Cura version:</b> {version}<br/>").format(version = self.cura_version)
crash_info += catalog.i18nc("@label Platform", "<b>Platform:</b> {platform}<br/>").format(platform = platform.platform())
crash_info += catalog.i18nc("@label Qt version", "<b>Qt version:</b> {qt}<br/>").format(qt = QT_VERSION_STR)
crash_info += catalog.i18nc("@label PyQt version", "<b>PyQt version:</b> {pyqt}<br/>").format(pyqt = PYQT_VERSION_STR)
crash_info += catalog.i18nc("@label OpenGL", "<b>OpenGL:</b> {opengl}<br/>").format(opengl = self._getOpenGLInfo())
label.setText(crash_info)
layout.addWidget(label)
group.setLayout(layout)
self.data["cura_version"] = self.cura_version
self.data["os"] = {"type": platform.system(), "version": platform.version()}
self.data["qt_version"] = QT_VERSION_STR
self.data["pyqt_version"] = PYQT_VERSION_STR
return group
def _getOpenGLInfo(self):
info = "<ul>"
info += catalog.i18nc("@label OpenGL version", "<li>OpenGL Version: {version}</li>").format(version = OpenGL.getInstance().getOpenGLVersion())
info += catalog.i18nc("@label OpenGL vendor", "<li>OpenGL Vendor: {vendor}</li>").format(vendor = OpenGL.getInstance().getGPUVendorName())
info += catalog.i18nc("@label OpenGL renderer", "<li>OpenGL Renderer: {renderer}</li>").format(renderer = OpenGL.getInstance().getGPUType())
info += "</ul>"
self.data["opengl"] = {"version": OpenGL.getInstance().getOpenGLVersion(), "vendor": OpenGL.getInstance().getGPUVendorName(), "type": OpenGL.getInstance().getGPUType()}
return info
def _exceptionInfoWidget(self):
group = QGroupBox()
group.setTitle(catalog.i18nc("@title:groupbox", "Exception traceback"))
layout = QVBoxLayout()
text_area = QTextEdit()
trace_dict = traceback.format_exception(self.exception_type, self.value, self.traceback)
trace = "".join(trace_dict)
text_area.setText(trace)
text_area.setReadOnly(True)
layout.addWidget(text_area)
group.setLayout(layout)
# Parsing all the information to fill the dictionary
summary = trace_dict[len(trace_dict)-1].rstrip("\n")
module = trace_dict[len(trace_dict)-2].rstrip("\n").split("\n")
module_split = module[0].split(", ")
filepath = module_split[0].split("\"")[1]
directory, filename = os.path.split(filepath)
line = int(module_split[1].lstrip("line "))
function = module_split[2].lstrip("in ")
code = module[1].lstrip(" ")
# Using this workaround for a cross-platform path splitting
split_path = []
folder_name = ""
# Split until reach folder "cura"
while folder_name != "cura":
directory, folder_name = os.path.split(directory)
if not folder_name:
break
split_path.append(folder_name)
# Look for plugins. If it's not a plugin, the current cura version is set
isPlugin = False
module_version = self.cura_version
module_name = "Cura"
if split_path.__contains__("plugins"):
isPlugin = True
# Look backwards until plugin.json is found
directory, name = os.path.split(filepath)
while not os.listdir(directory).__contains__("plugin.json"):
directory, name = os.path.split(directory)
json_metadata_file = os.path.join(directory, "plugin.json")
try:
with open(json_metadata_file, "r") as f:
try:
metadata = json.loads(f.read())
module_version = metadata["version"]
module_name = metadata["name"]
except json.decoder.JSONDecodeError:
# Not throw new exceptions
Logger.logException("e", "Failed to parse plugin.json for plugin %s", name)
except:
# Not throw new exceptions
pass
exception_dict = dict()
exception_dict["traceback"] = {"summary": summary, "full_trace": trace}
exception_dict["location"] = {"path": filepath, "file": filename, "function": function, "code": code, "line": line,
"module_name": module_name, "version": module_version, "is_plugin": isPlugin}
self.data["exception"] = exception_dict
return group
def _logInfoWidget(self):
group = QGroupBox()
group.setTitle(catalog.i18nc("@title:groupbox", "Logs"))
layout = QVBoxLayout()
text_area = QTextEdit()
tmp_file_fd, tmp_file_path = tempfile.mkstemp(prefix = "cura-crash", text = True)
os.close(tmp_file_fd)
with open(tmp_file_path, "w") as f:
faulthandler.dump_traceback(f, all_threads=True)
with open(tmp_file_path, "r") as f:
logdata = f.read()
text_area.setText(logdata)
text_area.setReadOnly(True)
layout.addWidget(text_area)
group.setLayout(layout)
self.data["log"] = logdata
return group
def _userDescriptionWidget(self):
group = QGroupBox()
group.setTitle(catalog.i18nc("@title:groupbox", "User description"))
layout = QVBoxLayout()
# When sending the report, the user comments will be collected
self.user_description_text_area = QTextEdit()
self.user_description_text_area.setFocus(True)
layout.addWidget(self.user_description_text_area)
group.setLayout(layout)
return group
def _buttonsWidget(self):
buttons = QDialogButtonBox()
buttons.addButton(QDialogButtonBox.Close)
buttons.addButton(catalog.i18nc("@action:button", "Send report"), QDialogButtonBox.AcceptRole)
buttons.rejected.connect(self.dialog.close)
buttons.accepted.connect(self._sendCrashReport)
return buttons
def _sendCrashReport(self):
# Before sending data, the user comments are stored
self.data["user_info"] = self.user_description_text_area.toPlainText()
# Convert data to bytes
binary_data = json.dumps(self.data).encode("utf-8")
# Submit data
kwoptions = {"data": binary_data, "timeout": 5}
if Platform.isOSX():
kwoptions["context"] = ssl._create_unverified_context()
Logger.log("i", "Sending crash report info to [%s]...", self.crash_url)
try:
f = urllib.request.urlopen(self.crash_url, **kwoptions)
Logger.log("i", "Sent crash report info.")
f.close()
except urllib.error.HTTPError:
Logger.logException("e", "An HTTP error occurred while trying to send crash report")
except Exception: # We don't want any exception to cause problems
Logger.logException("e", "An exception occurred while trying to send crash report")
os._exit(1)
def show(self):
self.dialog.exec_()
os._exit(1)

View File

@ -10,7 +10,6 @@ from UM.Application import Application
import UM.FlameProfiler
from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.ExtruderStack import ExtruderStack #To listen to changes on the extruders.
from cura.Settings.MachineManager import MachineManager #To listen to changes on the extruders of the currently active machine.
catalog = i18nCatalog("cura")

View File

@ -41,8 +41,9 @@ if "PYTHONPATH" in os.environ.keys(): # If PYTHONPATH is u
sys.path.insert(1, PATH_real) # Insert it at 1 after os.curdir, which is 0.
def exceptHook(hook_type, value, traceback):
import cura.CrashHandler
cura.CrashHandler.show(hook_type, value, traceback)
from cura.CrashHandler import CrashHandler
_crash_handler = CrashHandler(hook_type, value, traceback)
_crash_handler.show()
sys.excepthook = exceptHook

View File

@ -79,7 +79,7 @@ class MachineSettingsAction(MachineAction):
@pyqtSlot()
def onFinishAction(self):
# Restore autoslicing when the machineaction is dismissed
if self._backend.determineAutoSlicing():
if self._backend and self._backend.determineAutoSlicing():
self._backend.tickle()
def _onActiveExtruderStackChanged(self):

View File

@ -118,24 +118,23 @@ class VersionUpgrade27to30(VersionUpgrade):
if not parser.has_section("general"):
parser.add_section("general")
# Need to exclude the following names:
# - ultimaker2_plus
# - ultimaker2_go
# - ultimaker2_extended
# - ultimaker2_extended_plus
exclude_prefix_list = ["ultimaker2_plus_",
"ultimaker2_go_",
"ultimaker2_extended_",
"ultimaker2_extended_plus_"]
# The ultimaker 2 family
ultimaker2_prefix_list = ["ultimaker2_extended_",
"ultimaker2_go_",
"ultimaker2_"]
# ultimaker 2+ is a different family, so don't do anything with those
exclude_prefix_list = ["ultimaker2_extended_plus_",
"ultimaker2_plus_"]
# set machine definition to "ultimaker2" for the custom quality profiles that can be for the ultimaker 2 family
file_base_name = os.path.basename(filename)
if file_base_name.startswith("ultimaker2_"):
skip_this = False
for exclude_prefix in exclude_prefix_list:
if file_base_name.startswith(exclude_prefix):
skip_this = True
break
if not skip_this:
parser["general"]["definition"] = "ultimaker2"
is_ultimaker2_family = False
if not any(file_base_name.startswith(ep) for ep in exclude_prefix_list):
is_ultimaker2_family = any(file_base_name.startswith(ep) for ep in ultimaker2_prefix_list)
# ultimaker2 family quality profiles used to set as "fdmprinter" profiles
if is_ultimaker2_family and parser["general"]["definition"] == "fdmprinter":
parser["general"]["definition"] = "ultimaker2"
# Update version numbers
parser["general"]["version"] = "2"

View File

@ -63,6 +63,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr ""
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr ""
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr ""
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr ""
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Druck vollendet"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "&Plugins"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "Plugins durchsuchen..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "Installierte plugins..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Impresión terminada"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "&Complementos"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "Examinar complementos..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "Complementos instalados..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Tulosta valmis"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "&Lisäosat"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "Selaa lisäosia..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "Asennetut lisäoset..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Impression terminée"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "&Plug-ins"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "Parcourir les plug-ins..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "Plug-ins installés..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Stampa finita"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "&Plugin"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "Sfoglia plugin..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "Plugin installati..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Print klaar"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "&Plugins"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "Door invoegtoepassingen bladeren..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "Geïnstalleerde plugins..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -63,6 +63,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Drukowanie zakończone"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "W&tyczki"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "Przeglądaj wtyczki..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "Zainstalowane wtyczki..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -62,6 +62,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Impressão Concluída"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "&Complementos"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "Navegar complementos..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "Complementos instalados..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -63,6 +63,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Печать завершена"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "Плагины"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "Просмотр плагинов..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "Установленные плагины..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Baskı tamamlandı"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "&Eklentiler"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "Eklentilere göz at..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "Yüklü eklentiler..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"

View File

@ -63,6 +63,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "打印完成"
#: Manually added for resources/Cura/Cura.qml
msgctxt "@title:menu menubar:toplevel"
msgid "P&lugins"
msgstr "插件"
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Browse plugins..."
msgstr "浏览插件..."
#: Manually added for resources/Cura/Actions.qml
msgctxt "@action:menu"
msgid "Installed plugins..."
msgstr "已安装插件..."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"