Compare commits

...

13 Commits

Author SHA1 Message Date
Julien Duponchelle
4e172fc7e3 2.0.2 2017-05-30 09:02:02 +02:00
Julien Duponchelle
8ed8a2c115 Show a default symbol in case of corrupted file
Fix https://github.com/GNS3/gns3-server/issues/1043
2017-05-24 12:26:18 +02:00
Julien Duponchelle
073665a75d When another gui is already running exit instead of proper close to avoid any issue
Fix #2059
2017-05-23 18:00:02 +02:00
Julien Duponchelle
4ccc67aa46 Fix duplicate on remote server use wrong location
Fix https://github.com/GNS3/gns3-server/issues/1040
2017-05-23 17:25:29 +02:00
Julien Duponchelle
6e2632e91f Display the location of settings when we disallow opening due to old release 2017-05-23 15:54:35 +02:00
Julien Duponchelle
38ddcde902 Improve search for dynamips in development on OSX 2017-05-23 14:24:54 +02:00
Julien Duponchelle
436563afcb Merge pull request #2079 from GNS3/pyup-update-pytest-3.0.7-to-3.1.0
Update pytest to 3.1.0
2017-05-23 08:29:55 +02:00
pyup-bot
7eaab3e38b Update pytest from 3.0.7 to 3.1.0 2017-05-23 02:25:48 +02:00
Julien Duponchelle
0927a2a8c9 Fix error display when loading a .png custom symbol 2017-05-22 13:38:04 +02:00
Julien Duponchelle
87e6159ff6 Fix a crash in the progress dialog
Fix #2064
2017-05-22 09:56:31 +02:00
Julien Duponchelle
effdcf5e24 Fix a race condition when exporting a closed project
Fix #2078
2017-05-22 09:54:17 +02:00
Julien Duponchelle
021cdd2e65 Fix RuntimeError: wrapped C/C++ object of type NodeItem has been deleted
Fix #2070
2017-05-19 17:39:34 +02:00
Julien Duponchelle
363c4a9966 2.0.2dev1 2017-05-16 09:15:18 +02:00
13 changed files with 79 additions and 33 deletions

View File

@@ -1,5 +1,17 @@
# Change Log
## 2.0.2 30/05/2017
* Show a default symbol in case of corrupted file
* When another gui is already running exit instead of proper close to avoid any issue
* Fix duplicate on remote server use wrong location
* Display the location of settings when we disallow opening due to old release
* Improve search for dynamips in development on OSX
* Fix error display when loading a .png custom symbol
* Fix a crash in the progress dialog
* Fix a race condition when exporting a closed project
* Fix RuntimeError: wrapped C/C++ object of type NodeItem has been deleted
## 2.0.1 16/05/2017
* Improve inline help. Fixes #1999. Add a warning about wifi interfaces in the cloud. Fixes #1902.

View File

@@ -1,6 +1,6 @@
-rrequirements.txt
pep8==1.7.0
pytest==3.0.7
pytest==3.1.0
pytest-pythonpath==0.7.1 # useful for running tests outside tox
pytest-timeout==1.2.0

View File

@@ -51,7 +51,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "sync+https://b9912fe90df3496e98ccfb44d9b9baf0:ac42a40e66314b48883243402b957290@sentry.io/38506"
DSN = "sync+https://063691a489374eda912ad454a1d80777:5ddb34d6b23c4a08b040efce23aaac78@sentry.io/38506"
if hasattr(sys, "frozen"):
cacert = get_resource("cacert.pem")
if cacert is not None and os.path.isfile(cacert):

View File

@@ -132,7 +132,15 @@ class ProjectDialog(QtWidgets.QDialog, Ui_ProjectDialog):
new_project_name)
name = name.strip()
if reply and len(name) > 0:
Controller.instance().post("/projects/{project_id}/duplicate".format(project_id=project_id), self._duplicateCallback, body={"name": name})
if Controller.instance().isRemote():
Controller.instance().post("/projects/{project_id}/duplicate".format(project_id=project_id),
self._duplicateCallback,
body={"name": name})
else:
project_location = os.path.join(Topology.instance().projectsDirPath(), name)
Controller.instance().post("/projects/{project_id}/duplicate".format(project_id=project_id),
self._duplicateCallback,
body={"name": name, "path": project_location})
def _duplicateCallback(self, result, error=False, **kwargs):
if error:

View File

@@ -142,8 +142,9 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
def symbol(self):
return self._symbol
def _symbolLoadedCallback(self, path):
renderer = QImageSvgRenderer(path)
@qslot
def _symbolLoadedCallback(self, path, *args):
renderer = QImageSvgRenderer(path, fallback=":/icons/cancel.svg")
renderer.setObjectName(path)
self.setSharedRenderer(renderer)
if self._node.settings().get("symbol") != self._symbol:

View File

@@ -20,7 +20,7 @@ Base class for shape items (Rectangle, ellipse etc.).
"""
import xml.etree.ElementTree as ET
from ..qt import QtCore, QtGui, QtWidgets, QtSvg
from ..qt import QtCore, QtGui, QtWidgets
from .drawing_item import DrawingItem
from .utils import colorFromSvg
@@ -35,12 +35,11 @@ class ShapeItem(DrawingItem):
QtCore.Qt.SolidLine: "",
QtCore.Qt.NoPen: None,
QtCore.Qt.DashLine: "25, 25",
QtCore.Qt.DotLine: "5, 25",
QtCore.Qt.DashDotLine: "5, 25, 25",
QtCore.Qt.DotLine: "5, 25",
QtCore.Qt.DashDotLine: "5, 25, 25",
QtCore.Qt.DashDotDotLine: "25, 25, 5, 25, 5"
}
"""
Base class to draw shapes on the scene.
"""
@@ -192,10 +191,10 @@ class ShapeItem(DrawingItem):
element.set("fill-opacity", str(self.brush().color().alphaF()))
dasharray = self.QT_DASH_TO_SVG[pen.style()]
if dasharray is None: # No border to the element
if dasharray is None: # No border to the element
return element
elif dasharray == "":
pass # Solid line
pass # Solid line
else:
element.set("stroke-dasharray", dasharray)
element.set("stroke-width", str(pen.width()))
@@ -239,7 +238,7 @@ class ShapeItem(DrawingItem):
stroke = svg[0].get("stroke-dasharray")
if stroke:
for (qt_stroke, svg_stroke) in self.QT_DASH_TO_SVG.items():
if svg_stroke == stroke:
if svg_stroke == stroke:
pen.setStyle(qt_stroke)
self.setPen(pen)

View File

@@ -210,7 +210,7 @@ class LocalConfig(QtCore.QObject):
if "version" in self._settings:
if parse_version(self._settings["version"])[:2] > parse_version(__version__)[:2]:
QtWidgets.QApplication(sys.argv) # We need to create an application because settings are loaded before Qt init
QtWidgets.QMessageBox.critical(None, "Version error", "Your settings are for version {} of GNS3. You cannot use a previous version of GNS3 without risking losing data.".format(self._settings["version"]))
QtWidgets.QMessageBox.critical(None, "Version error", "Your settings are for version {} of GNS3. You cannot use a previous version of GNS3 without risking losing data. If you want to reset delete the settings in {}".format(self._settings["version"], self.configDirectory()))
# Exit immediately not clean but we want to avoid any side effect that could corrupt the file
sys.exit(1)

View File

@@ -997,7 +997,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
reply = QtWidgets.QMessageBox.warning(self, "GNS3", "Another GNS3 GUI is already running. Continue?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.No:
self.close()
sys.exit(1)
return
if not sys.platform.startswith("win") and os.geteuid() == 0:

View File

@@ -54,6 +54,8 @@ class DynamipsPreferencesPage(QtWidgets.QWidget, Ui_DynamipsPreferencesPageWidge
file_filter = "Executable (*.exe);;All files (*.*)"
dynamips_path = shutil.which("dynamips")
if sys.platform.startswith("darwin") and dynamips_path is None:
dynamips_path = "/Applications/GNS3.app/Contents/Resources/dynamips"
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select Dynamips", dynamips_path, file_filter)
if not path:
return

View File

@@ -16,21 +16,27 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import xml.etree.ElementTree as ET
from . import QtCore
from . import QtSvg
from . import QtGui
import logging
log = logging.getLogger(__name__)
class QImageSvgRenderer(QtSvg.QSvgRenderer):
"""
Renderer pixmap and svg to SVG item
:param path_or_data: Svg element of path to a SVG
:param fallback: Image to display if the image is not working
"""
def __init__(self, path_or_data=None):
def __init__(self, path_or_data=None, fallback=None):
super().__init__()
self._fallback = fallback
self._svg = """<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{width}" height="{height}"></svg>"""
self.load(path_or_data)
@@ -43,21 +49,38 @@ class QImageSvgRenderer(QtSvg.QSvgRenderer):
except ValueError:
pass # On windows we can get an error because the path is too long (it's the svg data)
res = super().load(path_or_data)
# If we can't render a SVG we load and base64 the image to create a SVG
if self.isValid():
return res
try:
# We load the SVG with ElementTree before
# because Qt when failing loading send noise to logs
# and their is no way to prevent that
if not path_or_data.startswith(":"):
ET.parse(path_or_data)
res = super().load(path_or_data)
# If we can't render a SVG we load and base64 the image to create a SVG
if self.isValid():
return res
except ET.ParseError:
pass
image = QtGui.QImage(path_or_data)
data = QtCore.QByteArray()
buf = QtCore.QBuffer(data)
image.save(buf, 'PNG')
self._svg = """<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{width}" height="{height}">
<image width="{width}" height="{height}" xlink:href="data:image/png;base64,{data}"/>
</svg>""".format(data=bytes(data.toBase64()).decode(),
width=image.rect().width(),
height=image.rect().height())
return super().load(self._svg.encode())
if image.rect().width() > 0:
self._svg = """<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{width}" height="{height}">
<image width="{width}" height="{height}" xlink:href="data:image/png;base64,{data}"/>
</svg>""".format(data=bytes(data.toBase64()).decode(),
width=image.rect().width(),
height=image.rect().height())
res = super().load(self._svg.encode())
elif self._fallback:
log.error("Invalid or corrupted image file")
res = super().load(self._fallback)
else:
self._svg = """<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
</svg>"""
res = super().load(self._svg.encode())
return res
def svg(self):
"""

View File

@@ -35,11 +35,11 @@ class ExportProjectWorker(QtCore.QObject):
self._path = path
def run(self):
self._project.get("/export?include_images={}".format(self._include_images),
self._exportReceived,
downloadProgressCallback=self._downloadFileProgress,
timeout=None)
if self._project:
self._project.get("/export?include_images={}".format(self._include_images),
self._exportReceived,
downloadProgressCallback=self._downloadFileProgress,
timeout=None)
def _exportReceived(self, content, error=False, server=None, context={}, **kwargs):
if error:

View File

@@ -117,7 +117,8 @@ class ProgressDialog(QtWidgets.QProgressDialog):
@qslot
def accept(self):
log.debug("{} thread finished".format(self._worker.objectName()))
if self._worker:
log.debug("{} thread finished".format(self._worker.objectName()))
self._cleanup()
super().accept()

View File

@@ -17,7 +17,7 @@
# __version__ is a human-readable version number.
__version__ = "2.0.1"
__version__ = "2.0.2"
# If it's a git checkout try to add the commit
if "dev" in __version__:
@@ -37,4 +37,4 @@ or negative for a release candidate or beta (after the base version
number has been incremented)
"""
__version_info__ = (2, 0, 1, 0)
__version_info__ = (2, 0, 2, 0)