01. april 2004 - 12:01Der er
17 kommentarer og 2 løsninger
Random valg af linie i Listbox (ved tryk på button)
Jeg har et musikprogram hvor jeg gerne vil have en tilføjelse, og det er at man trykker på en knap og så afspiller den random valgte sange fra Listboxen...
bliver det da den samme (tilfældige) rækkefølge med borrisholt og nifs svar: Dvs: 1. gang = 1, 4, 2, 5, 6 - 2. gang = 1, 4, 2, 5, 6 - 2 ??
Kunne da være dejligt hvis det var en forskellig random rækkefølge for hver gang man trykkede på knappen....
Men hvordan får man den til at gøre det, når rækkefølgen skal ændrer sig for hver gang man trykker på en button..... man kan jo ikke genstarte programmet for hver gang?
procedure TForm1.FormCreate(Sender: TObject); begin Randomize; end;
Jeg kan anbefale dig at slå op i Delphis hjælp. Den er bekvent placeret på F1-tasten og man skriver blot randomize;, stiller cursoren inde i ordet og trykker F1. I den forbindelse vil jeg understrege, at det ikke er ulovligt at søge oplysninger selv - dine to sidste spørgsmål synes jeg ikke røber stor aktivitet fra din side.
procedure Reset; var i : integer; begin for i := 0 to ListBox1.Count - 1 do ListBox1.Items.Objects[i] := nil; end;
function PickOne : string; var bOK : boolean; i : integer; begin result := '';
// Check for any to pick from (they might all be taken) i := -1; bOK := false; while (i < ListBox1.Count - 1) and not bOK do begin inc(i); bOK := not boolean(ListBox1.Items.Objects[i]); end;
i := -1; // A none-to-pick-value if bOK then begin // There are some... bOK := false; // Reset flag while not bOK do begin i := random(ListBox1.Count); bOK := boolean(ListBox1.Items.Objects[i]); end; // Set picked flag ListBox1.Items.Objects[i] := pointer(1); // a.k.a. True end;
if bOK then result := ListBox1.Items[i]; end;
begin MessageDlg(PickOne, mtInformation, [mbOK], 0); end;
Det grimme består i, at da en linie i en TStringList (som TListBox'en bruger til linierne) ikke har en "Tag"-property som man kan bruge (modsat de visuelle komponenter), så bruger jeg en property der er beregnet til at indeholde objekter (pointere) som flag på om den er valgt eller ej.
Det, at jeg sætter den fundne linies objekt til pointer(1), får pointeren til at pege på adresse 1 i hukommelsen - nu bruger jeg ikke objects til andet - og det er derfor det virker!!! Man bruger en property hvis navn indikerer et helt andet brug.
Den pænere måde er at lave et objekt der indeholder linien i ListBoksen og en property der hedder Picked. Objektet placeres i liniens Object-property.
Kig her: Et pænere eksempel, men hvis du ikke har erfaring udi objekter så er det måske spildte kræfter ;-)
type TLine = class // Først lige en lille klasse private fText : string; fPicked : boolean; // Er den tidligere valgt? public constructor Create(const aText : string); property Picked : boolean read fPicked write fPicked; end;
// Indlæser fra en tekstfil, opretter objekterne og fylder dem i listboksen procedure TForm1.ReadText; var fp : text; st : string; Line : TLine; begin if not FileExists('test.dat') then exit;
assignfile(fp,'test.dat'); reset(fp); ListBox1.Items.BeginUpdate; try ListBox1.Clear; while not eof(fp) do begin readln(fp,st); Line := TLine.Create(st); ListBox1.AddItem(Line.Text,Line); end; // while finally ListBox1.Items.EndUpdate; closefile(fp); end; end;
// For at frigive objekterne må man kalde nedenstående procedure TForm1.ClearText; var i : integer; begin for i := 0 to ListBox1.Count - 1 do ListBox1.Items.Objects[i].Free; ListBox1.Clear; end;
function TForm1.PickOne : string; var bOK : boolean; i : integer; begin result := '';
// Check for any to pick from (they might all be taken) i := -1; bOK := false; while (i < ListBox1.Count - 1) and not bOK do begin inc(i); bOK := not TLine(ListBox1.Items.Objects[i]).Picked; end;
i := -1; // A none-to-pick-value if bOK then begin // There are some... bOK := false; // Reset flag while not bOK do begin i := random(ListBox1.Count); bOK := not TLine(ListBox1.Items.Objects[i]).Picked; end;
// Set picked flag TLine(ListBox1.Items.Objects[i]).Picked := true; end;
if bOK then result := ListBox1.Items[i]; end;
Det ændrede ikke meget ved PickOne, men den er pænere. Man kan så diskutere om ikke en løsning der fylder 20 linier er bedre end den der fylder det dobbelte.
Hrc: Tak for den gode beskrivelse..... men det tyder lidt på at løkken:
procedure TfrmMain.Button1Click(Sender: TObject);
procedure Reset; var i : integer; begin for i := 0 to ListBox1.Count - 1 do ListBox1.Items.Objects[i] := nil; end;
function PickOne : string; var bOK : boolean; i : integer; begin result := '';
// Check for any to pick from (they might all be taken) i := -1; bOK := false; while (i < ListBox1.Count - 1) and not bOK do begin inc(i); bOK := not boolean(ListBox1.Items.Objects[i]); end;
i := -1; // A none-to-pick-value if bOK then begin // There are some... bOK := false; // Reset flag while not bOK do begin i := random(ListBox1.Count); bOK := boolean(ListBox1.Items.Objects[i]); end; // Set picked flag ListBox1.Items.Objects[i] := pointer(1); // a.k.a. True end;
if bOK then result := ListBox1.Items[i]; end;
begin MessageDlg(PickOne, mtInformation, [mbOK], 0); end;
er uendelig, for min pc går kold hver gang jeg starter programmet....
Jeg kan umuligt gennemskue hvad du har gang i med her er en algoritme der kan gøre det for dig :
var i: Integer; PrevSelected: Boolean; AllSelected: Boolean; begin AllSelected := True; for i := 0 to ListBox1.Items.Count - 1 do AllSelected := AllSelected AND (ListBox1.Items.Objects[i] <> nil);
if AllSelected then Exit;
PrevSelected := True; while PrevSelected do begin i := Random(ListBox1.Items.Count); PrevSelected := ListBox1.Items.Objects[i] <> nil; end;
if i >= 0 then begin ListBox1.Items.Objects[i] := TObject(i+1);//DU MÅ Ikke STOPPE 0 (NUL) In s på denne plads, så virker det ikkke ListBox2.Items.Add(ListBox1.Items[i]); end; end;
borrisholt: Der manglede kun et lille "not" i mit eksempel - som i øvrigt gør nøjagtigt det samme som dit.
function PickOne : string; var bOK : boolean; i : integer; begin result := '';
// Check for any to pick from (they might all be taken) i := -1; bOK := false; while (i < ListBox1.Count - 1) and not bOK do begin inc(i); bOK := not boolean(ListBox1.Items.Objects[i]); end;
i := -1; // A none-to-pick-value if bOK then begin // There are some... bOK := false; // Reset flag while not bOK do begin i := random(ListBox1.Count); bOK := NOT boolean(ListBox1.Items.Objects[i]); // << Her var fejlen. end; // Set picked flag ListBox1.Items.Objects[i] := pointer(1); // a.k.a. True end;
if bOK then result := ListBox1.Items[i]; end;
og tjaa, jeg ved ikke, men hos mig virker det altså med at
borrisholt: Jeg synes nu ikke der er den store forskel fra det du "umuligt kan gennemskue" og dit eksempel. Opbygningen er den samme. Om vi typecaster eller tjekker om noget er nil er detaljer.
I øvrigt: At nogen skulle sidestille min programmering med noget fra C++ kulturen - tjaa, Jeg typecaster, det indrømmer jeg, men det er nu alligevel første gang det er sket. Troede alligevel at kvaliteten var bedre end det ;-)
Fair nok, lad os lige fjerne et par mis forståelser : 1) jeg havde en skod dag i går, og endnu mere i Mandags ! Hotlinen ! 2) Det var prodoc's jeg ikke lige kunne overskue ! 3) Den der med at tage den boolske værdi på en pointer, DET VAR DET jeg mente var en C++ ting ! 4) LÆS http://www.eksperten.dk/spm/488793
borrisholt (og undskyld til prodoc for at bruge tråden som platform for dette):
ad. 1: Jeg var overhovedet ikke fornærmet eller noget. Skoddage har vi alle og det skinnede lidt igennem i dit indlæg.
ad. 2: Det som prodoc rodede med var jo et eksempel jeg havde lavet.
ad. 3: Indrømmet. Når jeg typecaster kan det blive underligt. Min (usikre) favorit-linie er inc(i,integer(i < 10)); - og det er ret nørdet. Heldigvis returnerer Delphis boolske udtryk altid 0 eller 1 - havde været uheldigt om integer(i < 10) returnerede et tilfældigt tal <> 0. Det er den slags skod-kode hvorom jeg moraliserer når andre bruger det.
ad. 4: Tak for de rosende ord - det betyder en del når det kommer fra dig (kompliment hermed returneret). Jeg har altid været inkarneret delphi-programmør, men har lært en masse i den korte tid jeg har spøgt på eksperten. Med min tilstædeværelse håber jeg at kunne præge andres programmering så så de rutinemæssigt undgår "globale variable", glemte objekter og anden usikker kodning.
ad. 4b: Min offentlige email-adresse er i øvrigt "hrc_public snabela hotmail dot com" - håber notationen forvirrer eventuelle smail-sugende spamrobotter.
Det er helt i orden drenge:) og Borrisholt, undskyld hvis jeg trængte mig på! Borrisholt: jeg har prøvet dig eksempel: var i: Integer; PrevSelected: Boolean; AllSelected: Boolean; begin AllSelected := True; for i := 0 to ListBox1.Items.Count - 1 do AllSelected := AllSelected AND (ListBox1.Items.Objects[i] <> nil);
if AllSelected then Exit;
PrevSelected := True; while PrevSelected do begin i := Random(ListBox1.Items.Count); PrevSelected := ListBox1.Items.Objects[i] <> nil; end;
if i >= 0 then begin ListBox1.Items.Objects[i] := TObject(i+1);//DU MÅ Ikke STOPPE 0 (NUL) In s på denne plads, så virker det ikkke ListBox2.Items.Add(ListBox1.Items[i]); end; end;
-Men er det ikke muligt at den bare markerer linien i Listbox1 (og ikke skriver det til Listbox2, har prøvet men den vil bare ikke)
-og kan man få den til at loope, så når den har taget alle linierne (random) een gang så starter den forfra med en random?
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.