macias

Python 2.5

Z opóźnieniem godnym człowieka leniwego donoszę o nowej (2.5) wersji interpretera cPython.

Deweloperzy zaszczycili nas niezłą porcją nowej funkcjonalności:

  • konstrukt with - zamknięcie bloku instrukcji w ustalonym kontekście. Zwinna i skuteczna próba wyeliminowania potrzeby stosowania bloku try: finally: np. do operacji na pliku z zapewnieniem jego natychmiastowego zamknięcia.
  • nowe możliwości w komunikacji z generatorami: push, throw i close. push "wkłada" do generatora dowolną wartość, która w generatorze jest zwracana przez wyrażenie yield. throw sprawia, że yield rzuca wskazany wyjątek. close kończy działanie generatora.
  • nowe wyrażenie: wartośc_jeśli_true if warunek else wartość_jeśli_false (odpowiednik operatora .?.:. z C). Warto zwrócić szczególną uwagę na inną kolejność "operandów" w stosunku do analogonu z C.
  • relatywne/absolutne importy
  • połączenie try: catch: i try: finally: w jedną strukturę try: catch: finally:
  • Nowa hierarchia klas wyjątków już jako new-style classes
  • Do tego masa łat i poprawionych błędów, poprawy szybkości dzięki NeedForSpeed sprint w Islandii, cProfile - owoc GSoC2006, zmiany wewnętrzne w kompilatorze, dodane/usunięte moduły z biblioteki standardowej, ...

Generalnie warto ściągnąć, poczytać, zainteresować się (o ile to jeszcze nie zachodzi), bo Python staje się coraz bardziej dojrzały i jest zdecydowanie godny polecenia jako język ogólnego przeznaczenia.

Skomentuj (4) 2006-09-20 09:28:21 Edytuj

grzywacz

'Profesjonalizm'

Tak się akurat złożyło, że kilka dni temu przeglądałem pewną stronę, która nie wzbudziła we mnie zbyt dużej sympatii. Miałem ochotę wyżyć się na niej, więc z ciekawości zagłębiłem się w kod w poszukiwaniu oznak spartolonej webmasterskiej roboty. Na pierwszy ogień poszedł formularz kontaktowy i... bingo. To naprawdę smutne, ale ktoś użył do przekazania docelowego adresu email ukrytego pola formularza. Po co - to przekracza jak zwykle moje pojęcie, ale nie jest to ani pierwszy, ani ostatni raz, gdy widzę taki popis.

HTMLowy formularz jest nim tylko po stronie przeglądarki. To ona wyświetla (lub nie) zdefiniowane przez twórcę strony pola, umożliwia podanie wartości (dowolnych lub spełniających pewne kryterium) oraz zajmuje się przesłaniem go do serwera. Problem w tym, że myślenie o aplikacji sieciowej jako o czymś, co działa tylko z przeglądarką jest błędem tragicznym. To, co jest przesyłane do serwera formularzem już nie jest - to odpowiednio sformatowany ciąg bajtów i nic nie przeszkadza, by umieścić w nim dowolnie wybrane wartości. I tak wszystkie ograniczenia wymuszane po stronie przeglądarki można spokojnie zignorować, przekazując aplikacji dane, które nie spełniają poczynionych przez niedouczonego programistę założeń. Co z tego, że pole formularza jest "ukryte"?

Oczywiście autor strony otrzymał ode mnie email z powiadomieniem o tym, jakiego bubla popełnił i na czym polega jego błąd. Dla hecy wysłałem mu go ze strony pewnej firmy spedycyjnej jego autorstwa (i z adresu tejże firmy). Śmiesznie. A mimo wszystko nadal robi mi się niedobrze, gdy patrzę na obietnice profesjonalizmu i najwyższego standardu, które widnieją na jego firmowej witrynie...

Skomentuj (0) 2006-09-18 20:10:27 Edytuj

macias

turbogears.widgets.Form

Poznając Turbogears niesposób ominąć pakiet turbogears.widgets. Otóż jest to już dość duży (i ciągle rosnący) zbiór łatwych do wielokrotnego wykorzystania "kontrolek".

Tutaj pod pojęciem kontrolka nie kryje się zbyt dużo. Jest nią dowolny element strony, który możemy nazwać "samodzielnym". Toteż mamy kontrolki tabelek, obrazków z linkiem, itd. Wiele z nich jest AJAXowata, co dodaje im uroku i funkcjonalności (np. AutoCompleteField - pole tekstowe pobierające z serwera podpowiedzi w trakcie wpisywania).

Ważną częścią całej kolekcji są formularze i komponenty formularzy.

Działamy:

class DodajWizyteFields( widgets.WidgetsList ):
    pacjenci_combo = widgets.SingleSelectField(  "pacjent_id",  label="Pacjent",    validator=TableIdValidator( model.Pacjent ) )
    typy_combo     = widgets.SingleSelectField(  "typ_id",      label="Typ wizyty", validator=TableIdValidator( model.TypWizyty ) )
    kwota_text     = widgets.TextField(          "kwota",       label="Kwota",      validator=validators.Int() )
    data_text      = widgets.CalendarDatePicker( "data_wizyty", label="Data wizyty", button_text=u"Wybierz date" )

class DodajWizyteForm( widgets.ListForm ):
    fields = DodajWizyteFields()
    submit_text = "Dodaj wizyte"

SingleSelectField to nic innego niż kombo box, CalendarDatePicker to bardzo funkcjonalny Javascriptowy kalendarz, a ListForm to odmiana formularza bazujący na szkielecie <ul>..</ul>. Reszta powinna być jasna lub okazać się jasna po dalszej lekturze.

Czas na kontroler:

class Wizyty( object ):
    add_visit_form = dodaj_wizyte.DodajWizyteForm()

    @expose( template="pacjenci.templates.wizyty.dodaj_formularz" )
    def dodaj_formularz( self ):
        options = { "pacjent_id": [ ( pac.id, pac.ImieNazwiskoDataUr() ) for pac in model.Pacjent.select() ],
                        "typ_id": [ ( typ.id, typ.nazwa ) for typ in model.TypWizyty.select() ] }
        return dict( add_visit_form=self.add_visit_form,
                     options=options,
                     action="dodaj" )

Tworzymy jedną instancję formularza jako pole klasowe add_visit_form. Kontroler dodaj_formularz pobiera opcje dostępne do wyboru w polach formularza (lista pacjentów i lista typów) w formie ( id, nazwa ) i zwraca słownik zmiennych dostepnych później w szablonie:

    <box>
        <title>Dodaj wizytę:</title>
        <body>
            ${add_visit_form.display( options=options, action=action )}
        </body>
    </box>

Metoda .display() formularza generuje kod HTML odpowiadający wszystkim zdefiniowanym wcześniej polom formularza. Jedna instancja formularza jest wielokrotnego użycia, bo przy każdym żądaniu dostępne opcje pobierane są na nowo. Przyjrzyjmy się kontrolerowi dodaj:

    @expose()
    @error_handler( dodaj_formularz )
    @validate( form=add_visit_form )
    def dodaj( self, pacjent_id, typ_id, kwota, data_wizyty ):
        model.hub.hub.begin()
        try:
            p = model.Pacjent.get( pacjent_id )
            t = model.TypWizyty.get( typ_id )
            model.Wizyta( pacjent=p, typ=t, kwota=kwota, data_wizyty=data_wizyty )
        except:
            model.hub.hub.rollback()
            raise
        model.hub.hub.commit()
        redirect( turbogears.url( "lista" ) )

Dekorator @validate( form=add_visit_form ) odpowiada za odpowiednie potraktowanie parametrów żądania. Otóż zostają one zwalidowane przy pomocy walidatorów dla pól formularza (co więcej zostają one skonwertowane do odpowiednich wartości pythonowych). Dla przykładu: walidator validators.Int() użyty dla pola kwota_text sprawia, że tekst wysłany w żądaniu HTTP jako wartość parametru kwota zostaje skonwertowany do obiektu pythonowego typu int. W razie niepowodzenia (np. podano tekst "dziesięć") wchodzi w grę dekorator @error_handler( dodaj_formularz ). Sprawia on, że sterowanie przejmuje kontroler dodaj_formularz. Co więcej formularz add_visit_form użyty do skontrolowania parametrów niesie ze sobą informacje o zaistniałych błędach co jest później uwzględnione przy wyświetleniu tegoż formularza na nowo (przy odpowiednim polu pojawia się komunikat o błędzie).

Pozostałe walidatory zapewniają, że jeśli znaleźliśmy się w kontrolerze dodaj, to znaczy, że wszystkie parametry wejściowe są w całkowitym porządku (i odpowiedniego typu). Zatem ograniczamy się do dodania odpowiedniego rekordu do bazy danych i oddajemy sterowanie kontrolerowi lista.

Pozostaje jeszcze wyjaśnić jak utworzyć nowy walidator (pozostaje do wyjaśnienia dużo więcej, ale...). W naszy przypadku to TableIdValidator:

class TableIdValidator( validators.FancyValidator ):
    messages = { 'not_found': "There is no record with this ID",
                   'not_int': "ID format incorrect. It should be an integer." }

    def __init__( self, sqlobject_table ):
        self.used_table = sqlobject_table

    def validate_python( self, value, state ):
        try:
            self.used_table.get( value )
        except SQLObjectNotFound:
            raise validators.Invalid( self.message( 'not_found', state ), value, state )

    def _to_python( self, value, state ):
        try:
            id = int( value )
        except ValueError:
            raise validators.Invalid( self.message( 'not_int', state ), value, state )
        return id

Dziedziczymy po validators.FancyValidator i dostarczamy metody .validate_python() i ._to_python() (w naszym przypadku akurat te i tylko te). ._to_python() odpowiada za konwersję z napisu do odpowiedniego typu pythonowego (u nas int), a .validate_python() sprawdza, czy uzyskany obiekt pythonowy jest zgodny z naszymi oczekiwaniami (czy jest poprawnym kluczem w odpowiedniej tabeli). Komunikat z jakim rzucamy Invalid ..... pojawia się na formularzu w przypadku wystąpienia tego z przypuszczalnych błędów - nic dodać nic ująć.

Na życzenie zamieszczam skrinszoty: Przykład formularza Formularz z wyświetlonym błędem

Skomentuj (2) 2006-09-17 22:58:01 Edytuj

grzywacz

Joymouse

Integracja (i małe rozszerzenie) testowego kodu emulującego mysz przy pomocy joysticka/klawiatury z kodem Battle for Wesnoth poszła szybko i sprawnie. Dzięki temu grę na GP2X daje się już obsługiwać! Prawie. Ktoś niesprytnie użył osobnej pętli zdarzeń do obsługi (zamykania) dialogów w trakcie gry i niestety nie da się przebrnąć przez pierwszy z nich, ale to już tylko kwestia małej poprawki. Jak na początek - jest dobrze!

Oprócz tego dodałem możliwość wymuszenia na grze, by uznała cache plików konfiguracyjnych za poprawny, bez względu na wynik liczenia sum kontrolnych lub znaczników czasowych. Co prawda powoduje to, że zmiana danych nie zostanie odzwierciedlona w grze (możliwe, że nawet wprowadzi jakieś błędy), ale czterokrotna redukcja czasu uruchamiania była tego warta. Zresztą i tak prawie nikt samodzielnie konfiguracji nie modyfikuje.

Nowy kod dostępny jest rzecz jasna w gałęzi rozwojowej.

Skomentuj (0) 2006-09-17 12:58:43 Edytuj

grzywacz

Pierwszy sukces

Wczoraj wieczorem usiadłem na chwilkę do Wesnoth i po krótkim boju z kompilacją biblioteki SDL_net na moją ARMową maszynkę oraz małych poprawkach w konfiguracji autoconfa udało mi się skompilować grę na GP2X:

grzywacz@stealth bin% file wesnoth
wesnoth: ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.0.0, statically linked, stripped

Statycznie zlinkowany program ma 7.2MB i... uruchamia się! Zdjęcie zamieszczę przy okazji. Do rozwiązania mam teraz kilka problemów:

  • Emulacja myszki przy pomocy joysticka (kod koncepcyjny już jest, muszę go zintegrować z grą)
  • Kompilacja biblioteki SDL_mixer tak, by nie segfaultowała w funkcji Mix_OpenAudio()
  • Opcja wyłączenia automatycznej weryfikacji i budowy cache'u plików konfiguracyjnych gry
Zmierzę się z nimi w najbliższej wolnej chwili.

Skomentuj (9) 2006-09-15 13:40:12 Edytuj

grzywacz

W sercu GP2X (1)

Słowo wstępu

GP2X to niszowa konsolka do zastosowań wszelkich (przez producenta określana jako "personal entertainment player"), działająca pod kontrolą Linuksa. Mimo tego, że została wyprodukowana przez GamePark Holdings, nieznaną u nas firmę z Korei Południowej, zamknięty w czarnej obudowie sprzęt jest dość ciekawy, a jego wykorzystanie może być interesującym wyzwaniem dla każdego zapalonego programisty. W kolejnych wpisach postaram się rzucić nieco więcej światła na poszczególne jej elementy.

MMSP2

Głównym układem konsoli jest MP2520F, będący częścią platformy MMSP2. Jest to układ typu SoC, czyli, mówiąc bardzo potocznie, kupa składającej się na komputer elektroniki zintegrowanej w ramach jednej kości. W jego skład wchodzą dwa 32-bitowe procesory ARM 920T i 940T (artykuł w Wikipedii wart przeczytania), kontroler pamięci, kontroler przerwań, procesor video, postprocesor video, procesor graficzny (2D), procesor obrazu, układ dźwiękowy zgodny z AC97, kontroler obrazu oraz zestaw różnorodnych komponentów wejścia/wyjścia (USB, kontroler pamięci Flash oraz SD/MMC, UART, I2C). Oprócz tego realizuje funkcje DMA, zegarów czasu rzeczywistego oraz zarządania energią. Co ciekawe, konsola nie posiada zasilanej pamięci CMOS, przez co nie jest w stanie pamiętać ustawionego czasu (zegary resetowane są przy każdym uruchomieniu).

Komunikacja

Poszczególne komponenty komunikują się między sobą oraz z urządzeniami zewnętrznymi przy pomocy 4 niezależnych szyn danych, przy czym dostęp do pamięci możliwy jest tylko przy pomocy jednej z nich (dzielonej przez oba procesory oraz kontroler DMA). Oprócz tego możliwa jest bezpośrednia, potokowa komunikacja między postprocesorem video a kontrolerem obrazu, dzięki czemu zdekodowany obraz w formacie YUV może być od razu wyświetlany i opcjonalnie poddany dodatkowemu przetwarzaniu, umożliwiającemu takie operacje jak deblocking, deringing, skalowanie, konwersje przestrzeni kolorów oraz dithering (realizowane sprzętowo). Podobne połączenie funkcjonuje między procesorem obrazu (mogącym przetwarzać obraz z kamer - tu niewykorzystywane) a postprocesorem video. Nie trzeba dodawać, że takie rozwiązanie znacznie odciąża pamięć.

Procesor 2D

Producent firmuje tę część MMSP2 jako zdolną do akcelerowania operacji GDI oraz Direct2D w Windows CE. Użytkowników GP2X, która pracuje pod kontrolą Linuksa to rzecz jasna nie interesuje. Oczywiście nie wyklucza to użycia go do zwiększenia wydajności operacji graficznych. Część możliwości dostępna jest w łatwy sposób dzięki implementacji sprzętowo akcelerowanych funkcji w bibliotece SDL (blitting, double buffernig, ...). Inne wymagają od programisty bezpośredniej interakcji ze sprzętem.

Procesor video

Układ ten zdolny jest do sprzętowego dekodowania filmów zakodowanych zgodnie z MPEG4, DivX 3.11/4/5 lub MJPEG. Oprócz tego zapewnia część z dostępnych funkcji obróbki obrazu (m.in. deblocking i deringing). Maksymalne parametry dekodowanego obrazu to rozdzielczość 720x480 przy 30 klatkach na sekundę (przetestowałem, działa jak złoto :)).

Postprocesor video

Ten element MMSP2 umożliwia dalszą obróbkę obrazu video, w tym skalowanie, obroty, kontrolę nasycania, kontrastu, jasności, gammy, dithering, alpha blending, color keying oraz konwersje przestrzeni kolorów. Pomniejszanie obrazu przydaje się, gdy chcemy wyświetlić film w niskiej (320x240) rozdzielczości ubsługiwanej przez wbudowany ekran LCD, powiększanie zaś, gdy chcemy skorzystać z wyjścia S-Video i wyświetlić obraz z konsoli na telewizorze.

Kontroler obrazu

Maksymalna obsługiwana rozdzielczość wyświetlanego obrazu to 1024x768, choć w GP2X wykorzystać można tylko 320x240. Obsługiwane są wszystkie typowe głębie kolorów RGB (8-24 bity) oraz kilka bardziej egzotycznych.

Wejście/wyjście

MMSP2 daje spory wybór interfejsów we./wyj., z czego nie wszystkie daje się w pełni wykorzystać w GP2X. Kontroler hosta USB umożliwia podłączenie zewnętrznych urządzeń takich jak np. mata do DDRa ;-), ale niezasilany port w konsolce wymaga do tego użycia dodatkowego huba. GP2X może działać też jako zwykłe urządzenie USB (sieć, dostęp do karty SD w trybie usb storage). Kontroler UART umożliwia skorzystanie z 4 portów szeregowych, ale potrzebna jest do tego specjalna płytka rozszerzająca dla developerów, podłączana do portu Ext(ension). Opcjonalnie można pobawić się lutownicą, jak w opisie dodawania odbiornika GPS

Podsumowanie

W powyższym zestawieniu pominąłem dokładniejsze opisy modułów DMA, I2C, zarządzania energią, zegarów oraz watchdoga, jako że nie są one specjalnie interesujące z punktu widzenia użytkownika konsoli. O układzie dźwiękowym mogę niestety powiedzieć tylko tyle, że działa. Szczegółowe informacje na temat budowy układu MMSP2 można znaleźć na stronie producenta, w opisie chipsetu oraz pełnym manualu. W następnej części przyjrzę się nieco dokładniej obu procesorom konsolki i postaram się przedstawić ich zalety, wady oraz sposoby radzenia sobie z tymi ostatnimi.

Skomentuj (0) 2006-09-10 22:06:24 Edytuj

macias

Na czasie być [nie tak dobrze].

Pomiędzy letnimi obowiązkami domowymi (przetwory z owoców, patroszenie grzybów, ...) i pracą znalazłem chwilę czasu na kontynuację zabawy z frejmłorkami łebowymi (konkretnie Pythonowymi a najkonkretniej TurboGears). Jakiś czas temu natknąłem się na framework o magicznej nazwie Nevow. Moje dotychczasowe przeżycia z Turbogears są wystarczająco pozytywne, aby nie przesiadać się całkowicie - najbardziej zastanawiający okazał się podzbiór Nevow: system szablonów Stan. Krótkie poszukiwania i bingo - TurboStan, który jest prostym adapterem Stan'a do TurboGears.

Tyle po wprowadzeniu. Teraz sedno.

Próba instalacji:

# easy_install TurboStan
Coś nie tak z Twisted z której Nevow (jako zależność TurboStan'a) korzysta jako serwera sieciowego. Szukam i updejtuję i po dobrych 20 minutach rozwiązanie: zamiast skoncentrować się na twisted i instalować wersje niestabilne itd. wystarczyło zainstalować moduł twisted-web od którego de facto Nevow był zależny ("wina" Gentoo :) - rozbili twisted na podmoduły).

Idę dalej. TurboStan zainstalowany. W międzyczasie (tak, wiem: nie ma takiego słowa) próbuję uruchomić mój stary projekt robiąc uprzednio to co zwykle w takim momencie:

$ cd ~/turbogears-dev
$ svn up
$ su
Password:
# easy_install .
$ cd -
Stąd tytuł - akurat jeśli chodzi o aktualnego trunka TurboGears, to do najstabilniejszych on nie należy. O czym niebawem się przekonałem i zakończyłem na tarczy (oczywiście na razie).
Uruchomienie projektu:
$ ./start-pacjenci.py
Traceback (most recent call last):
  File "./start-pacjenci.py", line 5, in ?
    import turbogears
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/__init__.py", line 6, in ?
    from turbogears.controllers import expose, flash, validate, redirect, 
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/controllers.py", line 14, in ?
    from turbogears import view, database, errorhandling
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/view/__init__.py", line 1, in ?
    from turbogears.view.base import *
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/view/base.py", line 340, in ?
    _load_engines()
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/view/base.py", line 338, in _load_engines
    engines[entrypoint.name] = engine(stdvars, engine_options)
  File "/usr/lib/python2.4/site-packages/TurboStan-0.8.6-py2.4.egg/turbostan/stansupport.py", line 113, in __init__
    ns [ 'tg' ] = self.get_extra_vars ( ) [ 'tg' ]
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/view/base.py", line 307, in stdvars
    tg_js="/" + turbogears.startup.webpath + "tg_js",
AttributeError: 'module' object has no attribute 'startup'
$ ls -l //usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/
razem 338
...
-rw-r--r-- 1 root root 10604 wrz  7 22:01 startup.py
...
No i bądź mądry, skąd taki błąd. Zaraz napiszę, ale wcześniej spróbuj dojść do tego sam(a).
Otóż prosto: do próby użycia 'podmodułu' startup doszło w trakcie inicjowania modułu turbogears, ale przed jego załadowaniem startup'a w wyniku tej inicjacji. Rodzaj 'cyklicznej' zależności.

Zatem patch i dalej:

$ diff -u stansupport_old.py stansupport.py
--- stansupport_old.py  2006-09-07 23:44:53.000000000 +0200
+++ stansupport.py      2006-09-07 23:28:19.000000000 +0200
@@ -105,6 +105,9 @@
             self.basedir = os.path.join ( os.getcwd ( ), self.basedir )
         log.info ( "templates root = %s" % self.basedir )

+        self._default_ns = None
+
+    def _prepare_default_ns( self ):
         ns = { }
         ns.update ( __import__ ( 'nevow.tags', ns, ns, [ '__all__' ] ).__dict__ )
         ns.update ( __import__ ( 'nevow.entities', ns, ns, [ '__all__' ] ).__dict__ )
@@ -180,6 +183,8 @@
             pretty = False

         if format == 'html':
+            if self._default_ns == None:
+                self._prepare_default_ns()
             ns.update ( self._default_ns )
         else: # import user-defined Stan tags and flatteners
             ns.update ( __import__ ( '%s.tags' % format, ns, ns, [ '__all__' ] ).__dict__ )
$ ./start-pacjenci.py
2006-09-07 23:46:14,597 cherrypy.msg INFO CONFIG: Server parameters:
2006-09-07 23:46:14,599 cherrypy.msg INFO CONFIG:   server.environment: development
2006-09-07 23:46:14,600 cherrypy.msg INFO CONFIG:   server.log_to_screen: True
2006-09-07 23:46:14,601 cherrypy.msg INFO CONFIG:   server.log_file:
2006-09-07 23:46:14,602 cherrypy.msg INFO CONFIG:   server.log_tracebacks: True
2006-09-07 23:46:14,603 cherrypy.msg INFO CONFIG:   server.log_request_headers: True
2006-09-07 23:46:14,604 cherrypy.msg INFO CONFIG:   server.protocol_version: HTTP/1.0
2006-09-07 23:46:14,605 cherrypy.msg INFO CONFIG:   server.socket_host:
2006-09-07 23:46:14,606 cherrypy.msg INFO CONFIG:   server.socket_port: 8080
2006-09-07 23:46:14,607 cherrypy.msg INFO CONFIG:   server.socket_file:
2006-09-07 23:46:14,608 cherrypy.msg INFO CONFIG:   server.reverse_dns: False
2006-09-07 23:46:14,609 cherrypy.msg INFO CONFIG:   server.socket_queue_size: 5
2006-09-07 23:46:14,610 cherrypy.msg INFO CONFIG:   server.thread_pool: 10
2006-09-07 23:46:14,643 turbogears.visit INFO Visit Tracking starting
2006-09-07 23:46:14,651 turbogears.visit.sovisit INFO Succesfully loaded "pacjenci.model.VisitIdentity"
2006-09-07 23:46:14,652 turbogears.visit INFO Visit filter initialised
 1/QueryOne:  SELECT tbl_name FROM sqlite_master WHERE type='table' AND tbl_name = 'tg_visit'
 1/QueryR  :  SELECT tbl_name FROM sqlite_master WHERE type='table' AND tbl_name = 'tg_visit'
Unhandled exception in thread started by <bound method Server._start of <cherrypy._cpserver.Server object at 0xb7b7450c>>
Traceback (most recent call last):
  File "/usr/lib/python2.4/site-packages/CherryPy-2.2.1-py2.4.egg/cherrypy/_cpserver.py", line 78, in _start
    Engine._start(self)
  File "/usr/lib/python2.4/site-packages/CherryPy-2.2.1-py2.4.egg/cherrypy/_cpengine.py", line 108, in _start
    func()
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/startup.py", line 219, in startTurboGears
    ext.start_extension()
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/visit/api.py", line 68, in start_extension
    create_extension_model()
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/visit/api.py", line 87, in create_extension_model
    _manager.create_model()
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/visit/sovisit.py", line 34, in create_model
    TG_Visit.createTable(ifNotExists=True)
  File "/usr/lib/python2.4/site-packages/SQLObject-0.7.1dev_r1860-py2.4.egg/sqlobject/main.py", line 1320, in createTable
    if ifNotExists and conn.tableExists(cls.sqlmeta.table):
  File "/usr/lib/python2.4/site-packages/SQLObject-0.7.1dev_r1860-py2.4.egg/sqlobject/sqlite/sqliteconnection.py", line 201, in tableExists
    result = self.queryOne("SELECT tbl_name FROM sqlite_master WHERE type='table' AND tbl_name = '%s'" % tableName)
  File "/usr/lib/python2.4/site-packages/SQLObject-0.7.1dev_r1860-py2.4.egg/sqlobject/dbconnection.py", line 760, in queryOne
    return self._dbConnection._queryOne(self._connection, s)
  File "/usr/lib/python2.4/site-packages/SQLObject-0.7.1dev_r1860-py2.4.egg/sqlobject/dbconnection.py", line 342, in _queryOne
    self._executeRetry(conn, c, s)
  File "/usr/lib/python2.4/site-packages/SQLObject-0.7.1dev_r1860-py2.4.egg/sqlobject/dbconnection.py", line 298, in _executeRetry
    return cursor.execute(query)
pysqlite2.dbapi2.Warning: You can only execute one statement at a time.
To wynik rozpoczętych niedawno prac nad rozwątkowaniem głównych części rdzenia TG. Z drugiej strony ORM z którego korzystam (SQLObject) z wątkami sobie nie radzi i jest problem.
Po chwili paniki, myśl: Na pewno jak założę nowy projekt, to się wygeneruje model ze świeższych szablonów i będzie lepiej (choć trochę lepiej).
$ tg-admin quickstart
Enter project name: hihi
Enter package name [hihi]:
Do you need Identity (usernames/passwords) in this project? [no]
Selected and implied templates:
  TurboGears#tgbase      tg base template
  TurboGears#turbogears  web framework

Variables:
  egg:         hihi
  identity:    none
  package:     hihi
  project:     hihi
  sqlalchemy:  False
Creating template tgbase
Creating directory ./hihi
  Recursing into +einame+.egg-info
    Creating ./hihi/hihi.egg-info/
...
  Copying setup.py_tmpl to ./hihi/setup.py
  Copying start-+package+.py_tmpl to ./hihi/start-hihi.py
Running /usr/bin/python setup.py egg_info
Error (exit code: 1)
Traceback (most recent call last):
  File "setup.py", line 2, in ?
    from turbogears.finddata import find_package_data
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/__init__.py", line 5, in ?
    from turbogears import config
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/config.py", line 3, in ?
    from cherrypy import config
ImportError: No module named cherrypy

Traceback (most recent call last):
  File "/usr/bin/tg-admin", line 7, in ?
    sys.exit(
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/command/base.py", line 353, in main
    command.run()
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/command/quickstart.py", line 195, in run
    command.run(cmd_args)
  File "/usr/lib/python2.4/site-packages/PasteScript-0.9.7-py2.4.egg/paste/script/command.py", line 210, in run
    result = self.command()
  File "/usr/lib/python2.4/site-packages/PasteScript-0.9.7-py2.4.egg/paste/script/create_distro.py", line 129, in command
    cwd=output_dir)
  File "/usr/lib/python2.4/site-packages/PasteScript-0.9.7-py2.4.egg/paste/script/command.py", line 565, in run_command
    raise OSError("Error executing command %s" % cmd)
OSError: Error executing command /usr/bin/python
Tajemnicze zniknięcie modułu cherrypy (serwera HTTP, z którego korzysta TurboGears) pozostaje dla mnie niewyjaśnione. Instaluję na nowo. I zakładam nowy projekt:
$ tg-admin quickstart
Enter project name: hihi
Enter package name [hihi]:
Do you need Identity (usernames/passwords) in this project? [no]
Selected and implied templates:
  TurboGears#tgbase      tg base template
  TurboGears#turbogears  web framework

Variables:
  egg:         hihi
  identity:    none
  package:     hihi
  project:     hihi
  sqlalchemy:  False
Creating template tgbase
Creating directory ./hihi
  Recursing into +einame+.egg-info
    Creating ./hihi/hihi.egg-info/
...
  Copying setup.py_tmpl to ./hihi/setup.py
  Copying start-+package+.py_tmpl to ./hihi/start-hihi.py
Running /usr/bin/python setup.py egg_info
Error (exit code: 1)
Traceback (most recent call last):
  File "setup.py", line 2, in ?
    from turbogears.finddata import find_package_data
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/__init__.py", line 6, in ?
    from turbogears.controllers import expose, flash, validate, redirect, 
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/controllers.py", line 9, in ?
    import kid
  File "/usr/lib/python2.4/site-packages/kid-0.9.3-py2.4.egg/kid/__init__.py", line 27, in ?
    from kid.pull import ElementStream, Element, SubElement, Fragment, 
  File "/usr/lib/python2.4/site-packages/kid-0.9.3-py2.4.egg/kid/pull.py", line 11, in ?
    from kid.et import *  # ElementTree
  File "/usr/lib/python2.4/site-packages/kid-0.9.3-py2.4.egg/kid/et.py", line 41, in ?
    import xml.etree.ElementTree as ET
ImportError: No module named etree.ElementTree

Traceback (most recent call last):
  File "/usr/bin/tg-admin", line 7, in ?
    sys.exit(
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/command/base.py", line 353, in main
    command.run()
  File "/usr/lib/python2.4/site-packages/TurboGears-1.1a0-py2.4.egg/turbogears/command/quickstart.py", line 195, in run
    command.run(cmd_args)
  File "/usr/lib/python2.4/site-packages/PasteScript-0.9.7-py2.4.egg/paste/script/command.py", line 210, in run
    result = self.command()
  File "/usr/lib/python2.4/site-packages/PasteScript-0.9.7-py2.4.egg/paste/script/create_distro.py", line 129, in command
    cwd=output_dir)
  File "/usr/lib/python2.4/site-packages/PasteScript-0.9.7-py2.4.egg/paste/script/command.py", line 565, in run_command
    raise OSError("Error executing command %s" % cmd)
OSError: Error executing command /usr/bin/python
znów coś z modułami, ale tym razem dziwniej: niby nie ma 'podmodułu' etree.ElementTree modułu xml ze standard library - coś jest nieźle skopane, albo... Mignęło cos pod czachą... Patrzę i mnie zmyło: PEP-0356, bo nie ma ebuilda do python'a 2.5, a to w nim wbudowano do stdlib ElementTree, a już na tyle późno, że zdążę tylko napisać na Jabbie o tym fenomenalnym wieczorze.

c.d.n
W następnych odcinkach: o TurboStanie, IronPythonie i masie innych. Stay tuned.

Skomentuj (0) 2006-09-08 00:13:04 Edytuj

grzywacz

GP2X - raj dla majsterkowiczów

Pomysł dość zwariowany i wymagający świetnego rozeznania w sprzęcie oraz oprogramowaniu, ale spójrzcie tylko na to: GP2X z wewnętrznym odbiornikiem GPS. Ta sama osoba wyposażyła wcześniej swoją konsolkę w standardowy port USB, dający możliwość wykorzystania trybu "usb host" do podłączania zewnętrznych urządzeń, choćby przechowywania danych. Oczywiście z racji tego, że na konsoli działa Linux, obsługa peryferiów nie nastręcza żadnych problemów.

Skomentuj (6) 2006-09-07 00:36:07 Edytuj

grzywacz

Druga (i pierwsza) aplikacja

Hello world

Co prawda granie jeszcze mi się nie znudziło, ale możliwość odpalenia na GP2X własnej aplikacji w końcu okazało się zbyt wielką pokusą. Musiałem to zrobić. Zacząłem od ściągnięcia oficjalnego SDK, w którego skład wchodzi podstawowy toolchain, z kompilatorem (gcc 3.x), asemblerem, linkerem, biblioteką C i kilkoma innymi, potrzebnymi do kompilacji narzędziami. Ubogo, ale wystarczyło do skompilowania pierwszego "hello world", które po skopiowaniu na kartę SD i uruchomieniu w emulatorze terminala wypisało eleganckie: "Hi! This is your GP2X ready software!". Pierwszy sukces.

SDL

Przyznam, że jestem leniem. Zamiast w pocie czoła kompletować sobie środowisko wraz z wszystkimi niezbędnymi bibliotekami, skorzystałem z gotowego skryptu autorstwa ooPo'a i archiwum źródeł (do pobrania tutaj), dzięki którym dwoma wywołaniami make zbudowałem nowe gcc (4.0.2) z wszystkimi pomocniczymi aplikacjami oraz bibliotekami (SDL z dodatkami, libjpeg, libpng, zlib, libogg, libvorbis, libxml...) - jednym słowem wszystko, co może przydać się w pisaniu aplikacji na GP2X. SDLowe "hello world" było już tylko formalnością:

SDLowe Hello World

Odbijające się po ekranie ludziki z zestawu ikon nuvola, wyłączane naciśnięciem dowolnego przycisku konsolki. Zdaje się zatem, że wszystko działa i w wolnej chwili będę mógł napisać coś bardziej użytecznego. Jako ciekawostka:

bouncers.gpe: ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.0.0, statically linked, for GNU/Linux 2.0.0, stripped

Skomentuj (2) 2006-09-04 10:24:37 Edytuj

grzywacz

GP2X - pierwsze wrażenia

Jakiś czas temu (na swojej prywatnej jabbie) pisałem o konsolce GP2X. Jako że jestem już jej szczęśliwym posiadaczem i trochę ją potestowałem, to podzielę sie wrażeniami z, jak na razie krótkiego, użytkowania tego sprzętu.

Zaczynamy od marudzenia

Pierwszy zawód spotkał mnie dość szybko - ponieważ konsolka sama w sobie ma bardzo mało pamięci stałej (ok. 20MB wolnego miejsca na wbudowanym flashu, które zostało niewykorzystane przez system), to z konieczności należy zaopatrzyć się w kartę SD o odpowiednio dużej pojemności. Oprócz karty należałoby kupić czytnik, ale że GP2X daje się podłączyć do komputera przy pomocy USB i udostępnić kartę/flash w trybie usb storage, to zakup ten sobie na początek darowałem. Błąd. Otrzymany w zestawie (certyfikowany przez producenta i w ogóle) kabel okazał się być wadliwy. Po połączeniu nim sprzętów kernel zaczął wypluwać co sekundę brzydkie błędy:

kjut kernel: hub 1-0:1.0: connect-debounce failed, port 1 disabled
Szybkie poszukiwanie w sieci ujawniło, że najprawdopodobniej jest to wina przewodu. "Na wszelki wypadek" kupiłem czytnik kart SD, dodatkowy przewód był w zestawie i... magia, podłączenie GP2X przy jego pomocy rozwiązało problem! Dodam, że "certyfikowane" akcesoria są oczywiście odpowiednio droższe od tanich zamienników, które bez problemu można kupić w sklepie. Brzydko.

Po udanym bezpośrednim połączeniu konsolki z komputerem naszły mnie wątpliwości, czy słusznie zrobiłem wydając dodatkowe pieniądze na czytnik, mogłem przecież kupić sam kabel, co najmniej o połowę tańszy. Jednak warto było. Połączenie z konsolką nie jest zbyt stabilne i w pewnych (tajemniczych dla mnie) warunkach potrafi się przerwać, a szybkość kopiowania danych nie jest nadzwyczajna. Czytnik oszczędza więc czas i nerwy, a w przyszłości może znajdę dla niego jeszcze inne zastosowanie.

Słodycz

Powyższe wyczerpuje listę problemów, które do tej pory miałem z GP2X. Gdy je już rozwiązałem, pozostała sama słodycz.

Sprzęt bardzo ładnie daje sobie radę z odtwarzaniem filmów - co prawda domyślnie obsługiwany jest tylko obraz zakodowany MPEG4 oraz dźwięk MP3/OGG Vorbis, ale w większości przypadków to zupełnie wystarcza. Bez większych problemów można znaleźć w sieci aplikacje (przerobiony oryginalny player, MPlayer) z obsługą większej liczby formatów, jak choćby dźwięku w AC3. Obsługa nie nastręcza trudności. Dźwięk odtwarzany przez słuchawki brzmi bardzo dobrze, a jakości obrazu nie można niczego zarzucić. Ekran jest jasny i nie smuży. Ponadto mamy możliwość wyboru częstotliwości zegara procesora - dzięki temu można odpowiednio dopasować jego wydajność (i pobór energii!) do oglądanego filmu.

Odtwarzanie muzyki sprawdza się równie dobrze. Obsługiwane są formaty MP3 oraz OGG Vorbis, do wyboru jest kilka ustawień equalizera, a jednym przyciskiem można zablokować pozostałe i wyłączyć ekran - idealne rozwiązanie, gdy jesteśmy w drodze.

Przeglądarka grafik działa szybko, a jej obsługa jest bardzo intuicyjna. Pozwala na łatwe skalowanie oraz obracanie obrazu, przesuwanie go oraz przełączanie między kolejnymi plikami. Stanowi drobny, ale bardzo funkcjonalny element całości.

Dodatkowych aplikacji nie brakuje - konsolka przyszła do mnie z preinstalowanym demem gry Vektar, dynamicznej strzelaninki z miłą dla oka i ucha oprawą. Szybko zaopatrzyłem się w kolejne - Quake'a, Sudoku, Powermangę (wreszcie doceniłem fakt, że napisano ją z myślą o małej rozdzielczości), C-Dogs, UQM... Do tego emulator terminala (obsługiwany joystickiem!) i alternatywny do oficjalnego czytnik ebooków. Znalazłem też trochę ładnych scenowych demek demonstrujących możliwości drzemiące w tym niepozornym kawałku sprzętu. Zainteresowanych odsyłam do archiwum aplikacji dla GP2X.

Gdy nacieszę się już stroną użytkową konsolki, spróbuję napisać na nią moją pierwszą aplikację - mały krok w kierunku zrobienia kompletnego portu Battle for Wesnoth, przymiarką do którego są prace nad trybem tiny gui, o których pisałem już kilkukrotnie na mojej prywatnej jabbie. Z postępów prac zdam tu rzecz jasna szczegółową relację. ;-)

Skomentuj (4) 2006-09-03 17:49:55 Edytuj

grzywacz

Witamy

Witamy Jabbowiczów!

SCINIX to jabba o tematyce informatycznej, prowadzona przez (jak na razie) 2 studentów informatyki z Wydziału Matematyki Uniwersytetu Łódzkiego. Cel jest prosty - mieć gdzie pisać o interesujących nas sprawach i jednocześnie robić to w towarzystwie o podobnych zainteresowaniach. Ma to w zamyśle zaowocować większą ilością częściej dodawanej treści o dobrze sprecyzowanej tematyce (czyli nie tak, jak to ma teraz miejsce na mojej, grzywaczowej jabbie).

Jest to eskperyment i czas pokaże, czy nam się uda. Na razie musimy jeszcze trochę popracować nad szablonem, ale na dniach ruszamy pełną parą. ;-)

Skomentuj (2) 2006-09-02 15:29:57 Edytuj

Kategorie

Blogroll  |  Edytuj

Linki  |  Edytuj

Feeds