Hello radsoft,


A quick and "dirty" modification of your project just to give you something. I'll try to improve that tonight.


Query is written on multiple lines to show you how to spread it over multiple lines (beware of spaces between some instructions : if you forget them, the query will fail).


Also, I changed the Date format of your date field to text : the date format was not showing up in the dbgrid even with data in database. I will investigate tonight too.


Last thing : you talk about a comma separated list you receive through COM port right ?
Do you know how to handle those strings and turn them into "smaller bits" to save them in database ? Have you ever used StringLists ?


Anyway, back to work now smile


Cheers


Math

Hello Dmitry,


For upgrading purposes of databases generated by MVD, I need to set

     SQLExecute('PRAGMA foreign_keys=OFF');

and then return the foreign key constraints back to normal with

     SQLExecute('PRAGMA foreign_keys=ON');

This works on the main MVD database (the one attached by default), but if I attach a second database name (for example) OLD_DB, how do I run that query specifying it should impact the attached database and not the default one ?


Thanks in advance


Cheers


Mathias

Hello radoft


Assuming your Time() is a TDateTime how do you get this variable ?


Did you try something like :

procedure Form1_Button1_OnClick (Sender: string; var Cancel: boolean);
var
    timeNow : Extended;
begin
    timeNow := Time; //Time is system time and should respect your systems settings regarding the format
    SQLExecute('INSERT INTO TestData(DataDate) VALUES("'+TimeToStr(timeNow)+'")');
end;


If you get the Time variable directly you can even simplify like this:

procedure Form1_Button1_OnClick (Sender: string; var Cancel: boolean);
begin
    SQLExecute('INSERT INTO TestData(DataDate) VALUES("'+TimeToStr(Time)+'")');
end;

Let me known if this helps.


Cheers


Mathias

54

(24 replies, posted in General)

Hello AD,


Took me some time, but I think I have something for you.


Try this little tool and tell me what you think. Instructions are included inside, just click on the 'question mark' icon, but here they are as well :

  • use SQliteCompare to generate a conversion script

  • start the tool and select your database to convert

  • load the conversion script

  • wait

https://mega.nz/#!tkoklabQ!Sbm6R5jMuVhh … d7GLpadsJU


If this tool works, I could build a version you could distribute with the needed script and instructions to your users, as well as upload it here for all users if you think it is useful.


I had to use Delphi with RAD Studio because I could not find a way to disable Foreign Key Constraints on an attached database with MVD (they are enabled again at the end of the conversion process).
The idea was to build a generic tool, not one with hard-coded instructions that would only work for a specific version and a specific database scheme. This is why the conversion script has to be loaded. But then, if the only thing you have to provide to your users is the conversion script along with the new version of your application, that is simple enough for them to do.


Cheers


Mathias

PS : many things could go wrong, starting with the SQLite connection. Let me known

55

(24 replies, posted in General)

Better safe than sorry,


QUERY 7
Add a blank space at the end of the first line, your commands are not separated.
Replace

issueFilterValue)'

With

issueFilterValue) '

Other than that I don't see anything else


Cheers



Math

56

(24 replies, posted in General)

Hello AD,


QUERY 5, there are two typos :
Replace

SQLExecute('CREATE TABLE issueFilter;'+

With

SQLExecute('CREATE TABLE issueFilter'+

There is a nasty "  ;  " in your code where it should not be
Replace

'issueFilter TEXT,'

With

'issueFilter TEXT'

No "  ,  " at the end because this is the last line of instruction


QUERY6 found just one
Replace at the end

')');

With

'))');

I think you closed one less "  (  " than you had opened


Other errors are due to the missing tables that could not be created.


Let me known if this solved your issue


Cheers



Mathias

57

(24 replies, posted in General)

Sent you a forum email with the download link smile

58

(24 replies, posted in General)

Basically, this is a second program.


The user :
- starts the program
- loads his "old database"
- no mater where the database was, it is copied in the  DB_OLD subdirectory of the tool
- the version of the database is checked
- if version is OK, conversion process can begin
- at the end, the newly created database is saved in DB_NEW subdirectory

All that is left for the user is the replace the database shipped with the new version of the application with this newly converted one, and start the program.


Code is commented


Hope this helps


Cheers


Mathias

59

(24 replies, posted in General)

Hello AD,


Right now, I'm uploading the tool I made for you to look at it.

60

(24 replies, posted in General)

Hello  AD,


I had a quick look at your code, and I think there is a extra comma at the end of your script


Find this :

'id_company INTEGER REFERENCES company(id) ON DELETE CASCADE,'+
')');

Try to replace with :

'id_company INTEGER REFERENCES company(id) ON DELETE CASCADE')');

Let me known if this solved the error.


Take care my friend. Cheers


Mathias

61

(24 replies, posted in General)

Hello both of you old friends smile


AD, what you want to do is use an SQLTransaction. With this, you can chain all your SQLExecute commands and execute them all at once.


No need for

BEGIN TRANSACTION;

at the beginning and no need for

COMMIT TRANSACTION;

at the end, this is all contained in the MVD SQLTransaction.


On  a button click, this would give you something like that (with the example you posted previoulsy)

procedure Form1_Button1_OnClick (Sender: string; var Cancel: boolean);
var
    //Declare a SQLTransaction variable
    SqliteTr: TDBXSqliteTransaction;
begin
    //Initiate the transaction
    SqliteTr := BeginSQLiteTransaction;

    //SQL command 1
    SQLExecute('CREATE TABLE temp_Application_534011718'+
    '('+
    'id INTEGER PRIMARY KEY ASC AUTOINCREMENT,'+
    'appName TEXT NOT NULL,'+
    'code TEXT,'+
    'issues TEXT,'+
    'todo TEXT,'+
    'notes TEXT,'+
    'id_Status INTEGER,'+
    'id_Groups INTEGER,'+
    'id_Categories INTEGER,'+
    'FOREIGN KEY (id_Status) REFERENCES Status(id),'+
    'FOREIGN KEY (id_Groups) REFERENCES Groups(id),'+
    'FOREIGN KEY (id_Categories) REFERENCES Categories(id)'+
    ');');

    //SQL command 2
    SQLExecute('INSERT INTO temp_Application_534011718 (id,appName,code,issues,todo,notes,id_Status,id_Groups,id_Categories) SELECT id,appName,code,issues,todo,notes,id_Status,id_Groups,id_Categories FROM Application;');

    //SQL command 3
    SQLExecute('DROP TABLE Application;');

    //SQL command 4
    SQLExecute('ALTER TABLE temp_Application_534011718 RENAME TO Application;');

    //SQL command 5
    SQLExecute('DROP TABLE Issues;');

    //Commit the SQLTransaction
    CommitSQLiteTransaction(SqliteTr);
end;

end;

Hope this helped


Cheers


Mathias

62

(24 replies, posted in General)

Hello AD1408 and happy new year,


I known the problem ! My application has had so many changes, that it can be hard to tracks user version and adapt their database to the new format (because they don't want to start from scratch and loose their data).


To compare two sqlite databases, I use this tool : Sqlite Compare
https://www.codeproject.com/Articles/22 … re-Utility


This nice (and Free) utility will compare for you two sqlite databases (structure only, or structure and data) and generates SQL scripts to upgrade from old model to new model.


Tables, triggers, foreign keys.... it handles everything and quite well.


Each time I release a new version, the conversion scripts comes with it. When the new application starts (with the old data), version numbers are compared and the scripts are applied on a copy of the user database.


The only thing the user has to do is replace the current database with the converted one.


Hope this helps


Cheers


Mathias

63

(3 replies, posted in General)

Thanks Dmitry,


Exploring your suggestion, I ended up with a procedure (on click of the TImage) but there are various cases to consider :
- image is in landscape or portrait mode
- image is larger or smaller to screen resolution


I known this is not MVD but Delphi, but this is what I came up with. This work fine in my application but would you mind giving your advises, I'm sure I overlooked some details (I use TImage because all major image formats are already supported and I don't need to convert)


NOTE : the form that displays the image is created on click and destroyed on Close and images are displayed with a ratio of 0.9 to their original size


//-06-DISPLAYING IMAGE FULL SIZE ON CLICK
procedure TfrmAssetEdit.Image3Click(Sender: TObject);
var
  F_Im_View : TfrmImageView;
  ImageH, ImageW, ScreenH, ScreenW : Integer;
  RatioI : Double;
begin
    try
       F_Im_View := TfrmImageView.Create(nil);
      //Screen Dimensions
      ScreenH := Screen.Height;
      ScreenW := Screen.Width;

      //Image Dimensions
      ImageH := Image3.Picture.Height;
      ImageW := Image3.Picture.Width;
      //Image Ration
      RatioI := ImageW / ImageH;
        if RatioI > 1  then  //Image is in landscape mode
          begin
            if (ImageW <= ScreenW) AND (ImageH <= ScreenH)then
              begin
                F_Im_View.Image1.Picture := Image3.Picture;
                F_Im_View.ClientWidth := Round(ImageW * 0.9);
                F_Im_View.Image1.Width := Round(ImageW * 0.9);
                F_Im_View.ClientHeight := Round(ImageH * 0.9);
                F_Im_View.Image1.Height := Round(ImageH * 0.9);
                F_Im_View.Image1.Proportional := True;
                F_Im_View.ShowModal;
              end
            else
              begin
                F_Im_View.Image1.Picture := Image3.Picture;
                F_Im_View.ClientHeight := Round(ScreenH * 0.9);
                F_Im_View.Image1.Height := Round(ScreenH * 0.9);
                F_Im_View.ClientWidth :=  Round(ImageW * ScreenH * 0.9 / ImageH);
                F_Im_View.Image1.Width := Round(ImageW * ScreenH * 0.9 / ImageH);
                F_Im_View.Image1.Proportional := True;
                F_Im_View.ShowModal;
              end;
          end
        else if RatioI <= 1 then //Image is in Portait mode
          if (ImageW <= ScreenW) AND (ImageH <= ScreenH)then
              begin
                F_Im_View.Image1.Picture := Image3.Picture;
                F_Im_View.ClientWidth := Round(ImageW * 0.9);
                F_Im_View.Image1.Width := Round(ImageW * 0.9);
                F_Im_View.ClientHeight := Round(ImageH * 0.9);
                F_Im_View.Image1.Height := Round(ImageH * 0.9);
                F_Im_View.Image1.Proportional := True;
                F_Im_View.ShowModal;
              end
          else
              begin
                F_Im_View.Image1.Picture := Image3.Picture;
                F_Im_View.ClientHeight := Round(ScreenH * 0.9);
                F_Im_View.Image1.Height := Round(ScreenH * 0.9);
                F_Im_View.ClientWidth := Round((ImageW * ScreenH * 0.9) / ImageH);
                F_Im_View.Image1.Width := Round((ImageW * ScreenH * 0.9) / ImageH);
                F_Im_View.Image1.Proportional := True;
                F_Im_View.ShowModal;
              end;
    finally
      F_Im_View.Free;
  end;
end;

Cheers


Mathias

64

(3 replies, posted in General)

Hello Dmitry, and a happy new year to you.


I have a question, just out of curiosity : do you use a special Component to display images in MVD or is it a standard Delphi one ?


I am specially interested in that functionality where when clicked, an image displayed in an Image of DBImage component opens full size.
How did you do that ? Is it an extra Delphi Component ?


Cheers and happy new year again


Mathias

65

(2 replies, posted in General)

Hello Dmitry, and thank you for the upload.


I confirm that the same source code compiles and runs flawlessly with MVD 3.6 while MVD 4.1 generates the previously stated error message.


I'll try to strip down all irrelevant code and database data to upload my code so that you can test it for yourself.


Until then, have yourself a Merry Christmas.


Thanks for the hard work Dmitry


Cheers


Mathias

66

(8 replies, posted in General)

Hello guys,


@Derek : you're right, I've been on and off for a few month because work was quite heavy lately, but I always keep an eye on the forum and on old friends smile


Three things I wanted to add :


First, 'keywords.TableGrid1.dbItemID := lid;' refers to a database ID, so should not be "broken" by any sorting. The tablegrid I tested it on was sorted and it still worked.
But then, this 'Last_Insert_id' thing only works on a database INSERT. If you try this method on an UPDATE, it won't give you the last modified ID, but the last inserted one, which could have been added hours ago...
If you want to select the row after an UPDATE, you'll have to identify the database ID with another method first.


Second, you can not set the focus on multiple objects on a form. So if you want to highlight lines in different grid, don't use the 'setfocus' command.
I personally setup my grids with a 'selectfullrow' option and this is enough to highlight the row I want.
More on that after Christmas if needed.


Last but not least, I see a lot of attempt at Treeviews, so I thought I might post my implementation of it here :

My application is an asset manager, assets to which you can attach keywords to identify them. They also have a link to the online shop you can find them and a status flag (owned, wanted, download in progress...).


The following treeview represents the assets grouped by Keywords, and the vendors with icons for each levels and a special icon to show their status.

You will see :
first level : keywords (this is test data)
second level : vendors
third level : assets

Each level has it's own icon, and some assets (third level) have a little green tick, showing they are owned. And each level also has a counter showing how many items it contains.


https://i.imgur.com/7YRkQNc.png


I've stripped down all the rest of the code, all other forms and all irrelevant data from the database because the package was over 300Mo wether now, it's a little under 1,5 Mo.


Hope this helps


Have yourself a Merry Christmas as used to sing Franck Sinatra.


Cheers


Mathias

67

(8 replies, posted in General)

Hello my friend,


Has strange as it may seem, you don(t need to use SelectedRow. It i used to find the ID or index of the selected row, not to set it.


Use a simple :

procedure keywords_Button8_OnClick (Sender: string; var Cancel: boolean);
var
    lid : Integer;
begin
    lid := Last_Insert_id;
    keywords.TableGrid1.dbItemID := lid;
end;

And you will get the focus on the last inserted row in the Tablegrid (my procedure is on a button click, but should work at the end of any procedure)


Merry Christmas


Cheers


Mathias

68

(2 replies, posted in General)

Hello Dmitry,


Did you change anything in the EhLib component you were using ? Because since I updated to version 4.1 I now get "List index out of bound errors" and application crashes with the error message :

Exception ElistError... List Index out of bounds(0)


I've tried to replace the sqlite3.dll provided with MVD with a more recent one but nothing changes...


I'd love to post a sample of my project, but the attached database is fat too big to be posted here : compressed project is over 100 Mo.
I'll try to minimize it as much as possible, but it's difficult.


The problem happens when, clicking in Tablegrid1, I expect results to show in Tablegrids2 and is the same whether I :

  • use a SQLsearch button (standard method) with a Search Increment set to that button

  • use a SQL Query button with the Search Increment

  • use a SQL Query button with an oncellclick event


Any idea ?


While this is investigated, would you mind linking the latest stable 3.x version ?

Thanks in advance


Cheers


Mathias

69

(5 replies, posted in Script)

Hello Vasco,


Stoping à procedure while it is running is not very difficult, but your approach will depend on the type of procedure.


If for example this is a simple procedure waiting for a user input for example, and the edit field has not been filled before the button click, you can use something like :

If length(Form2.Edit1.Text) = 0 then
     Begin
          Cancel := True;
          Showmessage(sorry you left the field empty);
          Form2.Close;
     End;

If the procedure is looping through calculations or repeating tasks and you want to stop it on a condition ? You could use Exit or Break like ehwagner suggested but make sure you condition is tested each cycle or it will not stop. And yes, if the procedure exits, you can issue a Close command.


Now, the case I have been faced many times is as follows : one of my application has very long procedures testing thousands of records in a very long loop. The user is informed of the progress of the operation whith a progress bar but sometimes he might want to stop the process to resume it sometimes later.

     if condition = False then
          begin
               Break;
           end
     else if condition = True then...

In that case, my approach is different :
- I create a global Boolean variable (because I use it in many places)
- I set this variable to true at the beginning of the procedure
- as long as the cancel button is not clicked the procedure runs
- if the Cancel button is clicked, the procedure stops and after a message the form is closed


This looks like :

running := True;
for i := 0 to online_count - 1 do
     begin
          if running = False then
               begin
                    Break;
               end
          else if running = True then
               begin
                   .......

Then the cancel button is associated with a simple procedure like :

procedure BtCancel_OnClick (Sender: string; var Cancel: boolean);
begin
    running := False;
end;

But the procedure will not know if the button has been clicke dor not because it uses all the cycles and does not get messages from the "outside", the user interface is kind of frozen.
So, to make sure the user can interact with the interface, I had IN THE LOOP (very important) an instruvction that tells the procedure to "listen to input if any" with :

Application.ProcessMessages;

The skeleton of the code is :

running := True;

for i := 0 to online_count - 1 do
     begin
          if running = False then
               begin
                    Break; //but you cna do what you what you want here
               end
          else if running = True then
               begin
                   .......
               end;
          Application.ProcessMessages; //this is tested each cycle of the for i := 0 to some value and will detect a click on the cancel button
     end;

Hope this helped


Cheers



Mathias

70

(8 replies, posted in Script)

Hey Vasco,


I am sorry I missed your previous post sad


But you finally found your solution right ?


If not, do not hesitate to ask, I will pay more attention to notifications smile


Cheers



Math

71

(4 replies, posted in Reports)

Hello Vasco,


I am not sure I got your question right but If you are using a SQL report, just add a WHERE clause after your JOIN like :


LEFT OUTER JOIN orcamento on orcamento.id_assistencia = assistencia.id
WHERE orcamento.boolean = 0

Now, why do you use LEFT OUTER JOIN ?


When you do a SELECT with INNER JOIN like :

SELECT
x
FROM table_a
INNER JOIN table_b

you will get all records that have x in table_a AND a link in table_b


If instead you use LEFT OUTER JOIN, you will get all records that have x in table_a EVEN IF they have no link in table_b.


For example, if you do :

SÉLECT
customer.name,
sex.gender
FROM
customer
LEFT OUTER JOIN sex on customer.id_sex = sex.id

The query will return ALL customer names EVEN if no sex (male or female) has been declared for them. But if you use INNER JOIN you will only get customer names that have a sex declared.


I know this is not the subject of your question, but I was surprised to read that so I ask  smile


Finally, from memory because I am not in front of my computer, if you don’t want to use the WhERE clause in your query, just add the orcamento.boolean in the SELECT part and filter the boolean field to 0 in the report.


Hope this helped and I understood your question right.


Cheers


Mathias

72

(14 replies, posted in SQL queries)

Hello wenchester21,


I think Dmitry is correct, his solutionis the simplest you can apply.


But you could have to use script for other reasons than printing.
In that case, you can use the property of your Tablegrid to achieve that.

Let's say you have a Tablegrid1 on Form1 and the user selected some products in it. In order to get the ID of these selected products, you could do something like :

procedure Form1_Button1_OnClick (Sender: string; var Cancel: boolean);
var
    i : Integer;
    product_id : Integer;
begin
    for i := 0 to Form1.TableGrid1.RowCount - 1 do
        begin
            if Form1.TableGrid1.Selected[i] then //if the row is selected
                begin
                    product_id := Form1.TableGrid1.dbIndexToID(i); //convert the row number to database ID
                    ShowMessage(IntToStr(product_id));
                end;
        end;
end;

Of course, this would give you a series of popups with the id number in it which is of no real use, but you get the general idea.


If you need help to insert them in a temporary table of in a stringlist or array, just say so and I'll provide an example.


Hope this helps


Cheers


Mathias

73

(25 replies, posted in General)

Hello all,


Little precision for you guys using the GetTickCount instruction.


This command counts the number of milliseconds elapsed since the computer started. As this is coded on a DWORD32 field, there is a limit that corresponds to a little less than 50 days uptime.


This might be suitable for personal use, but we have at the office, server with more than a year uptime so this function would not work, because it tends to 0 after 50 days : once you've reached the limit, stop - start =0 because the two variables are the same.


Source and explanation :
https://www.thoughtco.com/accurately-me … me-1058453


Cheers


Mathias

74

(8 replies, posted in Script)

Hello,


Sorry I missed you message.


The content of a TStringList is a string, with all the values separated by a comma

,

If you want an array, you need to split the TStringList with the correct command:

LocalArray := SplitString(LocalList,',');

Sending you an example is not really easy as I have no project in mind that could help you right now.


But if you want to transfer a table's content to another, why do you plan on using TStringLists or Arrays ? Why not direct SQL or SQLTransactions ?


Give me a little more detail on what you want to do and I'll try to find an answer for you


Cheers



Mathias

75

(20 replies, posted in General)

Nice work, can't wait for the release  smile