Skip to content

Commit

Permalink
Merge pull request #2 from BritishGeologicalSurvey/concat_sample_name
Browse files Browse the repository at this point in the history
Update CSV export formatting and scaling usability
  • Loading branch information
leorudczenko authored May 4, 2023
2 parents 88d2b26 + 382bfc9 commit f9d5a70
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 53 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,17 @@
User Interface with data preprocessing and data flow
---
+testing_mode: bool
+default_settings: dict
+default_settings: dict[str, Any]
+image_filepath: str
+csv_filepath: str
+point_colour: str
+status_bar_messages: dict
+status_bar_messages: dict[str, dict[str, Any]]
+graphics_view: GraphicsView
+graphics_scene: GraphicsScene
+table_model: TableModel
+table_view: TableView
+set_scale_dialog: SetScaleDialog
+main_input_widgets: list[QWidget]

+setup_ui_elements()
+set_colour_button_style()
Expand All @@ -65,6 +66,7 @@
+update_analysis_points()
+set_point_colour()
+toggle_scaling_mode()
+toggle_main_input_widgets(enable)
+clear_scale_clicked()
+set_scale(scale)
+get_point_settings(analysis_point, clicked_column_index)
Expand Down Expand Up @@ -155,9 +157,9 @@
QAbstractTableModel
Manage AnalysisPoint data
---
+headers: list
+_data: list
+editable_columns: list
+headers: list[str]
+_data: list[list[Any]]
+editable_columns: list[int]

+headerData(section, orientation, role)
+columnCount(*args)
Expand Down
Binary file modified class_relationship_diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 3 additions & 7 deletions instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ _Export the current analysis point data to a TACtool CSV file._

By default, the exported file will be a CSV file. However, you can add your own file extension to the filename if you wish to create a different file type, though this is not recommended.

Upon export, the **sample_name** and **id** columns will be concatenated into a single column labeled **Name**, using the character pattern **_#** to join them.

## User Interface Buttons

### Clear Points
Expand Down Expand Up @@ -84,19 +86,13 @@ _Note: The **Diameter** is measured in **µm**_

### Scale

The scale can be set in 1 of 2 ways.

**Set Scale Dialog**
To set the scale, complete the following steps:

- Press the _Set Scale_ button. A _Set Scale_ window will then open.
- The image on the main window will become slightly grey. This means you can now draw a line across the image. Start by clicking once to create the start of the line and clicking again to create the end of the line. Pressing the _Clear_ button will remove any current lines.
- Now you change the estimated distance in microns. To do this, either use the up/down arrows next to the _Distance_ input box, or type in your own value. It must be a whole number.
- Pressing _OK_ will confirm the new scale and close the _Set Scale_ window.

**Directly Setting the Scale Value**

Alternatively, if you already know the scale value you would like to use, you can input this by typing it into the _Scale_ input box on the main window, next to the _Set Scale_ button.

_Note: The **Scale** is measured in **Pixels per µm**_

## Analysis Points
Expand Down
18 changes: 9 additions & 9 deletions tactool/set_scale_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def __init__(self, testing_mode: bool) -> None:

# Setting the Dialog Box settings
self.setWindowTitle("Set Scale")
self.setGeometry(0, 50, 100, 200)
self.setMinimumSize(100, 200)
self.setWindowFlags(
Qt.Window | Qt.WindowCloseButtonHint | Qt.WindowStaysOnTopHint
)
Expand All @@ -60,19 +60,19 @@ def setup_ui_elements(self) -> None:
self.clear_scale_button = QPushButton("Clear", self)
self.cancel_button = QPushButton("Cancel", self)

# Distance label and input
distance_label = QLabel("Distance (µm):")
self.distance_input = QSpinBox()
self.distance_input.setMaximum(100000)

# Pixels label and input
pixels_label = QLabel("Pixels:")
self.pixel_input = QLineEdit()
self.pixel_input.setText(self.pixel_input_default)
self.pixel_input.setEnabled(False)

# Distance label and input
distance_label = QLabel("Distance (µm):")
self.distance_input = QSpinBox()
self.distance_input.setMaximum(100000)

# Scale label and input
scale_label = QLabel("Scale:")
scale_label = QLabel("Scale (Pixels per µm):")
self.scale_value = QLineEdit()
self.scale_value.setValidator(QDoubleValidator())
self.scale_value.setEnabled(False)
Expand All @@ -86,10 +86,10 @@ def setup_ui_elements(self) -> None:

# Arrange label and input boxes layout
settings_widgets_layout = QVBoxLayout()
settings_widgets_layout.addWidget(pixels_label)
settings_widgets_layout.addWidget(self.pixel_input)
settings_widgets_layout.addWidget(distance_label)
settings_widgets_layout.addWidget(self.distance_input)
settings_widgets_layout.addWidget(pixels_label)
settings_widgets_layout.addWidget(self.pixel_input)
settings_widgets_layout.addWidget(scale_label)
settings_widgets_layout.addWidget(self.scale_value)

Expand Down
20 changes: 17 additions & 3 deletions tactool/table_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
from csv import writer
from pathlib import Path
from textwrap import dedent
from typing import Optional
from typing import (
Any,
Optional,
)

from PyQt5.QtCore import (
pyqtSignal,
Expand Down Expand Up @@ -105,7 +108,7 @@ def __init__(self) -> None:
super().__init__()
# Set the headers of the table to be the names of the Analysis Point attributes
self.headers = AnalysisPoint.field_names()
self._data = []
self._data: list[list[Any]] = []
self.editable_columns = [
self.headers.index("label"),
self.headers.index("sample_name"),
Expand Down Expand Up @@ -292,6 +295,8 @@ def convert_export_headers(self) -> list[str]:
headers = self.headers[:len(self.headers) - 3]
for old_header, new_header in zip(header_conversions, header_conversions.values()):
headers[headers.index(old_header)] = new_header
# Remove the sample_name field
headers.pop(headers.index("sample_name"))

# Insert a new Z column after the Y column for the laser formatting
z_index = headers.index("Y") + 1
Expand All @@ -303,7 +308,16 @@ def convert_export_point(self, analysis_point: AnalysisPoint) -> list:
"""
Function to convert an Analysis Point formatting for a CSV export.
"""
headers = self.headers[:len(self.headers) - 3]
id_idx, sample_name_idx = headers.index("id"), headers.index("sample_name")
analysis_point_row = analysis_point.aslist()[:len(self.headers) - 3]
z_index = self.headers.index("y") + 1

# Concat the sample_name and id into 1 column
# Also pads zeros on id column value
analysis_point_row[id_idx] = f"{analysis_point.sample_name}_#{analysis_point.id:03d}"
analysis_point_row.pop(sample_name_idx)

# Insert a new Z column after the Y column for the laser formatting
z_index = headers.index("y") + 1
analysis_point_row.insert(z_index, 0)
return analysis_point_row
67 changes: 50 additions & 17 deletions tactool/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
from textwrap import dedent
from typing import Optional

from PyQt5.QtCore import (
QModelIndex,
)
from PyQt5.QtCore import QModelIndex
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (
QColorDialog,
Expand Down Expand Up @@ -72,6 +70,7 @@ def __init__(self, testing_mode: bool) -> None:

# Setup the User Interface
self.setWindowTitle("TACtool")
self.setMinimumSize(750, 650)
self.graphics_view = GraphicsView()
self.graphics_scene = self.graphics_view.graphics_scene
self.table_model = self.graphics_view.graphics_scene.table_model
Expand All @@ -80,6 +79,19 @@ def __init__(self, testing_mode: bool) -> None:
self.setup_ui_elements()
self.connect_signals_and_slots()
self.toggle_status_bar_messages()
self.main_input_widgets = [
self.menu_bar_file,
self.sample_name_input,
self.mount_name_input,
self.material_input,
self.label_input,
self.colour_button,
self.diameter_input,
self.reset_ids_button,
self.reset_settings_button,
self.clear_points_button,
self.table_view,
]


def setup_ui_elements(self) -> None:
Expand Down Expand Up @@ -122,14 +134,15 @@ def setup_ui_elements(self) -> None:

# Input for point diameter
diameter_label = QLabel("Diameter (μm):")
self.diameter_box = QSpinBox()
self.diameter_box.setValue(self.default_settings["diameter"])
self.diameter_box.setMaximum(100000)
self.diameter_input = QSpinBox()
self.diameter_input.setValue(self.default_settings["diameter"])
self.diameter_input.setMaximum(100000)

# Input for scaling
scale_label = QLabel("Scale (Pixels per µm):")
self.scale_value = QLineEdit()
self.scale_value.setText(self.default_settings["scale"])
self.scale_value_input = QLineEdit()
self.scale_value_input.setText(self.default_settings["scale"])
self.scale_value_input.setDisabled(True)
self.set_scale_button = QPushButton("Set Scale", self)

# Main buttons
Expand Down Expand Up @@ -165,9 +178,9 @@ def setup_ui_elements(self) -> None:

# Scaling widgets
sidebar.addWidget(diameter_label)
sidebar.addWidget(self.diameter_box)
sidebar.addWidget(self.diameter_input)
sidebar.addWidget(scale_label)
sidebar.addWidget(self.scale_value)
sidebar.addWidget(self.scale_value_input)
sidebar.addWidget(self.set_scale_button)
sidebar.addStretch(stretch=6)

Expand Down Expand Up @@ -246,7 +259,7 @@ def ref_points(self: Window):
return condition, message

def set_scale(self: Window):
condition_1 = self.scale_value.text() == self.default_settings["scale"]
condition_1 = self.scale_value_input.text() == self.default_settings["scale"]
condition_2 = len(self.table_model.reference_points) >= 3
condition = condition_1 and condition_2
message = "Have you set a scale?"
Expand Down Expand Up @@ -394,6 +407,11 @@ def process_tactool_csv(self, filepath: str) -> None:
reader = DictReader(csv_file)
# Iterate through each line in the CSV file
for id, item in enumerate(reader):

# Split the id and sample_name value from the Name column
if "_#" in item["Name"]:
item["sample_name"], item["Name"] = item["Name"].rsplit("_#", maxsplit=1)

# The default ID value is incremented with the row number
default_values["Name"] = id + 1
# If there is a Z column which is requried for the laser, then remove it
Expand Down Expand Up @@ -498,7 +516,7 @@ def validate_current_data(self, validate_image: bool = False) -> bool:
return False

# If the scale value has not been changed
if self.scale_value.text() == self.default_settings["scale"]:
if self.scale_value_input.text() == self.default_settings["scale"]:
message = dedent("""
A scale value has not been set.
Expand Down Expand Up @@ -538,9 +556,9 @@ def add_analysis_point(
# Get the required input values from the window input settings
# Coordinates and the Point ID are taken from the arguments, notes defaults to None
label = self.label_input.currentText()
diameter = self.diameter_box.value()
diameter = self.diameter_input.value()
colour = self.point_colour
scale = float(self.scale_value.text())
scale = float(self.scale_value_input.text())
sample_name = self.sample_name_input.text()
mount_name = self.mount_name_input.text()
material = self.material_input.text()
Expand Down Expand Up @@ -681,6 +699,11 @@ def toggle_scaling_mode(self) -> None:
if self.set_scale_dialog is None:
# Create the Set Scale Dialog box
self.set_scale_dialog = SetScaleDialog(self.testing_mode)
# Disable main window input widgets
self.toggle_main_input_widgets(False)
# Move the Set Scale Dialog box to be at the top left corner of the main window
main_window_pos = self.pos()
self.set_scale_dialog.move(main_window_pos.x() + 50, main_window_pos.y() + 50)

# Connect the Set Scale dialog buttons
self.set_scale_dialog.set_scale_clicked.connect(self.set_scale)
Expand All @@ -691,6 +714,16 @@ def toggle_scaling_mode(self) -> None:
# Else when the program is in scaling mode, reset the Set Scaling Dialog value
else:
self.set_scale_dialog = None
# Enable main window widgets
self.toggle_main_input_widgets(True)


def toggle_main_input_widgets(self, enable: bool) -> None:
"""
Toggle each of the input widgets in the main window to be enabled or disabled.
"""
for widget in self.main_input_widgets:
widget.setEnabled(enable)


def clear_scale_clicked(self) -> None:
Expand All @@ -707,7 +740,7 @@ def set_scale(self, scale: float) -> None:
"""
Function to set the scale of the program given when the Set scale button is clicked in the Set Scale dialog box.
"""
self.scale_value.setText(str(scale))
self.scale_value_input.setText(str(scale))
self.toggle_status_bar_messages()


Expand Down Expand Up @@ -775,10 +808,10 @@ def update_point_settings(
self.label_input.setCurrentText(label)

if diameter is not None:
self.diameter_box.setValue(diameter)
self.diameter_input.setValue(diameter)

if scale is not None:
self.scale_value.setText(str(scale))
self.scale_value_input.setText(str(scale))
self.toggle_status_bar_messages()

if colour is not None:
Expand Down
12 changes: 6 additions & 6 deletions test/data/analysis_points_complete.csv
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Name,Type,X,Y,diameter,scale,colour,sample_name,mount_name,material,notes
1,RefMark,472,336,10,1,#ffff00,sample_x83,mount_x81,rock,
2,RefMark,394,318,10,1,#ffff00,sample_x83,mount_x81,rock,
3,RefMark,469,268,10,1,#ffff00,sample_x83,mount_x81,rock,point3
4,Spot,527,340,10,1,#204a87,sample_x67,mount_x15,duck,"point4 with whitespace, and comma"
5,Spot,362,380,15,1,#204a87,sample_x67,mount_x15,duck,point5 with whitespace
Name,Type,X,Y,diameter,scale,colour,mount_name,material,notes
sample_x83_#001,RefMark,472,336,10,1,#ffff00,mount_x81,rock,this point has padded zeros in the id column
sample_x83_#2,RefMark,394,318,10,1,#ffff00,mount_x81,rock,
sample_x83_#3,RefMark,469,268,10,1,#ffff00,mount_x81,rock,point3
sample_x67_#4,Spot,527,340,10,1,#204a87,mount_x15,duck,"point4 with whitespace, and comma"
sample_x67_#5,Spot,362,380,15,1,#204a87,mount_x15,duck,point5 with whitespace
Loading

0 comments on commit f9d5a70

Please sign in to comment.