Fran├žois Rappaz
and 1 contributors
    # $self->{subform} = [];

    #my %formatters_db;
    #my %formatters_f;
    # $self->{dates_formatted} = \(keys %{$self->{date_formatters}});
    foreach my $v ( keys %{ $self->{date_formatters} } ) {
        $log{ $id }->debug( "** " . $v . " **" );
        push @dates, $v;
    }
    $self->{dates_formatted} = \@dates;
    my %hdates = map { $_ => 1 } @dates;
    $self->{hdates_formatted} = \%hdates;
    $self->{dates_formatters} = {};

    $self->{pos2del}   = [];
    $self->{inserting} = 0;

NAME

Wx::Perl::DbLinker::Wxform - a module that display data from a database in a Wx widgets user interface described in a xrc file.

VERSION

See Version in Wx::Perl::DbLinker

SYNOPSIS

        use Rdb::Coll::Manager;
        use Rdb::Biblio::Manager;

        use Gtk2::Ex::DbLinker::RdbDataManager;
        use Wx::Perl::DbLinker::Wxform;

        use Wx qw[:everything];
        use Wx::XRC;

         $self->{xrc} = Wx::XmlResource->new();
         $self->{xrc}->InitAllHandlers();
         $self->{xrc}->Load($path_to_xrc_file) or die(....);

         $self->{frame} = $self->{xrc}->LoadFrame(undef, 'mainwindow'});

This gets the Rose::DB::Object::Manager (we could have use plain sql command, or DBIx::Class object instead), and the DataManager object we pass to the form constructor.

        my $data = Rdb::Mytable::Manager->get_mytable(query => [pk_field => {eq => $value]);

        my $dman = Gtk2::Ex::DbLinker::RdbDataManager->new(data=> $data, meta => Rdb::Mytable->meta );

This create the form.

                $self->{form_coll} = Wx::Perl::DbLinker::Wxform->new(
                        data_manager => $dman,
                        builder =>      $builder,
                        rec_spinner => $self->{dnav}->get_object('RecordSpinner'),
                        status_label=>  $self->{dnav}->get_object('lbl_RecordStatus'),
                        rec_count_label => $self->{dnav}->get_object("lbl_recordCount"),
                        on_current =>  sub {on_current($self)},
                        date_formatters => {
                                field_id1 => ["%Y-%m-%d", "%d-%m-%Y"], 
                                field_id2 => ["%Y-%m-%d", "%d-%m-%Y"], },
                        time_zone => 'Europe/Zurich',
                        locale => 'fr_CH',
            );

$builder is an object that has a get_object($name) method described below and frame method that return the main frame.

rec_spinner, status_label, rec_count_label are Gtk2 widget used to display the position of the current record. See one of the example 2 files in the examples folder for more details. date_formatters receives a hash of id for the Gtk2::Entries in the Glade file (keys) and an arrays (values) of formating strings.

In this array

  • pos 0 is the date format of the database.

  • pos 1 is the format to display the date in the form.

time_zone and locale are needed by Date::Time::Strptime.

To display new rows on a bound subform, connect the on_change event to the field of the primary key in the main form. In this sub, call a sub to synchonize the form:

In the main form:

    sub on_nofm_changed {
        my $widget = shift;
        my $self = shift;
        my $pk_value = $widget->get_text();
        $self->{subform_a}->synchronize_with($pk_value);
        ...
        }

In the subform_a module

    sub synchronize_with {
        my ($self,$value) = @_;
        my $data = Rdb::Product::Manager->get_product(with_objects => ['seller_product'], query => ['seller_product.no_seller' => {eq => $value}]);
        $self->{subform_a}->get_data_manager->query($data);     
        $self->{subform_a}->update;
     }

Dealing with many to many relationship

It's the sellers and products situation where a seller sells many products and a product is selled by many sellers. One way is to have a insert statement that insert a new row in the linking table (named transaction for example) each time a new row is added in the product table.

An other way is to create a data manager for the transaction table

With DBI

        $dman = Gtk2::Ex::DbLinker::DbiDataManager->new( dbh => $self->{dbh}, sql =>{select =>"no_seller, no_product", from => "transaction", where => ""});

With Rose::DB::Object

        $data = Rdb::Transaction::Manager->get_transaction(query=> [no_seller => {eq => $current_seller }]);

        $dman = Gtk2::Ex::DbLinker::RdbDataManager->new(data => $data, meta=> Rdb::Transaction->meta);

And keep a reference of this for latter

      $self->{linking_data} = $dman;

If you want to link a new row in the table product with the current seller, create a method that is passed and array of primary key values for the current seller and the new product.

        sub update_linking_table {
                my ( $self, $keysref) = @_;
                my @keys = keys %{$keysref};
                my $f =  $self->{main_form};
                my $dman = $self->{main_abo}->{linking_data};
                $dman->new_row;
                foreach my $k (@keys){
                        my $value = ${$keysref}{$k};
                        $dman->set_field($k, $value );
                }
                $dman->save;
        }

This method is to be called when a new row has been added to the product table:

        sub on_newproduct_applied_clicked {
                my $button = shift;
                my $self = shift;
                my $main = $f->{main_form};
                $self->{product}->apply;
                my %h;
                $h{no_seller}= $main->{no_seller};
                $h{no_product}= $self->{abo}->get_widget_value("no_product");
                $self->update_linking_table(\%h);
        }

You may use the same method to delete a row from the linking table

        my $data = Rdb::Transaction::Manager->get_transaction(query=> [no_seller => {eq => $seller }, no_product=>{eq => $product } ] );
        $f->{linking_data}->query($data);
        $f->{linking_data}->delete;

DESCRIPTION

This module automates the process of tying data from a database to Wx widgets form described by a xrc file. Name the widgets in the xrc file using the fields in your data source.

Steps for use:

  • Create a xxxDataManager object that contains the rows to display

  • Create a builder object with a get_object($name) method

  • Create a Wx::Perl::DbLinker::Wxform object that links the data and your form

  • You would then typically connect the buttons to the methods below to handle common actions such as inserting, moving, deleting, etc.

METHODS

constructor

The new(); method expects a list of parameter name => value or a hash reference of parameter name (key) / value pairs

The parameters are:

  • data_manager a instance of a xxxDataManager object

  • builder

    $builder is an object that must have a get_object($object_name) which is a wrapper around Wx::Window::FindWindowByName and frame() method that return the application main window (holds in $self-{frame}> below:

       sub get_object {
            my ($self, $id) = @_;
            my $w = Wx::Window::FindWindowByName($id, $self->{frame});
            $log{ $id }->debug("get_object: $id not found") unless (defined $w);
            return $w;
        }

The following parameters are optional

  • datawidgets a reference to an array of id in the glade file that will display the fields

  • rec_spinner the name of a GtkSpinButton to use as the record spinner or a reference to this widget. The default is to use a widget called RecordSpinner.

  • rec_count_label name (default to "lbl_RecordCount") or a reference to a label that indicate the position of the current row in the rowset

  • status_label name (default to "lbl_RecordStatus") or a reference to a label that indicate the changed or syncronized flag of the current row

  • on_current a reference to sub that will be called when moving to a new record

  • date_formatters a reference to an hash of Gtk2Entries id (keys), and format strings that follow Rose::DateTime::Util (value) to display formatted Date

  • auto_apply defaults to 1, meaning that apply will be called if a changed has been made in a widget before moving to another record. Set this to 0 if you don't want this feature

  • datawidgets_changed is a hash ref having the field's name as keys and a code ref that will be called when the field content is changed.

                    $self->{sform} = Wx::Perl::DbLinker::Wxform->new(
                            ...
                            datawidgets_changed => {langid=> sub{ on_langid_changed(@_, $self) } },
    
                    );
    
                    sub on_langid_changed {
                            my ($widget, $event, $self) = @_;
                            my $value = $widget->GetLineText(0);
                            if ($value) {
                                    $self->{langid} = $value;
                                    $self->{sf_list}->get_data_manager->query( $self->{schema}->resultset('Speak')->search_rs({langid => $value, countryid => {'!=' => $self->{countryid}} })  );
                                    $self->{sf_list}->update;
                            }
                    }
  • datawidgets_ro is an array ref that gives the field's name that will be read-only.

add_combo( data_manager => $dman, id => 'noed', fields => ["id", "nom"] );

Once the constructor has been called, combo designed in the xrc file received their rows with this method. Two Wx objects are supported Wx::ListCtrl and Wx::Combo.

return value an array ref of the first field values is returned since in you have to retrieve the index of the selected row (holding nom values) to access the corresponding id

parameters is a list of paramater name => value or a hash reference of key (parameter name) and value, and the parameters are:

  • data_manager a dataManager instance that holds the rows of the combo

  • id the id of the widget in the xrc file

  • fields an array reference holdings the names of fields in the combo (this parameter is needed with RdbDataManager only)

Wx::Perl::DbLinker::Wxform->add_combo( data_manager => $combodata, id => 'countryid', builder => $builder );

This method can also be called as a class method, when the underlying form is not bound to any table. You need to pass the builder object (described above) as a supplemental parameter.

get_combo_ids( $combo_name );

Return an hash ref of { line number => first column value } for combo_name. First line is 0.

update();

See "update()" in Gtk2::Ex::DbLinker::AbForm

get_data_manager();

See "get_data_manager()" in Gtk2::Ex::DbLinker::AbForm

set_data_manager( $dman )

See "set_data_manager( $dman )" in Gtk2::Ex::DbLinker::AbForm

get_widget_value ( $widget_id );

Returns the value of a data widget from its id

set_widget_value ( $widget_id, $value );

Sets the value of a data widget from its id

Methods applied to a row of data

insert() See "insert()" in Gtk2::Ex::DbLinker::AbForm
delete() See "delete()" in Gtk2::Ex::DbLinker::AbForm
apply() See "apply()" in Gtk2::Ex::DbLinker::AbForm
undo() See "undo()" in Gtk2::Ex::DbLinker::AbForm
next() See "Moving between rows" in Gtk2::Ex::DbLinker::AbForm
previous() See "Moving between rows" in Gtk2::Ex::DbLinker::AbForm
first() See "Moving between rows" in Gtk2::Ex::DbLinker::AbForm
last() See "Moving between rows" in Gtk2::Ex::DbLinker::AbForm
add_childform( $childform ) See "add_childform( $childform )" in Gtk2::Ex::DbLinker::AbForm
has_changed() See "has_changed()" in Gtk2::Ex::DbLinker::AbForm

SUPPORT

Any Wx::Perl::DbLinker::Wxform questions or problems can be posted to me (rappazf) on my gmail account.

The current state of the source can be extract using Mercurial from http://sourceforge.net/projects/wx-perl-dblinker/.

AUTHOR

François Rappaz <rappazf@gmail.com>

COPYRIGHT

Copyright (c) 2015-2017 by F. Rappaz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

Gtk2::Ex::DbLinker Gtk2::Ex::DbLinker::DbTools.