Skip to content

Commit b387784

Browse files
committed
Update UI styling and labels
1 parent af17c26 commit b387784

File tree

1 file changed

+184
-53
lines changed
  • coffee_ws/src/python_dynamixel_ui/python_dynamixel_ui

1 file changed

+184
-53
lines changed

coffee_ws/src/python_dynamixel_ui/python_dynamixel_ui/ui.py

Lines changed: 184 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -41,42 +41,136 @@ def __init__(self, motor_id, motor_name="Motor", default_angle=0, parent=None):
4141
self.angle = default_angle # Angle in degrees
4242
self.position = int(default_angle * POSITIONS_PER_DEGREE) # Position in Dynamixel units
4343
self.radius = 100
44-
self.circle_center = QPoint(self.radius + 20, self.radius + 20)
44+
self.circle_center = QPoint(self.radius + 10, self.radius + 10) # Adjusted for new layout
4545
self.dragging = False
4646
self.torque_enabled = False
4747
self.motor_connected = False # Track motor connection status
48-
self.setMinimumSize(2 * (self.radius + 40), 2 * (self.radius + 60))
48+
self.setMinimumSize(2 * (self.radius + 60), 2 * (self.radius + 120)) # Larger for card design
49+
50+
# Card styling with shadow effect
51+
self.setStyleSheet("""
52+
MotorControlWidget {
53+
background-color: #ffffff;
54+
border-radius: 15px;
55+
border: 1px solid #e1e8ed;
56+
margin: 10px;
57+
}
58+
MotorControlWidget:hover {
59+
border: 1px solid #3498db;
60+
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.1);
61+
}
62+
""")
4963

50-
# Create layout for the control buttons
64+
# Create layout for the card content
5165
self.main_layout = QVBoxLayout(self)
52-
self.main_layout.setContentsMargins(0, 0, 0, 0)
66+
self.main_layout.setContentsMargins(20, 20, 20, 20) # Card padding
67+
self.main_layout.setSpacing(15) # Space between card sections
68+
69+
# Card header with motor name and ID
70+
header_layout = QHBoxLayout()
71+
72+
# Motor name
73+
self.name_label = QLabel(self.motor_name)
74+
self.name_label.setFont(QFont('Segoe UI', 16, QFont.Bold))
75+
self.name_label.setStyleSheet("color: #2c3e50; margin-bottom: 5px;")
76+
header_layout.addWidget(self.name_label)
77+
78+
# Motor ID badge
79+
self.id_badge = QLabel(f"ID: {self.motor_id}")
80+
self.id_badge.setFont(QFont('Segoe UI', 10, QFont.Medium))
81+
self.id_badge.setStyleSheet("""
82+
QLabel {
83+
background-color: #3498db;
84+
color: white;
85+
border-radius: 10px;
86+
padding: 4px 8px;
87+
max-width: 50px;
88+
}
89+
""")
90+
self.id_badge.setAlignment(Qt.AlignCenter)
91+
header_layout.addWidget(self.id_badge)
92+
93+
self.main_layout.addLayout(header_layout)
5394

5495
# Widget for drawing the circle
5596
self.circle_widget = QWidget()
56-
self.circle_widget.setMinimumSize(2 * (self.radius + 40), 2 * (self.radius + 40))
97+
self.circle_widget.setMinimumSize(2 * (self.radius + 20), 2 * (self.radius + 20)) # Smaller since we have header
5798
self.circle_widget.paintEvent = self.paintCircleWidget
5899
self.circle_widget.mousePressEvent = self.circleMousePressEvent
59100
self.circle_widget.mouseMoveEvent = self.circleMouseMoveEvent
60101
self.circle_widget.mouseReleaseEvent = self.circleMouseReleaseEvent
61102
self.main_layout.addWidget(self.circle_widget)
62103

63-
# Controls
64-
controls_layout = QHBoxLayout()
104+
# Controls section with better styling
105+
controls_container = QWidget()
106+
controls_container.setStyleSheet("""
107+
QWidget {
108+
background-color: #f8f9fa;
109+
border-radius: 8px;
110+
padding: 10px;
111+
margin-top: 10px;
112+
}
113+
""")
65114

66-
# Torque toggle
115+
controls_layout = QVBoxLayout(controls_container) # Changed to vertical for better card layout
116+
controls_layout.setSpacing(10)
117+
118+
# Torque toggle with modern styling
67119
self.torque_checkbox = QCheckBox("Enable Torque")
68120
self.torque_checkbox.setChecked(self.torque_enabled)
69121
self.torque_checkbox.setEnabled(False) # Disabled until motor connects
122+
self.torque_checkbox.setFont(QFont('Segoe UI', 11))
123+
self.torque_checkbox.setStyleSheet("""
124+
QCheckBox {
125+
color: #2c3e50;
126+
spacing: 8px;
127+
}
128+
QCheckBox::indicator {
129+
width: 18px;
130+
height: 18px;
131+
border-radius: 9px;
132+
border: 2px solid #bdc3c7;
133+
}
134+
QCheckBox::indicator:checked {
135+
background-color: #27ae60;
136+
border: 2px solid #27ae60;
137+
}
138+
QCheckBox::indicator:disabled {
139+
background-color: #ecf0f1;
140+
border: 2px solid #bdc3c7;
141+
}
142+
""")
70143
self.torque_checkbox.stateChanged.connect(self.toggleTorque)
71144
controls_layout.addWidget(self.torque_checkbox)
72145

73-
# Reset position button
74-
self.reset_button = QPushButton("Reset Position")
146+
# Reset position button with modern styling
147+
self.reset_button = QPushButton("Reset Position")
75148
self.reset_button.setEnabled(False) # Disabled until motor connects
149+
self.reset_button.setFont(QFont('Segoe UI', 11))
150+
self.reset_button.setStyleSheet("""
151+
QPushButton {
152+
background-color: #3498db;
153+
color: white;
154+
border: none;
155+
border-radius: 6px;
156+
padding: 8px 16px;
157+
font-weight: 500;
158+
}
159+
QPushButton:hover {
160+
background-color: #2980b9;
161+
}
162+
QPushButton:pressed {
163+
background-color: #21618c;
164+
}
165+
QPushButton:disabled {
166+
background-color: #bdc3c7;
167+
color: #7f8c8d;
168+
}
169+
""")
76170
self.reset_button.clicked.connect(self.resetPosition)
77171
controls_layout.addWidget(self.reset_button)
78172

79-
self.main_layout.addLayout(controls_layout)
173+
self.main_layout.addWidget(controls_container)
80174

81175
# Conversion helpers
82176
self.rosnode = None # Will be set by parent
@@ -98,20 +192,31 @@ def paintCircleWidget(self, event):
98192
else:
99193
painter.setPen(QPen(Qt.black, 2))
100194
painter.setBrush(QBrush(QColor(240, 240, 240))) # Light gray when disabled
101-
circle_rect = QRect(20, 20, 2 * self.radius, 2 * self.radius)
195+
circle_rect = QRect(10, 10, 2 * self.radius, 2 * self.radius) # Adjusted for new layout
102196
painter.drawEllipse(circle_rect)
103197

104-
# Draw motor name
105-
painter.setFont(QFont("Arial", 12, QFont.Bold))
106-
name_rect = QRect(20, 2 * self.radius + 30, 2 * self.radius, 30)
107-
painter.drawText(name_rect, Qt.AlignCenter, self.motor_name)
108-
109-
# Draw angle text with connection status
198+
# Draw angle text with connection status (centered below circle)
199+
painter.setFont(QFont("Segoe UI", 12, QFont.Medium))
200+
if self.motor_connected:
201+
angle_text = f"{self.angle:.1f}°"
202+
pos_text = f"Position: {self.position}"
203+
else:
204+
angle_text = f"{self.angle:.1f}°"
205+
pos_text = "DISCONNECTED"
206+
207+
# Angle text
208+
angle_rect = QRect(10, 2 * self.radius + 20, 2 * self.radius, 25)
209+
painter.setPen(QPen(Qt.black, 1))
210+
painter.drawText(angle_rect, Qt.AlignCenter, angle_text)
211+
212+
# Position/status text
213+
pos_rect = QRect(10, 2 * self.radius + 40, 2 * self.radius, 25)
110214
if self.motor_connected:
111-
angle_text = f"{self.angle:.1f}° (Pos: {self.position})"
215+
painter.setPen(QPen(QColor("#7f8c8d"), 1))
112216
else:
113-
angle_text = f"{self.angle:.1f}° (DISCONNECTED)"
114-
painter.drawText(name_rect.translated(0, 25), Qt.AlignCenter, angle_text)
217+
painter.setPen(QPen(QColor("#e74c3c"), 1))
218+
painter.setFont(QFont("Segoe UI", 10))
219+
painter.drawText(pos_rect, Qt.AlignCenter, pos_text)
115220

116221
# Draw line from center to edge (like a clock hand)
117222
if self.torque_enabled:
@@ -221,53 +326,77 @@ def __init__(self, node):
221326

222327
def initUI(self):
223328
self.setWindowTitle('Dynamixel Motor Control')
224-
self.setGeometry(100, 100, 600, 500)
329+
self.setGeometry(100, 100, 800, 650) # Larger window for card layout
225330

226-
# Main widget and layout
331+
# Main widget and layout with better spacing
227332
main_widget = QWidget()
333+
main_widget.setStyleSheet("""
334+
QWidget {
335+
background-color: #f8f9fa;
336+
font-family: 'Segoe UI', 'Arial', sans-serif;
337+
}
338+
""")
228339
self.setCentralWidget(main_widget)
229340
main_layout = QVBoxLayout(main_widget)
341+
main_layout.setSpacing(20) # Better spacing between sections
342+
main_layout.setContentsMargins(30, 20, 30, 20) # Consistent margins
230343

231-
# Title label
344+
# Title label with modern styling
232345
title_label = QLabel('Dynamixel Motor Control')
233346
title_label.setAlignment(Qt.AlignCenter)
234-
title_label.setFont(QFont('Arial', 16, QFont.Bold))
347+
title_label.setFont(QFont('Segoe UI', 24, QFont.Bold))
348+
title_label.setStyleSheet("""
349+
QLabel {
350+
color: #2c3e50;
351+
padding: 15px;
352+
margin-bottom: 10px;
353+
}
354+
""")
235355
main_layout.addWidget(title_label)
236356

237-
# Service status indicator
357+
# Service status indicator with card styling
238358
self.status_label = QLabel('Service Status: Checking connection...')
239359
self.status_label.setAlignment(Qt.AlignCenter)
240-
self.status_label.setFont(QFont('Arial', 11))
360+
self.status_label.setFont(QFont('Segoe UI', 12, QFont.Medium))
241361
self.update_service_status(False) # Initialize as disconnected
242362
main_layout.addWidget(self.status_label)
243363

244-
# Motor controls layout
245-
motors_layout = QHBoxLayout()
364+
# Motor controls in card layout
365+
motors_container = QWidget()
366+
motors_container.setStyleSheet("""
367+
QWidget {
368+
background-color: transparent;
369+
}
370+
""")
371+
motors_layout = QHBoxLayout(motors_container)
372+
motors_layout.setSpacing(30) # Space between motor cards
373+
motors_layout.setContentsMargins(0, 0, 0, 0)
246374

247-
# Pan motor control (default angle: 90 degrees)
375+
# Pan motor control card
248376
self.pan_motor = MotorControlWidget(DXL_PAN_ID, "Pan Motor", DEFAULT_PAN_ANGLE)
249377
self.pan_motor.set_ros_node(self.node)
250378
motors_layout.addWidget(self.pan_motor)
251379

252-
# Tilt motor control (default angle: 180 degrees)
380+
# Tilt motor control card
253381
self.tilt_motor = MotorControlWidget(DXL_TILT_ID, "Tilt Motor", DEFAULT_TILT_ANGLE)
254382
self.tilt_motor.set_ros_node(self.node)
255383
motors_layout.addWidget(self.tilt_motor)
256384

257-
main_layout.addLayout(motors_layout)
385+
main_layout.addWidget(motors_container)
258386

259-
# Help instructions
260-
help_label = QLabel('Instructions: To control motors, run "ros2 run dynamixel_sdk_examples read_write_node" in another terminal')
387+
# Help instructions with modern styling
388+
help_label = QLabel('💡 To control motors, run "ros2 run dynamixel_sdk_examples read_write_node" in another terminal')
261389
help_label.setAlignment(Qt.AlignCenter)
262-
help_label.setFont(QFont('Arial', 9))
390+
help_label.setFont(QFont('Segoe UI', 11))
263391
help_label.setStyleSheet("""
264392
QLabel {
265-
color: #666666;
266-
background-color: #f5f5f5;
267-
border: 1px solid #cccccc;
268-
border-radius: 3px;
269-
padding: 5px;
270-
margin: 5px;
393+
color: #6c757d;
394+
background-color: #ffffff;
395+
border: 1px solid #dee2e6;
396+
border-radius: 8px;
397+
padding: 15px 20px;
398+
margin: 10px 0px;
399+
line-height: 1.4;
271400
}
272401
""")
273402
main_layout.addWidget(help_label)
@@ -279,24 +408,26 @@ def update_service_status(self, connected):
279408
self.status_label.setText('✓ Dynamixel Service: CONNECTED - Motors ready for control')
280409
self.status_label.setStyleSheet("""
281410
QLabel {
282-
background-color: #d4e8d4;
283-
color: #2e7d2e;
284-
border: 2px solid #4caf50;
285-
border-radius: 5px;
286-
padding: 8px;
287-
font-weight: bold;
411+
background-color: #d5f4e6;
412+
color: #27ae60;
413+
border: 2px solid #27ae60;
414+
border-radius: 10px;
415+
padding: 12px 20px;
416+
font-weight: 600;
417+
margin: 5px;
288418
}
289419
""")
290420
else:
291421
self.status_label.setText('⚠ Dynamixel Service: DISCONNECTED - Start "read_write_node" to control motors')
292422
self.status_label.setStyleSheet("""
293423
QLabel {
294-
background-color: #f8d7da;
295-
color: #721c24;
296-
border: 2px solid #f44336;
297-
border-radius: 5px;
298-
padding: 8px;
299-
font-weight: bold;
424+
background-color: #ffeaa7;
425+
color: #d63031;
426+
border: 2px solid #fdcb6e;
427+
border-radius: 10px;
428+
padding: 12px 20px;
429+
font-weight: 600;
430+
margin: 5px;
300431
}
301432
""")
302433

0 commit comments

Comments
 (0)