ListCtrl Position eines Eintrages bestimmen

Plattformunabhängige GUIs mit wxWidgets.
Antworten
Benutzeravatar
stasikz
User
Beiträge: 18
Registriert: Donnerstag 5. Juli 2007, 21:34

Freitag 14. Dezember 2007, 19:17

Hallo Leute!
Ich versuche einen Downloadmanager zu programieren, und bin auf folgendes Problem gestoßen:
Bild
Ein Download wird bei mir durch Selektiren eines Antrages in der Liste und anschließendem Klicken Auf Button begonnen. Dabei wird an eine Download-Klasse die Position des Eintrages mit pos = self.urls.GetFocusedItem() übergeben. Soweit sogut. Dieses Download wird auch gestartet(im Bild datei "4.rar") und läuft ohne Probleme, dabei wird in regelmäßigen Abständen der Fortschritt des Downloads in die Downloaded-Spalte übergeben, dabei benutze ich folgende Anweisung: self.urls.SetStringItem(pos, 2, fortschritt), dabei ist fortschritt die übertragene Größe.
wenn ich jetzt während des Downloads ein Eintrag aus der Liste lösche(im Beispiel 3.rar) Dann ändert sich die Position des laufenden Eintrages(wird nach oben verschoben) self.urls.SetStringItem(pos, 2, fortschritt) schreibt aber weiterhin in die alte Position, auf der jetzt eine andere Datei ist(1.rar)
und jetzt die eingentliche Frage: mit welcher Anweisung kann ich die aktuelle Position meines laufenden eintrages Bestimmen?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Freitag 14. Dezember 2007, 20:35

stasikz hat geschrieben:mit welcher Anweisung kann ich die aktuelle Position meines laufenden eintrages Bestimmen?
Hallo stasikz!

So würde ich an die Sache heran gehen:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import wx
import sys
import itertools

wx.SetDefaultPyEncoding("iso-8859-15")


class MyFrame(wx.Frame):
    
    def __init__(
        self, parent = None, title = "Example", size = wx.Size(550, 420)
    ):
        wx.Frame.__init__(self, parent, -1, title, size = size)
        
        panel = wx.Panel(self)
        
        vbox_main = wx.BoxSizer(wx.VERTICAL)
        panel.SetSizer(vbox_main)
        
        listctrl = wx.ListCtrl(panel, style = wx.LC_REPORT)
        vbox_main.Add(listctrl, 1, wx.EXPAND | wx.ALL, 5)
        listctrl.InsertColumn(sys.maxint, "Spalte A")
        listctrl.InsertColumn(sys.maxint, "Spalte B")
        
        # Automatisch nummerierter Tupel :-)
        counter = itertools.count()
        data = (
            (counter.next(), "A", "AA"),
            (counter.next(), "B", "BB"),
            (counter.next(), "C", "CC"),
        )
        
        for id, item1_text, item2_text in data:
            item = listctrl.InsertStringItem(sys.maxint, item1_text)
            print item
            listctrl.SetStringItem(item, 1, item2_text)
            listctrl.SetItemData(item, id)
        
        # Reihenfolge verändern (einfach einen neuen Eintrag einfügen)
        item = listctrl.InsertStringItem(0, "Hallo")
        listctrl.SetStringItem(item, 1, "Welt")
        listctrl.SetItemData(item, counter.next())
        
        # Eintrag mit der ID 1 ("B") suchen
        for item in xrange(listctrl.GetItemCount()):
            if listctrl.GetItemData(item) == 1:
                listctrl.SetStringItem(item, 1, "Gefunden")
                print item
                break


def main():
    """Testing"""
    app = wx.PySimpleApp()
    f = MyFrame()
    f.Center()
    f.Show()
    app.MainLoop()


if __name__ == "__main__":
    main()
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
stasikz
User
Beiträge: 18
Registriert: Donnerstag 5. Juli 2007, 21:34

Samstag 15. Dezember 2007, 14:49

Danke Gerold für deine schnelle Antwort, allerdings habe ich immer noch ein Problem:
meine OnDelete Funktion:

Code: Alles auswählen

def OnDelete(self, event):
        item = self.urls.GetFocusedItem()
        print item
        if item != -1:
            pos = self.urls.GetItemData(item)
            print pos
            self.urls.DeleteItem(pos)
            pos = self.urls.GetFocusedItem()
            print pos
            self.urls.Select(pos)
wenn ich z.B zuerst dritten Einrtag in der Liste lösche, dann wird folgendes ausgegeben:2 (Position in der Liste)
2(id von GetItemData, zugewiesen mit counter.next())
2 (self.urls.GetFocusedItem())
wenn ich dann wieder dritten eintrag lösche, dann kommt das raus
2,3,2, was auch richtig ist, da sich die Liste nach oben verschoben hat, und auf Position 2(dritter Eintrag) der eintrag mit ID = 3 ist.
wenn ich dann aber versuche denn letzten Eintag zu löschen z.b mit ID = 7 von counter.next() zugewiesen, dann bekomme ich folgende Fehlermeldung: couldn't retrieve information about list control item 7
oder es wird folgendes ausgegeben: 5,7,5 der eintrag wird aber nicht gelöscht.

Edit: ich habe gesehen, dass Gerold in seinem Code oft sys.maxint

Code: Alles auswählen

listctrl.InsertColumn(sys.maxint, "Spalte A")
verwendet, muss man dort nicht immer eine eindeutige Zahl eingeben, die sich dann immer ändert und als ID dient?[/code]
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Samstag 15. Dezember 2007, 20:29

stasikz hat geschrieben:couldn't retrieve information about list control item 7
[...]
muss man dort nicht immer eine eindeutige Zahl eingeben, die sich dann immer ändert und als ID dient?[/code]
Hallo stasikz!

1.) Du musst vor jedem Zugriff die Position ermitteln, so wie ich es in Zeile 47 mache.

2.) Wenn das mit sys.maxint nicht funktionieren würde, dann hätte ich es dir nich so vorgezeigt. Beim Hinzufügen einer Zeile bekommst du eine Zahl zurück und diese Zahl zeigt die **momentane** Position des Eintrages an. Diese Position kann sich ändern. Deshalb habe ich eine eindeutige ID mit SetItemData dazu geschrieben. Bei jedem Zugriff wird zuerst jedes Element der Liste durchlaufen und nach dieser ID befragt. Wurde das Element mit der gewünschten ID gefunden, weiß man auch an welcher Position es steht und mit dieser Information kann man auf das Element zugreifen.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Samstag 15. Dezember 2007, 20:43

...vielleicht verstehe ich aber auch dein Problem nicht ganz. Wie auch immer. Ich habe mal das Beispiel so verändert, dass man per Mausklick einen Eintrag löschen kann.

Code: http://paste.pocoo.org/show/15834/

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
stasikz
User
Beiträge: 18
Registriert: Donnerstag 5. Juli 2007, 21:34

Sonntag 16. Dezember 2007, 00:04

1.) Du musst vor jedem Zugriff die Position ermitteln, so wie ich es in Zeile 47 mache.
Vielen Dank Gerold, das war genau das Problem. Ich dachte, dass man durch

Code: Alles auswählen

pos = self.urls.GetItemData(item)
direkt auf die Wetre zugreifen, ohne die Position zu kennen.
Außerdem hat mir deine Lösung mit dem Popup Menu sehr gefallen. :D

Was ich noch fragen wollte, kann es sein, dass wx.CallAfter() ziemlich langsam ist und etwa 0.1 ms braucht, um eine ziemlich einfache Funktion auszuführen? hängt die Geschwindigkeit vom Python oder allgemein vom Rechner ab oder ist der Wert 0.1 ms schon sehr gut? :D
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Sonntag 16. Dezember 2007, 00:32

stasikz hat geschrieben:kann es sein, dass wx.CallAfter() ziemlich langsam ist und etwa 0.1 ms braucht, um eine ziemlich einfache Funktion auszuführen
Hallo stasikz!

``wx.CallAfter()`` legt einen Event in die Eventwarteschlange. Wx arbeitet erst die noch anstehenden Events ab bevor ein so unwichtiges Event wie das von wx.CallAfter initiierte Event an die Reihe kommt.

Mit wx.CallAfter() kann aber am Einfachsten sicher gestellt werden, dass sich Threads nicht in die Quere kommen. Wenn du eine schnellere Reaktion brauchst, dann musst du dich selber (umständlich) um die Threadtrennung kümmern. So etwas möchte man, wenn möglich, lieber vermeiden.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten