views:

272

answers:

1

Hi,

I'm using a GtkSheet widget in PyGTK to power my application's spreadsheet, and it gives me an API to pull and push data out of cells. (I looked at using GtkTreeView, but it seemed to be too much work)

What I don't understand is how to intercept paste requests (via ie. CTRL+V) so that I can process them rather than passing it through to the widget. Currently, when pasting from a spreadsheet the data shows up as follows:

Source becomes Destination

Is there a signal I should intercept?

I'm on Ubuntu 9.10, Python 2.6.

+3  A: 

To catch the paste event, you need to first create a custom entry class (PastableEntry in this example) that inherits from gtksheet.ItemEntry. During its initialisation, we connect to the paste-clipboard signal to trap paste events:

class PastableEntry(gtksheet.ItemEntry):
    def __init__(self):
        gtksheet.ItemEntry.__init__(self)
        self.connect('paste-clipboard', self.__on_paste)

The hard work is in the event handler. First we need to get the clipboard contents. In Unix, clipboard sources can advertise multiple data formats. Based on your screenshot, I assume you're trying to copy data from Gnumeric. Gnumeric supports application/x-gnumeric, text/html, UTF8_STRING, COMPOUND_TEXT, and STRING. For this example we'll use the UTF8_STRING format, which looks like this:

1,1 <tab> 1,2 <tab> 1,3 <newline>
2,1 <tab> 2,2 <tab> 2,3 <newline>
3,1 <tab> 3,2 <tab> 3,3

Obviously this fails horribly if any of the cells contain a tab or newline character, but we'll use this for simplicity. In a real world application you may want to parse the application/x-gnumeric or text/html formatted data.

Back to our PastableEntry class, now we define the paste event handler:

    def __on_paste(self, entry):
        clip = gtk.Clipboard()
        data = clip.wait_for_contents('UTF8_STRING')
        text = data.get_text()
        sheet = self.parent
        o_row, o_col = sheet.get_active_cell()
        for i_row, row in enumerate(text.split('\n')):
            for i_col, cell in enumerate(row.split('\t')):
                sheet.set_cell_text(o_row + i_row, o_col + i_col, cell)
        self.stop_emission('paste-clipboard')

It should be quite self-explanatory. We split the clipboard data into rows (by newline characters) and then into cells (by tab characters), and set the Sheet cell values accordingly.

The stop_emission is there to stop GTK+ from running the default handler for paste operations. Without that line, the selected cell will be overwritten with the raw data.

We then register the class with GObject:

gobject.type_register(PastableEntry)

Finally, to actually use our custom entry class, pass it to the constructor of gtksheet.Sheet:

s = gtksheet.Sheet(20, 20, "Sheet 1", entry_type=PastableEntry)
Johannes Sasongko