mirror of
https://github.com/GNS3/gns3-gui.git
synced 2026-06-02 00:32:03 +03:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e172fc7e3 | ||
|
|
8ed8a2c115 | ||
|
|
073665a75d | ||
|
|
4ccc67aa46 | ||
|
|
6e2632e91f | ||
|
|
38ddcde902 | ||
|
|
436563afcb | ||
|
|
7eaab3e38b | ||
|
|
0927a2a8c9 | ||
|
|
87e6159ff6 | ||
|
|
effdcf5e24 | ||
|
|
021cdd2e65 | ||
|
|
363c4a9966 |
12
CHANGELOG
12
CHANGELOG
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user