I did ask this question on the Inno Setup newsgroup but did not get any responses.
Can someone please clarify if it is possible to use Item Data in the [Code] section with TNewComboBox controls, like you can do in other languages?
I tried to find out and could not see anything on the subject.
For the benefit of completeness ... in my current script I have this pre-processor script at the top of my file:
; Language values
#define MDB_ENG "0"
#define MDB_ESP "1"
#define MDB_DEU "2"
#define MDB_ITA "3"
#define MDB_NLD "4"
#define MDB_TRK "5"
#define MDB_PTB "6"
#define MDB_SVE "7"
#define MDB_DAN "8"
#define MDB_RUS "9"
#define MDB_FIN "10"
#define MDB_AFK "11"
#define MDB_CHS "12"
#define MDB_SQI "13"
#define MDB_FRA "14"
#define MDB_BGR "15"
#define MDB_TWI "16"
#define MDB_FPO "17"
#define MDB_SWK "18"
#define MDB_ELL "19"
#define MDB_UKR "20"
#define MDB_KHM "21"
#define MDB_ROM "22"
#define MDB_SMO "23"
#define MDB_IND "24"
#define MDB_VIT "25"
#define MDB_ARA "26"
#define MDB_PLK "27"
#define MDB_SRN "28"
#define MDB_JPN "29"
#define MDB_LIN "30"
#define DatabaseFiles() \
DatabaseFile('English', 'English') + \
DatabaseFile('Spanish', 'Español') + \
DatabaseFile('German', 'Deutsch') + \
DatabaseFile('Italian', 'Italiano') + \
DatabaseFile('Dutch', 'Nederlands') + \
DatabaseFile('Turkish', 'Türkçe') + \
DatabaseFile('Portuguese', 'Português') + \
DatabaseFile('Swedish', 'Svenska') + \
DatabaseFile('Danish', 'Dansk') + \
DatabaseFile('Russian', 'Русский') + \
DatabaseFile('Finnish', 'Suomi') + \
DatabaseFile('Afrikaans', 'Afrikaans') + \
DatabaseFile('Chinese Simplified', '汉语(简化字)') + \
DatabaseFile('Albanian', 'Shqip') + \
DatabaseFile('French', 'Française') + \
DatabaseFile('Bulgarian', 'Български') + \
DatabaseFile('Twi', 'Akan') + \
DatabaseFile('Tagalog', 'Tagalog') + \
DatabaseFile('Swahili', 'Kiswahili') + \
DatabaseFile('Greek', 'Ελληνική') + \
DatabaseFile('Ukrainian', 'Українська') + \
DatabaseFile('Cambodian', 'Cambodian') + \
DatabaseFile('Romanian', 'Română') + \
DatabaseFile('Samoan', 'Faa-Sāmoa') + \
DatabaseFile('Indonesian', 'Indonesia') + \
DatabaseFile('Vietnamese', 'Vietnamese') + \
DatabaseFile('Arabic', 'العربية') + \
DatabaseFile('Polish', 'Polski') + \
DatabaseFile('Sranantongo', 'Sranantongo') + \
DatabaseFile('Japanese', '日本語') + \
DatabaseFile('Lingala', 'Lingala')
Then, down in the [Code] section where I create my custom page, I create the combo like this:
// cbDatabase
cbDatabase := TNewComboBox.Create(Page);
with cbDatabase do
begin
#define DatabaseFile(Name, Text) \
'Items.Add(''' + Text + ''');' + NewLine + \
'if ActiveLanguage = ''' + Name + ''' then ItemIndex := Items.Count - 1;' + NewLine
Parent := Page.Surface;
Left := ScaleX(8);
Top := ScaleY(128);
Width := ScaleX(177);
Height := ScaleY(21);
Style := csDropDownList;
TabOrder := 3;
// Languages
{#DatabaseFiles}
end;
So, I have to upgrade this code to use the Objects approach described in the answer here. And I need to make it sortable. I am sure other code will be affected but best to not bloat this question.
You can use Objects property:
var
Page: TWizardPage;
ComboBox: TNewComboBox;
DataLabel: TLabel;
procedure AddItem(S: string; Data: Integer);
begin
ComboBox.Items.Objects[ComboBox.Items.Add(S)] := Integer(Data);
end;
procedure ComboBoxChange(Sender: TObject);
var
I: Integer;
begin
if ComboBox.ItemIndex >= 0 then
begin
I := ComboBox.Items.Objects[ComboBox.ItemIndex];
DataLabel.Caption := IntToStr(I);
end
else DataLabel.Caption := 'none';
end;
procedure InitializeWizard();
begin
Page := CreateCustomPage(wpSelectDir, 'Combo box test', '');
ComboBox := TNewComboBox.Create(WizardForm);
ComboBox.Style := csDropDownList;
ComboBox.Parent := Page.Surface;
ComboBox.OnChange := #ComboBoxChange;
AddItem('one', 1);
AddItem('two', 2);
AddItem('twelve', 12);
AddItem('hundred', 100);
DataLabel := TLabel.Create(WizardForm);
DataLabel.Parent := Page.Surface;
DataLabel.Top := ComboBox.Top + ComboBox.Height + ScaleY(8);
end;
To glue it together with your code, I assume you want something like this:
#define DatabaseFile(Name, Text, Data) \
'AddItem(''' + Text + ''', Data);' + NewLine + \
'if ActiveLanguage = ''' + Name + ''' then ItemIndex := Items.Count - 1;' + NewLine
#define MDB_AFK 11
#define MDB_TWI 16
...
#define DatabaseFiles() \
DatabaseFile('Afrikaans', 'Afrikaans', MDB_AFK) + \
DatabaseFile('Twi', 'Akan', MDB_TWI) + \
...
Related
How can I properly organize the loading of a specific path of the file system (Windows) in TreeView?
For example:
load files & folders to TreeView (with IOUtils)
(*
tv_zapis: TTreeView
uses
System.IOUtils,
System.Types;
*)
procedure TF1.tb_ReDirectClick(Sender: TObject);
{ ReDirect ( Reorganization Directoris ) dirs & files to TTreeView }
procedure DFToTv(_Tv: TTreeView;
withNode: TTreeNode;
dfPath: string);
var
InsNode: TTreeNode;
Short_Name,
Short_EXT: string; // '*.txt' - delete
sPath: string;
arMask: TStringDynArray;
FilterTN: TDirectory.TFilterPredicate;
SO: TSearchOption;
s: TShiftState;
begin
sPath := Trim(dfPath);
SO := TSearchOption.soAllDirectories; // Search for All Directories in dir
FilterTN := // Filter for Find files
function(const Path: string; const SearchRec: TSearchRec): Boolean
var
nDir, // name Folder ( ! not full path )
sDF: string;
fAtts: TFileAttributes;
begin
nDir := TPath.GetFileName(Path); { Path Name - name of 'folder' }
sDF := IncludeTrailingPathDelimiter(Path) + SearchRec.Name ; // + '\' + SearchRec.Name
// Ignor find dir '_Setting'
if (SearchRec.Name = APL_nameS) or (nDir = APL_nameS)
then exit(False);
{ Browse a file and read its attributes }
fAtts := TPath.GetAttributes(sDF, False);
if (TFileAttribute.faDirectory in fAtts) then
begin
{ if path ' FOLDER ' }
Short_Name := SearchRec.Name;
// ??????????
InsNode := withNode.Parent;
if (InsNode = nil) then
begin
// ShowMessage( ' 1 ? ' );
// ??????????
//if (InsNode.Text = tNode.Text) then
InsNode := _Tv.Items.AddChild(withNode, Short_Name)
//else
// InsNode := _Tv.Items.AddChild(InsNode, Short_Name);
end
else
begin
ShowMessage( ' 2 ? ' );
// ??????????
InsNode := _Tv.Items.AddChild(InsNode, Short_Name);
end;
InsNode.ImageIndex := 1;
InsNode.SelectedIndex := 2;
Exit(True);
end
else
begin
{ if path ' File ' }
Short_EXT := AnsiUpperCase(ExtractFileExt(SearchRec.Name));
if (Short_EXT = '.TXT') then
Short_Name := ChangeFileExt(SearchRec.Name , '')
else
Short_Name := SearchRec.Name;
ShowMessage( ' 3 ? ' );
// ??????????
InsNode := _Tv.Items.AddChild(InsNode.Parent, Short_Name);
if (Short_EXT = '.TXT') then
begin
InsNode.ImageIndex := 3;
InsNode.SelectedIndex := 4;
end
else
begin
InsNode.ImageIndex := 5;
InsNode.SelectedIndex := 5;
end;
Exit(True);
end;
end;
{ FIND FOLDER & File }
arMask := TDirectory.GetFileSystemEntries(sPath, SO, FilterTN);
Application.ProcessMessages;
//Screen.Cursor := crDefault;
// ShowMessage(' All Good :))) ');
(*
ShowMessage( 'sDF - ' + '`' + sDF + '`' + sLineBreak + sLineBreak +
'Path - ' + Path + sLineBreak + sLineBreak +
'SearchRec.Name - ' + '`' + SearchRec.Name + '`' + sLineBreak
);
*)
end;
var
tNode: TTreeNode;
nodeChild: Boolean;
begin
if (DirectoryExists(APL_Files) = False) then Exit;
tv_zapis.Items.Item[0].Selected := True;
Screen.Cursor := crHourGlass;
tv_zapis.Items.BeginUpdate;
try
tv_zapis.Items.Clear;
tNode := tv_zapis.Items.AddChild(nil, 'Dominant_Folder');
begin
DFToTv (tv_zapis, tNode, APL_Files);
end;
finally
tv_zapis.Items.EndUpdate;
Screen.Cursor := crDefault;
end;
end;
Everything functions correctly, but the path does not display correctly on the TreeView (tv_zapis).
I have an mp3.txt file which contains URLs to download. How do I read the contents to then download the URLs? I tried this but it's not working:
Func _downloader($link)
Local $dwnarrayread[3] = [IniRead(#ScriptDir & "\mp3.txt", "file_links", "link_1", Default), IniRead(#ScriptDir & "\mp3-2.txt", "file_links", "link_2", Default), IniRead(#ScriptDir & "\mp3-3.txt", "file_links", "link_3", Default)]
$dwnlink = InetGet($dwnarrayread[$link], #ScriptDir & "PSU-04.mp3", 1, 1)
Do
Sleep(50)
$prc = Round(InetGetInfo($dwnlink, 0) / (InetGetInfo($dwnlink, 1)) * 100)
GUICtrlSetData($progressbar1, $prc)
Until InetGetInfo($dwnlink, $INET_DOWNLOADCOMPLETE)
EndFunc
mp3.txt file :
[files_links]
link_1=https://ftp.psu.ac.th/pub/demo/mp3/PSU-04.mp3
link_2=http://somesite.com/files/file2.zip
link_3=http://somesite.com/files/file3.zip
How do I read the contents to then download the URLs?
Example (as per additional requirement):
_Download('mp3.txt', 'files_links', #DesktopDir & '\')
Func _Download(Const $sFileIni, Const $sSection, Const $sDest)
Local Const $aFile = IniReadSection($sFileIni, $sSection)
For $i1 = 1 To $aFile[0][0]
InetGet($aFile[$i1][1], $sDest & StringTrimLeft($aFile[$i1][1], StringInStr($aFile[$i1][1], '/', 0, -1)))
Next
EndFunc
Example as per GUICtrlSetData() -attempt:
#include <AutoItConstants.au3>
#include <InetConstants.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>
_Download('mp3.txt', 'files_links', #DesktopDir & '\')
Func _Download(Const $sFileIni, Const $sSection, Const $sDest)
Local Const $aFile = IniReadSection($sFileIni, $sSection)
Local $aDownload[$aFile[0][0] + 1]
$aDownload[0] = $aFile[0][0]
For $i1 = 1 To $aFile[0][0]
$aDownload[$i1] = InetGet($aFile[$i1][1], $sDest & StringTrimLeft($aFile[$i1][1], StringInStr($aFile[$i1][1], '/', 0, -1)), $INET_FORCERELOAD, $INET_DOWNLOADBACKGROUND)
Next
Local Const $iWidth = 400, _
$iHeight = 20
Local $hWnd = GUICreate(#ScriptName, $iWidth, $iHeight * $aDownload[0])
Local $aHnd[$aDownload[0] + 1]
For $i1 = 1 To $aDownload[0]
$aHnd[$i1] = GUICtrlCreateProgress(0, ($i1 - 1) * $iHeight, $iWidth, $iHeight)
GUICtrlSetTip($aHnd[$i1], $aFile[$i1][1])
Next
GUISetState(#SW_SHOW, $hWnd)
Local $aInfo
While Not (GUIGetMsg() = $GUI_EVENT_CLOSE)
For $i1 = 1 To $aDownload[0]
$aInfo = InetGetInfo($aDownload[$i1])
GUICtrlSetData($aHnd[$i1], ($aInfo[$INET_DOWNLOADCOMPLETE] Or $aInfo[$INET_DOWNLOADSUCCESS]) ? 100 : $aInfo[$INET_DOWNLOADSIZE] / $aInfo[$INET_DOWNLOADREAD])
Next
If InetGetInfo() Then ContinueLoop
MsgBox($MB_OK, #ScriptName, 'No more running downloads.', 0, $hWnd)
ExitLoop
WEnd
For $i1 = 1 To $aDownload[0]
InetClose($aDownload[$i1])
Next
EndFunc
I would like to create a loop for changing interactions name in PyMol. But after one selection loop it crashes and doesn't work.
def get_dists(interactions): # interactions=([1,2], [3,4])
for i in interactions:
a = "////" + str(i[0]) + "/C2'"
b = "////" + str(i[1]) + "/C2'"
cmd.distance("(" + a + ")", "(" + b + ")")
for j in range(1, 599):
x = "dist" + "0" + str(j)
y = str(i[0]) + " " + str(i[1])
cmd.set_name(str(x), str(y))
In Pymol the default name of interactions is dist01, 02 , 03.
I want to change these to 1_3, 5_59, 4_8, (interaction between residue).
Your code is totally fine except for one thing: If PyMol doesn't succeed with set_name the whole script is aborted. When you change it to, it should work:
try:
cmd.set_name(str(x), str(y))
except:
print('failed to rename')
Some additional comments:
y = str(i[0]) + " " + str(i[1]) should be y = str(i[0]) + "_" + str(i[1])
this line is probably for padding zeros x = "dist" + "0" + str(j). This is only needed when j is a single digit, otherwise the name of the distance objects is dist20 or dist123
cmd.set_name(str(x), str(y)) can be simplified to cmd.set_name(x, y) since x and y are already strings.
This question already has an answer here:
Inno Setup - How to display localized RTF text in custom page
(1 answer)
Closed 5 years ago.
In another question, I asked about importing an RTF file into InnoSetup to use for a custom wizard page:
Import external RTF file for TRichEditViewer?
Unfortunately, I was only able to figure out how to use the answer so that the external file would be loaded if that file existed on the user's system, and in this case, it doesn't exist on the user's system; it's a file I created to display the installer.
I couldn't figure out how to load the external file so that it would be saved inside the compiled installer script and be visible on someone else's system.
Here is the code I put together. I've experimented with creating separate procedures for loading the string, but haven't been able to figure out how to make it work. I'll be grateful for any help:
procedure CreateTheWizardPages;
var
#ifndef UNICODE
rtfstr: string;
#else
rtfstr: AnsiString;
#endif
var
Page: TWizardPage;
RichEditViewer: TRichEditViewer;
vDosFolder: String;
begin
LoadStringFromFile('c:\dropbox\vdosinst\custom.rtf', rtfstr);
if RegQueryStringValue(HKEY_CURRENT_USER, 'Software\WPDOS.org','vDosDir', vDosFolder) then
begin
if ((DirExists(vDosFolder + '\62Config')) OR (DirExists(vDosFolder + '\61Config')) OR (DirExists(vDosFolder + '\51Config'))) then
begin
Page := CreateCustomPage(wpInfoBefore, 'How to install vDosWP-VDM with an existing vDosWP system', 'Read this message for important information!');
RichEditViewer := TRichEditViewer.Create(Page);
RichEditViewer.Width := Page.SurfaceWidth;
RichEditViewer.Height := Page.SurfaceHeight;
RichEditViewer.Parent := Page.Surface;
RichEditViewer.ScrollBars := ssVertical;
RichEditViewer.UseRichEdit := True;
RichEditViewer.RTFText := rtfstr;
RichEditViewer.ReadOnly := True;
end;
end;
end;
procedure InitializeWizard(); // ISSI_ added to string
begin
CreateTheWizardPages;
end;
You can add this compile time macro before your code block:
#pragma parseroption -p-
#define FileHandle
#define FileLine
#define FileName
#define Result
#sub ProcessFileLine
#define FileLine = FileRead(FileHandle)
#if Len(Result) > 0 && !FileEof(FileHandle)
#expr Result = Result + "#10#13 + \n"
#endif
#if FileLine != '\0'
#expr Result = Result + "'" + FileLine + "'"
#endif
#endsub
#sub ProcessFile
#for {FileHandle = FileOpen(FileName); \
FileHandle && !FileEof(FileHandle); ""} \
ProcessFileLine
#if FileHandle
#expr FileClose(FileHandle)
#endif
#endsub
#define ReadFileAsStr(str AFileName) \
Result = '', FileName = AFileName, ProcessFile, Result
This macro outputs file content as string constant. This works for most RTF files, but some characters inside RTF can broke this code.
To fix this you need to escape ', " and may be some other characters inside ProcessFileLine sub.
Then you can use this macro in [Code] block this way:
RichEditViewer.RTFText := {#emit ReadFileAsStr("custom.rtf")};
Delphi used: 2007.
Hello,
I have a simple web page with two text input and one file input. Now, for the form to be sent, both the text inputs and the file input have to be filled. With Synapse, I know how to upload a file (HttpPostFile) and how to post data (HttpMethod). However, I don't know how to do both.
After looking at the source code of Synapse, I guess I have to "format" my data with boundaries or something like that. I guess I should have one boundary for my input file and another boundary for my text inputs. I found an article on the subject, but it's about sending email attachments. I tried to reproduce what they said with Synapse, with no results.
Code for HttpPostFile:
function HttpPostFile(const URL, FieldName, FileName: string;
const Data: TStream; const ResultData: TStrings): Boolean;
var
HTTP: THTTPSend;
Bound, s: string;
begin
Bound := IntToHex(Random(MaxInt), 8) + '_Synapse_boundary';
HTTP := THTTPSend.Create;
try
s := '--' + Bound + CRLF;
s := s + 'content-disposition: form-data; name="' + FieldName + '";';
s := s + ' filename="' + FileName +'"' + CRLF;
s := s + 'Content-Type: Application/octet-string' + CRLF + CRLF;
WriteStrToStream(HTTP.Document, s);
HTTP.Document.CopyFrom(Data, 0);
s := CRLF + '--' + Bound + '--' + CRLF;
WriteStrToStream(HTTP.Document, s);
HTTP.MimeType := 'multipart/form-data; boundary=' + Bound;
Result := HTTP.HTTPMethod('POST', URL);
if Result then
ResultData.LoadFromStream(HTTP.Document);
finally
HTTP.Free;
end;
end;
Thank you.
Your code is close. You are only sending your file field but not your text fields. To do all three, try this instead:
function HttpPostFile(const URL, InputText1FieldName, InputText1, InputText2FieldName, InputText2, InputFileFieldName, InputFileName: string; InputFileData: TStream; ResultData: TStrings): Boolean;
var
HTTP: THTTPSend;
Bound: string;
begin
Bound := IntToHex(Random(MaxInt), 8) + '_Synapse_boundary';
HTTP := THTTPSend.Create;
try
WriteStrToStream(HTTP.Document,
'--' + Bound + CRLF +
'Content-Disposition: form-data; name=' + AnsiQuotedStr(InputText1FieldName, '"') + CRLF +
'Content-Type: text/plain' + CRLF +
CRLF);
WriteStrToStream(HTTP.Document, InputText1);
WriteStrToStream(HTTP.Document,
CRLF +
'--' + Bound + CRLF +
'Content-Disposition: form-data; name=' + AnsiQuotedStr(InputText2FieldName, '"') + CRLF +
'Content-Type: text/plain' + CRLF +
CRLF);
WriteStrToStream(HTTP.Document, InputText2);
WriteStrToStream(HTTP.Document,
CRLF +
'--' + Bound + CRLF +
'Content-Disposition: form-data; name=' + AnsiQuotedStr(InputFileFieldName, '"') + ';' + CRLF +
#9'filename=' + AnsiQuotedStr(InputFileName, '"') + CRLF +
'Content-Type: application/octet-string' + CRLF +
CRLF);
HTTP.Document.CopyFrom(InputFileData, 0);
WriteStrToStream(HTTP.Document,
CRLF +
'--' + Bound + '--' + CRLF);
HTTP.MimeType := 'multipart/form-data; boundary=' + Bound;
Result := HTTP.HTTPMethod('POST', URL);
if Result then
ResultData.LoadFromStream(HTTP.Document);
finally
HTTP.Free;
end;
end;
If you switch to Indy, you can use its TIdMultipartFormDataStream class:
function HttpPostFile(const URL, InputText1FieldName, InputText1, InputText2FieldName, InputText2, InputFileFieldName, InputFileName: string; InputFileData: TStream; ResultData: TStrings): Boolean;
var
HTTP: TIdHTTP;
Input: TIdMultipartFormDataStream;
Output: TMemoryStream;
begin
Result := False;
try
Output := TMemoryStream.Create;
try
HTTP := TIdHTTP.Create;
try
Input := TIdMultipartFormDataStream.Create;
try
Input.AddFormField(InputText1FieldName, InputText1);
Input.AddFormField(InputText2FieldName, InputText2);
Input.AddFormField(InputFileFieldName, 'application/octet-stream', '', InputFileData, InputFileName);
HTTP.Post(URL, Input, Output);
finally
Input.Free;
end;
finally
HTTP.Free;
end;
Output.Position := 0;
ResultData.LoadFromStream(Output);
Result := True;
finally
Output.Free;
end;
except
end;
end;
I also use synapse in my projects. To be make my work simple and faster with Synapse, I wrote THTTPSendEx class, that gives fast speed of using and minimum of code and more features.
Currently it's a beta version.
It's views like Indy.
Create THTTPSendEx class.
Create methods OnBeginWork, OnWork, OnWorkEnd from it prototypes(see pas file), and assign it to created class. Thats all what you need, and just call GET, POST functions of the class.
I also implement multipart-fomdata for fast file posting in this format as TMultipartFormDataStream class.
With it you can easy write files and fields.
Example of using:
var
HTTP:THTTPSendEx;
Data:TMultipartFormDataStream;
sHTML:string; //Recived HTML code from web
begin
HTTP:=THTTPSEndEx.Create;
Data:=TMultipartFormDataStream.Create;
try
Data.AddFile('myFile','Path to the local file(No UNC paths)');
Data.DataEnd;
if HTTP.Post('URL HERE',Data,sHTML) then
begin
//Connection established
//Check HTTP response
if HTTP.IsSuccessfull then //HTTP returns "200 OK" code.
begin
ShowMessage('File successfully posted to the server.');
end;
end else
begin
ShowMessage('Can not establish a connection to the server...'+#13+'Network is not avaliable or server socket does not exist.');
end;
finally
FreeAndNil(HTTP);
FreeAndNil(Data);
end;
end;
You can see it at my web-site.
If you have any ideas for this, please write it's as a comment to project page.
Sorry for mistakes in english.