Python 2 zu 3

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
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

Habe bis jetzt nur mit Python 3 gearbeitet. Kann mit jemand diesen Code so verändern, dass es auch unter Pyhton 3 funktioniert? wäre echt nett !!!

Code: Alles auswählen

from Tkinter import *
import thread
import cmath
import time

class TkAnalogClock(Canvas):

    _grad = 0.1047

    def __init__(self,master,size=100,clockbg='white',timeshift=0,show_second=1,**kw):
        Canvas.__init__(self,master,width=size,height=size,**kw)

        self.show_second = show_second
        self.clockbg = clockbg
        self.timeshift = timeshift
        self.size = size
        self.__setup()

    def __setup(self):
        size = self.size
        c = size/2
        s = c/10
        mthick = size/50
        hthick = size/40
        dot_min = 1
        dot_max = dot_min + 4
        ldot_min = dot_max + c/10

        self.center = [c,c]
        self.create_oval(1,1,size-1,size-1,fill=self.clockbg)
        self.create_oval(c-hthick,c-hthick,c+hthick,c+hthick,fill='black')

        n =  [(c-1,dot_min),(c-1,dot_max),(c+1,dot_max),(c+1,dot_min)]
        nl = [(c-1,ldot_min),(c-1,dot_min),(c+1,dot_min),(c+1,ldot_min)]
        for i in range(60):
            if i%5 == 0:
                self.create_polygon(nl)
                self.__rotate(self._grad*i,nl,None)
            else:
                if size > 99:
                    self.create_polygon(n)
                    self.__rotate(self._grad*i,n,None)

        self.coords = {
            "second": [(c-1,c),(c-1,c-size*0.4),(c+1,c-size*0.4),(c+1,c)],
            "minute": [(c-mthick,c),(c,c-size*0.4),(c+mthick,c)],
            "hour"  : [(c-hthick,c),(c,c-size*0.25),(c+hthick,c)]
        }

        if self.show_second:
            self.create_polygon(self.coords["second"],tags="second")
        self.create_polygon(self.coords["minute"],tags="minute")
        self.create_polygon(self.coords["hour"],tags="hour")

        h,m,s = time.localtime()[3:6]
        h = h+self.timeshift

        self.__rotate(self._grad*(h%12*5+m/12),self.coords["hour"],"hour")
        self.__rotate(self._grad*m,self.coords["minute"],"minute")
        if self.show_second:
            self.__rotate(self._grad*s,self.coords["second"],"second")

        thread.start_new_thread(self.__tick, (h,m,s))

    def __rotate(self,direction,coordinates,tag):
        angle = cmath.exp(direction*1j)
        offset = complex(self.center[0], self.center[1])
        newxy = []
        for x,y in coordinates:
            v = angle * (complex(x,y) - offset) + offset
            newxy.append((v.real,v.imag))
        self.delete(tag)
        self.create_polygon(newxy,tags=tag)
        if tag:
            self.coords[tag] = newxy
        self.update()

    def __tick(self,h,m,s):
        while 1:
            s += 1
            if s % 60 == 0:
                s = 0
                m += 1
                self.__rotate(self._grad,self.coords["minute"],"minute")
                if m % 12 == 0:
                    h += 1
                    self.__rotate(self._grad,self.coords["hour"],"hour")
            if self.show_second:
                self.__rotate(self._grad,self.coords["second"],"second")
            time.sleep(1)

if __name__ == '__main__':
    root = Tk()

    '''
    f = Frame()
    f.pack(side=LEFT)

    Label(f,text="London").grid(row=0,column=0)
    Label(f,text="Paris").grid(row=0,column=1)    
    Label(f,text="NewYork").grid(row=2,column=0)
    Label(f,text="Peking").grid(row=2,column=1)
    TkAnalogClock(f,size=100,timeshift=-1).grid(row=1,column=0)
    TkAnalogClock(f,size=90,timeshift=-2,show_second=0).grid(row=1,column=1)
    TkAnalogClock(f,size=70,timeshift=1).grid(row=3,column=0)
    TkAnalogClock(f,size=50,timeshift=2,show_second=0).grid(row=3,column=1)
    
    TkAnalogClock(root,size=200,clockbg="red").pack(side=RIGHT)
    '''

    TkAnalogClock(root,size=500).pack()
    root.mainloop()
Benutzeravatar
sparrow
User
Beiträge: 4535
Registriert: Freitag 17. April 2009, 10:28

Versuch es doch erst einmal selbst, schau wo Fehler kommen und korrigiere diese.
Die Unterschiede sind ja nicht so gravierend.
BlackJack

Wobei der erste Schritt sein sollte einfach mal das 2to3.py-Skript anzuwenden.
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

Der 2to3 Script ist ja mal klasse !! kannte ich vorher nicht. Hat sofort geklappt ;)

@ sparrow: Bin erst Anfänger und kenne nicht alle angewendete Module. Hatte es am Anfang versucht, jedoch nicht hin bekommen.
BlackJack

@Evilsadness: Ich habe mir den Code selbst jetzt mal angeschaut: Der ist gefährlich. Nicht nur das er die Aufgabe der `mainloop()` übernimmt, was zumindest mal unschön ist, wird die `__tick()`-Methode, die einmal in der Sekunde die GUI aktualisiert, in einem eigenen Thread ausgeführt, was nicht erlaubt ist, weil Tk, wie die meisten anderen GUI-Toolkits auch, nicht thread-sicher ist. Das Verhalten des Programms ist undefiniert. Bis hin zu harten Programmabstürzen kann da alles mögliche mit der GUI passieren.

Wenn man das behebt hat die Uhr immer noch Probleme mit der Genauigkeit, sowohl was die Uhrzeit selbst angeht, als auch die Anzeige der Zeit.

`sleep()` schläft in der Regel nicht *genau* die angegebene Zeit. Das kann man bei einem Betriebssystem in dem mehrere Programme nebeneinander laufen und sich die selben Ressourcen teilen müssen auch nicht erwarten. Das heisst die Uhr wird mit der Zeit nachgehen, weil sich die Ungenauigkeiten mit der Zeit auf ein bemerkbares Mass aufaddieren.

Ähnliches Problem bei der Anzeige: Das Rechnen mit Gleitpunktzahlen ist ungenau. Wenn man also die Zeigerpositionen immer aus der letzten bekannten Position plus einem zeigerabhängigen Winkel berechnet, werden sich auch hier im Laufe der Zeit Ungenauigkeiten aggregieren die irgendwann auffallen werden.
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

Habe gerade gemerkt, dass es Probleme macht.
Ich wollte die Uhr in mein Programm hinzufügen, hat auch alles geklappt, jedoch kann ich das Programm ohne Fehlermeldung nicht mehr schließen.
Ich wollte mich garnicht zusehr mit der "Uhr" befassen, wäre halt nur schön, wenn ich eine Uhr in mein Programm angezeigt bekomme.

@BlackJack Vielen Dank auf jeden Fall!! Sehe öfters deine Kommentare in anderen Beiträgen und machst die Sache echt Super!!

Hättest du eventuell ein andere Uhren-Programm parat? oder kannst du das "schnell" mal änder, damit es läuft? Habe einiges zutun bis Silvester und wollte mich nicht die nächsten Tage damit beschäftigen, da ich ziemlich lange dafür brauchen würde, weil dies "zuhoch" für mich wäre.
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

keiner eine Uhr?
Benutzeravatar
pixewakb
User
Beiträge: 1413
Registriert: Sonntag 24. April 2011, 19:43

Du könntest mal den Python3-Quellcode posten, denn offenbar hast du den Code ja portiert bekommen. Möglicherweise findet sich dann jemand, der sich den Quellcode antun möchte. Nur am Rande: Warum hängst du so an der Uhr? Du könntest auch eine digitale Uhr 16:33:... einbauen, das dürfte weniger umständlich und recht fix zu programmieren sein.
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

ja klar wäre das einfacher. würde ich auch zur not nehmen, jedoch sieht mein "StartFenster" von meinem Programm leer aus und die Uhr würde da Perfekt hinpassen!

Ich will ja nicht unbedingt die Uhr, nur wenn irgendeiner vielleicht mal so eine programmiert hat, wäre es ja keine große Arbeit dies zu Posten. Vielleicht steht ja ein fertiges im Internet iwo, aber mit meiner Suche habe ich nur die eine Uhr gefunden.

Hier der "Schlecht Programmierte" Code in Python 3:

Code: Alles auswählen

from tkinter import *
import _thread
import cmath
import time

class TkAnalogClock(Canvas):

    _grad = 0.1047

    def __init__(self,master,size=100,clockbg='white',timeshift=0,show_second=1,**kw):
        Canvas.__init__(self,master,width=size,height=size,**kw)

        self.show_second = show_second
        self.clockbg = clockbg
        self.timeshift = timeshift
        self.size = size
        self.__setup()

    def __setup(self):
        size = self.size
        c = size/2
        s = c/10
        mthick = size/50
        hthick = size/40
        dot_min = 1
        dot_max = dot_min + 4
        ldot_min = dot_max + c/10

        self.center = [c,c]
        self.create_oval(1,1,size-1,size-1,fill=self.clockbg)
        self.create_oval(c-hthick,c-hthick,c+hthick,c+hthick,fill='black')

        n =  [(c-1,dot_min),(c-1,dot_max),(c+1,dot_max),(c+1,dot_min)]
        nl = [(c-1,ldot_min),(c-1,dot_min),(c+1,dot_min),(c+1,ldot_min)]
        for i in range(60):
            if i%5 == 0:
                self.create_polygon(nl)
                self.__rotate(self._grad*i,nl,None)
            else:
                if size > 99:
                    self.create_polygon(n)
                    self.__rotate(self._grad*i,n,None)

        self.coords = {
            "second": [(c-1,c),(c-1,c-size*0.4),(c+1,c-size*0.4),(c+1,c)],
            "minute": [(c-mthick,c),(c,c-size*0.4),(c+mthick,c)],
            "hour"  : [(c-hthick,c),(c,c-size*0.25),(c+hthick,c)]
        }

        if self.show_second:
            self.create_polygon(self.coords["second"],tags="second")
        self.create_polygon(self.coords["minute"],tags="minute")
        self.create_polygon(self.coords["hour"],tags="hour")

        h,m,s = time.localtime()[3:6]
        h = h+self.timeshift

        self.__rotate(self._grad*(h%12*5+m/12),self.coords["hour"],"hour")
        self.__rotate(self._grad*m,self.coords["minute"],"minute")
        if self.show_second:
            self.__rotate(self._grad*s,self.coords["second"],"second")

        _thread.start_new_thread(self.__tick, (h,m,s))

    def __rotate(self,direction,coordinates,tag):
        angle = cmath.exp(direction*1j)
        offset = complex(self.center[0], self.center[1])
        newxy = []
        for x,y in coordinates:
            v = angle * (complex(x,y) - offset) + offset
            newxy.append((v.real,v.imag))
        self.delete(tag)
        self.create_polygon(newxy,tags=tag)
        if tag:
            self.coords[tag] = newxy
        self.update()

    def __tick(self,h,m,s):
        while 1:
            s += 1
            if s % 60 == 0:
                s = 0
                m += 1
                self.__rotate(self._grad,self.coords["minute"],"minute")
                if m % 12 == 0:
                    h += 1
                    self.__rotate(self._grad,self.coords["hour"],"hour")
            if self.show_second:
                self.__rotate(self._grad,self.coords["second"],"second")
            time.sleep(1)

if __name__ == '__main__':
    root = Tk()
    TkAnalogClock(root,size=100).pack()
    root.mainloop()
Benutzeravatar
pixewakb
User
Beiträge: 1413
Registriert: Sonntag 24. April 2011, 19:43

Ok, jetzt können wir auch den Fehler reproduzieren.

Code: Alles auswählen

Unhandled exception in thread started by <bound method TkAnalogClock.__tick of <__main__.TkAnalogClock object at 0x03254810>>
Es ist ein Fehler im thread-Modul, wenn ich den Quelltext um die Uhrzeit richtig interpretiere und damit habe ich bisher noch nicht ausgiebig gearbeitet...

Ich meine - ohne es nachzuschauen -, dass man mit dem Modul nicht mehr arbeiten sollte (steht in meinem Buch als Modul der Wahl noch drin!) und es in Python 3 ein neues Modul (damit irre ich mich wohl) gibt. Außerdem meine ich, dass das Problem durch diese Zeile zustande kommt:

Code: Alles auswählen

_thread.start_new_thread(self.__tick, (h,m,s))
Ursache ist, dass du einen Thread startest, ohne dass er auch geschlossen wird, zumindest erkläre ich mir das gerade mit meinem sehr begrenzten Verständnis des Moduls so.

(Schaue ich mir morgen noch einmal in Ruhe an.)
BlackJack

@pixewakb: Argh, bitte. Ich hatte das alles schon weiter oben geschrieben. Tkinter und Threads lässt man sowieso ganz weil das in sich schon ein Fehler ist, zumindest wenn die Threads was an der GUI machen. Es gibt nicht erst seit Python 3 das `threading`-Modul sondern schon seit über 10 Jahren in Python 2. Das man `thread` nicht mehr verwenden soll ist seit dem bekannt. Das habe ich aber auch schon geschrieben.
Benutzeravatar
pixewakb
User
Beiträge: 1413
Registriert: Sonntag 24. April 2011, 19:43

Schau doch mal bitte in Weigend rein, nach dem ich gerade Python 3 lerne. Da wird das _thread-Modul vorgestellt - kein Hinweis, dass man es nicht nutzen sollte. Ich darf anmerken, dass das Wiki gerade nicht läuft, ein Hinweis auf der entsprechenden Seite der Buchbesprechung würde helfen.

Wie kann der Anfragende das Problem denn jetzt lösen? Mir würde nur try: ... except: ... als Workarround einfallen, was aber das Problem nicht löst. (Das Modul ist für mich auch Neuland...)
Benutzeravatar
sparrow
User
Beiträge: 4535
Registriert: Freitag 17. April 2009, 10:28

Der Anfragende kann das Problem lösen, indem er bereit ist Zeit in die Lösungsfindung zu investieren. BlackJack hat die Probleme ja bereits benannt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

pixewakb hat geschrieben:Schau doch mal bitte in Weigend rein, nach dem ich gerade Python 3 lerne. Da wird das _thread-Modul vorgestellt - kein Hinweis, dass man es nicht nutzen sollte. Ich darf anmerken, dass das Wiki gerade nicht läuft, ein Hinweis auf der entsprechenden Seite der Buchbesprechung würde helfen.
Ach und deswegen muss das richtig sein nur weil ein obsoletes Modul in einem Buch beschrieben wird? Interessant. Da könnt ich ja noch ganz viele andere Bücher nehmen und den Stuss von da als Wahrheit ansehen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
pixewakb
User
Beiträge: 1413
Registriert: Sonntag 24. April 2011, 19:43

Ich habe Informatik nicht studiert - momentan versuche ich im Python-Forum mitzulesen und mitzubekommen, wie ich Probleme lösen würde und wie die "Cracks" das machen.

Das Problem ist sicherlich, dass man als Newbie nicht abschätzen kann, ob ein Kapitel im Lehrbuch in Ordnung ist oder nicht. Ein Hinweis, z. B. im Wiki zu dem Buch wäre hilfreich und könnte Missverständnisse vermeiden helfen.
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Ein Modul das mit einem Unterstrich anfängt sieht ja schonmal sehr nach "nur für den internen Gebrauch"
aus.

Wie schon in hundert anderen Fäden hier geschrieben gibt es für wiederholende Aufgaben in tkinter
die Methode after:

Code: Alles auswählen

from tkinter import *
import cmath
import time

class TkAnalogClock(Canvas):
    [...]
    def _setup():
        [...]
        self._tick()

    def _tick():
        now = time.localtime()
        self.set_hour(now.tm_hour)
        self.set_minute(now.tm_min)
        self.set_second(now.tm_sec)
        self.after(1000,self._tick())
wie man den Zeiger für Stunde, Minute und Sekunde an die richtige Stelle setzt, sei
jedem als Übungsaufgabe selbst überlassen.

Grüße
Sirius
lunar

@pixewakb Glaube nicht blind, was irgendwo geschrieben steht. Verifiziere Informationen selbstständig aus anderen Quellen. Dazu muss man nicht studieren, dazu muss man nur lesen und selbstständig denken können. Das gilt im Übrigen nicht nur für Python, sondern für jedwedes Buch, jedwede Zeitung, etc. gleich welchen Inhalts.

Lese also in der offiziellen Dokumentation des "_thread"-Moduls nach:
This module provides low-level primitives for working with multiple threads (also called light-weight processes or tasks) — multiple threads of control sharing their global data space. For synchronization, simple locks (also called mutexes or binary semaphores) are provided. The threading module provides an easier to use and higher-level threading API built on top of this module.
Dazu kommt der Abschnitt über private Namen im Tutorial und die Namenskonventionen aus PEP 8, in denen recht klar erläutert wird, dass Namen mit führendem Unterstrich als „privat“ anzusehen sind und nach Möglichkeit nicht verwendet werden sollten.
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

Hab jetzt einfach ne digitale Uhr genommen.
ne kurze frage habe ich noch zu tkinter.
Gib es eine funktion oder irgendetwas, wo ich sehen kann, ob ein Fenster offen ist?
also zb.:

Code: Alles auswählen

if Hauptfenter = offen:
    #mache das und das
else:
    #mache was anderes
Vielen dank.
BlackJack

Ich habe mal eine Uhr gebastelt:

Code: Alles auswählen

#!/usr/bin/env python
"""
Simple analog clock widget for Tk UIs.

Works with Python 2 and 3.
"""
from __future__ import division
import cmath
import sys
try:
    import Tkinter as tk
except ImportError:
    import tkinter as tk
from datetime import datetime as DateTime, timedelta as TimeDelta
from math import pi as PI

__all__ = ['AnalogClock']
__author__ = "Marc 'BlackJack' Rintsch"
__version__ = '0.1'
__date__ = '2012-12-28'

if sys.version_info[0] > 2:
    xrange = range


class Hand(object):
    def __init__(self, clock, length, width, color, tick_factor=1):
        self.clock = clock
        self.length = length
        self.width = width
        self.color = color
        self.tick_factor = tick_factor
        self.tick = 0
        self.line = None
        self.draw()
    
    def draw(self):
        self.clock.delete(self.line)
        self.line = self.clock._create_rotated_line(
            0,
            self.length,
            self.tick * self.tick_factor,
            width=self.width,
            fill=self.color
        )
    
    def update(self, tick):
        if tick != self.tick:
            self.tick = tick
            self.draw()


class AnalogClock(tk.Canvas):
    def __init__(
        self, master, size=100, clock_bg='white', hands_color='black',
        seconds_hand_color='red', **kwargs
    ):
        tk.Canvas.__init__(self, master, width=size, height=size, **kwargs)
        self.size = size
        self.radius = size // 2
        
        tick_count = 60
        self.tick_coordinates = list()
        for i in xrange(tick_count):
            x_y = cmath.rect(1, (i - tick_count / 4) * PI / (tick_count / 2))
            self.tick_coordinates.append((x_y.real, x_y.imag))
        
        self.create_oval(1, 1, self.size - 1, self.size - 1, fill=clock_bg)
        for i in xrange(tick_count):
            self._create_rotated_line(
                self.radius - self.radius // 10,
                self.radius - self.radius // 50,
                i,
                width=(self.radius // 50 if i % 5 == 0 else self.radius // 100)
            )
        self.hands = [
            Hand(self, self.radius - 2, self.size // 25, hands_color),
            Hand(self, self.radius // 2, self.size // 25, hands_color, 5),
            Hand(self, self.radius - 2, self.size // 100, seconds_hand_color)
        ]
        self.update_hands()
    
    def _create_rotated_line(
        self, distance_a, distance_b, tick_index, **kwargs
    ):
        x, y = self.tick_coordinates[tick_index % len(self.tick_coordinates)]
        return self.create_line(
            x * distance_a + self.radius,
            y * distance_a + self.radius,
            x * distance_b + self.radius,
            y * distance_b + self.radius,
            **kwargs
        )
    
    def update_hands(self):
        time = DateTime.now()
        for hand, value in zip(
            self.hands, (time.minute, time.hour, time.second)
        ):
            hand.update(value)
    
    def run(self):
        self.update_hands()
        self.after(500, self.run)


def main():
    root = tk.Tk()
    for i in xrange(5):
        clock = AnalogClock(root, (i + 1) * 100)
        clock.pack(side=tk.LEFT)
        clock.run()
    root.mainloop()


if __name__ == '__main__':
    main()
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

TOP!
Antworten