Hello Dmitry and all MVD fans,


On a Form, I have (on the left), a Tablegrid.
With OnCellClick, I get the ID of the selected item, and display results of various query in Edit Fields, Memo, Comboboxes and DBImages all over the Form.


From here, no problem, quite easy.


http://i.imgur.com/ZLJFtvl.jpg


But look at my code :


id := IntToStr(list_asset.TableGrid1.dbItemID);
    list_asset.EdName.Text := SQLExecute('SELECT asset_name FROM asset WHERE asset.id="'+id+'"');
    list_asset.EdSKU.Text := SQLExecute('SELECT asset_sku FROM asset WHERE asset.id="'+id+'"');
    list_asset.EdPrice.Text := SQLExecute('SELECT asset_price FROM asset WHERE asset.id="'+id+'"');
    list_asset.EdURL.Text := SQLExecute('SELECT asset_url FROM asset WHERE asset.id="'+id+'"');
    list_asset.DBImage1.LoadFromDatabase('asset','asset_img1',StrToInt(id));
    list_asset.DBImage2.LoadFromDatabase('asset','asset_img2',StrToInt(id));
    list_asset.DBImage3.LoadFromDatabase('asset','asset_img3',StrToInt(id));
    list_asset.Edvendor.Text := SQLExecute('SELECT vendor.vendor_name FROM vendor INNER JOIN asset ON vendor.id = asset.id_vendor WHERE asset.id ="'+id+'"');
    list_asset.MmDetail.Text := SQLExecute('SELECT asset_descr FROM asset WHERE asset.id="'+id+'"');
    list_asset.ComboBox1.dbItemID := SQLExecute('SELECT main_cat.id FROM main_cat INNER JOIN asset ON main_cat.id = asset.id_main_cat WHERE asset.id ="'+id+'"');
    list_asset.ComboBox2.dbItemID := SQLExecute('SELECT sub_cat.id FROM sub_cat INNER JOIN asset ON sub_cat.id = asset.id_sub_cat WHERE asset.id ="'+id+'"');
    list_asset.ComboBox3.dbItemID := SQLExecute('SELECT minor_cat.id FROM minor_cat INNER JOIN asset ON minor_cat.id = asset.id_minor_cat WHERE asset.id ="'+id+'"');

Do you see how many SQLExecute there are here ?


Know, I don't know if it's with TStringList or with Array of string (or something else), but there must be a way to get all data from


SQLExecute('SELECT * FROM asset WHERE asset.id="'+id+'"');

and then to get each result with something like :


list_asset.EdName.Text := Array[0];
list_asset.EdURL.Text := Array[1];

or


list_asset.EdName.Text := TStringList[0];
list_asset.EdURL.Text := TStringList[1];

What would you use, TStringList or Array on a simple query like :


SELECT asset_name, asset_sku, asset_price, asset_url FROM asset

and how ?


Thanks in advance


Cheers


Mathias

Seems to me MVD is already a very nice RAD smile

378

(2 replies, posted in General)

Hello Dmitry,


Thank you for your precisions, I'm glad not to have to worry about freeing memory and destroying variables after using them.


Can't wait for the text parser to make it's way into MVD smile


Have a good day


Cheers


Mathias

379

(5 replies, posted in Script)

Hello Dmitry,

Works like a charm, as usual.

I was pretty sure there was something with the name, but I couldn't get my hands on it.

Thanks a million

Cheers

Mathias

380

(5 replies, posted in Script)

Stupid me, I forgot to join it smile

Latest beta

Cheers

Math

381

(5 replies, posted in Script)

Hello Dmitry and all MVD fans


To illustrate my previous post, here is a little project (last beta requiered for HTTPGetFile)


  • Click the download button to retrieve the file (it is stored in MVD folder under the name test.jpg)

  • Allow a few second for the image to be downloaded and displayed on the right DBImage

  • Load the test.jpg image in the left DBImage

  • Click the left SAVE button

  • Click the right SAVE button


Now if you look into the database (SQLLite studio or equivalent), you'll see the left image is saved, the right one is not.


The only difference is that the left one is loader manually, when the right one is loaded through script.


Can anyone guess why it works in one case and not in the other ?


Cheers


Mathias

382

(10 replies, posted in Script)

If it's OK to close the second Form to trigger the text transfer, you can do :

procedure Form2_OnClose (Sender: string; Action: string);
begin
    Form1.Edit1.Text := Form2.Edit1.Text;
end;

If there is text present in Form1.Edit1 it will be replaced.



If you need to keep the second form open but still update the first one, you could use :

procedure Form2_OnMouseMove (Sender: string; Shift, Alt, Ctrl: boolean; X, Y: Integer);
begin
    Form1.Edit1.Text := Form2.Edit1.Text;
end;

Not really "by the book" but it works.

Just after typing the text, as soon as the mouse is moved, the text is transfered

383

(10 replies, posted in Script)

My mistake...

Why did I wanted it to be a ComboBox ? Don't know smile

I'll rework my answer

384

(5 replies, posted in Script)

Hello Dmitry and all MVD fans,


Simple question I'm sure for you :


When I put a DBImage1 on Form1, and run the program, when I load the image, it is saved in Database without problems (Type StoreFile, not LinkFile).


But when I do something like :

HTTPGetFile(MAINIMG, SKU+'-001-main-image.jpg');
asset_add_web_result.DBImage1.Picture.LoadFromFile(SKU+'-001-main-image.jpg');

on running the program the image is correctly displayed in DBImage1, but when I click save, it's not saved into database.


The DBImage1 is linked normally with a table and a field, the SAVE button take into account the DMImage1 field and the correct table like normal, the only difference is that I don't load manually the image into the DBImage1, but I load it with a script.


I think it as to do with the _filename that is added automaticly by MVD to the database when you decide to store the file in database, but that's just a guess.


Any idea ?


Wish you all a good day


Cheers


Mathias

385

(2 replies, posted in General)

Hello Dmitry and all other MVD fans,


I have to admit I am a bit confused. I've read so many things, tested so many functions and worked so much with MVD (which is a greeeeat tool), that I believe I'm begining to find my way decently around Delphi.


But... (of course, there is a but)


The application I'm building is huge. I've reached 1200 lines of code, and some procedures handle more than a 100 variables. And everything is working pretty much smoothly.


The question I'd like to ask today is this :


I'm handling a laaaaarge quantity of text, contained into a string, that I got like this :


source := HTTPGet(some URL);

This "source", is then assigned to various variables (name_source, url_source, sku_source...), because this way, I only call the HTTPGet function once and therefore it's faster (and I don't risk getting banned from the site I visit for server hamering).


I have to use all those variables containing the SAME text because, in order to find the info I need (based on HTML tags), I perform a lot of delete and copy operation to finally reach the text I want between specific tags.


To be precise, I use the same source text 9 times, because I'm looking for 9 different info on the same page, but the page is beeing destroyed each time I copy / delete part of it.


If you add to this all the if, else if , else I've added to the code in case there are (for example) not 1, not 2 but URL I want to grab... or even 0 in which case I have to handle the empty returned string before database saving...


Oooh, this works... but is it the right way to do it ?


I mean, keeping so many variables in memory... declaring in advance so many variables (you have to declare them up to the maximum elements you might encounter right ?)...


Questions :


About memory : after declaring 100 variables and assigning values to them, do I have to free memory, or destroy the variables, or do they die at the end of the procedure, or on closing of the form (after database injection) ?


About text handling : Is there a HTMLParser integrated to MVD, or do you plan to integrate one ? Wouldn't that be easier to walk the DOM and get directly to TAG ?


About SQLExecute : God, I do that so many times in my code... Do I have to close the SQL connexuion somewhere or the Form closing does it ?


About the large amount of text I'm handling through variables : Is it the right method ? Sould I assign that to a TStringList, or a TMemoryStream. I read about Dictionaries and the advantage of refereing to all my variables with only one name and indexes. Can I do the same thing with TStringList or TMemoryStream ?


I know that's a lot of questions, and I know you have other posts to answer, I apologize for that smile


I wish you all a good day


Cheers


Mathias

386

(4 replies, posted in General)

Hey Dmitry,

I missed you answer, sorry.

I got out of this by putting the Application.ProcessMessages;  AFTER each instruction I wanted executed while the general script I'm running is computing.

That does the trick for now.

But you'll see my code, don't worry. I think I'm reaching my limits on that project smile

Cheers

Math

387

(4 replies, posted in General)

Hello Dmitry,


Thank you for your fast answer !

Do you mean inserting

Application.ProcessMessages; 

just after the "Begin" clause of the long procedure ?

Or somewhere else in the code ?

388

(4 replies, posted in General)

Hello Dmitry and all MVD fans,


With "small" applications and "fast" computation times, there is no difference for the end user because he gets the result pretty fast.


But I'm experiencing someting "odd" :


I have a procedure that is almost 600 lines long and which involves more than a hundred variables...

The process involves getting the source code of a web page, shuffling through it for scraping info, saving images to disk and so on.

On some pages, the computation time can be as long as 10 seconds.


During this time the GUI (the Form the user has in front of him) is completely frozen. Windows sometimes even reports it as "non responding" and propose to close it.


Of course, if you wait, you finally get you results and everything works fine (I'm handling exceptions all over the code).


Is there a way around this ? I read about threads and running processes in their own thread with TThread.Execute but I'm not sure what to do with this and even if MVD supports it.


Dmitry ? Did you already had long computation times on applications, and do you know how to make it though the application is not frozen during all the process ?


Have a good day all


Cheers


Mathias

389

(3 replies, posted in General)

Hello ehwagner,

If you search my posts, you'll find some talking about Dates manipulations.

I did a few projets on the subjet of finding differences between two dates, and this involved incrementing dates.

Might not be exactly what you're looking for, but maybe a good start ?

Cheers

Mathias

390

(10 replies, posted in Script)

I think the key is the OnChange event on the source Combobox

On the same Form you could do :

procedure Form1_ComboBox2_OnChange (Sender: string);
begin
    Form1.ComboBox1.dbItemID := Form1.ComboBox2.dbItemID;
end;

On two different Forms :

procedure Form2_ComboBox1_OnChange (Sender: string);
begin
    Form1.ComboBox1.dbItemID := Form2.ComboBox1.dbItemID;
end;

391

(2 replies, posted in SQL queries)

Dmitry,

As always, my saviour !!

Works like a charm. Thanks a lot.

Cheers

Mathias

Hello Dmitry and all MVD fans,


I'll try to be as clear as possible.


In one projet, I have a TableGrid in wich rows are displayed, filtered by 4 comboboxes.

All info beeing in the same table, the filtering works very well, even if some Comboboxes are empty.


http://i.imgur.com/etszq5m.jpg


As you can see here, one Combobox is empty and it still works when you click on the FILTER button.

The more Comboboxes you use, the more filtering you get, BUT you don't have to use ALL Comboboxes.


The FILTER button is configured like this

http://i.imgur.com/dftbP0d.jpg


It works very well !


In another project, because infos are in multiple tables, I can not use the search function for the button, because you can only assign, one table.


So I wrote a query for the button.

Here it is :

SELECT
asset.asset_sku,
asset.asset_name,
artist.artist_name,
keyword.keyword_text,
main_cat.main_cat_name,
vendor.vendor_name
FROM
asset
INNER JOIN asset_artist ON asset_artist.id_asset = asset.id
INNER JOIN artist ON asset_artist.id_artist = artist.id
INNER JOIN asset_kw ON asset_kw.id_asset = asset.id
INNER JOIN keyword ON asset_kw.id_keyword = keyword.id
INNER JOIN main_cat ON asset.id_main_cat = main_cat.id
INNER JOIN vendor ON asset.id_vendor = vendor.id
WHERE
(artist.id = {ComboBox1}) AND
(keyword.id = {ComboBox2}) AND
(vendor.id = {ComboBox3}) AND
(main_cat.id = {ComboBox4});

This code works well, no problem except one : it only works if ALL 4 Comboboxes are selected. If one is empty, there is no result returned.


I suspect it is because of the AND clause in the query which seems logical.


My question to Dmitry : do you do something special in the search query built-in behind the button to be able to handle empty comboboxes in MVD ? In other words, what is your trick to handle the empty id returned by non used Comboboxes ? Do you have some kind of wildcard or some conditional CLAUSE like if id=NULL then select * (just guessing) ?


Have a nice day and thanks in advance


Cheers


Mathias

393

(7 replies, posted in General)

Hello Dmitry,

Had the opportunity to test what you added to the beta, and everything works fine.

You're a magician !!

Thanks again and cheers

Mathias

394

(2 replies, posted in Script)

Hello Dmitry,

As always, fast and efficient.

I had the opportunity to try the new function, and it works perfectly for images (and files) coming from sites in http AND https.

Thank you Dmitry, sincerely.

As soon as my clients pay their bills, I'll purchase my own licence !

Cheers

Mathias

395

(7 replies, posted in General)

Oups...

Hello Dmitry,

You have done a miracle by adding the HTTPget function up and running.

It works fine with adresses in http

But with adresses in https, the answer is :

http://i.imgur.com/SkurmRp.jpg

As I assume you are using iDHTTP, do you think you could add IdSSLOpenSSL as well ?

That'd be awesome !!

Have a good weekend

Cheers

Mathias

396

(1 replies, posted in FAQ)

Hello MVD fans and Dmitry,


Some more work for you.


Let say you are looking for a string or a character in a larger string, and you want to count how many times that string (or character) appears in a full page of text.


You can do it by using the Copy(Source, Position, Length) function and comparing the result with the string you are looking for.
WARNING : The Copy function is case sensitive. If you want to get ride of that, you can apply a lowercase() function to your text before searching (it's in the comment).


The code is fully commented step by step
.

Have a good day and... have some fun !! MVD is an awesome tool.


Cheers


Mathias


PS : this is my approach of the problem, there might be some more efficient ones, like RegEx for example.
PS2 : version is beta 2.2 but should work with other version

397

(2 replies, posted in Script)

Hello Dmitry and all MVD fans,

In the context of my web page scraping tool, I successfully retrieve infos from a given web page, this works fine.

I also tried to parse and get images adresses on that same page to display on my Form.

  • I've tried LoadFromFile() but it's only local files.

  • I've tried LoadFromFile(HTTPGet(Image URL)), but still working locall so no success.

  • I've tried to load the image with HTTPGet on the image adress, then save it locally with WriteLnToFile then load it with LoadFromFile, but the result I get is garbage.

  • I've considered using LoadFromStream() but I was unable to initialise the additional function like IdHTTP

I'm out of ideas smile

Two questions :
1- Do you see a way around this with the function we have included in MVD at the moment ?
2- Is it possible to add functions by declaring something like uses .... IdHTTP (with the .pas or .dll file included in app folder of course) ?

have a good day

Cheers

Mathias

398

(2 replies, posted in FAQ)

You're welcome Dmitry !

With all what you do, that was the minimum I could give back.

Hope it's not full of bugs smile

Cheers

Mathias

399

(2 replies, posted in FAQ)

Hello all,


Dmitry just added the HTTPGet function to it's latest beta version (2.2) and it's huuuuge !


I thought I'd give a little explanation for those of you who want to use it.


WHAT IS A WEB PAGE SCRAPER ?
It's a way to get info from the source code of a page, to save into a database (for example).


WHAT DO I NEED IT FOR ?
I use it for storing info about products I own, after getting the info directly from the web page of the seller, that way I have my own catalogue, without entering the info manually which is time consuming if there are a lot of products involved. In my case it's 3D assets, and the count is over 3.000.
I even added a "wishlist" with product infos and a "wanted" tag attached to the assets I'm looking for. Anyway...


LIMITATIONS
No need to build an application if you only need info from 1 page. The idea behind a web page scraper is to gain time by automating a process that would take forever to do manually.
The web page scraper will only work as long as the page format is not modified, because you need to find fixed positions in the source code of the page in order to retrieve the correct info.


Enough will all this. I prepared a little project so that you can see how it works.
I'm scraping the My Visual Database Homepage to get the current version number and the facebook page URL.

Remember :
- this is a nonsense to build an application just for that, but that's just an example of how it works.
- this works only with the page http://myvisualdatabase.com/, and as long as the presentation does not change.


The code is full commented, and function used are Delete and Pos. You can approach the problem with other string routines like Copy, that's up to you.


I wish you all a good day


Cheers


Mathias


Version required to test the project is beta 2.2

400

(1 replies, posted in Script)

Found the error !!

I was trying the create a source2 from delete on source.

But in fact source is modified by the delete function, no need to declare source2.

procedure Form2_Button1_OnClick (Sender: string; var Cancel: boolean);
var
    URL : String;
    Source : String;
    Longueur : String;
    StartPos : Integer;
    Source2 : String;
begin
    URL := Form2.Edit1.Text;
    Source := HTTPGet(URL);
    Longueur := IntToStr(Length(Source));
    Form2.Edit2.Text := Longueur;
    StartPos := Pos('<span class="sku_number">',Source);
    Form2.Edit3.Text := IntToStr(StartPos);
    Delete(Source,1,StartPos);
    Form2.Edit4.Text := Source;

end;