Forum Archive

modify tableViewCell delete

Drizzel

Why can’t I put a console alert in a modified tableViewCell delete function? My screen always freezes when trying to delete a tableViewCell. Here’s a basic example:

```
import ui
import console

def edited_delete(tv, section, row):
# Called when the user confirms deletion of the given row.
try:
console.alert('delete' '', '', 'confirm', hide_cancel_button=False)
tv.delete_rows([row])
except KeyboardInterrupt: None

data = [0,1,2,3,4,5,6]

table = ui.TableView()
table.data_source = ui.ListDataSource(data)
table.data_source.tableview_delete = edited_delete

table.present()```

cvp

@Drizzel No freeze for me but delete row 1, confirm gives an error

Traceback (most recent call last):
  File "/private/var/mobile/Containers/Shared/AppGroup/668A7D98-7216-47ED-917D-AA0B6173167E/Pythonista3/Documents/t.py", line 8, in edited_delete
    tv.delete_rows([row])
ValueError: Inconsistent number of rows after deletion
JonB

In order for the UI to continue responding, you cannot put modal things in a callback like that. Instead, you can pop up a message, which then has a callback to continue whatever operation was being done. Or, you can use in_background, I think.

As cvp suggests, I think you need to tell the data source that the data is deleted. The tv delete row method is just the display part -- shows an animation and removes the row.

Drizzel

@cvp yeah, I missed some lines, I added them in the code below.
@JonB That fixes the frozen screen, thanks :)
However, the visual behaviour is odd now, although I might just have to accept that

import ui
import console

def song_delete(tv, section, row):
    # Called when the user confirms deletion of the given row.
    try:
        console.alert('delete' '', '', 'confirm', hide_cancel_button=False)
        data.pop(row)
        tv.data_source = ui.ListDataSource(data)
        tv.delete_rows([row])
    except KeyboardInterrupt: None
    table.data_source.tableview_delete = ui.in_background(song_delete)

data = [0,1,2,3,4,5,6]

table = ui.TableView()
table.data_source = ui.ListDataSource(data)
table.data_source.tableview_delete = ui.in_background(song_delete)

table.present()

cvp

@Drizzel why did you put in the song_delete function the line

    table.data_source.tableview_delete = ui.in_background(song_delete) 

and what is odd?

Drizzel

@cvp My apologies for being unclear, I just renamed the modified delete function to song_delete. And I would much prefer to just have the tableViewCell show the delete button while the console alert is shown. And after I confirmed or cancelled the delete, it should slide back or be deleted.

But as I said above, I might just get used to it and leave it as is :)

import ui
import console

def delete(tv, section, row):
    #Called when the user confirms deletion of the given row.
    try:
        console.alert('delete' '', '', 'confirm', hide_cancel_button=False)
        data.pop(row)
        tv.data_source = ui.ListDataSource(data)
        tv.delete_rows([row])
    except KeyboardInterrupt: None
    table.data_source.tableview_delete = ui.in_background(delete) #otherwise the tableview_delete function turs back to the standard behaviour

data = [0, 1, 2, 3, 4, 5, 6]

table = ui.TableView()
table.data_source = ui.ListDataSource(data)
table.data_source.tableview_delete = ui.in_background(delete)

table.present()


cvp

@Drizzel understood, thanks. Not sure you could get the wanted visual process

cvp

@Drizzel It should be possible in Objectivec, see here, but sincerely, it seems too complex for me 😢

JonB

You ought to be able to use ui.in_background as a decorator, and eliminate the other places you use it.

cvp

You could start from this little script, based on this

# coding: utf-8
# https://gist.github.com/shaun-h/da54aafc2aeaf5a316e8de1b56d19fd3
from __future__ import absolute_import
from objc_util import *
import ui
from collections import OrderedDict
from six.moves import range
import console

UITableView = ObjCClass('UITableView')
UITableViewCell = ObjCClass('UITableViewCell')

class UITableViewStyle(object):
        UITableViewStylePlain = 0
        UITableViewStyleGrouped = 1

def test_data(number):
    data = OrderedDict()
    valueRange = 7
    for i in range(0,number):
        ii = i * valueRange
        key = str(ii)
        value = []
        for j in range(ii,ii+valueRange):
            value.append(str(j))
        data[key] = value
    return data

data = test_data(1)

def tableView_cellForRowAtIndexPath_(self,cmd,tableView,indexPath):
    ip = ObjCInstance(indexPath)
    cell = ObjCInstance(tableView).dequeueReusableCellWithIdentifier_('mycell')

    if cell == None:
        cell = UITableViewCell.alloc().initWithStyle_reuseIdentifier_(0,'mycell')
    key = list(data.keys())[ip.section()]
    text = ns(data[key][ip.row()])
    cell.textLabel().setText_(text)

    return cell.ptr

def numberOfSectionsInTableView_(self,cmd,tableView):
    return len(data)

def tableView_numberOfRowsInSection_(self,cmd, tableView,section):
    key = list(data.keys())[section]
    return ns(len(data[key])).integerValue()

def sectionIndexTitlesForTableView_(self,cmd,tableView):
    return ns(list(data.keys())).ptr

def tableView_sectionForSectionIndexTitle_atIndex_(self,cmd,tableView,title,index):
    #I have assumed order and number of list is the same from list and sections
    return index

def tableView_titleForHeaderInSection_(self,cmd,tableView,section):
    return ns('Header for ' + list(data.keys())[section]).ptr

def tableView_titleForFooterInSection_(self,cmd,tableView,section):
    return ns('Footer for ' + list(data.keys())[section]).ptr

def tableView_commitEditingStyle_forRowAtIndexPath_(self,cmd,tableView,editingStyle,indexPath):
    if editingStyle == 1:
        # delete
        section_row = ObjCInstance(indexPath)
        row = section_row.row()
        console.alert('delete row',str(row), 'confirm', hide_cancel_button=False)


#def tableView_canEditRowAtIndexPath_(self,cmd,tableView,indexPath):
    #pass

#def tableView_canMoveRowAtIndexPath_(self,cmd,tableView,indexPath):
    #pass

#def tableView_moveRowAtIndexPath_toIndexPath_(self,cmd,tableView,fromIndexPath,toIndexPath):
    #pass



methods = [tableView_cellForRowAtIndexPath_,tableView_numberOfRowsInSection_,numberOfSectionsInTableView_,tableView_titleForHeaderInSection_,tableView_sectionForSectionIndexTitle_atIndex_,sectionIndexTitlesForTableView_,tableView_titleForFooterInSection_, tableView_commitEditingStyle_forRowAtIndexPath_]
protocols = ['UITableViewDataSource']
TVDataSourceAndDelegate = create_objc_class('TVDataSourceAndDelegate', NSObject, methods=methods, protocols=protocols)

class MyTableView(ui.View):
    @on_main_thread
    def __init__(self, *args, **kwargs):
        ui.View.__init__(self, *args, **kwargs)
        frame = CGRect(CGPoint(0, 0), CGSize(self.width, self.height))
        self.tableView = UITableView.alloc().initWithFrame_style_(frame, UITableViewStyle.UITableViewStylePlain)
        flex_width, flex_height = (1<<1), (1<<4)
        self.tableView.setAutoresizingMask_(flex_width|flex_height)

        #set delegate
        self.tb_ds = TVDataSourceAndDelegate.alloc().init().autorelease()
        self.tableView.setDataSource_(self.tb_ds)
        self_objc = ObjCInstance(self)
        self_objc.addSubview_(self.tableView)
        self.tableView.release()

if __name__ == '__main__':
    wv = MyTableView()
    wv.name = 'for @Drizzel eyes only 😀'
    wv.present() 

Drizzel

@cvp Thanks a lot! Definitely didn’t expect thou to write a full script :) Although, after all, you basically coded the base for the music player I’m working on.
I will definitely try it out tomorrow, I’m too tired right now😴

I already love the “for @Drizzel eyes only 😀”

cvp

@Drizzel The script is from Shaun-h, not mine. I only added some lines for your alert in the delete case.
And the code is not sufficient so, you still have to add some lines for the real delete, when confirmed. Good work and good luck