diff --git a/parallax/__init__.py b/parallax/__init__.py
index ea068cb..88f2bfe 100644
--- a/parallax/__init__.py
+++ b/parallax/__init__.py
@@ -4,7 +4,7 @@
import os
-__version__ = "0.37.22"
+__version__ = "0.37.23"
# allow multiple OpenMP instances
os.environ["KMP_DUPLICATE_LIB_OK"] = "True"
diff --git a/parallax/calculator.py b/parallax/calculator.py
new file mode 100644
index 0000000..98b0248
--- /dev/null
+++ b/parallax/calculator.py
@@ -0,0 +1,186 @@
+import os
+import logging
+import numpy as np
+from PyQt5.QtWidgets import QWidget, QGroupBox, QLineEdit, QPushButton
+from PyQt5.uic import loadUi
+from PyQt5.QtCore import Qt
+
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
+
+package_dir = os.path.dirname(os.path.abspath(__file__))
+debug_dir = os.path.join(os.path.dirname(package_dir), "debug")
+ui_dir = os.path.join(os.path.dirname(package_dir), "ui")
+
+class Calculator(QWidget):
+ def __init__(self, model):
+ super().__init__()
+ self.model = model
+
+ 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)
+
+ # Create the number of GroupBox for the number of stages
+ self.create_stage_groupboxes()
+ self.model.add_calc_instance(self)
+
+ def show(self):
+ # Refresh the list of stage to show
+ self.set_calc_functions()
+ # Show
+ super().show() # Show the widget
+
+ def set_calc_functions(self):
+ for stage_sn, item in self.model.transforms.items():
+ transM, scale = item[0], item[1]
+ if transM is not None: # Set calc function for calibrated stages
+ push_button = self.findChild(QPushButton, f"convert_{stage_sn}")
+ if not push_button:
+ logger.warning(f"Error: QPushButton for {stage_sn} not found")
+ continue
+ self.enable(stage_sn)
+ push_button.clicked.connect(self.create_convert_function(stage_sn, transM, scale))
+ else: # Block calc functions for uncalibrated stages
+ self.disable(stage_sn)
+
+ def create_convert_function(self, stage_sn, transM, scale):
+ logger.debug(f"\n=== Creating convert function ===")
+ logger.debug(f"Stage SN: {stage_sn}")
+ logger.debug(f"transM: {transM}")
+ logger.debug(f"scale: {scale}")
+ return lambda: self.convert(stage_sn, transM, scale)
+
+ def convert(self, sn, transM, scale):
+ # Enable the groupBox for the stage
+ globalX = self.findChild(QLineEdit, f"globalX_{sn}").text()
+ globalY = self.findChild(QLineEdit, f"globalY_{sn}").text()
+ globalZ = self.findChild(QLineEdit, f"globalZ_{sn}").text()
+ localX = self.findChild(QLineEdit, f"localX_{sn}").text()
+ localY = self.findChild(QLineEdit, f"localY_{sn}").text()
+ localZ = self.findChild(QLineEdit, f"localZ_{sn}").text()
+
+ logger.debug("- Convert -")
+ logger.debug(f"Global: {globalX}, {globalY}, {globalZ}")
+ logger.debug(f"Local: {localX}, {localY}, {localZ}")
+ trans_type, local_pts, global_pts = self.get_transform_type(globalX, globalY, globalZ, localX, localY, localZ)
+ if trans_type == "global_to_local":
+ local_pts_ret = self.apply_inverse_transformation(global_pts, transM, scale)
+ self.show_local_pts_result(sn, local_pts_ret)
+ elif trans_type == "local_to_global":
+ global_pts_ret = self.apply_transformation(local_pts, transM, scale)
+ self.show_global_pts_result(sn, global_pts_ret)
+ else:
+ logger.warning(f"Error: Invalid transforsmation type for {sn}")
+ return
+
+ def show_local_pts_result(self, sn, local_pts):
+ # Show the local points in the QLineEdit
+ self.findChild(QLineEdit, f"localX_{sn}").setText(f"{local_pts[0]:.2f}")
+ self.findChild(QLineEdit, f"localY_{sn}").setText(f"{local_pts[1]:.2f}")
+ self.findChild(QLineEdit, f"localZ_{sn}").setText(f"{local_pts[2]:.2f}")
+
+ def show_global_pts_result(self, sn, global_pts):
+ self.findChild(QLineEdit, f"globalX_{sn}").setText(f"{global_pts[0]:.2f}")
+ self.findChild(QLineEdit, f"globalY_{sn}").setText(f"{global_pts[1]:.2f}")
+ self.findChild(QLineEdit, f"globalZ_{sn}").setText(f"{global_pts[2]:.2f}")
+
+ def get_transform_type(self, globalX, globalY, globalZ, localX, localY, localZ):
+ def is_valid_number(s):
+ try:
+ float(s)
+ return True
+ except ValueError:
+ return False
+
+ # Strip any whitespace or tabs from the inputs
+ globalX = globalX.strip() if globalX else ""
+ globalY = globalY.strip() if globalY else ""
+ globalZ = globalZ.strip() if globalZ else ""
+ localX = localX.strip() if localX else ""
+ localY = localY.strip() if localY else ""
+ localZ = localZ.strip() if localZ else ""
+
+ # Check if all global and local coordinates are valid numbers
+ global_valid = all(is_valid_number(val) for val in [globalX, globalY, globalZ])
+ local_valid = all(is_valid_number(val) for val in [localX, localY, localZ])
+
+ if global_valid and local_valid:
+ # Convert to float
+ global_pts = [float(globalX), float(globalY), float(globalZ)]
+ local_pts = [float(localX), float(localY), float(localZ)]
+ return "global_to_local", local_pts, global_pts
+ elif local_valid:
+ local_pts = [float(localX), float(localY), float(localZ)]
+ return "local_to_global", local_pts, None
+ elif global_valid:
+ global_pts = [float(globalX), float(globalY), float(globalZ)]
+ return "global_to_local", None, global_pts
+ else:
+ return None, None, None
+
+ def apply_transformation(self, local_point_, transM_LR, scale):
+ local_point = local_point_ * scale
+ local_point = np.append(local_point, 1)
+ global_point = np.dot(transM_LR, local_point)
+ logger.debug(f"local_to_global: {local_point_} -> {global_point[:3]}")
+ logger.debug(f"R: {transM_LR[:3, :3]}\nT: {transM_LR[:3, 3]}")
+ return global_point[:3]
+
+ def apply_inverse_transformation(self, global_point, transM_LR, scale):
+ # Transpose the 3x3 rotation part
+ R_T = transM_LR[:3, :3].T
+ local_point = np.dot(R_T, global_point - transM_LR[:3, 3])
+ logger.debug(f"global_to_local {global_point} -> {local_point / scale}")
+ logger.debug(f"R.T: {R_T}\nT: {transM_LR[:3, 3]}")
+ return local_point / scale
+
+ def disable(self, sn):
+ # Clear the QLineEdit for the stage
+ self.findChild(QLineEdit, f"localX_{sn}").setText(f"")
+ self.findChild(QLineEdit, f"localY_{sn}").setText(f"")
+ self.findChild(QLineEdit, f"localZ_{sn}").setText(f"")
+ self.findChild(QLineEdit, f"globalX_{sn}").setText(f"")
+ self.findChild(QLineEdit, f"globalY_{sn}").setText(f"")
+ self.findChild(QLineEdit, f"globalZ_{sn}").setText(f"")
+
+ # Find the QGroupBox for the stage
+ group_box = self.findChild(QGroupBox, f"groupBox_{sn}")
+ group_box.setEnabled(False)
+ group_box.setStyleSheet("background-color: #333333;")
+ group_box.setTitle(f"{sn} (Uncalibrated)")
+
+ def enable(self, sn):
+ # Find the QGroupBox for the stage
+ group_box = self.findChild(QGroupBox, f"groupBox_{sn}")
+ if not group_box.isEnabled():
+ group_box.setEnabled(True)
+ group_box.setStyleSheet("background-color: black;")
+ group_box.setTitle(f"{sn}")
+
+ def create_stage_groupboxes(self):
+ # Loop through the number of stages and create copies of groupBoxStage
+ 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)
+
+ # Set the visible title of the QGroupBox to sn
+ group_box.setTitle(f"{sn}")
+
+ # Append _{sn} to the QGroupBox object name
+ group_box.setObjectName(f"groupBox_{sn}")
+
+ # Find all QLineEdits and QPushButtons in the group_box and rename them
+ # globalX -> globalX_{sn} ..
+ # localX -> localX_{sn} ..
+ for line_edit in group_box.findChildren(QLineEdit):
+ line_edit.setObjectName(f"{line_edit.objectName()}_{sn}")
+
+ # convert -> convert_{sn}
+ for push_button in group_box.findChildren(QPushButton):
+ push_button.setObjectName(f"{push_button.objectName()}_{sn}")
+
+ # Add the newly created QGroupBox to the layout
+ self.ui.verticalLayout_QBox.addWidget(group_box)
\ No newline at end of file
diff --git a/parallax/main_window_wip.py b/parallax/main_window_wip.py
index a5016e0..8dda4bb 100644
--- a/parallax/main_window_wip.py
+++ b/parallax/main_window_wip.py
@@ -183,6 +183,7 @@ def refresh_stages(self):
"""Search for connected stages"""
if not self.dummy:
self.model.scan_for_usb_stages()
+ self.model.init_transforms()
def record_button_handler(self):
"""
@@ -775,4 +776,5 @@ def save_user_configs(self):
def closeEvent(self, event):
self.model.close_all_point_meshes()
+ self.model.close_clac_instance()
event.accept()
\ No newline at end of file
diff --git a/parallax/model.py b/parallax/model.py
index efb9e37..1960e59 100755
--- a/parallax/model.py
+++ b/parallax/model.py
@@ -28,6 +28,9 @@ def __init__(self, version="V1", bundle_adjustment=False):
# point mesh
self.point_mesh_instances = {}
+ # Calculator
+ self.calc_instance = None
+
# stage
self.nStages = 0
self.stages = {}
@@ -46,18 +49,9 @@ def __init__(self, version="V1", bundle_adjustment=False):
self.calibrations = {}
self.coords_debug = {}
- self.cal_in_progress = False
- self.accutest_in_progress = False
- self.lcorr, self.rcorr = False, False
-
- self.img_point_last = None
- self.obj_point_last = None
+ # Transformation matrices of stages to global coords
self.transforms = {}
- def set_last_object_point(self, obj_point):
- """Set the last object point."""
- self.obj_point_last = obj_point
-
def add_calibration(self, cal):
"""Add a calibration."""
self.calibrations[cal.name] = cal
@@ -66,27 +60,15 @@ def set_calibration(self, calibration):
"""Set the calibration."""
self.calibration = calibration
- def set_lcorr(self, xc, yc):
- """Set left coordinates."""
- self.lcorr = [xc, yc]
-
- def clear_lcorr(self):
- """Clear left coordinates."""
- self.lcorr = False
-
- def set_rcorr(self, xc, yc):
- """Set right coordinates."""
- self.rcorr = [xc, yc]
-
- def clear_rcorr(self):
- """Clear right coordinates."""
- self.rcorr = False
-
def init_stages(self):
"""Initialize stages."""
self.stages = {}
self.stages_calib = {}
+ def init_transforms(self):
+ for stage_sn in self.stages.keys():
+ self.transforms[stage_sn] = [None, None]
+
def add_video_source(self, video_source):
"""Add a video source."""
self.cameras.append(video_source)
@@ -134,7 +116,16 @@ def get_stage(self, stage_sn):
return self.stages.get(stage_sn)
def add_stage_calib_info(self, stage_sn, info):
- """Add a stage."""
+ """Add a stage.
+ info['detection_status']
+ info['transM']
+ info['L2_err']
+ info['scale']
+ info['dist_traveled']
+ info['status_x']
+ info['status_y']
+ info['status_z']
+ """
self.stages_calib[stage_sn] = info
def get_stage_calib_info(self, stage_sn):
@@ -145,6 +136,10 @@ def reset_stage_calib_info(self):
"""Reset stage calibration info."""
self.stages_calib = {}
+ def add_transform(self, stage_sn, transform, scale):
+ """Add transformation matrix between local to global coordinates."""
+ self.transforms[stage_sn] = [transform, scale]
+
def add_probe_detector(self, probeDetector):
"""Add a probe detector."""
self.probeDetectors.append(probeDetector)
@@ -154,7 +149,7 @@ def reset_coords_intrinsic_extrinsic(self):
self.coords_axis = {}
self.camera_intrinsic = {}
self.camera_extrinsic = {}
-
+
def add_pos_x(self, camera_name, pt):
"""Add position x."""
self.pos_x[camera_name] = pt
@@ -220,4 +215,12 @@ def add_point_mesh_instance(self, instance):
def close_all_point_meshes(self):
for instance in self.point_mesh_instances.values():
instance.close()
- self.point_mesh_instances.clear()
\ No newline at end of file
+ self.point_mesh_instances.clear()
+
+ def add_calc_instance(self, instance):
+ self.calc_instance = instance
+
+ def close_clac_instance(self):
+ if self.calc_instance is not None:
+ self.calc_instance.close()
+ self.calc_instance = None
\ No newline at end of file
diff --git a/parallax/point_mesh.py b/parallax/point_mesh.py
index 2d54236..ecf0f75 100644
--- a/parallax/point_mesh.py
+++ b/parallax/point_mesh.py
@@ -71,7 +71,6 @@ def _parse_csv(self):
self.local_pts_org = self.df[['local_x', 'local_y', 'local_z']].values
self.local_pts = self._local_to_global(self.local_pts_org, self.R[self.sn], self.T[self.sn], self.S[self.sn])
self.points_dict['local_pts'] = self.local_pts
- print(self.R[self.sn], self.T[self.sn], self.S[self.sn])
self.global_pts = self.df[['global_x', 'global_y', 'global_z']].values
self.points_dict['global_pts'] = self.global_pts
diff --git a/parallax/probe_calibration.py b/parallax/probe_calibration.py
index 2a5485d..c1bd8ad 100644
--- a/parallax/probe_calibration.py
+++ b/parallax/probe_calibration.py
@@ -152,14 +152,16 @@ def clear(self, sn = None):
"""
Clears all stored data and resets the transformation matrix to its default state.
"""
+ self.model_LR, self.transM_LR, self.transM_LR_prev = None, None, None
+ self.scale = np.array([1, 1, 1])
+
if sn is None:
self._create_file()
else:
self.df = pd.read_csv(self.csv_file)
self.df = self.df[self.df["sn"] != sn]
self.df.to_csv(self.csv_file, index=False)
- self.model_LR, self.transM_LR, self.transM_LR_prev = None, None, None
- self.scale = np.array([1, 1, 1])
+ self.model.add_transform(sn, self.transM_LR, self.scale)
def _remove_duplicates(self, df):
# Drop duplicate rows based on 'ts_local_coords', 'global_x', 'global_y', 'global_z' columns
@@ -264,6 +266,7 @@ def _get_transM_LR_orthogonal(self, local_points, global_points, remove_noise=Tr
def _get_transM(self, df, remove_noise=True, save_to_csv=False, file_name=None, noise_threshold=40):
local_points, global_points = self._get_local_global_points(df)
+ valid_indices = np.ones(len(local_points), dtype=bool) # Initialize valid_indices as a mask with all True values
if remove_noise:
if self._is_criteria_met_points_min_max() and len(local_points) > 10 \
@@ -598,7 +601,8 @@ def update(self, stage, debug_info=None):
self._update_local_global_point(debug_info) # Do no update if it is duplicates
filtered_df = self._filter_df_by_sn(self.stage.sn)
- self.transM_LR = self._get_transM(filtered_df)
+ self.transM_LR = self._get_transM(filtered_df, noise_threshold=100) # TODO original
+ #self.transM_LR = self._get_transM(filtered_df, remove_noise=False) # Test
if self.transM_LR is None:
return
@@ -615,7 +619,9 @@ def update(self, stage, debug_info=None):
def complete_calibration(self, filtered_df):
# save the filtered points to a new file
self.file_name = f"points_{self.stage.sn}.csv"
- self.transM_LR = self._get_transM(filtered_df, save_to_csv=True, file_name=self.file_name, noise_threshold=20)
+ self.transM_LR = self._get_transM(filtered_df, save_to_csv=True, file_name=self.file_name, noise_threshold=20) # TODO original
+ #self.transM_LR = self._get_transM(filtered_df, save_to_csv=True, file_name=self.file_name, remove_noise=False) # Test
+
if self.transM_LR is None:
return
@@ -638,6 +644,9 @@ def complete_calibration(self, filtered_df):
else:
return
+ # Register into model
+ self.model.add_transform(self.stage.sn, self.transM_LR, self.scale)
+
# Emit the signal to indicate that calibration is complete
self.calib_complete.emit(self.stage.sn, self.transM_LR, self.scale)
logger.debug(
diff --git a/parallax/probe_detect_manager.py b/parallax/probe_detect_manager.py
index 96fcbbd..2b863f5 100644
--- a/parallax/probe_detect_manager.py
+++ b/parallax/probe_detect_manager.py
@@ -264,11 +264,11 @@ def run(self):
logger.debug("probe_detect_manager running ")
while self.running:
if self.new:
- self.frame = self.process_draw_reticle(self.frame)
if self.is_detection_on:
self.frame, self.timestamp = self.process(
self.frame, self.timestamp
)
+ self.frame = self.process_draw_reticle(self.frame)
self.frame_processed.emit(self.frame)
self.new = False
time.sleep(0.001)
diff --git a/parallax/probe_detector.py b/parallax/probe_detector.py
index 06248a1..586ae1c 100644
--- a/parallax/probe_detector.py
+++ b/parallax/probe_detector.py
@@ -315,19 +315,22 @@ def _get_probe_point(self, mask, p1, p2, img_fname=None):
- probe_base (tuple): Coordinates of the probe base.
"""
+ if mask is None:
+ mask = np.zeros((self.IMG_SIZE[1], self.IMG_SIZE[0]), dtype=np.uint8)
+
+
mask = cv2.copyMakeBorder(
mask, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=[0, 0, 0]
)
- dist_transform = cv2.distanceTransform(mask, cv2.DIST_L2, 3)
- """
- if img_fname:
- mask_ = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
- cv2.circle(mask_, p1, 5, (0, 255, 0), -1) # Green circle
- cv2.circle(mask_, p2, 5, (0, 0, 255), -1) # Green circle
-
- output_fname = os.path.basename(img_fname).replace('.', '_mask.')
- cv2.imwrite('output/' + output_fname, mask_)
+ """
+ # TODO Draw a border inside the mask with the specified thickness
+ border_size = 200
+ cv2.rectangle(mask, (border_size, border_size),
+ (mask.shape[1] - border_size - 1, mask.shape[0] - border_size - 1),
+ 0, border_size)
"""
+ dist_transform = cv2.distanceTransform(mask, cv2.DIST_L2, 3)
+
dist_p1 = dist_transform[p1[1], p1[0]] # [y, x]
dist_p2 = dist_transform[p2[1], p2[0]]
logger.debug(f"dist_p1: {dist_p1}, dist_p2: {dist_p2}")
diff --git a/parallax/probe_fine_tip_detector.py b/parallax/probe_fine_tip_detector.py
index aae7599..b3b9ec5 100644
--- a/parallax/probe_fine_tip_detector.py
+++ b/parallax/probe_fine_tip_detector.py
@@ -46,12 +46,18 @@ def _is_valid(cls, img):
f"get_probe_precise_tip fail. N of contours_boundary :{len(contours_boundary)}"
)
return False
-
+ """
boundary_img = np.zeros_like(img)
boundary_img[0, 0] = 255
boundary_img[width - 1, 0] = 255
boundary_img[0, height - 1] = 255
boundary_img[width - 1, height - 1] = 255
+ """
+ boundary_img[0, 0] = 255
+ boundary_img[0, width - 1] = 255
+ boundary_img[height - 1, 0] = 255
+ boundary_img[height - 1, width - 1] = 255
+
and_result = cv2.bitwise_and(and_result, boundary_img)
contours_boundary, _ = cv2.findContours(
and_result, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
diff --git a/parallax/stage_widget.py b/parallax/stage_widget.py
index dd26365..3a12c65 100644
--- a/parallax/stage_widget.py
+++ b/parallax/stage_widget.py
@@ -19,6 +19,7 @@
from .probe_calibration import ProbeCalibration
from .stage_listener import StageListener
from .stage_ui import StageUI
+from .calculator import Calculator
logger = logging.getLogger(__name__)
logger.setLevel(logging.WARNING)
@@ -89,6 +90,14 @@ def __init__(self, model, ui_dir, screen_widgets):
self.view_trajectory_button_handler
)
+ # Calculation Button
+ self.calculation_btn = self.probe_calib_widget.findChild(
+ QPushButton, "calculation_btn"
+ )
+ self.calculation_btn.clicked.connect(
+ self.calculation_button_handler
+ )
+
# Reticle Widget
self.reticle_detection_status = (
"default" # options: default, process, detected, accepted, request_axis
@@ -151,6 +160,10 @@ def __init__(self, model, ui_dir, screen_widgets):
self.filter = "no_filter"
logger.debug(f"filter: {self.filter}")
+ # Calculator
+ self.calculation_btn.hide()
+ self.calculator = Calculator(self.model)
+
def reticle_detection_button_handler(self):
"""
Handles clicks on the reticle detection button, initiating or canceling reticle detection.
@@ -738,6 +751,7 @@ def probe_detect_default_status_ui(self, sn = None):
""")
self.hide_x_y_z()
self.hide_trajectory_btn()
+ self.hide_calculation_btn()
self.probeCalibrationLabel.setText("")
self.probe_calibration_btn.setChecked(False)
@@ -759,10 +773,18 @@ def probe_detect_default_status_ui(self, sn = None):
self.filter = "no_filter"
logger.debug(f"filter: {self.filter}")
- # Reset the probe calibration status
- self.probeCalibration.clear(self.selected_stage_id)
- # update global coords. Set to '-' on UI
- self.stageListener.requestClearGlobalDataTransformM(sn = sn)
+ if sn is not None:
+ # Reset the probe calibration status
+ self.probeCalibration.clear(self.selected_stage_id)
+ # update global coords. Set to '-' on UI
+ self.stageListener.requestClearGlobalDataTransformM(sn = sn)
+ else: # Reset all probel calibration status
+ for sn in self.model.stages.keys():
+ self.probeCalibration.clear(sn)
+ self.stageListener.requestClearGlobalDataTransformM(sn = sn)
+
+ # Set as Uncalibrated
+ self.calculator.set_calc_functions()
def probe_detect_default_status(self, sn = None):
"""
@@ -840,6 +862,8 @@ def probe_detect_accepted_status(self, stage_sn, transformation_matrix, scale, s
self.hide_x_y_z()
if not self.viewTrajectory_btn.isVisible():
self.viewTrajectory_btn.show()
+ if not self.calculation_btn.isVisible():
+ self.calculation_btn.show()
if self.filter == "probe_detection":
for screen in self.screen_widgets:
camera_name = screen.get_camera_name()
@@ -895,6 +919,10 @@ def hide_trajectory_btn(self):
if self.viewTrajectory_btn.isVisible():
self.viewTrajectory_btn.hide()
+ def hide_calculation_btn(self):
+ if self.calculation_btn.isVisible():
+ self.calculation_btn.hide()
+
def calib_x_complete(self, switch_probe = False):
"""
Updates the UI to indicate that the calibration for the X-axis is complete.
@@ -1073,4 +1101,7 @@ def update_stages(self, prev_stage_id, curr_stage_id):
self.probe_detection_status = probe_detection_status
def view_trajectory_button_handler(self):
- self.probeCalibration.view_3d_trajectory(self.selected_stage_id)
\ No newline at end of file
+ self.probeCalibration.view_3d_trajectory(self.selected_stage_id)
+
+ def calculation_button_handler(self):
+ self.calculator.show()
\ No newline at end of file
diff --git a/ui/calc.ui b/ui/calc.ui
new file mode 100644
index 0000000..6a5315a
--- /dev/null
+++ b/ui/calc.ui
@@ -0,0 +1,155 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 819
+ 653
+
+
+
+ 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;
+}
+
+
+
+
+ 10
+ 10
+ 781
+ 621
+
+
+
+
+ 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::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/calc_QGroupBox.ui b/ui/calc_QGroupBox.ui
new file mode 100644
index 0000000..9bf4901
--- /dev/null
+++ b/ui/calc_QGroupBox.ui
@@ -0,0 +1,241 @@
+
+
+ SN
+
+
+
+ 0
+ 0
+ 803
+ 123
+
+
+
+
+ 700
+ 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;
+}
+
+
+
+
+ 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
+
+
+
+
+
+ globalX
+ globalY
+ globalZ
+ convert
+ localX
+ localY
+ localZ
+
+
+
+
diff --git a/ui/point_mesh.ui b/ui/point_mesh.ui
index 4299064..c755488 100644
--- a/ui/point_mesh.ui
+++ b/ui/point_mesh.ui
@@ -25,6 +25,10 @@
Form
+
+
+ resources/3D_map.pngresources/3D_map.png
+
QWidget{
background-color: rgb(0,0,0);
diff --git a/ui/probe_calib.ui b/ui/probe_calib.ui
index 53b875f..35d0833 100644
--- a/ui/probe_calib.ui
+++ b/ui/probe_calib.ui
@@ -7,7 +7,7 @@
0
0
541
- 784
+ 783
@@ -26,8 +26,8 @@
1
- -
-
+
-
+
false
@@ -44,7 +44,7 @@
- Y
+ Z
@@ -61,8 +61,21 @@
- -
-
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
false
@@ -79,12 +92,40 @@
- Z
+ X
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 200
+ 40
+
+
+
+
+ 300
+ 40
+
+
+
+ Probe Calibration
+
+
+ true
+
+
+
+ -
+
false
@@ -101,25 +142,12 @@
- X
+ Y
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
+
-
+
0
@@ -138,6 +166,9 @@
30
+
+ <html><head/><body><p><span style=" font-size:7pt;">Calculator</span></p></body></html>
+
QPushButton{
@@ -154,14 +185,18 @@ QPushButton:hover {
}
- 3D
+
+
+
+
+ resources/calc.pngresources/calc.png
false
- -
+
-
@@ -177,8 +212,8 @@ QPushButton:hover {
- -
-
+
-
+
0
@@ -187,21 +222,43 @@ QPushButton:hover {
- 200
- 40
+ 40
+ 30
- 300
- 40
+ 40
+ 30
+
+ <html><head/><body><p><span style=" font-size:7pt;">Trajectory</span></p></body></html>
+
+
+
+QPushButton{
+ color: white;
+ background-color: black;
+}
+
+QPushButton:pressed {
+ background-color: rgb(224, 0, 0);
+}
+
+QPushButton:hover {
+ background-color: rgb(100, 30, 30);
+}
+
- Probe Calibration
+
+
+
+
+ resources/3D_map.pngresources/3D_map.png
- true
+ false
diff --git a/ui/resources/3D_map.png b/ui/resources/3D_map.png
new file mode 100644
index 0000000..3ddfd93
Binary files /dev/null and b/ui/resources/3D_map.png differ
diff --git a/ui/resources/calc.png b/ui/resources/calc.png
new file mode 100644
index 0000000..0c53f0b
Binary files /dev/null and b/ui/resources/calc.png differ
diff --git a/ui/stage_info.ui b/ui/stage_info.ui
index 8215993..102a3bf 100644
--- a/ui/stage_info.ui
+++ b/ui/stage_info.ui
@@ -199,6 +199,11 @@
-
+
+ QLabel {
+ color: yellow;
+}
+
-
@@ -263,6 +268,11 @@
-
+
+ QLabel {
+ color: yellow;
+}
+
-
@@ -370,6 +380,11 @@
-
+
+ QLabel {
+ color: yellow;
+}
+
-