Mein Programm als Android APK

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Chazy
User
Beiträge: 9
Registriert: Samstag 8. Dezember 2012, 09:29

Hallihalo,

ich hatte mal die Idee, eine BinärUhr in Python zu programmieren.
Die Graphics hab ich mir aus "Graphics.py" importiert.
Nun würde mich interessieren, ob man mein Programm auch auf meinem AndroidHandy zum laufen bringen könnte.

Hier erstmal der Code:

Code: Alles auswählen

from graphics import *
from time import *

def binClock():
	h=strftime("%I", localtime())
	h=int(h[0:2])
	m=strftime("%M", localtime())
	m=int(m[0:2])
	calcTime(h,m)

def calcTime(h,m):

  h1=[8,4,2,1]
  m1=[32,16,8,4,2,1]
  lh=[]
  lm=[]
  counterH=0
  counterM=0
  while True:
    
    for i in h1:
      counterH=counterH+i
      if counterH < h:
	lh.append(i)
      if counterH == h:
	lh.append(i)
	break
      if counterH > h:
	counterH=counterH-i
    break
      
  while True:

    for i in m1:
      counterM=counterM+i
      if counterM < m:
	lm.append(i)
      if counterM == m:
	lm.append(i)
	break
      if counterM > m:
	counterM=counterM-i
	
    break  
  timeToList(h1,m1,lh,lm)
  
def timeToList(h1,m1,lh,lm):

  l1=range(len(h1))
  l2=range(len(m1))
  for i in range(0,len(h1)):
    l1[i]=' '
  for i in range(0,len(m1)):
    l2[i]=' '
  for i in range(len(lh)):
    x = h1.index(lh[i-1])
    l1[x]='X'
  for i in range(len(lm)):
    x = m1.index(lm[i-1])
    l2[x]='X'

  clockView(l1,l2)

def clockView(l1,l2):
  
  win=GraphWin("BinaryClock", 250,150)
  c = Circle(Point(50,50), 10)
  d = Circle(Point(100,50), 10)
  e = Circle(Point(150,50), 10)
  f = Circle(Point(200,50), 10)
  g = Circle(Point (50, 100), 10)
  h = Circle(Point (80, 100), 10)
  i = Circle(Point (110, 100), 10)
  j = Circle(Point (140, 100), 10)
  k = Circle(Point (170, 100), 10)
  l = Circle(Point (200, 100), 10)
  
  c1=[c,d,e,f]
  c2=[g,h,i,j,k,l]

  for i in range(len(c1)):
    if l1[i] == 'X':
      c1[i].setFill('blue')
      c1[i].draw(win)
    else:
      c1[i].draw(win)
      
  for i in range(len(c2)):
    if l2[i] == 'X':
      c2[i].setFill('blue')
      c2[i].draw(win)
    else:
      c2[i].draw(win)

  win.getMouse()
  win.close()

binClock()
Ich weiß nicht was von dem Code zu halten ist, bin erst im 2. semester ... aber es tut ;)

MFG
BlackJack

@Chazy: Der Code mag funktionieren, ist aber nicht so besonders.

Erstmal für andere die den vielleicht mal ausprobieren wollen die Quelle für das `graphics`-Modul: http://mcsp.wartburg.edu/zelle/python/

Die Form, also zum Beispiel Namenskonventionen, Leerzeichen, und Einrückung entsprechen nicht dem Style Guide for Python Code.

Die Namen sind überwiegend auch sehr schlecht. Die lokalen Namen viel zu kurz und nichtssagend und die Funktionsnamen nicht immer passend zu dem was die Funktion tut.

Sternchenimporte sollte man vermeiden. Damit werden Programme ganz schnell unübersichtlich, weil man nicht einfach heraus finden kann aus welchem Modul welcher Name kommt. Ausserdem bekommt man Probleme wenn verschiedene Module gleiche Namen enthalten. Man sollte entweder die Objekte die man verwendet explizit importortieren oder nur das Modulobjekt und über das dann auf die enthaltenen Objekte zugreifen.

`binClock()` ist übermässig umständlich. Hast Du Dir das Ergebnis von `localtime()` denn nicht mal angeschaut? Warum dieser unsinnige Umweg über eine Zeichenkette mit `strftime()` um die dann wieder in eine Zahl zu wandeln. Wobei dort das „slicing” der Zeichenkette total überflüssig ist.

Des weiteren enthält die Funktion eine „race condidion” die zu falschen Zeitanzeigen führen kann. Wenn der erste `localtime()`-Aufruf ganz am Ende einer 59sten Minute erfolgt und der zweite Aufruf schon in der darauf folgenden Minute, dann passen Minute und Stunde nicht mehr zusammen. Dann wird zum Beispiel 10:59 Uhr angezeigt obwohl es erst 10:00 Uhr ist.

`h` und `m` mögen hier zwar noch ”verständlich” sein, ich würde sie aber trotzdem `hour` und `minute` nennen. Namen sollen dem Leser möglichst deutlich vermitteln was die Werte dahinter bedeuten.

Anstelle des `time`-Moduls was eine relativ dünne Schicht über die Zeitfunktionen der C-Bibliothek darstellt, würde ich persönlich das `datetime`-Modul verwenden.

Funktionsnamen sollten Tätigkeiten beschreiben, was der Name `binClock()` nicht ist.

In `calcTime()` sind es dann wieder die lokalen Namen. Hier machen sie das Ganze IMHO nahezu unlesbar. Dazu dass sie kurz und absolut nichtssagend sind kommen noch die Zeichen 1 und l die in vielen Schriftarten so ähnlich sind, dass man sie nur mit Mühe auseinanderhalten kann.

Zum Algorithmus ist zu sagen, dass sich hier Code wiederholt der sich nicht wiederholen sollte. Und insgesamt ist der Algorithmus der sich über `calcTime()` und `timeToList()` erstreckt total überkompliziert. Eine Zahl in eine Liste mit Bits umzusetzen ist eigentlich *eine* Schleife. Man itertiert über die Zweierpotenzen und testet mit Bitoperationen ob das jeweilige Bit gesetzt ist oder nicht und fügt dementsprechend das passende Zeichen der Liste hinzu. Das war's. Eventuell muss man die Liste danach noch umdrehen wenn man die Zweierpotenzen aufsteigend erzeugt hat. *Erzeugt* und nicht per Hand in eine Liste geschrieben.

Was machen die ``while True:``-Schleifen in `calcTime()` eigentlich? Wie oft werden die *immer* durchlaufen?

```for i in range(len(sequence)):`` ist in Python ein „anti pattern”. In 99,99% der Fälle macht man da etwas falsch, denn man braucht nur recht selten *nur* den Index innerhalb der Schleife. Man kann über Elemente von Sequenzen direkt iterieren, ohne den Umweg über einen Index. Wenn man denn Index *zusätzlich* braucht, gibt es die `enumerate()`-Funktion.

Überhaupt ist `timeToList()` „unpythonisch”. Man erzeugt in der Regel keine Listen mit Objekten die man eigentlich gar nicht benötigt weil sie im nachfolgenden Code der Reihe nach durch andere Werte ersetzt werden. In solchen Fällen fängt idiomatisches Python mit einer leeren Liste an, an die die Werte die da am Ende auch stehen sollen angehängt werden. Eventuell das ganze auch als „list comprehension” formuliert wo es sich anbietet.

Dann überschreibst Du die nicht benötigten Zahlen erst einmal komplett mit ' ', wobei auch hier Werte an Stellen geschrieben werden, die später wieder durch andere Ersetzt werden. Statt gleich in *einer* Schleife zu entscheiden welcher Wert an welchen Platz kommen soll.

Die Schleifen für die 'X' sind vom Laufzeitverhalten sehr unschön. die `index()`-Methode sucht linear nach dem Wert.

Letztendlich sind beide Funktionen überflüssig weil man mit der `format()`-Funktion auf Zeichenketten Zahlen in eine Binärdarstellung aus '0' und '1' formatieren kann.

In `clockView()` ist zu viel von Hand geschriebener Code beziehungsweise Kopieren und Einfügen. Die beiden Listen mit den Kreisen liessen sich so einfach per Code erstellen statt jeden Kreis per Hand zu schreiben. Stell Dir mal den Aufwand vor die Kreisgrösse, -position, oder den Abstand bei Deinem Quelltext verändern zu müssen. Grössen oder Abstände die von anderen Werten abhängen sollten vom Programm berechnet werden und nicht vom Programmierer. Hier läge IMHO die Hauptkomplexität bei dem Programm.

Das eine Funktion die jeweils nächste aufruft ist nicht gut. So lassen sich die einzelnen Funktionen weder separat testen, noch wiederverwenden, weil jeder Aufruf immer die anderen Aufrufe nach sich zieht.

Das Programm könnte dann so aussehen:

Code: Alles auswählen

#!/usr/bin/env python
from graphics import Circle, GraphWin, Point
from time import localtime


def get_binary_time(time=None):
    now = localtime(time)
    return ('{0:04b}'.format(now.tm_hour), '{0:06b}'.format(now.tm_min))


def display_binary_values(binary_values):
    padding = 50
    radius = 10
    min_distance = 10
    
    diameter = 2 * radius
    max_circles = max(map(len, binary_values))
    width, height = (
        (diameter + min_distance) * x - min_distance + 2 * padding
        for x in [max_circles, len(binary_values)]
    )
    window = GraphWin('BinaryClock', width, height)
    
    for row_number, digits in enumerate(binary_values):
        spacing = (width - 2 * padding - diameter) / (len(digits) - 1)
        y = padding + radius + (diameter + min_distance) * row_number
        for i, digit in enumerate(digits):
            circle = Circle(
                Point(int(i * spacing) + padding + radius, y),
                radius
            )
            if digit == '1':
                circle.setFill('blue')
            circle.draw(window)
    
    window.getMouse()
    window.close()


def main():
    display_binary_values(get_binary_time())


if __name__ == '__main__':
    main()
In die Dokumentation gehört dann wahrscheinlich eine Zeichnung an welcher die Berechnungen für die Grafik deutlich gemacht werden.

Was die Android-App angeht: Das würde ich entweder „nativ” machen, also in Java, oder als HTML5-App in JavaScript. Bevorzugt letzteres, weil das unabhängiger vom Gerät ist.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

BlackJack hat geschrieben:Was die Android-App angeht: Das würde ich entweder „nativ” machen, also in Java, oder als HTML5-App in JavaScript. Bevorzugt letzteres, weil das unabhängiger vom Gerät ist.
Richtig, am besten als HTML5-App, evtl. in CoffeeScript. Kann man dann mittels PhoneGap/Apache Cordova in ein APK kompilieren.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Chazy
User
Beiträge: 9
Registriert: Samstag 8. Dezember 2012, 09:29

@ blackJack

Erstmal danke für deine ausführliche Antwort! Ich muss hinzufügen, dass ich mich erst seit diesem Semester mich mit Python beschäftige, und wie ich nach dem lesen deines Code's feststellen konnte, ich es sehr kompliziert gemacht habe.
Ich habe zwar von der Formatierung die eine Zahl in eine Binärzahl umwandelt gelesen; dennoch wollte ich sie vermeiden, und es irgendwie selber hinbekommen über eine Liste.
Was die Namensgebung meiner Variablen angeht kann ich nur sagen, dass ich nur davon ausging, dass ich selbst den Code verstehen müsste. Sprich, ich habe zu dem Zeitpunkt nicht daran gedacht, mal es hier in dem Forum zu posten.
Oder nennt man Variablen IMMER so deutlich, auch wenn man nur selbst was mit dem Code zu tun hat? klingt zwar jetzt vielleicht ein wenig dämlich die Frage, aber das war mir so nicht bewusst :lol:

Ich habe ein wenig gebraucht, bis ich deinem Code folgen konnte, ist nämlich noch etwas zu hoch für mich :mrgreen:
Wie genau Zeichnet er in der Funktion 'display_binary_values' die Kreise in den bestimmten Abständen?
Das ist mir noch nicht so wirklich klar =/
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Chazy hat geschrieben:Oder nennt man Variablen IMMER so deutlich, auch wenn man nur selbst was mit dem Code zu tun hat? klingt zwar jetzt vielleicht ein wenig dämlich die Frage,
@Chazy: Das ist keineswegs dämlich: Nicht nur die Variablen sollten klar bezeichnet, sondern auch der Code so kommentiert sein, dass ein Fremder damit klar kommen kann. Wenn Du Dich eine Weile mit Deinem Code nicht mehr befasst hast, bist Du schnell selbst der Fremde.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Chazy hat geschrieben:Oder nennt man Variablen IMMER so deutlich, auch wenn man nur selbst was mit dem Code zu tun hat? klingt zwar jetzt vielleicht ein wenig dämlich die Frage, aber das war mir so nicht bewusst :lol:
Ja natürlich. Stellt dir vor du schaust dir den Code in 6 Monaten an, dann weißt du doch auch nicht mehr was die Variabennamen sollen und stehst wie der Ochs vorm Berg.

Das schöne: gute Variablennamen kosten nichts. Also kann man einfach immer gute Namen verwenden und muss sich keine Gedanken machen wann man gute und wann man schlechte Variablennamen nutzen muss.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@Chazy: Ich würde Namen immer deutlich wählen, denn selbst bei Quelltext den (wahrscheinlich) niemand anders zu sehen bekommt, weiss man *selbst* nach einem Jahr meistens nicht mehr so genau was einbuchstabige Namen eigentlich mal bedeuten sollten.

Hier der Quelltext mit einer eigenen Funktion zum Umwandeln in Binärdarstellung und Kommentaren zu einigen Variablen beim zeichnen der Kreise:

Code: Alles auswählen

#!/usr/bin/env python
from graphics import Circle, GraphWin, Point
from time import localtime


def int2binary(value, length):
    result = list()
    while value:
        result.append(str(value & 1))
        value >>= 1
    result.reverse()
    return ''.join(result).zfill(length)


def get_binary_time(time=None):
    now = localtime(time)
    return (int2binary(now.tm_hour, 4), int2binary(now.tm_min, 6))


def display_binary_values(binary_values):
    # 
    # Space between the window borders and the circles.
    # 
    padding = 50
    # 
    # Radius of one circle.
    # 
    radius = 10
    # Minimal distance between two circle's circumferences.
    # 
    min_distance = 10
    
    diameter = 2 * radius
    # 
    # Maximal number of circles in a row.
    # 
    max_circles = max(map(len, binary_values))
    width, height = (
        (diameter + min_distance) * x - min_distance + 2 * padding
        for x in [max_circles, len(binary_values)]
    )
    window = GraphWin('BinaryClock', width, height)
    
    for row_number, digits in enumerate(binary_values):
        # 
        # Distance between the centers of two circles.
        # 
        spacing = (width - 2 * padding - diameter) / (len(digits) - 1)
        y = padding + radius + (diameter + min_distance) * row_number
        for i, digit in enumerate(digits):
            circle = Circle(
                Point(int(i * spacing) + padding + radius, y),
                radius
            )
            if digit == '1':
                circle.setFill('blue')
            circle.draw(window)
    
    window.getMouse()
    window.close()


def main():
    display_binary_values(get_binary_time())


if __name__ == '__main__':
    main()
Wie gesagt: Die Berechnungen macht man sich am besten anhand einer Zeichnung klar.

Edit: Achtung: Der doofe Syntax-Highlighter vom Forum macht aus einem einfachen '&' ein '&'
Chazy
User
Beiträge: 9
Registriert: Samstag 8. Dezember 2012, 09:29

danke an die antworten. es war erfreulich, dass sie nicht herablassend waren, wie in manch anderen Foren.

Ich bin den Code mal Zeile für Zeile durchgegangen, daher wirbelt es ein Paar fragen für mich auf.

In der Funktion 'get_binary_time' hast du den Übergabeparameter 'time' = 'None' gesetzt.
Ich hab die Funktion mal ohne Übergabe getestet und da würde das gleiche Ergebnis rauskommen, wenn ich einfach sage 'now = localtime()'. Daher wundert es mich, warum du 'Time=None' als Übergabe deklarierst :K

Gibt es auch einen Fall, wo '__name__' nicht gleich '__main__' ist? Oder warum diese Überprüfung?
kann man nicht einfach die main() aufrufen ohne die Überprüfung? Was bezweckt diese?

Ich habe solangsam das Gefühl, dass wir im Studium mit Python ganz anders umgehen wie die, die es beruflich oder täglich machen :lol:
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo Chazy,

Die Variable __name__ enthält bei Modulen seinen Namen, also, wenn Du
im Hauptprogramm zum Beispiel:

Code: Alles auswählen

import mymodul
hast, und in mymodul die Variable __name__ ausgibst erhältst Du 'mymodul'.
Wird eine Datei jedoch nicht als Modul sondern als Programm ausgeführt,
dann gibt es ja keinen Modulnamen, stattdessen steht in __name__ '__main__'.
Damit kann man Programme schreiben, die für sich genommen eigenständig sind,
aber sie können auch ihre Funktionen wie ein Modul anderen Programmen
zugänglich machen.

Zu get_binary_time: Der Sinn von default-Parametern ist doch gerade der,
dass man sie auch weglassen kann, die Funktion aber mächtiger ist, wenn
man ihr zusätzlich noch Parameter übergibt. In diesem Fall also eine Funktion
die nicht nur die aktuelle Zeit zurückgibt, sondern auch einen frei gewälten
Zeitpunkt ins Binäre verwandelt.

Grüße
Sirius
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Hier ist noch ein Link zu Python for Android. Ich habe es allerdings noch nicht selber getestet.
Chazy
User
Beiträge: 9
Registriert: Samstag 8. Dezember 2012, 09:29

okay das scheint alles ziemlich einleuchtend zu sein :o

Eine abschließende Frage hätte ich noch :)
Und zwar ist es so, dass man das Programm immer neu ausführen muss, wenn man die aktuelle Uhrzeit angezeigt bekommen will.

Gibt es einen Code, der das Programm neu aufruft/aktualisiert, wenn es eine Änderung gibt? In dem Fall meine ich als Änderung die Uhrzeit.
Wenn ja, könnt ihr mich auf was hinweisen?
Ich habe schon total banale Dinge getestet wie:

Code: Alles auswählen

while True:
  main()
Hat ja "überraschungsweise" nicht getan :D

Wie könnte man da also rangehen an die Sache?
Antworten