Power Table drag and drop

I cannot get this to function. I am using the code in the manual but it literally does nothing.

[code]
destDataset = event.source.data.getData()
pyRowData = system.dataset.toPyDataSet(rowData)

Loop thru all the rows that have been selected and dragged to the

destination table.

for row in pyRowData:
newRow = []
for column in row:
newRow.append(column)
destDataset = system.dataset.addRow(event.source.data, dropIndexLocation, newRow)

Adds the rows to the destination table.

event.source.data.setData(destDataset)

Optional. Deletes the dragged rows from the source table.

#sourceDataset = system.dataset.deleteRows(sourceTable.getData(), rows)
#sourceTable.setData(sourceDataset)[/code]

Thanks!!

Did you indent all your code?

The editor wants to put your first line at the left margin, but because you’re doing the body of the function, it needs to start out indented. That’s tripped me up more than once.

I don’t think this is it. I tried a single indent of all of the code and still have the same result. I can initiate a messagebox inside the event but if I try writing the internal variables of the function to custom variables on the root container it doesn’t write anything. I also get no errors either. Very wierd. Ignition version 7.7.0

I don’t know why but it works now with this code:

[code] # manipulate underlying data here

destDataset = self.getData()
pyRowData = system.dataset.toPyDataSet(rowData)        
#system.gui.messageBox("TYES1")	
	 
	
# Loop thru all the rows that have been selected and dragged to the 
# destination table.        
for row in pyRowData:
	#system.gui.messageBox("TYES2")
	newRow = []
	for column in row:
		#system.gui.messageBox("TYES3")
		newRow.append(column)
	destDataset = system.dataset.addRow(destDataset,dropIndexLocation , 
		newRow)
	
		
# Adds the rows to the destination table.
self.setData(destDataset)
#event.source.parent.dataSet.setData(destDataset)

 

# Optional. Deletes the dragged rows from the source table.        
sourceDataset = system.dataset.deleteRows(sourceTable.getData(), rows)
sourceTable.setData(sourceDataset)

[/code]

Hmmm… I can’t replicate this. Here’s what I did:
[ul][li]Set up two power tables on a window.[/li]
[li]Both have row dragging enabled. [/li]
[li]Both are using the test data. [/li]
[li]In onRowsDropped, both have exactly the script from the manual[/li][/ul]
Dragging and dropping works like a charm.

If you try exactly these steps and still have a problem, you may want to have Tech Support do a GoToMeeting with you. I’m sure it’s something easy that’s been overlooked.

(note that the online manual isn’t displaying the initial indentation very well. I’ll see what I can do to fix that for 7.7.2. To make sure there’s no questions, I’ve uploaded a screenshot of the script.)


Ha! posted at the same time.

Glad it’s working for you now. :slight_smile:

I did have another question though. If I am dragging and dropping to the same table, what method would you recommend for deleting the original row? I don’t think the code at the bottom will work in this situation because if you have inserted the row above the original location then the row index will have gone up by 1 (or by as many rows as you’ve dropped).

You got me curious, so I experimented. As you suspected, the indices get messed up. If I get time later on, I’ll try to whip up a script that handles this correctly. (Don’t hold your breath, though. I literally have 70 things on my to do list right now.) :confused:

I think I got it. Requires 2 custom properties on the table component

[code] self.droppedRow = dropIndexLocation
self.originalRow = rows[0]
if system.gui.confirm(“Move KQC?”):
if self.droppedRow > self.originalRow:
# manipulate underlying data here
destDataset = self.getData()
pyRowData = system.dataset.toPyDataSet(rowData)
# Loop thru all the rows that have been selected and dragged to the
# destination table.
for row in pyRowData:
newRow = []
for column in row:
newRow.append(column)
destDataset = system.dataset.addRow(destDataset,dropIndexLocation, newRow)
#if dropIndexLocation <= rows(0,0):
#rows = rows + 1

		# Adds the rows to the destination table.
		#system.gui.messageBox(rows)
		self.setData(destDataset)
		# Optional. Deletes the dragged rows from the source table.        
		sourceDataset = system.dataset.deleteRows(sourceTable.getData(), rows)
		sourceTable.setData(sourceDataset)
	if self.droppedRow <= self.originalRow:
		# manipulate underlying data here
		destDataset = self.getData()
		pyRowData = system.dataset.toPyDataSet(rowData)        
		# Loop thru all the rows that have been selected and dragged to the 
		# destination table.        
		for row in pyRowData:
			newRow = [] 
			for column in row:
				newRow.append(column)
			destDataset = system.dataset.addRow(destDataset,dropIndexLocation, newRow)
		#if dropIndexLocation <= rows(0,0):
			#rows = rows + 1
			
		# Adds the rows to the destination table.
		#system.gui.messageBox(rows)
		self.setData(destDataset)
		# Optional. Deletes the dragged rows from the source table. 
		deleteRow = self.originalRow + 1
		rows = [deleteRow]       
		sourceDataset = system.dataset.deleteRows(sourceTable.getData(), rows)
		sourceTable.setData(sourceDataset)[/code]
2 Likes

:thumb_left:

Just a note for any future users who cruise through this thread – a quick glance says this is probably relying on multiple interval selection being turned off. You’ll have a much more fun (not!) calculation if it’s turned on.

1 Like

Yes, sorry I forgot to mention that this only applies to a single selection. I did not calculate a range on the rows selected as it did not apply to my project. Thanks for your help!!

Thanks! Saved me an hour…

2 Likes

Here is what I cam up with. This allows a single row to be moved between tables, and reordered in it’s own table. I have tested it with a few different sets of data, but let me know if you find something that doesn’t work. Put this code on the onRowsDropped function of all tables that you want to enable dragging on.

This does not work on tables that allow multiple rows to be selected.

Hopefully the comments help explain what I’m doing.

# If dragging to another table, rather than itseelf.
if self != sourceTable:
	#Get the current dataset of the destination table. All the rows
	destDataset = self.getData()
	
	#Convert the row that was dragged from the source table, to a py dataset
	pyRowData = system.dataset.toPyDataSet(rowData)        

	#Loop through all rows that were selected (should just be 1)
	#And then loop through each column, adding it to the 'newRow' array
	#Then, add the new row to the destination dataset.
	for row in pyRowData:
		newRow = []
		for column in row:
			newRow.append(column)
		destDataset = system.dataset.addRow(destDataset, dropIndexLocation, newRow)
			
	# Add the entire dataset to the table object
	self.setData(destDataset)
	
	# Deletes the dragged rows from the source table.
	# rows is actually an array, but we are only selecting one row      
	sourceDataset = system.dataset.deleteRows(sourceTable.getData(), rows)
	sourceTable.setData(sourceDataset)

#Dragging to itself		
else:

	#Convert the dragged row to a pyDataSet
	pyRowData = system.dataset.toPyDataSet(rowData) 

	#Get the number of rows originally in the dataset, used to calculate index later
	sourceDataset_OrignalCount = self.getData().getRowCount()

	#Delete the row from the dataset
	sourceDataset = system.dataset.deleteRows(self.getData(), rows)

	#Loop through the dragged row data, and add it to the dataset
	for row in pyRowData:
		newRow = []
		for column in row:
			newRow.append(column)

		#If dragging to the row after the last one, there will need to be an adjustment made.
		#The index was deleted earlier, so the new one will be out of range.
		#We need to reduce the index by 1
		if sourceDataset_OrignalCount == dropIndexLocation:
			sourceDataset = system.dataset.addRow(sourceDataset, dropIndexLocation-1, newRow)
		#If the row was not dragged to the end, nothing needs to be changed
		else:
			sourceDataset = system.dataset.addRow(sourceDataset, dropIndexLocation, newRow)

	# Add the altered dataset back to the table object.
	self.setData(sourceDataset)
	

2 Likes

This allows a single row or multiple rows to be moved between the same table. Seems to work with multiple and single interval selection mode. Sorry for the list comprehensions. The try and except block are needed because of indexing issues when moving rows to the bottom of a dataset.

data = self.data

allRows = [[rowData.getValueAt(rowNdx, colNdx) for colNdx in range(rowData.columnCount)] for rowNdx in range(rowData.rowCount)]

try:
	self.data = system.dataset.deleteRows(self.data,rows)
	self.data = system.dataset.addRows(self.data, dropIndexLocation, allRows)
except:
	data = system.dataset.addRows(data, dropIndexLocation, allRows)
	data = system.dataset.deleteRows(data,rows)
	self.data = data

self.selectedRow = -1
3 Likes

I have used this and it works, but since my data is coming from a database it reverts back when it polls.

You will need to update the respective databases during the operation to either remove or insert the rows.

If you're trying to use this to "sort", then that's a different problem and will not be able to work on a dataset that is bound to a database source (unless you are doing some work to insure the order of the rows is persisted).

1 Like

Thanks for your reply, still learning databases. I seem to be missing something

# If dragging to another table, rather than itseelf.
	if self != sourceTable:
		#Get the current dataset of the destination table. All the rows
		destDataset = self.getData()
		
		#Convert the row that was dragged from the source table, to a py dataset
		pyRowData = system.dataset.toPyDataSet(rowData)        
	
		#Loop through all rows that were selected (should just be 1)
		#And then loop through each column, adding it to the 'newRow' array
		#Then, add the new row to the destination dataset.
		for row in pyRowData:
			newRow = []
			for column in row:
				newRow.append(column)
			destDataset = system.dataset.addRow(destDataset, dropIndexLocation, newRow)
				
		# Add the entire dataset to the table object
		self.setData(destDataset)
		
		# Deletes the dragged rows from the source table.
		# rows is actually an array, but we are only selecting one row      
		sourceDataset = system.dataset.deleteRows(sourceTable.getData(), rows)
		sourceTable.setData(sourceDataset)
	
	#Dragging to itself		
	else:
	
		#Convert the dragged row to a pyDataSet
		pyRowData = system.dataset.toPyDataSet(rowData) 
	
		#Get the number of rows originally in the dataset, used to calculate index later
		sourceDataset_OrignalCount = self.getData().getRowCount()
	
		#Delete the row from the dataset
		sourceDataset = system.dataset.deleteRows(self.getData(), rows)
	
		#Loop through the dragged row data, and add it to the dataset
		for row in pyRowData:
			newRow = []
			for column in row:
				newRow.append(column)
	
			#If dragging to the row after the last one, there will need to be an adjustment made.
			#The index was deleted earlier, so the new one will be out of range.
			#We need to reduce the index by 1
			if sourceDataset_OrignalCount == dropIndexLocation:
				sourceDataset = system.dataset.addRow(sourceDataset, dropIndexLocation-1, newRow)
			#If the row was not dragged to the end, nothing needs to be changed
			else:
				sourceDataset = system.dataset.addRow(sourceDataset, dropIndexLocation, newRow)
	
		# Add the altered dataset back to the table object.
		self.setData(sourceDataset)
		
		
# This example will take a dataset from a table component, and insert new records into the database, one row at a time
		 
		 
		# Read the contents of the table
		tableData = event.source.parent.getComponent('PT').data
		 
		# Convert it to a PyDataset. This is mostly for convenience, as they're easier to iterate through
		pyData = system.dataset.toPyDataSet(tableData)
		 
		# Build the query we'll use. You could easily modify the line to accommodate the table you're trying to insert into.
		query = "INSERT INTO proSchedule_D1 (CaseAmt, Date, Description, Line, Lot, MO, Product) VALUES (?, ?, ?, ?, ?, ?, ?)"
		 
		# Iterate
		for row in pyData:
		 
		    # Build an arguments list based on the current row. Using indexing here, so 'row[0]' is the 1st column, 'row[1]' is the 2nd column, etc
		    args = [CaseAmt, Date, Description, Line, Lot, MO, Product]
		     
		    # Add a row to the database. You could optionally check the contents of the row first, and add an if-statement to prevent the record based on some criteria
		    system.db.runPrepUpdate(query, args,)

You need to get the data from the row(s) you are dropping and insert that into the database table that is providing the data to the table that is the drop target.

The script that you've used to start with, doesn't deal with database tables, but rather with the datasets on the power table components. Those two things are similar but not the same.

Your addition to that script I don't believe will execute as you are using variables which have not been defined. On top of that, you would be running a query for each row in the entire dataset. Not only is this not good for performance, but it will also potentially duplicate rows, or error depending on how the table in the database is configured. None of those things are good.

What you really need to do here is use the rowData parameter of the onRowsDropped extension function to build an update query to insert the newly dropped rows into the database table. Then refresh the binding so that the table will be updated.

Something like this:

#convert the dropped row data to a pyDataSet for easy iteration
pyRowData = system.dataset.toPyDataSet(rowData)

#build the query string
query = "INSERT INTO proSchedule_D1 (CaseAmt, Date, Description, Line, Lot, MO, Product) VALUES {}"
#add a set of place holders for each row that has been dropped, this accounts for dropping multiple rows.
query.format(",".join(["(?,?,?,?,?,?,?)"] * rowData.rowCount))

#build the list of values for the arguments
args = [col for col in row for row in pyRowData]

#update the database with the new rows
system.db.runPrepUpdate(query, args)

#refresh the binding on the table the rows were dropped on
system.db.refresh(self, "data")

Contrary to what the name might suggest system.db.refresh() is actually used to fire a binding, since you will have inserted new rows into the database, this will cause the binding which I would expect to have a SELECT query of some type in it, to execute populating the table with the new data that has been added.

1 Like

I've tried to figure this out for myself for a couple day but just cant put my finger on it. Bunch of variations, it does not like the args (no viable alternative) unless is ## the format line.

The problem is, that line 88 is missing a closing parenthesis.

It should be:

query.format(",".join(["(?,?,?,?,?,?,?)"] * rowData.rowCount))

Also, it appears this is for Ignition 7, so the format function might not be available.

In which case your script should look like this:

#convert the dropped row data to a pyDataSet for easy iteration
pyRowData = system.dataset.toPyDataSet(rowData)

#build the query string
query = "INSERT INTO proSchedule_D1 (CaseAmt, Date, Description, Line, Lot, MO, Product) VALUES %s"
#add a set of place holders for each row that has been dropped, this accounts for dropping multiple rows.
query = query % (",".join(["(?,?,?,?,?,?,?)"] * rowData.rowCount))

#build the list of values for the arguments
args = [col for col in row for row in pyRowData]

#update the database with the new rows
system.db.runPrepUpdate(query, args)

#refresh the binding on the table the rows were dropped on
system.db.refresh(self, "data")