Merge 2.1 to 2.2

This commit is contained in:
grossmj
2017-10-13 18:11:27 +08:00
40 changed files with 176254 additions and 63 deletions

View File

@@ -6,6 +6,8 @@ notifications:
script:
- docker build -t gns3-gui-test .
- docker run gns3-gui-test
before_deploy:
pip install twine
deploy:
provider: pypi
user: noplay

View File

@@ -1,5 +1,39 @@
# Change Log
## 2.1.0rc2 04/10/2017
* Only show "can't get settings from controller" message in debug mode.
* Remove explicit Telnet path on OS X. Ref #2274
* Disable WebSocket notification for lower PyQT version than 5.6. Fixes #2272
* Increase timeout to 5 minutes when creating and restoring a snapshot.
* Add more information when a request timeouts. Ref #2277.
* Do not show the progress dialog when moving a node. Ref #2275.
* Increase timer before showing a progress dialog from 250ms to 500ms. Ref #2275.
* Use embedded Telnet client on OS X. Ref #2274.
* Fix small bug when adding an appliance template and the name already exists.
* Use RAW sockets by default on Linux for VMware VM connections.
* Increase timeout to get compute servers from controller. Ref #2269.
* Fix "Node doesn't exist" after deletion, but still on the canvas. Fixes #2266.
* Make sure the warning button icon appears in cloud properties dialog on Windows. Fixes #2245.
* Fix bug when cancelling the importation of a configuration file. Fixes #2260.
## 2.1.0rc1 13/09/2017
* Fix missing spice console option in appliance template schema. Fixes #2255.
## 2.1.0b2 05/09/2017
* Fix resources dependencies for cloud configuration page (Fixes: #2251)
* Disabled possibility of moving items under zero layer (Fixes #2220)
* dialog-warning.svg fallback for themed icon (Ref. #2245)
* Change width of packet filters dialog (Fixes #2244)
* Fix high CPU usage when using packet filters. Fixes #2240.
* Toggle Node menu item (Fixes #2227)
* Fixes multiselection styles change crash on LineItem (#2216)
* Fixes loading symbols for QEMU at Edit Page (#2214)
* Fixes exception when right click on Dynamips router in the device dock (#2211)
* Update frame_relay_switch_configuration_page.ui
## 2.1.0b1 04/08/2017
* Info added to the Nat node

View File

@@ -58,12 +58,12 @@ class ComputeManager(QtCore.QObject):
if self._controller.connected() and datetime.datetime.now().timestamp() - self._last_computes_refresh > 5:
self._last_computes_refresh = datetime.datetime.now().timestamp()
self._refreshingComputes = True
self._controller.get("/computes", self._listComputesCallback, showProgress=False, timeout=15)
self._controller.get("/computes", self._listComputesCallback, showProgress=False, timeout=30)
def _controllerConnectedSlot(self):
if self._controller.connected():
self._refreshingComputes = True
self._controller.get("/computes", self._listComputesCallback, showProgress=False, timeout=15)
self._controller.get("/computes", self._listComputesCallback, showProgress=False, timeout=30)
def _controllerDisconnectedSlot(self):
for compute_id in list(self._computes):

View File

@@ -51,7 +51,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "sync+https://11f1de2cf38745ed8075e492c8a274e9:1035b6adddef40c88acd71346241072e@sentry.io/38506"
DSN = "sync+https://8b9c164183434bd7a7a76a64e92f3721:f6e37f470bfe4e1cb60e0e63a87cf72c@sentry.io/38506"
if hasattr(sys, "frozen"):
cacert = get_resource("cacert.pem")
if cacert is not None and os.path.isfile(cacert):

View File

@@ -470,6 +470,8 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
while len(appliance_configuration["name"]) == 0 or not config.is_name_available(appliance_configuration["name"]):
QtWidgets.QMessageBox.warning(self.parent(), "Add appliance", "The name \"{}\" is already used by another appliance".format(appliance_configuration["name"]))
appliance_configuration["name"], ok = QtWidgets.QInputDialog.getText(self.parent(), "Add appliance", "New name:", QtWidgets.QLineEdit.Normal, appliance_configuration["name"])
if not ok:
return False
appliance_configuration["name"] = appliance_configuration["name"].strip()
if "qemu" in appliance_configuration:

View File

@@ -85,7 +85,7 @@ class SnapshotsDialog(QtWidgets.QDialog, Ui_SnapshotsDialog):
snapshot_name, ok = QtWidgets.QInputDialog.getText(self, "Snapshot", "Snapshot name:", QtWidgets.QLineEdit.Normal, "Unnamed")
if ok and snapshot_name and self._project:
Controller.instance().post("/projects/{}/snapshots".format(self._project.id()), self._createSnapshotsCallback, {"name": snapshot_name})
Controller.instance().post("/projects/{}/snapshots".format(self._project.id()), self._createSnapshotsCallback, {"name": snapshot_name}, timeout=300)
def _createSnapshotsCallback(self, result, error=False, server=None, context={}, **kwargs):
if error:
@@ -131,7 +131,7 @@ class SnapshotsDialog(QtWidgets.QDialog, Ui_SnapshotsDialog):
if reply == QtWidgets.QMessageBox.Cancel:
return
Controller.instance().post("/projects/{}/snapshots/{}/restore".format(self._project.id(), snapshot_id), self._restoreSnapshotsCallback)
Controller.instance().post("/projects/{}/snapshots/{}/restore".format(self._project.id(), snapshot_id), self._restoreSnapshotsCallback, timeout=300)
def _restoreSnapshotsCallback(self, result, error=False, server=None, context={}, **kwargs):
if error:

View File

@@ -21,6 +21,7 @@ Style editor to edit Shape items.
from ..qt import QtCore, QtWidgets, QtGui
from ..ui.style_editor_dialog_ui import Ui_StyleEditorDialog
from ..items.shape_item import ShapeItem
class StyleEditorDialog(QtWidgets.QDialog, Ui_StyleEditorDialog):
@@ -115,7 +116,9 @@ class StyleEditorDialog(QtWidgets.QDialog, Ui_StyleEditorDialog):
for item in self._items:
item.setPen(pen)
if brush:
# on multiselection it's possible to select many type of items
# but brush can be applied only on ShapeItem,
if brush and isinstance(item, ShapeItem):
item.setBrush(brush)
item.setRotation(self.uiRotationSpinBox.value())

View File

@@ -135,6 +135,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
self.scene().addItem(item)
super().setEnabled(enabled)
self.toggleUiDeviceMenu()
def reset(self):
"""
Remove all the items from the scene and
@@ -476,6 +478,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
else:
super().mousePressEvent(event)
self.toggleUiDeviceMenu()
def mouseReleaseEvent(self, event):
"""
Handles all mouse release events.
@@ -498,6 +502,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
item.setSelected(True)
super().mouseReleaseEvent(event)
self.toggleUiDeviceMenu()
def wheelEvent(self, event):
"""
Handles zoom in or out using the mouse wheel.
@@ -1174,6 +1180,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
self._import_config_dir,
"All files (*.*);;Config files (*.cfg)",
"Config files (*.cfg)")
if not path:
continue
self._import_config_dir = os.path.dirname(path)
item.node().importFile(config_file, path)
@@ -1221,12 +1229,9 @@ class GraphicsView(QtWidgets.QGraphicsView):
for item in items:
for config_file in item.node().configFiles():
path, ok = QtWidgets.QFileDialog.getSaveFileName(self, "Export file", os.path.join(self._export_configs_to_dir, item.node().name() + "_" + os.path.basename(config_file)), "All files (*.*);;Config files (*.cfg)")
if not path:
continue
self._export_configs_to_dir = os.path.dirname(path)
item.node().exportFile(config_file, path)
def getCommandLineSlot(self):
@@ -1500,6 +1505,9 @@ class GraphicsView(QtWidgets.QGraphicsView):
elif item.parentItem() is None:
item.delete()
self.scene().clearSelection()
self.toggleUiDeviceMenu()
def allocateCompute(self, node_data, module_instance):
"""
Allocates a server.
@@ -1547,9 +1555,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
"""
node = Topology.instance().getNode(node_id)
name = "Node"
if node:
if node.name():
name = node.name()
if node and node.name():
name = node.name()
if self._main_window and not sip.isdeleted(self._main_window):
QtWidgets.QMessageBox.critical(self._main_window, name, message.strip())
@@ -1592,3 +1599,11 @@ class GraphicsView(QtWidgets.QGraphicsView):
painter.drawLine(rect.left(), y, rect.right(), y)
y += gridSize
painter.restore()
def toggleUiDeviceMenu(self):
""" Hook which enables/disables uiDeviceMenu based on the current items selection"""
items = self.scene().selectedItems()
if len(items) > 0:
self._main_window.uiDeviceMenu.setEnabled(True)
else:
self._main_window.uiDeviceMenu.setEnabled(False)

View File

@@ -309,10 +309,10 @@ class HTTPClient(QtCore.QObject):
return request()
else:
self._query_waiting_connections.append((request, callback))
# If we are not connected and we enqueue the first query we open the conection
# enqueue the first query and open the connection if we are not connected
if len(self._query_waiting_connections) == 1:
log.debug("Connection to {}".format(self.url()))
self._executeHTTPQuery("GET", "/version", self._callbackConnect, {}, server=server, timeout=5, showProgress=False)
self._executeHTTPQuery("GET", "/version", self._callbackConnect, {}, server=server, timeout=10, showProgress=False)
def _connectionError(self, callback, msg="", server=None):
"""
@@ -550,7 +550,7 @@ class HTTPClient(QtCore.QObject):
self._notify_progress_start_query(context["query_id"], progressText, response)
if timeout is not None:
QtCore.QTimer.singleShot(timeout * 1000, qpartial(self._timeoutSlot, response))
QtCore.QTimer.singleShot(timeout * 1000, qpartial(self._timeoutSlot, response, timeout))
return response
@@ -585,14 +585,14 @@ class HTTPClient(QtCore.QObject):
else:
callback(content, server=server, context=context)
def _timeoutSlot(self, response):
def _timeoutSlot(self, response, timeout):
"""
Beware it's call for all request you need to check the status of the response
"""
# We check if we received HTTP headers
if not sip.isdeleted(response) and response.isRunning() and not len(response.rawHeaderList()) > 0:
if not response.error() != QtNetwork.QNetworkReply.NoError:
log.warn("Timeout request {}".format(response.url().toString()))
log.warning("Timeout after {} seconds for request {}".format(timeout, response.url().toString()))
response.abort()
def disconnect(self):

View File

@@ -88,7 +88,7 @@ class DrawingItem:
def updateDrawing(self):
if self._id:
self._project.put("/drawings/" + self._id, self.updateDrawingCallback, body=self.__json__())
self._project.put("/drawings/" + self._id, self.updateDrawingCallback, body=self.__json__(), showProgress=False)
@qslot
def updateDrawingCallback(self, result, error=False, **kwargs):
@@ -165,6 +165,7 @@ class DrawingItem:
"""
QtWidgets.QGraphicsItem.setZValue(self, value)
if self.zValue() < 0:
self.setFlag(self.ItemIsSelectable, False)
self.setFlag(self.ItemIsMovable, False)

View File

@@ -48,6 +48,13 @@ class EllipseItem(QtWidgets.QGraphicsEllipseItem, ShapeItem):
super().paint(painter, option, widget)
self.drawLayerInfo(painter)
def setZValue(self, value):
"""
Sets Z value of the item
:param value: z layer
"""
return ShapeItem.setZValue(self, value)
def toSvg(self):
"""
Return an SVG version of the shape

View File

@@ -64,6 +64,13 @@ class ImageItem(QtSvg.QGraphicsSvgItem, DrawingItem):
super().paint(painter, option, widget)
self.drawLayerInfo(painter)
def setZValue(self, value):
"""
Sets Z value of the item
:param value: z layer
"""
return DrawingItem.setZValue(self, value)
def fromSvg(self, svg):
renderer = QImageSvgRenderer(svg)
self.setSharedRenderer(renderer)

View File

@@ -63,6 +63,13 @@ class LineItem(QtWidgets.QGraphicsLineItem, DrawingItem):
super().paint(painter, option, widget)
self.drawLayerInfo(painter)
def setZValue(self, value):
"""
Sets Z value of the item
:param value: z layer
"""
return DrawingItem.setZValue(self, value)
def toSvg(self):
"""
Return an SVG version of the shape

View File

@@ -483,7 +483,7 @@ class LinkItem(QtWidgets.QGraphicsPathItem):
link_center = QtCore.QPointF(self.source.x() + self.dx / 2.0 - 11, self.source.y() + self.dy / 2.0 - 11)
if self._suspend_item is None:
self._suspend_item = SvgIconItem(':/icons/pause.svg', self)
self._suspend_item.setScale(0.6)
self._suspend_item.setScale(0.6)
if not self._suspend_item.isVisible():
self._suspend_item.show()
self._suspend_item.setPos(link_center)
@@ -537,7 +537,7 @@ class LinkItem(QtWidgets.QGraphicsPathItem):
link_center = QtCore.QPointF(self.source.x() + self.dx / 2.0 - 11, self.source.y() + self.dy / 2.0 - 11)
if self._filter_item is None:
self._filter_item = SvgIconItem(':/icons/filter.svg', self)
self._filter_item.setScale(0.6)
self._filter_item.setScale(0.6)
if not self._filter_item.isVisible():
self._filter_item.show()
self._filter_item.setPos(link_center)

View File

@@ -46,6 +46,13 @@ class RectangleItem(QtWidgets.QGraphicsRectItem, ShapeItem):
super().paint(painter, option, widget)
self.drawLayerInfo(painter)
def setZValue(self, value):
"""
Sets Z value of the item
:param value: z layer
"""
return ShapeItem.setZValue(self, value)
def toSvg(self):
"""
Return an SVG version of the shape
@@ -61,4 +68,3 @@ class RectangleItem(QtWidgets.QGraphicsRectItem, ShapeItem):
rect = self._styleSvg(rect)
return ET.tostring(svg, encoding="utf-8").decode("utf-8")

View File

@@ -111,6 +111,13 @@ class TextItem(QtWidgets.QGraphicsTextItem, DrawingItem):
super().paint(painter, option, widget)
self.drawLayerInfo(painter)
def setZValue(self, value):
"""
Sets Z value of the item
:param value: z layer
"""
return DrawingItem.setZValue(self, value)
def toSvg(self):
"""
Return an SVG version of the text

View File

@@ -150,7 +150,7 @@ class LocalConfig(QtCore.QObject):
def _getSettingsCallback(self, result, error=False, **kwargs):
self._refreshingSettings = False
if error:
log.error("Can't get settings from controller")
log.debug("Can't get settings from controller")
return
if result == {} and self._settings != {}:
self._settings_retrieved_from_controller = True

View File

@@ -132,7 +132,7 @@ class LocalServer(QtCore.QObject):
if win32serviceutil.QueryServiceStatus(service_name, None)[1] != win32service.SERVICE_RUNNING:
return False
except pywintypes.error as e:
if e.winerror == 1060:
if e.winerror == 1060: # service is not installed
return False
else:
log.error("Could not check if the {} service is running: {}".format(service_name, e.strerror))

View File

@@ -651,10 +651,11 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
return
self._screenshots_dir = os.path.dirname(path)
# add the extension if missing
file_format = "." + selected_filter[:4].lower().strip()
if not path.endswith(file_format):
path += file_format
# add the extension if missing (Mac OS automatically adds an extension already)
if not sys.platform.startswith("darwin"):
file_format = "." + selected_filter[:4].lower().strip()
if not path.endswith(file_format):
path += file_format
if not self.createScreenshot(path):
QtWidgets.QMessageBox.critical(self, "Screenshot", "Could not create screenshot file {}".format(path))

View File

@@ -19,7 +19,7 @@
Configuration page for clouds.
"""
from gns3.qt import QtCore, QtWidgets
from gns3.qt import QtCore, QtGui, QtWidgets
from gns3.dialogs.symbol_selection_dialog import SymbolSelectionDialog
from gns3.controller import Controller
from gns3.node import Node
@@ -68,6 +68,12 @@ class CloudConfigurationPage(QtWidgets.QWidget, Ui_cloudConfigPageWidget):
self.uiShowSpecialInterfacesCheckBox.stateChanged.connect(self._showSpecialInterfacesSlot)
self.uiSymbolToolButton.clicked.connect(self._symbolBrowserSlot)
# add an icon to warning button
icon = QtGui.QIcon.fromTheme("dialog-warning")
if icon.isNull():
icon = QtGui.QIcon(':/icons/dialog-warning.svg')
self.uiEthernetWarningPushButton.setIcon(icon)
def _EthernetChangedSlot(self):
"""
Enables the use of the delete button.

View File

@@ -82,9 +82,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="dialog-warning"/>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/builtin/ui/cloud_configuration_page.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
# Created by: PyQt5 UI code generator 5.9
#
# WARNING! All changes made in this file will be lost!
@@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_cloudConfigPageWidget(object):
def setupUi(self, cloudConfigPageWidget):
cloudConfigPageWidget.setObjectName("cloudConfigPageWidget")
cloudConfigPageWidget.resize(758, 299)
cloudConfigPageWidget.resize(821, 363)
self.verticalLayout = QtWidgets.QVBoxLayout(cloudConfigPageWidget)
self.verticalLayout.setObjectName("verticalLayout")
self.uiTabWidget = QtWidgets.QTabWidget(cloudConfigPageWidget)
@@ -46,8 +46,6 @@ class Ui_cloudConfigPageWidget(object):
self.gridLayout_3.addWidget(self.uiEthernetListWidget, 1, 0, 1, 5)
self.uiEthernetWarningPushButton = QtWidgets.QPushButton(self.EthernetTab)
self.uiEthernetWarningPushButton.setText("")
icon = QtGui.QIcon.fromTheme("dialog-warning")
self.uiEthernetWarningPushButton.setIcon(icon)
self.uiEthernetWarningPushButton.setObjectName("uiEthernetWarningPushButton")
self.gridLayout_3.addWidget(self.uiEthernetWarningPushButton, 0, 1, 1, 1)
self.uiShowSpecialInterfacesCheckBox = QtWidgets.QCheckBox(self.EthernetTab)

View File

@@ -199,7 +199,8 @@ class QemuVMPreferencesPage(QtWidgets.QWidget, Ui_QemuVMPreferencesPageWidget):
dialog.show()
if dialog.exec_():
# update the icon
item.setIcon(0, QtGui.QIcon(qemu_vm["symbol"]))
Controller.instance().getSymbolIcon(qemu_vm["symbol"], qpartial(self._setItemIcon, item))
if qemu_vm["name"] != item.text(0):
new_key = "{server}:{name}".format(server=qemu_vm["server"], name=qemu_vm["name"])
if new_key in self._qemu_vms:

View File

@@ -54,8 +54,6 @@ class VMwarePreferencesPage(QtWidgets.QWidget, Ui_VMwarePreferencesPageWidget):
else:
# VMnet limit on Linux is 255
self.uiVMnetEndRangeSpinBox.setMaximum(255)
# Block host network traffic is only supported on Windows for now
self.uiBlockHostTrafficCheckBox.setEnabled(False)
def _vmrunPathBrowserSlot(self):
"""

View File

@@ -32,7 +32,7 @@ VMWARE_SETTINGS = {
"host_type": "ws",
"vmnet_start_range": 2,
"vmnet_end_range": DEFAULT_VMNET_END_RANGE,
"block_host_traffic": True,
"block_host_traffic": sys.platform.startswith("win"), # block host traffic on Windows only (due to winpcap packet duplication issue).
}
VMWARE_VM_SETTINGS = {

View File

@@ -230,7 +230,7 @@ class Node(BaseNode):
if self.initialized():
log.debug("{} is updating settings: {}".format(self.name(), params))
body = self._prepareBody(params)
self.controllerHttpPut("/nodes/{node_id}".format(node_id=self._node_id), self.updateNodeCallback, body=body, timeout=timeout)
self.controllerHttpPut("/nodes/{node_id}".format(node_id=self._node_id), self.updateNodeCallback, body=body, timeout=timeout, showProgress=False)
def updateNodeCallback(self, result, error=False, **kwargs):
"""
@@ -402,7 +402,7 @@ class Node(BaseNode):
if error:
log.error("error while deleting {}: {}".format(self.name(), result["message"]))
self.server_error_signal.emit(self.id(), result["message"])
return
# delete the node even if there is an error on server side
self.deleted_signal.emit()
self._module.removeNode(self)

View File

@@ -195,7 +195,11 @@ class NodesView(QtWidgets.QTreeWidget):
if not node:
return
for module in MODULES:
node_class = module.getNodeType(node["node_type"])
if node["node_type"] == "dynamips":
node_class = module.getNodeType(node["node_type"], node["platform"])
else:
node_class = module.getNodeType(node["node_type"])
if node_class:
break

View File

@@ -38,7 +38,7 @@ class Progress(QtCore.QObject):
show_signal = QtCore.Signal()
hide_signal = QtCore.Signal()
def __init__(self, parent, min_duration=1000, delay=250):
def __init__(self, parent, min_duration=1000, delay=500):
"""
:param min_duration: When the windows is display the windows is display for at least min_duration
:param delay: Delay before display the first dialog
@@ -48,11 +48,6 @@ class Progress(QtCore.QObject):
self._progress_dialog = None
self._show_lock = False
# Timer called for refreshing the progress dialog status
self._rtimer = QtCore.QTimer()
self._rtimer.timeout.connect(self.update)
self._rtimer.start(250)
# When in millisecond we started to show the progress dialog
self._display_start_time = 0

View File

@@ -531,7 +531,7 @@ class Project(QtCore.QObject):
return
# Qt websocket before Qt 5.6 doesn't support auth
if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0"):
if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0"):
path = "/projects/{project_id}/notifications".format(project_id=self._id)
self._notification_stream = Controller.instance().createHTTPQuery("GET", path, self._endListenNotificationCallback,
downloadProgressCallback=self._event_received,

View File

@@ -288,12 +288,12 @@ def qslot(func):
return func_wrapper
# Log qt error to Python log
# Log Qt messages to Python log
def myQtMsgHandler(msg_type, msg_log_context, msg_string):
if "_COMPIZ_TOOLKIT_ACTION" in msg_string:
# Qt < 5.6 issue: https://github.com/GNS3/gns3-gui/issues/2020
return
log.error(msg_string)
log.info(msg_string)
QtCore.qInstallMessageHandler(myQtMsgHandler)

View File

@@ -279,7 +279,7 @@
"title": "Architecture emulated"
},
"console_type": {
"enum": ["telnet", "vnc"],
"enum": ["telnet", "vnc", "spice"],
"title": "Type of console connection for the administration of the appliance"
},
"boot_priority": {

View File

@@ -60,6 +60,16 @@ class ConsoleThread(QtCore.QThread):
except ValueError:
self.consoleError.emit("Syntax error in command: {}".format(command))
return
if sys.platform.startswith("darwin") and hasattr(sys, "frozen"):
# Add to the path where the OS search executables, this is to force using the embedded telnet
# in the DMG on Mac OS
frozen_dir = os.path.dirname(os.path.abspath(sys.executable))
if sys.platform.startswith("darwin"):
frozen_dirs = [
frozen_dir,
os.path.normpath(os.path.join(frozen_dir, '..', 'Resources'))
]
os.environ["PATH"] = os.pathsep.join(frozen_dirs) + os.pathsep + os.environ.get("PATH", "")
subprocess.call(args, env=os.environ)
def run(self):

View File

@@ -9,7 +9,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1357</width>
<width>799</width>
<height>255</height>
</rect>
</property>

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/filter_dialog.ui'
# Form implementation generated from reading ui file '/home/dominik/projects/gns3-gui/gns3/ui/filter_dialog.ui'
#
# Created by: PyQt5 UI code generator 5.8.2
#
@@ -12,7 +12,7 @@ class Ui_FilterDialog(object):
def setupUi(self, FilterDialog):
FilterDialog.setObjectName("FilterDialog")
FilterDialog.setWindowModality(QtCore.Qt.ApplicationModal)
FilterDialog.resize(1357, 255)
FilterDialog.resize(799, 255)
self.verticalLayout_3 = QtWidgets.QVBoxLayout(FilterDialog)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.uiVerticalLayout = QtWidgets.QVBoxLayout()

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
__version__ = "2.1.0dev4"
__version__ = "2.1.0dev8"
__version_info__ = (2, 1, 0, -99)
# If it's a git checkout try to add the commit

View File

@@ -0,0 +1,36 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" y2="50" gradientUnits="userSpaceOnUse" x2="42" y1="18" x1="12">
<stop stop-color="#ddd" offset="0"/>
<stop stop-color="#fff" offset="1"/>
</linearGradient>
<linearGradient id="e" x2="0" gradientUnits="userSpaceOnUse" y2="46.2" y1="41.2">
<stop offset="0"/>
<stop stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="b" y2="51" gradientUnits="userSpaceOnUse" y1="24" x2="38" x1="6">
<stop stop-color="#e88" offset="0"/>
<stop stop-color="#d33" offset="1"/>
</linearGradient>
<radialGradient id="f" gradientUnits="userSpaceOnUse" cy="41.2" cx="39.1" gradientTransform="matrix(2 0 0 1.28 -39.1 -11.6)" r="3.9">
<stop offset="0"/>
<stop stop-opacity="0" offset="1"/>
</radialGradient>
</defs>
<g opacity=".4">
<path id="d" fill="url(#f)" d="m39.1 46.1c3.23 0 7.9-2.24 7.9-5s-3.7-5-7.9-5z"/>
<use x="-48" xlink:href="#d" transform="scale(-1,1)"/>
<path fill="url(#e)" d="m8.9 36.1h30.2v10h-30.2z"/>
</g>
<g stroke-linejoin="round">
<g stroke="#900" stroke-width="6">
<path id="c" d="m7 40h34l-17-32z"/>
</g>
<use stroke-width="4" stroke="url(#b)" xlink:href="#c"/>
<use stroke-width="2" stroke="#c00" xlink:href="#c" fill="#c00"/>
<path stroke="url(#a)" d="m9.8 38.3h28.4l-14.2-26.7z" fill="url(#a)"/>
</g>
<path d="m22.75 30.4l-0.5-10h3.5l-0.6 10z"/>
<circle cy="33.58" cx="24" r="2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -107,6 +107,7 @@
<file>icons/camera-photo.svg</file>
<file>icons/camera-photo-hover.svg</file>
<file>icons/front.svg</file>
<file>icons/dialog-warning.svg</file>
<file>images/gns3_logo.png</file>
<file>images/gns3_icon_128x128.png</file>
<file>images/gns3_icon_256x256.png</file>

View File

@@ -189,7 +189,7 @@ def test_create_new_version():
# tests what happens without versions in file
wrong_appliance_fp, wrong_appliance_file = tempfile.mkstemp()
with open(appliance_path) as f:
with open(appliance_path, encoding='utf-8') as f:
appliance = json.loads(f.read())
del appliance['versions']
os.write(wrong_appliance_fp, json.dumps(appliance).encode())

View File

@@ -60,6 +60,7 @@ def link(devices, controller, project):
link = Link(devices[0], devices[0].ports()[0], devices[1], devices[1].ports()[0])
data = {
"suspend": False,
"nodes": [
{"node_id": devices[0].node_id(), "adapter_number": 0, "port_number": 0},
{"node_id": devices[1].node_id(), "adapter_number": 0, "port_number": 0}
@@ -83,6 +84,7 @@ def test_create_link(devices, project, controller):
link = Link(devices[0], devices[0].ports()[0], devices[1], devices[1].ports()[0])
data = {
"suspend": False,
"nodes": [
{"node_id": devices[0].node_id(), "adapter_number": 0, "port_number": 0},
{"node_id": devices[1].node_id(), "adapter_number": 0, "port_number": 0},