Skip to content

Commit acf13aa

Browse files
onMapToolChanged can cause null pointer deref
1 parent e310ec3 commit acf13aa

File tree

2 files changed

+33
-58
lines changed

2 files changed

+33
-58
lines changed

ai_tracer.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ class ShiftClickState(Enum):
4848
# polygon geometries. It handles the drawing of rubber bands on the
4949
# map canvas and the capturing of clicks to build the geometry.
5050
class AIVectorizerTool(QgsMapToolCapture):
51-
def __init__(self, plugin, current_mode):
51+
def __init__(self, plugin):
5252
# Extend QgsMapToolCapture
5353
cadDockWidget = plugin.iface.cadDockWidget()
54-
super(AIVectorizerTool, self).__init__(plugin.iface.mapCanvas(), cadDockWidget, current_mode)
54+
super(AIVectorizerTool, self).__init__(plugin.iface.mapCanvas(), cadDockWidget, QgsMapToolCapture.CaptureNone)
5555

5656
self.plugin = plugin
57-
self.rb = self.initRubberBand(current_mode)
57+
self.rb = self.initRubberBand()
5858

5959
# Options
6060
self.num_completions = 50
@@ -84,13 +84,14 @@ def __init__(self, plugin, current_mode):
8484
self.snapIndicator = QgsSnapIndicator(plugin.iface.mapCanvas())
8585
self.snapper = plugin.iface.mapCanvas().snappingUtils()
8686

87-
def initRubberBand(self, mode):
88-
if mode == QgsMapToolCapture.CaptureLine:
87+
def initRubberBand(self):
88+
if self.mode() == QgsMapToolCapture.CaptureLine:
8989
rb = QgsRubberBand(self.plugin.iface.mapCanvas(), QgsWkbTypes.LineGeometry)
90-
elif mode == QgsMapToolCapture.CapturePolygon:
90+
elif self.mode() == QgsMapToolCapture.CapturePolygon:
9191
rb = QgsRubberBand(self.plugin.iface.mapCanvas(), QgsWkbTypes.PolygonGeometry)
9292
else:
93-
raise ValueError
93+
# TODO not sure how we could get here
94+
rb = QgsRubberBand(self.plugin.iface.mapCanvas(), QgsWkbTypes.PolygonGeometry)
9495

9596
rb.setFillColor(self.digitizingFillColor())
9697
rb.setStrokeColor(self.digitizingStrokeColor())
@@ -223,17 +224,24 @@ def canvasReleaseEvent(self, e):
223224
finally:
224225
self.autocomplete_task = None
225226

226-
# Right click means we end
227-
if e.button() == Qt.RightButton:
228-
vlayer = self.plugin.iface.activeLayer()
229-
if not isinstance(vlayer, QgsVectorLayer):
230-
self.plugin.iface.messageBar().pushMessage(
231-
"Bunting Labs AI Vectorizer",
232-
"No active vector layer.",
233-
Qgis.Warning,
234-
duration=15)
235-
return
227+
vlayer = self.plugin.iface.activeLayer()
228+
if not isinstance(vlayer, QgsVectorLayer):
229+
self.plugin.iface.messageBar().pushMessage(
230+
"Bunting Labs AI Vectorizer",
231+
"No active vector layer.",
232+
Qgis.Warning,
233+
duration=15)
234+
return
235+
elif vlayer.wkbType() not in [QgsWkbTypes.LineString, QgsWkbTypes.MultiLineString,
236+
QgsWkbTypes.Polygon, QgsWkbTypes.MultiPolygon]:
237+
self.plugin.iface.messageBar().pushMessage(
238+
"Bunting Labs AI Vectorizer",
239+
"Unsupported vector layer type for AI autocomplete.",
240+
Qgis.Warning,
241+
duration=15)
242+
return
236243

244+
if e.button() == Qt.RightButton:
237245
# Will be converted to the relevant geometry
238246
curve = self.captureCurve()
239247

@@ -287,7 +295,6 @@ def canvasReleaseEvent(self, e):
287295
if len(self.vertices) >= 2 and not (e.modifiers() & Qt.ShiftModifier) and not wasDoubleClick:
288296
root = QgsProject.instance().layerTreeRoot()
289297
rlayers = [node.layer() for node in root.children() if isinstance(node, QgsLayerTreeLayer) and isinstance(node.layer(), QgsRasterLayer) and node.itemVisibilityChecked()]
290-
vlayer = self.plugin.iface.activeLayer()
291298

292299
project_crs = QgsProject.instance().crs()
293300

@@ -308,4 +315,5 @@ def canvasReleaseEvent(self, e):
308315
def deactivate(self):
309316
self.rb.reset()
310317

311-
self.scissors_icon.hide()
318+
self.scissors_icon.hide()
319+
self.plugin.action.setChecked(False)

plugin.py

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ def __init__(self, iface):
4242
# Set by the text box in save settings
4343
self.api_key_input = None
4444

45-
self.action = None
46-
self.tracer = None
45+
icon_path = os.path.join(os.path.dirname(__file__), "vectorizing_icon.png")
46+
self.action = QAction(QIcon(icon_path), '<b>Vectorize with AI</b><p>Toggle editing on a vector layer then enable to autocomplete new geometries.</p>', None)
47+
self.tracer = AIVectorizerTool(self)
4748

4849
# Read the plugin version
4950
try:
@@ -58,9 +59,6 @@ def __init__(self, iface):
5859
self.settings_action = None
5960

6061
def update_checkable(self):
61-
if self.action is None:
62-
return
63-
6462
# Before onboarding, it's always checkable.
6563
if self.settings.value(SETTING_TOS, "") != "y" or self.settings.value(SETTING_API_TOKEN, "") == "":
6664
self.action.setEnabled(True)
@@ -82,35 +80,16 @@ def current_layer_change_event(self, layer):
8280

8381
self.update_checkable()
8482

85-
def initTracer(self):
86-
self.tracer = None
87-
88-
# see what type of layer we are vectorizing
89-
vlayer = self.iface.activeLayer()
90-
if isinstance(vlayer, QgsVectorLayer):
91-
if vlayer.wkbType() in [QgsWkbTypes.LineString, QgsWkbTypes.MultiLineString]:
92-
self.tracer = AIVectorizerTool(self, QgsMapToolCapture.CaptureLine)
93-
elif vlayer.wkbType() in [QgsWkbTypes.Polygon, QgsWkbTypes.MultiPolygon]:
94-
self.tracer = AIVectorizerTool(self, QgsMapToolCapture.CapturePolygon)
95-
else:
96-
self.iface.messageBar().pushMessage(
97-
"Bunting Labs AI Vectorizer",
98-
"Unsupported vector layer type for AI autocomplete.",
99-
Qgis.Warning,
100-
duration=15)
101-
10283
def initGui(self):
10384
# Initialize the plugin GUI
104-
icon_path = os.path.join(os.path.dirname(__file__), "vectorizing_icon.png")
10585

106-
self.action = QAction(QIcon(icon_path), '<b>Vectorize with AI</b><p>Toggle editing on a vector layer then enable to autocomplete new geometries.</p>', self.iface.mainWindow())
86+
# Because we don't pass iface.mainWindow() to the QAction constructor in __init__
87+
self.iface.mainWindow().addAction(self.action)
88+
10789
self.action.setCheckable(True)
10890
self.action.triggered.connect(self.toolbarClick)
10991
self.iface.addToolBarIcon(self.action)
11092

111-
# Uncheck ourselves if they change tools
112-
self.iface.mapCanvas().mapToolSet.connect(self.onMapToolChanged)
113-
11493
# Trigger a current layer change event to get the right action
11594
self.current_layer_change_event(self.iface.activeLayer())
11695

@@ -508,17 +487,6 @@ def unload(self):
508487

509488
self.iface.removeToolBarIcon(self.action)
510489

511-
def onMapToolChanged(self, newTool, _):
512-
if not isinstance(newTool, AIVectorizerTool):
513-
self.deactivate()
514-
515-
def deactivate(self):
516-
if isinstance(self.tracer, AIVectorizerTool):
517-
self.tracer.deactivate()
518-
519-
self.action.setChecked(False)
520-
self.tracer = None
521-
522490
def toolbarClick(self):
523491
if self.action.isChecked():
524492
# Depending on how many settings someone has already set,
@@ -532,7 +500,6 @@ def toolbarClick(self):
532500
self.openEmailDialog()
533501
return
534502

535-
self.initTracer()
536503
self.iface.mapCanvas().setMapTool(self.tracer)
537504

538505
self.action.setChecked(True)

0 commit comments

Comments
 (0)