51

(13 replies, posted in General)

Here's the Tic-Tac game project made for MVD 5.6. If anyone wants to improve it, feel free. If anyone knows how I can set up the Sudoku, they're welcome!

52

(4 replies, posted in General)

sparrow wrote:

Hi Derek, Roberto

Roberto ,
Memo supports left, center and right alignment. Unfortunately, it doesn't support justified text alignment.
Memo displays information as plain text, and the only way to do this is by adding extra spaces between words,
and it's only for viewing. Character widths vary across fonts. We'll need to calculate word widths, etc. Maybe I'll give it a try.
For now, I've tried this:
To view notes, you can try using RichEdit, as in the example below. It uses justified text alignment. You can change the font and its size.
It's just a window similar to Memo. The downside is that RichEdit requires manual output processing
(for example: Form1.RichEdit.Text := SQLExecute (' SELECT text FROM ....' ). Accordingly, saving text from RichEdit also requires
manual processing.
Whether the game is worth the effort is up to you.


Hi Sparrow and Derek! I was familiar with justified alignment in RTF. I saw that in MEMO this is complicated, because RTF aligns by increasing the space size...but thank you both for your intention.

Has anyone created a script to justify text in a memo field?

54

(14 replies, posted in General)

Now the trend line is for line chart.

Var
     ChartLine : TChart;

procedure Form1_bLinUpdate_OnClick (Sender: TObject; var Cancel: boolean);
//*** Proc 1334 - LINHA DE TENDÊNCIA GRÁFICO DE LINHA - CHATGPT
var
  Results   : TDataSet;
  DateValue : Double;    // valor TDateTime vindo do SQL
  sValor    : String;
  yVal      : Double;

  // somatórios para regressão (usamos X deslocado = Data - BaseDate)
  sumX, sumY, sumXY, sumX2: Double;
  n: Integer;
  a, b: Double;          // coeficientes da reta y = a*Xoffset + b
  BaseDate, FirstDate, LastDate: Double;
  Xoffset: Double;
  Trend: TLineSeries;
begin
  // Garante que exista ao menos uma série para os valores
  if ChartLine.SeriesCount = 0 then
    ChartLine.AddSeries(TLineSeries.Create(ChartLine));

  // Limpa série de valores
  ChartLine.Series[0].Clear;

  // Assegura que exista a série 1 para a tendência e que seja TLineSeries
  if ChartLine.SeriesCount < 2 then
    ChartLine.AddSeries(TLineSeries.Create(ChartLine))
  else if not (ChartLine.Series[1] is TLineSeries) then
  begin
    ChartLine.Series[1].Free;
    ChartLine.AddSeries(TLineSeries.Create(ChartLine));
  end;

  Trend := TLineSeries(ChartLine.Series[1]);
  Trend.Clear;
  Trend.SeriesColor := clRed;
  Trend.Title := 'Tendência';

  // Formatação do eixo X como data (ajuste visual)
  ChartLine.Axes.Bottom.LabelStyle := talValue;
  ChartLine.Axes.Bottom.DateTimeFormat := 'dd/mm/yyyy';
  ChartLine.Axes.Bottom.LabelsAngle := 90;
  ChartLine.Legend.Visible := True;

  // Inicializa acumuladores
  sumX := 0; sumY := 0; sumXY := 0; sumX2 := 0;
  n := 0;
  BaseDate := 0; FirstDate := 0; LastDate := 0;

  // Query (ordem ASC para cronologia)
  SQLQuery(
    'SELECT Data, valor FROM tbl_medicoes WHERE'+
    ' id_tbl_parametros = '+IntToStr(Form1.cbo_parametro.dbItemID)+' and id_tbl_usuarios = '+IntToStr(Form1.cbo_user.dbItemID)+
    ' and Data >= '+Form1.dt_ano_mes_ini.sqlDate+' and Data <= '+Form1.dt_ano_mes_fim.sqlDate+' and'+
    ' case when length("'+Form1.cbo_sit.Text+'") = 0 then 1=1 else id_tbl_situacao = ' + IntToStr(Form1.cbo_sit.dbItemID)+' end and'+
    ' case when length("'+Form1.cbo_status.Text+'") = 0 then 1=1 else id_tbl_status = ' + IntToStr(Form1.cbo_status.dbItemID)+' end'+
    ' ORDER BY Data ASC',
    Results
  );

  // Percorre resultados — plota pontos e acumula para regressão (usando offset de data)
  while not Results.Eof do
  begin
    DateValue := SQLDateTimeToDateTime(Results.FieldByName('Data').AsString);
    sValor := Results.FieldByName('valor').AsString;

    // tenta converter o valor (aceita "12,5" ou "12.5")
    try
      yVal := StrToFloatLocale(sValor);
    except
      // se não for número válido, ignora este registro
      Results.Next;
      Continue;
    end;

    // primeira data define base (offset = 0)
    if n = 0 then
    begin
      BaseDate := DateValue;
      FirstDate := DateValue;
    end;

    LastDate := DateValue;
    Xoffset := DateValue - BaseDate; // em dias (Double)

    // adiciona ponto real no gráfico (eixo X = DateValue)
    ChartLine.Series[0].AddXY(DateValue, yVal);

    // acumula somatórios usando Xoffset
    sumX  := sumX + Xoffset;
    sumY  := sumY + yVal;
    sumXY := sumXY + (Xoffset * yVal);
    sumX2 := sumX2 + (Xoffset * Xoffset);
    Inc(n);

    Results.Next;
  end;

  // calcula regressão (mínimos quadrados) sobre Xoffset
  if n > 1 then
  begin
    // coeficiente angular a e intercepto b (em relação a Xoffset)
    a := (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
    b := (sumY - a * sumX) / n;

    // Desenha a linha de tendência usando as datas reais (primeira e ultima)
    // yParaFirst = a*(FirstDate-BaseDate) + b  (FirstDate-BaseDate = 0)
    Trend.AddXY(FirstDate, a * 0 + b, '', clRed);
    Trend.AddXY(LastDate,  a * (LastDate - BaseDate) + b, '', clRed);
  end;

  // Títulos para a legenda
  ChartLine.Series[0].Title := 'Valores';
  Trend.Title := 'Tendência';
end;

// Função auxiliar que tenta converter string float aceitando pontos ou vírgulas
function StrToFloatLocale(const S: string): Double;
var
  s1: string;
begin
  // tenta conversão direta (sistema local)
  try
    Result := StrToFloat(S);
    Exit;
  except end;

  // tenta trocar '.' por ',' (ex.: "2.25" -> "2,25")
  s1 := ReplaceStr(S, '.', ',');
  try
    Result := StrToFloat(s1);
    Exit;
  except end;

  // tenta trocar ',' por '.'
  s1 := ReplaceStr(S, ',', '.');
  Result := StrToFloat(s1);
end;

procedure Form1_OnShow (Sender: string; Action: string);
begin

    // CHARTLINE====================================================================
    ChartLine := TChart.Create(Form1);
    ChartLine.Parent := Form1.Panel1;
    ChartLine.Align  := alClient;
    ChartLine.AddSeries(TLineSeries.Create(ChartLine));
    ChartLine.Series[0].XValues.DateTime := True;
    ChartLine.BottomAxis.LabelsAngle     := 90;
end;

function ReplaceStrEx(AText, FromText, ToText: string): string;
var //*** Proc 60 - replace por Dmitry
    ArStr: array of string;
    i, c: integer;
begin
    ArStr := SplitString(FromText, ',');
    c := Length(ArStr)-1;
    for i := 0 to c do
    begin
        AText := ReplaceStr(AText, ArStr[i], ToText);
    end;
    result := AText;
end;

55

(14 replies, posted in General)

jean.brezhonek wrote:

Hi jrga

This proves that what we feared is going to happen.
AI is going to destroy a lot of jobs, first and foremost the entire
world of programmers.
You Give us an example.
JB


Hi JB, AI makes a lot of mistakes! In the case of the trendline, I had to explain what I wanted it to do several times. It made mistakes and repeated some errors. I had to correct parts of the generated script, including the final script. I provided a starting script containing a function created by Dmitry himself (ReplaceStrEx). The AI insisted on giving this function a different name. It takes time to get a result, which often occurs through trial and error. It can help in some cases and speed up problem-solving, but only occasionally. I doubt it will ever fully replace the work of a programmer.

56

(14 replies, posted in General)

Eureka! I managed to add a trendline to a bar chart (MVD 5.6) with the help of ChatGPT. I searched the forum for a solution, but couldn't find one.


Var
   ChartBar: TChart;

procedure Form1_OnShow (Sender: string; Action: string);
begin

    // CHARTBAR====================================================================
    ChartBar := TChart.Create(Form1);
    ChartBar.Parent := Form1.PanelBar;
    ChartBar.Align := alClient;
    ChartBar.AddSeries(TBarSeries.Create(ChartBar));

    TBarSeries(ChartBar.Series[0]).Marks.Visible := False;
    ChartBar.Axes.Bottom.LabelsAngle := 90;
    ChartBar.View3DOptions.Zoom := 90; // Ajuste o zoom para caber todos os labels
    ChartBar.Legend.Visible := true;
    Form1.bBarUpdate.Click;
end;

procedure Form1_bBarUpdate_OnClick (Sender: TObject; var Cancel: boolean);
var
    Results: TDataSet;
    sData: string;
    sValue: string;
    x: integer;
    sumX, sumY, sumXY, sumX2: Double;
    n: Integer;
    a, b: Double;  // Coeficientes da reta Y = aX + b
    firstX, lastX: Integer;
begin
    ChartBar.Series[0].Clear;
    if ChartBar.SeriesCount < 2 then
        ChartBar.AddSeries(TLineSeries.Create(ChartBar)); // cria série p/ tendência

    ChartBar.Series[1].Clear;  // limpa a linha de tendência
    TLineSeries(ChartBar.Series[1]).SeriesColor := clRed; // cor da tendência

    x := 1;
    sumX := 0;
    sumY := 0;
    sumXY := 0;
    sumX2 := 0;
    n := 0;

    SQLQuery('SELECT strftime("%d/%m/%Y",Data) as DMA, valor FROM tbl_medicoes WHERE'+
    ' id_tbl_parametros = '+inttostr(form1.cbo_parametro.dbItemID)+' and id_tbl_usuarios = '+inttostr(form1.cbo_user.dbItemID)+
    ' and Data >= '+form1.dt_ano_mes_ini.sqlDate+' and Data <= '+form1.dt_ano_mes_fim.sqlDate+' and'+
    ' case when length("'+form1.cbo_sit.text+'") = 0 then 1=1 else id_tbl_situacao = ' + inttostr(form1.cbo_sit.dbItemID)+' end and'+
    ' case when length("'+form1.cbo_status.text+'") = 0 then 1=1 else id_tbl_status = ' + inttostr(form1.cbo_status.dbItemID)+' end'+
    ' ORDER BY Data ASC', Results);

    while not Results.Eof do
    begin
        sData := Results.FieldByName('DMA').asString;
        sValue := Results.FieldByName('valor').asString;


        if sValue <> '' then
        begin
            ChartBar.Series[0].AddXY(
                x,
                StrToFloat(ReplaceStr(sValue, '.', ',')),
                sData,
                RandomColor()
            );

            // acumula p/ regressão linear
            sumX  := sumX + x;
            sumY  := sumY + StrToFloat(ReplaceStrEx(sValue, '.', ','));
            sumXY := sumXY + (x * StrToFloat(ReplaceStrEx(sValue, '.', ',')));
            sumX2 := sumX2 + (x * x);
            Inc(n);

            if n = 1 then firstX := x;
            lastX := x;
        end;

        Results.Next;
        Inc(x);
    end;

    // calcula coeficientes da reta
    if n > 1 then
    begin
        a := (n*sumXY - sumX*sumY) / (n*sumX2 - sumX*sumX); // inclinação
        b := (sumY - a*sumX) / n;                          // intercepto

        // adiciona 2 pontos extremos p/ desenhar linha de tendência
        ChartBar.Series[1].AddXY(firstX, a*firstX + b, 'Tendência', clRed);
        ChartBar.Series[1].AddXY(lastX, a*lastX + b, '', clRed);
    end;

    ChartBar.Series[0].Title := 'Valores';
    ChartBar.Series[1].Title := 'Tendência';

end;


function ReplaceStrEx(AText, FromText, ToText: string): string;
var //*** Proc 60 - replace por Dmitry
    ArStr: array of string;
    i, c: integer;
begin
    ArStr := SplitString(FromText, ',');
    c := Length(ArStr)-1;
    for i := 0 to c do
    begin
        AText := ReplaceStr(AText, ArStr[i], ToText);
    end;
    result := AText;
end;

function RandomColor: TColor;
begin // Função para gerar uma cor aleatória em barras dos gráficos
    Result := RGB(Random(256), Random(256), Random(256));
end;
sparrow wrote:

Hi Roberto


Yes, there is such a problem.
I make a replacement in the script and substitute the value into the query.
It will be something like this:

                 rIndice := ReplaceStr(floattostr(form1.Edit_valor.Value),',','.');
                 cbo_status := sqlexecute(
                       ' select id_tbl_status from tbl_referencias where '+
                       ' id_tbl_situacao = 1 and id_tbl_parametros = 1 and '+
                       ' '+rIndice+' between minimo and maximo');

If you do not use a thousand separator, you can simplify it.

rIndice := ReplaceStr(form1.Edit_valor.Text,',','.');

Sparrow, tank you!

How to handle a value with decimal places, coming from a form field, which uses a comma as a separator, in an SQL statement.

                 rIndice := form1.Edit_valor.Value;
                 cbo_status := sqlexecute(
                       ' select id_tbl_status from tbl_referencias where '+
                       ' id_tbl_situacao = 1 and id_tbl_parametros = 1 and '+
                       ' CAST(REPLACE("'+floattostr(rIndice)+'", ",", ".") AS REAL) '+
                       ' between minimo and maximo');

59

(9 replies, posted in General)

v_pozidis wrote:

With avast free version it orks fine, with bit defender and Kaspersky paid versions it stop compiling and warn for virus. I um sure that it must be a false alarm. Does anyone has the same problem and how can we solve it to make the compiled file to be us a clean one, because when the antivirus scan it it delete the file



I have to tell Avast that an MVD application is an exception, always the first time I run it... The problem is the end user encountering this, not understanding what is happening and making the wrong decision....

60

(9 replies, posted in General)

vovka3003 wrote:

Enigma Virtual Box


It works! I've tested it.

61

(19 replies, posted in General)

Finally, I thought it was important to include the ability to print the table grid's content (filtered or not) using the "print" button. This will make it easier if the user needs to show a report of the measurements they took to their doctor.

62

(19 replies, posted in General)

sparrow wrote:

Hi Jean, Roberto

Ok. )
In order for the program to work and not stop by error in version 6.5 (what I checked), some changes need to be made.

1. Change in the "Form1_OnShow" procedure. The diagram initialization must occur before accessing them in the checkboxes. Otherwise, an error.
2. When selecting the year filter (cbo_ano), the year value disappears after updating the tables and graphs. That is why a small correction to the query was suggested above.

I am posting the corrected version so that users of new versions of MVD can launch and work with the program.


Best regards, sparrow.

Now I understand, thank you!

63

(19 replies, posted in General)

Hi Jean, I added a button to delete the contents of the DIABETE table, allowing a new user to start their measurement history. I also added a question at the end, "if a backup is made," if any changes were made to the database. I created the Help.chm with the MyNotesKeeper application.


Dear Sparrow, the SQLite statement you suggested for updating cbo_ano was already contained in the script... thanks anyway!

64

(19 replies, posted in General)

sparrow wrote:
jrga wrote:

Hi Jean, I found your project interesting! I adapted it to my needs (the most widely used language and metric for diabetes in Brazil). I've included a few modifications...


Hi Roberto


This will fix the display in the year filter (cbo_ano)

procedure Form1_cbo_ano_OnEnter (Sender: TObject);
begin
    form1.cbo_ano.dbSQL := 'select distinct strftime("%Y", Date_Prise) id, strftime("%Y", Date_Prise) from diabete order by date_prise';
    form1.cbo_ano.dbUpdate;
end;

Thank you Sparrow!

65

(19 replies, posted in General)

Hi Jean, I found your project interesting! I adapted it to my needs (the most widely used language and metric for diabetes in Brazil). I've included a few modifications...

66

(0 replies, posted in Script)

The "compilation" of the MVD 5.6 project generates a file called "project_name.exe." I took this file (exe) and placed it in the folder of another MVD project, and when I ran it, the application in the new folder worked normally...


I thought that the EXE of an MVD project could only be run in the project for which it was "compiled." I've since concluded that I was wrong!


The "project_name.exe" is a "runtime" that executes an MVD application, requiring only the files: graphics.dll, sqlite3.dll, forms.xlm, settings.ini, tables.ini, script.pas, and sqlite.db to be contained in the same folder as the EXE?


I also noticed that the EXE file used in this case could have any name.


This doesn't diminish MVD's capabilities in any way. On the contrary, it simply demonstrates its versatility!


I'm not an expert on the subject and just wanted to share the information.


P.S.: This BAT was the only one that worked on Windows 11 and allows you to run any MVD application, having the EXE in another folder (single EXE)

@echo off
setlocal

REM Pega a pasta onde o .bat está
set "BATDIR=%~dp0"

REM Caminho do exe original
set "SRC=C:\run_mvd.exe"

REM Copia o executável para a pasta do bat
copy /Y "%SRC%" "%BATDIR%run_mvd.exe" >nul

REM Vai para a pasta do bat
cd /d "%BATDIR%"

REM Executa o programa e espera terminar
start "" /wait "run_mvd.exe"

REM Apaga o executável sem pedir confirmação
del /f /q "run_mvd.exe"

endlocal

I used this statement in a "SQL query" button


SELECT DISTINCT
    Items.id AS ID,
    CASE WHEN finalizado = 1 THEN '✔' ELSE ' ' END AS Ok,
    Items.nome_item AS Item,
    ItemsTree.Name AS Tree
FROM Items
INNER JOIN ItemsTree ON Items.id_ItemsTree = ItemsTree.id
LEFT OUTER JOIN tbl_table ON Items.id = tbl_table.id_Items
LEFT OUTER JOIN tbl_campos ON tbl_table.id = tbl_campos.id_tbl_table
LEFT OUTER JOIN tbl_fld_tipos ON tbl_fld_tipos.id = tbl_campos.id_tbl_fld_tipos
WHERE
    IFNULL(Items.nome_item, '') ||
    IFNULL(ItemsTree.Name, '') ||
    IFNULL(Items.notas, '') ||
    IFNULL(Items.texto_rtf, '') ||
    IFNULL(tbl_table.nome_tabela, '') ||
    IFNULL(tbl_table.descricao, '') ||
    IFNULL(tbl_table.notas, '') ||
    IFNULL(tbl_campos.name, '') ||
    IFNULL(tbl_fld_tipos.Name, '') ||
    IFNULL(tbl_campos.descricao, '') ||
    IFNULL(tbl_campos.notas, '')
LIKE "%{Edit_busca}%"

I created a second script in MVD, which is called by the main script through "uses script_name.pas." I placed messages in this second script with accents—for example, "Attention!"—and sometimes these characters aren't displayed correctly. How do I fix this?

COPILOT: (SOLVED!)

This issue with accents in MVD scripts is usually related to the character encoding used in the file and the code page of the terminal where it's executed. Here are some practical solutions to resolve this:

1. Use the correct encoding when saving the script
—UTF-8 without BOM is the most recommended.
—If you're using an editor like Notepad++, go to:
—Encoding → Convert to UTF-8 (without BOM)
—Then save the file again.

2. Avoid copying and pasting from external sources
Sometimes, when copying text from browsers or editors, accents appear in a different encoding. Type directly into the editor or use tools that clean up formatting.

Great app for backing up data and MVD files, in the development phase - batch backup. Various synchronization options. I use the "mirror" option for synchronization. I also use the option to move files from the previous backup, which have undergone new modifications, to a folder "c:\my_mvd\%date%" (see macros option). Backs up to the cloud... numerous options. There are versions for OSX, Windows, and Linux. Link below



https://freefilesync.org/

derek wrote:

Hi Roberto, Konstantin,
Attached are a couple of suggestions that might help.
1.  Hide Columns Persistent.
With this option, the selection of hidden columns is persistent between sessions. 
Click on the 'Columns' button and then click on the columns in the 'pop-up' grid that you want to hide / unhide (highlighted with an 'x') and then click on the 'Columns' button again.
2.  Re-sort Columns Persistent.
With this option, the order of the columns in the tablegrid is persistent between sessions.
Click on the 'Columns' button and then 'drag and drop' the columns in the 'pop-up' grid in whatever order you want.  When they are in the order that you want, click on the 'Columns' button again to re-sort the tablegrid.
Maybe this can give you some ideas.
Regards,
Derek.

Hi Derek, I understand the dynamics of storing information about modified columns in a specific table and restoring this situation in the OnShow procedure. Thank you.

k245 wrote:

Hide and Seek and Leapfrog
https://k245.ru/wp-content/uploads/2025/07/Igry-detej.jpg
Custom column settings: built-in capabilities supplemented by scripts.
https://k245.ru/en/mvdb-en/hide-and-see … pfrog.html


Very good, congratulations!

72

(12 replies, posted in Script)

this one is good too

73

(12 replies, posted in Script)

Another possibility...that I learned about here on the forum.

k245 wrote:

Is the current user setting for visibility and column position saved the next time the application is launched?

Hi k245! I ran some tests with an application without any scripts, and unfortunately, the information about which columns were hidden or moved isn't saved.

Anyway, my goal was to enable better analysis of monthly values by comparing specific columns and moving and/or hiding one or more of them.

Just sharing a little information. I wasn't aware of MVD's native ability to hide and move columns in a tablegrid so easily. The following link shows a short video showing how this is easily done by the end user.

To do this, in the tablegrid properties, options, set the properties to the following status:
- goCanHideColumn = true
- goDisableColumnMoving = false


https://www.youtube.com/watch?v=smervFM_XHc