Compare commits

...

63 Commits

Author SHA1 Message Date
Jeremy Grossmann
5f34eb18d1 Merge pull request #3829 from GNS3/release/v2.2.59
Release v2.2.59
2026-05-08 20:06:01 +08:00
grossmj
3a822c02b2 Merge branch 'master' into 2.2 2026-05-08 19:49:31 +08:00
grossmj
84967d4c87 Release v2.2.59 2026-05-08 19:06:04 +08:00
grossmj
6981870554 Update requirements.txt & python_requires 2026-05-08 17:01:14 +08:00
grossmj
7c06038072 Remove psutil version check 2026-05-08 16:41:46 +08:00
grossmj
86be15d474 Fix remaining PyQt6 compatibility issues. Fixes #3822 2026-05-08 13:00:37 +08:00
grossmj
2c64f83d05 Add --title to remote-viewer console commands. Fixes #3783 2026-05-08 12:54:06 +08:00
grossmj
6cc024ed91 Fix deleting drawings. Ref #3810 2026-04-13 12:03:49 +08:00
Jeremy Grossmann
607343292b Merge pull request #3821 from GNS3/release/v2.2.58.1
Release v2.2.58.1
2026-04-12 21:18:16 +08:00
grossmj
03f74d77e4 Development on 2.2.59.dev2 2026-04-12 21:17:21 +08:00
grossmj
1cf86ed4b5 Release v2.2.58.1 2026-04-12 20:59:21 +08:00
Jeremy Grossmann
c4570098a4 Merge pull request #3820 from GNS3/fix/v2.2.58
Fix callback issues in v2.2.58
2026-04-12 20:55:27 +08:00
grossmj
45fe2189f9 Fix tests 2026-04-12 20:53:28 +08:00
grossmj
852ebddf3b Fix callback issues in v2.2.58 2026-04-12 20:49:20 +08:00
grossmj
7baf6af346 Merge branch 'master' into 2.2 2026-04-12 20:35:12 +08:00
grossmj
030c6a4d6d Development on 2.2.59.dev1 2026-04-11 18:05:54 +08:00
Jeremy Grossmann
39759e2da0 Merge pull request #3816 from GNS3/release/v2.2.58
Release v2.2.58
2026-04-11 18:02:02 +08:00
grossmj
9447f21de8 Fix tests 2026-04-11 17:59:48 +08:00
Jeremy Grossmann
0fe25f61b3 Merge branch 'master' into release/v2.2.58 2026-04-11 17:43:12 +08:00
grossmj
96aa00f33b Fix running tests 2026-04-11 17:37:04 +08:00
grossmj
05813736ee Release v2.2.58 2026-04-10 20:36:01 +08:00
grossmj
adf0ed84f4 Update snapshot date & time format 2026-04-09 20:22:39 +08:00
grossmj
5f5c128183 Finish to fix HTTP DELETE requests are silently dropped. Fixes #3810 2026-04-07 23:23:26 +08:00
grossmj
a016726bb1 Fix bug when HTTP DELETE requests are silently dropped. Fixes #3810 2026-04-07 21:44:06 +08:00
grossmj
ec5e0071f8 Fix debug message in http_client.py 2026-04-07 19:12:53 +08:00
Jeremy Grossmann
af2bf23abb Merge pull request #3814 from GNS3/enhancement/3754
Show if a linked base VM or not in device's properties
2026-04-06 23:11:32 +08:00
grossmj
da7a95da97 Show if a linked base VM or not in device's properties 2026-04-06 23:08:09 +08:00
grossmj
da9c36f476 Add HTTP method to response timeout slot. Ref #3810 2026-04-06 22:34:05 +08:00
Jeremy Grossmann
cd7e9221fd Merge pull request #3813 from GNS3/enhancement/3396
Add default link style section in preferences
2026-04-06 19:16:15 +08:00
grossmj
e6c5e2101b Add default link style section in preferences
Update the default style colors based on the interface style
2026-04-06 19:11:45 +08:00
grossmj
5e225020e0 Support bring to front for vnc and spice consoles on Linux. Fixes #3783 2026-04-05 22:40:18 +08:00
grossmj
adb8d37a91 Update appliance schemas 2026-04-04 19:38:57 +08:00
grossmj
f356c743c7 Fix bug when adding multiple nodes at the same time. Fixes #3807 2026-03-25 08:52:32 +08:00
Jeremy Grossmann
e5e9eb02bc Merge pull request #3805 from GNS3/release/v2.2.57
Release v2.2.57
2026-03-24 10:10:28 +08:00
grossmj
898a03f676 Fix QWebSocket error signal. Fixes #3804 2026-03-24 10:07:44 +08:00
grossmj
6a033658ef Fix QWebSocket error signal. Fixes #3804 2026-03-24 09:16:07 +08:00
grossmj
d56b9660c9 Development on 2.2.58.dev1 2026-03-24 08:35:52 +08:00
grossmj
3709b7646d Release v2.2.57 2026-03-23 09:45:47 +08:00
Jeremy Grossmann
b1af4c7f8b Merge pull request #3798 from GNS3/bugfix/3794
Fix error reporting after PyQt6 migration
2026-03-21 10:38:05 +08:00
grossmj
a6e26d8ea9 Fix errors from controller are not reported 2026-03-21 10:31:22 +08:00
grossmj
c98e0753f2 Deactivate 'use default IOU values' by default and update RAM/NVRAM values 2026-03-10 23:33:15 +08:00
grossmj
f34af19101 Fix QMenu parents 2026-02-20 19:07:15 +08:00
grossmj
fb984e5e89 Fix UltraVNC preconfigured command 2026-02-20 16:40:22 +08:00
grossmj
c4deabc3fa Merge branch 'master' into 2.2
# Conflicts:
#	gns3/graphics_view.py
2026-02-20 16:24:41 +08:00
Jeremy Grossmann
05927453b3 Merge pull request #3753 from hjicks/master
settings.py: add OpenBSD packet capture command
2026-02-19 21:20:21 +08:00
grossmj
bc4e5f7300 Add missing QDarkStyle in requirements.txt 2026-02-18 22:56:52 +08:00
grossmj
9568f08058 Backport: dark style 2026-02-18 22:56:45 +08:00
grossmj
b3a4822335 Fix bug when dragging scene 2026-02-18 22:54:27 +08:00
grossmj
699ad62b54 Upgrade pytest and jsonschema dependencies 2026-02-11 17:42:11 +08:00
Jeremy Grossmann
8a24ff6aa8 Merge pull request #3780 from GNS3/release/v2.2.56.1
Release v2.2.56.1
2026-02-11 16:53:04 +08:00
grossmj
2c0f925d5f Merge remote-tracking branch 'origin/2.2' into 2.2 2026-02-11 16:47:31 +08:00
grossmj
457bc6823d Upgrade sentry-sdk and psutil dependencies 2026-02-11 16:46:25 +08:00
Jeremy Grossmann
9e89bb6971 Merge pull request #3785 from anthonyroussel/fix-qt6-tests
Fix tests after PyQt6 migration
2026-02-05 16:58:13 +08:00
Anthony ROUSSEL
e6e2a1cafb Fix tests after PyQt6 migration 2026-02-04 21:06:12 +01:00
Jeremy Grossmann
d60908897f Merge pull request #3784 from mrognor/master
Fix mouse manipulation bug
2026-02-03 21:08:49 +08:00
mrognor
5b7a25c8a7 Fix mouse manipulation bug 2026-02-03 16:03:12 +03:00
Jeremy Grossmann
93a9e44afe Merge pull request #3778 from SalehAlolayan/master
Fixing tab name in Superputty + Adding Superputty VNC support
2026-02-01 11:53:42 +08:00
Saleh Alolayan
2168af80c7 Add Multi VNC tab handling in Superputty 2026-01-30 23:39:58 +03:00
grossmj
00929a2c71 Development on 2.2.57.dev2 2026-01-28 18:44:51 +08:00
Saleh Alolayan
b7cd582384 Handling space in name for Superputty 2026-01-25 20:25:08 +03:00
Saleh Alolayan
265d1c5934 Merge branch 'GNS3:master' into master 2026-01-24 21:25:40 +03:00
Saleh Alolayan
9d14aab04a Fixing tab name in Superputty + Adding Superputty VNC support
Fixing tab name in Superputty + Adding Superputty VNC support
2026-01-24 21:24:34 +03:00
saeed
740fdc40c5 settings.py: add OpenBSD packet capture command 2025-09-05 16:43:33 +03:30
52 changed files with 115083 additions and 115057 deletions

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build and run Docker image
run: |
docker build -t gns3-gui-test .

View File

@@ -1,5 +1,44 @@
# Change Log
## 2.2.59 08/05/2026
* Remove psutil version check
* Fix remaining PyQt6 compatibility issues. Fixes #3822
* Add --title to remote-viewer console commands. Fixes #3783
* Fix deleting drawings. Ref #3810
## 2.2.58.1 12/04/2026
* Fix callback issues in found in v2.2.58
## 2.2.58 10/04/2026
* Update snapshot date & time format
* Fix bug when HTTP DELETE requests are silently dropped. Fixes #3810
* Show if a linked base VM or not in device's properties
* Add HTTP method to response timeout slot. Ref #3810
* Add default link style section in preferences
* Update the default style colors based on the interface style
* Support bring to front for vnc and spice consoles on Linux. Fixes #3783
* Update appliance schemas
* Fix bug when adding multiple nodes at the same time. Fixes #3807
* Fix QWebSocket error signal. Fixes #3804
## 2.2.57 23/03/2026
* Fix errors from controller are not reported
* Deactivate 'use default IOU values' by default and update RAM/NVRAM values
* Fix QMenu parents
* Fix UltraVNC preconfigured command
* Dark style
* Fix bug when dragging scene
* Upgrade pytest and jsonschema dependencies
* Upgrade sentry-sdk and psutil dependencies
* Fix mouse manipulation bug
* Add Multi VNC tab handling in Superputty
* Fixing tab name in Superputty + Adding Superputty VNC support
* settings.py: add OpenBSD packet capture command
## 2.2.56.1 28/01/2026
* Fix line style support for links

View File

@@ -3,7 +3,7 @@ FROM ubuntu:latest
MAINTAINER GNS3 Team
RUN apt-get update
RUN apt-get install -y --force-yes python3 python3-pyqt5 python3-pip python3-pyqt5.qtsvg python3-pyqt5.qtwebsockets python3-dev xvfb
RUN apt-get install -y --force-yes python3 python3-pyqt6 python3-pip python3-pyqt6.qtsvg python3-pyqt6.qtwebsockets python3-dev xvfb
RUN apt-get clean
ADD dev-requirements.txt /dev-requirements.txt

View File

@@ -1,4 +1,5 @@
-rrequirements.txt
pytest==8.4.2 # version 8.4.2 is the last one supporting Python 3.9
pytest==8.4.2; python_version == '3.9' # version 8.4.2 is the last one supporting Python 3.9
pytest==9.0.2; python_version >= '3.10'
pytest-timeout==2.4.0

View File

@@ -251,7 +251,7 @@ class BaseNode(QtCore.QObject):
return self._ports
def controllerHttpPost(self, path, callback, body={}, context={}, **kwargs):
def controllerHttpPost(self, path, callback, body=None, context=None, **kwargs):
"""
POST on current server / project
@@ -263,7 +263,7 @@ class BaseNode(QtCore.QObject):
self._project.post(path, callback, body=body, context=context, **kwargs)
def controllerHttpPut(self, path, callback, body={}, context={}, **kwargs):
def controllerHttpPut(self, path, callback, body=None, context=None, **kwargs):
"""
PUT on current server / project
@@ -275,7 +275,7 @@ class BaseNode(QtCore.QObject):
self._project.put(path, callback, body=body, context=context, **kwargs)
def controllerHttpGet(self, path, callback, context={}, **kwargs):
def controllerHttpGet(self, path, callback, context=None, **kwargs):
"""
Get on current server / project
@@ -287,7 +287,7 @@ class BaseNode(QtCore.QObject):
self._project.get(path, callback, context=context, **kwargs)
def controllerHttpDelete(self, path, callback, context={}, **kwargs):
def controllerHttpDelete(self, path, callback, context=None, **kwargs):
"""
Delete on current server / project

View File

@@ -247,7 +247,7 @@ class Controller(QtCore.QObject):
def createHTTPQuery(self, method, path, *args, **kwargs):
"""
Forward the query to the HTTP client or controller depending of the path
Forward the query to the HTTP client or controller depending on the path
"""
if self._http_client:
@@ -432,7 +432,10 @@ class Controller(QtCore.QObject):
else:
self._notification_stream = self._http_client.connectWebSocket(self._websocket, "/notifications/ws")
self._notification_stream.textMessageReceived.connect(self._websocket_event_received)
self._notification_stream.error.connect(self._websocket_error)
if parse_version(QtCore.QT_VERSION_STR) < parse_version("6.5.0"):
self._notification_stream.error.connect(self._websocket_error)
else:
self._notification_stream.errorOccurred.connect(self._websocket_error)
self._notification_stream.sslErrors.connect(self._sslErrorsSlot)
log.info("Listening for controller notifications on '{}'".format(self._notification_stream.requestUrl().toString()))

View File

@@ -50,7 +50,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "https://2173ac82d30385607ee40ae11973e298@o19455.ingest.us.sentry.io/38506"
DSN = "https://dd662ce99d7e4a04714a89939ec523c9@o19455.ingest.us.sentry.io/38506"
_instance = None
def __init__(self):

View File

@@ -67,7 +67,7 @@ class SnapshotsDialog(QtWidgets.QDialog, Ui_SnapshotsDialog):
for snapshot in result:
item = QtWidgets.QListWidgetItem(self.uiSnapshotsList)
item.setText("{} on {}".format(snapshot["name"], datetime.fromtimestamp(snapshot["created_at"]).strftime("%d/%m/%y at %H:%M:%S")))
item.setText("{} on {}".format(snapshot["name"], datetime.fromtimestamp(snapshot["created_at"]).strftime("%Y-%m-%d at %H:%M:%S")))
item.setData(QtCore.Qt.ItemDataRole.UserRole, snapshot["snapshot_id"])
if self.uiSnapshotsList.count():

View File

@@ -41,6 +41,10 @@ class StyleEditorDialogLink(QtWidgets.QDialog, Ui_StyleEditorDialog):
self._link = link
self._link_style = {}
self.uiBorderColorLabel.setText("Link color")
self.uiBorderWidthLabel.setText("Link width")
self.uiBorderStyleLabel.setText("Link style")
self.uiBorderColorPushButton.clicked.connect(self._setBorderColorSlot)
self.uiButtonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Apply).clicked.connect(self._applyPreferencesSlot)
@@ -102,10 +106,11 @@ class StyleEditorDialogLink(QtWidgets.QDialog, Ui_StyleEditorDialog):
self._link.setPen(pen)
new_link_style = {}
new_link_style["color"] = self._border_color.name()
new_link_style["width"] = self.uiBorderWidthSpinBox.value()
new_link_style["type"] = border_style.value
new_link_style = {
"color": self._border_color.name(),
"width": self.uiBorderWidthSpinBox.value(),
"type": border_style.value,
}
# Store values
self._link.setLinkStyle(new_link_style)

View File

@@ -636,8 +636,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
hBar = self.horizontalScrollBar()
vBar = self.verticalScrollBar()
delta = mapped_global_pos - self._last_mouse_position
hBar.setValue(hBar.value() + (delta.x() if QtWidgets.QApplication.isRightToLeft() else -delta.x()))
vBar.setValue(vBar.value() - delta.y())
hBar.setValue(int(hBar.value() + (delta.x() if QtWidgets.QApplication.isRightToLeft() else -delta.x())))
vBar.setValue(int(vBar.value() - delta.y()))
self._last_mouse_position = mapped_global_pos
if self._adding_link and self._newlink and self._newlink in self.scene().items():
# update the mouse position when the user is adding a link.
@@ -735,7 +735,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
for node_number in range(integer):
x = event.position().x() - (150 // 2) + (node_number % max_nodes_per_line) * offset
y = event.position().y() - (70 // 2) + (node_number // max_nodes_per_line) * offset
if self.createNodeFromTemplateId(template_id, QtCore.QPoint(x, y)) is False:
if self.createNodeFromTemplateId(template_id, QtCore.QPointF(x, y)) is False:
event.ignore()
break
else:
@@ -762,7 +762,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
:param pos: position where to display the menu
"""
menu = QtWidgets.QMenu()
menu = QtWidgets.QMenu(parent=self)
self.populateDeviceContextualMenu(menu)
menu.exec(pos)
menu.clear()

View File

@@ -275,18 +275,25 @@ class HTTPClient(QtCore.QObject):
:param query: The Server to connect
"""
def createHTTPQuery(self, method, path, callback, body={}, context={},
downloadProgressCallback=None,
showProgress=True,
ignoreErrors=False,
progressText=None,
timeout=120,
server=None,
prefix="/v2",
params={},
networkManager=None,
eventsHandler=None,
**kwargs):
def createHTTPQuery(
self,
method,
path,
callback,
body=None,
context=None,
downloadProgressCallback=None,
showProgress=True,
ignoreErrors=False,
progressText=None,
timeout=120,
server=None,
prefix="/v2",
params=None,
networkManager=None,
eventsHandler=None,
**kwargs
):
"""
Call the remote server, if not connected, check connection before
@@ -328,17 +335,24 @@ class HTTPClient(QtCore.QObject):
# return
# self._last_query_timestamp = now
request = qpartial(self._executeHTTPQuery, method, path, qpartial(callback), body, context,
downloadProgressCallback=downloadProgressCallback,
showProgress=showProgress,
ignoreErrors=ignoreErrors,
progressText=progressText,
networkManager=networkManager,
server=server,
timeout=timeout,
prefix=prefix,
eventsHandler=eventsHandler,
params=params)
request = qpartial(
self._executeHTTPQuery,
method,
path,
qpartial(callback),
body,
context,
downloadProgressCallback=downloadProgressCallback,
showProgress=showProgress,
ignoreErrors=ignoreErrors,
progressText=progressText,
networkManager=networkManager,
server=server,
timeout=timeout,
prefix=prefix,
eventsHandler=eventsHandler,
params=params
)
if self._connected:
return request()
@@ -347,7 +361,14 @@ class HTTPClient(QtCore.QObject):
# 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=10, showProgress=False)
self._executeHTTPQuery(
"GET",
"/version",
self._callbackConnect,
server=server,
timeout=10,
showProgress=False
)
def _connectionError(self, callback, msg="", server=None):
"""
@@ -370,7 +391,16 @@ class HTTPClient(QtCore.QObject):
def _retryConnection(self, server=None):
log.debug("Retry connection to {}".format(self.url()))
self._retry += 1
QtCore.QTimer.singleShot(1000, qpartial(self._executeHTTPQuery, "GET", "/version", self._callbackConnect, {}, server=server, timeout=5))
QtCore.QTimer.singleShot(
1000,
qpartial(
self._executeHTTPQuery,
"GET",
"/version",
self._callbackConnect,
server=server,
timeout=5)
)
def _callbackConnect(self, params, error=False, server=None, **kwargs):
"""
@@ -512,7 +542,7 @@ class HTTPClient(QtCore.QObject):
:param params: Dictionary of query string parameters
:returns: String of the query string
"""
if params == {}:
if params is None:
query_string = ""
else:
query_string = "?"
@@ -523,7 +553,25 @@ class HTTPClient(QtCore.QObject):
query_string += urllib.parse.urlencode(params)
return query_string
def _executeHTTPQuery(self, method, path, callback, body, context={}, downloadProgressCallback=None, showProgress=True, ignoreErrors=False, progressText=None, server=None, timeout=120, prefix="/v2", params={}, networkManager=None, eventsHandler=None, **kwargs):
def _executeHTTPQuery(
self,
method,
path,
callback,
body=None,
context=None,
downloadProgressCallback=None,
showProgress=True,
ignoreErrors=False,
progressText=None,
server=None,
timeout=120,
prefix="/v2",
params=None,
networkManager=None,
eventsHandler=None,
**kwargs
):
"""
Call the remote server
@@ -532,7 +580,7 @@ class HTTPClient(QtCore.QObject):
:param body: params to send (dictionary)
:param callback: callback method to call when the server replies
:param context: Pass a context to the response callback
:param downloadProgressCallback: Callback called when received something, it can be an incomplete response
:param downloadProgressCallback: callback called when received something, it can be an incomplete response
:param showProgress: Display progress to the user
:param networkManager: The network manager to use. If None use default
:param progressText: Text display to user in progress dialog. None for auto generated
@@ -558,7 +606,7 @@ class HTTPClient(QtCore.QObject):
request = self._addAuth(request)
request.setRawHeader(b"User-Agent", "GNS3 QT Client v{version}".format(version=__version__).encode())
# By default QT doesn't support GET with body even if it's in the RFC that's why we need to use sendCustomRequest
# By default, QT doesn't support GET with body even if it's in the RFC that's why we need to use sendCustomRequest
body = self._addBodyToRequest(body, request)
if not networkManager:
@@ -568,14 +616,16 @@ class HTTPClient(QtCore.QObject):
response = networkManager.sendCustomRequest(request, method.encode(), body)
except SystemError as e:
log.error("Can't send query: {}".format(str(e)))
return
return None
context = copy.copy(context)
if context:
context = copy.copy(context)
else:
context = dict()
context["query_id"] = str(uuid.uuid4())
response.finished.connect(qpartial(self._processResponse, response, server, callback, context, body, ignoreErrors))
#FIXME:
#response.error.connect(qpartial(self._processError, response, server, callback, context, body, ignoreErrors))
response.errorOccurred.connect(qpartial(self._processError, response, server, callback, context, body, ignoreErrors))
if downloadProgressCallback is not None:
response.readyRead.connect(qpartial(self._readyReadySlot, response, downloadProgressCallback, context, server))
@@ -637,7 +687,8 @@ class HTTPClient(QtCore.QObject):
# 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.NetworkError.NoError:
log.warning("Timeout after {} seconds for request {}. Please check the connection is not blocked by a firewall or an anti-virus.".format(timeout, response.url().toString()))
method = response.request().attribute(QtNetwork.QNetworkRequest.Attribute.CustomVerbAttribute).data().decode()
log.warning("Timeout after {} seconds for request {} '{}'. Please check the connection is not blocked by a firewall or an anti-virus.".format(timeout, method, response.url().toString()))
response.abort()
def disconnect(self):
@@ -660,12 +711,13 @@ class HTTPClient(QtCore.QObject):
error_message = "{} ({}:{})".format(response.errorString(), self._host, self._port)
if not ignore_errors:
log.debug("Response error: %s for %s (error: %d)", error_message, response.url().toString(), error_code)
method = response.request().attribute(QtNetwork.QNetworkRequest.Attribute.CustomVerbAttribute).data().decode()
log.debug("Response error: %s for %s '%s' (error: %d)", error_message, method, response.url().toString(), error_code.value)
if "query_id" in context:
self._notify_progress_end_query(context["query_id"])
if error_code < 200 or error_code == 403:
if error_code.value < 200 or error_code.value == 403:
if error_code == QtNetwork.QNetworkReply.NetworkError.OperationCanceledError: # It's legit to cancel do not disconnect
error_message = "Operation timeout" # It's clearer than cancel because cancel is triggered by us when we timeout
elif error_code == QtNetwork.QNetworkReply.NetworkError.NetworkSessionFailedError:

View File

@@ -106,6 +106,11 @@ class DrawingItem:
"""
if error:
if "doesn't exist" in result.get("message", ""):
log.warning("Drawing not found on server, recreating: {}".format(self._id))
self._id = None
self.create()
return True
log.error("Error while updating drawing: {}".format(result["message"]))
return False
self.setPos(QtCore.QPointF(result["x"], result["y"]))
@@ -214,7 +219,7 @@ class DrawingItem:
"""
Deletes this drawing.
:param skip_controller: Do not replicate change on the controller (usefull when it's already deleted on controller)
:param skip_controller: Do not replicate change on the controller (useful when it's already deleted on controller)
"""
self.setDeleting()
@@ -222,7 +227,7 @@ class DrawingItem:
from ..topology import Topology
Topology.instance().removeDrawing(self)
if self._id and not skip_controller:
self._project.delete("/drawings/" + self._id, None, body=self.__json__())
self._project.delete("/drawings/" + self._id, None)
def itemChange(self, change, value):

View File

@@ -322,7 +322,7 @@ class LinkItem(QtWidgets.QGraphicsPathItem):
# create the contextual menu
self.setHovered(True)
self.setAcceptHoverEvents(False)
menu = QtWidgets.QMenu()
menu = QtWidgets.QMenu(parent=self.scene().parent())
self.populateLinkContextualMenu(menu)
menu.exec(QtGui.QCursor.pos())
self.setAcceptHoverEvents(True)

View File

@@ -402,7 +402,7 @@ class NodeItem(QtSvgWidgets.QGraphicsSvgItem):
"""
self._selected_port = None
menu = QtWidgets.QMenu()
menu = QtWidgets.QMenu(parent=self.scene().parent())
ports = self._node.ports()
if not ports:
QtWidgets.QMessageBox.critical(self.scene().parent(), "Link", "No port available, please configure this device")

View File

@@ -132,7 +132,7 @@ class TextItem(QtWidgets.QGraphicsTextItem, DrawingItem):
text.set("text-decoration", "underline")
text.set("fill", "#" + hex(self.defaultTextColor().rgba())[4:])
text.set("fill-opacity", str(self.defaultTextColor().alphaF()))
text.text = self.toPlainText()
text.text = self.toPlainText() or " "
svg = ET.tostring(svg, encoding="utf-8").decode("utf-8")
return svg

View File

@@ -84,14 +84,21 @@ class Link(QtCore.QObject):
self._initialized = False
self._filters = {}
self._suspend = False
self._nodes = []
# Boolean if True we are creating the first instance of this node
# if false the node already exist in the topology
# use to avoid erasing information when reloading
self._creator = False
self._nodes = []
self._link_style = {}
# Add the default link style from the topology view settings
from .main_window import MainWindow
topology_view_settings = MainWindow.instance().uiGraphicsView.settings()
self._link_style = {
"color": topology_view_settings.get("default_link_color", "#000000"),
"width": topology_view_settings.get("default_link_width", 2),
"type": topology_view_settings.get("default_link_type", 1)
}
body = self._prepareParams()
if self._link_id:

View File

@@ -36,7 +36,6 @@ import time
import locale
import argparse
import signal
import psutil
try:
from gns3.qt import QtCore, QtWidgets
@@ -191,9 +190,6 @@ def main():
if parse_version(QtCore.QT_VERSION_STR) < parse_version("6.3.1"):
raise SystemExit("Requirement is PyQt6 version 6.3.1 or higher, got version {}".format(QtCore.QT_VERSION_STR))
if parse_version(psutil.__version__) < parse_version("2.2.1"):
raise SystemExit("Requirement is psutil version 2.2.1 or higher, got version {}".format(psutil.__version__))
# check for the correct locale
# (UNIX/Linux only)
locale_check()

View File

@@ -321,6 +321,11 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
style = new_settings.get("style")
if style and new_settings["style"] != self._settings["style"]:
self._setStyle(style)
QtWidgets.QMessageBox.information(
self,
"Interface style",
"Please restart the application to fully apply the {} style.".format(style)
)
self._settings.update(new_settings)
# save the settings
@@ -1491,6 +1496,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
style.setCharcoalStyle()
elif style_name == "Classic":
style.setClassicStyle()
elif style_name == "Dark":
style.setDarkStyle()
else:
style.setLegacyStyle()

View File

@@ -38,9 +38,9 @@ IOU_DEVICE_SETTINGS = {
"private_config": "",
"console_type": "telnet",
"console_auto_start": False,
"use_default_iou_values": True,
"ram": 256,
"nvram": 128,
"use_default_iou_values": False,
"ram": 1024,
"nvram": 256,
"ethernet_adapters": 2,
"serial_adapters": 2,
"compute_id": "local",

View File

@@ -463,8 +463,8 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
if "linked_clone" in settings:
self.uiBaseVMCheckBox.setChecked(settings["linked_clone"])
else:
self.uiBaseVMCheckBox.hide()
if node:
self.uiBaseVMCheckBox.setEnabled(False)
self.uiHdaDiskImageLineEdit.setText(settings["hda_disk_image"])
self.uiHdbDiskImageLineEdit.setText(settings["hdb_disk_image"])
@@ -601,7 +601,7 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
else:
settings["name"] = name
if "linked_clone" in settings:
if "linked_clone" in settings and not node:
settings["linked_clone"] = self.uiBaseVMCheckBox.isChecked()
settings["hda_disk_image"] = self.uiHdaDiskImageLineEdit.text().strip()

View File

@@ -88,7 +88,8 @@ class QemuVM(Node):
"kernel_command_line": "",
"port_name_format": "Ethernet{0}",
"port_segment_size": 0,
"first_port_name": ""}
"first_port_name": "",
"linked_clone": False}
self.settings().update(qemu_vm_settings)

View File

@@ -133,8 +133,8 @@ class VirtualBoxVMConfigurationPage(QtWidgets.QWidget, Ui_virtualBoxVMConfigPage
if "linked_clone" in settings:
self.uiBaseVMCheckBox.setChecked(settings["linked_clone"])
else:
self.uiBaseVMCheckBox.hide()
if node:
self.uiBaseVMCheckBox.setEnabled(False)
else:
self.uiNameLabel.hide()
@@ -220,7 +220,7 @@ class VirtualBoxVMConfigurationPage(QtWidgets.QWidget, Ui_virtualBoxVMConfigPage
else:
settings["name"] = name
if "linked_clone" in settings:
if "linked_clone" in settings and not node:
settings["linked_clone"] = self.uiBaseVMCheckBox.isChecked()
if not node:

View File

@@ -57,7 +57,8 @@ class VirtualBoxVM(Node):
"custom_adapters": VBOX_VM_SETTINGS["custom_adapters"],
"port_name_format": "Ethernet0",
"port_segment_size": 0,
"first_port_name": None}
"first_port_name": None,
"linked_close": False}
self.settings().update(virtualbox_vm_settings)

View File

@@ -133,8 +133,8 @@ class VMwareVMConfigurationPage(QtWidgets.QWidget, Ui_VMwareVMConfigPageWidget):
if "linked_clone" in settings:
self.uiBaseVMCheckBox.setChecked(settings["linked_clone"])
else:
self.uiBaseVMCheckBox.hide()
if node:
self.uiBaseVMCheckBox.setEnabled(False)
else:
self.uiNameLabel.hide()
@@ -219,7 +219,7 @@ class VMwareVMConfigurationPage(QtWidgets.QWidget, Ui_VMwareVMConfigPageWidget):
else:
settings["name"] = name
if "linked_clone" in settings:
if "linked_clone" in settings and not node:
settings["linked_clone"] = self.uiBaseVMCheckBox.isChecked()
if not node:

View File

@@ -59,7 +59,8 @@ class VMwareVM(Node):
"custom_adapters": VMWARE_VM_SETTINGS["custom_adapters"],
"port_name_format": "Ethernet{0}",
"port_segment_size": 0,
"first_port_name": None}
"first_port_name": None,
"linked_clone": False}
self.settings().update(vmware_vm_settings)

View File

@@ -750,8 +750,12 @@ class Node(BaseNode):
wmctrl_path = shutil.which("wmctrl")
if wmctrl_path:
try:
# use wmctrl to raise the window based on the node name (this doesn't work well with window having multiple tabs)
subprocess.run([wmctrl_path, "-Fa", self.name()], check=True, env=os.environ)
console_type = self.consoleType()
if console_type == "telnet":
# use wmctrl to raise the window based on the node name (this doesn't work well with window having multiple tabs)
subprocess.run([wmctrl_path, "-Fa", self.name()], check=True, env=os.environ)
else:
subprocess.run([wmctrl_path, "-a", '(' + self.name() + ')'], check=True, env=os.environ)
return True
except subprocess.CalledProcessError:
log.debug("Could not find window title '{}' to bring it to front".format(self.name()))

View File

@@ -194,7 +194,7 @@ class NodesView(QtWidgets.QTreeWidget):
def _showContextualMenu(self, pos):
menu = QtWidgets.QMenu()
menu = QtWidgets.QMenu(parent=self)
refresh_action = QtGui.QAction("Refresh templates", menu)
refresh_action.setIcon(get_icon("reload.svg"))
refresh_action.triggered.connect(self.refresh)

View File

@@ -63,6 +63,12 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
self.uiDefaultLabelColorPushButton.clicked.connect(self._setDefaultLabelColorSlot)
self.uiDefaultNoteFontPushButton.clicked.connect(self._setDefaultNoteFontSlot)
self.uiDefaultNoteColorPushButton.clicked.connect(self._setDefaultNoteColorSlot)
self.uiDefaultLinkColorPushButton.clicked.connect(self._setDefaultLinkColorSlot)
self.uiDefaultLinkStyleComboBox.addItem("Solid", QtCore.Qt.PenStyle.SolidLine)
self.uiDefaultLinkStyleComboBox.addItem("Dash", QtCore.Qt.PenStyle.DashLine)
self.uiDefaultLinkStyleComboBox.addItem("Dot", QtCore.Qt.PenStyle.DotLine)
self.uiDefaultLinkStyleComboBox.addItem("Dash Dot", QtCore.Qt.PenStyle.DashDotLine)
self.uiDefaultLinkStyleComboBox.addItem("Dash Dot Dot", QtCore.Qt.PenStyle.DashDotDotLine)
self.uiBrowseConfigurationPushButton.clicked.connect(self._browseConfigurationDirectorySlot)
self._default_label_color = QtGui.QColor(QtCore.Qt.GlobalColor.black)
self.uiStyleComboBox.addItems(STYLES)
@@ -288,6 +294,16 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
self._default_note_color = color
self.uiDefaultNoteStylePlainTextEdit.setStyleSheet("color : {}".format(color.name()))
def _setDefaultLinkColorSlot(self):
"""
Slot to select the default link color.
"""
color = QtWidgets.QColorDialog.getColor(self._default_link_color, self)
if color.isValid():
self._default_link_color = color
self.uiDefaultLinkColorPushButton.setStyleSheet("background-color: {};".format(color.name()))
def _populateGeneralSettingWidgets(self, settings):
"""
Populates the widgets with the settings.
@@ -367,6 +383,15 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
self._default_note_color = qt_color
self.uiDefaultNoteStylePlainTextEdit.setStyleSheet("color : {}".format(qt_color.name()))
qt_color = QtGui.QColor(settings["default_link_color"])
if qt_color.isValid():
self._default_link_color = qt_color
self.uiDefaultLinkColorPushButton.setStyleSheet("background-color: {};".format(qt_color.name()))
self.uiDefaultLinkWidthSpinBox.setValue(settings["default_link_width"])
index = self.uiDefaultLinkStyleComboBox.findData(settings["default_link_type"])
if index != -1:
self.uiDefaultLinkStyleComboBox.setCurrentIndex(index)
def loadPreferences(self):
"""
Loads the general preferences.
@@ -415,18 +440,23 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
from ..main_window import MainWindow
MainWindow.instance().setSettings(new_general_settings)
new_graphics_view_settings = {"scene_width": self.uiSceneWidthSpinBox.value(),
"scene_height": self.uiSceneHeightSpinBox.value(),
"draw_rectangle_selected_item": self.uiRectangleSelectedItemCheckBox.isChecked(),
"draw_link_status_points": self.uiDrawLinkStatusPointsCheckBox.isChecked(),
"show_interface_labels_on_new_project": self.uiShowInterfaceLabelsOnNewProject.isChecked(),
"limit_size_node_symbols": self.uiLimitSizeNodeSymbolCheckBox.isChecked(),
"show_grid_on_new_project": self.uiShowGridOnNewProject.isChecked(),
"snap_to_grid_on_new_project": self.uiSnapToGridOnNewProject.isChecked(),
"default_label_font": self.uiDefaultLabelStylePlainTextEdit.font().toString(),
"default_label_color": self._default_label_color.name(),
"default_note_font": self.uiDefaultNoteStylePlainTextEdit.font().toString(),
"default_note_color": self._default_note_color.name()}
new_graphics_view_settings = {
"scene_width": self.uiSceneWidthSpinBox.value(),
"scene_height": self.uiSceneHeightSpinBox.value(),
"draw_rectangle_selected_item": self.uiRectangleSelectedItemCheckBox.isChecked(),
"draw_link_status_points": self.uiDrawLinkStatusPointsCheckBox.isChecked(),
"show_interface_labels_on_new_project": self.uiShowInterfaceLabelsOnNewProject.isChecked(),
"limit_size_node_symbols": self.uiLimitSizeNodeSymbolCheckBox.isChecked(),
"show_grid_on_new_project": self.uiShowGridOnNewProject.isChecked(),
"snap_to_grid_on_new_project": self.uiSnapToGridOnNewProject.isChecked(),
"default_label_font": self.uiDefaultLabelStylePlainTextEdit.font().toString(),
"default_label_color": self._default_label_color.name(),
"default_note_font": self.uiDefaultNoteStylePlainTextEdit.font().toString(),
"default_note_color": self._default_note_color.name(),
"default_link_color": self._default_link_color.name(),
"default_link_width": self.uiDefaultLinkWidthSpinBox.value(),
"default_link_type": self.uiDefaultLinkStyleComboBox.currentData().value
}
node_grid_size = self.uiNodeGridSizeSpinBox.value()
drawing_grid_size = self.uiDrawingGridSizeSpinBox.value()

View File

@@ -318,7 +318,7 @@ class Project(QtCore.QObject):
if self._id is None:
return
Controller.instance().post("/projects/{project_id}/nodes/start".format(project_id=self._id), None, body={}, timeout=None)
Controller.instance().post("/projects/{project_id}/nodes/start".format(project_id=self._id), None, timeout=None)
def duplicate(self, name=None, path=None, callback=None):
"""
@@ -345,7 +345,7 @@ class Project(QtCore.QObject):
if self._id is None:
return
Controller.instance().post("/projects/{project_id}/nodes/stop".format(project_id=self._id), None, body={}, timeout=None)
Controller.instance().post("/projects/{project_id}/nodes/stop".format(project_id=self._id), None, timeout=None)
def suspend_all_nodes(self):
"""Suspend all nodes belonging to this project"""
@@ -354,7 +354,7 @@ class Project(QtCore.QObject):
if self._id is None:
return
Controller.instance().post("/projects/{project_id}/nodes/suspend".format(project_id=self._id), None, body={}, timeout=None)
Controller.instance().post("/projects/{project_id}/nodes/suspend".format(project_id=self._id), None, timeout=None)
def reload_all_nodes(self):
"""Reload all nodes belonging to this project"""
@@ -363,7 +363,7 @@ class Project(QtCore.QObject):
if self._id is None:
return
Controller.instance().post("/projects/{project_id}/nodes/reload".format(project_id=self._id), None, body={}, timeout=None)
Controller.instance().post("/projects/{project_id}/nodes/reload".format(project_id=self._id), None, timeout=None)
def reset_console_all_nodes(self):
"""Reset console for all nodes belonging to this project"""
@@ -372,7 +372,7 @@ class Project(QtCore.QObject):
if self._id is None:
return
Controller.instance().post("/projects/{project_id}/nodes/console/reset".format(project_id=self._id), None, body={}, timeout=None)
Controller.instance().post("/projects/{project_id}/nodes/console/reset".format(project_id=self._id), None, timeout=None)
def get(self, path, callback, **kwargs):
"""
@@ -386,7 +386,7 @@ class Project(QtCore.QObject):
"""
self._projectHTTPQuery("GET", path, callback, **kwargs)
def post(self, path, callback, body={}, **kwargs):
def post(self, path, callback, body=None, **kwargs):
"""
HTTP POST on the remote server
@@ -398,7 +398,7 @@ class Project(QtCore.QObject):
"""
self._projectHTTPQuery("POST", path, callback, body=body, **kwargs)
def put(self, path, callback, body={}, **kwargs):
def put(self, path, callback, body=None, **kwargs):
"""
HTTP PUT on the remote server
@@ -410,7 +410,7 @@ class Project(QtCore.QObject):
"""
self._projectHTTPQuery("PUT", path, callback, body=body, **kwargs)
def delete(self, path, callback, body={}, **kwargs):
def delete(self, path, callback, **kwargs):
"""
HTTP DELETE on the remote server
@@ -420,9 +420,9 @@ class Project(QtCore.QObject):
Full arg list in createHTTPQuery
"""
self._projectHTTPQuery("DELETE", path, callback, body=body, **kwargs)
self._projectHTTPQuery("DELETE", path, callback, **kwargs)
def _projectHTTPQuery(self, method, path, callback, body={}, **kwargs):
def _projectHTTPQuery(self, method, path, callback, body=None, **kwargs):
"""
HTTP query on the remote server
@@ -588,7 +588,7 @@ class Project(QtCore.QObject):
self._closing = True
if self._id:
self.project_about_to_close_signal.emit()
Controller.instance().post("/projects/{project_id}/close".format(project_id=self._id), self._projectClosedCallback, body={}, progressText="Close the project")
Controller.instance().post("/projects/{project_id}/close".format(project_id=self._id), self._projectClosedCallback, progressText="Close the project")
else:
# The project is not initialized when we close it
self._closed = True
@@ -600,7 +600,7 @@ class Project(QtCore.QObject):
Delete the project from all servers
"""
self.project_about_to_close_signal.emit()
Controller.instance().delete("/projects/{project_id}".format(project_id=self._id), self._projectClosedCallback, body={}, progressText="Delete the project")
Controller.instance().delete("/projects/{project_id}".format(project_id=self._id), self._projectClosedCallback, progressText="Delete the project")
def _projectClosedCallback(self, result, error=False, server=None, **kwargs):
@@ -642,7 +642,10 @@ class Project(QtCore.QObject):
path = "/projects/{project_id}/notifications/ws".format(project_id=self._id)
self._notification_stream = Controller.instance().httpClient().connectWebSocket(self._websocket, path)
self._notification_stream.textMessageReceived.connect(self._websocket_event_received)
self._notification_stream.error.connect(self._websocket_error)
if parse_version(QtCore.QT_VERSION_STR) < parse_version("6.5.0"):
self._notification_stream.error.connect(self._websocket_error)
else:
self._notification_stream.errorOccurred.connect(self._websocket_error)
self._notification_stream.sslErrors.connect(self._sslErrorsSlot)
log.info("Listening for project notifications on '{}'".format(self._notification_stream.requestUrl().toString()))

View File

@@ -68,6 +68,7 @@ class PyCutExt(QtWidgets.QTextEdit):
super().__init__(parent)
self.interpreter = interpreter
self._default_text_color = QtGui.QColor(0, 0, 0) # black
self.colorizer = SyntaxColor()
# session log
@@ -170,6 +171,20 @@ class PyCutExt(QtWidgets.QTextEdit):
else:
return self.line
def setDefaultTextColor(self, color):
"""
Set the default text color and reset the colorizer with the new color.
"""
self._default_text_color = color
self.colorizer.set_default_text_color(color)
self.setTextColor(color)
# reset everything
self.clear()
self.write(self.intro + '\n\n')
self.write(sys.ps1)
def write(self, text, error=False, warning=False):
"""
Simulates stdin, stdout, and stderr.
@@ -193,7 +208,7 @@ class PyCutExt(QtWidgets.QTextEdit):
elif warning:
color = QtGui.QColor(255, 128, 0) # orange
else:
color = QtGui.QColor(0, 0, 0) # black
color = self._default_text_color
char_format.setForeground(QtGui.QBrush(color))
cursor.setCharFormat(char_format)
@@ -374,16 +389,15 @@ class PyCutExt(QtWidgets.QTextEdit):
if not word:
continue
(R, G, B) = self.colorizer.get_color(word)
color = self.colorizer.get_color(word)
char_format = cursor.charFormat()
char_format.setForeground(QtGui.QBrush(QtGui.QColor(R, G, B)))
char_format.setForeground(QtGui.QBrush(color))
cursor.setCharFormat(char_format)
class SyntaxColor:
"""
Allows to color python keywords.
Allows to color Python keywords.
"""
keywords = set(["and", "del", "from", "not", "while",
@@ -394,27 +408,22 @@ class SyntaxColor:
"continue", "finally", "is", "return",
"def", "for", "lambda", "try"])
def __init__(self):
self._default_text_color = QtGui.QColor(0, 0, 0) # black
def set_default_text_color(self, default_text_color):
self._default_text_color = default_text_color
def get_color(self, word):
""" Return a color tuple (R,G,B) depending of the string word """
"""
Return a color based on the string word.
"""
stripped = word.strip()
if(stripped in self.keywords):
return (165, 42, 42) # brown
elif(self.is_python_string(stripped)):
return (61, 120, 9) # dark green
if stripped in self.keywords:
return QtGui.QColor(165, 42, 42) # brown
else:
return (0, 0, 0)
def is_python_string(self, string):
"""
Return True if string is enclosed by a string mark
"""
# return (
# (string.startswith("'''") and string.endswith("'''")) or
# (string.startswith('"""') and string.endswith('"""')) or
# (string.startswith("'") and string.endswith("'")) or
# (string.startswith('"') and string.endswith('"'))
# )
return False
return self._default_text_color

View File

@@ -375,8 +375,7 @@
"high",
"normal",
"low",
"very low",
"null"]
"very low"]
}
},
"required": [

View File

@@ -308,13 +308,15 @@
"qemu_disk_interfaces": {
"enum": [
"ide",
"sata",
"nvme",
"scsi",
"sd",
"mtd",
"floppy",
"pflash",
"virtio",
"sata"
"none"
]
},
"qemu_properties": {
@@ -561,8 +563,7 @@
"high",
"normal",
"low",
"very low",
"null"
"very low"
]
}
}

View File

@@ -60,7 +60,8 @@ if sys.platform.startswith("win"):
'MobaXterm': r'"{}\Mobatek\MobaXterm Personal Edition\MobaXterm.exe" -newtab "title {{name}} & telnet {{host}} {{port}}"'.format(program_files_x86),
'Royal TS V3': r'{}\code4ward.net\Royal TS V3\RTS3App.exe /connectadhoc:{{host}} /adhoctype:terminal /p:IsTelnetConnection="true" /p:ConnectionType="telnet;Telnet Connection" /p:Port="{{port}}" /p:Name="{{name}}"'.format(program_files),
'Royal TS V5': r'"{}\Royal TS V5\RoyalTS.exe" /protocol:terminal /using:adhoc /uri:"{{host}}" /property:Port="{{port}}" /property:IsTelnetConnection="true" /property:Name="{{name}}"'.format(program_files_x86),
'SuperPutty': r'SuperPutty.exe -telnet "{host} -P {port} -wt \"{name}\""',
'SuperPutty + PuTTY': r'SuperPutty.exe -telnet "{host} -P {port} -loghost \"{name}\""',
'SuperPutty + KiTTY': r'SuperPutty.exe -telnet "{host} -P {port} -title \"{name}\""',
'SecureCRT': r'"{}\VanDyke Software\SecureCRT\SecureCRT.exe" /N "{{name}}" /T /TELNET {{host}} {{port}}'.format(program_files),
'SecureCRT (personal profile)': r'"{}\AppData\Local\VanDyke Software\SecureCRT\SecureCRT.exe" /T /N "{{name}}" /TELNET {{host}} {{port}}'.format(userprofile),
'TeraTerm Pro': r'"{}\teraterm\ttermpro.exe" /W="{{name}}" /M="ttstart.macro" /T=1 {{host}} {{port}}'.format(program_files_x86),
@@ -179,7 +180,8 @@ if sys.platform.startswith("win"):
# Windows
PRECONFIGURED_VNC_CONSOLE_COMMANDS = {
'TightVNC (included with GNS3)': 'tvnviewer.exe {host}:{port}',
'UltraVNC': r'"{}\uvnc bvba\UltraVNC\vncviewer.exe" {{host}}:{{port}}'.format(program_files)
'UltraVNC': r'"{}\uvnc bvba\UltraVNC\vncviewer.exe" {{host}}:{{port}}'.format(program_files),
'SuperPutty': r'SuperPutty.exe -vnc {host}::{port}',
}
# default Windows VNC console command
@@ -206,7 +208,7 @@ else:
'TightVNC': 'vncviewer {host}:{port}',
'Vinagre': 'vinagre {host}::{port}',
'gvncviewer': 'gvncviewer {host}:{display}',
'Remote Viewer': 'remote-viewer vnc://{host}:{port}',
'Remote Viewer': 'remote-viewer --title "({name})" vnc://{host}:{port}',
'KRDC': 'krdc vnc://{host}:{port}'
}
@@ -217,7 +219,7 @@ else:
if sys.platform.startswith("win"):
# Windows
PRECONFIGURED_SPICE_CONSOLE_COMMANDS = {
'Remote Viewer': r'"{}\VirtViewer v11.0-256\bin\remote-viewer.exe" spice://{{host}}:{{port}}'.format(program_files),
'Remote Viewer': r'"{}\VirtViewer v11.0-256\bin\remote-viewer.exe" --title "({{name}})" spice://{{host}}:{{port}}'.format(program_files),
}
# default Windows SPICE console command
@@ -226,7 +228,7 @@ if sys.platform.startswith("win"):
elif sys.platform.startswith("darwin"):
# Mac OS X
PRECONFIGURED_SPICE_CONSOLE_COMMANDS = {
'Remote Viewer': '/Applications/RemoteViewer.app/Contents/MacOS/RemoteViewer spice://{host}:{port}',
'Remote Viewer': '/Applications/RemoteViewer.app/Contents/MacOS/RemoteViewer --title "({name})" spice://{host}:{port}',
}
# default Mac OS X SPICE console command
@@ -234,7 +236,7 @@ elif sys.platform.startswith("darwin"):
else:
PRECONFIGURED_SPICE_CONSOLE_COMMANDS = {
'Remote Viewer': 'remote-viewer spice://{host}:{port}',
'Remote Viewer': 'remote-viewer --title "({name})" spice://{host}:{port}',
}
# default SPICE console command on other systems
@@ -257,6 +259,11 @@ elif sys.platform.startswith("freebsd"):
# FreeBSD
PRECONFIGURED_PACKET_CAPTURE_READER_COMMANDS = {WIRESHARK_NORMAL_CAPTURE: 'wireshark {pcap_file} --capture-comment "{project} {link_description}"',
WIRESHARK_LIVE_TRAFFIC_CAPTURE: 'gtail -f -c +0b {pcap_file} | wireshark --capture-comment "{project} {link_description}" -o "gui.window_title:{link_description}" -k -i -'}
elif sys.platform.startswith("openbsd"):
# OpenBSD
PRECONFIGURED_PACKET_CAPTURE_READER_COMMANDS = {WIRESHARK_NORMAL_CAPTURE: 'wireshark {pcap_file} --capture-comment "{project} {link_description}"',
WIRESHARK_LIVE_TRAFFIC_CAPTURE: 'tail -f -c +0 {pcap_file} | wireshark --capture-comment "{project} {link_description}" -o "gui.window_title:{link_description}" -k -i -'}
else:
PRECONFIGURED_PACKET_CAPTURE_READER_COMMANDS = {WIRESHARK_NORMAL_CAPTURE: 'wireshark {pcap_file} --capture-comment "{project} {link_description}"',
WIRESHARK_LIVE_TRAFFIC_CAPTURE: 'tail -f -c +0b {pcap_file} | wireshark --capture-comment "{project} {link_description}" -o "gui.window_title:{link_description}" -k -i -'}
@@ -268,7 +275,7 @@ if sys.platform.startswith("win"):
# Windows 64-bit
DEFAULT_PACKET_CAPTURE_ANALYZER_COMMAND = r'"{}\SolarWinds\ResponseTimeViewer\ResponseTimeViewer.exe" {{pcap_file}}'.format(program_files_x86)
STYLES = ["Charcoal", "Classic", "Legacy"]
STYLES = ["Charcoal", "Dark", "Classic", "Legacy"]
SYMBOL_THEMES = ["Classic",
"Affinity-square-blue",
@@ -322,6 +329,9 @@ GRAPHICS_VIEW_SETTINGS = {
"default_label_color": "#000000",
"default_note_font": "TypeWriter,10,-1,5,75,0,0,0,0,0",
"default_note_color": "#000000",
"default_link_color": "#000000",
"default_link_type": 1,
"default_link_width": 2,
"zoom": None,
"show_layers": False,
"snap_to_grid": False,

View File

@@ -20,7 +20,7 @@ Sets window styles
"""
import sys
from gns3.qt import QtCore, QtGui
from gns3.qt import QtCore, QtGui, QtWidgets
class Style:
@@ -42,16 +42,11 @@ class Style:
icon.addPixmap(QtGui.QPixmap(active_file), QtGui.QIcon.Mode.Active, QtGui.QIcon.State.Off)
return icon
def setLegacyStyle(self):
def _setLegacyIcons(self):
"""
Sets the legacy GUI style.
Sets the legacy icons.
"""
graphics_view = self._mw.uiGraphicsView
if hasattr(graphics_view, 'resetGridColors'):
graphics_view.resetGridColors()
self._mw.setStyleSheet("")
self._mw.uiNewProjectAction.setIcon(QtGui.QIcon(":/icons/new-project.svg"))
self._mw.uiOpenProjectAction.setIcon(QtGui.QIcon(":/icons/open.svg"))
self._mw.uiOpenApplianceAction.setIcon(QtGui.QIcon(":/icons/open.svg"))
@@ -97,17 +92,25 @@ class Style:
icon.addPixmap(QtGui.QPixmap(":/icons/unlock.svg"), QtGui.QIcon.Mode.Active, QtGui.QIcon.State.Off)
self._mw.uiLockAllAction.setIcon(icon)
def setClassicStyle(self):
def setLegacyStyle(self):
"""
Sets the classic GUI style.
Sets the legacy GUI style.
"""
graphics_view = self._mw.uiGraphicsView
if hasattr(graphics_view, 'resetGridColors'):
graphics_view.resetGridColors()
self._mw.setStyleSheet("")
QtGui.QGuiApplication.setPalette(QtWidgets.QApplication.style().standardPalette())
self._mw.uiConsoleTextEdit.setDefaultTextColor(QtGui.QColor(0, 0, 0))
self._resetDefaultColors()
self._setLegacyIcons()
def _setClassicIcons(self):
"""
Sets the classic icons.
"""
self._mw.uiNewProjectAction.setIcon(self._getStyleIcon(":/classic_icons/new-project.svg", ":/classic_icons/new-project-hover.svg"))
self._mw.uiOpenProjectAction.setIcon(self._getStyleIcon(":/classic_icons/open.svg", ":/classic_icons/open-hover.svg"))
self._mw.uiOpenApplianceAction.setIcon(self._getStyleIcon(":/classic_icons/open.svg", ":/classic_icons/open-hover.svg"))
@@ -158,22 +161,26 @@ class Style:
icon.addPixmap(QtGui.QPixmap(":/classic_icons/unlock-hover.svg"), QtGui.QIcon.Mode.Active, QtGui.QIcon.State.Off)
self._mw.uiLockAllAction.setIcon(icon)
def setCharcoalStyle(self):
def setClassicStyle(self):
"""
Sets the charcoal GUI style.
Sets the classic GUI style.
"""
graphics_view = self._mw.uiGraphicsView
if hasattr(graphics_view, 'resetGridColors'):
graphics_view.resetGridColors()
style_file = QtCore.QFile(":/styles/charcoal.css")
style_file.open(QtCore.QIODeviceBase.OpenModeFlag.ReadOnly)
style = QtCore.QTextStream(style_file).readAll()
if sys.platform.startswith("darwin"):
style += "QDockWidget::title {text-align: center; background-color: #535353}"
self._mw.setStyleSheet("")
QtGui.QGuiApplication.setPalette(QtWidgets.QApplication.style().standardPalette())
self._mw.uiConsoleTextEdit.setDefaultTextColor(QtGui.QColor(0, 0, 0))
self._resetDefaultColors()
self._setClassicIcons()
def _setCharcoalIcons(self):
"""
Sets the charcoal icons.
"""
self._mw.setStyleSheet(style)
self._mw.uiNewProjectAction.setIcon(self._getStyleIcon(":/charcoal_icons/new-project.svg", ":/charcoal_icons/new-project-hover.svg"))
self._mw.uiOpenProjectAction.setIcon(self._getStyleIcon(":/charcoal_icons/open.svg", ":/charcoal_icons/open-hover.svg"))
self._mw.uiOpenApplianceAction.setIcon(self._getStyleIcon(":/charcoal_icons/open.svg", ":/charcoal_icons/open-hover.svg"))
@@ -223,3 +230,70 @@ class Style:
icon.addPixmap(QtGui.QPixmap(":/charcoal_icons/unlock.svg"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon.addPixmap(QtGui.QPixmap(":/charcoal_icons/unlock-hover.svg"), QtGui.QIcon.Mode.Active, QtGui.QIcon.State.Off)
self._mw.uiLockAllAction.setIcon(icon)
def _resetDefaultColors(self):
"""
Reset the default colors if switching from the Dark style.
"""
# set the default colors to black if they are still set to light gray (set by the dark style)
graphics_view = self._mw.uiGraphicsView
topology_view_settings = graphics_view.settings()
if topology_view_settings["default_note_color"] == "#dfe1e2":
topology_view_settings["default_note_color"] = "#000000"
if topology_view_settings["default_label_color"] == "#dfe1e2":
topology_view_settings["default_label_color"] = "#000000"
if topology_view_settings["default_link_color"] == "#dfe1e2":
topology_view_settings["default_link_color"] = "#000000"
graphics_view.setSettings(topology_view_settings)
def setCharcoalStyle(self):
"""
Sets the charcoal GUI style.
"""
graphics_view = self._mw.uiGraphicsView
if hasattr(graphics_view, 'resetGridColors'):
graphics_view.resetGridColors()
style_file = QtCore.QFile(":/styles/charcoal.css")
style_file.open(QtCore.QIODeviceBase.OpenModeFlag.ReadOnly)
style = QtCore.QTextStream(style_file).readAll()
if sys.platform.startswith("darwin"):
style += "QDockWidget::title {text-align: center; background-color: #535353}"
self._mw.setStyleSheet(style)
QtWidgets.QApplication.setPalette(QtWidgets.QApplication.style().standardPalette())
self._mw.uiConsoleTextEdit.setDefaultTextColor(QtGui.QColor(0, 0, 0))
self._resetDefaultColors()
self._setCharcoalIcons()
def setDarkStyle(self):
"""
Sets the dark GUI style.
"""
graphics_view = self._mw.uiGraphicsView
if hasattr(graphics_view, 'resetGridColors'):
graphics_view.resetGridColors()
import qdarkstyle
style = qdarkstyle.load_stylesheet(qt_api='pyqt6')
style += "QMenu::item { padding: 5px; }"
self._mw.setStyleSheet(style)
color = QtGui.QColor(0xdf, 0xe1, 0xe2) # light gray
palette = QtGui.QPalette()
palette.setColor(QtGui.QPalette.ColorRole.Text, color)
QtGui.QGuiApplication.setPalette(palette)
self._mw.uiConsoleTextEdit.setDefaultTextColor(color)
# set the default colors to the light gray if they are still set to black
# (i.e. not customized by the user) to be visible in the dark style
topology_view_settings = graphics_view.settings()
if topology_view_settings["default_note_color"] == "#000000":
topology_view_settings["default_note_color"] = color.name()
if topology_view_settings["default_label_color"] == "#000000":
topology_view_settings["default_label_color"] = color.name()
if topology_view_settings["default_link_color"] == "#000000":
topology_view_settings["default_link_color"] = color.name()
graphics_view.setSettings(topology_view_settings)
self._setCharcoalIcons() # use the charcoal icons for the dark style

View File

@@ -281,7 +281,7 @@ class TopologySummaryView(QtWidgets.QTreeWidget):
Contextual menu to expand and collapse the tree.
"""
menu = QtWidgets.QMenu()
menu = QtWidgets.QMenu(parent=self)
expand_all = QtGui.QAction("Expand all", menu)
expand_all.setIcon(get_icon("plus.svg"))
expand_all.triggered.connect(self._expandAllSlot)

View File

@@ -641,25 +641,43 @@
<attribute name="title">
<string>Topology view</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="uiSceneWidthLabel">
<layout class="QGridLayout" name="gridLayout_6">
<item row="3" column="0">
<widget class="QLabel" name="uiDrawingGridSizeLabel">
<property name="text">
<string>Default width:</string>
<string>Default drawing grid size:</string>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QLabel" name="uiNotePreviewLabel">
<item row="2" column="0">
<widget class="QLabel" name="uiNodeGridSizeLabel">
<property name="text">
<string>Default note style:</string>
<string>Default node grid size:</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="uiShowInterfaceLabelsOnNewProject">
<property name="text">
<string>Show interface labels on new project</string>
<item row="2" column="1">
<widget class="QSpinBox" name="uiNodeGridSizeSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>150</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>75</number>
</property>
</widget>
</item>
@@ -670,122 +688,298 @@
</property>
</widget>
</item>
<item row="14" column="0" colspan="3">
<widget class="QPlainTextEdit" name="uiDefaultNoteStylePlainTextEdit">
<item row="10" column="0" colspan="2">
<widget class="QToolBox" name="toolBox">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>472</width>
<height>101</height>
</rect>
</property>
<attribute name="label">
<string>Default label style</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="uiDefaultLabelFontPushButton">
<property name="text">
<string>&amp;Select default font</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="uiDefaultLabelColorPushButton">
<property name="text">
<string>&amp;Select default color</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QPlainTextEdit" name="uiDefaultLabelStylePlainTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="plainText">
<string>AaBbYyZz</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>309</width>
<height>101</height>
</rect>
</property>
<attribute name="label">
<string>Default note style</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="uiDefaultNoteStylePlainTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="plainText">
<string>AaBbYyZz</string>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_14">
<item>
<widget class="QPushButton" name="uiDefaultNoteFontPushButton">
<property name="text">
<string>&amp;Select default font</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="uiDefaultNoteColorPushButton">
<property name="text">
<string>&amp;Select default color</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>472</width>
<height>106</height>
</rect>
</property>
<attribute name="label">
<string>Default link style</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="uiDefaultLinkColorLabel">
<property name="text">
<string>Color:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="uiDefaultLinkColorPushButton">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="uiDefaultLinkWidthLabel">
<property name="text">
<string>Width:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="uiDefaultLinkWidthSpinBox">
<property name="suffix">
<string> px</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="uiDefaultLinkStyleLabel">
<property name="text">
<string>Style:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="uiDefaultLinkStyleComboBox"/>
</item>
</layout>
</widget>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="uiSceneWidthSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
<enum>Qt::StrongFocus</enum>
</property>
<property name="readOnly">
<bool>true</bool>
<property name="suffix">
<string> pixels</string>
</property>
<property name="plainText">
<string>AaBbYyZz</string>
<property name="minimum">
<number>500</number>
</property>
<property name="maximum">
<number>1000000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="value">
<number>2000</number>
</property>
</widget>
</item>
<item row="15" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout_14">
<item>
<widget class="QPushButton" name="uiDefaultNoteFontPushButton">
<property name="text">
<string>&amp;Select default font</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="uiDefaultNoteColorPushButton">
<property name="text">
<string>&amp;Select default color</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
<item row="3" column="1">
<widget class="QSpinBox" name="uiDrawingGridSizeSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>25</number>
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<widget class="QCheckBox" name="uiLimitSizeNodeSymbolCheckBox">
<item row="0" column="0">
<widget class="QLabel" name="uiSceneWidthLabel">
<property name="text">
<string>Limit the size of node symbols</string>
<string>Default width:</string>
</property>
</widget>
</item>
<item row="12" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="uiDefaultLabelFontPushButton">
<property name="text">
<string>&amp;Select default font</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="uiDefaultLabelColorPushButton">
<property name="text">
<string>&amp;Select default color</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="11" column="0" colspan="3">
<widget class="QPlainTextEdit" name="uiDefaultLabelStylePlainTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="5" column="0">
<widget class="QCheckBox" name="uiDrawLinkStatusPointsCheckBox">
<property name="text">
<string>Draw link status points</string>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="readOnly">
<property name="checked">
<bool>true</bool>
</property>
<property name="plainText">
<string>AaBbYyZz</string>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="uiShowInterfaceLabelsOnNewProject">
<property name="text">
<string>Show interface labels on new project</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="uiShowGridOnNewProject">
<property name="text">
<string>Show grid on new project</string>
</property>
</widget>
</item>
@@ -817,120 +1011,7 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="uiSceneWidthSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="suffix">
<string> pixels</string>
</property>
<property name="minimum">
<number>500</number>
</property>
<property name="maximum">
<number>1000000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="value">
<number>2000</number>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QCheckBox" name="uiSnapToGridOnNewProject">
<property name="text">
<string>Snap to grid on new project</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="uiDrawingGridSizeLabel">
<property name="text">
<string>Default drawing grid size:</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="uiLabelPreviewLabel">
<property name="text">
<string>Default label style:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="uiNodeGridSizeLabel">
<property name="text">
<string>Default node grid size:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="uiDrawingGridSizeSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>25</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="uiNodeGridSizeSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>150</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>75</number>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QCheckBox" name="uiShowGridOnNewProject">
<property name="text">
<string>Show grid on new project</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="uiRectangleSelectedItemCheckBox">
<property name="text">
<string>Draw a rectangle when an item is selected</string>
@@ -940,7 +1021,7 @@
</property>
</widget>
</item>
<item row="16" column="0">
<item row="13" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -953,16 +1034,33 @@
</property>
</spacer>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="uiDrawLinkStatusPointsCheckBox">
<item row="9" column="0" colspan="2">
<widget class="QCheckBox" name="uiLimitSizeNodeSymbolCheckBox">
<property name="text">
<string>Draw link status points</string>
</property>
<property name="checked">
<bool>true</bool>
<string>Limit the size of node symbols</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QCheckBox" name="uiSnapToGridOnNewProject">
<property name="text">
<string>Snap to grid on new project</string>
</property>
</widget>
</item>
<item row="11" column="0">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="uiMiscTab">

View File

@@ -1,6 +1,6 @@
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/general_preferences_page.ui'
#
# Created by: PyQt6 UI code generator 6.10.1
# Created by: PyQt6 UI code generator 6.7.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
@@ -293,118 +293,14 @@ class Ui_GeneralPreferencesPageWidget(object):
self.uiMiscTabWidget.addTab(self.uiSPICETab, "")
self.uiSceneTab = QtWidgets.QWidget()
self.uiSceneTab.setObjectName("uiSceneTab")
self.gridLayout_3 = QtWidgets.QGridLayout(self.uiSceneTab)
self.gridLayout_3.setObjectName("gridLayout_3")
self.uiSceneWidthLabel = QtWidgets.QLabel(parent=self.uiSceneTab)
self.uiSceneWidthLabel.setObjectName("uiSceneWidthLabel")
self.gridLayout_3.addWidget(self.uiSceneWidthLabel, 0, 0, 1, 1)
self.uiNotePreviewLabel = QtWidgets.QLabel(parent=self.uiSceneTab)
self.uiNotePreviewLabel.setObjectName("uiNotePreviewLabel")
self.gridLayout_3.addWidget(self.uiNotePreviewLabel, 13, 0, 1, 1)
self.uiShowInterfaceLabelsOnNewProject = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiShowInterfaceLabelsOnNewProject.setObjectName("uiShowInterfaceLabelsOnNewProject")
self.gridLayout_3.addWidget(self.uiShowInterfaceLabelsOnNewProject, 6, 0, 1, 2)
self.uiSceneHeightLabel = QtWidgets.QLabel(parent=self.uiSceneTab)
self.uiSceneHeightLabel.setObjectName("uiSceneHeightLabel")
self.gridLayout_3.addWidget(self.uiSceneHeightLabel, 1, 0, 1, 1)
self.uiDefaultNoteStylePlainTextEdit = QtWidgets.QPlainTextEdit(parent=self.uiSceneTab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiDefaultNoteStylePlainTextEdit.sizePolicy().hasHeightForWidth())
self.uiDefaultNoteStylePlainTextEdit.setSizePolicy(sizePolicy)
self.uiDefaultNoteStylePlainTextEdit.setMaximumSize(QtCore.QSize(16777215, 50))
self.uiDefaultNoteStylePlainTextEdit.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.uiDefaultNoteStylePlainTextEdit.setReadOnly(True)
self.uiDefaultNoteStylePlainTextEdit.setObjectName("uiDefaultNoteStylePlainTextEdit")
self.gridLayout_3.addWidget(self.uiDefaultNoteStylePlainTextEdit, 14, 0, 1, 3)
self.horizontalLayout_14 = QtWidgets.QHBoxLayout()
self.horizontalLayout_14.setObjectName("horizontalLayout_14")
self.uiDefaultNoteFontPushButton = QtWidgets.QPushButton(parent=self.uiSceneTab)
self.uiDefaultNoteFontPushButton.setObjectName("uiDefaultNoteFontPushButton")
self.horizontalLayout_14.addWidget(self.uiDefaultNoteFontPushButton)
self.uiDefaultNoteColorPushButton = QtWidgets.QPushButton(parent=self.uiSceneTab)
self.uiDefaultNoteColorPushButton.setObjectName("uiDefaultNoteColorPushButton")
self.horizontalLayout_14.addWidget(self.uiDefaultNoteColorPushButton)
spacerItem7 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_14.addItem(spacerItem7)
self.gridLayout_3.addLayout(self.horizontalLayout_14, 15, 0, 1, 3)
self.uiLimitSizeNodeSymbolCheckBox = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiLimitSizeNodeSymbolCheckBox.setObjectName("uiLimitSizeNodeSymbolCheckBox")
self.gridLayout_3.addWidget(self.uiLimitSizeNodeSymbolCheckBox, 9, 0, 1, 2)
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.uiDefaultLabelFontPushButton = QtWidgets.QPushButton(parent=self.uiSceneTab)
self.uiDefaultLabelFontPushButton.setObjectName("uiDefaultLabelFontPushButton")
self.horizontalLayout_5.addWidget(self.uiDefaultLabelFontPushButton)
self.uiDefaultLabelColorPushButton = QtWidgets.QPushButton(parent=self.uiSceneTab)
self.uiDefaultLabelColorPushButton.setObjectName("uiDefaultLabelColorPushButton")
self.horizontalLayout_5.addWidget(self.uiDefaultLabelColorPushButton)
spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(spacerItem8)
self.gridLayout_3.addLayout(self.horizontalLayout_5, 12, 0, 1, 3)
self.uiDefaultLabelStylePlainTextEdit = QtWidgets.QPlainTextEdit(parent=self.uiSceneTab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiDefaultLabelStylePlainTextEdit.sizePolicy().hasHeightForWidth())
self.uiDefaultLabelStylePlainTextEdit.setSizePolicy(sizePolicy)
self.uiDefaultLabelStylePlainTextEdit.setMaximumSize(QtCore.QSize(16777215, 50))
self.uiDefaultLabelStylePlainTextEdit.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.uiDefaultLabelStylePlainTextEdit.setReadOnly(True)
self.uiDefaultLabelStylePlainTextEdit.setObjectName("uiDefaultLabelStylePlainTextEdit")
self.gridLayout_3.addWidget(self.uiDefaultLabelStylePlainTextEdit, 11, 0, 1, 3)
self.uiSceneHeightSpinBox = QtWidgets.QSpinBox(parent=self.uiSceneTab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiSceneHeightSpinBox.sizePolicy().hasHeightForWidth())
self.uiSceneHeightSpinBox.setSizePolicy(sizePolicy)
self.uiSceneHeightSpinBox.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus)
self.uiSceneHeightSpinBox.setMinimum(500)
self.uiSceneHeightSpinBox.setMaximum(1000000)
self.uiSceneHeightSpinBox.setSingleStep(100)
self.uiSceneHeightSpinBox.setProperty("value", 1000)
self.uiSceneHeightSpinBox.setObjectName("uiSceneHeightSpinBox")
self.gridLayout_3.addWidget(self.uiSceneHeightSpinBox, 1, 1, 1, 1)
self.uiSceneWidthSpinBox = QtWidgets.QSpinBox(parent=self.uiSceneTab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiSceneWidthSpinBox.sizePolicy().hasHeightForWidth())
self.uiSceneWidthSpinBox.setSizePolicy(sizePolicy)
self.uiSceneWidthSpinBox.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus)
self.uiSceneWidthSpinBox.setMinimum(500)
self.uiSceneWidthSpinBox.setMaximum(1000000)
self.uiSceneWidthSpinBox.setSingleStep(100)
self.uiSceneWidthSpinBox.setProperty("value", 2000)
self.uiSceneWidthSpinBox.setObjectName("uiSceneWidthSpinBox")
self.gridLayout_3.addWidget(self.uiSceneWidthSpinBox, 0, 1, 1, 1)
self.uiSnapToGridOnNewProject = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiSnapToGridOnNewProject.setObjectName("uiSnapToGridOnNewProject")
self.gridLayout_3.addWidget(self.uiSnapToGridOnNewProject, 8, 0, 1, 2)
self.gridLayout_6 = QtWidgets.QGridLayout(self.uiSceneTab)
self.gridLayout_6.setObjectName("gridLayout_6")
self.uiDrawingGridSizeLabel = QtWidgets.QLabel(parent=self.uiSceneTab)
self.uiDrawingGridSizeLabel.setObjectName("uiDrawingGridSizeLabel")
self.gridLayout_3.addWidget(self.uiDrawingGridSizeLabel, 3, 0, 1, 1)
self.uiLabelPreviewLabel = QtWidgets.QLabel(parent=self.uiSceneTab)
self.uiLabelPreviewLabel.setObjectName("uiLabelPreviewLabel")
self.gridLayout_3.addWidget(self.uiLabelPreviewLabel, 10, 0, 1, 1)
self.gridLayout_6.addWidget(self.uiDrawingGridSizeLabel, 3, 0, 1, 1)
self.uiNodeGridSizeLabel = QtWidgets.QLabel(parent=self.uiSceneTab)
self.uiNodeGridSizeLabel.setObjectName("uiNodeGridSizeLabel")
self.gridLayout_3.addWidget(self.uiNodeGridSizeLabel, 2, 0, 1, 1)
self.uiDrawingGridSizeSpinBox = QtWidgets.QSpinBox(parent=self.uiSceneTab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiDrawingGridSizeSpinBox.sizePolicy().hasHeightForWidth())
self.uiDrawingGridSizeSpinBox.setSizePolicy(sizePolicy)
self.uiDrawingGridSizeSpinBox.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus)
self.uiDrawingGridSizeSpinBox.setMinimum(5)
self.uiDrawingGridSizeSpinBox.setMaximum(100)
self.uiDrawingGridSizeSpinBox.setSingleStep(5)
self.uiDrawingGridSizeSpinBox.setProperty("value", 25)
self.uiDrawingGridSizeSpinBox.setObjectName("uiDrawingGridSizeSpinBox")
self.gridLayout_3.addWidget(self.uiDrawingGridSizeSpinBox, 3, 1, 1, 1)
self.gridLayout_6.addWidget(self.uiNodeGridSizeLabel, 2, 0, 1, 1)
self.uiNodeGridSizeSpinBox = QtWidgets.QSpinBox(parent=self.uiSceneTab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
@@ -417,20 +313,163 @@ class Ui_GeneralPreferencesPageWidget(object):
self.uiNodeGridSizeSpinBox.setSingleStep(5)
self.uiNodeGridSizeSpinBox.setProperty("value", 75)
self.uiNodeGridSizeSpinBox.setObjectName("uiNodeGridSizeSpinBox")
self.gridLayout_3.addWidget(self.uiNodeGridSizeSpinBox, 2, 1, 1, 1)
self.uiShowGridOnNewProject = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiShowGridOnNewProject.setObjectName("uiShowGridOnNewProject")
self.gridLayout_3.addWidget(self.uiShowGridOnNewProject, 7, 0, 1, 2)
self.uiRectangleSelectedItemCheckBox = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiRectangleSelectedItemCheckBox.setChecked(True)
self.uiRectangleSelectedItemCheckBox.setObjectName("uiRectangleSelectedItemCheckBox")
self.gridLayout_3.addWidget(self.uiRectangleSelectedItemCheckBox, 4, 0, 1, 3)
spacerItem9 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.gridLayout_3.addItem(spacerItem9, 16, 0, 1, 1)
self.gridLayout_6.addWidget(self.uiNodeGridSizeSpinBox, 2, 1, 1, 1)
self.uiSceneHeightLabel = QtWidgets.QLabel(parent=self.uiSceneTab)
self.uiSceneHeightLabel.setObjectName("uiSceneHeightLabel")
self.gridLayout_6.addWidget(self.uiSceneHeightLabel, 1, 0, 1, 1)
self.toolBox = QtWidgets.QToolBox(parent=self.uiSceneTab)
self.toolBox.setObjectName("toolBox")
self.page = QtWidgets.QWidget()
self.page.setGeometry(QtCore.QRect(0, 0, 472, 101))
self.page.setObjectName("page")
self.gridLayout_3 = QtWidgets.QGridLayout(self.page)
self.gridLayout_3.setObjectName("gridLayout_3")
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.uiDefaultLabelFontPushButton = QtWidgets.QPushButton(parent=self.page)
self.uiDefaultLabelFontPushButton.setObjectName("uiDefaultLabelFontPushButton")
self.horizontalLayout_5.addWidget(self.uiDefaultLabelFontPushButton)
self.uiDefaultLabelColorPushButton = QtWidgets.QPushButton(parent=self.page)
self.uiDefaultLabelColorPushButton.setObjectName("uiDefaultLabelColorPushButton")
self.horizontalLayout_5.addWidget(self.uiDefaultLabelColorPushButton)
spacerItem7 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(spacerItem7)
self.gridLayout_3.addLayout(self.horizontalLayout_5, 1, 0, 1, 1)
self.uiDefaultLabelStylePlainTextEdit = QtWidgets.QPlainTextEdit(parent=self.page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiDefaultLabelStylePlainTextEdit.sizePolicy().hasHeightForWidth())
self.uiDefaultLabelStylePlainTextEdit.setSizePolicy(sizePolicy)
self.uiDefaultLabelStylePlainTextEdit.setMaximumSize(QtCore.QSize(16777215, 50))
self.uiDefaultLabelStylePlainTextEdit.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.uiDefaultLabelStylePlainTextEdit.setReadOnly(True)
self.uiDefaultLabelStylePlainTextEdit.setObjectName("uiDefaultLabelStylePlainTextEdit")
self.gridLayout_3.addWidget(self.uiDefaultLabelStylePlainTextEdit, 0, 0, 1, 1)
self.toolBox.addItem(self.page, "")
self.page_2 = QtWidgets.QWidget()
self.page_2.setGeometry(QtCore.QRect(0, 0, 309, 101))
self.page_2.setObjectName("page_2")
self.gridLayout_5 = QtWidgets.QGridLayout(self.page_2)
self.gridLayout_5.setObjectName("gridLayout_5")
self.uiDefaultNoteStylePlainTextEdit = QtWidgets.QPlainTextEdit(parent=self.page_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiDefaultNoteStylePlainTextEdit.sizePolicy().hasHeightForWidth())
self.uiDefaultNoteStylePlainTextEdit.setSizePolicy(sizePolicy)
self.uiDefaultNoteStylePlainTextEdit.setMaximumSize(QtCore.QSize(16777215, 50))
self.uiDefaultNoteStylePlainTextEdit.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.uiDefaultNoteStylePlainTextEdit.setReadOnly(True)
self.uiDefaultNoteStylePlainTextEdit.setObjectName("uiDefaultNoteStylePlainTextEdit")
self.gridLayout_5.addWidget(self.uiDefaultNoteStylePlainTextEdit, 0, 0, 1, 1)
self.horizontalLayout_14 = QtWidgets.QHBoxLayout()
self.horizontalLayout_14.setObjectName("horizontalLayout_14")
self.uiDefaultNoteFontPushButton = QtWidgets.QPushButton(parent=self.page_2)
self.uiDefaultNoteFontPushButton.setObjectName("uiDefaultNoteFontPushButton")
self.horizontalLayout_14.addWidget(self.uiDefaultNoteFontPushButton)
self.uiDefaultNoteColorPushButton = QtWidgets.QPushButton(parent=self.page_2)
self.uiDefaultNoteColorPushButton.setObjectName("uiDefaultNoteColorPushButton")
self.horizontalLayout_14.addWidget(self.uiDefaultNoteColorPushButton)
spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_14.addItem(spacerItem8)
self.gridLayout_5.addLayout(self.horizontalLayout_14, 1, 0, 1, 1)
self.toolBox.addItem(self.page_2, "")
self.page_3 = QtWidgets.QWidget()
self.page_3.setGeometry(QtCore.QRect(0, 0, 472, 106))
self.page_3.setObjectName("page_3")
self.formLayout = QtWidgets.QFormLayout(self.page_3)
self.formLayout.setObjectName("formLayout")
self.uiDefaultLinkColorLabel = QtWidgets.QLabel(parent=self.page_3)
self.uiDefaultLinkColorLabel.setObjectName("uiDefaultLinkColorLabel")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.uiDefaultLinkColorLabel)
self.uiDefaultLinkColorPushButton = QtWidgets.QPushButton(parent=self.page_3)
self.uiDefaultLinkColorPushButton.setText("")
self.uiDefaultLinkColorPushButton.setObjectName("uiDefaultLinkColorPushButton")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.uiDefaultLinkColorPushButton)
self.uiDefaultLinkWidthLabel = QtWidgets.QLabel(parent=self.page_3)
self.uiDefaultLinkWidthLabel.setObjectName("uiDefaultLinkWidthLabel")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.uiDefaultLinkWidthLabel)
self.uiDefaultLinkWidthSpinBox = QtWidgets.QSpinBox(parent=self.page_3)
self.uiDefaultLinkWidthSpinBox.setMinimum(1)
self.uiDefaultLinkWidthSpinBox.setMaximum(100)
self.uiDefaultLinkWidthSpinBox.setProperty("value", 2)
self.uiDefaultLinkWidthSpinBox.setObjectName("uiDefaultLinkWidthSpinBox")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.uiDefaultLinkWidthSpinBox)
self.uiDefaultLinkStyleLabel = QtWidgets.QLabel(parent=self.page_3)
self.uiDefaultLinkStyleLabel.setObjectName("uiDefaultLinkStyleLabel")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.uiDefaultLinkStyleLabel)
self.uiDefaultLinkStyleComboBox = QtWidgets.QComboBox(parent=self.page_3)
self.uiDefaultLinkStyleComboBox.setObjectName("uiDefaultLinkStyleComboBox")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.uiDefaultLinkStyleComboBox)
self.toolBox.addItem(self.page_3, "")
self.gridLayout_6.addWidget(self.toolBox, 10, 0, 1, 2)
self.uiSceneWidthSpinBox = QtWidgets.QSpinBox(parent=self.uiSceneTab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiSceneWidthSpinBox.sizePolicy().hasHeightForWidth())
self.uiSceneWidthSpinBox.setSizePolicy(sizePolicy)
self.uiSceneWidthSpinBox.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus)
self.uiSceneWidthSpinBox.setMinimum(500)
self.uiSceneWidthSpinBox.setMaximum(1000000)
self.uiSceneWidthSpinBox.setSingleStep(100)
self.uiSceneWidthSpinBox.setProperty("value", 2000)
self.uiSceneWidthSpinBox.setObjectName("uiSceneWidthSpinBox")
self.gridLayout_6.addWidget(self.uiSceneWidthSpinBox, 0, 1, 1, 1)
self.uiDrawingGridSizeSpinBox = QtWidgets.QSpinBox(parent=self.uiSceneTab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiDrawingGridSizeSpinBox.sizePolicy().hasHeightForWidth())
self.uiDrawingGridSizeSpinBox.setSizePolicy(sizePolicy)
self.uiDrawingGridSizeSpinBox.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus)
self.uiDrawingGridSizeSpinBox.setMinimum(5)
self.uiDrawingGridSizeSpinBox.setMaximum(100)
self.uiDrawingGridSizeSpinBox.setSingleStep(5)
self.uiDrawingGridSizeSpinBox.setProperty("value", 25)
self.uiDrawingGridSizeSpinBox.setObjectName("uiDrawingGridSizeSpinBox")
self.gridLayout_6.addWidget(self.uiDrawingGridSizeSpinBox, 3, 1, 1, 1)
self.uiSceneWidthLabel = QtWidgets.QLabel(parent=self.uiSceneTab)
self.uiSceneWidthLabel.setObjectName("uiSceneWidthLabel")
self.gridLayout_6.addWidget(self.uiSceneWidthLabel, 0, 0, 1, 1)
self.uiDrawLinkStatusPointsCheckBox = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiDrawLinkStatusPointsCheckBox.setChecked(True)
self.uiDrawLinkStatusPointsCheckBox.setObjectName("uiDrawLinkStatusPointsCheckBox")
self.gridLayout_3.addWidget(self.uiDrawLinkStatusPointsCheckBox, 5, 0, 1, 2)
self.gridLayout_6.addWidget(self.uiDrawLinkStatusPointsCheckBox, 5, 0, 1, 1)
self.uiShowInterfaceLabelsOnNewProject = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiShowInterfaceLabelsOnNewProject.setObjectName("uiShowInterfaceLabelsOnNewProject")
self.gridLayout_6.addWidget(self.uiShowInterfaceLabelsOnNewProject, 6, 0, 1, 2)
self.uiShowGridOnNewProject = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiShowGridOnNewProject.setObjectName("uiShowGridOnNewProject")
self.gridLayout_6.addWidget(self.uiShowGridOnNewProject, 7, 0, 1, 1)
self.uiSceneHeightSpinBox = QtWidgets.QSpinBox(parent=self.uiSceneTab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiSceneHeightSpinBox.sizePolicy().hasHeightForWidth())
self.uiSceneHeightSpinBox.setSizePolicy(sizePolicy)
self.uiSceneHeightSpinBox.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus)
self.uiSceneHeightSpinBox.setMinimum(500)
self.uiSceneHeightSpinBox.setMaximum(1000000)
self.uiSceneHeightSpinBox.setSingleStep(100)
self.uiSceneHeightSpinBox.setProperty("value", 1000)
self.uiSceneHeightSpinBox.setObjectName("uiSceneHeightSpinBox")
self.gridLayout_6.addWidget(self.uiSceneHeightSpinBox, 1, 1, 1, 1)
self.uiRectangleSelectedItemCheckBox = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiRectangleSelectedItemCheckBox.setChecked(True)
self.uiRectangleSelectedItemCheckBox.setObjectName("uiRectangleSelectedItemCheckBox")
self.gridLayout_6.addWidget(self.uiRectangleSelectedItemCheckBox, 4, 0, 1, 2)
spacerItem9 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.gridLayout_6.addItem(spacerItem9, 13, 0, 1, 1)
self.uiLimitSizeNodeSymbolCheckBox = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiLimitSizeNodeSymbolCheckBox.setObjectName("uiLimitSizeNodeSymbolCheckBox")
self.gridLayout_6.addWidget(self.uiLimitSizeNodeSymbolCheckBox, 9, 0, 1, 2)
self.uiSnapToGridOnNewProject = QtWidgets.QCheckBox(parent=self.uiSceneTab)
self.uiSnapToGridOnNewProject.setObjectName("uiSnapToGridOnNewProject")
self.gridLayout_6.addWidget(self.uiSnapToGridOnNewProject, 8, 0, 1, 2)
spacerItem10 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.gridLayout_6.addItem(spacerItem10, 11, 0, 1, 1)
self.uiMiscTabWidget.addTab(self.uiSceneTab, "")
self.uiMiscTab = QtWidgets.QWidget()
self.uiMiscTab.setObjectName("uiMiscTab")
@@ -457,14 +496,14 @@ class Ui_GeneralPreferencesPageWidget(object):
self.uiDirectFileUpload = QtWidgets.QCheckBox(parent=self.uiMiscTab)
self.uiDirectFileUpload.setObjectName("uiDirectFileUpload")
self.verticalLayout_2.addWidget(self.uiDirectFileUpload)
spacerItem10 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.verticalLayout_2.addItem(spacerItem10)
spacerItem11 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.verticalLayout_2.addItem(spacerItem11)
self.uiMiscTabWidget.addTab(self.uiMiscTab, "")
self.verticalLayout.addWidget(self.uiMiscTabWidget)
self.horizontalLayout_6 = QtWidgets.QHBoxLayout()
self.horizontalLayout_6.setObjectName("horizontalLayout_6")
spacerItem11 = QtWidgets.QSpacerItem(324, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_6.addItem(spacerItem11)
spacerItem12 = QtWidgets.QSpacerItem(324, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_6.addItem(spacerItem12)
self.uiRestoreDefaultsPushButton = QtWidgets.QPushButton(parent=GeneralPreferencesPageWidget)
self.uiRestoreDefaultsPushButton.setObjectName("uiRestoreDefaultsPushButton")
self.horizontalLayout_6.addWidget(self.uiRestoreDefaultsPushButton)
@@ -472,6 +511,7 @@ class Ui_GeneralPreferencesPageWidget(object):
self.retranslateUi(GeneralPreferencesPageWidget)
self.uiMiscTabWidget.setCurrentIndex(0)
self.toolBox.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(GeneralPreferencesPageWidget)
GeneralPreferencesPageWidget.setTabOrder(self.uiProjectsPathLineEdit, self.uiProjectsPathToolButton)
GeneralPreferencesPageWidget.setTabOrder(self.uiProjectsPathToolButton, self.uiSymbolsPathLineEdit)
@@ -568,26 +608,31 @@ class Ui_GeneralPreferencesPageWidget(object):
self.uiSPICEConsoleCommandLineEdit.setToolTip(_translate("GeneralPreferencesPageWidget", "<html><head/><body><p>Command line replacements:</p><ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li style=\" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%h or {host} = console IP or hostname</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%p or {port} = console port</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%d or {name} = node name</li><ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%P or {project} = project name</li></ul><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%i or {project_id} = project UUID</li><ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%n or {node_id} = node UUID</li></ul><li style=\" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%c or {url} = server URL</li></ul></body></html>"))
self.uiSPICEConsolePreconfiguredCommandPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Edit"))
self.uiMiscTabWidget.setTabText(self.uiMiscTabWidget.indexOf(self.uiSPICETab), _translate("GeneralPreferencesPageWidget", "SPICE"))
self.uiSceneWidthLabel.setText(_translate("GeneralPreferencesPageWidget", "Default width:"))
self.uiNotePreviewLabel.setText(_translate("GeneralPreferencesPageWidget", "Default note style:"))
self.uiShowInterfaceLabelsOnNewProject.setText(_translate("GeneralPreferencesPageWidget", "Show interface labels on new project"))
self.uiDrawingGridSizeLabel.setText(_translate("GeneralPreferencesPageWidget", "Default drawing grid size:"))
self.uiNodeGridSizeLabel.setText(_translate("GeneralPreferencesPageWidget", "Default node grid size:"))
self.uiSceneHeightLabel.setText(_translate("GeneralPreferencesPageWidget", "Default height:"))
self.uiDefaultNoteStylePlainTextEdit.setPlainText(_translate("GeneralPreferencesPageWidget", "AaBbYyZz"))
self.uiDefaultNoteFontPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default font"))
self.uiDefaultNoteColorPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default color"))
self.uiLimitSizeNodeSymbolCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Limit the size of node symbols"))
self.uiDefaultLabelFontPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default font"))
self.uiDefaultLabelColorPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default color"))
self.uiDefaultLabelStylePlainTextEdit.setPlainText(_translate("GeneralPreferencesPageWidget", "AaBbYyZz"))
self.uiSceneHeightSpinBox.setSuffix(_translate("GeneralPreferencesPageWidget", " pixels"))
self.toolBox.setItemText(self.toolBox.indexOf(self.page), _translate("GeneralPreferencesPageWidget", "Default label style"))
self.uiDefaultNoteStylePlainTextEdit.setPlainText(_translate("GeneralPreferencesPageWidget", "AaBbYyZz"))
self.uiDefaultNoteFontPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default font"))
self.uiDefaultNoteColorPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default color"))
self.toolBox.setItemText(self.toolBox.indexOf(self.page_2), _translate("GeneralPreferencesPageWidget", "Default note style"))
self.uiDefaultLinkColorLabel.setText(_translate("GeneralPreferencesPageWidget", "Color:"))
self.uiDefaultLinkWidthLabel.setText(_translate("GeneralPreferencesPageWidget", "Width:"))
self.uiDefaultLinkWidthSpinBox.setSuffix(_translate("GeneralPreferencesPageWidget", " px"))
self.uiDefaultLinkStyleLabel.setText(_translate("GeneralPreferencesPageWidget", "Style:"))
self.toolBox.setItemText(self.toolBox.indexOf(self.page_3), _translate("GeneralPreferencesPageWidget", "Default link style"))
self.uiSceneWidthSpinBox.setSuffix(_translate("GeneralPreferencesPageWidget", " pixels"))
self.uiSnapToGridOnNewProject.setText(_translate("GeneralPreferencesPageWidget", "Snap to grid on new project"))
self.uiDrawingGridSizeLabel.setText(_translate("GeneralPreferencesPageWidget", "Default drawing grid size:"))
self.uiLabelPreviewLabel.setText(_translate("GeneralPreferencesPageWidget", "Default label style:"))
self.uiNodeGridSizeLabel.setText(_translate("GeneralPreferencesPageWidget", "Default node grid size:"))
self.uiShowGridOnNewProject.setText(_translate("GeneralPreferencesPageWidget", "Show grid on new project"))
self.uiRectangleSelectedItemCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Draw a rectangle when an item is selected"))
self.uiSceneWidthLabel.setText(_translate("GeneralPreferencesPageWidget", "Default width:"))
self.uiDrawLinkStatusPointsCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Draw link status points"))
self.uiShowInterfaceLabelsOnNewProject.setText(_translate("GeneralPreferencesPageWidget", "Show interface labels on new project"))
self.uiShowGridOnNewProject.setText(_translate("GeneralPreferencesPageWidget", "Show grid on new project"))
self.uiSceneHeightSpinBox.setSuffix(_translate("GeneralPreferencesPageWidget", " pixels"))
self.uiRectangleSelectedItemCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Draw a rectangle when an item is selected"))
self.uiLimitSizeNodeSymbolCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Limit the size of node symbols"))
self.uiSnapToGridOnNewProject.setText(_translate("GeneralPreferencesPageWidget", "Snap to grid on new project"))
self.uiMiscTabWidget.setTabText(self.uiMiscTabWidget.indexOf(self.uiSceneTab), _translate("GeneralPreferencesPageWidget", "Topology view"))
self.uiCheckForUpdateCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Automatically check for update"))
self.uiCrashReportCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Send anonymous crash reports"))

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@ def get_icon(filename):
from gns3.main_window import MainWindow
style_name = MainWindow.instance().settings().get("style")
if style_name.startswith("Charcoal"):
if style_name.startswith("Charcoal") or style_name.startswith("Dark"):
style_dir = ":/charcoal_icons/"
elif style_name == "Classic":
style_dir = ":/classic_icons/"

View File

@@ -23,8 +23,8 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
__version__ = "2.2.56.1"
__version_info__ = (2, 2, 56, 0)
__version__ = "2.2.59"
__version_info__ = (2, 2, 59, 0)
if "dev" in __version__:
try:

View File

@@ -1,3 +1,3 @@
-rrequirements.txt
PyQt6==6.10.1
PyQt6==6.10.2

View File

@@ -1,5 +1,7 @@
jsonschema>=4.25.1,<4.26 # version 4.25.1 is the last to support Python 3.9
sentry-sdk>=2.50.0,<3 # optional dependency
psutil>=7.2.1
jsonschema==4.25.1; python_version == '3.9' # version 4.25.1 is the last to support Python 3.9
jsonschema>=4.26.0,<4.27; python_version >= '3.10'
sentry-sdk>=2.59.0,<3 # optional dependency
psutil>=7.2.2
distro>=1.9.0
truststore>=0.10.4; python_version >= '3.10'
QDarkStyle==3.2.3

View File

@@ -13,10 +13,14 @@ QGraphicsView, QTextEdit, QPlainTextEdit, QTreeWidget, QListWidget, QLineEdit, Q
}
QLabel, QMenu, QStatusBar {
color: black;
color: #dedede;
}
QToolBox::tab {
color: #dedede;
font: bold 12px;
}
QMenuBar::item {
background-color: #535353;
}

View File

@@ -79,7 +79,7 @@ setup(
include_package_data=True,
package_data={"gns3": ["configs/*.txt", "schemas/*.json"]},
platforms="any",
python_requires=">=3.8",
python_requires=">=3.9",
setup_requires=["setuptools>=45.2"],
classifiers=[
"Development Status :: 5 - Production/Stable",

View File

@@ -23,8 +23,8 @@ from gns3.qt import QtGui, QtCore
def test_toSvg(project, controller):
ellipse = EllipseItem(width=400, height=100, project=project)
pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
pen.setStyle(QtCore.Qt.DashLine)
pen = QtGui.QPen(QtCore.Qt.GlobalColor.black, 2, QtCore.Qt.PenStyle.SolidLine, QtCore.Qt.PenCapStyle.RoundCap, QtCore.Qt.PenJoinStyle.RoundJoin)
pen.setStyle(QtCore.Qt.PenStyle.DashLine)
ellipse.setPen(pen)
svg = ET.fromstring(ellipse.toSvg())
assert float(svg.get("width")) == 400.0

View File

@@ -32,7 +32,7 @@ def test_dump():
font.setUnderline(True)
font.setStrikeOut(True)
label.setFont(font)
label.setDefaultTextColor(QtCore.Qt.red)
label.setDefaultTextColor(QtCore.Qt.GlobalColor.red)
assert label.dump() == {
"text": "Test",
@@ -54,7 +54,7 @@ def test_setStyle():
font.setUnderline(True)
font.setStrikeOut(False)
label.setFont(font)
label.setDefaultTextColor(QtCore.Qt.red)
label.setDefaultTextColor(QtCore.Qt.GlobalColor.red)
style = label.dump()["style"]
label2 = LabelItem()
@@ -65,4 +65,4 @@ def test_setStyle():
assert label2.font().bold()
assert label2.font().strikeOut() is False
assert label2.font().underline()
assert label2.defaultTextColor() == QtCore.Qt.red
assert label2.defaultTextColor() == QtCore.Qt.GlobalColor.red

View File

@@ -23,8 +23,8 @@ from gns3.qt import QtGui, QtCore
def test_toSvg(project, controller):
line = LineItem(dst=QtCore.QPointF(400, 280), project=project)
pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
pen.setStyle(QtCore.Qt.DashLine)
pen = QtGui.QPen(QtCore.Qt.GlobalColor.black, 2, QtCore.Qt.PenStyle.SolidLine, QtCore.Qt.PenCapStyle.RoundCap, QtCore.Qt.PenJoinStyle.RoundJoin)
pen.setStyle(QtCore.Qt.PenStyle.DashLine)
line.setPen(pen)
svg = ET.fromstring(line.toSvg())
assert float(svg.get("width")) == 400.0
@@ -42,8 +42,8 @@ def test_toSvg(project, controller):
def test_toSvg_negative_y(project, controller):
line = LineItem(dst=QtCore.QPointF(400, -280), project=project)
pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
pen.setStyle(QtCore.Qt.DashLine)
pen = QtGui.QPen(QtCore.Qt.GlobalColor.black, 2, QtCore.Qt.PenStyle.SolidLine, QtCore.Qt.PenCapStyle.RoundCap, QtCore.Qt.PenJoinStyle.RoundJoin)
pen.setStyle(QtCore.Qt.PenStyle.DashLine)
line.setPen(pen)
svg = ET.fromstring(line.toSvg())
assert float(svg.get("width")) == 400.0
@@ -69,7 +69,7 @@ def test_fromSvg(project, controller):
assert line.line().x2() == 250
assert line.line().y2() == 150
assert hex(line.pen().color().rgba()) == "0xff0000ff"
assert line.pen().style() == QtCore.Qt.DashDotLine
assert line.pen().style() == QtCore.Qt.PenStyle.DashDotLine
assert line.pos().x() == 50
assert line.pos().y() == 84
@@ -84,7 +84,7 @@ def test_fromSvg_top_direction(project, controller):
assert line.line().x2() == 250
assert line.line().y2() == 0
assert hex(line.pen().color().rgba()) == "0xff0000ff"
assert line.pen().style() == QtCore.Qt.DashDotLine
assert line.pen().style() == QtCore.Qt.PenStyle.DashDotLine
assert line.pos().x() == 50
assert line.pos().y() == 84
@@ -92,7 +92,7 @@ def test_fromSvg_top_direction(project, controller):
def test_fromSvg_solid_stroke(project, controller):
line = LineItem(project=project)
line.fromSvg('<svg height="150" width="250"><line x1="0" y1="0" x2="250" y2="150" stroke-width="5" stroke="#0000ff" /></svg>')
assert line.pen().style() == QtCore.Qt.SolidLine
assert line.pen().style() == QtCore.Qt.PenStyle.SolidLine
def test_fromEmptySvg(project, controller):

View File

@@ -23,8 +23,8 @@ from gns3.qt import QtGui, QtCore
def test_toSvg(project, controller):
rect = RectangleItem(width=400, height=280, project=project)
pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
pen.setStyle(QtCore.Qt.DashLine)
pen = QtGui.QPen(QtCore.Qt.GlobalColor.black, 2, QtCore.Qt.PenStyle.SolidLine, QtCore.Qt.PenCapStyle.RoundCap, QtCore.Qt.PenJoinStyle.RoundJoin)
pen.setStyle(QtCore.Qt.PenStyle.DashLine)
rect.setPen(pen)
svg = ET.fromstring(rect.toSvg())
assert float(svg.get("width")) == 400.0
@@ -49,13 +49,13 @@ def test_fromSvg(project, controller):
assert rect.pen().width() == 5
assert hex(rect.pen().color().rgba()) == "0xff0000ff"
assert hex(rect.brush().color().rgba()) == "0x80ff00ff"
assert rect.pen().style() == QtCore.Qt.DashDotLine
assert rect.pen().style() == QtCore.Qt.PenStyle.DashDotLine
def test_fromSvg_solid_stroke(project, controller):
rect = RectangleItem(project=project)
rect.fromSvg('<svg height="150" width="250"><rect height="150" stroke-width="5" stroke="#0000ff" fill="#ff00ff" width="150" /></svg>')
assert rect.pen().style() == QtCore.Qt.SolidLine
assert rect.pen().style() == QtCore.Qt.PenStyle.SolidLine
def test_fromEmptySvg(project, controller):

View File

@@ -46,7 +46,7 @@ def test_fromSvg(project, controller):
font.setItalic(True)
font.setStrikeOut(True)
text.setFont(font)
text.setDefaultTextColor(QtCore.Qt.red)
text.setDefaultTextColor(QtCore.Qt.GlobalColor.red)
text.setPlainText("Hello")
text2 = TextItem(project=project)

View File

@@ -35,7 +35,7 @@ def network_manager(response):
def response():
response = unittest.mock.MagicMock()
type(response).finished = unittest.mock.PropertyMock(return_value=FakeQtSignal())
response.error.return_value = QtNetwork.QNetworkReply.NoError
response.error.return_value = QtNetwork.QNetworkReply.NetworkError.NoError
response.attribute.return_value = 200
response.header.return_value = "application/json"
return response
@@ -66,7 +66,6 @@ def test_get_connected(http_client, http_request, network_manager, response):
http_client.createHTTPQuery("GET", "/test", callback)
http_request.assert_called_with(QtCore.QUrl("http://127.0.0.1:3080/v2/test"))
http_request.setRawHeader.assert_any_call(b"Content-Type", b"application/json")
http_request.setRawHeader.assert_any_call(b"User-Agent", "GNS3 QT Client v{version}".format(version=__version__).encode())
assert network_manager.sendCustomRequest.called
args, kwargs = network_manager.sendCustomRequest.call_args
@@ -80,7 +79,7 @@ def test_get_connected(http_client, http_request, network_manager, response):
def test_paramsToQueryString(http_client):
assert http_client._paramsToQueryString({}) == ""
assert http_client._paramsToQueryString(None) == ""
res = http_client._paramsToQueryString({"a": 1, "b": 2})
assert res == "?a=1&b=2" or res == "?b=2&a=1"
res = http_client._paramsToQueryString({"a": 1, "b": 2, "c": None})
@@ -96,7 +95,6 @@ def test_get_connected_auth(http_client, http_request, network_manager, response
http_client.createHTTPQuery("GET", "/test", callback)
http_request.assert_called_with(QtCore.QUrl("http://gns3@127.0.0.1:3080/v2/test"))
http_request.setRawHeader.assert_any_call(b"Content-Type", b"application/json")
http_request.setRawHeader.assert_any_call(b"Authorization", b"Basic Z25zMzozc25n")
http_request.setRawHeader.assert_any_call(b"User-Agent", "GNS3 QT Client v{version}".format(version=__version__).encode())
assert network_manager.sendCustomRequest.called
@@ -155,7 +153,7 @@ def test_post_not_connected_connection_failed(http_client, http_request, network
# Trigger the completion of /version
response.finished.emit()
response.error.emit(QtNetwork.QNetworkReply.ConnectionRefusedError)
response.error.emit(QtNetwork.QNetworkReply.NetworkError.ConnectionRefusedError)
assert callback.called
@@ -178,7 +176,7 @@ def test_post_not_connected_connection_failed_retry(http_client, http_request, n
# Trigger the completion of /version
response.finished.emit()
response.error.emit(QtNetwork.QNetworkReply.ConnectionRefusedError)
response.error.emit(QtNetwork.QNetworkReply.NetworkError.ConnectionRefusedError)
assert http_client._retryConnection.called
assert not callback.called
@@ -206,7 +204,7 @@ def test_readyReadySlot(http_client):
response = unittest.mock.MagicMock()
server = unittest.mock.MagicMock()
response.header.return_value = "application/json"
response.error.return_value = QtNetwork.QNetworkReply.NoError
response.error.return_value = QtNetwork.QNetworkReply.NetworkError.NoError
response.attribute.return_value = 200
response.readAll.return_value = b'{"action": "ping"}'
@@ -224,7 +222,7 @@ def test_readyReadySlotHTTPError(http_client):
response = unittest.mock.MagicMock()
server = unittest.mock.MagicMock()
response.header.return_value = "application/json"
response.error.return_value = QtNetwork.QNetworkReply.NoError
response.error.return_value = QtNetwork.QNetworkReply.NetworkError.NoError
response.attribute.return_value = 404
http_client._readyReadySlot(response, callback, {"query_id": "bla"}, server)
@@ -238,7 +236,7 @@ def test_readyReadySlotConnectionRefusedError(http_client):
response = unittest.mock.MagicMock()
server = unittest.mock.MagicMock()
response.header.return_value = "application/json"
response.error.return_value = QtNetwork.QNetworkReply.ConnectionRefusedError
response.error.return_value = QtNetwork.QNetworkReply.NetworkError.ConnectionRefusedError
response.attribute.return_value = 200
http_client._readyReadySlot(response, callback, {"query_id": "bla"}, server)
@@ -255,7 +253,7 @@ def test_readyReadySlotPartialJSON(http_client):
server = unittest.mock.MagicMock()
response.header.return_value = "application/json"
response.readAll.return_value = b'{"action": "ping"'
response.error.return_value = QtNetwork.QNetworkReply.NoError
response.error.return_value = QtNetwork.QNetworkReply.NetworkError.NoError
response.attribute.return_value = 200
http_client._readyReadySlot(response, callback, {"query_id": "bla"}, server)
@@ -276,7 +274,7 @@ def test_readyReadySlotPartialBytes(http_client):
server = unittest.mock.MagicMock()
response.header.return_value = "application/octet-stream"
response.readAll.return_value = b'hello'
response.error.return_value = QtNetwork.QNetworkReply.NoError
response.error.return_value = QtNetwork.QNetworkReply.NetworkError.NoError
response.attribute.return_value = 200
http_client._readyReadySlot(response, callback, {"query_id": "bla"}, server)

View File

@@ -65,7 +65,7 @@ def link(devices, controller, project):
{"node_id": devices[0].node_id(), "adapter_number": 0, "port_number": 0},
{"node_id": devices[1].node_id(), "adapter_number": 0, "port_number": 0}
],
"link_style": {},
"link_style": {'color': '#000000', 'width': 2, 'type': 1},
"filters": {},
}
@@ -90,7 +90,7 @@ def test_create_link(devices, project, controller):
{"node_id": devices[0].node_id(), "adapter_number": 0, "port_number": 0},
{"node_id": devices[1].node_id(), "adapter_number": 0, "port_number": 0},
],
"link_style": {},
"link_style": {'color': '#000000', 'width': 2, 'type': 1},
"filters": {},
}

View File

@@ -1,4 +1,4 @@
-rrequirements.txt
PyQt6==6.10.1 # pyup: ignore
PyQt6==6.10.2 # pyup: ignore
pywin32==311 # pyup: ignore