Programming Learning

Free IT e-learning

[Delphi / Lazarus] Membuat Aplikasi Realcount Pilpres 2014 “Sedot KPU”

Seiring dengan keramaian baik dimedia sosial, TV, dan media2 lainnya membicarakan hasil pilpres dan banyak dari teman-teman kita yang membuat “scrapping tool” untuk menarik data dari KPU, maka saya tertarik untuk membuat aplikasi serupa. aplikasi ini saya namakan “Sedot KPU 1.0”

Ide awal pembuatan aplikasi ini adalah menarik data KPU-DA1yang beralamat di http://pilpres2014.kpu.go.id/da1.php , dengan memanfaatkan lazarus dan library synapse (http://www.ararat.cz/synapse/doku.php/start) saya memulai untuk membuat aplikasi ini.

 

Investigasi Lingkungan

Untuk langkah awal pembuatan aplikasi ini, saya melihat terlebih dahulu web KPU yang akan kita “scrap” ini, berikut alur investigasi saya :

1. Saya membuka web http://pilpres2014.kpu.go.id/da1.php dengan safari, kemudian dengan menggunakan fasilitas “Developer Tool” saya memulai untuk “membongkar” rute web. berikut tampilan awal web tersebut :

web KPU

2. Dari web KPU saya mencoba untuk memakainya terlebih dahulu, ternyata mereka memakai javascript disetiap “onChange” pada saat kita memilih provinsi, kabupaten dan kota. lalu saya memulai investigasi lebih lanjut dengan menggunakan “inspect” dari developer tool safari.berikut gambar inspectnya :

Screen Shot 2014-07-20 at 12.59.27 AM

Screen Shot 2014-07-20 at 1.01.45 AM

3. Dari gambar diatas dapat kita lihat, bahwa setiap event “OnChange” akan memanggil fungsi javascript yang bernama “selectCat” dimana difungsi tersebut akan meload data dari parameter yang kita pilih.

 

Pembatasan Ide

OK, investigasi cukup..kita berikan batas pada ide ini supaya tidak terlalu kompleks, batasannya adalah :

  1. Aplikasi hanya akan menyimpan data daerah beserta hasil akhir perolehan tiap calon per daerah.
  2. Aplikasi akan menampilkan dashboard perolehan secara nasional dengan menggunakan chart.

 

Perancangan Sistem

Sebelum memulai ke bagian teknis, dan sebelum menentukan teknologi yang akan kita gunakan. maka kita akan merancang sistem kita terlebih dahulu. berikut gambar skema beserta penjelasannya :

  1. Aplikasi akan melakukan scrapping mulai dari data provinsi sampai kota, dan apabila sampai kota aplikasi ini akan mengambil data perolehan kedua calon. berikut gambar diweb yang akan kita ambil datanya :
    Screen Shot 2014-07-20 at 1.14.54 AM
  2. Pada saat scrapping, data yang didapat akan selalu disimpan didatabase. hal ini supaya kita mudah dalam membuat pengelolaan datanya apabila nantinya akan dikembangkan lebih jauh.

 

Analisa Teknologi

Selanjutnya, supaya apa yang telah dirancang dapat kita implementasikan dengan baik maka kita perlu menganalisa teknologi-teknologi yang kita anggap “mumpuni” untuk mendukung ide awal kita ini, berikut pemaparannya :

  1. Kita akan menggunakan Lazarus, kenapa lazarus? karena kebetulan saya lancar menggunakan bahasa pemrograman ini dan gratis!🙂
  2. Didalam lazarus, kita install library synapse (fungsi http), zeosdbo (koneksi database) dan memakai library internal regexpr. selain itu kita akan menggunakan komponen-komponen visual seperti biasanya untuk mempercantik User Interface.
  3. Untuk database, kita akan menggunakan MySQL.

 

Mulai Pembuatan!

Bagian ini pasti yang paling ditunggu-tunggu oleh para koder🙂 tapi jangan remehkan bagian-bagian sebelumnya karena sebenarnya konsep awal itu menentukan kekuatan dari sistem yang akan kita buat🙂 disini saya tidak akan jelaskan semua secara teknis, tetapi saya akan jelaskan beberapa bagian-bagian penting dari aplikasi ini. ok let’s start :

  1. Kita akan membuat 1 Table dan 1 View untuk menampung data yang akan ditangkap, berikut DDL sqlnya :
    -- --------------------------------------------------------
    -- Host: 127.0.0.1
    -- Server version: 5.6.12 - MySQL Community Server (GPL)
    -- Server OS: Win64
    -- HeidiSQL Version: 8.3.0.4694
    -- --------------------------------------------------------
    
    /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
    /*!40101 SET NAMES utf8 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    
    -- Dumping database structure for da1
    DROP DATABASE IF EXISTS `da1`;
    CREATE DATABASE IF NOT EXISTS `da1` /*!40100 DEFAULT CHARACTER SET utf8 */;
    USE `da1`;
    
    -- Dumping structure for table da1.dataimport
    DROP TABLE IF EXISTS `dataimport`;
    CREATE TABLE IF NOT EXISTS `dataimport` (
    `idreg` varchar(20) NOT NULL,
    `p` varchar(10) NOT NULL,
    `name` varchar(50) NOT NULL,
    `gp` varchar(10) NOT NULL,
    `prabowo` int(11) NOT NULL DEFAULT '0',
    `jokowi` int(11) NOT NULL DEFAULT '0',
    PRIMARY KEY (`idreg`,`p`)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
    
    -- Data exporting was unselected.
    
    -- Dumping structure for view da1.vw_realcount
    DROP VIEW IF EXISTS `vw_realcount`;
    -- Creating temporary table to overcome VIEW dependency errors
    CREATE TABLE `vw_realcount` (
    `prabowo` DECIMAL(32,0) NULL,
    `jokowi` DECIMAL(32,0) NULL,
    `prabowopr` VARCHAR(40) NOT NULL COLLATE 'utf8_general_ci',
    `jokowipr` VARCHAR(40) NOT NULL COLLATE 'utf8_general_ci'
    ) ENGINE=MyISAM;
    
    -- Dumping structure for view da1.vw_realcount
    DROP VIEW IF EXISTS `vw_realcount`;
    -- Removing temporary table and create final VIEW structure
    DROP TABLE IF EXISTS `vw_realcount`;
    CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` VIEW `vw_realcount` AS select sum(a.prabowo) prabowo,sum(a.jokowi) jokowi,ifnull(round((sum(a.prabowo)/(sum(a.prabowo)+sum(a.jokowi)))*100,2),'0') as prabowopr,
    ifnull(round((sum(a.jokowi)/(sum(a.prabowo)+sum(a.jokowi)))*100,2),'0') as jokowipr
    from dataimport a ;
    /*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
    /*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
    /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
  2. Kita buat project dilazarus dan membuat terlebih dahulu DataModule yang berisi sebagai berikut :
    Screen Shot 2014-07-20 at 2.11.26 AM
    Terlihat bahwa didalam DataModule ada 2 TZConnection, TZQuery dan TDataSource. sengaja dibuatkan 2 koneksi untuk monitoring dan scrapping (sebelumnya saya coba membuat 1 koneksi selalu bentrok dan error)
  3. Selanjutnya kita buat unit yang bertugas untuk menangkap konten web kpu kemudian mem-parse menggunakan regex. unit ini saya namakan DA1Class, berikut isinya :
    unit DA1Class;
    
    {$mode objfpc}{$H+}
    
    interface
    
    uses
      Classes, SysUtils,httpsend, udm,RegExpr,fpjson,Clipbrd,QueryExecutor;
    
    type
      TDA1 = class;
    
      { TDA1 }
    
      { TItemVal }
    
      TItemVal = class
        Sec,ID,Caption: string;
        p,j: integer;
        constructor Create(ASec,AID,ACaption: string ; Ap,Aj: integer);
      end;
    
      { TDA1Executor }
    
      TDA1Executor = class(TThread)
      private
        FID,FIDParent: string;
        FExecutor,FTemp: TQueryList;
        FName: string;
        FDA1: TDA1;
        procedure syncExecutor;
      public
        constructor Create(AID,AIDParent:String ;var AExecutor: TQueryList);
        destructor Destroy;override;
        property Name: string read FName write FName;
      protected
        procedure Execute; override;
      end;
    
      TDA1 = class
      private
        FID,FIDParent: string;
        FQueryExecutor: TQueryList;
        FThreadList: TList;
        function GetUrlData(AUrl: string): string;
        function getBaseRegex(AText,AExpression: string ; var outReg: TRegExpr): boolean;
        function SimpleRegEx(AText,AExpression: string): string;
        procedure getData(Ap,Agp: string;ASec: integer=0);
        function getLastRegData(AText,AExpression: string): string;
        function getRegIndex(AText,AExpression: string ; AIndex: integer): string;
        procedure SaveRegion(AID,AName,AParent,AGrandParent: string);
        procedure SaveCount(AID,AParent,APr,AJk: string);
    
        //spesific parse
        function getLabel(s: string): string;
      public
        property ID: string read FID write FID;
        property IDParent: string read FIDParent write FIDParent;
        property ThreadList: TList read FThreadList write FThreadList;
        constructor Create;
        destructor Destroy;override;
        procedure ImportData;
      end;
    
    var
      _QueryExecutor: TQueryList;
    
    const
      URL_DA1 = 'http://pilpres2014.kpu.go.id/da1.php';
    
      table_pattern = '<table.*?>(.*?)</table>';
      tr_pattern = '<tr.*?>(.*?)</tr>';
      td_pattern = '<td.*?>(.*?)</td>';
    
    implementation
    
    { TDA1Executor }
    
    procedure TDA1Executor.syncExecutor;
    begin
      FExecutor := FTemp;
    end;
    
    constructor TDA1Executor.Create(AID,AIDParent:String ;var AExecutor: TQueryList);
    begin
      inherited Create(True);
      FreeOnTerminate:=True;
      FID:=AID;
      FIDParent:=AIDParent;
      FTemp := AExecutor;
      Synchronize(@syncExecutor);
      FDA1 := TDA1.Create;
      FDA1.ID:=AID;
      FDA1.IDParent:=AIDParent;
    end;
    
    destructor TDA1Executor.Destroy;
    begin
      FreeAndNil(FDA1);
      inherited;
    end;
    
    procedure TDA1Executor.Execute;
    var
      s: string;
    begin
      try
      	 FDA1.ImportData;
      except
    
      end;
    end;
    
    { TItemVal }
    
    constructor TItemVal.Create(ASec, AID, ACaption: string ; Ap,Aj: integer);
    begin
      Sec:=ASec;
      ID:=AID;
      Caption:=ACaption;
      p := Ap;
      j := Aj;
    end;
    
    { TDA1 }
    
    function TDA1.getLabel(s: string): string;
    var
      sTemp: string;
    begin
      sTemp := StringReplace(SimpleRegEx(s,'[?>].*.[?=<]'),'<','',[rfReplaceAll]);
      sTemp := StringReplace(sTemp,'>','',[rfReplaceAll]);
      Result := Trim(sTemp);
    end;
    
    constructor TDA1.Create;
    begin
      FThreadList := TList.Create;
      FQueryExecutor := _QueryExecutor;
    end;
    
    destructor TDA1.Destroy;
    begin
      FreeAndNil(FThreadList);
    end;
    
    function TDA1.GetUrlData(AUrl: string): string;
    var
      slData: TStringList;
    begin
      slData := TStringList.Create;
      try
        HttpGetText(AUrl,slData);
        result := slData.Text;
      finally
        FreeAndNil(slData);
      end;
    end;
    
    function TDA1.getBaseRegex(AText, AExpression: string; var outReg: TRegExpr
      ): boolean;
    begin
      Result := False;
      outReg.Expression:=AExpression;
      Result := outReg.Exec(AText);
    end;
    
    function TDA1.SimpleRegEx(AText, AExpression: string): string;
    var
      o: TRegExpr;
    begin
      o := TRegExpr.Create;
      try
        if getBaseRegex(AText,AExpression,o) then
        begin
           Result := o.Match[0];
        end;
      finally
        FreeAndNil(o);
      end;
    end;
    
    procedure TDA1.getData(Ap, Agp: string;ASec: integer);
    
    function getId(s: string): string;
    begin
      result := StringReplace(SimpleRegEx(s,'[?"].*.[?="]'),'"','',[rfReplaceAll]);
    end;
    
    var
      sUri,sData,s: string;
      o: TJSONObject;
      a: TJSONArray;
      oReg: TRegExpr;
      sTable,sRow,sJk,sPr: string;
      i: integer;
      AThread: TDA1Executor;
    begin
      oReg := TRegExpr.Create;
      try
        try
          sUri:=Format('%s?cmd=select&grandparent=%s&parent=%s',[URL_DA1,AGp,Ap]);
    
          sData:='';
    
          //get data from server
          while Trim(sData) = '' do
            sData:=GetUrlData(sUri);
    
          if getBaseRegex(sData,'<option.\svalue=\s*"[^"]*".\s*[^"]*>',oReg) then
            begin
              repeat
                s := oReg.Match[0];
                SaveRegion(getId(s),getLabel(s),Ap,AGp);
    
                //jika data provinsi, maka buat thread sejumlah provinsi
                if Ap = '' then
                begin
                  AThread := TDA1Executor.Create(getId(s),ap,FQueryExecutor);
                  AThread.Name:=getLabel(s);
                  AThread.Resume;
                  FThreadList.Add(AThread);
                end
                else
            	    getData(getId(s),Ap,ASec+1);
    
              until oReg.ExecNext = false;
            end
          else
          begin
            if Pos('Rincian Jumlah Perolehan Suara Pasangan Calon Presiden dan Wakil Presiden',sData) > 0 then
            begin
              sTable:=getLastRegData(sData,table_pattern);
    
              //prabowo
              sRow:=getRegIndex(sTable,tr_pattern,3);
              sPr:=getLastRegData(sRow,td_pattern);
    
              //jokowi
              sRow:=getRegIndex(sTable,tr_pattern,4);
              sJk:=getLastRegData(sRow,td_pattern);
    
              //save count result
              SaveCount(Ap,Agp,getLabel(sPr),getLabel(sJk));
            end;
          end;
          //sData:='satu';
    
        except
          //error?retrying.
          getData(Ap,Agp,ASec);
        end;
    
      finally
        FreeAndNil(oReg);
      end;
    end;
    
    function TDA1.getLastRegData(AText, AExpression: string): string;
    var
      o: TRegExpr;
      s: string;
    begin
      s := '';
      o := TRegExpr.Create;
      try
        if getBaseRegex(AText,AExpression,o) then
        begin
          repeat
            s := o.Match[0];
    			until not o.ExecNext;
    		end;
    	finally
        FreeAndNil(o);
    	end;
    
      Result := s;
    end;
    
    function TDA1.getRegIndex(AText, AExpression: string; AIndex: integer): string;
    var
      o: TRegExpr;
      i: integer;
    begin
      Result:='';
      o := TRegExpr.Create;
      try
        i:=0;
        if getBaseRegex(AText,AExpression,o) then
        begin
          while i<AIndex do
          begin
            o.ExecNext;
            inc(i,1);
    		  end;
          Result := o.Match[0];
    		end;
    	finally
        FreeAndNil(o);
    	end;
    end;
    
    procedure TDA1.SaveRegion(AID, AName, AParent,AGrandParent: string);
    var
      ASql: string;
    begin
      ASql := Format('replace into dataimport(idreg,name,p,gp)values(''%s'',''%s'',''%s'',''%s'')',[AID,AName,AParent,AGrandParent]);
      FQueryExecutor.AddJob(ASql);
    end;
    
    procedure TDA1.SaveCount(AID, AParent, APr, AJk: string);
    var
      ASql: string;
    begin
      ASql := Format('update dataimport set prabowo = ''%s'', jokowi = ''%s'' where idreg=''%s'' and p=''%s''',[APr,AJk,AID,AParent]);
      FQueryExecutor.AddJob(ASql);
    end;
    
    
    procedure TDA1.ImportData;
    begin
      getData(FID,FIDParent);
    end;
    
    end.
    

    Pada kode diatas dapat kita lihat class utama yaitu “TDA1” yang bertugas untuk menangkap isi web kpu dan menyimpan ke dalam database, diclass tersebut terdapat beberapa procedure dan function utama yaitu :

    function GetUrlData(AUrl: string): string; //fungsi untuk mendapatkan konten web
    function getBaseRegex(AText,AExpression: string ; var outReg: TRegExpr): boolean; //untuk parse regex dan mengeluarkan result berupa object TRegExpr
    function SimpleRegEx(AText,AExpression: string): string; //parse regex simple (menampilkan hasil kecocokan yang pertama)
    procedure getData(Ap,Agp: string;ASec: integer=0); //fungsi parse seluruh konten secara rekursif
    function getLastRegData(AText,AExpression: string): string; //fungsi parse regex dan mendapatkan hasil terakhir
    function getRegIndex(AText,AExpression: string ; AIndex: integer): string; //fungsi parse regex dengan mendapatkan hasil sesuai dengan index yang ditentukan
    procedure SaveRegion(AID,AName,AParent,AGrandParent: string); //procedure untuk menyimpan daerah
    procedure SaveCount(AID,AParent,APr,AJk: string); //procedure untuk menyimpan hasil hitungan suara
    

  4. Pada unit DA1Class terdapat pula class “TDA1Executor = class(TThread) “, class ini adalah thread yang berguna supaya ketika berjalan, tidak membebani aplikasi (aplikasi tidak terlihat hang). dalam aplikasi ini kita akan membuat multi thread dimana thread berjumlah pada jumlah provinsi.
  5. setelah itu kita buat juga unit “QueryExecutor”, unit ini berisi class untuk mengexecute query-query yang ada, hal ini dilakukan supaya kita memisah antara fungsi scrapping dengan penyimpanan database. class utama yang berada didalam unit ini adalah “TQueryList”, berikut code dari unit ini :
    unit QueryExecutor;
    
    {$mode objfpc}{$H+}
    
    interface
    
    uses
     Classes, SysUtils,ZConnection, ZDataset,udm;
    
    type
    
     { TQueryExecutor }
    
     TQueryExecutor = class(TThread)
     private
     FQuery: TZQuery;
     FList: TStringList;
     public
     constructor Create(ASQLList: TStringList);
     destructor Destroy;
     protected
     procedure Execute; override;
     end;
    
     TQueryList = class
     private
     FSQLList: TStringList;
     FExecutor: TQueryExecutor;
     public
     constructor Create;
     destructor Destroy;
     procedure AddJob(ASQL: string);
     end;
    
    
    
    implementation
    
    { TQueryExecutor }
    
    constructor TQueryExecutor.Create(ASQLList: TStringList);
    begin
     inherited Create(True);
     FQuery := TZQuery.Create(nil);
     FQuery.Connection := dm.zConn;
     FList := ASQLList;
     FreeOnTerminate:=True;
    end;
    
    destructor TQueryExecutor.Destroy;
    begin
     FreeAndNil(FQuery);
     inherited;
    end;
    
    procedure TQueryExecutor.Execute;
    var
     asql : string;
    begin
     FreeOnTerminate:=True;
     while (true) and (not Terminated) do
     begin
     try
     if FList.Count < 1 then
     begin
     Sleep(500);
     Continue;
     end;
    
     if Trim(FList[0]) = '' then
     begin
     Sleep(500);
     Continue;
     end;
     with FQuery do
     begin
     Close;
     sql.Text:=FList[0];
     asql := FList[0];
     ExecSQL;
     FList.Delete(0);
     //FList.Delete(0);
     //sleep(100)
     end;
    
     except
     with dm do
     begin
     //zConn.Disconnect;
     zConn.Connect;
     end;
     end;
     end;
    end;
    
    { TQueryExecutor }
    
    constructor TQueryList.Create;
    begin
     FSQLList := TStringList.Create;
     FExecutor := TQueryExecutor.Create(FSQLList);
     FExecutor.Resume;
    end;
    
    destructor TQueryList.Destroy;
    begin
     FreeAndNil(FSQLList);
     FExecutor.Terminate;
    end;
    
    procedure TQueryList.AddJob(ASQL: string);
    begin
     FSQLList.Add(ASQL);
    end;
    
    end.
    
  6. Pada unit QueryExecutor terlihat bahwa untuk mengexecute query, kita menggunakan sistem antrian yang dijalankan oleh class thread “TQueryExecutor = class(TThread) “, alasan menggunakan TThread supaya tidak membebani thread utama (form aplikasi) yang akan menyebabkan hang.
  7. Setelah itu kita desain Form Utama sebagai berikut :
    Screen Shot 2014-07-20 at 2.15.30 AM Screen Shot 2014-07-20 at 2.15.53 AM
  8. Pada desain itu terlihat 2 tab yang masing berbeda fungsinya, tab 1 berfungsi menampilkan thread2 yang akan berjalan, kemudian pada tab 2 adalah dashboard yang berfungsi untuk menampilkan hasil real count. terdapat juga TTimer yang akan menampilkan data secara periodik. berikut isi kode Form utama ini :
    unit mainunit;
    
    {$mode objfpc}{$H+}
    
    interface
    
    uses
    Classes, SysUtils, FileUtil, TAGraph, TASeries, TASources, TAChartListbox,
    TADbSource, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, ExtCtrls,
    ZConnection, httpsend, RegExpr, DA1Class, QueryExecutor;
    
    type
    
    { TFormMain }
    
    TFormMain = class(TForm)
    btnDownload: TButton;
    ChartPie: TChart;
    ChartPiePieSeries1: TPieSeries;
    chartSource: TListChartSource;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    lbTotal: TLabel;
    lbpr: TLabel;
    lbjk: TLabel;
    lvThread: TListView;
    PageControl1: TPageControl;
    Panel1: TPanel;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    tmMon: TTimer;
    procedure btnDownloadClick(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure tmMonTimer(Sender: TObject);
    private
    FDA1: TDA1;
    
    { private declarations }
    public
    { public declarations }
    end;
    
    var
    FormMain: TFormMain;
    
    const
    URL_DA1 = 'http://pilpres2014.kpu.go.id/da1.php';
    
    implementation
    
    uses udm;
    {$R *.lfm}
    
    { TFormMain }
    
    procedure TFormMain.FormShow(Sender: TObject);
    begin
    
    end;
    
    procedure TFormMain.tmMonTimer(Sender: TObject);
    var
    i: integer;
    sPr,sJk: string;
    begin
    lvThread.Items.BeginUpdate;
    lvThread.Items.Clear;
    for i:= 0 to FDA1.ThreadList.Count - 1 do
    begin
    if FDA1.ThreadList[i] <> nil then
    begin
    if Trim(TDA1Executor(FDA1.ThreadList[i]).Name) = '' then Continue;
    with lvThread.Items.Add do
    begin
    Caption:=TDA1Executor(FDA1.ThreadList[i]).Name;
    SubItems.Add('Running');
    end;
    end;
    end;
    
    lvThread.Items.EndUpdate;
    
    with dm.qView do
    begin
    Close;
    sql.Text:='select * from vw_realcount';
    Open;
    sPr:=(Format('0|%f|?|Prabowo - Hatta (%f%s)',[FieldByName('prabowopr').AsFloat,FieldByName('prabowopr').AsFloat,'%']));
    sJk:=(Format('0|%f|?|Jokowi - JK (%f%s)',[FieldByName('jokowipr').AsFloat,FieldByName('jokowipr').AsFloat,'%']));
    
    if (FieldByName('prabowopr').AsFloat < 1) and (FieldByName('jokowipr').AsFloat < 1) then exit;
    chartSource.DataPoints.Clear;
    chartSource.DataPoints.Add(sPr);
    chartSource.DataPoints.Add(sJk);
    
    lbpr.Caption:= FormatFloat('#,##0',FieldByName('prabowo').AsFloat);
    lbjk.Caption:= FormatFloat('#,##0',FieldByName('jokowi').AsFloat);
    lbTotal.Caption:=FormatFloat('#,##0',FieldByName('prabowo').AsFloat + FieldByName('jokowi').AsFloat);
    end;
    end;
    
    procedure TFormMain.btnDownloadClick(Sender: TObject);
    var
    s: string;
    oQuery: TQueryList;
    begin
    FDA1 := TDA1.Create;
    oQuery := TQueryList.Create;
    try
    FDA1.ID:='';
    FDA1.IDParent:='0';
    FDA1.ImportData;
    tmMon.Enabled:=True;
    finally
    end;
    btnDownload.Enabled:=False;
    end;
    
    procedure TFormMain.Button2Click(Sender: TObject);
    begin
    
    end;
    
    procedure TFormMain.FormCreate(Sender: TObject);
    begin
    dm := Tdm.Create(Self);
    _QueryExecutor := TQueryList.Create;
    end;
    
    end.
    
    
  9. Setelah itu kita coba aplikasinya, berikut ini hasil aplikasi ketika dijalankan :
    screenshot
  10. Pada gambar diatas terlihat aplikasi berjalan dengan lancar.

 

Penutup

Demikian tutorial ini, semoga dapat berguna bagi teman-teman sekalian, bagi yang ingin mendapatkan sourcecodenya bisa didownload disini : https://www.dropbox.com/s/m10yvzxdjagtif3/kpu_da1.rar

2 responses to “[Delphi / Lazarus] Membuat Aplikasi Realcount Pilpres 2014 “Sedot KPU”

  1. Rochmad Sigit Juli 21, 2014 pukul 2:23 am

    Heheh Om Tigor,
    semoga ‘selamat’ dari bullying sampai pengumuman pilpres nanti heuheuheuheu :p

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: