Skip to content

Commit 806672e

Browse files
Merge pull request #69 from DiamondLightSource/245-MianWindow
Refactor main window
2 parents 4a5fa35 + 2b08fc0 commit 806672e

File tree

11 files changed

+357
-197
lines changed

11 files changed

+357
-197
lines changed

dls_barcode/gui/barcode_table.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ def _init_ui(self):
2727

2828
# Create barcode table - lists all the barcodes in a record
2929
self._table = QtGui.QTableWidget()
30-
self._table.setFixedWidth(110)
31-
self._table.setFixedHeight(600)
30+
self._table.setMinimumWidth(110)
31+
self._table.setMinimumHeight(600)
3232
self._table.setColumnCount(1)
3333
self._table.setRowCount(10)
3434
self._table.setHorizontalHeaderLabels(['Barcode'])

dls_barcode/gui/image_frame.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ def __init__(self, title):
1616
def _init_ui(self):
1717
# Image frame - displays image of the currently selected scan record
1818
self._frame = QLabel()
19-
self._frame.setStyleSheet("background-color: black; color: red; font-size: 30pt; text-align: center")
20-
self._frame.setFixedWidth(500)
21-
self._frame.setFixedHeight(500)
19+
self._frame.setStyleSheet("background-color: white; color: red; font-size: 30pt; text-align: center")
20+
self._frame.setMinimumWidth(500)
21+
self._frame.setMinimumHeight(500)
2222
self._frame.setAlignment(Qt.AlignCenter)
2323

2424
vbox = QVBoxLayout()

dls_barcode/gui/main_window.py

Lines changed: 35 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,39 @@
1-
import multiprocessing
2-
import queue
3-
import time
1+
import sys
2+
from PyQt4 import QtGui
43

5-
from PyQt4 import QtGui, QtCore
4+
from dls_barcode.config import BarcodeConfigDialog
65

7-
from dls_barcode.config import BarcodeConfig, BarcodeConfigDialog
8-
from dls_barcode.camera import CameraScanner, CameraSwitch, NoNewBarcodeMessage, ScanErrorMessage
96
from dls_barcode.gui.scan_button import ScanButton
10-
from dls_util import Beeper
11-
from dls_util.file import FileManager
7+
128
from .barcode_table import BarcodeTable
139
from .image_frame import ImageFrame
1410
from .record_table import ScanRecordTable
1511
from .message_box import MessageBox
1612
from .message_factory import MessageFactory
1713
from .menu_bar import MenuBar
1814

19-
RESULT_TIMER_PERIOD = 1000 # ms
20-
VIEW_TIMER_PERIOD = 1 # ms
21-
MESSAGE_TIMER_PERIOD = 1 # ms
22-
2315

2416
class DiamondBarcodeMainWindow(QtGui.QMainWindow):
2517
""" Main GUI window for the Barcode Scanner App.
2618
"""
27-
def __init__(self, config_file, version):
19+
20+
def __init__(self, config, version):
2821
super(DiamondBarcodeMainWindow, self).__init__()
2922

30-
self._config = BarcodeConfig(config_file, FileManager())
23+
self._config = config
3124
self._version = version
25+
self._cleanup = None
26+
self._initialise_scanner = None
27+
self._camera_capture_alive = None
3228

3329
# UI elements
3430
self._record_table = None
3531
self._barcode_table = None
3632
self._image_frame = None
3733
self._scan_button = None
3834

39-
# Scan elements
40-
self._camera_scanner = None
41-
self._camera_switch = None
42-
4335
self._init_ui()
4436

45-
# Queue that holds new results generated in continuous scanning mode
46-
self._result_queue = multiprocessing.Queue()
47-
self._view_queue = multiprocessing.Queue()
48-
self._message_queue = multiprocessing.Queue()
49-
self._initialise_scanner()
50-
self._reset_msg_timer()
51-
52-
# Timer that controls how often new scan results are looked for
53-
self._result_timer = QtCore.QTimer()
54-
self._result_timer.timeout.connect(self._read_result_queue)
55-
self._result_timer.start(RESULT_TIMER_PERIOD)
56-
57-
self._view_timer = QtCore.QTimer()
58-
self._view_timer.timeout.connect(self._read_view_queue)
59-
self._view_timer.start(VIEW_TIMER_PERIOD)
60-
61-
self._message_timer = QtCore.QTimer()
62-
self._message_timer.timeout.connect(self._read_message_queue)
63-
self._message_timer.start(MESSAGE_TIMER_PERIOD)
64-
65-
self._restart_live_capture_from_side()
66-
6737
def _init_ui(self):
6838
""" Create the basic elements of the user interface.
6939
"""
@@ -73,19 +43,19 @@ def _init_ui(self):
7343
self.setWindowTitle('Diamond Puck Barcode Scanner')
7444
self.setWindowIcon(self._window_icon)
7545

76-
self._menu_bar = MenuBar(self.menuBar(), self._version, self._cleanup, self._on_options_action_clicked,self._on_about_action_clicked)
46+
self._menu_bar = MenuBar(self.menuBar(), self._version)
7747

7848
# Barcode table - lists all the barcodes in a record
7949
self._barcode_table = BarcodeTable(self._config)
8050

8151
# Scan button - start/stop scan
82-
self._scan_button = ScanButton('Start/stop scan', self._on_scan_action_clicked)
52+
self._scan_button = ScanButton('Start/stop scan')
8353

8454
# Image frame - displays image of the currently selected scan record
8555
self._image_frame = ImageFrame("Plate Image")
8656

8757
# Scan record table - lists all the records in the store
88-
self._record_table = ScanRecordTable(self._barcode_table, self._image_frame, self._config, self._to_run_on_table_clicked)
58+
self._record_table = ScanRecordTable(self._barcode_table, self._image_frame, self._config)
8959

9060
# Message display
9161
self._message_box = MessageBox()
@@ -116,18 +86,28 @@ def _init_ui(self):
11686

11787
self.show()
11888

89+
def set_actions_triger(self, cleanup, initialise_scanner, camera_capture_alive):
90+
self._cleanup = cleanup
91+
self._initialise_scanner = initialise_scanner
92+
self._camera_capture_alive = camera_capture_alive
93+
self._scan_button.click_action(self._on_scan_action_clicked)
94+
self._menu_bar.exit_action_triggered(self._cleanup)
95+
self._menu_bar.about_action_trigerred(self._on_about_action_clicked)
96+
self._menu_bar.optiones_action_triggered(self._on_options_action_clicked)
97+
self._record_table.cell_pressed_action_triggered(self._to_run_on_table_clicked)
98+
11999
def _to_run_on_table_clicked(self):
120100
self._cleanup()
121101
self._scan_button.setStartLayout()
122102

123103
def _on_about_action_clicked(self):
124104
QtGui.QMessageBox.about(self, 'About', "Version: " + self._version)
125105

106+
126107
def _on_scan_action_clicked(self):
127108
print("MAIN: Scan menu clicked")
128109
if not self._camera_capture_alive():
129110
self._initialise_scanner()
130-
self._restart_live_capture_from_side()
131111
self._scan_button.setDelayedStopLayout()
132112
else:
133113
self._cleanup()
@@ -144,122 +124,20 @@ def closeEvent(self, event):
144124
self._cleanup()
145125
event.accept()
146126

147-
def _cleanup(self):
148-
if not self._camera_capture_alive():
149-
return
150-
151-
self._camera_scanner.kill()
152-
self._camera_scanner = None
153-
self._camera_switch = None
154-
155-
def _initialise_scanner(self):
156-
self._camera_scanner = CameraScanner(self._result_queue, self._view_queue, self._message_queue, self._config)
157-
self._camera_switch = CameraSwitch(self._camera_scanner, self._config.top_camera_timeout)
127+
def displayScanCompleteMessage(self):
128+
self._message_box.display(MessageFactory.scan_completed_message())
158129

159-
def _camera_capture_alive(self):
160-
return self._camera_scanner is not None and self._camera_switch is not None
130+
def displayScanErrorMessage(self, scanner_msg):
131+
self._message_box.display(MessageFactory.from_scanner_message(scanner_msg))
161132

162-
def _read_view_queue(self):
163-
if self._view_queue.empty():
164-
return
165-
166-
try:
167-
image = self._view_queue.get(False)
168-
except queue.Empty:
169-
return
133+
def displayScanTimeoutMessage(self):
134+
self._message_box.display(MessageFactory.scan_timeout_message())
170135

136+
def displayPuckImage(self, image):
171137
self._image_frame.display_puck_image(image)
172138

173-
def _read_message_queue(self):
174-
if self._message_queue.empty():
175-
return
176-
177-
try:
178-
scanner_msg = self._message_queue.get(False)
179-
except queue.Empty:
180-
return
181-
182-
if self._camera_switch.is_side():
183-
if not self._msg_timer_is_running():
184-
# The result queue is read at a slower rate - use a timer to give it time to process a new barcode
185-
self._start_msg_timer()
186-
elif self._has_msg_timer_timeout() and isinstance(scanner_msg, NoNewBarcodeMessage):
187-
self._message_box.display(MessageFactory.scan_completed_message())
188-
elif isinstance(scanner_msg, ScanErrorMessage):
189-
self._message_box.display(MessageFactory.from_scanner_message(scanner_msg))
190-
self._reset_msg_timer()
191-
else:
192-
self._reset_msg_timer()
193-
194-
def _reset_msg_timer(self):
195-
self._record_msg_timer = None
196-
197-
def _start_msg_timer(self):
198-
self._record_msg_timer = time.time()
199-
200-
def _msg_timer_is_running(self):
201-
return self._record_msg_timer is not None
202-
203-
def _has_msg_timer_timeout(self):
204-
timeout =2 * RESULT_TIMER_PERIOD / 1000
205-
return self._msg_timer_is_running() and time.time() - self._record_msg_timer > timeout
206-
207-
def _read_result_queue(self):
208-
""" Called every second; read any new results from the scan results queue, store them and display them.
209-
"""
210-
if not self._camera_capture_alive():
211-
return
212-
213-
if self._camera_switch.is_side():
214-
self._read_side_scan()
215-
else:
216-
self._read_top_scan()
217-
218-
def _read_side_scan(self):
219-
if self._result_queue.empty():
220-
return
221-
222-
# Get the result
223-
plate, holder_image = self._result_queue.get(False)
224-
if not plate.is_full_valid():
225-
return
226-
227-
# Barcode successfully read
228-
Beeper.beep()
229-
print("MAIN: puck barcode recorded")
230-
holder_barcode = plate.barcodes()[0]
231-
if not self._record_table.is_latest_holder_barcode(holder_barcode):
232-
self._latest_holder_barcode = holder_barcode
233-
self._latest_holder_image = holder_image
234-
self._restart_live_capture_from_top()
235-
236-
def _read_top_scan(self):
237-
if self._result_queue.empty():
238-
if self._camera_switch.is_top_scan_timeout():
239-
self._message_box.display(MessageFactory.scan_timeout_message())
240-
print("\n*** Scan timeout ***")
241-
self._restart_live_capture_from_side()
242-
return
243-
244-
# Get the result
245-
plate, pins_image = self._result_queue.get(False)
246-
247-
# Add new record to the table - side is the _latest_holder_barcode read first, top is the plate
248-
self._record_table.add_record_frame(self._latest_holder_barcode, plate, self._latest_holder_image, pins_image)
249-
if not plate.is_full_valid():
250-
return
251-
252-
# Barcodes successfully read
253-
Beeper.beep()
254-
print("Scan Completed", self._camera_switch.get_scan_time())
255-
self._restart_live_capture_from_side()
256-
257-
def _restart_live_capture_from_top(self):
258-
self._camera_switch.restart_live_capture_from_top()
259-
260-
def _restart_live_capture_from_side(self):
261-
self._reset_msg_timer()
262-
self._camera_switch.restart_live_capture_from_side()
263-
264-
139+
def addRecordFrame(self, latest_holder_barcode, plate, latest_holder_image, pins_image):
140+
self._record_table.add_record_frame(latest_holder_barcode, plate, latest_holder_image, pins_image)
265141

142+
def isLatestHolderBarcode(self, holder_barcode):
143+
return self._record_table.is_latest_holder_barcode(holder_barcode)

dls_barcode/gui/menu_bar.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,53 @@ class MenuBar(QMainWindow):
77
""" GUI component. Displays a start/stop button
88
"""
99

10-
def __init__(self, mainMenu, version, clean_up, on_options_action_clicked, on_about_action_clicked):
10+
def __init__(self, mainMenu, version):
1111
super(MenuBar, self).__init__()
1212
self._version = version
13-
self._cleanup = clean_up
14-
self._on_options_action_clicked = on_options_action_clicked
15-
self._on_about_action_clicked = on_about_action_clicked
1613

1714
self._exit_icon = self.style().standardIcon(QStyle.SP_DialogCloseButton)
1815
self._config_icon = self.style().standardIcon(QStyle.SP_FileDialogDetailedView)
1916
self._about_icon = self.style().standardIcon(QStyle.SP_FileDialogInfoView)
2017

2118
self._mainMenu = mainMenu
19+
self._exit_action = None
20+
self._options_action = None
21+
self._about_action = None
2222

2323
self._init_ui()
2424

2525
def _init_ui(self):
2626
"""Create and populate the menu bar.
2727
"""
2828
# Exit Application
29-
exit_action = QAction(self._exit_icon, '&Exit', self)
30-
exit_action.setShortcut('Ctrl+Q')
31-
exit_action.setStatusTip('Exit application')
32-
exit_action.triggered.connect(self._cleanup)
33-
exit_action.triggered.connect(qApp.quit)
29+
self._exit_action = QAction(self._exit_icon, '&Exit', self)
30+
self._exit_action.setShortcut('Ctrl+Q')
31+
self._exit_action.setStatusTip('Exit application')
3432

3533
# Open options dialog
36-
options_action = QAction(self._config_icon, '&Config', self)
37-
options_action.setShortcut('Ctrl+O')
38-
options_action.setStatusTip('Open Options Dialog')
39-
options_action.triggered.connect(self._on_options_action_clicked)
34+
self._options_action = QAction(self._config_icon, '&Config', self)
35+
self._options_action.setShortcut('Ctrl+O')
36+
self._options_action.setStatusTip('Open Options Dialog')
4037

4138
# Show version number
42-
about_action = QAction(self._about_icon, "About", self)
43-
about_action.triggered.connect(self._on_about_action_clicked)
39+
self._about_action = QAction(self._about_icon, "About", self)
4440

4541
# Create menu bar
4642
file_menu = self._mainMenu.addMenu('&File')
47-
file_menu.addAction(exit_action)
43+
file_menu.addAction(self._exit_action)
4844

4945
option_menu = self._mainMenu.addMenu('&Options')
50-
option_menu.addAction(options_action)
46+
option_menu.addAction(self._options_action)
5147

5248
help_menu = self._mainMenu.addMenu('?')
53-
help_menu.addAction(about_action)
49+
help_menu.addAction(self._about_action)
50+
51+
def exit_action_triggered(self, cleanup):
52+
self._exit_action.triggered.connect(cleanup)
53+
self._exit_action.triggered.connect(qApp.quit)
54+
55+
def optiones_action_triggered(self, on_options_action_clicked):
56+
self._options_action.triggered.connect(on_options_action_clicked)
57+
58+
def about_action_trigerred(self, on_about_action_clicked):
59+
self._about_action.triggered.connect(on_about_action_clicked)

0 commit comments

Comments
 (0)