Frontend für Modem-Interneteinwahl (Tkinter)

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
abgdf

Sonntag 30. Juli 2006, 01:01

Hallo,

hab mir hier ein Tkinter-Frontend für die Interneteinwahl mit meinem 56K-Modem gebaut (also sowas ähnliches wie kinternet).
Die Einwahl erfolgt mit "cinternet", das wohl kaum jemand hat, dieses ist aber z.B. Teil von SuSE 10.0.
Außerdem ist es auf 3 Tarife eben meines Providers ausgelegt.
Es zeigt (mir) dann aber auch die angefallenen Kosten und summiert diese in einer Log-Datei.
Ich hab das Skript hier "entschärft", also die Einwahl in der Funktion con1() auskommentiert. Ebenso einige Zeilen in den Funktionen con2() und updex(), so daß man sich das mal relativ "gefahrlos" ansehnen können sollte (möglicherweise wird eine Datei "~/internetlog" erstellt).

Im Prinzip ist das Skript wohl nur für mich geeignet, aber ich wollte es trotzdem hier mal vorstellen.
Vielleicht kann/will es jemand ja für sich anpassen oder so:

Edit: Mein Skript habe ich jetzt weiter unten gepostet.

Viele Grüße

Edit: Einige Einrückungen hier im Posting sehen falsch aus :shock: (was in Python ja tödlich ist); beim Einfügen in einen Editor sieht es dann aber wieder richtig aus :? .
Zuletzt geändert von abgdf am Montag 31. Juli 2006, 15:25, insgesamt 1-mal geändert.
BlackJack

Sonntag 30. Juli 2006, 09:08

Einrückung scheint doch okay zu sein!? Also auch in der Anzeige hier im Forum.

Das Übliche: Teilweise zu lange Zeilen und es könnten deutlich mehr Leerzeichen im Quelltext sein. DasmanLeerzeichenbenutzthateinenGrund,nämlichbessereLesbarkeit.

Die Namenswahl ist auch nicht immer so toll, 1-Buchstaben-Namen und so etwas wie `updrbtns()` oder `updex()` sind nicht besonders aussagekräftig. Warum nicht `update_and_exit()`?

`TkApp.check()` könnte `True` oder `False` zurückgeben, je nachdem ob die Verbindung steht oder nicht. So wie es jetzt ist, steht an zwei Stellen der gleiche Quelltext um den Rückgabewert auseinander zu nehmen. Der Name `is_connected()` wäre dann aussagekräftiger als `check()`.

Apropos `True` und `False`: Die sollte man anstelle von 0 und 1 verwenden wenn man Wahrheitswerte ausdrücken möchte.

Die aktuelle Zeit in Stunden und Minuten als Zeichenkette wird an mehr als einer Stelle recht umständlich ermittelt, wo es doch `time.strftime()` gibt, was für's Datum ja verwendet wird.

Das Aufteilen der Sekunden in Minuten und Sekunden in `TkApp.contime()` ginge mit `divmod()` kürzer.

Eine ganze Menge Quelltext wiederholt sich dann in `TkApp.discon()`. In der Methode habe ich dann auch das erste Mal gesehen das `TkApp.x` nicht nur wahr oder falsch sein kann, sondern hier an eine 2 gebunden wird. Dafür sollte man Konstanten einführen um den Quelltext lesbarer zu machen. Also irgendwo am Anfang mal ``NEVER_CONNECTED, CONNECTED, DISCONNECTED = xrange(3)`` oder so ähnlich schreiben und dann diese Namen bei Zuweisungen und Vergleichen verwenden.

In `TkApp.discon()` und `TkApp.con2()` werden einige Attribute für `TkApp` Objekte neu eingeführt. Das ist zumindest ein "code smell".

`TkApp.z()` braucht `self` nicht, wäre also eher eine Funktion anstelle einer Methode. Das gleiche gilt für `TkApp.check()`.

Mal abgesehen davon das der Name `z()` schlecht ist, gibt's diese Funktionalität schon im `locale` Modul:

Code: Alles auswählen

In [16]: import locale

In [17]: locale.setlocale(locale.LC_ALL)
Out[17]: 'de_DE.UTF-8'

In [18]: locale.format("%.2f", 1000000.15, True)
Out[18]: '1.000.000,15'
Wenn man 1000er Punkte in der Online-Rechnung hat, sollte man aber schleunigst den Anbieter wechseln. ;-)
abgdf

Sonntag 30. Juli 2006, 16:47

@BlackJack:

Herzlichen Dank für das ausführliche Feedback und die wertvollen Hinweise.
Damit kann ich meinen "Programmierstil" weiter verbessern.

Viele Grüße
abgdf

Sonntag 30. Juli 2006, 17:53

Hallo nochmal, BlackJack,

Deine Kritikpunkte sind alle absolut berechtigt.
Manches, wie z.B. divmod(), beruht eben auf Python-Spezialitäten, die ich erst entdecke. Dafür sind Deine Hinweise sehr hilfreich.

Die Funktion z() kommt noch aus BASIC-Zeiten, wo es sowas wie ein locale-Modul (meines Wissens) nicht gab. Zudem hab ich zumindest damals immer mit globalen Variablen hantiert; "z" in "GOSUB z" war eben am Ende des Alpahbets ...
Bzgl. Tausenderpunkten hatte ich mir auch eine eigene Funktion gebaut; wußte ja nicht, daß das in Python alles so einfach ist. War wohl eher C-mäßiges Denken, wo man im Zweifel viel selbst konstruieren (oder noch umständlicher entsprechende Bibliotheken suchen) muß. Sie nimmt einen mit z() in einen String gewandelten "float" (mit "," für das Dezimalkomma), und gibt einen String mit Tausenderpunkten zurück:

Code: Alles auswählen

def tpoints(d):
    klen=d.find(',')
    b=""
    c=""
    o=float(1)
    while klen-o >= 0:
        b=b+d[klen-int(o)]
        if (o/3) == int (o/3) and (klen-o) != 0:
            b=b+'.'
        o+=1
    for i in range(len(b)-1,-1,-1):
        c=c+b[i]
    c=c+d[klen:]
    return (c)
Hatte ich seinerzeit schon recht lange für gebraucht ...

Viele Grüße
BlackJack

Sonntag 30. Juli 2006, 21:21

In C gäbe es dafür `setlocale()` und `strfmon()` (string format monetary). Python ist an erstaunlich vielen Stellen nur eine dünne Schicht über den entsprechenden C-Bibliotheken. :-)
abgdf

Montag 31. Juli 2006, 15:28

... aber eine wichtige :) .

Inzwischen sieht mein Skript so aus.
Einiges habe ich berücksichtigt, anderes noch nicht.
Aber es läuft (jetzt sogar besser):

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf8 -*-

import Tkinter
from Tkconstants import *
import os
import time
import sys

# Defining some constants first. You may need to change these.

LOGFILE = os.environ["HOME"] + "/internetlog"
APPFONT = "{suse sans} 15 {normal}"
INTERNETSTART = (
"cinternet --interface-number=01 --provider-name=780 --start",
"cinternet --interface-number=01 --provider-name=770 --start",
"cinternet --interface-number=01 --provider-name=785 --start"
)
INTERNETSTATUS = "cinternet --interface-number=01 --status"
INTERNETLOG = "cinternet --interface-number=01 --log"
INTERNETSTOP = "cinternet  --interface-number=01 --stop"
INTERNETKBS = "cinternet --interface-number=01 -l | grep CONNECT"
INTERNETNUM = "cinternet --interface-number=01 -l | grep ATDT"

class TkApp:

    def __init__(self): 

        self.provider = "780"

        if time.localtime()[3] >= 18:
            self.provider = "785"

        self.x = 0
        # Values of self.x:
        # 0: disconnected at startup, 1: connecting,
        # 2: connected, 3: disconnected after connection.
        # 4: disconnected while connecting.

        self.act = False
        self.updated = False
        self.intime = 0
        self.outtime = 0
        self.mw = Tkinter.Tk()
        self.mw.title("Internet Connector")
        self.mw.iconname("Internet Connector")
        self.mw.geometry("+250+120")
        self.mw.option_add("*font", APPFONT)
        self.frame1=Tkinter.Frame(self.mw)
        self.frame2=Tkinter.Frame(self.frame1)
        self.conbtn = Tkinter.Button(self.frame1,text = "Connect", command = self.doConnect)
        self.disconbtn = Tkinter.Button(self.frame1,text = "Disconnect", command = self.doDisconnnect)
        self.rbv = Tkinter.Variable()
        self.rb780 = Tkinter.Radiobutton(self.frame2,text = "780", variable=self.rbv, value = "780", indicatoron = 0, selectcolor = "grey")
        self.rb770 = Tkinter.Radiobutton(self.frame2, text = "770", value = "770", variable = self.rbv, indicatoron=0, selectcolor = "grey")
        self.rb785 = Tkinter.Radiobutton(self.frame2, text = "785", value = "785", variable = self.rbv, indicatoron = 0, selectcolor = "grey")
        self.rb780.pack(side = LEFT)
        self.rb770.pack(side = RIGHT)
        self.rb785.pack(side = RIGHT)
        self.conbtn.pack(side = LEFT,anchor = W)
        self.disconbtn.pack(side=RIGHT,anchor = E)
        self.frame2.pack(fill = NONE)
        self.frame1.pack(side = TOP, fill = BOTH, padx = 20, pady = 10)
        self.tfield = Tkinter.Text(self.mw,width = 50, height = 15, background = 'white', foreground = 'black')
        self.tfield.pack(fill = BOTH)
        self.tfrow = 1
        self.status = StatusBar(self.mw) 
        self.status.pack(side = BOTTOM, fill = BOTH)
        self.frame3 = Tkinter.Frame(self.mw)
        self.exbtn = Tkinter.Button(self.frame3,text = "Exit", command = self.mw.destroy)
        self.ctbtn = Tkinter.Button(self.frame3,text = "Current costs", command = self.currentCosts)
        self.updateLogfilebtn = Tkinter.Button(self.frame3,text = "Update logfile", command = self.updateLogfile)
        self.frame3.pack(side = BOTTOM, fill = BOTH, padx = 20, pady = 10)

        # If you want three buttons in a row, pack the middle one last. 

        self.exbtn.pack(side = LEFT, anchor = W)
        self.updateLogfilebtn.pack(side = RIGHT, anchor = E)
        self.ctbtn.pack()

        self.updateRadiobuttons()
        self.status.set('Disconnected.')
        self.mw.mainloop()

    def doConnect(self):

        if self.x == 1:
            self.status.set('Already connecting !')
            return()

        if self.x == 2:
            self.status.set('Already connected !')
            return()

        self.provider=self.rbv.get()

        self.act = False

        self.updated = False

        if len(sys.argv) > 1:
            self.provider = sys.argv[1]

        self.tfrow = 1
        self.tfield.delete('1.0', END)
        for i in range(1, 20):
            self.tfset(i, " \n")

        # Uncomment to activate:
        # for i in INTERNETSTART:
            # if i.find(self.provider) != -1:
                # os.system(i)

        self.status.set('Connecting.')

        # Eventloop taking care of next function call. Using this
        # instead of time.sleep(2) to keep the GUI intact:

        self.x = 1
        self.mw.after_idle(self.completeConnection)

    def completeConnection(self):

        if self.x != 1:
            return()

        a = checkConnection()
        b = a[0].split(" ")[1].rstrip("\n")

        # Uncomment to activate:
        # if b != "connected":
            # self.tfset(self.tfrow, "Modem-status: " + b.capitalize() + ".")
            # self.mw.after(2000, self.completeConnection)

            # if checkConnectError() == 1:
                # self.tfset(self.tfrow + 2, 'Error. Please click "Disconnect" to terminate the connection.')
                # self.status.set('Error while connecting.')

            # return()

        self.tfield.delete('1.0', END)
        self.tfrow = 1
        for i in range(1, 20):
            self.tfset(i, " \n")

        modstatus = "Modem-status: Connected"
        
        num = getTelephoneNumber()
        kbs = getKbs()

        if num != "a":
            modstatus += " to " + num

        if kbs != "a":
            modstatus += " with "+kbs+" Bits/s"

        modstatus += "."

        self.tfset(self.tfrow, modstatus)
        self.status.set('Connected.')

        self.today = time.strftime("%d.%m.%Y")
        self.tfrow += 2
        self.tfset(self.tfrow, "Date: "+ self.today + ".\n")

        self.intime = time.time()
        hours = str(time.localtime(self.intime)[3])
        mins = str(time.localtime(self.intime)[4])
        if len(mins) == 1:
            mins = "0" + mins

        self.tfrow += 1
        self.tfset(self.tfrow, "Login-Time: "+hours+":"+mins+".")
        self.updateRadiobuttons()
        self.x = 2

    def currentCosts(self):

        if self.x != 2:
            self.status.set('Not connected !')
            return()

        acttime = time.time()
        hours = str(time.localtime(acttime)[3])
        mins = str(time.localtime(acttime)[4])
        if len(mins) == 1:
            mins = "0" + mins

        if self.act == True:
            self.tfrow -= 4

        self.tfrow += 2
        self.tfset(self.tfrow, "It is now: " + hours + ":" + mins + ".")

        tdif = float(int(acttime - self.intime))

        mins = int(tdif / 60.)

        secs = int(((tdif/60.) - mins) * 60) 

        self.tfrow += 1
        self.tfset(self.tfrow, "Currently connected for: " + str(mins) + " minutes and " + str(secs) + " seconds.")

        mins = int(tdif/60.)
        if mins != tdif/60.:
            mins += 1

        costs = self.getCosts(mins)

        self.tfrow += 1

        if costs < 100: 
            self.tfset(self.tfrow, "Costs up to now: " + str(int(round(costs))) + " Cents.")
        else:
            self.tfset(self.tfrow, "Costs up to now: " + z(round(costs/100.0,2)) + " EUR.\n")

        if costs == 0:
            self.status.set("Error. This wasn't for free !")

        self.updateRadiobuttons()
        self.act = True

    def doDisconnnect(self):

        if self.x == 0:
            self.status.set('Not yet connected !')
            return()

        if self.x == 3:
            self.status.set('Already disconnected !')
            return()

        if self.x == 4:
            self.status.set('Connection already terminated !')
            return()

        os.system(INTERNETSTOP)
        self.status.set('Disconnecting.')

        # Disconnecting is rather fast, so this seems alright:

        a = checkConnection()
        b=a[0].split(" ")[1].rstrip("\n")
        while b != "disconnected":
            time.sleep(1)
            self.mw.update_idletasks()
            a = checkConnection()
            b = a[0].split(" ")[1].rstrip("\n")

        if self.intime == 0:        
            self.status.set("Dialing terminated.")
            self.x = 4
            self.updateRadiobuttons()
            return()

        if self.act == True:
            self.tfrow -= 4

        self.tfrow += 2
        self.tfset(self.tfrow, "Modem-status: " + b.capitalize() + ".")
        self.status.set('Disconnected.')

        self.outtime = time.time()
        hours = str(time.localtime(self.outtime)[3])
        mins = str(time.localtime(self.outtime)[4])
        if len(mins) == 1:
            mins = "0" + mins

        self.tfrow += 1
        self.tfset(self.tfrow, "Logout-Time: " + hours + ":" + mins + ".")

        self.tdif = float(int(self.outtime - self.intime))

        self.minutes = int(self.tdif/60.)
        if self.minutes != self.tdif/60.:
            self.minutes += 1

        self.tfrow += 1
        self.tfset(self.tfrow, "Time connected: " + str(self.minutes) + " minutes.")

        self.costs = self.getCosts(self.minutes)

        self.tfrow += 1

        if self.costs < 100: 
            self.tfset(self.tfrow, "Total costs: " + str(int(round(self.costs))) + " Cents.")
        else:
            self.tfset(self.tfrow, "Total costs: " + z(round(self.costs/100.0,2)) + " EUR.\n")

        if self.costs == 0:
            self.status.set("Error. This wasn't for free.")

        self.updateRadiobuttons()
        self.x = 3
        
    def updateLogfile(self):

        if self.x == 0 or self.x == 1 or self.x == 4:
            self.status.set("Logfile not updated, previous connection required !")
            return()

        if self.x == 2:
            self.status.set("Logfile not updated, still connected !")
            return()

        if self.updated == True:
            self.status.set("Logfile not updated. It's been done before !")
            return()

        line = self.today + "\t" + self.provider + "\t" + str(self.minutes) + "\t" + str(int(round(self.costs))) + "\n"

        if not os.access(LOGFILE, os.F_OK):
            f = file(LOGFILE, "w")
            f.close()

        f = file(LOGFILE, "r")
        a = f.readlines()
        f.close()

        if len(a) == 0:
            a = ["Date\tProvider\tMinutes\tCosts (Cents)\n"]

        if len(a) > 1:
            a.pop() 
            a.pop() 
        a.append(line)

        minssum = 0
        costsum = 0

        for u in a[1:]:
            u = u.rstrip("\n")
            b = u.split("\t")
            minssum += int(b[2])
            costsum += float(b[3])
    
        a.append("-------------------------------------\n")
        a.append("Total:\t"+str(minssum) + " minutes\t" + z(costsum / 100.0) + " EUR\n")

        # Uncomment to activate:
        # f = file(LOGFILE, "w")
        # f.writelines(a)
        # f.close()

        self.status.set('Logfile updated.')
        self.updated = True

    def tfset(self, row, text):
        self.tfield.delete(str(row) + '.0', str(row) + '.end')
        self.tfield.insert(str(row) + '.0', text)

    def updateRadiobuttons(self):

        if self.provider == "780":
            self.rb780.select()

        if self.provider == "770":
            self.rb770.select()

        if self.provider == "785":
            self.rb785.select()

    def getCosts(self,mins):
        
        costs = 0

        if self.provider == "780":
            costs = mins * 1.29

        if self.provider == "770":
            costs = 5.9 + mins * 0.78

        if self.provider == "785":
            if time.localtime()[3] < 18:
                costs = mins * 3.79
            else:
                costs = mins * 0.95

        return(costs)


class StatusBar(Tkinter.Frame):  
 
    def __init__(self,master): 
        Tkinter.Frame.__init__(self,master) 
        self.label = Tkinter.Label(self, bd = 1, relief = SUNKEN, anchor = W) 
        self.label.pack(fill = X)  
 
    def set(self, format): 
        self.label.config(text = format) 
        self.label.update_idletasks()  
 
    def clear(self): 
        self.label.config(text='') 
        self.label.update_idletasks() 

def getKbs():
    f = os.popen(INTERNETKBS)
    a = f.readlines()
    f.close()
    if len(a) == 0:
        return("a")
    b = (a[0].split(" "))[2].split("/")[0]
    if len(b) > 3:
        b = b[0:len(b)-3]+"." + b[len(b)-3:]
    return(b)

def getTelephoneNumber():
    f = os.popen(INTERNETNUM)
    a = f.readlines()
    f.close()
    if len(a) < 2:
        return("a")
    b = a[1].split("ATDT")[1].rstrip("\n")
    return(b)

def z(a):
    d = "%.2f" % a
    d = d.replace('.',',')
    return(d)

def checkConnection():
    f = os.popen(INTERNETSTATUS)
    a = f.readlines()
    f.close()
    return(a)

def checkConnectError():
    b=0
    f = os.popen(INTERNETLOG)
    a = f.readlines()
    f.close()
    for i in a:
        if i.find("?~?~?~?~?~?~?~") != -1:
            b = 1
    return(b)

if __name__ == "__main__":
   app = TkApp()
Viele Grüße

Updated: 14.8.2006.
Antworten