Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
pt2d
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
QIM
Tools
pt2d
Commits
7f9fa646
Commit
7f9fa646
authored
6 months ago
by
s224389
Browse files
Options
Downloads
Patches
Plain Diff
Added ability to drag points around. Also implemented point class for better tracking.
parent
b69dbdf7
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
GUI_draft.py
+120
-42
120 additions, 42 deletions
GUI_draft.py
with
120 additions
and
42 deletions
GUI_draft.py
+
120
−
42
View file @
7f9fa646
...
...
@@ -8,6 +8,42 @@ from PyQt5.QtWidgets import (
)
from
PyQt5.QtGui
import
QPixmap
,
QPen
,
QBrush
from
PyQt5.QtCore
import
Qt
,
QRectF
import
math
class
PointItem
(
QGraphicsEllipseItem
):
"""
Represents a single draggable point on the scene.
"""
def
__init__
(
self
,
x
,
y
,
radius
=
4
,
parent
=
None
):
super
().
__init__
(
x
-
radius
,
y
-
radius
,
2
*
radius
,
2
*
radius
,
parent
)
self
.
_x
=
x
self
.
_y
=
y
self
.
_r
=
radius
self
.
setBrush
(
QBrush
(
Qt
.
red
))
self
.
setPen
(
QPen
(
Qt
.
red
))
def
get_pos
(
self
):
"""
Return the (x, y) of this point in scene coords.
"""
return
(
self
.
_x
,
self
.
_y
)
def
set_pos
(
self
,
x
,
y
):
"""
Move point to (x,y).
This also updates the ellipse rectangle so the visual dot moves.
"""
self
.
_x
=
x
self
.
_y
=
y
self
.
setRect
(
x
-
self
.
_r
,
y
-
self
.
_r
,
2
*
self
.
_r
,
2
*
self
.
_r
)
def
distance_to
(
self
,
x_other
,
y_other
):
"""
Euclidean distance from this point to arbitrary (x_other, y_other).
"""
dx
=
self
.
_x
-
x_other
dy
=
self
.
_y
-
y_other
return
math
.
sqrt
(
dx
*
dx
+
dy
*
dy
)
class
ImageGraphicsView
(
QGraphicsView
):
...
...
@@ -30,7 +66,6 @@ class ImageGraphicsView(QGraphicsView):
# Points and dot items
self
.
points
=
[]
self
.
point_items
=
[]
self
.
editor_mode
=
False
self
.
dot_radius
=
4
...
...
@@ -43,6 +78,8 @@ class ImageGraphicsView(QGraphicsView):
self
.
_press_view_pos
=
None
self
.
_drag_threshold
=
5
self
.
_was_dragging
=
False
self
.
_dragging_idx
=
None
self
.
_drag_offset
=
(
0
,
0
)
def
load_image
(
self
,
image_path
):
"""
Load an image and fit it in the view.
"""
...
...
@@ -56,7 +93,6 @@ class ImageGraphicsView(QGraphicsView):
# Clear existing dots from previous image
self
.
points
.
clear
()
self
.
_clear_point_items
()
# Reset transform then fit image in view
self
.
resetTransform
()
...
...
@@ -72,7 +108,28 @@ class ImageGraphicsView(QGraphicsView):
self
.
_was_dragging
=
False
self
.
_press_view_pos
=
event
.
pos
()
# Switch to closed-hand cursor while left mouse is down
if
self
.
editor_mode
:
# Check if we're near a point
idx
=
self
.
_find_point_near
(
event
.
pos
(),
threshold
=
10
)
if
idx
is
not
None
:
# Start dragging that point
self
.
_dragging_idx
=
idx
# Compute offset so point doesn't jump if clicked off-center
scene_pos
=
self
.
mapToScene
(
event
.
pos
())
px
,
py
=
self
.
points
[
idx
].
get_pos
()
self
.
_drag_offset
=
(
scene_pos
.
x
()
-
px
,
scene_pos
.
y
()
-
py
)
# Temporarily disable QGraphicsView's panning
self
.
setDragMode
(
QGraphicsView
.
NoDrag
)
self
.
viewport
().
setCursor
(
Qt
.
ClosedHandCursor
)
return
else
:
# Not near a point, so we do normal panning
self
.
setDragMode
(
QGraphicsView
.
ScrollHandDrag
)
self
.
viewport
().
setCursor
(
Qt
.
ClosedHandCursor
)
else
:
# Editor mode is off => always do normal panning
self
.
setDragMode
(
QGraphicsView
.
ScrollHandDrag
)
self
.
viewport
().
setCursor
(
Qt
.
ClosedHandCursor
)
elif
event
.
button
()
==
Qt
.
RightButton
:
...
...
@@ -82,41 +139,46 @@ class ImageGraphicsView(QGraphicsView):
super
().
mousePressEvent
(
event
)
def
mouseMoveEvent
(
self
,
event
):
"""
If movement > _drag_threshold: consider it a drag.
The actual panning is handled by QGraphicsView in ScrollHandDrag mode.
"""
# Check if the mouse is being dragged
if
self
.
_dragging_idx
is
not
None
:
# Move that point to new coords
scene_pos
=
self
.
mapToScene
(
event
.
pos
())
x_new
=
scene_pos
.
x
()
-
self
.
_drag_offset
[
0
]
y_new
=
scene_pos
.
y
()
-
self
.
_drag_offset
[
1
]
self
.
points
[
self
.
_dragging_idx
].
set_pos
(
x_new
,
y_new
)
return
# Skip QGraphicsView's panning logic
else
:
# Old logic: if movement > threshold -> set _was_dragging = True
if
self
.
_mouse_pressed
and
(
event
.
buttons
()
&
Qt
.
LeftButton
):
# If the mouse moved more than the threshold, consider it a drag
dist
=
(
event
.
pos
()
-
self
.
_press_view_pos
).
manhattanLength
()
if
dist
>
self
.
_drag_threshold
:
self
.
_was_dragging
=
True
super
().
mouseMoveEvent
(
event
)
def
mouseReleaseEvent
(
self
,
event
):
"""
After releasing the left button, go back to arrow cursor.
If it wasn
'
t a drag, treat as a click (Editor Mode: add dot).
"""
# Let QGraphicsView handle release first
super
().
mouseReleaseEvent
(
event
)
if
event
.
button
()
==
Qt
.
LeftButton
and
self
.
_mouse_pressed
:
self
.
_mouse_pressed
=
False
# Always go back to arrow cursor AFTER letting QGraphicsView handle release
self
.
viewport
().
setCursor
(
Qt
.
ArrowCursor
)
if
not
self
.
_was_dragging
:
# It's a click: if editor mode is ON add a dot
if
self
.
editor_mode
:
# If we were dragging a point, stop.
if
self
.
_dragging_idx
is
not
None
:
self
.
_dragging_idx
=
None
self
.
_drag_offset
=
(
0
,
0
)
self
.
setDragMode
(
QGraphicsView
.
ScrollHandDrag
)
else
:
# We were NOT dragging a point => check if it was a click to add a new point
if
not
self
.
_was_dragging
and
self
.
editor_mode
:
self
.
_add_point
(
event
.
pos
())
self
.
_was_dragging
=
False
def
wheelEvent
(
self
,
event
):
"""
Mouse wheel = zoom.
"""
zoom_in_factor
=
1.25
...
...
@@ -136,9 +198,8 @@ class ImageGraphicsView(QGraphicsView):
scene_pos
=
self
.
mapToScene
(
view_pos
)
x
,
y
=
scene_pos
.
x
(),
scene_pos
.
y
()
self
.
points
.
append
((
x
,
y
))
dot
=
self
.
_create_dot_item
(
x
,
y
)
self
.
point_items
.
append
(
dot
)
dot
=
PointItem
(
x
,
y
,
radius
=
self
.
dot_radius
)
self
.
points
.
append
(
dot
)
self
.
scene
.
addItem
(
dot
)
def
_remove_point
(
self
,
view_pos
):
...
...
@@ -146,22 +207,19 @@ class ImageGraphicsView(QGraphicsView):
scene_pos
=
self
.
mapToScene
(
view_pos
)
x_click
,
y_click
=
scene_pos
.
x
(),
scene_pos
.
y
()
# Define threshold for removing a point
threshold
=
10
closest_idx
=
None
min_dist
=
float
(
'
inf
'
)
# Find the closest point to the click
for
i
,
(
x
,
y
)
in
enumerate
(
self
.
points
):
dist_sq
=
(
x
-
x_click
)
**
2
+
(
y
-
y_click
)
**
2
if
dist_sq
<
min_dist
:
min_dist
=
dist_sq
for
i
,
point_item
in
enumerate
(
self
.
points
):
dist
=
point_item
.
distance_to
(
x_click
,
y_click
)
if
dist
<
min_dist
:
min_dist
=
dist
closest_idx
=
i
# Remove the closest point if it's within the threshold
if
closest_idx
is
not
None
and
min_dist
<=
threshold
**
2
:
self
.
scene
.
removeItem
(
self
.
point_items
[
closest_idx
])
del
self
.
point_items
[
closest_idx
]
# Remove if within threshold
if
closest_idx
is
not
None
and
min_dist
<=
threshold
:
self
.
scene
.
removeItem
(
self
.
points
[
closest_idx
])
del
self
.
points
[
closest_idx
]
def
_create_dot_item
(
self
,
x
,
y
):
...
...
@@ -174,9 +232,29 @@ class ImageGraphicsView(QGraphicsView):
def
_clear_point_items
(
self
):
"""
Remove all dot items from the scene.
"""
for
item
in
self
.
point_items
:
self
.
scene
.
removeItem
(
item
)
self
.
point_items
=
[]
for
p
in
self
.
points
:
self
.
scene
.
removeItem
(
p
)
self
.
points
.
clear
()
def
_find_point_near
(
self
,
view_pos
,
threshold
=
10
):
scene_pos
=
self
.
mapToScene
(
view_pos
)
x_click
,
y_click
=
scene_pos
.
x
(),
scene_pos
.
y
()
closest_idx
=
None
min_dist
=
float
(
'
inf
'
)
for
i
,
p
in
enumerate
(
self
.
points
):
dist
=
p
.
distance_to
(
x_click
,
y_click
)
if
dist
<
min_dist
:
min_dist
=
dist
closest_idx
=
i
if
closest_idx
is
not
None
and
min_dist
<=
threshold
:
return
closest_idx
return
None
class
MainWindow
(
QMainWindow
):
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment