diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 019bc1c7..d6362b37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: - main jobs: + # Job 1: Linters for Pull Requests linters: runs-on: windows-latest strategy: @@ -27,3 +28,29 @@ jobs: run: flake8 parallax tests continue-on-error: true + # Job 2: Build Documentation for Pushes to Main + build-docs: + runs-on: ubuntu-latest + steps: + # Step 1: Checkout the repository + - name: Checkout Repository + uses: actions/checkout@v3 + + # Step 2: Set up Python + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.8' + + # Step 3: Install dependencies (including Sphinx) + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r docs/requirements.txt + + # Step 4: Build the documentation + - name: Build Documentation + run: | + sphinx-build -b html docs/source docs/_build + # Fail the workflow if documentation build fails + continue-on-error: false diff --git a/parallax/__init__.py b/parallax/__init__.py index 3f9c7742..642a0470 100644 --- a/parallax/__init__.py +++ b/parallax/__init__.py @@ -4,7 +4,7 @@ import os -__version__ = "0.37.27" +__version__ = "0.37.29" # allow multiple OpenMP instances os.environ["KMP_DUPLICATE_LIB_OK"] = "True" diff --git a/parallax/calculator.py b/parallax/calculator.py index 2b6ae8da..61383965 100644 --- a/parallax/calculator.py +++ b/parallax/calculator.py @@ -20,7 +20,7 @@ def __init__(self, model, reticle_selector, stage_controller): self.reticle = None self.stage_controller = stage_controller - self.ui = loadUi(os.path.join(ui_dir, "calc_move.ui"), self) + self.ui = loadUi(os.path.join(ui_dir, "calc.ui"), self) self.setWindowTitle(f"Calculator") self.setWindowFlags(Qt.Window | Qt.WindowMinimizeButtonHint | \ Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) @@ -236,7 +236,7 @@ def _disable(self, sn): group_box = self.findChild(QGroupBox, f"groupBox_{sn}") group_box.setEnabled(False) group_box.setStyleSheet("background-color: #333333;") - group_box.setTitle(f"{sn} (Uncalibrated)") + group_box.setTitle(f"(Uncalibrated) {sn}") def _enable(self, sn): # Find the QGroupBox for the stage @@ -251,11 +251,11 @@ def _create_stage_groupboxes(self): for sn in self.model.stages.keys(): # Load the QGroupBox from the calc_QGroupBox.ui file group_box = QGroupBox(self) - #loadUi(os.path.join(ui_dir, "calc_QGroupBox.ui"), group_box) # TODO - loadUi(os.path.join(ui_dir, "calc_QGroupBox_move.ui"), group_box) + loadUi(os.path.join(ui_dir, "calc_QGroupBox.ui"), group_box) # Set the visible title of the QGroupBox to sn group_box.setTitle(f"{sn}") + group_box.setAlignment(Qt.AlignRight) # title alignment to the right # Append _{sn} to the QGroupBox object name group_box.setObjectName(f"groupBox_{sn}") @@ -274,7 +274,6 @@ def _create_stage_groupboxes(self): # Add the newly created QGroupBox to the layout widget_count = self.ui.verticalLayout_QBox.count() self.ui.verticalLayout_QBox.insertWidget(widget_count - 1, group_box) - #self.ui.verticalLayout_QBox.addWidget(group_box) def _connect_move_stage_buttons(self): stop_button = self.ui.findChild(QPushButton, f"stopAllStages") @@ -302,11 +301,16 @@ def _move_stage(self, stage_sn, move_type): # Convert the text to float, round it, then cast to int x = float(self.findChild(QLineEdit, f"localX_{stage_sn}").text())/1000 y = float(self.findChild(QLineEdit, f"localY_{stage_sn}").text())/1000 - z = 15.0 + z = 15.0 # Z is inverted in the server. except ValueError as e: logger.warning(f"Invalid input for stage {stage_sn}: {e}") return # Optionally handle the error gracefully (e.g., show a message to the user) + # Safety Check: Check z=15 is high position of stage. + if not self._is_z_safe_pos(stage_sn, x, y, z): + logger.warning(f"Invalid z position for stage {stage_sn}") + return + # Use the confirm_move_stage function to ask for confirmation if self._confirm_move_stage(x, y): # If the user confirms, proceed with moving the stage @@ -323,6 +327,41 @@ def _move_stage(self, stage_sn, move_type): # If the user cancels, do nothing print("Stage move canceled by user.") + def _is_z_safe_pos(self, stage_sn, x, y, z): + """ + Check if the Z=15 position is safe for the stage. (z=15 is the top of the stage) + + Args: + stage_sn (str): The serial number of the stage. + x (float): The x-coordinate of the stage. + y (float): The y-coordinate of the stage. + z (float): The z-coordinate (set to 15.0). + + Returns: + bool: True if the Z position is safe, False otherwise. + """ + # Z is inverted in the server + local_pts_z15 = [float(x)*1000, float(y)*1000, float(15.0 - z)*1000] # Should be top of the stage + local_pts_z0 = [float(x)*1000, float(y)*1000, 15.0*1000] # Should be bottom + for sn, item in self.model.transforms.items(): + if sn != stage_sn: + continue + + transM, scale = item[0], item[1] + if transM is not None: + try: + # Apply transformations to get global points for Z=15 and Z=0 + global_pts_z15 = self._apply_transformation(local_pts_z15, transM, scale) + global_pts_z0 = self._apply_transformation(local_pts_z0, transM, scale) + + # Ensure that Z=15 is higher than Z=0 and Z=15 is positive + if global_pts_z15[2] > global_pts_z0[2] and global_pts_z15[2] > 0: + return True + except Exception as e: + logger.error(f"Error applying transformation for stage {stage_sn}: {e}") + return False + return False + def _confirm_move_stage(self, x, y): """ Displays a confirmation dialog asking the user if they are sure about moving the stage. diff --git a/parallax/camera.py b/parallax/camera.py index 7fa16205..77456813 100755 --- a/parallax/camera.py +++ b/parallax/camera.py @@ -21,8 +21,7 @@ import PySpin except ImportError: PySpin = None - logger.warn("Could not import PySpin.") - + logger.warning("Could not import PySpin.") def list_cameras(dummy=False, version="V1"): """ @@ -254,22 +253,23 @@ def set_wb(self, channel, wb=1.2): Args: - wb (float): The desired white balance value. min:1.8, max:2.5 """ - if self.device_color_type == "Color": - self.node_wbauto_mode.SetIntValue( - self.node_wbauto_mode_off.GetValue() - ) - if channel == "Red": - self.node_balanceratio_mode.SetIntValue( - self.node_balanceratio_mode_red.GetValue() - ) - self.node_wb.SetValue(wb) - elif channel == "Blue": - self.node_balanceratio_mode.SetIntValue( - self.node_balanceratio_mode_blue.GetValue() + try: + if self.device_color_type == "Color": + self.node_wbauto_mode.SetIntValue( + self.node_wbauto_mode_off.GetValue() ) - self.node_wb.SetValue(wb) - else: - pass + if channel == "Red": + self.node_balanceratio_mode.SetIntValue( + self.node_balanceratio_mode_red.GetValue() + ) + self.node_wb.SetValue(wb) + elif channel == "Blue": + self.node_balanceratio_mode.SetIntValue( + self.node_balanceratio_mode_blue.GetValue() + ) + self.node_wb.SetValue(wb) + except Exception as e: + logger.error(f"An error occurred while setting the white balance: {e}") def get_wb(self, channel): """ @@ -302,8 +302,11 @@ def set_gamma(self, gamma=1.0): Args: - gamma (float): The desired gamma value. min:0.25 max:1.25 """ - self.node_gammaenable_mode.SetValue(True) - self.node_gamma.SetValue(gamma) + try: + self.node_gammaenable_mode.SetValue(True) + self.node_gamma.SetValue(gamma) + except Exception as e: + logger.error(f"An error occurred while setting the gamma: {e}") def disable_gamma(self): """ @@ -318,10 +321,13 @@ def set_gain(self, gain=20.0): Args: - gain (float): The desired gain value. min:0, max:27.0 """ - self.node_gainauto_mode.SetIntValue( - self.node_gainauto_mode_off.GetValue() - ) - self.node_gain.SetValue(gain) + try: + self.node_gainauto_mode.SetIntValue( + self.node_gainauto_mode_off.GetValue() + ) + self.node_gain.SetValue(gain) + except Exception as e: + logger.error(f"An error occurred while setting the gain: {e}") def get_gain(self): """ @@ -346,10 +352,13 @@ def set_exposure(self, expTime=16000): Args: - expTime (int): The desired exposure time in microseconds. """ - self.node_expauto_mode.SetIntValue( - self.node_expauto_mode_off.GetValue() - ) # Return back to manual mode - self.node_exptime.SetValue(expTime) + try: + self.node_expauto_mode.SetIntValue( + self.node_expauto_mode_off.GetValue() + ) # Return back to manual mode + self.node_exptime.SetValue(expTime) + except Exception as e: + logger.error(f"An error occurred while setting the exposure: {e}") def get_exposure(self): """ @@ -434,26 +443,30 @@ def begin_continuous_acquisition(self): print("Error: camera is already running") return -1 - # set acquisition mode continuous (continuous stream of images) - node_acquisition_mode = PySpin.CEnumerationPtr( - self.node_map.GetNode("AcquisitionMode") - ) - node_acquisition_mode_continuous = node_acquisition_mode.GetEntryByName( - "Continuous" - ) - acquisition_mode_continuous = ( - node_acquisition_mode_continuous.GetValue() - ) - node_acquisition_mode.SetIntValue(acquisition_mode_continuous) - - # Begin Acquisition: Image acquisition must be ended when no more images are needed. - self.camera.BeginAcquisition() - logger.debug(f"BeginAcquisition {self.name(sn_only=True)} ") - self.running = True - self.capture_thread = threading.Thread( - target=self.capture_loop, daemon=True - ) - self.capture_thread.start() + try: + # set acquisition mode continuous (continuous stream of images) + node_acquisition_mode = PySpin.CEnumerationPtr( + self.node_map.GetNode("AcquisitionMode") + ) + node_acquisition_mode_continuous = node_acquisition_mode.GetEntryByName( + "Continuous" + ) + acquisition_mode_continuous = ( + node_acquisition_mode_continuous.GetValue() + ) + node_acquisition_mode.SetIntValue(acquisition_mode_continuous) + + # Begin Acquisition: Image acquisition must be ended when no more images are needed. + self.camera.BeginAcquisition() + logger.debug(f"BeginAcquisition {self.name(sn_only=True)} ") + self.running = True + self.capture_thread = threading.Thread( + target=self.capture_loop, daemon=True + ) + self.capture_thread.start() + except Exception as e: + logger.error(f"An error occurred while starting the camera: {e}") + print(f"Error: An error occurred while starting the camera {e}") def capture_loop(self): """ @@ -537,8 +550,7 @@ def get_last_capture_time(self, millisecond=False): ) def save_last_image( - self, filepath, isTimestamp=False, custom_name="Microscope_" - ): + self, filepath, isTimestamp=False, custom_name="Microscope_"): """ Saves the last captured image to the specified file path. diff --git a/parallax/probe_calibration.py b/parallax/probe_calibration.py index cf9f2eab..282d2638 100644 --- a/parallax/probe_calibration.py +++ b/parallax/probe_calibration.py @@ -7,7 +7,7 @@ import csv import logging import os - +import datetime import numpy as np import pandas as pd from PyQt5.QtCore import QObject, pyqtSignal @@ -89,8 +89,11 @@ def __init__(self, model, stage_listener): self.origin, self.R, self.scale = None, None, np.array([1, 1, 1]) self.avg_err = None self.last_row = None + + # create file for points.csv + self.log_dir = None self._create_file() - + def reset_calib(self, sn=None): """ Resets calibration to its initial state, clearing any stored min and max values. @@ -119,9 +122,10 @@ def _create_file(self): Creates or clears the CSV file used to store local and global points during calibration. """ package_dir = os.path.dirname(os.path.abspath(__file__)) - debug_dir = os.path.join(os.path.dirname(package_dir), "debug") - os.makedirs(debug_dir, exist_ok=True) - self.csv_file = os.path.join(debug_dir, "points.csv") + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + self.log_dir = os.path.join(os.path.dirname(package_dir), f"debug/log_{timestamp}") + os.makedirs(self.log_dir, exist_ok=True) # Create the log directory if it doesn't exist + self.csv_file = os.path.join(self.log_dir, "points.csv") # Check if the file exists and remove it if it does if os.path.exists(self.csv_file): @@ -523,11 +527,12 @@ def _save_df_to_csv(self, df, file_name): Args: filtered_df (pd.DataFrame): DataFrame containing filtered local and global points. """ + if self.log_dir is None: + logger.error("log_dir is not initialized.") + return + # Save the updated DataFrame back to the CSV file - package_dir = os.path.dirname(os.path.abspath(__file__)) - debug_dir = os.path.join(os.path.dirname(package_dir), "debug") - os.makedirs(debug_dir, exist_ok=True) - csv_file = os.path.join(debug_dir, file_name) + csv_file = os.path.join(self.log_dir, file_name) df.to_csv(csv_file, index=False) return csv_file @@ -618,7 +623,8 @@ def update(self, stage, debug_info=None): def complete_calibration(self, filtered_df): # save the filtered points to a new file print("ProbeCalibration: complete_calibration") - self.file_name = f"points_{self.stage.sn}.csv" + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + self.file_name = f"points_{self.stage.sn}_{timestamp}.csv" self.transM_LR = self._get_transM(filtered_df, save_to_csv=True, file_name=self.file_name, noise_threshold=20) if self.transM_LR is None: @@ -628,7 +634,7 @@ def complete_calibration(self, filtered_df): self._print_formatted_transM() print("=========================================================") self._update_info_ui(disp_avg_error=True, save_to_csv=True, \ - file_name = f"transM_{self.stage.sn}.csv") + file_name = f"transM_{self.stage.sn}_{timestamp}.csv") if self.model.bundle_adjustment: self.old_transM, self.old_scale = self.transM_LR, self.scale @@ -639,7 +645,7 @@ def complete_calibration(self, filtered_df): self._print_formatted_transM() print("=========================================================") self._update_info_ui(disp_avg_error=True, save_to_csv=True, \ - file_name = f"transM_BA_{self.stage.sn}.csv") + file_name = f"transM_BA_{self.stage.sn}_{timestamp}.csv") else: return diff --git a/parallax/screen_coords_mapper.py b/parallax/screen_coords_mapper.py index 01d27655..dab11eee 100644 --- a/parallax/screen_coords_mapper.py +++ b/parallax/screen_coords_mapper.py @@ -26,7 +26,7 @@ def _clicked_position(self, camera_name, pos): global_coords = self._get_global_coords_BA(camera_name, pos) if global_coords is None: return - + global_coords = np.round(global_coords*1000, decimals=1) reticle_name = self.reticle_selector.currentText() if "Proj" not in reticle_name: diff --git a/parallax/stage_controller.py b/parallax/stage_controller.py index ad51cd6c..299b3ee5 100644 --- a/parallax/stage_controller.py +++ b/parallax/stage_controller.py @@ -98,7 +98,9 @@ def move_request(self, command): def _check_z_position(self, probe_index, target_z, command): """Check Z position and proceed with X, Y movement once target is reached.""" self.timer_count += 1 - if self.timer_count > 30: # 30 * 500 ms = 15 seconds + # Outside software might control the stage and never reached to z target. + # Thus, stop the timer after 20 seconds. + if self.timer_count > 40: # 40 * 500 ms = 20 seconds if hasattr(self, 'timer') and self.timer.isActive(): self.timer.stop() logger.warning("Timer stopped due to timeout.") diff --git a/ui/calc.ui b/ui/calc.ui index 85598f30..7c05ff4b 100644 --- a/ui/calc.ui +++ b/ui/calc.ui @@ -6,8 +6,8 @@ 0 0 - 890 - 347 + 953 + 638 @@ -39,6 +39,23 @@ QPushButton#startButton:disabled:checked { } QPushButton#startButton:disabled:!checked { background-color: lightGreen; +} +QMessageBox { + background-color: rgb(00,00,00); + color: #FFFFFF; +} +QMessageBox QLabel { + color: #FFFFFF; +} +QMessageBox QPushButton { + background-color: rgb(50,50,50); + color: #FFFFFF; +} +QMessageBox QPushButton:hover { + background-color: rgb(100, 30, 30); +} +QMessageBox QPushButton:pressed { + background-color: rgb(224, 0, 0); } @@ -46,8 +63,8 @@ QPushButton#startButton:disabled:!checked { 10 10 - 861 - 621 + 921 + 601 @@ -105,7 +122,7 @@ QPushButton#startButton:disabled:!checked { } - (x, y, z) + (x, y, z) Qt::AlignCenter @@ -132,6 +149,40 @@ QPushButton#startButton:disabled:!checked { 10 + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + resources/stop-sign.pngresources/stop-sign.png + + + + 64 + 64 + + + + + + diff --git a/ui/calc_QGroupBox.ui b/ui/calc_QGroupBox.ui index 8c9ac62e..fa8a18ed 100644 --- a/ui/calc_QGroupBox.ui +++ b/ui/calc_QGroupBox.ui @@ -6,14 +6,14 @@ 0 0 - 829 - 123 + 914 + 130 750 - 100 + 130 @@ -41,13 +41,31 @@ QPushButton#startButton:disabled:checked { } QPushButton#startButton:disabled:!checked { background-color: lightGreen; +} + +QMessageBox { + background-color: rgb(00,00,00); + color: #FFFFFF; +} +QMessageBox QLabel { + color: #FFFFFF; +} +QMessageBox QPushButton { + background-color: rgb(50,50,50); + color: #FFFFFF; +} +QMessageBox QPushButton:hover { + background-color: rgb(100, 30, 30); +} +QMessageBox QPushButton:pressed { + background-color: rgb(224, 0, 0); } - 140 - 40 + 130 + 58 100 40 @@ -72,8 +90,8 @@ QPushButton#startButton:disabled:!checked { - 250 - 40 + 240 + 58 100 40 @@ -98,8 +116,8 @@ QPushButton#startButton:disabled:!checked { - 30 - 40 + 20 + 58 100 40 @@ -124,8 +142,8 @@ QPushButton#startButton:disabled:!checked { - 410 - 40 + 400 + 58 100 40 @@ -145,8 +163,8 @@ QPushButton#startButton:disabled:!checked { - 360 - 50 + 350 + 68 40 21 @@ -175,8 +193,8 @@ QPushButton#startButton:disabled:!checked { - 630 - 40 + 620 + 58 100 40 @@ -202,8 +220,8 @@ QPushButton#startButton:disabled:!checked { - 520 - 40 + 510 + 58 100 40 @@ -229,9 +247,9 @@ QPushButton#startButton:disabled:!checked { - 750 - 50 - 61 + 740 + 68 + 51 23 @@ -250,6 +268,34 @@ QPushButton#startButton:disabled:!checked { Clear + + + + 810 + 58 + 61 + 41 + + + + + 7 + + + + + + + + resources/move_xy0.pngresources/move_xy0.png + + + + 64 + 64 + + + globalX diff --git a/ui/calc_QGroupBox_move.ui b/ui/calc_QGroupBox_move.ui deleted file mode 100644 index 0e27780c..00000000 --- a/ui/calc_QGroupBox_move.ui +++ /dev/null @@ -1,311 +0,0 @@ - - - SN - - - - 0 - 0 - 914 - 123 - - - - - 750 - 100 - - - - GroupBox - - - QWidget{ -background-color: rgb(00,00,00); -color: #FFFFFF; -} -QPushButton{ - background-color: black; -} - QPushButton:pressed { - background-color: rgb(224, 0, 0); -} -QPushButton:hover { - background-color: rgb(100, 30, 30); -} -QPushButton#startButton:disabled:checked { - color: gray; -} -QPushButton#startButton:disabled:checked { - background-color: #ffaaaa; -} -QPushButton#startButton:disabled:!checked { - background-color: lightGreen; -} - -QMessageBox { - background-color: rgb(00,00,00); - color: #FFFFFF; -} -QMessageBox QLabel { - color: #FFFFFF; -} -QMessageBox QPushButton { - background-color: rgb(50,50,50); - color: #FFFFFF; -} -QMessageBox QPushButton:hover { - background-color: rgb(100, 30, 30); -} -QMessageBox QPushButton:pressed { - background-color: rgb(224, 0, 0); -} - - - - - 140 - 40 - 100 - 40 - - - - - 100 - 40 - - - - - 8 - - - - QLineEdit { - color: yellow; -} - - - - - - 250 - 40 - 100 - 40 - - - - - 100 - 40 - - - - - 8 - - - - QLineEdit { - color: yellow; -} - - - - - - 30 - 40 - 100 - 40 - - - - - 100 - 40 - - - - - 8 - - - - QLineEdit { - color: yellow; -} - - - - - - 410 - 40 - 100 - 40 - - - - - 100 - 40 - - - - - 8 - - - - - - - 360 - 50 - 40 - 21 - - - - - 40 - 0 - - - - - 40 - 16777215 - - - - - 12 - - - - - - - - - - 630 - 40 - 100 - 40 - - - - - 100 - 40 - - - - - 200 - 16777215 - - - - - 8 - - - - - - - 520 - 40 - 100 - 40 - - - - - 100 - 40 - - - - - 200 - 16777215 - - - - - 8 - - - - - - - 750 - 50 - 51 - 23 - - - - - 40 - 0 - - - - - 7 - - - - Clear - - - - - - 820 - 40 - 61 - 41 - - - - - 7 - - - - - - - - resources/move_xy0.pngresources/move_xy0.png - - - - 64 - 64 - - - - - - globalX - globalY - globalZ - convert - localX - localY - localZ - - - - diff --git a/ui/calc_move.ui b/ui/calc_move.ui deleted file mode 100644 index 02feeb39..00000000 --- a/ui/calc_move.ui +++ /dev/null @@ -1,206 +0,0 @@ - - - Form - - - - 0 - 0 - 953 - 638 - - - - Form - - - - resources/calc.pngresources/calc.png - - - QWidget{ -background-color: rgb(00,00,00); -color: #FFFFFF; -} -QPushButton{ - background-color: black; -} - QPushButton:pressed { - background-color: rgb(224, 0, 0); -} -QPushButton:hover { - background-color: rgb(100, 30, 30); -} -QPushButton#startButton:disabled:checked { - color: gray; -} -QPushButton#startButton:disabled:checked { - background-color: #ffaaaa; -} -QPushButton#startButton:disabled:!checked { - background-color: lightGreen; -} -QMessageBox { - background-color: rgb(00,00,00); - color: #FFFFFF; -} -QMessageBox QLabel { - color: #FFFFFF; -} -QMessageBox QPushButton { - background-color: rgb(50,50,50); - color: #FFFFFF; -} -QMessageBox QPushButton:hover { - background-color: rgb(100, 30, 30); -} -QMessageBox QPushButton:pressed { - background-color: rgb(224, 0, 0); -} - - - - - 10 - 10 - 921 - 601 - - - - - 10 - - - - - - - - 16777215 - 50 - - - - QLabel { - color: yellow; -} - - - Global - - - Qt::AlignCenter - - - - - - - - 16777215 - 50 - - - - Local - - - Qt::AlignCenter - - - - - - - - - - - QLabel { - color: yellow; -} - - - (x, y, z) - - - Qt::AlignCenter - - - - - - - (x, y, z) - - - Qt::AlignCenter - - - - - - - - - 20 - - - 10 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - resources/stop-sign.pngresources/stop-sign.png - - - - 64 - 64 - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - diff --git a/ui/reticle_QGroupBox.ui b/ui/reticle_QGroupBox.ui index 6c91580c..40a8a663 100644 --- a/ui/reticle_QGroupBox.ui +++ b/ui/reticle_QGroupBox.ui @@ -6,7 +6,7 @@ 0 0 - 1109 + 829 150 @@ -142,11 +142,11 @@ QPushButton#startButton:disabled:!checked { - 7 + 8 - RotDegrees + Rotate Degrees (CCW) @@ -160,7 +160,7 @@ QPushButton#startButton:disabled:!checked { - 7 + 8 @@ -200,11 +200,11 @@ QPushButton#startButton:disabled:!checked { - 7 + 8 - ReticleName + Reticle Name @@ -218,7 +218,7 @@ QPushButton#startButton:disabled:!checked { - 7 + 8 @@ -236,7 +236,7 @@ QPushButton#startButton:disabled:!checked { - 7 + 8 @@ -248,16 +248,27 @@ QPushButton#startButton:disabled:!checked { 770 70 - 30 - 31 + 40 + 40 + + + 40 + 40 + + - 30 - 16777215 + 40 + 40 + + + 10 + + - diff --git a/ui/reticle_metadata.ui b/ui/reticle_metadata.ui index cd3832e3..0d2ff551 100644 --- a/ui/reticle_metadata.ui +++ b/ui/reticle_metadata.ui @@ -6,8 +6,8 @@ 0 0 - 890 - 183 + 881 + 303 @@ -52,7 +52,7 @@ QPushButton#startButton:disabled:!checked { 10 10 - 861 + 851 831 @@ -89,6 +89,11 @@ QPushButton#startButton:disabled:!checked { 16777215 + + + 15 + + + @@ -102,6 +107,11 @@ QPushButton#startButton:disabled:!checked { 16777215 + + + 10 + + Update