# Table cell change to date and time picker

**URL:** <https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785>\
**Category:** Ignition\
**Tags:** perspective\
**Created:** [January 13, 2025, 1:52am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785 "2025-01-13T01:52:10Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![chaoliang](https://avatars.discourse-cdn.com/v4/letter/c/ba8739/32.png) [@chaoliang](https://forum.inductiveautomation.com/u/chaoliang)\
**Post date:** [January 13, 2025, 1:52am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/1 "2025-01-13T01:52:11Z")

</div>

I want to be able to select time from the perspective table.  
The table get data from a dataset.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/6/16011002771a2b74d15b39cac5502477146f8264.png)  
it's setup as per the following post.

> [@Perspective table edit cell with date](https://forum.inductiveautomation.com/t/perspective-table-edit-cell-with-date/58818):
>
> Is there a solution that allows editing date values in a table cell using an appropriate date component? When make a date column editable, it allows text entry only, and according to the doc string for the onEditCellCommit event, date values are not supported: value (int | float | bool | str): The committed value. I also tried putting a popup where the cell is located, but onClick is only firing when I click a header and the onRowClick event does not provide the XY information needed. Any …

the table has been setup nicely.  
But for the viewparams setup, how do I point this value to the value of this row at this column?  
or do I need to write script to get the row index, then assign the value of this Value[row][column] to this param?

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/b/5bd00c8b089815c5937ab11837a18872a979eddb.png)

I searched the forum, but haven't found the answer yet.

Regards

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [January 13, 2025, 2:41am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/2 "2025-01-13T02:41:07Z")

</div>

Change the name of the view parameter to `value`. Do not configure it in `columns`, as it is automatic.

---

<div class="post-metadata">

**Author:** ![chaoliang](https://avatars.discourse-cdn.com/v4/letter/c/ba8739/32.png) [@chaoliang](https://forum.inductiveautomation.com/u/chaoliang)\
**Post date:** [January 13, 2025, 4:39am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/3 "2025-01-13T04:39:02Z")

</div>

Thank you for the reply.

I have now configured to `value`, now I can see the different time values of each row.

Then I changed params of the `value` so I can read and write, but somehow I cannot change values from the table. If I change directly from the dataset, the new values can be updated to the table.

1. component params `value` setup.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/a/daa20e09fcd7804d12aa88725ddb608836d308a9.png)
2. time picker value binding. bidirectional is already selected.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/f/1f7c7e13a58557f7928f326e0f4a8a2e6136d871.png)
3. the table configuration of the `columns`. I tried with or without the `viewParams` setting of the `value` tag, neither way works.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/f/5/f574d8b1da496c360ab55b25e18f2ae76ac33e49.png)

when changing the time by clicking the up/down arrow, 1 sec later, the value changed back.  
do I need to setup any action trigger each time I change the value?

Thanks.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [January 13, 2025, 1:17pm UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/4 "2025-01-13T13:17:54Z")

</div>

You need to message to the containing view so it can change the table data.

---

<div class="post-metadata">

**Author:** ![james.vosler](https://avatars.discourse-cdn.com/v4/letter/j/8797f3/32.png) [@james.vosler](https://forum.inductiveautomation.com/u/james.vosler)\
**Post date:** [January 13, 2025, 1:49pm UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/5 "2025-01-13T13:49:08Z")

</div>

There are several implicit parameters for the subviews. See:

[https://forum.inductiveautomation.com/t/perspective-table-dynamic-column-viewparams-per-row/34657/2](https://forum.inductiveautomation.com/t/perspective-table-dynamic-column-viewparams-per-row/34657/2)

or  
[https://www.docs.inductiveautomation.com/docs/8.1/appendix/components/perspective-components/perspective-display-palette/perspective-table/displaying-a-subview-in-a-table-row#:~:text=Summary%20of%20Subviews%E2%80%8B,a%20value%20under%20this%20object](https://www.docs.inductiveautomation.com/docs/8.1/appendix/components/perspective-components/perspective-display-palette/perspective-table/displaying-a-subview-in-a-table-row#:~:text=Summary%20of%20Subviews%E2%80%8B,a%20value%20under%20this%20object).

So your viewParams would not include the element ‘time’.

You don’t even have to add the implicit parameters in the columns viewParams configuration.

You DO have to declare them in the embedded view with the correct name or you won’t have a property value to reference them.

embedded\_view.params: {

value: null, # The value of the row from the selected column arrives here. The param property name must be ‘value’.

rowData: null, # Include this if you need to refer to data from other columns.

rowIndex: 0, # Include this if you need the index into the full dataset.

row: 0 # Include this if you need the index into the filtered dataset.

}

Hopefully, that answers your question. Happy programming!

---

<div class="post-metadata">

**Author:** ![jespinmartin1](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jespinmartin1/32/57175_2.png) [@jespinmartin1](https://forum.inductiveautomation.com/u/jespinmartin1)\
**Post date:** [January 13, 2025, 2:20pm UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/6 "2025-01-13T14:20:55Z")

</div>

Is the param bidirectional?

---

<div class="post-metadata">

**Author:** ![chaoliang](https://avatars.discourse-cdn.com/v4/letter/c/ba8739/32.png) [@chaoliang](https://forum.inductiveautomation.com/u/chaoliang)\
**Post date:** [January 14, 2025, 5:19am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/7 "2025-01-14T05:19:46Z")

</div>

Thanks all for the comments.

Here's the working solution.

1. setup a time picker component.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/4/54541c9f9de8c99b597aecb10efb5cdfd67792f1.png)
2. configure these parameters.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/7/2/72ac5ed2200c3367da6e7b5ce139b3300d0f1fe2.png)  
`column`, `columnIndex`, `row`, `rowIndex` and `value`, are the default params passed from the table to the component. As I have two time picker, one for start time, one for end time. so I create a new tag "tag\_name" to record which time it is.
3. the time picker `value` is bound to the `view.params.value`.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/3/b/3bcfda02cb2c79e5b1f2d749990764bc448fe309.png)  
the `change script` of the `value` binding is:

```python
def valueChanged(self, previousValue, currentValue, origin, missedEvents):
		value = currentValue.value
		if value is not None:
			if 'Binding' not in str(origin):
				msg = 'UpdateRow'
				rowIndex = self.view.params.rowIndex
				tag_name = self.view.params.tag_name
				payload =	{
							'value'	: value,
							'rowIndex': rowIndex,
							'tag_name': tag_name
							}
				system.perspective.sendMessage(msg, payload)

```

once configured, whenever the time picker value is changes, it will send a message.  
4. this is the table configuration of the time picker.  
Processing: image.png…  
5. this is the message handler script of the table.

```python
def onMessageReceived(self, payload):

	value = payload['value']
	rowIndex = int(payload['rowIndex'])
	tag_name = str(payload['tag_name'])
	tagPath = "[default]Site_Control/ds_csp_1"
	dataset = system.tag.readBlocking(tagPath)[0].value
	system.tag.write(tagPath, system.dataset.setValue(dataset, rowIndex, tag_name, value))

```

In summary, the basic data flow is:

1. operator change time picker value.
2. the following values are sent from the table component to the embedded time picker component.  
`column`, `columnIndex`, `row`, `rowIndex` and `value`
3. the time picker component detects the value change, then it executes the message script.
4. the table received the message handler, it executes the logic to write to the table binding tag
5. the updated tag is bound to the table, then the table is refreshed with the new value.

---

<div class="post-metadata">

**Author:** ![chaoliang](https://avatars.discourse-cdn.com/v4/letter/c/ba8739/32.png) [@chaoliang](https://forum.inductiveautomation.com/u/chaoliang)\
**Post date:** [January 14, 2025, 5:20am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/8 "2025-01-14T05:20:33Z")

</div>

as Phil mentioned, you need to create message to send back the updated value.

---

<div class="post-metadata">

**Author:** ![chaoliang](https://avatars.discourse-cdn.com/v4/letter/c/ba8739/32.png) [@chaoliang](https://forum.inductiveautomation.com/u/chaoliang)\
**Post date:** [January 14, 2025, 5:23am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/9 "2025-01-14T05:23:11Z")

</div>

The following script converts pyDataset to dataset.  
I found it handy sometimes.

```python
def pyds_to_ds(value):
	   headers = system.dataset.getColumnHeaders(value)
	   newData=[]
	   for rowIndex in range(value.rowCount):
	       	row = [value.getValueAt(rowIndex,col) for col in range(value.columnCount)]
	       	newData.append(row)
	   return system.dataset.toDataSet(headers,newData)

```

---

<div class="post-metadata">

**Author:** ![Andrew\_Buldyzhov](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/andrew_buldyzhov/32/74246_2.png) [@Andrew\_Buldyzhov](https://forum.inductiveautomation.com/u/Andrew_Buldyzhov)\
**Post date:** [May 2, 2025, 7:21am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/10 "2025-05-02T07:21:43Z")

</div>

This solution works well for me when I use Dataset, but when I convert it to PyDataSet (to change cell background to highlight exceeded limits), then the date value is not sent into the embedded view. Does this automatic sending of a column parameter named "value" work for Dataset only but not for PyDataSet?  
I also tried to create the parameter named "value" explicitly.

---

<div class="post-metadata">

**Author:** ![Transistor](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/transistor/32/3380_2.png) [@Transistor](https://forum.inductiveautomation.com/u/Transistor)\
**Post date:** [May 2, 2025, 7:42am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/11 "2025-05-02T07:42:19Z")

</div>

> ... when I convert it to PyDataSet (to change cell background to highlight exceeded limits) ...

I've only done that by building a JSON dictionary as used in the sample data loaded by default with the Perspective Table component. The manual offers alternatives.  
[Styling Rows Based on Values | Ignition User Manual](https://www.docs.inductiveautomation.com/docs/8.1/appendix/components/perspective-components/perspective-display-palette/perspective-table/styling-rows).

---

<div class="post-metadata">

**Author:** ![Andrew\_Buldyzhov](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/andrew_buldyzhov/32/74246_2.png) [@Andrew\_Buldyzhov](https://forum.inductiveautomation.com/u/Andrew_Buldyzhov)\
**Post date:** [May 2, 2025, 11:56am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/12 "2025-05-02T11:56:41Z")

</div>

I have fixed the issue by adding a condition to skip the date column. It now stays scalar so the automatic passing of the "value" parameter into the embedded view works well.  
@Transistor , thank you for your comment. It has lead my research into the right direction.  
Here's the resulting code of my Transform script:

```python
def transform(self, value, quality, timestamp):
    
    HI_LIMIT = 5.0
    
    for row in value: # Iterate through each row in the data
        for column, cell_value in row.items(): # Iterate through each cell in the row
            if column != "SampleDate": # Skip SampleDate column not to break the date picker in the embedded view
	            
	            if cell_value > HI_LIMIT:
	                row[column] = {
	                    "value": cell_value,
	                    "style": {
	                        "backgroundColor": "#FFAAAA"
	                    }
	                }                
                	
    return value

```

---

<div class="post-metadata">

**Author:** ![Transistor](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/transistor/32/3380_2.png) [@Transistor](https://forum.inductiveautomation.com/u/Transistor)\
**Post date:** [May 2, 2025, 5:13pm UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/13 "2025-05-02T17:13:23Z")

</div>

Nobody has ever accused me of being an expert coder but I did pick up some useful tips on CodeAesthetic's [Why You Shouldn't Nest Your Code](https://www.youtube.com/watch?v=CFRhGnuXG-4&ab_channel=CodeAesthetic). He's using C in the video but the same principles will apply to your Python code.

Your code is four levels deep by the time you get to `row[column] = {`. I've refactored it below, refactoring by "inversion" with early `continue` and now it's only two levels deep. I think it's a lot easier to follow.

> [@Andrew\_Buldyzhov](#):
>
> ```python
> def transform(self, value, quality, timestamp):
>     
> HI_LIMIT = 5.0
>     
> for row in value: # Iterate through each row in the data
> for column, cell_value in row.items(): # Iterate through each cell in the row
> if column = "SampleDate": # Skip SampleDate column not to break the date picker in the embedded view
> continue
> if cell_value <= HI_LIMIT:
> continue
> row[column] = {
> "value": cell_value,
> "style": {
> "backgroundColor": "#FFAAAA"
> }
> }                
>                 	
> return value
> 
> ```

---

<div class="post-metadata">

**Author:** ![Andrew\_Buldyzhov](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/andrew_buldyzhov/32/74246_2.png) [@Andrew\_Buldyzhov](https://forum.inductiveautomation.com/u/Andrew_Buldyzhov)\
**Post date:** [May 6, 2025, 10:00am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/14 "2025-05-06T10:00:45Z")

</div>

Indeed, the code looks better without the excessive nesting. Thank you for your suggestion, @Transistor. Only I think we need to use `continue` instead of `return` to skip the current cycle and proceed with the next iteration. Also, these two conditions could be combined into one:

```python
if (column = "SampleDate") or (cell_value <= HI_LIMIT):
    continue

```

In my resulting code it cannot be done, though, because the structure changed a bit:

```python
def transform(self, value, quality, timestamp):
    # Fetch limits from the database using a named query
    limits_query = system.db.runNamedQuery("Select_all_limits")
    
    # Extract low and high limits from the query result
    low_limits = limits_query[0]
    high_limits = limits_query[1]
    
    for row in value: # Iterate through each row in the data
        for column, cell_value in row.items(): # Iterate through each cell in the row
            if (column in ("SampleDate", "id")) or cell_value == None: # Skip SampleDate column not to break the date picker in the embedded view
            	continue
            try:
                numeric_value = float(cell_value) # Convert cell value to a float
                
                # Get the low and high limits for the current column
                low_limit = float( low_limits[column])
                high_limit = float(high_limits[column])
                
                # Check if the value is outside the limits and set the background color accordingly
                if (numeric_value >= low_limit) and (numeric_value <= high_limit):
                	continue 
                row[column] = {
                    "value": cell_value,
                    "style": {
                        "backgroundColor": "#FFAAAA" # Red background for out-of-limit values
                    }
                }
            except ValueError:
                pass # if conversion to float fails
    
    return value

```

---

<div class="post-metadata">

**Author:** ![Transistor](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/transistor/32/3380_2.png) [@Transistor](https://forum.inductiveautomation.com/u/Transistor)\
**Post date:** [May 6, 2025, 10:56am UTC](https://forum.inductiveautomation.com/t/table-cell-change-to-date-and-time-picker/100785/15 "2025-05-06T10:56:19Z")

</div>

> [@Andrew\_Buldyzhov](#):
>
> Only I think we need to use `continue` instead of `return`

You're right. I'll edit my post!
