Skip to content

Commit

Permalink
ENH: Improve terminology selector
Browse files Browse the repository at this point in the history
Allow deselecting anatomic region and anatomic region modifier ("None" and "No region modifier" items are added to the comboboxes). Before this commit, once a region or modifier was selected, it was not possible to deselect it.

Add the anatomic region to the default generated name, because often it is the only difference between segment names (e.g., "bleeding in Cerebellum" and "bleeding in Subdural space").

To reduce occurrences of undefined (gray) color: if color is not specified for the selected type modifier then use the color of the non-modified type.

Added a few missing translation functions to allow full internationalization.
  • Loading branch information
lassoan committed Apr 15, 2024
1 parent 71ccf6d commit 5c44b86
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
</property>
<property name="defaultText">
<string>Select type modifier...</string>
<string>No type modifier</string>
</property>
</widget>
</item>
Expand Down Expand Up @@ -240,7 +240,7 @@
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
</property>
<property name="defaultText">
<string>Select anatomic region modifier...</string>
<string>No region modifier</string>
</property>
</widget>
</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class qSlicerTerminologyNavigatorWidgetPrivate: public Ui_qSlicerTerminologyNavi
//-----------------------------------------------------------------------------
qSlicerTerminologyNavigatorWidgetPrivate::qSlicerTerminologyNavigatorWidgetPrivate(qSlicerTerminologyNavigatorWidget& object)
: q_ptr(&object)
, NoneItemName(QString("[ None ]"))
, NoneItemName(QString("[ ") + qSlicerTerminologyNavigatorWidget::tr("None") + QString(" ]"))
, NameAutoGenerated(true)
, ColorAutoGenerated(true)
, GeneratedColor(vtkSlicerTerminologyType::INVALID_COLOR[0], vtkSlicerTerminologyType::INVALID_COLOR[1], vtkSlicerTerminologyType::INVALID_COLOR[2])
Expand Down Expand Up @@ -410,16 +410,22 @@ QColor qSlicerTerminologyNavigatorWidgetPrivate::terminologyRecommendedColor()
unsigned char r = vtkSlicerTerminologyType::INVALID_COLOR[0];
unsigned char g = vtkSlicerTerminologyType::INVALID_COLOR[1];
unsigned char b = vtkSlicerTerminologyType::INVALID_COLOR[2];
if (!this->CurrentTypeObject->GetHasModifiers() || this->CurrentTypeModifierObject->GetCodeValue() == nullptr)

// Use the color specified in the modifier by default
if (this->CurrentTypeObject->GetHasModifiers() && this->CurrentTypeModifierObject->GetCodeValue())
{
this->CurrentTypeObject->GetRecommendedDisplayRGBValue(r,g,b);
this->CurrentTypeModifierObject->GetRecommendedDisplayRGBValue(r, g, b);
}
else

// If color is not defined for the modifier then use the non-modified type object's color
if (r == vtkSlicerTerminologyType::INVALID_COLOR[0]
&& g == vtkSlicerTerminologyType::INVALID_COLOR[1]
&& b == vtkSlicerTerminologyType::INVALID_COLOR[2])
{
this->CurrentTypeModifierObject->GetRecommendedDisplayRGBValue(r,g,b);
this->CurrentTypeObject->GetRecommendedDisplayRGBValue(r,g,b);
}

// Use generated color if recommended color is missing (i.e. the default invalid color)
// Use generated color otherwise (i.e. the default invalid color)
if ( r == vtkSlicerTerminologyType::INVALID_COLOR[0]
&& g == vtkSlicerTerminologyType::INVALID_COLOR[1]
&& b == vtkSlicerTerminologyType::INVALID_COLOR[2] )
Expand Down Expand Up @@ -763,7 +769,7 @@ bool qSlicerTerminologyNavigatorWidget::terminologyEntry(vtkSlicerTerminologyEnt
}

// Anatomic region
if (d->CurrentRegionObject)
if (d->CurrentCategoryObject->GetShowAnatomy() && d->CurrentRegionObject)
{
entry->GetAnatomicRegionObject()->Copy(d->CurrentRegionObject);
}
Expand All @@ -773,7 +779,7 @@ bool qSlicerTerminologyNavigatorWidget::terminologyEntry(vtkSlicerTerminologyEnt
}

// Anatomic region modifier
if (d->CurrentRegionModifierObject)
if (d->CurrentCategoryObject->GetShowAnatomy() && d->CurrentRegionModifierObject)
{
entry->GetAnatomicRegionModifierObject()->Copy(d->CurrentRegionModifierObject);
}
Expand Down Expand Up @@ -967,11 +973,36 @@ QString qSlicerTerminologyNavigatorWidget::nameFromTerminology(vtkSlicerTerminol
// Assemble from selection if there is no label
if (name.isEmpty())
{
name = entry->GetTypeObject()->GetCodeMeaning();

// Set type and modifier in name
if (entry->GetTypeObject()->GetHasModifiers() && entry->GetTypeModifierObject() && entry->GetTypeModifierObject()->GetCodeValue())
{
name += QString(", %1").arg(entry->GetTypeModifierObject()->GetCodeMeaning());
//: For formatting of terminology entry with a modifier. %1 is structure name (e.g., "Kidney"), %2 is modifier (e.g., "Left")
name = tr("%1, %2")
.arg(entry->GetTypeObject()->GetCodeMeaning())
.arg(entry->GetTypeModifierObject()->GetCodeMeaning());
}
else
{
name = entry->GetTypeObject()->GetCodeMeaning();
}
}

if (entry->GetAnatomicRegionObject() && entry->GetAnatomicRegionObject()->GetCodeValue())
{
if (entry->GetAnatomicRegionModifierObject() && entry->GetAnatomicRegionModifierObject()->GetCodeValue())
{
//: For formatting of terminology entry name. %1 is type name (e.g., "Mass"), %2 is region name (e.g., "Kidney"), %2 is region modifier (e.g., "Left")
name = tr("%1 in %2, %3")
.arg(name)
.arg(entry->GetAnatomicRegionObject()->GetCodeMeaning())
.arg(entry->GetAnatomicRegionModifierObject()->GetCodeMeaning());
}
else
{
//: For formatting of terminology entry name. %1 is type name (e.g., "Mass"), %2 is region name (e.g., "Liver")
name = tr("%1 in %2")
.arg(name)
.arg(entry->GetAnatomicRegionObject()->GetCodeMeaning());
}
}

Expand Down Expand Up @@ -1338,14 +1369,10 @@ void qSlicerTerminologyNavigatorWidget::populateTypeModifierComboBox()
return;
}

int selectedIndex = -1;
int selectedIndex = 0;

// Add none item so that no modifier can be selected even if there are options
d->ComboBox_TypeModifier->addItem(tr("No type modifier"));
if (d->CurrentTypeModifierObject->GetCodeValue() == nullptr)
{
selectedIndex = 0;
}

// Get type modifier names
std::vector<vtkSlicerTerminologiesModuleLogic::CodeIdentifier> typeModifiers;
Expand Down Expand Up @@ -2057,9 +2084,15 @@ void qSlicerTerminologyNavigatorWidget::populateRegionTable()
d->CurrentAnatomicContextName.toUtf8().constData(),
regions, d->SearchBox_AnatomicRegion->text().toUtf8().constData() );

QTableWidgetItem* selectedItem = nullptr;
d->tableWidget_AnatomicRegion->setRowCount(regions.size());
d->tableWidget_AnatomicRegion->setRowCount(regions.size() + 1); // +1 for the "None" item

int index = 0;

// Add "None" item
QTableWidgetItem* noneRegionItem = new QTableWidgetItem(d->NoneItemName);
d->tableWidget_AnatomicRegion->setItem(index++, 0, noneRegionItem);
QTableWidgetItem* selectedItem = noneRegionItem;

std::vector<vtkSlicerTerminologiesModuleLogic::CodeIdentifier>::iterator idIt;
for (idIt=regions.begin(); idIt!=regions.end(); ++idIt, ++index)
{
Expand Down Expand Up @@ -2117,8 +2150,11 @@ void qSlicerTerminologyNavigatorWidget::populateRegionModifierComboBox()
vtkSlicerTerminologiesModuleLogic::CodeIdentifierFromTerminologyType(d->CurrentRegionObject),
regionModifiers );

int selectedIndex = -1;
int index = 0;
// Add "none" item
d->ComboBox_AnatomicRegionModifier->addItem(tr("No region modifier"));
int selectedIndex = 0;
int index = 1; // "none" item is 0, start adding items from 1

std::vector<vtkSlicerTerminologiesModuleLogic::CodeIdentifier>::iterator idIt;
for (idIt=regionModifiers.begin(); idIt!=regionModifiers.end(); ++idIt, ++index)
{
Expand Down Expand Up @@ -2244,6 +2280,16 @@ bool qSlicerTerminologyNavigatorWidget::setCurrentRegion(vtkSlicerTerminologyTyp
// Only enable region modifier combobox if there are items in it
d->ComboBox_AnatomicRegionModifier->setEnabled(d->ComboBox_AnatomicRegionModifier->count());

// "None" is selected
if (!region->GetCodeValue())
{
d->resetCurrentRegion();
d->tableWidget_AnatomicRegion->blockSignals(true);
d->tableWidget_AnatomicRegion->setCurrentCell(0, 0);
d->tableWidget_AnatomicRegion->blockSignals(false);
return false;
}

// Select region if found
QTableWidgetItem* regionItem = d->findTableWidgetItemForType(d->tableWidget_AnatomicRegion, region);
if (regionItem)
Expand Down Expand Up @@ -2275,19 +2321,22 @@ void qSlicerTerminologyNavigatorWidget::onRegionSelected(QTableWidgetItem* curre
qCritical() << Q_FUNC_INFO << ": Failed to access terminology logic";
return;
}
vtkSlicerTerminologiesModuleLogic::CodeIdentifier regionId(
currentItem->data(CodingSchemeDesignatorRole).toString().toUtf8().constData(),
currentItem->data(CodeValueRole).toString().toUtf8().constData(),
currentItem->text().toUtf8().constData() );
vtkSmartPointer<vtkSlicerTerminologyType> region = vtkSmartPointer<vtkSlicerTerminologyType>::New();
if (!logic->GetRegionInAnatomicContext(
d->CurrentAnatomicContextName.toUtf8().constData(),
regionId, region) )
if (!currentItem->data(CodeValueRole).isNull())
{
qCritical() << Q_FUNC_INFO << ": Failed to find region '" << currentItem->text();
return;
// Not the "None" item is selected
vtkSlicerTerminologiesModuleLogic::CodeIdentifier regionId(
currentItem->data(CodingSchemeDesignatorRole).toString().toUtf8().constData(),
currentItem->data(CodeValueRole).toString().toUtf8().constData(),
currentItem->text().toUtf8().constData());
if (!logic->GetRegionInAnatomicContext(
d->CurrentAnatomicContextName.toUtf8().constData(),
regionId, region))
{
qCritical() << Q_FUNC_INFO << ": Failed to find region '" << currentItem->text();
return;
}
}

// Set current region
this->setCurrentRegion(region);

Expand Down Expand Up @@ -2345,18 +2394,22 @@ void qSlicerTerminologyNavigatorWidget::onRegionModifierSelectionChanged(int ind
qCritical() << Q_FUNC_INFO << ": Failed to access terminology logic";
return;
}
QMap<QString, QVariant> userData = d->ComboBox_AnatomicRegionModifier->itemData(index).toMap();
vtkSlicerTerminologiesModuleLogic::CodeIdentifier modifierId(
userData[QString::number(CodingSchemeDesignatorRole)].toString().toUtf8().constData(),
userData[QString::number(CodeValueRole)].toString().toUtf8().constData(),
d->ComboBox_AnatomicRegionModifier->itemText(index).toUtf8().constData() );
if (!logic->GetRegionModifierInAnatomicRegion(
d->CurrentAnatomicContextName.toUtf8().constData(),
vtkSlicerTerminologiesModuleLogic::CodeIdentifierFromTerminologyType(d->CurrentRegionObject),
modifierId, modifier) )
if (index > 0)
{
qCritical() << Q_FUNC_INFO << ": Failed to find modifier '" << d->ComboBox_AnatomicRegionModifier->itemText(index);
return;
// Modifier selected (not the "No region modifier" item, which is at index = 0)
QMap<QString, QVariant> userData = d->ComboBox_AnatomicRegionModifier->itemData(index).toMap();
vtkSlicerTerminologiesModuleLogic::CodeIdentifier modifierId(
userData[QString::number(CodingSchemeDesignatorRole)].toString().toUtf8().constData(),
userData[QString::number(CodeValueRole)].toString().toUtf8().constData(),
d->ComboBox_AnatomicRegionModifier->itemText(index).toUtf8().constData());
if (!logic->GetRegionModifierInAnatomicRegion(
d->CurrentAnatomicContextName.toUtf8().constData(),
vtkSlicerTerminologiesModuleLogic::CodeIdentifierFromTerminologyType(d->CurrentRegionObject),
modifierId, modifier))
{
qCritical() << Q_FUNC_INFO << ": Failed to find modifier '" << d->ComboBox_AnatomicRegionModifier->itemText(index);
return;
}
}

// Set current region modifier
Expand Down

0 comments on commit 5c44b86

Please sign in to comment.