Repository: pymeasure/pymeasure

Issue:
FutureWarning by pandas when using TableWidget
The lines 176 and 177 in the table_widget.py script generate a `FutureWarning` by Pandas. This is particularly annoying, since the lines are executed multiple times a second when using a `TableWidget` and quickly fill up the console.

```
C:\<...>\miniforge3\envs\test\Lib\site-packages\pymeasure\display\widgets\table_widget.py:176: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`
  value = results.data.iloc[row][col]
C:\<...>\miniforge3\envs\test\Lib\site-packages\pymeasure\display\widgets\table_widget.py:177: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`
  column_type = results.data.dtypes[col]
```

The warnings can be avoided by replacing the lines with (though there is probably a more concise solution for the first line):
```python
value = results.data.iloc[row].iloc[col]
column_type = results.data.dtypes.iloc[col]
```

My current workaround (aside from editing table_widget.py myself) is to suppress `FutureWarning` by including
```python
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
```

<details>

<summary>Here is a MWE to generate the warning</summary>

```python
r"""
MWE for FutureWarning by pandas when using TableWidget in PyMeasure 0.13.1

Conda setup: ``conda create -n test -c conda-forge pymeasure pyqt``

Hit the 'Queue' button to start the procedure and see this warning in the
console:

C:\<...>\Lib\site-packages\pymeasure\display\widgets\table_widget.py:176:
    FutureWarning: Series.__getitem__ treating keys as positions is deprecated.
    In a future version, integer keys will always be treated as labels
    (consistent with DataFrame behavior). To access a value by position,
    use `ser.iloc[pos]`
  value = results.data.iloc[row][col]
C:\<...>\Lib\site-packages\pymeasure\display\widgets\table_widget.py:177:
    FutureWarning: Series.__getitem__ treating keys as positions is deprecated.
    In a future version, integer keys will always be treated as labels
    (consistent with DataFrame behavior). To access a value by position,
    use `ser.iloc[pos]`
  column_type = results.data.dtypes[col]
"""

from time import sleep
from random import random

import sys
import tempfile

from pymeasure.display.Qt import QtWidgets
from pymeasure.experiment import Procedure
from pymeasure.experiment import Results
from pymeasure.display.widgets import TableWidget
from pymeasure.display.windows.managed_window import ManagedWindow


class SimpleProcedure(Procedure):
    DATA_COLUMNS = ['iter', 'rand']

    def execute(self):
        # run for ~5 seconds & send random values
        for i in range(10):
            self.emit('results', {'iter': i,
                                  'rand': random()})
            sleep(0.5)


class MainWindow(ManagedWindow):
    def __init__(self):
        widget_list = (
            TableWidget("Experiment Table", SimpleProcedure.DATA_COLUMNS,
                        by_column=True, column_index='iter'),
            )
        super().__init__(
            procedure_class=SimpleProcedure,
            widget_list=widget_list,
            x_axis='iter',
            y_axis='rand'
            )
        self.setWindowTitle('MWE FutureWarning using TableWidget')

    def queue(self):
        filename = tempfile.mktemp()

        procedure = self.make_procedure()
        results = Results(procedure, filename)
        experiment = self.new_experiment(results)

        self.manager.queue(experiment)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
```

</details>

<details>

<summary>System details</summary>

- Operating system: Windows 10
- Python distribution: Miniforge3 (conda)
- Environment setup: `conda create -n test -c conda-forge pymeasure pyqt`
- Output of `conda list`

```
# packages in environment at C:\<...>\miniforge3\envs\test:
#
# Name                    Version                   Build  Channel
bzip2                     1.0.8                hcfcfb64_5    conda-forge
ca-certificates           2024.2.2             h56e8100_0    conda-forge
gettext                   0.21.1               h5728263_0    conda-forge
glib                      2.78.4               h39d0aa6_4    conda-forge
glib-tools                2.78.4               h0a98069_4    conda-forge
gst-plugins-base          1.22.9               h001b923_0    conda-forge
gstreamer                 1.22.9               hb4038d2_0    conda-forge
icu                       73.2                 h63175ca_0    conda-forge
intel-openmp              2024.0.0         h57928b3_49841    conda-forge
krb5                      1.21.2               heb0366b_0    conda-forge
libblas                   3.9.0              21_win64_mkl    conda-forge
libcblas                  3.9.0              21_win64_mkl    conda-forge
libclang                  15.0.7          default_hde6756a_4    conda-forge
libclang13                15.0.7          default_h85b4d89_4    conda-forge
libexpat                  2.6.1                h63175ca_0    conda-forge
libffi                    3.4.2                h8ffe710_5    conda-forge
libglib                   2.78.4               h39d0aa6_4    conda-forge
libhwloc                  2.9.3           default_haede6df_1009    conda-forge
libiconv                  1.17                 hcfcfb64_2    conda-forge
libjpeg-turbo             3.0.0                hcfcfb64_1    conda-forge
liblapack                 3.9.0              21_win64_mkl    conda-forge
libogg                    1.3.4                h8ffe710_1    conda-forge
libpng                    1.6.43               h19919ed_0    conda-forge
libsqlite                 3.45.1               hcfcfb64_0    conda-forge
libvorbis                 1.3.7                h0e60522_0    conda-forge
libxml2                   2.12.5               hc3477c8_0    conda-forge
libzlib                   1.2.13               hcfcfb64_5    conda-forge
mkl                       2024.0.0         h66d3029_49657    conda-forge
numpy                     1.26.4          py312h8753938_0    conda-forge
openssl                   3.2.1                hcfcfb64_0    conda-forge
packaging                 24.0               pyhd8ed1ab_0    conda-forge
pandas                    2.2.1           py312h2ab9e98_0    conda-forge
pcre2                     10.43                h17e33f8_0    conda-forge
pint                      0.23               pyhd8ed1ab_0    conda-forge
pip                       24.0               pyhd8ed1ab_0    conda-forge
ply                       3.11                       py_1    conda-forge
pthreads-win32            2.9.1                hfa6e2cd_3    conda-forge
pymeasure                 0.13.1             pyhd8ed1ab_0    conda-forge
pyqt                      5.15.9          py312he09f080_5    conda-forge
pyqt5-sip                 12.12.2         py312h53d5487_5    conda-forge
pyqtgraph                 0.13.4             pyhd8ed1ab_0    conda-forge
pyserial                  3.5                pyhd8ed1ab_0    conda-forge
python                    3.12.2          h2628c8c_0_cpython    conda-forge
python-dateutil           2.9.0              pyhd8ed1ab_0    conda-forge
python-tzdata             2024.1             pyhd8ed1ab_0    conda-forge
python_abi                3.12                    4_cp312    conda-forge
pytz                      2024.1             pyhd8ed1ab_0    conda-forge
pyvisa                    1.14.1             pyhd8ed1ab_0    conda-forge
qt-main                   5.15.8              h9e85ed6_19    conda-forge
setuptools                69.1.1             pyhd8ed1ab_0    conda-forge
sip                       6.7.12          py312h53d5487_0    conda-forge
six                       1.16.0             pyh6c4a22f_0    conda-forge
tbb                       2021.11.0            h91493d7_1    conda-forge
tk                        8.6.13               h5226925_1    conda-forge
toml                      0.10.2             pyhd8ed1ab_0    conda-forge
tomli                     2.0.1              pyhd8ed1ab_0    conda-forge
typing_extensions         4.10.0             pyha770c72_0    conda-forge
tzdata                    2024a                h0c530f3_0    conda-forge
ucrt                      10.0.22621.0         h57928b3_0    conda-forge
vc                        14.3                hcf57466_18    conda-forge
vc14_runtime              14.38.33130         h82b7239_18    conda-forge
vs2015_runtime            14.38.33130         hcb4865c_18    conda-forge
wheel                     0.42.0             pyhd8ed1ab_0    conda-forge
xz                        5.2.6                h8d14728_0    conda-forge
zstd                      1.5.5                h12be248_0    conda-forge
```

</details>


Discussion:
Thanks for raising this issue. @CasperSchippers , @msmttchr could you take a look, whether the first suggestion (with the `iloc`) would fix that problem without causing issues for older versions?
I've had a look and think that the suggestion with (`iloc`) should indeed work with older versions. I think that in principle that way should've been preferred all along when indexing by integer position. Technically, since we're indexing for a single scalar value, I thin that technically the recomended indexing method would be `iat` (which is alledgeldy a lot faster, not sure if we'd notice though).

Resolving patch:
diff --git a/examples/Basic/gui_table.py b/examples/Basic/gui_table.py
--- a/examples/Basic/gui_table.py
+++ b/examples/Basic/gui_table.py
@@ -94,8 +94,7 @@ def __init__(self):
             inputs=['iterations', 'delay', 'seed'],
             displays=['iterations', 'delay', 'seed'],
             widget_list=widget_list,
-            filename_input=False,
-            directory_input=False,
+            enable_file_input=False,
         )
         logging.getLogger().addHandler(widget_list[1].handler)
         log.setLevel(self.log_level)
diff --git a/pymeasure/display/widgets/table_widget.py b/pymeasure/display/widgets/table_widget.py
--- a/pymeasure/display/widgets/table_widget.py
+++ b/pymeasure/display/widgets/table_widget.py
@@ -173,8 +173,8 @@ def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
         if index.isValid() and role in (QtCore.Qt.ItemDataRole.DisplayRole, SORT_ROLE):
             try:
                 results, row, col = self.translate_to_local(index.row(), index.column())
-                value = results.data.iloc[row][col]
-                column_type = results.data.dtypes[col]
+                value = results.data.iat[row, col]
+                column_type = results.data.dtypes.iat[col]
                 # Cast to column type
                 value_render = column_type.type(value)
             except (IndexError, ValueError, TypeError):


Test patch: