Repository: biolab/orange3

Issue:
Predictions: IndexError when clicking outside the table
<!-- 
Thanks for taking the time to report a bug!
If you're raising an issue about an add-on (i.e., installed via Options > Add-ons), raise an issue in the relevant add-on's issue tracker instead. See: https://github.com/biolab?q=orange3
To fix the bug, we need to be able to reproduce it. Please answer the following questions to the best of your ability.
-->


- [ ] **What's wrong?**
<!-- Be specific, clear, and concise. Include screenshots if relevant. -->
<!-- If you're getting an error message, copy it, and enclose it with three backticks (```). -->
Predictions crashes when clicking outside the table fields.

```
-------------------------- AttributeError Exception ---------------------------
Traceback (most recent call last):
  File "/Users/ajda/orange/orange3/Orange/widgets/evaluate/owpredictions.py", line 1130, in select
    self.store.select(selection, flags)
  File "/Users/ajda/orange/orange3/Orange/widgets/evaluate/owpredictions.py", line 1057, in select
    rows = {selection.model().mapToSource(selection).row()}
AttributeError: 'NoneType' object has no attribute 'mapToSource'
-------------------------------------------------------------------------------
```


- [ ] **How can we reproduce the problem?** 
<!-- Upload a zip with the .ows file and data. -->
<!-- Describe the steps (open this widget, click there, then add this...) -->
Create a prediction workflow with a small test data set. Click outside the table.




- [ ] **What's your environment?**
<!-- To find your Orange version, see "Help → About → Version" or `Orange.version.full_version` in code -->
- Operating system: OSX
- Orange version: 3.28.dev
- How you installed Orange:



Discussion:
Hey, I would like to take a look at this one, may I? Thanks in advance!
@erelin6613 Hello! You are welcome to take this over! ⭐ 
@erelin6613 Hey, nice to meet you, thanks for the initiative. If you've any questions, feel free to ask on [Discord](https://discord.gg/FWrfeXV) :)
@irgolic @ajdapretnar
I have been trying to dig deep into the warning the terminal was displaying:
`QCssParser::parseColorValue: Specified color without alpha value but alpha given: 'rgb 0, 0, 0, 100'`, I am not sure if it might cause issue or not though.
For now I just added functionality to 'just not break' but was not able to discover some deeper problem if there is any. If you have any idea why `SharedSelectionStore.selection.model()` could return None, I appreciate you for suggestion. Otherwise a fix for stability is available on my forked repo.
@erelin6613 Create a new branch with your fix on your forked repo, then create a pull request https://github.com/biolab/orange3/compare, and we can see what's up.

You should be able to find some help here: https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/working-with-forks

Resolving patch:
diff --git a/Orange/widgets/evaluate/owpredictions.py b/Orange/widgets/evaluate/owpredictions.py
--- a/Orange/widgets/evaluate/owpredictions.py
+++ b/Orange/widgets/evaluate/owpredictions.py
@@ -15,7 +15,6 @@
     QModelIndex, QAbstractTableModel, QSortFilterProxyModel, pyqtSignal, QTimer,
     QItemSelectionModel, QItemSelection)
 
-from Orange.widgets.utils.colorpalettes import LimitedDiscretePalette
 from orangewidget.report import plural
 
 import Orange
@@ -32,6 +31,7 @@
 from Orange.widgets.utils.itemmodels import TableModel
 from Orange.widgets.utils.sql import check_sql_input
 from Orange.widgets.utils.state_summary import format_summary_details
+from Orange.widgets.utils.colorpalettes import LimitedDiscretePalette
 
 
 # Input slot for the Predictors channel
@@ -1054,7 +1054,10 @@ def select(self, selection: Union[QModelIndex, QItemSelection], flags: int):
                 flags that tell whether to Clear, Select, Deselect or Toggle
         """
         if isinstance(selection, QModelIndex):
-            rows = {selection.model().mapToSource(selection).row()}
+            if selection.model() is not None:
+                rows = {selection.model().mapToSource(selection).row()}
+            else:
+                rows = set()
         else:
             indices = selection.indexes()
             if indices:
@@ -1108,11 +1111,12 @@ def map_from_source(rows):
         try:
             yield
         finally:
-            deselected = map_from_source(old_rows - self._rows)
-            selected = map_from_source(self._rows - old_rows)
-            if selected or deselected:
-                for model in self._selection_models:
-                    model.emit_selection_rows_changed(selected, deselected)
+            if self.proxy.sourceModel() is not None:
+                deselected = map_from_source(old_rows - self._rows)
+                selected = map_from_source(self._rows - old_rows)
+                if selected or deselected:
+                    for model in self._selection_models:
+                        model.emit_selection_rows_changed(selected, deselected)
 
 
 class SharedSelectionModel(QItemSelectionModel):


Test patch: