Re: Help! Custom cell renderers blowing up ...



Hi Torsten. Thanks, once again, for the reply.

On Thu, 2013-02-21 at 09:39 +0000, Torsten Schoenfeld wrote:

On 21.02.2013 07:28, Dan Kasak wrote:
> Can't locate object method "EDITING_STARTED" via package
> "Gtk3::Ex::Datasheet::DBI::CellRendererText" at
> /usr/lib64/perl5/site_perl/5.12.4/Gtk3.pm line 228.

I don't get this with my Gtk3 port of odot, so… no clue off-hand.  Can 
you provide a self-contained example program exhibiting this?

Done. By the way ... perhaps an easier way of me getting this working is to look at your Gtk3 port of odot ;)

Some more comments on the code:

> package Gtk3::Ex::Datasheet::DBI::CellEditableText;

You might have to put empty implementations of the virtual functions of 
Gtk3::CellEditable here:

sub START_EDITING {
   # do nothing
}

sub EDITING_DONE {
   # do nothing
}

sub REMOVE_WIDGET {
   # do nothing
}

Also done.

> use Glib::Object::Subclass
>    Gtk3::CellRendererText::,
>    properties => [
>                      Glib::ParamSpec->object(
>                                                      "editable-widget",
>                                                      "Editable widget",
>                                                      "The editable
> that's used for cell editing.",
>
> Gtk3::Ex::Datasheet::DBI::CellEditableText::,
>                                                      [ qw( readable
> writable ) ]
>                                             ),

Do you need this property?  It was a hack in Odot, and I've now removed 
it.  If you don't need it, you can also consider moving the creation of 
the editable from INIT_INSTANCE to START_EDITING.

Hmmmm ... The only way I've seen a custom CellRendererText working is by implementing a CellEditableText, as per your previous example. Perhaps I don't need it? I didn't do it on purpose ... just pulled in example code and tweaked until it worked.

>              # For some reason, the last item returned by the above call to
>              # $parent->get_columns isn't a Gtk3::TreeViewColumn, and
> therefore
>              # the $parent_set_cursor line fails. Avoid this.
>              if ( ref $next_col eq 'Gtk3::TreeViewColumn' ) {
>                  $parent->set_cursor ( $path, $next_col, 1 )
>                      if $next_col;
>              }

That shouldn't happen.  What is the last item if it's not a column?

This is a comment from WAY back in Gtk2 land. When everything is working again ... I'll re-test.

> sub GET_SIZE {
>
>      my ( $self, $widget, $cell_area ) = @_;
>
>      return $self->SUPER::GET_SIZE ( $widget, $cell_area );
>
> }

If you simply chain up, you can also just not provide any implementation 
at all.  The effect will be the same, but it will be faster as it avoids 
the roundtrip marshalling of the arguments from C to Perl back to C. 
(And GET_SIZE will be called a lot.)

Ah. Noted. I think I must have had more functionality in there at one point, and removed it, not realising I could remove the whole thing.

>      #$editable->modify_font( Gtk2::Pango::FontDescription->from_string(
> "Arial " . $cell->get( "font" ) ) );
>      $editable->modify_font( Pango::FontDescription->from_string( "Arial
> " . $cell->get( "font" ) ) );

Do you really want to hard-code a particular font here, completely 
ignoring the user's preference?  If you want to modify the font size, 
use the Gtk3::CellRendererText's "size" property (which you inherit in 
your cell renderer).

Yeah :) To be honest ... this was to make things work properly on Windows. At the time I was writing it ... 1/2 the users were on Windows 2000, and for some reason that escapes me now, this was required. I'm free of these users now, by the way ;)

---

Example code that demonstrates a number of issues I'm facing. Now I don't get the error as before, but I get a segfault instead, when clicking inside a cell to edit :(

cell_renderer.pl:

---

use strict;

use Gtk3 -init;
use Glib qw / TRUE FALSE /;

my $builder = Gtk3::Builder->new();

$builder->add_objects_from_file( "cell_renderer.ui", "window1" );

my $treeview = $builder->get_object( "treeview1" );

my $model = Gtk3::ListStore->new(
    [
        "Glib::Int"
      , "Glib::String"
    ]
);

my $renderer_one = Gtk3::Ex::Datasheet::DBI::CellRendererText->new;
$renderer_one->set( editable => TRUE );

my $column_one = Gtk3::TreeViewColumn->new_with_attributes(
    "Column 1",
    $renderer_one,
    'text'  => 0
);

$treeview->append_column( $column_one );

my $renderer_two = Gtk3::Ex::Datasheet::DBI::CellRendererText->new;
$renderer_two->set( editable => TRUE );

my $column_two = Gtk3::TreeViewColumn->new_with_attributes(
    "Column 2",
    $renderer_two,
    'text'  => 1
);

$treeview->append_column( $column_two );

$model->set(
    $model->append
  , 0, "text 1"
  , 1, "text 2"
);

$builder->get_object( "treeview1" )->set_model( $model );

$builder->get_object( "window1" )->show;

Gtk3->main;

#######################################################################################
# Custom CellRendererText
#######################################################################################

package Gtk3::Ex::Datasheet::DBI::CellEditableText;

# Copied and pasted from Odot

use strict;
use warnings;

use Glib qw(TRUE FALSE);
use Glib::Object::Subclass
  Gtk3::TextView::,
    interfaces => [ Gtk3::CellEditable:: ]
  , properties => [
                    Glib::ParamSpec->boolean(
                                                    "editing-canceled",
                                                    "Editing canceled",
                                                    "Whether editing has been canceled.",
                                                    FALSE,
                                                    [ qw( readable writable ) ]
                                           )
                ];

sub START_EDITING {
    # do nothing
}

sub EDITING_DONE {
    # do nothing
}

sub REMOVE_WIDGET {
    # do nothing
}

sub set_text {
   
    my ( $editable, $text ) = @_;
   
    $text = "" unless ( defined( $text ) );
   
    $editable->get_buffer()->set_text( $text, length( $text ) );
   
}

sub get_text {
   
    my ( $editable ) = @_;
    my $buffer = $editable->get_buffer();
   
    return $buffer->get_text( $buffer->get_bounds(), TRUE );
   
}

sub select_all {
   
    my ( $editable ) = @_;
    my $buffer = $editable->get_buffer();
   
    my ( $start, $end ) = $buffer->get_bounds();
    $buffer->move_mark_by_name( insert => $start );
    $buffer->move_mark_by_name( selection_bound => $end );
   
}

1;

package Gtk3::Ex::Datasheet::DBI::CellRendererText;

# Originally from Odot, with bits and pieces from the CellRendererSpinButton example,
# and even some of my own stuff worked in :)

use constant x_padding => 2;
use constant y_padding => 3;

use strict;
use warnings;

use Glib qw(TRUE FALSE);

use Glib::Object::Subclass
  Gtk3::CellRendererText::,
  properties => [
                    Glib::ParamSpec->object(
                                                    "editable-widget",
                                                    "Editable widget",
                                                    "The editable that's used for cell editing.",
                                                    Gtk3::Ex::Datasheet::DBI::CellEditableText::,
                                                    [ qw( readable writable ) ]
                                           ),
                    Glib::ParamSpec->boolean(
                                                    "number",
                                                    "Number",
                                                    "Should I apply number formatting to the data?",
                                                    0,
                                                    [ qw( readable writable ) ]
                                            ),
                    Glib::ParamSpec->string(
                                                    "decimals",
                                                    "Decimals",
                                                    "How many decimal places should be displayed?",
                                                    -1,
                                                    [ qw( readable writable ) ]
                                           ),
                    Glib::ParamSpec->boolean(
                                                    "currency",
                                                    "Currency",
                                                    "Should I prepend a dollar sign to the data?",
                                                    0,
                                                    [ qw( readable writable ) ]
                                            )
                ];

sub EDITING_STARTED {
    # do nothing
}

sub START_EDITING {
   
    my ( $cell, $event, $view, $path, $background_area, $cell_area, $flags ) = @_;
   
    if ( $event ) {
        return unless ( $event->button == 1 );
    }
   
    ###
   
    my $editable = Gtk3::Ex::Datasheet::DBI::CellEditableText->new();
   
    $editable->set( border_width => $cell->get( "ypad" ) );
   
    $editable->signal_connect( key_press_event => sub {
       
        my ( $editable, $event ) = @_;
       
        if (
            $event->keyval == $Gtk3::Gdk::KEY_Return ||
            $event->keyval == $Gtk3::Gdk::KEY_KP_Enter
            and not $event-> state & qw(control-mask)
           )
        {
           
            # Grab parent
            my $parent = $editable->get_parent;
           
            $editable->{ _editing_canceled } = FALSE;
            $editable->editing_done();
            $editable->remove_widget();
           
            my ( $path, $focus_column ) = $parent->get_cursor;
            my @cols = $parent->get_columns;
            my $next_col = undef;
           
            foreach my $i (0..$#cols) {
                if ( $cols[$i] == $focus_column ) {
                    if ( $event->state >= 'shift-mask' ) {
                        # go backwards
                        $next_col = $cols[$i-1] if $i > 0;
                    } else {
                        # Go forwards
                        # First check whether the next column is read_only
                        while ( $i-1 < $#cols ) {
                            $i++;
                            if ( ! $cols[$i]->{definition}->{read_only} ) {
                                last;
                            }
                        }
                        $next_col = $cols[$i];
                    }
                    last;
                }
            }
           
            # For some reason, the last item returned by the above call to
            # $parent->get_columns isn't a Gtk3::TreeViewColumn, and therefore
            # the $parent_set_cursor line fails. Avoid this.
            if ( ref $next_col eq 'Gtk3::TreeViewColumn' ) {
                $parent->set_cursor ( $path, $next_col, 1 )
                    if $next_col;
            }
           
            return TRUE;
           
        }
       
        return FALSE;
       
    });
   
    $editable -> signal_connect( editing_done => sub {
       my ( $editable ) = @_;
       my $canceled = $editable->get( 'editing-canceled' );
       # or $editable -> { _editing_canceled } if you store it directly
       $cell->stop_editing( $canceled );
       unless ( $canceled ) {
           $cell->signal_emit( edited => $path, $editable->get_text() );
       }
    });
   
    $cell->set( editable_widget => $editable );
   
    ###
   
    #$editable->modify_font( Pango::FontDescription->from_string( "Arial " . $cell->get( "font" ) ) );
   
    $editable->{ _editing_canceled } = FALSE;
    $editable->{ _path } = $path;
    $editable->set( height_request => $cell_area->{height} );
   
    $editable->set_text( $cell->get( "text" ) );
    $editable->select_all();
    $editable->show();
   
    $editable -> signal_connect_after(
            'focus-out-event'       => sub {
                                                my ( $event_box, $event ) = @_;
                                                $cell->signal_emit( edited => $editable->{ _path }, $editable->get_text );
                                                return $event;
            }
                               );
   
    return $editable;
   
}

sub get_layout {
   
    my ( $cell, $widget ) = @_;
   
    return $widget->create_pango_layout( "" );
   
}

1;

---

cell_renderer.ui:

---

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkBox" id="box1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkLabel" id="label1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">label</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow" id="scrolledwindow1">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="shadow_type">in</property>
            <child>
              <object class="GtkTreeView" id="treeview1">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <child internal-child="selection">
                  <object class="GtkTreeSelection" id="treeview-selection2"/>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button1">
            <property name="label" translatable="yes">button</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]