Skip to content

Commit f80d339

Browse files
Merge pull request #65 from DiamondLightSource/I04_1-172
I04_1-172
2 parents 12e9bc6 + 32a4bfe commit f80d339

File tree

13 files changed

+173
-119
lines changed

13 files changed

+173
-119
lines changed

dls_barcode/camera/camera_switch.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ def __init__(self, camera_scanner, timeout_config):
1111
self._reset_top_scan_timer()
1212
self._switch_to_side()
1313

14-
def stop_live_capture(self):
14+
def _stop_live_capture(self):
1515
self._scanner.stop_scan()
1616
self._reset_top_scan_timer()
1717

1818
def restart_live_capture_from_side(self):
19-
self.stop_live_capture()
19+
self._stop_live_capture()
2020
self._switch_to_side()
2121
self._scanner.start_scan(CameraPosition.SIDE)
2222

2323
def restart_live_capture_from_top(self):
24-
self.stop_live_capture()
24+
self._stop_live_capture()
2525
self._switch_to_top()
2626
self._start_top_scan_timer()
2727
self._scanner.start_scan(CameraPosition.TOP)

dls_barcode/config/barcode_config.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ def __init__(self, file, file_manager):
2525
else:
2626
default_store = "../store/"
2727

28-
self.color_ok = add(ColorConfigItem, "Read Color", Color.Green())
29-
self.color_accept = add(ColorConfigItem, "Partially Read Color", Color.Yellow())
30-
self.color_unreadable = add(ColorConfigItem, "Not Read Color", Color.Red())
31-
self.color_empty = add(ColorConfigItem, "Empty Color", Color.Grey())
28+
self.color_ok = add(ColorConfigItem, "Pin/Puck Read", Color.Green())
29+
self.color_accept = add(ColorConfigItem, "Puck Partially Read", Color.Yellow())
30+
self.color_unreadable = add(ColorConfigItem, "Pin/Puck Not Read", Color.Red())
31+
self.color_empty = add(ColorConfigItem, "Pin Empty", Color.Grey())
3232

3333
self.plate_type = add(EnumConfigItem, "Sample Plate Type", default=Geometry.UNIPUCK, extra_arg=Geometry.TYPES)
3434
self.top_barcode_size = add(EnumConfigItem, "Datamatrix Size", default=DataMatrix.DEFAULT_SIZE,
@@ -38,11 +38,11 @@ def __init__(self, file, file_manager):
3838
self.scan_beep = add(BoolConfigItem, "Beep While Scanning", default=True)
3939
self.scan_clipboard = add(BoolConfigItem, "Results to Clipboard", default=True)
4040

41-
self.image_puck = add(BoolConfigItem, "Draw Puck", default=True)
42-
self.image_pins = add(BoolConfigItem, "Draw Slot Highlights", default=True)
41+
self.image_puck = add(BoolConfigItem, "Puck Highlight", default=True)
42+
self.image_pins = add(BoolConfigItem, "Slots Highlight", default=True)
4343
self.image_crop = add(BoolConfigItem, "Crop to Puck", default=True)
4444

45-
self.store_directory = add(DirectoryConfigItem, "Startup Store Directory", default=default_store)
45+
self.store_directory = add(DirectoryConfigItem, "Store Directory", default=default_store)
4646
self.store_capacity = add(IntConfigItem, "Results History Size", default=50)
4747

4848
self.console_frame = add(BoolConfigItem, "Print Frame Summary", default=False)

dls_barcode/config/barcode_config_dialog.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,38 @@
22
from .camera_config_control import CameraConfigControl
33
from .store_directory_config_control import StoreDirectoryConfigControl
44

5+
from PyQt4.QtGui import QStyle
6+
57

68
class BarcodeConfigDialog(ConfigDialog):
79
""" Dialog to edit the configuration options for the program. Provides a custom control for
810
setting up the camera.
911
"""
1012
def __init__(self, config, to_run_before_test_camera):
1113
ConfigDialog.__init__(self, config)
12-
13-
self._init_ui(to_run_before_test_camera)
14+
self._to_run_before_test_camera = to_run_before_test_camera
15+
self._config_icon = self.style().standardIcon(QStyle.SP_FileDialogDetailedView)
16+
self._init_ui()
1417
self.finalize_layout()
1518

16-
def _init_ui(self, to_run_before_test_camera):
19+
def _init_ui(self):
1720
self.setGeometry(100, 100, 450, 400)
1821

22+
self.setWindowIcon(self._config_icon)
23+
24+
self._to_run_before_test_camera()
25+
1926
cfg = self._config
2027
add = self.add_item
2128

22-
camera_top = CameraConfigControl(cfg.get_top_camera_config(), to_run_before_test_camera)
23-
camera_side = CameraConfigControl(cfg.get_side_camera_config(), to_run_before_test_camera)
29+
camera_top = CameraConfigControl(cfg.get_top_camera_config())
30+
camera_side = CameraConfigControl(cfg.get_side_camera_config())
2431

2532
self.start_group("Colors")
2633
add(cfg.color_ok)
2734
add(cfg.color_unreadable)
2835
add(cfg.color_empty)
36+
add(cfg.color_accept)
2937

3038
self.start_group("Top Camera")
3139
add(cfg.top_barcode_size)

dls_barcode/config/camera_config_control.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77

88
class CameraConfigControl(ConfigControl):
99
RES_TEXT_WIDTH = 50
10-
BUTTON_WIDTH = 100
10+
BUTTON_WIDTH = 150
1111

12-
def __init__(self, camera_config, to_run_before_test_camera):
12+
def __init__(self, camera_config):
1313
ConfigControl.__init__(self, camera_config)
1414
self._number_item = camera_config.camera_number
1515
self._width_item = camera_config.width
1616
self._height_item = camera_config.height
17-
self._to_run_before_test_camera = to_run_before_test_camera
1817
self._init_ui()
1918

2019
def _init_ui(self):
@@ -87,8 +86,6 @@ def _test_camera(self):
8786
QMessageBox.critical(self, "Camera Error", "Camera number, width, and height must be integers")
8887
return
8988

90-
self._to_run_before_test_camera()
91-
9289
# Check that we can connect to the camera
9390
try:
9491
stream = CameraStream(camera_num, camera_width, camera_height, use_default_as_backup=False)
@@ -130,6 +127,5 @@ def _test_camera(self):
130127

131128
def _open_camera_controls(self):
132129
camera_num = int(self.txt_number.text())
133-
self._to_run_before_test_camera()
134130
CameraStream.open_camera_controls(camera_num)
135131

dls_barcode/geometry/unipuck_locator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def find_location(self):
4242
hull = cv2.convexHull(match_cnt)
4343
hull_area = cv2.contourArea(hull)
4444
feature_area = cv2.contourArea(match_cnt)
45-
puck_area = math.pi * math.sqrt(radius)
45+
#puck_area = math.pi * math.sqrt(radius)
4646
area_factor = feature_area / hull_area
4747
#puck_area_factor = feature_area / puck_area
4848
if round(area_factor, 2) > FEATURE_HULL_MATCH_FACTOR:# and puck_area_factor > PUCK_FEATURE_AREA_FACTOR_MIN:

dls_barcode/gui/image_frame.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,18 @@
77
class ImageFrame(QGroupBox):
88
""" GUI component. Displays an image of the currently selected barcode.
99
"""
10-
def __init__(self, width_item, height_item, title):
10+
def __init__(self, title):
1111
super(ImageFrame, self).__init__()
1212

1313
self.setTitle(title)
14-
self._width_item = width_item
15-
self._height_item = height_item
1614
self._init_ui()
1715

1816
def _init_ui(self):
1917
# Image frame - displays image of the currently selected scan record
2018
self._frame = QLabel()
2119
self._frame.setStyleSheet("background-color: black; color: red; font-size: 30pt; text-align: center")
22-
self._frame.setFixedWidth(self._width_item)
23-
self._frame.setFixedHeight(self._height_item)
20+
self._frame.setFixedWidth(500)
21+
self._frame.setFixedHeight(500)
2422
self._frame.setAlignment(Qt.AlignCenter)
2523

2624
vbox = QVBoxLayout()

dls_barcode/gui/main_window.py

Lines changed: 37 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@
66

77
from dls_barcode.config import BarcodeConfig, BarcodeConfigDialog
88
from dls_barcode.camera import CameraScanner, CameraSwitch, NoNewBarcodeMessage
9+
from dls_barcode.gui.scan_button import ScanButton
910
from dls_util import Beeper
1011
from dls_util.file import FileManager
1112
from .barcode_table import BarcodeTable
1213
from .image_frame import ImageFrame
1314
from .record_table import ScanRecordTable
1415
from .message_box import MessageBox
1516
from .message_factory import MessageFactory
17+
from .menu_bar import MenuBar
1618

17-
18-
RESULT_TIMER_PERIOD = 1000 # ms
19-
VIEW_TIMER_PERIOD = 1 # ms
20-
MESSAGE_TIMER_PERIOD = 1 # ms
19+
RESULT_TIMER_PERIOD = 1000 # ms
20+
VIEW_TIMER_PERIOD = 1 # ms
21+
MESSAGE_TIMER_PERIOD = 1 # ms
2122

2223

2324
class DiamondBarcodeMainWindow(QtGui.QMainWindow):
@@ -33,6 +34,7 @@ def __init__(self, config_file, version):
3334
self._record_table = None
3435
self._barcode_table = None
3536
self._image_frame = None
37+
self._scan_button = None
3638

3739
# Scan elements
3840
self._camera_scanner = None
@@ -65,35 +67,39 @@ def __init__(self, config_file, version):
6567
def _init_ui(self):
6668
""" Create the basic elements of the user interface.
6769
"""
68-
self._init_icons()
70+
self._window_icon = QtGui.QIcon("..\\resources\\icons\\qr_code_32.png")
6971

7072
self.setGeometry(100, 100, 1020, 650)
7173
self.setWindowTitle('Diamond Puck Barcode Scanner')
7274
self.setWindowIcon(self._window_icon)
7375

74-
self.init_menu_bar()
76+
self._menu_bar = MenuBar(self.menuBar(), self._version, self._cleanup, self._on_options_action_clicked,self._on_about_action_clicked)
7577

7678
# Barcode table - lists all the barcodes in a record
7779
self._barcode_table = BarcodeTable(self._config)
7880

81+
# Scan button - start/stop scan
82+
self._scan_button = ScanButton('Start/stop scan', self._on_scan_action_clicked)
83+
7984
# Image frame - displays image of the currently selected scan record
80-
self._image_frame = ImageFrame(500, 500, "Plate Image")
85+
self._image_frame = ImageFrame("Plate Image")
8186

8287
# Scan record table - lists all the records in the store
83-
self._record_table = ScanRecordTable(self._barcode_table, self._image_frame, self._config, self.on_record_table_clicked)
88+
self._record_table = ScanRecordTable(self._barcode_table, self._image_frame, self._config, self._to_run_on_table_clicked)
8489

8590
# Message display
8691
self._message_box = MessageBox()
87-
self._message_box.setFixedHeight(64)
88-
89-
# Open options first to make sure the cameras are set up correctly.
90-
# Start live capture of the side as soon as the dialog box is closed
91-
self._open_options_dialog()
9292

9393
# Create layout
94+
9495
hbox = QtGui.QHBoxLayout()
9596
hbox.setSpacing(10)
96-
hbox.addWidget(self._record_table)
97+
98+
table_vbox = QtGui.QVBoxLayout()
99+
table_vbox.addWidget(self._record_table)
100+
table_vbox.addWidget(self._scan_button)
101+
102+
hbox.addLayout(table_vbox)
97103
hbox.addWidget(self._barcode_table)
98104

99105
img_vbox = QtGui.QVBoxLayout()
@@ -110,52 +116,9 @@ def _init_ui(self):
110116

111117
self.show()
112118

113-
def _init_icons(self):
114-
self._window_icon = QtGui.QIcon("..\\resources\\icons\\qr_code_32.png")
115-
self._start_capture_icon = self.style().standardIcon(QtGui.QStyle.SP_MediaPlay)
116-
self._exit_icon = self.style().standardIcon(QtGui.QStyle.SP_DialogCloseButton)
117-
self._config_icon = self.style().standardIcon(QtGui.QStyle.SP_FileDialogDetailedView)
118-
self._about_icon = self.style().standardIcon(QtGui.QStyle.SP_FileDialogInfoView)
119-
120-
def init_menu_bar(self):
121-
"""Create and populate the menu bar.
122-
"""
123-
# Continuous scanner mode
124-
live_action = QtGui.QAction(self._start_capture_icon, '&Camera Capture', self)
125-
live_action.setShortcut('Ctrl+W')
126-
live_action.setStatusTip('Capture continuously from camera')
127-
live_action.triggered.connect(self._on_scan_action_clicked)
128-
129-
# Exit Application
130-
exit_action = QtGui.QAction(self._exit_icon, '&Exit', self)
131-
exit_action.setShortcut('Ctrl+Q')
132-
exit_action.setStatusTip('Exit application')
133-
exit_action.triggered.connect(self._cleanup)
134-
exit_action.triggered.connect(QtGui.qApp.quit)
135-
136-
# Open options dialog
137-
options_action = QtGui.QAction(self._config_icon, '&Config', self)
138-
options_action.setShortcut('Ctrl+O')
139-
options_action.setStatusTip('Open Options Dialog')
140-
options_action.triggered.connect(self._on_options_action_clicked)
141-
142-
# Show version number
143-
about_action = QtGui.QAction(self._about_icon, "About", self)
144-
about_action.triggered.connect(self._on_about_action_clicked)
145-
146-
# Create menu bar
147-
menu_bar = self.menuBar()
148-
file_menu = menu_bar.addMenu('&File')
149-
file_menu.addAction(exit_action)
150-
151-
scan_menu = menu_bar.addMenu('&Scan')
152-
scan_menu.addAction(live_action)
153-
154-
option_menu = menu_bar.addMenu('&Options')
155-
option_menu.addAction(options_action)
156-
157-
help_menu = menu_bar.addMenu('?')
158-
help_menu.addAction(about_action)
119+
def _to_run_on_table_clicked(self):
120+
self._cleanup()
121+
self._scan_button.setStartLayout()
159122

160123
def _on_about_action_clicked(self):
161124
QtGui.QMessageBox.about(self, 'About', "Version: " + self._version)
@@ -164,23 +127,16 @@ def _on_scan_action_clicked(self):
164127
print("MAIN: Scan menu clicked")
165128
if not self._camera_capture_alive():
166129
self._initialise_scanner()
167-
168-
self._restart_live_capture_from_side()
130+
self._restart_live_capture_from_side()
131+
self._scan_button.setDelayedStopLayout()
132+
else:
133+
self._cleanup()
134+
self._scan_button.setStartLayout()
169135

170136
def _on_options_action_clicked(self):
171-
result_ok = self._open_options_dialog()
172-
if not result_ok:
173-
return
174-
175-
self._cleanup()
176-
self._initialise_scanner()
177-
self._restart_live_capture_from_side()
178-
179-
def _open_options_dialog(self):
180-
dialog = BarcodeConfigDialog(self._config, self._before_test_camera)
181-
dialog.setWindowIcon(self._config_icon)
182-
result_ok = dialog.exec_()
183-
return result_ok
137+
dialog = BarcodeConfigDialog(self._config, self._cleanup)
138+
self._scan_button.setStartLayout()
139+
dialog.exec_()
184140

185141
def closeEvent(self, event):
186142
"""This overrides the method from the base class.
@@ -200,14 +156,6 @@ def _initialise_scanner(self):
200156
self._camera_scanner = CameraScanner(self._result_queue, self._view_queue, self._message_queue, self._config)
201157
self._camera_switch = CameraSwitch(self._camera_scanner, self._config.top_camera_timeout)
202158

203-
def on_record_table_clicked(self):
204-
if self._camera_capture_alive():
205-
self._camera_switch.stop_live_capture()
206-
207-
def _before_test_camera(self):
208-
# We need to stop the cameras otherwise the Test Camera button won't be able to open them
209-
self._cleanup()
210-
211159
def _camera_capture_alive(self):
212160
return self._camera_scanner is not None and self._camera_switch is not None
213161

@@ -242,17 +190,17 @@ def _read_message_queue(self):
242190
self._message_box.display(MessageFactory.from_scanner_message(scanner_msg))
243191

244192
def _reset_msg_timer(self):
245-
self._duplicate_record_msg_timer = None
193+
self._record_msg_timer = None
246194

247195
def _start_msg_timer(self):
248-
self._duplicate_record_msg_timer = time.time()
196+
self._record_msg_timer = time.time()
249197

250198
def _msg_timer_is_running(self):
251-
return self._duplicate_record_msg_timer is not None
199+
return self._record_msg_timer is not None
252200

253201
def _has_msg_timer_timeout(self):
254202
timeout = 2 * RESULT_TIMER_PERIOD / 1000
255-
return self._msg_timer_is_running() and time.time() - self._duplicate_record_msg_timer > timeout
203+
return self._msg_timer_is_running() and time.time() - self._record_msg_timer > timeout
256204

257205
def _read_result_queue(self):
258206
""" Called every second; read any new results from the scan results queue, store them and display them.
@@ -315,3 +263,5 @@ def _restart_live_capture_from_side(self):
315263
self._reset_msg_timer()
316264
self._camera_switch.restart_live_capture_from_side()
317265

266+
267+

0 commit comments

Comments
 (0)