Klik en sleep van TableView naar GridView

Klik en sleep
De eenvoudige voorbeelden van klik en sleep met de muis, zoals die waarbij een tekst van een TextBox naar een TextLabel worden gesleept, schieten te kort als je een TableView of een GridView gebruikt (beiden even roosters genoemd). Immers: je kan een heel rooster slepen naar een ander rooster en daar allerlei leuke dingen mee doen, maar meestal is dat niet de bedoeling.

Cel in plaats van rooster
Je wil een bepaald vakje, één cel, of eerder nog de inhoud daarvan, naar een andere plaats slepen. De bestemming is dan niet een element als TextBox of GridView, maar ook weer een bepaalde cel van een TableView of GridView rooster.

Drag..
De basis blijft hetzelfde:
Voor de vertrekplaats moet een _MouseDrag() gemaakt worden:

PUBLIC SUB TableView1_MouseDrag()
  IF Mouse.Left
    Drag.Icon = PictureBox1.Picture
    TableView1.Drag(TableView1[TableView1.Row, TableView1.Column].Text)
  ENDIF
END

Ik heb in de IDE op voorhand een PictureBox1 op FMain gezet, er in de IDE een beeldje voor gekozen uit de verzameling (stock), en die PictureBox1.Visible op FALSE gezet.

.. en Drop
De bestemming moet als eigenschap Drop = TRUE hebben:

GridView1.Drop = TRUE

en er moet drop code gemaakt worden:

PUBLIC SUB GridView1_Drop()
  GridView1[GridView1.RowAt(Drag.y), GridView1.ColumnAt(Drag.X)].Text = Drag.Data
END

Om de data in de Text te krijgen van de gewenste cel op rij, kolom

GridView1[row, column].Text = Drag.Data

moet je die coördinaten eerst detecteren:

GridView1.RowAt(Drag.y)
GridView1.ColumnAt(Drag.X)

Dit is de kleine “truuk” om het klikken en slepen tussen TableViews en GridViews mogelijk te maken.
Er zit nog wel een addertje onder het gras: als je een beetje buiten de bestaande cellen sleept en daar in het gridview gebied onder de laatste rij cellen bv loslaat, zijn de coördinaten niet gedefineerd. Crash!

Dus:
TRY GridView1[GridView1.RowAt(Drag.y), GridView1.ColumnAt(Drag.X)].Text = Drag.Data

Verder moet je de code aanvullen met alles wat moet gebeuren, zoals opslaan in een databank, berekeningen enz.

Rechter muisklik

Als je wil reageren op de rechtermuisklik kan dat in volgende twee stappen:

PUBLIC SUB SchermObject_MouseDown() 
  '
  IF Mouse.Right
    ' Doe iets
  ENDIF 
  '
END

Veelgebruikt is extra informatie of een extra optie geven voor het met de muis aangewezen object.

Om de gebruiker in te lichten vul je de “tooltip” in van het aangewezen object, zodat de extra mogelijkheid uitgelegd wordt alvorens geklikt is.

Voorbeeld: een rooster met gegevens van werknemers (Tableview met Employee data):

Twee andere procedures moeten bestaan;

  • newDataAvailable() AS BOOLEAN ‘ checkt of er nieuwe data is
  • doReloadData() ‘ haalt nieuwe data af en vult ze in de tabel
PUBLIC SUB tbvwEmpl_MouseDown() 'tbvwEmpl_DblClick()
  '
  IF Mouse.Right
    IF newDataAvailable()
      tbvwEmpl.Clear()
      doLoadData()
    ELSE 
      Message.Info("Up to date already")
    ENDIF  
  ENDIF 
  '
END

Data module met universele procedure om record toe te voegen

Gambas Modules of minstens stukken ervan kan je zo universeel maken dat je ze gemakkelijk kan hergebruiken.
Bij gebruik van een databank (bv MySQL/MariaDB) moet je altijd code schrijven om een record aan te maken. Het gemeenschappelijke daarin is minstens het toevoegen van een record met een sleutel (record id).

Je kan de aanmaak van een nieuw record (dus met nieuwe gegevens) opsplitsen in twee delen:

  • Aanmaken van een leeg record met een nieuw bekomen record nummer.
  • Het aanvullen van dat nieuwe record met alle andere gegevens, een update dus.

De update procedure moet je waarschijnlijk toch ook maken om gegevens te kunnen wijzigen aan een record waarvan je de id kent.

Het aanroepend programma kan gewoon een “ModData.saveRecord(id, data)” gebruiken, de module splitst het op in twee delen.
Als de id gegevens is, wordt de update procedure aangeroepen.
Als de id = 0 moet er een nieuw record gemaakt worden, daarna de update procudure met de ondertussen verkregen id.

Een datamodule ModData bevat:

PRIVATE sLastError AS String
' ...
PUBLIC SUB addRecord(sTablename AS String, sKeyname AS String) AS Long
' adds a record to a given database, get the record id of the empty record 
  DIM sSql AS String  
  DIM myResult AS Result
 ' 
  sSql = "INSERT INTO " & db.Quote(sTablename) & " (" & sKeyname & ") VALUES(0)" 
  DEBUG sSql
  '
  TRY $hConData.Exec(sSql)
  IF ERROR 
    sLastError &= "insert; " & Error.Text
    RETURN 0
  ELSE 
    sSql = "SELECT LAST_INSERT_ID() AS myId "
    'myId" ' FROM " & db.Quote(sTablename)
    TRY myResult = $hConData.Exec(sSql)
    IF ERROR 
      sLastError &= "last id ?" & Error.Text
      RETURN 0
    ELSE 
      RETURN myResult!myId 
    ENDIF 
  ENDIF 
  DEBUG sLastError
END

De procedure geeft het aangemaakte recordnummer terug. Daarna kunnen de gegevens ingevuld worden, wat minder universeel is. Veel kans dat dit stuk code enkel intern vanuit de module aangeroepen wordt, je zou ze dus ook PRIVATE kunnen maken.
Fouten vraagt het aanroepend programma/aanroepende procedure op met getLastError(), wat geimplementeerd is als

PUBLIC SUB getLastError() AS String
  '
  RETURN sLastError
  '
END

Alle procedures in de datamodule die een record moeten toevoegen in een database, kunnen deze zelfde functie gebruiken, mits aangeroepen met de juiste parameters voor de betreffende tabel.

Voorbeeld:

# Name Type Null Default Extra
1 emp_id bigint(20) No None AUTO_INCREMENT
2 emp_data1 int(11) No 0  
3 emp_data2 int(11) No 0  
4 emp_data3 char(1) Yes NULL  
5 emp_data4 varchar(11) Yes NULL  
6 emp_data5 datetime Yes NULL  
7 emp_data6 datetime Yes NULL  
8 emp_cre timestamp Yes CURRENT_TIMESTAMP  
9 emp_creby char(24) Yes NULL  
10 emp_upd datetime Yes NULL  
11 emp_updby char(24) Yes NULL  

Dit werkt op voorwaarde dat

  • de key record_id een getal is, bv van het type bigint(20)
  • de waarde van het veld record_id altijd automatisch wordt toegekend door de database (Auto Increment)
  • er standaardwaarden voorzien zijn in de struktuur van de tabel (0, NULL, ..).

Als geen standaardwaarden voorzien zijn krijg je een foutmelding; de database krijgt enkel een opdracht om de key in te vullen en weet niet wat er met de andere velden moet gebeuren.

Andere dingen die je kan standaardiseren:

  • Een foutmelding bijhouden (zie ook hierboven sLastError) en op laten vragen.
  • Een record verwijderen dat een bepaalde keywaarde heeft.
  • Opbouwen, openen en sluiten van de databaseverbinding
  • Een keuze tussen verschillende datasets voorzien (bv laptop/netwerk, ontwikkeling/productie)
  • enz.

Metadata
Dikwijls is het nuttig om te weten wanneer een record ontstaan is (creation date-time), en wie het gemaakt heeft (user). Als je daarvoor standaard velden opneemt in de database kan je die laten invullen vanuit deze record toevoegen-procedure. Het eenvoudigste voor het “createdon” veld is in de database als standaardwaarde “CURRENT_TIMESTAMP” in te stellen. Dan blijft nog het “createdby” veld, waar we de gebruikerslogin naam kunnen invullen (gb.user.name). Ofwel laat je een waarde opslaan die als parameter van de oproepende procedure komt, en kan het een andere vorm van gebruikers-id zijn, bv die waarmee in de applicatie ingelogd wordt.

  • Database: employee
  • Velden: id, naam, address, …, createdon, createdby
  • Database: department
  • Velden: id, building, floor, …, createdon, createdby

Dit is universeel; je kan employee.createdby of department.createdby enz. gebruiken; bij de AddRecord is het steeds dezelfde naam. Je moet dat dan voor alle tabellen zo doen!

Een andere mogelijkheid is de createdby optioneel te maken, zodat je ook nog minimale tabellen met enkel id kan houden.
En: als je niet de standaardnaam gebruikt, maar velden met een prefix voor de tabel gebruikt, moet je de veldnaam ergens vandaan halen.

  • Database: employee
  • Velden: emp_id, emp_naam, emp_address, …, emp_createdon, emp_createdby
  • Database: department
  • Velden: dep_id, dep_building, dep_floor, …, dep_createdon, dep_createdby

Gecombineerd:

PUBLIC SUB addRecord(sTablename AS String, sKeyname AS String, OPTIONAL sCreatedByField AS String) AS Long 
' adds a record to a given database, get the record id of the empty record 
  DIM sSql AS String  
  DIM myResult AS Result
 '
  sSql = "INSERT INTO " & db.Quote(sTablename) & " (" & sKeyname 
  IF sCreatedByField
    sSql &= "," & sCreatedByField & ") VALUES(0,&1)" ' & db.Quote(User.Name) & ")"
  ELSE 
    sSql &= ") VALUE(0)" 
  ENDIF 
  '
  DEBUG sSql
  '
  TRY $hconData.Exec(sSql, User.Name)
  IF ERROR 
    sLastError &= "insert; " & Error.Text
    RETURN 0
  ELSE 
    sSql = "SELECT LAST_INSERT_ID() AS myId "
    '
    TRY myResult = $hConData.Exec(sSql)
    IF ERROR 
      sLastError &= "last id ?" & Error.Text
      RETURN 0
    ELSE 
      RETURN myResult!myId 
    ENDIF 
  ENDIF 
  DEBUG sLastError
END 

De procedure AddRecord krijgt een optionele parameter “sCreatedByField”.

Als die ingevuld is, wordt dat veld gebruikt om de gebruikersnaam in te bewaren; ‘User.Name’ kan je in Gambas altijd gebruiken en geeft de loginnaam van de gebruiker die het programma draait. Hier wordt die ingevuld vanuit AddRecord, zodat er van het oproepend programma niet aan gedacht moet worden, of ook de waarde niet vergeten of verkeerd ingevuld kan worden; zelfs als de update nadien niet werkt heb je al meta-informatie.

Als geen veldnaam wordt doorgegeven om de createdby informatie in op te slaan, wordt enkel de record_id ingevuld.

Upd: Ps: misschien gebruik ik ook beter geen tabelnaam-PREFIX in de veldnamen … (http://gambas.copyleft.be/blog/archives/1453)

Gambas 3 op Linux Mint

Winnie de Pinguin schrijft (een laat verslag):

Ik bevestig dat Gambas perfect werkt op Linux Mint (Gambas versie 3 – v 2 niet gevonden in softwarebeheer, ook niet de runtime).
Gebruikt: menu, softwarebeheer. programmeren.

Gambas3
Complete visual development environment for gambas

Gambas is a free development environment based on a Basic interpreter with object extensions, like Visual Basic(tm) (but it is NOT a clone!). With Gambas, you can quickly design your program GUI, access MySQL or PostgreSQL databases, pilot KDE applications with DCOP, translate your program into many languages, and so on...

This package doesn't include anything: it is a metapackage to install the IDE and all the available Gambas components...

Op Linux Mint 15 Olivia (inderdaad van eind 2013…)

OpenSUSE 13.2: unable to find startup file

Net zoals ik met 13.1 wel eens lasthad van een “update=gambas weg” (of minstens gambas onbruikbaar), merk ik nu hetzelfde in OpenSUSE 13.2. Oorspronkelijk geïnstalleerd: Gambas3 uit de education repository.
Laatse versie van 04 feb 2015 heeft er blijkbaar voor gezorgd dat gambas niet meer opstart; in KDE blijft het icoon staan stuiteren (bouncen), en op de commandolijn zie je:

gambas3
gbx3: unable to find startup file

In de package manager zie ik echter dat er een viertal componenten niet aangevinkt staan:

  • gambas3-gb-gtk3 (The GTK3 GUI component
  • gambas3-gb-gb-inotify (Monitor filesystem events)
  • gambas3-gb-markdown (Gambas markdown syntax)
  • gambas3-gb-option (The C getopt() interface component)

Geen idee waarom/welke ik nodig zou hebben, maar ik vink ze allemaal aan en installeer ze.

Ondertussen merk ik nog met

/usr/bin/gambas3.gambas
gbr3: unable to load component: gb.markdown

En jawel! Gambas3 loopt weer!

Gambas munix9 repository: gepromoveerd?

Sinds een tijd valt het me op dat er geen Gambas updates meer binnenkomen, terwijl dat vroeger zeer frequent gebeurde. Mijn huidige Gambas3 is versie 3.6.2-10.1. Ik had ook even het probleem dat de iconen op de desktop, en de link in het menu naar het programma Gambas3 verdwenen waren.


Ik krijg ook een foutmelding “Cannot access installation media” op de repository “home:munix9”, die als URL heeft:
http://download.opensuse.org/repositories/home:/munix9:/gambas/openSUSE_13.1/

File ‘/repodata/repomd.xml’ not found on medium ‘http://download.opensuse.org/repositories/home:/munix9:/gambas/openSUSE_13.1/’


Ik zie op de server http://download.opensuse.org dat dit “path” niet meer bestaat (of nog wel bestaat maar leeg is). Als ik wat op en neer wandel door de directories zie ik dat er wel een andere weg is die naar Gambas3 leidt:

opensuseRepoMunix9

openSUSE Repo Munix9

Volg:
http://download.opensuse.org/repositories/home:/munix9/openSUSE_13.1/
om uit te komen op:

gambas3-3.7.0-4.1.x86_64.rpm 26-Mar-2015 10:00 23K

Wat inderdaad een recentere versie is.

Als ik deze repository instel, zie ik in Yast wel enkele componenten waarvoor een 3.6.2-1.2 aanwezig is, maar geen 3.7.0 wordt aangeboden!

gambas3-gb-geom
gambas3-gb-gui
gambas3-gb-maps
gambas3-gb-media-form
gambas3-script

En wat er in 3.7 beschikbaar is, zonder dat ik daarvan een 2.6 versie heb:

gambas3-gb-report2
gambas3-gb-scanner
gambas3-gb-sdl2
gambas3-gb-util
gambas3-scripter

En bovendien is er een hele verzameling 3.7.90.svn.70000-6.1 bijgekomen, maar die heb ik niet aangezet.

Ik kies voor alle gambas3 paketten wel de update naar 3.7 in Yast, en bevestig andere afhankelijkheden (en het verwijderen van “vervallen” componenten uit 3.6).

Gambas 3 start daarna mooi op als Gambas 3.7

Enkele dagen later is er een upgrade; ik stel ook vast dat deze component niet meer voorkomt in Yast2 lijst van gambas3:

gambas3-gb-media-form


ps: eerst kreeg ik een foutmelding, maar de volgende keer werd de update toch uitgevoerd;
30/03: Foutmelding bij update: problem connecting software origins.

File ‘./x86_64/gambas3-runtime-3.7.1-1.x86_64.rpm’ not found on medium ‘http://download.opensuse.org/repositories/home:/munix9/openSUSE_13.1/’

Update wordt daardoor overgeslagen.

Daarna toch uitgevoerd; 3.7.1

Gambas script als shell script

Ben je zodanig aan Gambas verslingerd dat je een shell script liefst in Gambas schrijft?

Eenvoudig script

Het eenvoudigste voorbeeld is altijd de “Hello World”; maak een tekstbestand helloGambas.sh in ~/bin met als inhoud:

#!/usr/bin/gbs2
Public Sub Main()
   Print "Hello Gambas World v. " & System.Version
End

De eerste lijn geeft aan waarmee dit script moet uitgevoerd worden. Daarvoor kijk je waar gambas staat, bv met “which gbs2”; het antwoord op OpenSuse: /usr/bin/gbs2, elders bv /usr/local/bin/gbs2, enz.

Als je met Gambas versie 3 werkt is het :

#!/usr/bin/gbs3
Public Sub Main()
   Print "Hello Gambas World v. " & System.Version
End

Pas ook hier de eerste lijn aan aan je eigen systeem.

Maak het script uitvoerbaar
chmod +x helloGambas.sh
En start het van de commandolijn met de naam: ./helloGambas.sh

De uitvoer moet natuurlijk

Hello Gambas World v. 2

of

Hello Gambas World v. 3

zijn.

Nu heb je een script dat je kan starten vanop de commandolijn, net zoals je andere shell scripts.

Programma in plaats van script

Als je een veel groter/uitgebreider programma wil laten draaien, kan dat door een nieuw project te openen, kies bij Project Type voor “Command Line Application”.

Dan krijg je geen FMain, dus geen venster dat geopend wordt bij het starten van het programma.
Je krijgt wel een MMain als startende module, en je kan klassen bijmaken enz.

Andere opties mag je wel aankruisen, zoals “settings file managament” en “database access”.

Het uiteindelijke programma compileer je tot mijnProgramma.gambas, en je maakt het uitvoerbaar als het dat nog niet zou zijn.

Je kan dat programma dan aanroepen vanin een shell script, bv:
/home/username/bin/runTest.sh
met als inhoud:
/home/username/uitvoerbaar/mijnProgramma.gambas

In het script kan je ervoor of erna een touch doen naar een bestand om te checken of het script gelopen heeft:


touch /home/username/startedScript.txt
/home/username/uitvoerbaar/mijnProgramma.gambas

Als je vanuit dit shell script je gambas programma kan starten, kan je runTest.sh opnemen in de cron jobs: uitvoeren vanuit de cron jobs,.

Verdere interessante mogelijkheden zijn: een script laten uitvoeren als CGI script in een webserver, …

Ontbrekende pdf-component in Gambas2 project

Bij het overbrengen van een project in de vorm van een source-code archief, kreeg ik na het uitpakken en compileren vanuit de IDE een foutmelding over een ontbrekende pdf-component (het te installeren programma maakt prints).

In mijn installatie op de doel-pc (uit OpenSUSE Education, Gambas 2.24.0-2.16 ) komt die pdf niet voor, maar ik heb een identiek systeem, waarop ik ontwikkel, en ze duidelijk wel aanwezig zijn. Mogelijk is er een iets andere versie van Gambas, of nog waarschijnlijker, een andere repository gebruikt bij installatie van Gambas.

In mijn geval heb ik de betreffende bestanden manueel naar het andere systeem gekopieerd:

gb.pdf.component
gb.pdf.la
gb.pdf.so (link)
gb.pdf.so.0
gb.pdf.so.0.0.0

Ze zijn terug te vinden in /usr/lib64/gambas2

Geen “mooie” oplossing, maar daarna werkte het programma wel normaal.

Gambas3 start niet op in OpenSUSE 13.1 (ok)

Waarschijnlijk weer na een update begin februari, ik heb helaas gemist waar/wanneer juist.

Huidige toestand:

OpenSUSE 13.1 met KDE, uptodate.
Gambas3 IDE: 3.6.2-1.2 uit de OpenSUSE build service, Education.

Veel (de meeste) andere bestanden zijn versie 3.6.2-10.1 en komen uit de “munich” repository.

Blijkbaar is de Gambas installatie slechts gedeeltelijk geupdate, en/of heeft de edu de munich overschreven. Heeft het iets te maken met die 1 en 10 na de hoofdversienummers?

Ik laat manueel updaten (Yast, install/remove software), daarvoor verzet ik alles wat op de Education repo staat naar de munich repo, en dat waren:

gambas3-devel
gambas3-gb-args
gambas3-gb-db
gambas3-gb-db-form
gambas3-gb-desktop
gambas3-gb-eval-highlight
gambas3-gb-form
gambas3-gb-form-dialog
gambas3-gb-form-mdi
gambas3-gb-image
gambas3-gb-image-effect
gambas3-gb-qt4
gambas3-gb-qt4-ext
gambas3-gb-qt4-webkit
gambas3-gb-settings
gambas3-ide

Daarna worden nog volgende Automatic Changes gedaan:

gambas3-gb-option
systemd-rpm-macros
gambas3-scripter
debhelper
rpm-build

Ok! Gambas 3.6.2 is terug.

Op een andere computer met ongeveer hetzelfde systeem (13.1), is enkel de IDE van versie 3.6.2-10.1, en al de rest is 3.6.2-1.2.
Daar doe ik een downgrade van dat ene pakket naar de Education repository (dus naar 3.6.2-1.2), maar dat geeft geen startende gambas3.
Ik doe dus een onconditionele upgrade van de hele lijst (na gambas3 selectie) in yast. Helaas lijkt yast van 3.6.2-1.2 naar 3.6.2-10.1 NIET als een UPGRADE te beschouwen, wat een bug lijkt in de interpretatie van de versienummers door Yast of de updater. En een keuze voor “heel de lijst – downgrade” zit niet in het rechtsklik menu. Ik zet even de Educational repository af, en doe dan een onconditionele upgrade.

nb: Misschien moet ik die Munich repository een wat hogere prioriteit geven? Dat doe je door die repository een lager nummer te geven, bv van 99 naar 98. In dit geval hielp het niet (versienummer-bug?).

nb 2: Achteraf merkte ik dat bepaalde iconen van mappen met gambas inhoud, verdwenen waren. Op mijn desktop had ik het icoon van de map veranderd naar een gambas icoon, dat waarschijnlijk deel uitmaakte van een gambas installatie. Ik ben niet helemaal zeker van het verband.

Update 27/3
: mogelijk is dit de oorzaak (en oplossing)?