Oldalak

2014. május 16., péntek

Játék a szálakkal I. rész

Ennek a cikknek az apropója az volt, hogyan tudnék "fájdalom mentesen" szálat létrehozni, feladatot végeztetni vele majd felszabadítani és egyszerű legyen használni. (a fájdalom mentes itt azt jelenti, hogy ne leakel-jen a program) Mindezt Delphi környezetben.
Az alábbi szál tipikusan olyan használatra alkalmas, amikor valamilyen műveletet kell végrehajtani a háttérben és ha az befejeződött, akkor felszabadítja a szálat.

A szál kódja az alábbi:

unit UTestThread;

interface

uses
  Classes;

type
  TProgressChanged = procedure (Sender : TObject; AProgress : Integer) of object;

  TestThread = class(TThread)
  private
    { Private declarations }
    FProgress           : Integer;
    FOnProgressChanged  : TProgressChanged;
  protected
    procedure SyncDoOnProgressChanged;
    procedure DoOnProgressChanged; virtual;
    procedure Execute; override;
  public
    constructor Create(AOnProgressChangeCallBack : TProgressChanged; AOnTerminateCallBack : TNotifyEvent);
    destructor Destroy; override;

    property OnProgressChanged : TProgressChanged read FOnProgressChanged write FOnProgressChanged;
  end;

implementation

uses
  SysUtils;

{ TestThread }

constructor TestThread.Create(AOnProgressChangeCallBack: TProgressChanged;
  AOnTerminateCallBack: TNotifyEvent);
begin
  inherited Create(True);
  FreeOnTerminate := False;
  Priority := tpNormal;
  FOnProgressChanged := AOnProgressChangeCallBack;
  Self.OnTerminate := AOnTerminateCallBack;
  Resume;
end;

destructor TestThread.Destroy;
begin

  inherited Destroy;
end;

procedure TestThread.DoOnProgressChanged;
begin
  if Assigned(OnProgressChanged) then
    FOnProgressChanged(Self, FProgress);
end;

procedure TestThread.Execute;
begin
  if Terminated then
    Exit;

  FProgress := 0;
  while (FProgress < 1000) and
        (not Terminated)
  do
    begin
      Inc(FProgress);
      SyncDoOnProgressChanged;
      Sleep(100);
    end;
end;

procedure TestThread.SyncDoOnProgressChanged;
begin
  Synchronize(DoOnProgressChanged);
end;

end.

A szál létrehozásakor a szál konstruktorában két callback függvényt kell megadni. Az egyik ami a folyamat állapotát aktualizálja, a másik pedig a szál befejeződésekor hívódik meg.

A szálat vezérlő modul kódja pedig így néz ki:

...
const
  WM_FREE_THREAD  = WM_USER + 1;

type
  TfrmMainTest = class(TForm)
    btnStart: TButton;
    ed1: TEdit;
    btnStop: TButton;
    procedure btnStartClick(Sender: TObject);
    procedure btnLeakClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    FThread : TestThread;

    procedure OnThreadFinished(Sender : TObject);
    procedure OnProgressChanged(Sender : TObject; AProgress : Integer);
    procedure StopWorkerThread;
  public
    { Public declarations }
    procedure WndProc(var Message: TMessage); override;

    destructor Destroy; override;
  end;

var
  frmMainTest: TfrmMainTest;

implementation

uses Math;

{$R *.dfm}

procedure TfrmMainTest.btnStartClick(Sender: TObject);
begin
  btnStart.Enabled := False;
  if not Assigned(FThread) then
    begin
      FThread := TestThread.Create(OnProgressChanged, OnThreadFinished);
    end;
end;

procedure TfrmMainTest.OnProgressChanged(Sender: TObject;
  AProgress: Integer);
begin
  ed1.Text := IntToStr(AProgress);
end;

procedure TfrmMainTest.OnThreadFinished(Sender: TObject);
begin
  PostMessage(Handle, WM_FREE_THREAD, 0, 0);
end;

procedure TfrmMainTest.StopWorkerThread;
begin
  if (FThread <> nil) then
    begin
      FThread.Terminate;
      FThread.WaitFor;
      FreeAndNil(FThread);
      btnStart.Enabled := True;
    end;
end;

procedure TfrmMainTest.WndProc(var Message: TMessage);
begin
  if (Message.Msg = WM_FREE_THREAD) then
    StopWorkerThread
  else
    inherited WndProc(Message);
end;

procedure TfrmMainTest.btnStopClick(Sender: TObject);
begin
  if Assigned(FThread) then
    begin
      FThread.Terminate;
    end;
end;

destructor TfrmMainTest.Destroy;
begin

  inherited Destroy;
end;

procedure TfrmMainTest.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  StopWorkerThread;
end;
...

Az elindított szál minden esetben felszabadul és a szál futása is megszakítható.

2013. március 13., szerda

File méretének lekérdezés WinAPI használatával

Az alábbi függvénnyel egy állomány méretét lehet lekérdezni. A lekérdezéshez a GetFileAttributesEx WinAPI függvényt használom fel.
A függvény a 2GiB -nál nagyobb méretű állományok méretét is helyesen adja vissza (nincs túlcsordulás), mert a visszatérési érték Int64-ben van.

function GetFileSize(AFileName : String) : Int64;
var
  rData : WIN32_FILE_ATTRIBUTE_DATA;
  iSize : Int64;
begin
  Result := 0;
  if GetFileAttributesEx(PChar(AFileName), GetFileExInfoStandard, @rData) then
    begin
      iSize := rData.nFileSizeHigh;
      iSize := iSize shl 32;
      iSize := iSize + rData.nFileSizeLow;
      Result := iSize;
    end;
end;

2013. március 4., hétfő

sfPopup használata

A minap abba a problémába ütköztem, hogy Samsung SMART TV alkalmazásban egy popup stílusú dialógus ablakot kellett készíteni. Nos a megoldás kézenfekvő, ezt meg lehet oldani jQuery segítségével és nem kell a ECMAScript stílusban megírni a kódot. A gondot viszont az jelenti, hogy a Samsung SMART TV fejlesztői API elírásai igencsak vérszegény leírások. A google keresője sem sokat segít ebben a feladatban. Ami viszont segítség, hogy a SMART TV emulátorában lehet debuggolni a Javascript kódot és a nyomkövetés közben meg lehet nézni a gyári függvényeket, illetve bele lehet "kukkantani" az sfPopup működtető kódjába. Ennek segítségével hoztam létre az alábbi működő popup ablakot.

index.html tartama:

 
  
  IME_Test

  
  
  
  
   
  
        
        
  
  
  
  
  
  
  

  
  

  

 

 
   
  
  
Ez egy tartalom

Main.js tartalma:
var widgetAPI = new Common.API.Widget();
var tvKey = new Common.API.TVKeyValue();
var pluginAPI = new Common.API.Plugin();
var fileSystemObj = new FileSystem();

var Main = {
 selIndex : 0,
 selListIndex : 0,
 SCROLL_UP : 1,
 SCROLL_DOWN : 2,
 use_scrollbar : false,
 scrollb_height : 0
};

Main.list = new Array();
Main.list_ex = new Array();

Main.onLoad = function() {
 // Enable key event processing
 this.enableKeys();
 widgetAPI.sendReadyEvent();
 this.list.push("A", "B", "C", "D", "É", "J", "K", "L", "M", "Q", "G", "S");
 this.init();
};

Main.onUnload = function() {

};

Main.init = function () {
 
 $("#popup").sfPopup({
  text: "",
  buttons: [], // "OK",
  title: "Gyors kereső",
  timeout: 0,
  defaultFocus: 0,
  keyhelp: {'UPDOWN':'Navigáció', 'ENTER':'Kiválaszt', 'RETURN':'Vissza'},
  //keyhelp: {'UPDOWN':'navigate', 'ENTER':'Select', 'RETURN':'Back'},
  callback: function (index) {
   
   dialogCallBack(Main.selIndex);
   
   Main.enableKeys();
  },
  onkeydown: function(keyCode) {
   
   alert("popup onkeydown event KEYDOWN : " + keyCode);
   
   if (event.defaultPrevented) {
    switch (keyCode) {

    case tvKey.KEY_ENTER:
     this.hide();
     break;
    case tvKey.KEY_UP:
     
     if (Main.selIndex == 0) {
      Main.selIndex = Main.list.length - 1;
     } else {
      Main.selIndex--;
     }
     Main.updateListSelection(Main.SCROLL_UP);
     break;
    case tvKey.KEY_DOWN:
     
     if (Main.selIndex == Main.list.length - 1) {
      Main.selIndex = 0;
     } else {
      Main.selIndex++;
     }
     Main.updateListSelection(Main.SCROLL_DOWN);
     break;
    }
   }
   sf.key.preventDefault();
   
   return true;
  }
  /*,
  actionpopup: true
  */
 });
};

function dialogCallBack(param) {
 alert("selected index: " + param);
 //$("#popup").sfPopup("hide");
 var item = Main.list[param];
 $("#text").html(param + ' érték: ' + item);
}

Main.enableKeys = function() {
 document.getElementById("anchor").focus();
};

Main.getListHtml = function () {
 var res = "";
 
 for (var i = 0; i < 5; i++) {
  if (i < this.list.length) {
   res += '
  • ' + this.list[i] + '
  • '; } else { res += '
  • '; } } /* for (var i = 0; i < this.list.length; i++) { res += '
  • ' + this.list[i] + '
  • '; } */ res = '
      ' + res + '
    '; return res; }; Main.initScroll = function() { if (this.list.length > 5) { $("#scrollbgalls").addClass('scrollbgalls'); $("#scrollmarkalls").addClass('scrollmarkalls'); $("#scrollbgalls").show(); $("#scrollmarkalls").show(); $("#scrollbgalls").height($(".internal_list").height()); this.use_scrollbar = true; this.scrollb_height = $("#scrollbgalls").height() - $("#scrollmarkalls").height(); } else { $("#scrollbgalls").removeClass('scrollbgalls'); $("#scrollmarkalls").removeClass('scrollmarkalls'); $("#scrollbgalls").hide(); $("#scrollmarkalls").hide(); this.use_scrollbar = false; } }; Main.updateListSelection = function(move_direction) { var len = this.list.length; if (len > 5) { len = 5; } if (move_direction == Main.SCROLL_DOWN) { if (this.selListIndex < 4) { this.selListIndex++; } else { if (this.selIndex == 0) { this.selListIndex = this.selIndex; // frissiteni kell a tartalmat for (var i = this.selIndex; i < len; i++) { var item = '#item_' + i; var li = $(item); if (li != undefined) { li.html(this.list[i]); } } } else { // frissiteni kell a tartalmat var j = 0; for (var i = this.selIndex - 4; i <= this.selIndex; i++) { var item = '#item_' + j; var li = $(item); if (li != undefined) { li.html(this.list[i]); } j++; } } } } else if (move_direction == Main.SCROLL_UP) { if (this.selListIndex == 0) { if (this.selIndex == this.list.length - 1) { var j = 0; for ( var i = this.selIndex - 4; i <= this.selIndex; i++) { var item = '#item_' + j; var li = $(item); if (li != undefined) { li.html(this.list[i]); } j++; } this.selListIndex = 4; } else { var j = 0; for ( var i = this.selIndex; i < this.selIndex + 5; i++) { var item = '#item_' + j; var li = $(item); if (li != undefined) { li.html(this.list[i]); } j++; } } } else { this.selListIndex--; } } for (var i = 0; i < len; i++) { var item = '#item_' + i; if (i == this.selListIndex) { $(item).addClass('selected_item'); $(item).blur(); } else { $(item).removeClass('selected_item'); } } if (this.use_scrollbar) { var top = (this.scrollb_height / (this.list.length - 1)) * this.selIndex; $("#scrollmarkalls").css("top", top + "px"); } }; Main.keyDown = function() { var keyCode = event.keyCode; alert("Key pressed: " + keyCode); switch(keyCode) { case tvKey.KEY_RETURN: case tvKey.KEY_PANEL_RETURN: alert("RETURN"); widgetAPI.sendReturnEvent(); break; case tvKey.KEY_LEFT: alert("LEFT"); break; case tvKey.KEY_RIGHT: alert("RIGHT"); break; case tvKey.KEY_UP: alert("UP"); //this.ShowPopup(); break; case tvKey.KEY_DOWN: alert("DOWN"); break; case tvKey.KEY_ENTER: case tvKey.KEY_PANEL_ENTER: alert("ENTER"); break; case tvKey.KEY_RED: this.selIndex = 0; this.selListIndex = 0; $("#popup").sfPopup("option", "text", this.getListHtml()); $("#popup").sfPopup("show"); this.initScroll(); this.updateListSelection(); break; case tvKey.KEY_GREEN: break; default: alert("Unhandled key"); break; } };

    Ezt a fenti kódot futtatva az alábbi kinézetű ablak fog megjelenni:

    2013. február 21., csütörtök

    Eredmény halmaz rendezése magyar ábécé szerint MySQL-ben

    A minap futottam abba a problémába, hogy egy SQL lekérdezésben az eredmény halmazt a magyar ábécé szerint kellett rendezni. Ez önmagában nem lenne probléma, ha a tábla illetve az a mező, amiből szelektálni akarunk a karakter készlete utf8_hungarian_ci (COLLATION) lenne. Jelen esetben a táblában lévő adatok migrációja UTF-8 kódolásra nem megoldható ezért a SELECT-ben kell megoldani ezt a konverziót.


    SELECT id, 
           CONVERT(CAST(nev_latin1_ben as BINARY) USING utf8) as name
    FROM tabla
    ORDER BY CONVERT(CAST(nev_latin1_ben as BINARY) USING utf8) COLLATE utf8_hungarian_ci;
    

    A fenti lekérdezés átkonvertálja a mezőben nev_latin1_ben mező tartalmát UTF-8 karakter kódolásra és az eredményhalmazt magyar ábécé szerint rendezi.

    2013. február 18., hétfő

    Tömb rendezése magyar ábécé szerint

    Ez egy példa, hogy hogyan lehet megvalósítani egyedi rendezést JavaScrip használatával.

    A függvény, ami az egyéni rendezést megvaólsítja:
         function mSort(a, b) {
            
            if (a == b){
                return 0;
            }
            var huAbcLower = "aábcdeéfghiíjklmnoóöőpqrstuúüűvwxyz";
            var huAbcUpper = "AÁBCDEÉFGHIÍJKLMNOÓÖŐPQRSTUÚÜŰVWXYZ";
            
            len_abc = huAbcLower.length;
            len_a = a.length;
            len_b = b.length;
            var loc_a;
            var loc_b;
            
            for (var n = 0; n < len_a && n < len_b; ++n) {
                for (var k = 0; k < len_abc; ++k) {
                    if (a.charAt(n) == huAbcLower.charAt(k) || a.charAt(n) == huAbcUpper.charAt(k)){
                        loc_a = k;
                    }
                    if (b.charAt(n) == huAbcLower.charAt(k) || b.charAt(n) == huAbcUpper.charAt(k)){
                        loc_b = k;
                    }
                }
                if (loc_a > loc_b){
                    return 1;
                }
                if (loc_a < loc_b){
                    return -1;
                }
            }
            if (len_a > len_b){
                return 1;
            }
            if (len_a < len_b){
                return -1;
            }
            return 0;
        }
    

    A használata pedig a következő képen néz ki:
       var arr = ["Körte", "barack", "alma"];
       
       arr.sort(mSort);
    

    A sort függvény callback paraméterének az általunk létrehozott függvényt kell megadni paraméternek.

    2013. február 4., hétfő

    Refactoring használata

    1. lépés: a kódblokk kijelölése, amit újra hasznosítani szerentnénk, majd a kijelölésen jobb kattintás. A megjelenő menüből a Refactor->Extract Method... menüpontot kell választani.
    2. Lépés: a megjelenő párbeszéd ablakban meg kell adni a metódus nevét.

    Az alábbi kódból:

    for (int i = 0; i iLen; i++)
    {
      int iKey;
      char cLetter;
      do
      {
        iKey = r.Next(65, 65 + (2 * iLen));
        cLetter = (char)iKey;
      } while (sSecret.Contains(cLetter.ToString()));
      sSecret += cLetter;
    }
    
    
    ez a metódus lett:

    private static string GenerateWord(int iLen)
    {
     string sSecret = "";
      for (int i = 0; i < iLen; i++)
      {
      int iKey;
      char cLetter;
    
      do
      {
      iKey = r.Next(65, 65 + (2 * iLen));
      cLetter = (char)iKey;
      } while (sSecret.Contains(cLetter.ToString()));
    
      sSecret += cLetter;
      }
      return sSecret;
    }
    
    A metódus hívási módja pedig ez:
    sSecret = GenerateWord(iLen);
    

    C# dll készítése

    1. Lépés: létre kell hozni egy új ClassLibrary-t. Ehhez a Solution Explorer-ben egy jobb egér gombot kell nyomni, majd a megjelenő menüben az Add->New Project menüpontot kell kiválasztani. Az alábbi ábra ezt szemlélteti.
    A menüpont kiválaszátása után a következő párbeszéd ablak jelenik meg:
    A megjelenő ablakban a Templates listából a Class Library elemet kell kiválasztanunk. A Name mezőbe azt a nevet kell írni. Ez a név lesz az a névtér, amin keresztül el tudjuk majd érni a DLL függvényeit.


    2. Lépés: a Dll metódusainak megírása.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleAppLibrary
    { 
            public class Game
            {
                    static Random r = new Random();
                    public string GenerateWord(int iLen)
                    {
                            string sSecret = "";
                            for (int i = 0; i < iLen; i++)
                            {
                                    int iKey;
                                    char cLetter;
    
     do {
    
       iKey = r.Next(65, 65 + (2 * iLen));
    
       cLetter = (char)iKey;
    
     } while (sSecret.Contains(cLetter.ToString()));
    
     sSecret += cLetter;
    
     }
    
     return sSecret;
    
     }
    
     public void Evaluate(string ASecret, string AAnswer, out int ABlack, out int AWhite)
    
     {
    
     // kimeneti valtozok inicializalasa
    
     ABlack = 0;
    
     AWhite = 0;
    
     for (int iCnt = 0; iCnt < AAnswer.Length; iCnt++)
    
     {
    
     // hanyadik pozicioban szerepel a valasz iCnt-dik
    
     // eleme a titokban
    
     int iPos = ASecret.IndexOf(AAnswer[iCnt]);
    
     
    
     if (iCnt == iPos)
    
     {
    
     ABlack++;
    
     }
    
     else if (iCnt > -1)
    
     {
    
     AWhite++;
    
     }
    
     }
    
     }
    
     }
    
    }
    
    
    

    3. Lépés: a DLL referenciájának hozzáadása az alkalmazáshoz. Ehhez a Solution Eplorer-ben az alkalmazásunkat kijelölve egy jobb egér gombot kell nyomni. A megjelenő menüből az Add Reference... menüpontot kell kiválasztani.
    Ezután a megjelenő párbeszéd ablakban a Project fülre kattintva ki kell választani a DLL-t, majd OK gombot kell nyomni.
    Ezután a Solution Eplorer-ben a References ág kiegészül az újonan hozzáadott DLL referenciájával.

    4. Lépés: a Dll metódusainak használata.

    ...
    using ConsoleAppLibrary;
    namespace ConsoleApplication2
    {
     class Program
     {
     static Game game = new Game();
      static void Main(string[] args)
     {
      ...
     sSecret = game.GenerateWord(iLen);
    ...