Im trying to create an mp3 player that later I may used as base to create a karaoke the only problem so far is that I want to show the *.mp3 files of a directory that the users select on a ListBox without having to add then manually 1 by 1 and that it only shows the song name not the current path. I have find some ways like using large functions or instead of a ListBox using a ComboBox but it isn't a easier way or transfer the files from the ComboBox to the Listbox?
Try this code it will add file name to ListBox and save its path to TStringList with same Index so you can use get both everywhere in your code
var
Form1: TForm1;
FilePath: TStringList;
implementation
{$R *.dfm}
procedure FindFiles(FilesList: TStrings; FilesPath: TStrings;
StartDir, FileMask: string);
var
SR: TSearchRec;
DirList: TStringList;
IsFound: Boolean;
i: integer;
begin
if StartDir[length(StartDir)] <> '\' then
StartDir := StartDir + '\';
IsFound := FindFirst(StartDir + FileMask, faAnyFile - faDirectory, SR) = 0;
while IsFound do
begin
FilesPath.Add(StartDir + SR.Name);
FilesList.Add(SR.Name);
IsFound := FindNext(SR) = 0;
end;
FindClose(SR);
DirList := TStringList.Create;
IsFound := FindFirst(StartDir + '*.*', faAnyFile, SR) = 0;
while IsFound do
begin
if ((SR.Attr and faDirectory) <> 0) and (SR.Name[1] <> '.') then
DirList.Add(StartDir + SR.Name);
IsFound := FindNext(SR) = 0;
end;
FindClose(SR);
for i := 0 to DirList.Count - 1 do
FindFiles(FilesList, FilePath, DirList[i], FileMask);
DirList.Free;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FilePath.Free;
end;
procedure TForm1.sButton1Click(Sender: TObject);
begin
FilePath := TStringList.Create;
FindFiles(sListBox1.Items, FilePath, EditStartDir.Text, '*.mp3');
{FilePath is where full file path saved, EditStartDir is where to search}
end;
procedure TForm1.sListBox1Click(Sender: TObject);
begin
If sListBox1.ItemIndex > -1 then
sEdit2.Text := FilePath.Strings[sListBox1.ItemIndex];
end;
end.
If you are interested only in listing all the files from specific folder without their icons then you might want to use FileListBox component from Win 3.1 tab.
You can easily define contents from which directory are shown in it by setting the Directory property. Note this property is not exposed to Object inspector to be used at design time but it is perfectly accessible at runtime.
The mentioned component also allows defining filters and even file attributes to show only files that you desire.
The only limitation of mentioned component is that it won't allow you to show files from subfolders which is something a lot of end users would probably like.
For that you would have to search for the files on your own and add them to your desired component one by one.
If you want to only display the filename instead of the whole file path you can use ExtractFileName procedure but keep in mind that you will still have to somehow keep the full path somewhere so that you know which file to open when certain file is selected on your desired component.
There are also some components available which mimic the file browsing behavior similar to Windows Explorer like TShellTreeView or TShellListView that shipped with older versions of Delphi.
And quick search on Google shows that there are even more similar components available.
Related
I would like to save a file as a "read-only" file. Is it possible?
This is a two stage process, that is only available on the Windows platform.
Save the file.
Add the read-only attribute to the file's metadata.
Assuming that you already know how to do stage 1, let's consider stage 2. Using the System.IOUtils unit you set this attribute like so:
uses
System.IOUtils;
....
var
attributes: TFileAttributes;
....
attributes := TFile.GetAttributes(FileName);
Include(attributes, faReadOnly);
TFile.SetAttributes(FileName, attributes);
If you wish to remove the read-only attribute then you use exactly the same code but replace Include with Exclude.
For platforms other than Windows, you can still use TFile.GetAttributes and TFile.SetAttributes, but the available attributes are very different, reflecting the different filesystem models of Windows and the POSIX platforms.
Unfortunately the RTL fails to provide any way to check for errors in this code. So if you want to check for errors (you should) then you are actually best calling the Windows API function SetFileAttributes directly. You might do that like this:
function FileSetAttribute(const FileName: string; const Attr: DWORD; const Value: Boolean): Boolean;
var
Flags, NewFlags: DWORD;
begin
Flags := GetFileAttributes(PChar(FileName));
if Flags=INVALID_FILE_ATTRIBUTES then begin
Result := False;
end else begin
if Value then begin
NewFlags := Flags or Attr
end else begin
NewFlags := Flags and not Attr;
end;
Result := (NewFlags=Flags) or SetFileAttributes(PChar(FileName), NewFlags);
end;
end;
function FileSetReadOnly(const FileName: string; ReadOnly: Boolean): Boolean;
begin
Result := FileSetAttribute(FileName, faReadOnly, ReadOnly);
end;
As discussed previously this code is for Windows only. The FileSetReadOnly function returns a boolean indicating whether or not it succeeded. In case of failure, you can then call GetLastError for extended error information.
Save it as normal, f.ex. via
VAR S : TFileStream;
.
S := TFileStream.Create(FileName,fmCreate);
<Write to stream>
S.Free;
Then
USES SysUtils;
// Set R/O
IF FileSetAttr(FileName,FileGetAttr(FileName) or faReadOnly)<>NO_ERROR THEN
RaiseLastOSError;
afterwards to mark it as "Read/Only".
If you want to update it later on, you need to remove the Read/Only flag first:
// Set R/W
IF FileSetAttr(FileName,FileGetAttr(FileName) AND NOT faReadOnly)<>NO_ERROR THEN
RaiseLastOSError;
then update the file, then mark it as Read/Only again.
I'm using Delphi 10.1 Berlin and I needed to use TAdoStoredProc in my project. I don't use any non-visual component in my forms or data modules. All my connection and db components are creating at run-time.
My Question: When I'm trying to execute TAdoStoredProc, it executes twice and my insert, update processes have worked twice.
var
mSp: TADOStoredProc;
i: Integer;
begin
mSp := TADOStoredProc.Create(nil);
mSp.Connection := conn;
mSp.ProcedureName := spname;
mSp.CommandTimeout := 600;
mSp.parameters.Refresh;
for i := 0 to parameters.Count - 1 do
begin
mSp.parameters.ParamByName(parameters[i].Name).Value := parameters[i].Value;
end;
mSp.ExecProc;
mSp.Open;
Result := mSp;
What should I do or change? Thanks.
As I see that your code required some return values. So you should use TAdoStoredProc like TAdoQuery. You don't need to use ExecProc function for execute, try to use only Open function.
You can just check out TADOStoredProc.ExecProc Method documentation from here.
If an application is only interested in the result set returned by a stored procedure, call Open method for the TADOStoredProc component or set its Active property to true.
I have a procedure to save a file, which is an ini file. The procedure below is just where the user chooses the directory and name of the file:
procedure TForm1.SaveFile(Sender: TObject);
var
Dialog : TSaveDialog;
begin
Dialog := TSaveDialog.Create(self);
try
//dialog properties go here
Dialog.Filter := 'Title (*.ini)|*.ini';
Dialog.Options := Dialog.Options + [ofOverwritePrompt];
if Dialog.Execute then
begin
//any saving procedures go here if required
ShowMessage('File saved: ' + Dialog.FileName);
end
else
ShowMessage('Save file was cancelled');
finally
Dialog.Free;
end;
end;
As you can see I set the filter to ini but no files show at all (it still saves)
The procedure below is where the user can select a previously saved file
procedure TForm1.LoadFileBtnClick(Sender: TObject);
Var
FileName, FileExtension : String;
SelectedFile : TOpenDialog;
begin
SelectedFile := TOpenDialog.Create(nil);
Try
if SelectedFile.Execute() then
FileName := SelectedFile.FileName
else
//Do whatever here if user doesn't select file
Finally
SelectedFile.Free; //Free dialog from memory
End;
FileExtension := ExtractFileExt(FileName);
if not (FileExtension = 'ini') then
exit
else
//Do whatever here if file extension matches specified type
end;
I get the extension of the filename and check if its ini. The ShowMessage is there to see what value is returned for testing purposes. When i click an ini file nothing is returned but when i click a text file '.txt' is returned. Why do my ini files save with the type as 'File' when they are ini files.
How could i change my load file procedure to detect if its an ini file and if not then exit?
Set the property DefaultExt in the SaveDialog to '.ini'
These will solve your problem
I think you are assuming that the fact that you are specifying the SaveDialog's Filter as you do forces the SaveDialog's FileName to have an extension of 'Ini'. It does not. The dialog's Filter property determines what files are listed in it, not the extension that the saved file has.
Try the following code:
if SaveDialog.Execute then
begin
SaveSeatingPlan(SaveDialog.FileName);
Assert(SameText(ExtractFileExt(SaveDialog.FileName), '.Ini'));
ShowMessage('File saved: ' + SaveDialog.FileName);
end else
ShowMessage('Save file was cancelled');
Instead of the Assert, you can of course append the '.Ini' extension in your code or, better, do what the other answer suggests.
i use dynamic array in delphi.
var
var
frame3:array[0..10] of TFrame3
procedure TForm1.Button1Click(sender:TObject);
begin
frame3[count] := TFrame3.create(self);
gridpanel2.insertcontrol(frame3[count]);
but this code is 'A component named Frame3 alredy exists.'
this error what can ido?
If you need multible instances of TFrame3 you need to give it a new name after you created it.
so change
frame3[count] := TFrame3.create(self);
gridpanel2.insertcontrol(frame3[count]);
To
frame3[count] := TFrame3.create(self);
frame3[count].Name := 'Frame3_' + InttoStr(Count);
gridpanel2.insertcontrol(frame3[count]);
The other issue is that it is not seen in your code how you are changing count loop variable. Is it defined in advance?
You need to do something like this:
procedure TForm1.Button1Click(sender:TObject);
var count:byte;
begin
for count:=1 to 10 do
begin
frame3[count] := TFrame3.create(self);
...
end;
or use any other way to set count before each array member (class instance) creation. This code will even probably not require to set Name property at all.
My program allows the user to change the location of the main set of files. Currently I have a text file with a fixed location that contains the folder location of the other files. However, this seems to almost defeat the purpose. Is there a better way of storing this file path?
Delphi contains the TRegistry class, which makes it very simple to save and retrieve your settings information from the Windows Registry.
uses
Registry;
const
RegKey = 'Software\Your Company\Your App\';
procedure TForm1.SaveSettingsClick(Sender: TObject);
var
Reg: TRegistry;
DataDir: string;
begin
// Use the result of SelectDirectory() or whatever means you use
// to get the desired location from the user here. ExtractFilePath()
// is only used as an example - it just gets the location of the
// application itself and then appends a subdirectory name to it.
DataDir:= ExtractFilePath(Application.ExeName) + 'Data\';
Reg := TRegistry.Create;
try
if Reg.OpenKey(RegKey, True) then
Reg.WriteString('DataDir', DataDir);
finally
Reg.Free;
end;
end;
procedure TForm1.GetSettingsClick(Sender: TObject);
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
if Reg.OpenKey(RegKey, False) then
Label1.Caption := Reg.ReadString('DataDir');
finally
Reg.Free;
end;
end;
As #SirRufo mentions in the comments, Delphi also has the TRegistryIniFile, which serves as a wrapper around the registry functions, allowing you to use it without any knowledge of the registry structure. (It's use is similar to that of TIniFile/TMemIniFile, which I'm describing next.)
If you prefer not to use the Registry, you can also use INI (text files) pretty easily. Delphi has support for standard (WinAPI based) INI files in TIniFile, as well as it's own (and IMO better implemented) TMemIniFile. Both are fairly compatible, so I'll demonstrate using just the TMemIniFile here.
(Locations used for reading/writing are for simplicity. In reality, you should use the appropriate subfolder of the %APPDATA% directory, obtained by the appropriate call to SHGetKnownFolderPath with the FOLDERID_RoamingAppData or FOLDERID_LocalAppData constant.)
uses
IniFiles;
// Writing
var
Ini: TMemIniFile;
RootDir: string;
begin
RootDir := ExtractFilePath(Application.ExeName);
Ini := TMemIniFile.Create(TheFile);
try
Ini.WriteString('Settings', 'DataDir', RootDir + 'Data\');
Ini.UpdateFile;
finally
Ini.Free;
end;
end;
// Reading
var
Ini: TMemIniFile;
RootDir: string;
begin
RootDir := ExtractFilePath(Application.ExeName);
Ini := TMemIniFile.Create(TheFile); // The file is your ini file name with path
try
DataDir := Ini.ReadString('Settings', 'DataDir', RootDir);
if DataDir = '' then
DataDir := RootDir; // No user specified location. Use app's dir
finally
Ini.Free;
end;
end;
I very much prefer inifiles. Your users can easily read and edit them if needed, you can keep all information neatly together, and you can easily copy or install configurations to other machines. Inifiles are so simple that you can easily parse them on platforms other than windows, and with any language.
Nowadays many people start using sqlite as some sort of filesystem replacement. They store settings, data files, history, logging, translations, etc all in an sqlite database. That happens on all platforms, even on mobile and embedded devices.
It takes a bit more to set it up, but after that it's pretty flexible.