@@ -42,13 +42,18 @@ DriveWidget::DriveWidget( QWidget* parent )
42
42
: QWidget( parent )
43
43
, linear_velocity_( 0 )
44
44
, angular_velocity_( 0 )
45
- , linear_max_ ( 10 )
46
- , angular_max_ ( 2 )
45
+ , linear_scale_ ( 10 )
46
+ , angular_scale_ ( 2 )
47
47
{
48
48
}
49
49
50
+ // This paintEvent() is complex because of the drawing of the two
51
+ // wheel arc-arrows.
50
52
void DriveWidget::paintEvent ( QPaintEvent* event )
51
53
{
54
+ // The background color and crosshair lines are drawn differently
55
+ // depending on whether this widget is enabled or not. This gives a
56
+ // nice visual indication of whether the control is "live".
52
57
QColor background;
53
58
QColor crosshair;
54
59
if ( isEnabled () )
@@ -61,6 +66,10 @@ void DriveWidget::paintEvent( QPaintEvent* event )
61
66
background = Qt::lightGray;
62
67
crosshair = Qt::darkGray;
63
68
}
69
+
70
+ // The main visual is a square, centered in the widget's area. Here
71
+ // we compute the size of the square and the horizontal and vertical
72
+ // offsets of it.
64
73
int w = width ();
65
74
int h = height ();
66
75
int size = (( w > h ) ? h : w) - 1 ;
@@ -70,10 +79,17 @@ void DriveWidget::paintEvent( QPaintEvent* event )
70
79
QPainter painter ( this );
71
80
painter.setBrush ( background );
72
81
painter.setPen ( crosshair );
82
+
83
+ // Draw the background square.
73
84
painter.drawRect ( QRect ( hpad, vpad, size, size ));
85
+
86
+ // Draw a cross-hair inside the square.
74
87
painter.drawLine ( hpad, height () / 2 , hpad + size, height () / 2 );
75
88
painter.drawLine ( width () / 2 , vpad, width () / 2 , vpad + size );
76
89
90
+ // If the widget is enabled and the velocities are not zero, draw
91
+ // some sweet green arrows showing possible paths that the wheels of
92
+ // a diff-drive robot would take if it stayed at these velocities.
77
93
if ( isEnabled () && (angular_velocity_ != 0 || linear_velocity_ != 0 ))
78
94
{
79
95
QPen arrow;
@@ -83,6 +99,10 @@ void DriveWidget::paintEvent( QPaintEvent* event )
83
99
arrow.setJoinStyle ( Qt::RoundJoin );
84
100
painter.setPen ( arrow );
85
101
102
+ // This code steps along a central arc defined by the linear and
103
+ // angular velocites. At each step, it computes where the left
104
+ // and right wheels would be and collecting the resulting points
105
+ // in the left_track and right_track arrays.
86
106
int step_count = 100 ;
87
107
QPointF left_track[ step_count ];
88
108
QPointF right_track[ step_count ];
@@ -97,7 +117,7 @@ void DriveWidget::paintEvent( QPaintEvent* event )
97
117
right_track[ 0 ].setY ( cy );
98
118
float angle = M_PI/2 ;
99
119
float delta_angle = angular_velocity_ / step_count;
100
- float step_dist = linear_velocity_ * size/2 / linear_max_ / step_count;
120
+ float step_dist = linear_velocity_ * size/2 / linear_scale_ / step_count;
101
121
for ( int step = 1 ; step < step_count; step++ )
102
122
{
103
123
angle += delta_angle / 2 ;
@@ -113,15 +133,23 @@ void DriveWidget::paintEvent( QPaintEvent* event )
113
133
cx = next_cx;
114
134
cy = next_cy;
115
135
}
136
+ // Now the track arrays are filled, so stroke each with a fat green line.
116
137
painter.drawPolyline ( left_track, step_count );
117
138
painter.drawPolyline ( right_track, step_count );
118
139
140
+ // Here we decide which direction each arrowhead will point
141
+ // (forward or backward). This works by comparing the arc length
142
+ // travelled by the center in one step (step_dist) with the arc
143
+ // length travelled by the wheel (half_track_width * delta_angle).
119
144
int left_arrow_dir = (-step_dist + half_track_width * delta_angle > 0 );
120
145
int right_arrow_dir = (-step_dist - half_track_width * delta_angle > 0 );
121
146
147
+ // Use MiterJoin for the arrowheads so we get a nice sharp point.
122
148
arrow.setJoinStyle ( Qt::MiterJoin );
123
149
painter.setPen ( arrow );
124
150
151
+ // Compute and draw polylines for each arrowhead. This code could
152
+ // probably be more elegant.
125
153
float head_len = size / 8.0 ;
126
154
QPointF arrow_head[ 3 ];
127
155
float x, y;
@@ -152,6 +180,9 @@ void DriveWidget::paintEvent( QPaintEvent* event )
152
180
}
153
181
}
154
182
183
+ // Every mouse move event received here sends a velocity because Qt
184
+ // only sends us mouse move events if there was previously a
185
+ // mouse-down event while in the widget.
155
186
void DriveWidget::mouseMoveEvent ( QMouseEvent* event )
156
187
{
157
188
sendVelocitiesFromMouse ( event->x (), event->y (), width (), height () );
@@ -162,19 +193,27 @@ void DriveWidget::mousePressEvent( QMouseEvent* event )
162
193
sendVelocitiesFromMouse ( event->x (), event->y (), width (), height () );
163
194
}
164
195
196
+ // When the mouse leaves the widget but the button is still held down,
197
+ // we don't get the leaveEvent(). However, when the mouse drags out
198
+ // of the widget and then other buttons are pressed (or possibly other
199
+ // window-manager things happen), we will get a leaveEvent() but not a
200
+ // mouseReleaseEvent(). Without this you can have a robot stuck "on"
201
+ // without the user controlling it.
165
202
void DriveWidget::leaveEvent ( QEvent* event )
166
203
{
167
204
stop ();
168
205
}
169
206
207
+ // Compute and emit linear and angular velocities based on Y and X
208
+ // mouse positions relative to the central square.
170
209
void DriveWidget::sendVelocitiesFromMouse ( int x, int y, int width, int height )
171
210
{
172
211
int size = (( width > height ) ? height : width );
173
212
int hpad = ( width - size ) / 2 ;
174
213
int vpad = ( height - size ) / 2 ;
175
214
176
- linear_velocity_ = (1.0 - float ( y - vpad ) / float ( size / 2 )) * linear_max_ ;
177
- angular_velocity_ = (1.0 - float ( x - hpad ) / float ( size / 2 )) * angular_max_ ;
215
+ linear_velocity_ = (1.0 - float ( y - vpad ) / float ( size / 2 )) * linear_scale_ ;
216
+ angular_velocity_ = (1.0 - float ( x - hpad ) / float ( size / 2 )) * angular_scale_ ;
178
217
update ();
179
218
Q_EMIT outputVelocity ( linear_velocity_, angular_velocity_ );
180
219
}
0 commit comments