Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: Fabio - Novembre 11, 2011, 05:10:50 pm

Titolo: TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 11, 2011, 05:10:50 pm
Ho una TTreeView che popolo senza problemi, ma quando vado per eliminare un elemnto mi va in errore dicendo

Citazione
<progetto> raised exception class 'external: sigsegv'.

E mi si ferma alla riga 392 di treeview.inc
Codice: [Seleziona]
Result:=NodeState in FStates;

Il codice di eliminazione è il seguente che l'ho trovato sul wiki:

Codice: [Seleziona]
procedure TfrmMain.btnDelHostClick(Sender: TObject);
  Procedure DeleteNode(Node:TTreeNode);
  begin
    while Node.HasChildren do DeleteNode(Node.GetLastChild);
    lstHosts.Items.Delete(Node);
  end;
begin
  if lstHosts.Selected = nil then exit;
  If lstHosts.Selected.HasChildren then
    if MessageDlg('Delete node and all children ?',mtConfirmation,
                 [mbYes,mbNo],0) <> mrYes then exit;
  DeleteNode(lstHosts.Selected);
end;

Debuggando va in crash alla riga
Codice: [Seleziona]
lstHosts.Items.Delete(Node);

Idee?
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: xinyiman - Novembre 11, 2011, 05:20:11 pm
Non ho lazarus sotto mano per provare, ma sei sicuro che stai cancellando un nodo? Magari si tratta di un figlio del nodo e mi sembra che vada identificato come DeleteSubNode

Ma ripeto, vado a memoria. Non prendere per oro colato. Fammi sapere se risolvi
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 11, 2011, 05:23:03 pm
Ti dico ho ripreso lo stesso esempio del wiki e poi ho provato sia su un nodo root senza figli che su un nodo figlio che su un nodo root con figli, in tutti e tre i casi sempre stesso errore.
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Legolas - Novembre 11, 2011, 06:10:49 pm
A me l'esempio funziona senza problemi. Non è che hai qualche oggetto appeso al nodo?

Edit: 'spetta, ho detto una boiata, gli oggetti appesi chiaramente non c'entrano niente  ;D

Quell'errore viene fuori quando provi a cancellare un nodo che non esiste. Non è che per qualche motivo la procedura di cancellazione viene chiamata due volte?
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 14, 2011, 09:21:36 am
Rispondo solo ora che il progetto l'ho in ufficio e nel weekend non potevo provare.

Sul tuo appunto ho provato a fare un debug passo passo e l'errore esce prima ancora che cominci a eliminare il nodo, sembra quasi non riesca a leggere il lstHosts.Selected

Confermo è quello, ho forzato un
Codice: [Seleziona]
DeleteNode(lstHosts.Items[1]);
E li l'errore non esce

Ehm ... aggiornamento ... io mi sto perdendo, ha funzionato una volta e poi ora da errore anche così.
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: xinyiman - Novembre 14, 2011, 09:48:36 am
Scusa ma che versione di Lazarus usi?!
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 14, 2011, 09:58:51 am
0.9.30
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: xinyiman - Novembre 14, 2011, 10:02:42 am
Hai provato ad installare una snapshot 0.9.31?! Magari così risolvi la problematica.
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 14, 2011, 10:06:32 am
Aggiungo una nota, se prima della cancellazione provo a leggere il nodo quello va senza problemi

Codice: [Seleziona]
ShowMessage(Node.Text);
lstHosts.Items.Delete(Node);

Lo shomessage mi mostra correttamente il testo del nodo ma poi al delete va in errore.

No, non ho provato lo snapshot, se a voi funziona con la stessa versione è qualcosa che sbaglio, devo cercare di capire cos'è.
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: xinyiman - Novembre 14, 2011, 10:13:49 am
Bho, a me funziona ma uso la 0.9.31
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 14, 2011, 10:48:30 am
La cosa si infittisce, ho creato un progetto nuovo, ho messo una treeview e un pulsante.

Ho popolato a runtime i nodi e inserito le stesse funzioni del wiki e dell'altro progetto al pulsante.

Qui li cancella senza problemi.
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: xinyiman - Novembre 14, 2011, 10:49:40 am
Veramente molto strano!
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 14, 2011, 03:28:43 pm
Ho trovato il barbatrucco, il pulsante che uso per eliminare la voce è disabilitato di default, nell'evento selectionchanged della treeview ho aggiunto il codice per abilitarmi il pulsante solo se in precedenza ho selezionato una voce
Remmando questo comando e lasciando il pulsante abilitato di default tutto funziona correttamente senza errori.

Se avete voglia di ripetere la situazione per vedere se anche a voi esce l'errore basta che fate un nuovo progetto, aggiungete un controllo treeview e un pulsante che sia disabilitato di default, nell'evento create del form popolate la treeview con dei dati a casa

Codice: [Seleziona]
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  lstHosts.Items.Add(nil, 'uno');
  lstHosts.Items.Add(nil, 'due');
  lstHosts.Items.Add(nil, 'tre');
  lstHosts.Items.Add(nil, 'quattro');
  lstHosts.Items.Add(nil, 'cinque');
end;

Ora nell'evento selectionchanged della treeview mettete l'abilitazione del pulsante

Codice: [Seleziona]
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
  btnDeleteItem.Enabled:=lstHosts.Selected.Selected;
end;

E infine la funzione al pulsante di eliminazione

Codice: [Seleziona]
procedure TfrmMain.btnDeleteItemClick(Sender: TObject);
  procedure DeleteNode(Node:TTreeNode);
  begin
    while Node.HasChildren do DeleteNode(Node.GetLastChild);
    lstHosts.Items.Delete(Node);
  end;
begin
  if lstHosts.Selected = nil then exit;
  If lstHosts.Selected.HasChildren then
    if MessageDlg('Delete node and all children ?', mtConfirmation
       , [mbYes,mbNo], 0) <> mrYes then exit;
  DeleteNode(lstHosts.Selected);
end;

Eseuite e provate a eliminare una voce, se vi crasha anche a voi remmate l'istruzione nel selectionchanged e abilitate di default il pulsante e riprovate ora non da + errori e cancella giusto.

Qualcuno sa spiegarmi come mai ?
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 14, 2011, 03:53:29 pm
Intando ho trovato come farlo funzionare ugualmente anche con l'abilitazione del pulsante anche se ancora non capisco il perchè nell'altra maniera non vada e faccia crashare tutto:

Codice: [Seleziona]
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
  btnDeleteItem.Enabled:=not(lstHosts.Selected = nil);
end;
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Legolas - Novembre 14, 2011, 04:32:23 pm
Perché con lstHosts.Selected.selected fai riferimento ad un oggetto (lstHosts.Selected) che hai appena cancellato :)

Probabilmente potresti anche fare qualcosa del tipo:

Codice: [Seleziona]
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
  btnDeleteItem.Enabled := lstHosts.items.SelectionCount > 0;
end;
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 14, 2011, 04:37:54 pm
Perché con lstHosts.Selected.selected fai riferimento ad un oggetto (lstHosts.Selected) che hai appena cancellato :)

Ma quello non scatta prima della procedura di cancellazione? Che legami hanno?
L'evento scatta solo quando clicco su un nodo della treeview PRIMA di premere il pulsante per l'eliminazione, abilita solamente il pulsante.
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Legolas - Novembre 14, 2011, 04:53:13 pm
Il fatto è che al momento della cancellazione del nodo l'evento viene generato di nuovo; a questo punto avviene l'errore. Prova a modificare la funzione così:

Codice: [Seleziona]
procedure TForm1.lstHostsSelectionChanged(Sender: TObject);
begin
  ShowMessage('sending 1');
  btnDeleteItem.Enabled:=lstHosts.Selected.Selected;
  ShowMessage('sending 2');
end;   

Tenendo conto di questo, ad esempio, così funziona:

Codice: [Seleziona]
procedure TForm1.lstHostsSelectionChanged(Sender: TObject);
begin
  if lstHosts.Selected <> nil then
    btnDeleteItem.Enabled:=lstHosts.Selected.Selected
  else
    btnDeleteItem.Enabled:=false;
end;   
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 14, 2011, 04:57:57 pm
Come ho scritto sopra ho già trovato come farla funzionare con il codice

Codice: [Seleziona]
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
  btnDeleteItem.Enabled:=not(lstHosts.Selected = nil);
end;

Volevo solo cercare di capire come mai quella che avevo usato la prima volta faceva collassare tutto ANCORA prima di cancellare il nodo.

Comunque non importa, grazie lo stesso.
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Legolas - Novembre 14, 2011, 08:50:59 pm
Come ho scritto sopra ho già trovato come farla funzionare con il codice

Codice: [Seleziona]
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
  btnDeleteItem.Enabled:=not(lstHosts.Selected = nil);
end;

Sì, ma assegni la negazione di un'identità quando basterebbe assegnare la non-identità. Questa:
Codice: [Seleziona]
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
  btnDeleteItem.Enabled:= lstHosts.Selected <> nil;
end;
è meno ridondante. :)

Citazione
Volevo solo cercare di capire come mai quella che avevo usato la prima volta faceva collassare tutto ANCORA prima di cancellare il nodo.

Comunque non importa, grazie lo stesso.

Prova questo con il codice che dava errore, tanto per vedere che in effetti il nodo va a nil:

Codice: [Seleziona]
procedure TForm1.lstHostsSelectionChanged(Sender: TObject);
begin
  Form1.Caption := IntToStr(integer(pointer(lstHosts.Selected)));
  btnDeleteItem.Enabled := lstHosts.Selected.Selected;
end;   

Nella caption del form c'è l'indirizzo di memoria del nodo selezionato. Nel momento in cui viene cancellato, il valore va a 0 immediatamente prima di andare in SIGSEGV, indice del free del nodo.
Titolo: Re:TTreeView ed eliminazione TTreeNode
Inserito da: Fabio - Novembre 15, 2011, 07:48:27 am
Ottimo grazie, ora mi è più chiaro.
Ho provato l'esempio che hai detto e fa come dici.

Grazie anche della precisazione dell'assegnazione.

Ciao.