Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Python: Kansiot kansion sisään

Sivun loppuun

ZcMander [12.01.2007 22:04:14]

#

Olen tekemässä RSS-virran lukuohjelmaa ja syötteet pitäisi saada lajiteltua hakemistoihin, ja niih vieltä mahdollisuus tehdä kansioita, periaatteessa kansioita voi olla vaikka 10 sisäkkäin, jos vain joku tarvii.

Ihmettelen, miten tämänlainen olisi järkevintä toteuttaa. Nykyisiltään on vain yksi lista, jossa on kaikki virrat alkioissaan.

virrat = [virta1, virta2, virta3]

No, tuhonhan pitäisi vielä saada mukaan kansiot, joten ajattelin tehdä uuden listan, jossa on kansiot

kansiot = [kansio1, kansio2, kansio3]

Sitten törmäsin ongelmaan, entäpäs jos käyttäjä haluaa siirrellä kansioita esim. kansio1:n virta1 ja virta2 väliin? Eihän tämmöinen voisi toimia, koska virta-listassa ei ole missäänkohdassa määritelty, että tähän tulee kansio1.

kansio2 = [
     virta3
]

kansio1 = [
     kansio2,
     virta1,
     virta2,
]

virrat = [
  kansio1
]

Mutta muuttujiahan ei voi tehdä valmiiksi, koska niitä täytyy voida tehdä ihan suorituksen aikanakin, ei lähdekoodia muokkaamalla, joten mitenhän tämänlainen ominaisuus olisi kaikkein viisainta tehdä?

EDIT: Ainiin, ja kielenähän on python

Blaze [12.01.2007 22:44:23]

#

ZcMander kirjoitti:

Sitten törmäsin ongelmaan, entäpäs jos käyttäjä haluaa siirrellä kansioita esim. kansio1:n virta1 ja virta2 väliin? Eihän tämmöinen voisi toimia, koska virta-listassa ei ole missäänkohdassa määritelty, että tähän tulee kansio1.

Jooh, eihän se noin pelaa. Sun pitää laittaa samaan listaan sekä virrat, että kansiot. Kantsii varmaan tehä joku yhteinen yliluokka/interface niille.

ZcMander [12.01.2007 23:30:15]

#

Meinaatkos jotenkin näin:

class kansio:
  nimi = ""
  virrat = [kansio4, virta, kansio5]

kansio1, kansio2, kansio3 = kansio()

virrat = [kansio1, kansio2, kansio3]

Pekka Karjalainen [13.01.2007 19:32:16]

#

Väsäsin pikaisesti esimerkin. Huomaa, että Kansio-luokan __str__-metodi on rekursiivinen. Jokaisessa kansiossa on lista alkioita, jotka voivat olla joko kansioita tai virtoja. Kun ne käydään läpi, valitaan uusi __str__-metodi alkion tyypin mukaan, joten virrat vain tulostetaan ja kansiot puolestaan tulostetaan sisallon kanssa rekursiivisesti.

Kansioita rakentaessa ja kasvatettaessa lisaa-metodilla tarkistan, että tuliko argumenttina lista vai jotain muuta. Oletan, että jotain muuta on sellainen, jonka voi suoraan pistää sinne näytille listan sisällä (ne []-merkit rakentavat sinne listan). Oikeassa ohjelmassa laajempi tarkistus on ehkä paikallaan.

Pythonin rekursioraja on jossain tuhannen paikkeilla oletuksellisesti, jos nyt äkkiä muistan oikein. Lienee aika turvallinen oletus, että käyttäjä ei niin paljon laita kansioita sisäkkäin. Voi sen estääkin, jos jokaiseen kansioon liittää vaikkapa tiedon, kuinka syvällä se on kansiohierarkiassa ja hylkää jonkin rajan ylittävät.

Tulostuksessa on hyvin yksinkertainen sisennystapa. Voit myös kokeilla ohjelmaani Python-tulkissa ja katsella siellä olioiden sisältöjä. Menettelyni varmasti valkenee parhaiten juuri siten. Yksi esimerkkiaineisto on koodissa mukana. Tein aika kiireessä tämän...

Huomaapa Pythonille sopiva tagi Putkassa :-)

# virrat.py - Putkaan esimerkki

class Virta(object):
    def __init__(self, nimi):
        self.nimi = nimi
    def __str__(self):
        return "virta: " + self.nimi

class Kansio(object):
    def __init__(self, nimi, sisalto):
        self.nimi = nimi
        if type(sisalto) == list:
            self.sisalto = sisalto
        else:
            self.sisalto = [sisalto]
    def lisaa(self, muut):
        if type(muut) == list:
            self.sisalto += muut
        else:
            self.sisalto += [muut]
    def __str__(self):
        esitys = "kansio: " + self.nimi + "\n"
        for alkio in self.sisalto:
            sisaesitys = str(alkio)
            for rivi in sisaesitys.split('\n'):
              if rivi: esitys += "** " + rivi + "\n"
        return esitys

if __name__=='__main__':
    paa  = Kansio ("paakansio", Virta ("virta1"))
    kan1 = Kansio ("alikansio 1", [Virta("virta2"), Virta("virta3")])
    kan2 = Kansio ("alikansio 2", Virta ("virta4"))
    paa.lisaa (kan2)
    paa.lisaa (kan1)
    kan2.lisaa (Kansio ("tyhja", []))
    kan1.lisaa (Kansio ("alikansio 3", [Virta ("extra 1"),
                Virta("extra 2"), Virta ("extra 3")]))
    print paa

En ole varma ymmärsinkö vaatimuksesi oikein. Jos meni mistiin, kerro lisää.

ZcMander [13.01.2007 23:07:24]

#

Jooh, sain jotain saman kaltaista aikaan:

#!/usr/bin/env python
# -*- coding: utf8 -*-
# Author:   Teijo Mursu
# Purpose: Puu
# Created: 13.01.2007

########################################################################
class Content:

  #----------------------------------------------------------------------
  def __init__(self, iterator=None, parent=None):
    """Constructor"""
    self.iterator = None
    self.parent = parent




########################################################################
class Feed(Content):
  """Tiedot virrasta """

  #----------------------------------------------------------------------
  def __init__(self, title, parent=None):
    """Constructor"""
    Content.__init__(self, parent=parent)

    self.feed = {"title" : title}

  #----------------------------------------------------------------------
  def __str__(self):
    """Palauttaa syötteen nimen """
    return self.feed["title"]

########################################################################
class Folder(Content):
  """Sisältää virrat ja muut kansiot """

  #----------------------------------------------------------------------
  def __init__(self, name, parent=None):
    """Constructor"""
    Content.__init__(self, parent=parent)

    self.name = name
    self.content = []

  #----------------------------------------------------------------------
  def __str__(self):
    """Palauttaa kansion nimen """
    return self.name


  #----------------------------------------------------------------------
  def add(self, content):
    """Lisää kansion/syotteen sisaltoon """
    self.content.append(content)

  #----------------------------------------------------------------------
  def remove(self, item):
    """Poistaa sisällöstä kansion/syotteen """
    if type(item) == Folder:
      item.remove_all()

    self.content.remove(item)

  #----------------------------------------------------------------------
  def remove_all(self):
    """Poistaa kaiken sisällön """
    self.content = []


#----------------------------------------------------------------------
def PrintTree(folder, depth=0):
  """Tulostaa puun """
  for content in folder.content:
    #print type(content) = <type 'instance'>
    if type(content) == Folder:
      PrintTree(content, depth+1)
    else:
      print ("*" * depth) + str(content)


if __name__ == '__main__':
  root = Folder("ROOT")

  folder1 = Folder("Folder1", root)
  folder2 = Folder("Folder2", folder1)

  root.add(folder1)
  folder1.add(folder2)

  root.add(Feed("Nidekon"))

  folder1.add( Feed("Folder1-feed1") )
  folder1.add( Feed("Folder1-feed2") )

  folder2.add( Feed("Folder2-feed1") )
  folder2.add( Feed("Folder2-feed2") )
  folder2.add( Feed("Folder2-feed3") )

  PrintTree(root)

On siinä vielä sellainen ongelma, että type(content) ei palauta muuta kuin <type 'instance'>

ZcMander [14.01.2007 10:31:45]

#

Ongelma ratkesi, tarvitsi muokata vain Content-luokkaa:

########################################################################
class Content(object):

  #----------------------------------------------------------------------
  def __init__(self, iterator=None, parent=None):
    """Constructor"""
    self.iterator = None
    self.parent = parent

Pekka Karjalainen [14.01.2007 11:22:52]

#

Tuon rekursion voi tietenkin poistaa tutulla tekniikalla, eli käyttää pinoa. Pythonin listasta saa hyvin pinon appendilla ja popilla. Luultavasti se ei tässä ole tarpeellinen muutos, koska ohjelman varsinainen tarkoitus ei ole juosta koko ajan tuota kansiopuuta ylös ja alas.

ZcMander [19.01.2007 23:22:10]

#

Edelleen vallan erikoista ongelmaa. Eli, mulla on käytössä äsköiset luokat. Lisään kansion kansioon näin:

  #----------------------------------------------------------------------
  def create_folder(self, name, iter=None):
    """Lisää kansion puuhun """
    #Tarkistaa, oliko tyhjä, joka annettiin
    iter = self.is_iter_none(self.feeds)

    #Tarkistetaan, että yritetään lisätä kansioon, muuten ei onnistu
    if type(iter) == feed.Folder:
      iter.add( feed.Folder(name) )
      return True
    else:
      return False

#----------------------------------------------------------------------
  def is_iter_none(self, iter):
    """Tarkistaa onko iter-muuttuja None, jos on niin palautetaan puun alku """
    if iter == None:
      return self.feeds #Juuritason kansio
    else:
      return iter

Ja gtk:n TreeViewin valittu kohta haetaan ja kutsutaan tuota funktiota:

def on_editfeed_create_folder_activate(self, widget, *args):
  print "Create folder"

  #Palauttaa valitun kohdat
  (model, iter) = self.controller.ui.get_selected_feed()

  #Jos ei ole valittu, jos esim TreeStore on tyhjä
  if iter == None:
    cont = self.controller.feeds.feeds #Juuritason kansio
  else:
    #Muutetaan iteraattori mukavempaan muotoon, poluksi
    iter = model.get_path(iter)
    #Haetaan kansio/syöte iteraattorin perusteella, toimiva funktio
    cont = self.controller.feeds.feeds.find_iter(iter)

  self.controller.feeds.create_folder("Example", cont)

Ja kun TreeView päivitetään, silloin vasta kansiot ja sen sisältö saa iteraattorin, jotta voidaan saada tietoon missä kohden kyseinen kansion/syöte on gtkTreeViewissä.

#----------------------------------------------------------------------
  def refresh_feeds_list(self):
    """Päivittää syötteet-listan """
    print "Refresh feeds list"
    curfolder = None

    #Tyhjentää TreeStoren, etttei vain lisätä
    self.window.feeds.get_model().clear()

    for i in self.controller.feeds.feeds.content:
      #Lisää yhden valinnan lisää, ensin tulee nimi ja sitten minkä kansion sisässä sisältö on.
      i.iterator = self.window.add_choice_feeds([str(i)], i.parent)

      #Iteraattori mukavempaan muotoon
      i.iterator = self.window.feeds.get_model().get_path(i.iterator)

      #Tarkistetaan onko lisättävän typpi kansio vai syöte
      if i == feed.Folder:
        #Vaihdetaan nykyinen kansio siihen
        curfolder = i.iterator
      else:
        #Muuten syötteen yllä on kansio
        i.parent = curfolder

Kuitenkin, ennen "self.controller.feeds.create_folder("Example", cont)" kohtaa on_editfeed_create_folder_activate-funktiossa on kaikki oikein, mutta kun tuo funktio suoritetaan ja päivitettään TreeViewi niin kansio onkin luotu juuritasolle, vaikka pitäisi olla luotu kansio kansion sisälle.

EDIT: Juuri tarkistin asian, kansio luodaan oikeastikkin juuritasolle, joten vika ei luultavasti ole mitenkään liittyvä TreeViewiin.

EDIT2: Feed-luokassa suorittamani testi osoitti, että kun lisäykset tehdään oikein, kansiot menevät sisäkkäin, kuten pitääkin. Ongelma ei ole Feed-luokassa.

EDIT3: Ehkä hieman selventävä kuva: http://zcmander.lagaakympil.org/kuvat/debug_jodiko.png
cont-muuttuja on saanut oikean arvon, mutta silti kansio tulee <feed.Folder 0x9b0966c>:n content-listaan vaikka pitäisi tulla <feed.Folder 0x9b6d6ec>:n content-listaan.

ZcMander [20.01.2007 13:39:22]

#

Hoh, jos poistan create_folder-funktiosta rivin: iter = self.is_iter_none(self.feeds) niin kansio luodaan oikeaan paikkaan, mutta niitä kansioita ei lisätä koskaan Treeviewin TreeStoreen.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta