#$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->{inserting}        = 0;
$self->{pos2del}          = [];

#parameter $in_db is 0 or 1 : # 0 we are reading from the db, and the format to use are at the pos 0 and 1 in the array of format for the field # 1 we are writing to the db and the format are to use in a revers order # $id is the field id # $v the date string from the form (if in_db is 1) or from the db (if in_db is 0) sub _format_date { my ( $self, $in_db, $id, $v ) = @_; my $id = id $self; $log{ $id }->debug( "format_date received date: " . $v ); my ( $pos1, $pos2 ) = ( $in_db ? ( 1, 0 ) : ( 0, 1 ) ); my $format = $self->{date_formatters}->{$id}->[$pos1]; my $f = $self->_get_dateformatter($format); my $dt = $f->parse_datetime($v) or croak( $f->errmsg ); $log{ $id }->debug( "format_date: date time object ymd: " . $dt->ymd ); $format = $self->{date_formatters}->{$id}->[$pos2]; $f = $self->_get_dateformatter($format); my $r = $f->format_datetime($dt) or croak( $f->errmsg ); $log{ $id }->debug( "format_date formatted date: " . $r );

return $r;

}

NAME

Gtk2::Ex::DbLinker::Form - a module that display data from a database in glade generated Gtk2 interface

VERSION

See Version in Gtk2::Ex::DbLinker

SYNOPSIS

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

use Gtk2::Ex::DbLinker::RdbDataManager;
use Gtk2::Ex::DbLinker::Form;

use Gtk2 -init;
use Gtk2::GladeXML;

 my $builder = Gtk2::Builder->new();
 $builder->add_from_file($path_to_glade_file);
 $builder->connect_signals($self);

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} = Gtk2::Ex::DbLinker::Form->new(
		data_manager => $dman,
		meta => Rdb::Mytable->meta,
		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',
    );

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 widgets on a Glade-generated form. All that is required is that you name your widgets the same as the fields in your data source.

Steps for use:

  • Create a xxxDataManager object that contains the rows to display

  • Create a Gtk2::GladeXML object (the form widget)

  • Create a Gtk2::Ex::DbLinker::Form 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 or a hash reference of parameters name => value pairs

  • data_manager a instance of a xxxDataManager object

  • builder a Gtk2::GladeXML builder

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

add_combo_values( $widget, $array_ref);

Populates a Gtk2::ComboBox or Gtk2::ComboBoxEntry widget with a static list of values. The array whose reference is stored in $array_ref is a list of array that described the rows: [[return value1, displayed value1], [...], ...]

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

Once the constructor has been called, combo designed in the glade file received their rows with this method. The parameter is a list of parameters name => value, or a hash reference of the same.

The paramaters are:

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

  • id the id of the widget in the glade file

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

Gtk2::Ex::DbLinker::Form-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 Gtk2::Builder object as a supplemental parameter.

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 Gk2::Ex::DbLinker 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/gtk2-ex-dblinker/.

AUTHOR

François Rappaz <rappazf@gmail.com>

COPYRIGHT

Copyright (c) 2014-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.

SEE ALSO

Gtk2::Ex::DBI

CREDIT

Daniel Kasak, whose modules initiate this work.