tatukgis, save & load layer properties - delphi

If someone have some experiences with TatukGIS developer kernel product, can you explain how to programmatically save and load layer properties like in their free TatukGIS viewer product?
The environment i use were Delphi 7 and BDS 2006
thanks
Edit, this code don't work:
var
lyrPeta: TGIS_LayerSHP;
begin
MapPath:= ExtractFilePath(Application.ExeName) + '\maps\';
lyrPeta:= TGIS_LayerSHP.Create;
lyrPeta.Path:= MapPath + 'jabodetabek'+'_asjalan.shp';
lyrPeta.Name:= 'jabodetabek'+'_asjalan.shp';
lyrPeta.IgnoreShapeParams:= True;
lyrPeta.UseConfig:= True;
lyrPeta.ConfigName:= MapPath + 'jalan.ini';
lyrPeta.ReadConfig;
GIS_Viewer.Add(lyrPeta);

We do this as below in VB6. I think our code was originally based on this FAQ entry. It makes the Tatuk control save an INI file with the layer properties. I believe it automatically reloads the layer properties when the app starts again. If you only want one file, rather than one file per layer, you could try a TTKGP file.
With layer ' the layer is an XGIS_LayerVector '
.StoreParamsInProject = False
.ConfigName = "c:\test\mylayer.ini" ' a separate INI file for each layer '
.WriteConfig
.SaveAll
End With
EDIT: And loading again - I think this is after the layer has been added to the GIS control.
With layer ' the layer is an XGIS_LayerVector '
.StoreParamsInProject = False
.ConfigName = "c:\test\mylayer.ini" ' a separate INI file for each layer '
.RereadConfig
End With
IMHO the Tatuk help and samples are hard to understand, although the product itself is pretty powerful. It's worth trying the forum for Tatuk questions as well, although you need a support agreement to access it :(

Related

Delphi, Handles in TPanel objects

I was reading the source code for tmplayer and MPUI, both media players that use MPlayer as a backend to play media files in Delphi and I noticed here as well as in other older codes that I tried to experiment with that I can't use
Form1.Rectangle1.Handle
or as it is in this example for tmp layer that is trying to pipe the video output at a TPanel
CmdLine := HomeDir + 'mplayer.exe -slave -identify'
+' -wid ' + IntToStr(Form1.Panel1.Handle) + ' -colorkey 0x101010'
+' -nokeepaspect -framedrop -autosync 100' + #32 + Media;
As you may see RAD studio xe6/7 complains that the element does not contain a parameter named Handle. Is this something that used to be valid in older versions but can't be used anymore, and if yes how can I accomplish the same task in a simple Firemonkey form.
EDIT
Ok, so now it is clear to me that the Handle parameter is only applicable for VCL Forms. Trying to rephrase my question,
How can I port that particular line of code (getting the handle of a component) in a Firemonkey form?
Panels and/or Rectangle in Firemonkey doesn't have Handle as member. FireMonkey Handle is member of Form component instead .
You can convert a FireMonkey handle to the platform specific handle with the function : WindowHandleToPlatform, but there is no way to get the handle of a control inside a Form .
Maybe you can replace the Rectangle with another inner form and get the handle of it. Take a look at the FMXEmbeddedForm .

How do I work with Word Documents without using COM Automation?

I have read multiple posts on the issue, and none seem to come to a decent conclusion to my question. (Perhaps I'm trying to see if anything has popped up lately.)
I have a small charity app that handles pledges. In doing so, it needs to work with and print documents.
Thing is, if Word is open in the background, the app thread will hang and won't respond to the closure of Word, and I have to roll back manually and close word. Sure, that all works fine, but I simply cannot guarantee that the end user will close Word, even if I put the instruction in a user manual.
I'm not too fussed about speed, but I guess that if it can be enhanced, it would be a nice little bonus.
Have any libraries been released for Delphi that will allow me to open, edit, print, and save documents? If not, is there a way to use Word Automation in such a way that it will not conflict with another open handle of Word when opened?
If you use GetActiveOleObject, you will get the running instance of Word.
By using CreateOleObject, you will get a new instance and shouldn't be troubled by other running instances.
In case you use the TWordApplication, wrapper you can set ConnectKind to ckNewInstance to accomplish this. By default, TWordApplication will try to connect with a running instance.
If you want to open edit and print Word documents and you don't mind using RTF format for what you're doing, investigate TRichView.
It will generate rich documents that are in RTF format, which is one of the formats MS word supports. I don't think it directly reads .DOC files but you can convert .DOC and .DOCX into RTF, for most simple files, but certain advanced formatting features would be lost in the conversion.
It has the advantage of working without any need for even any copy of MS Word to be installed on the machine that is going to do the document processing. For production of receipts and other simple documents, this would be the most reliable technique; Don't use Word directly, at all.
procedure PrintViaWord (const filename: string);
const
wdUserTemplatesPath = 2;
var
wrdApp, wrdDoc, wrdSel: variant;
begin
wrdApp:= CreateOleObject ('Word.Application'); // create new instance
sleep (5000); // this fixes a bug in Word 2010 to do with opening templates
wrdDoc:= wrdApp.documents.add (
wrdApp.Options.DefaultFilePath[wdUserTemplatesPath] + '\mytemplate.dot');
wrdDoc.Select;
wrdSel:= wrdApp.selection;
wrdApp.Options.CheckSpellingAsYouType:= 0;
wrdSel.paragraphformat.alignment:= 1;
wrdSel.typetext ('This is a program demonstrating how to open Word in the background'
+ ' and add some text, print it, save it and exit Word');
wrdDoc.SaveAs (filename + '.doc');
wrdApp.ActivePrinter:= 'Konica Minolta 211';
wrdApp.PrintOut;
WrdDoc.SaveAs (filename + '.doc');
wrdApp.quit;
wrdSel:= unassigned;
wrdDoc:= unassigned;
wrdApp:= unassigned
end;

Delphi Binary/Text File Necessity

I am an engineer and not a software programmer, so please excuse my ignorance.
I have written a Delphi(7SE) program to read “real” datatype from a USB port connected to two digital thermometers.
I have completed this much of the program.
What I have not completed as yet is explained by the following:
I wish to save this “real” data to a Binary File(s). A text file would be fine as well, but i'm concerned about having a big data file.
I also wish to read this data back from the Binary/Text File to display the data using my Delphi application.
I don’t think this would be too difficult. I currently save my data in .CSV format.
The twist here is that the binary file should contain data from different sessions initiated by the user of my application.
So when I click on say, a button called “ historical” data, a new window/form would pop up that would show different session times that I had started & stopped from earlier times. Then a session would be selected and data then retrieved for displaying.
Can this be done in one binary files or would you have to use 2 files: one for the “real” data and another which indexes the different session times?
My requirement for this way of saving binary data is that I would not have to keep typing in filenames and therefore keeping track of many data files.
For example a thermo.hst(historical data) and a thermo.idx (index file) file would contain all the information such as actual temp data, time of read data, session start & end times etc.
Any useful pointers and hopefully code with as much detail would be greatly appreciated.
I hope this sample code isn't too late to be helpful.
(I've added this as another answer from me so that I can cleanly list the code. If this or my previous post answers your question, please click the answer icon so I get reputation points!)
Below is some rough code that shows how to read the sections in an ini file and find the largest filename. I confirmed it compiles and seems to return valid values, but you'll need confirm it does what you need. It's more to show you the idea...
Note that if your data filenames have an extension, you'll have add code to remove the extension in my sample code using something like: FileName := ChangeFileExt(Filename, '').
// Call with an open inifile. Returns the name of the next filename, or '' if trouble
Function GetNextFileName( const IniFile: TInifile):String;
const
BASE_FILENAME = 'File.'; // sections in the ini file will be [File.1], [File.2], ... [File.100], etc.
var
Sections: TStringList;
NumericPartAsString: String;
NumericPartAsInteger: Integer;
ListIndex: Integer;
LargestFileNumberSeenSoFar: Integer;
begin
Result := '';
Sections := TStringList.Create;
IniFile.ReadSections(Sections); // fills StringList with the names of all sections in the ini file
if( Sections.Count = 0) then
Result := BASE_FILENAME + '1'
else
begin // find largest extension
LargestFileNumberSeenSoFar := -1;
ListIndex := 0;
while ListIndex <= (Sections.Count - 1) do // for every string (which is also a filename) in the string list:
begin
NumericPartAsString := StringReplace(Sections.Strings[ListIndex], BASE_FILENAME, '', []); // remove base filename
if (NumericPartAsString <> '') then
begin
NumericPartAsInteger := StrToIntDef(NumericPartAsString, -1);
if (NumericPartAsInteger > LargestFileNumberSeenSoFar) then
LargestFileNumberSeenSoFar := NumericPartAsInteger;
end;
inc(ListIndex);
end;
if (LargestFileNumberSeenSoFar > -1) then
Result := BASE_FILENAME + IntToStr(LargestFileNumberSeenSoFar + 1);
end;
Sections.Free;
end; { GetNextFileName }
procedure TForm1.Button1Click(Sender: TObject);
var
IniFile: TInifile;
NewFileName: String;
begin
IniFile := TInifile.Create('c:\junk\ini.ini');
NewFileName := GetNextFileName(Inifile);
if (NewFileName = '') then
ShowMessage('Error finding new filename')
else
ShowMessage('New filename is ' + NewFileName);
IniFile.Free;
end;
By using a database, you've in part just renamed part of the problem from "typing in file names and keeping track of many data files" to "typing in data set name and keeping track of many data sets."
In both cases, as an example, either the user or the program has to create a new file/data set name, and choose from a list of files/data sets to open later. And in both cases you have to make a call to a function named something like "DeleteDataSet".
You might re-consider whether you really need a database (with associated learning curve for API, how to define the data structure, update it in the field when something changes, browse the data in a viewer, access the data programatically, proprietary format, database maintenance, repair tools, installation, etc.) I'm sure that you could learn all these, and they might be valuable in future projects. But, maybe a simpler approach would be more appropriate and adequate for this one-time project by a non-software engineer.
If you're willing to have a proliferation of many unique, standalone data files on one folder, I'd encourage you to stick with what's working: use one CSV file per data set. (Have you run into speed or size issues with CSV files containing a single data set thus far? That would be an enormous amount of data!) One nice thing about CSV files is that you can just pop them into an editor to view or edit...
And, then, add a second file that contains filenames, and other descriptive information. This file would be a simple TIniFile:
[My Name one]
Date=06 June 2010
StartTime=12:30pm
StopTime=3:15pm
FileName=Data1.csv
[My Name two]
...
The tools available in Delphi for TIniFile will let you easily manage this list, including ReadSections into a string list that you can just assign to a combo box for the user to select a data set. (See example below) And, like the CSV files, you can just edit the .ini file in any text editor.
You'll need to build a UI to allow a user to delete a dataset (section in the ini file and associated .csv file). To give you an idea how the ini file would be used, here's the pseudo-code for deleting a data set:
(In IDE Object Inspector, set ComboBox.Style := csDropDownList to prevent user from typing in a name that doesn't exist.)
Load a combo-box that shows available data sets.
1. ComboBox.Items := IniFile.ReadSections;
In the combo-box's OnSelect event handler:
2. DeleteFile(IniFile.ReadString(CombBox.Text, 'FileName', ''));
3. IniFile.EraseSection(ComboBox.Text); // remove the section from the inifile
Heck, that's not a lot of code, even after you add a bit of protection and error checking!
Maybe the above solution will be voted down by others here as trying to put a round peg in a square hole or re-inventing the wheel. And I might agree with them. There are good arguments against this approach, including proliferation of many files. But, if it was me, I'd at least consider this approach as keeping-it-simple and not requiring anything new but that you learn the TIniFile object, which is quite powerful.
The data can be interleaved. Just start every block (a set of history) with a header that identifies the block and contains its length. When reading you can then easily separate that.
You can hold an additional index file next to this for fast access if you require, but if this is the case, I would start studying some embedded database. (TDBF, sqlite or embedded firebird).
I would also head in the database direction if I expected that my querying would get more complicated in the future.
If it is all about logging, the data doesn't get gigantic and the performance of the view is fine, I would keep the binary file and avoid the hassle of having users to install and maintain a databsae solution. (TDBF is maybe an exception to that, since it is completely statically linked)

any functions to create zip file of directory/file on vista with delphi 2009

I am looking for a simple method of zipping and compressing with delphi. I have already looked at the components at torry delphi:http://www.torry.net/pages.php?s=99. They all seem as though they would accomplish what I want however a few disadvantages to using them is that none of them run in delphi 2009 and are very complex which makes it difficult for me to port them to delphi 2009. And besides, the documentation on them is scarce, well at least to me. I need basic zipping functionality without the overhead of using a bunch of DLLs. My quest lead me to FSCTL_SET_COMPRESSION which I thought would have settled the issue but unfortunately this too did not work. CREATEFILE looked promising, until I tried it yielded the same result as FSCTL_SET... I know that there are some limited native zipping capability on windows. For instance if one right clicks a file or folder and selects -> sendTo ->zipped folder, a zipped archive is smartly created. I think if I was able to access that capability from delphi it will be a solution. On a side issue, does linux have its own native zipping functions that can be used similar to this?
TurboPower's excellent Abbrevia can be downloaded for D2009 here, D2010 support is underway and already available in svn according to their forum.
Abbrevia used to be a commercial (for $$$) product, which means that the documentation is quite complete.
I use Zipforge. Why are there problems porting these to D2009? Is it because of the 64bit??
Here is some sample code
procedure ZipIt;
var
Archiver: TZipForge;
FileName: String;
begin
try
Archiver:= TZipForge.create(self);
with Archiver do begin
FileName := 'c:\temp\myzip.zip';
// Create a new archive file
OpenArchive(fmCreate);
// Set path to folder with some text files to BaseDir
BaseDir := 'c:\temp\';
// Add all files and directories from 'C:\SOURCE_FOLDER' to the archive
AddFiles('myfiletozip.txt');
// Close the archive
CloseArchive;
end;
finally
Archiver.Free;
end;
end;
If you can "do" COM from Delphi, then you can take advantage of the built-in zip capability of the Windows shell. It gives you good basic capability.
In VBScript it looks like this:
Sub CreateZip(pathToZipFile, dirToZip)
WScript.Echo "Creating zip (" & pathToZipFile & ") from folder (" & dirToZip & ")"
Dim fso
Set fso= Wscript.CreateObject("Scripting.FileSystemObject")
If fso.FileExists(pathToZipFile) Then
WScript.Echo "That zip file already exists - deleting it."
fso.DeleteFile pathToZipFile
End If
If Not fso.FolderExists(dirToZip) Then
WScript.Echo "The directory to zip does not exist."
Exit Sub
End If
NewZip pathToZipFile
dim sa
set sa = CreateObject("Shell.Application")
Dim zip
Set zip = sa.NameSpace(pathToZipFile)
WScript.Echo "opening dir (" & dirToZip & ")"
Dim d
Set d = sa.NameSpace(dirToZip)
For Each s In d.items
WScript.Echo s
Next
' http://msdn.microsoft.com/en-us/library/bb787866(VS.85).aspx
' ===============================================================
' 4 = do not display a progress box
' 16 = Respond with "Yes to All" for any dialog box that is displayed.
' 128 = Perform the operation on files only if a wildcard file name (*.*) is specified.
' 256 = Display a progress dialog box but do not show the file names.
' 2048 = Version 4.71. Do not copy the security attributes of the file.
' 4096 = Only operate in the local directory. Don't operate recursively into subdirectories.
WScript.Echo "copying files..."
zip.CopyHere d.items, 4
' wait until finished
sLoop = 0
Do Until d.Items.Count <= zip.Items.Count
Wscript.Sleep(1000)
Loop
End Sub
COM also allws you to use DotNetZip, which is a free download, that does password-encrypted zips, zip64, Self-extracting archives, unicode, spanned zips, and other things.
Personally I use VCL Zip which runs with D2009 and D2010 perfectly fine. it does cost $120 at the time of this post but is very simple, flexible and most of all FAST.
Have a look at VCLZIP and download the trail if your interested
code wise:
VCLZip1.ZipName := ‘myfiles.zip’;
VCLZip1.FilesList.add(‘c:\mydirectory\*.*’);
VCLZip1.Zip;
is all you need for a basic zip, you can of course set compression levels, directory structures, zip streams, unzip streams and much more.
Hope this is of some assistance.
RE
Take a look at this OpenSource SynZip unit. It's even faster for decompression than the default unit shipped with Delphi, and it will generate a smaller exe (crc tables are created at startup).
No external dll is needed. Works from Delphi 6 up to XE. No problem with Unicode version of Delphi. All in a single unit.
I just made some changes to handle Unicode file names inside Zip content, not only Win-Ansi charset but any Unicode chars. Feedback is welcome.

Opening File paths with spaces in Delphi 5

(Using Delphi 5)
I am attempting to open a log file using the following code:
// The result of this is:
// C:\Program Files\MyProgram\whatever\..\Blah\logs\mylog.log
fileName := ExtractFilePath(Application.ExeName) + '..\Blah\logs\mylog.log';
// The file exists check passes
if (FileExists(fileName)) then
begin
logs := TStringList.Create();
// An exception is thrown here: 'unable to open file'
logs.LoadFromFile(fileName);
end;
If I relocate the log file to C:\mylog.log the code works perfectly. I'm thinking that the spaces in the file path are messing things up. Does anyone know if this is normal behavior for Delphi 5? If it is, is there a function to escape the space or transform the path into a windows 8.3 path?
I'm pretty sure that Delphi 5 handles spaces in filenames ok but it has been a very long time since I have used that specific version. Is the file currently open by another process? It also could be a permissions issue. Can you instead of loading it into a tStringList, try opening it with a tFileStream with the filemode set to "fmOpenRead or fmShareDenyNone".
fStm := tFileStream.Create( filename, fmOpenRead or fmShareDenyNone );
then load your tStringlist from the stream:
Logs.LoadFromStream ( fStm );
Are you sure its not the "..\" thats causing the problem rather than the spaces. Have you tried to see if it works at
c:\My\Path\nospaces\
If so and you are always using the ..\ path, maybe write a simple function to remove the last folder from your application path and create a full correct pathname.
It's odd that Delphi 5 would throw errors about this. I know of an issue with FileExists failing on files with an invalid last-modified-date (since it internally uses FileAge), but it's the opposite here. Instead of using "..\" I would consider risking the current path, and loading from a relative path: LoadFromFile('..\Something\Something.log'); especially for smaller applications, or by calling ExtractFilePath twice: ExtractFilePath(ExtractFilePath(Application.ExeName))
I'm pretty sure Delphi has always handled spaces so I doubt that is the issue.
You don't show the full path. Any chance it is really long? For example I could believe an issue with paths longer than 255 characters.
It's also a bad idea to put log files under Program Files. Often normal users are not given permission to write to anything under Program Files.
Delphi 5 can open files with spaces - that is certainly not the problem. To prove it, try copying it to c:\my log.log- it should open fine.
Is there any more information in the error message you receive? The most likely thing is that someone else (perhaps your own program) is still writing to the log.
The spaces are not a problem. While the '..' could be a problem in Delphi 5, mosts probably the file is locked by the process that writes to it. If you have control of it, make sure it opens the file with fmShareDenyWrite and not fmShareExclusive or fmShareCompat (which is the default).
Also, you can use:
fileName := ExpandFileName(ExtractFilePath(Application.ExeName) + '..\Blah\logs\mylog.log');
to obtain the absolute path from a relative path.
Also, as others have said, it is not good idea to write anything in Program Files. Regular users (that are not Administrators or Power Users) do not have rights to write there (although in Vista is will be virtualized, is is still not a good idea). Use the appropriate Application Data folder for the user (or all users). This folder can be obtained using:
SHGetFolderPath(0,folder,0,SHGFP_TYPE_CURRENT,#path[0])
where folder is either CSIDL_COMMON_APPDATA or CSIDL_LOCAL_APPDATA. See this delphi.about.com article for an example.
Simple :
// if log file = "C:\Program files\mylog.log"
// you'll get :
// »»»»» fileName = 'C:\Program files..\Blah\logs\mylog.log'
// if log file = "C:\mylog.log"
// you'll get :
// »»»»» fileName = 'C:..\Blah\logs\mylog.log'
Try this code instead, I'm pretty sure it will fit your needs :
fileName := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName))
+ '..\Blah\logs\mylog.log';
Regards,
Olivier
Delphi 5 has never had a problem opening files with spaces and I am still using it since it is uber stable and works great for older XP apps. You need to check your code closely.

Resources