Kirjautuminen

Haku

Tehtävät

Keskustelu: Yleinen keskustelu: Koodin jako eri luokkin

Sivun loppuun

kayttaja-15306 [23.12.2020 13:44:46]

#

Tervehdys,
yritän opetella luokkajakoa oop-tyylillä yksinkertaiselle sovellukselle, joka laskee käteen jäävän tulon veron pidätyksen jälkeen.
Ohjelmassa on kolme luokkaa: gui, income ja tax ja luokkien sisällä yksinkertaiset funktiot käteen jäävän summan laskemiseksi.

 def main():
    program = gui()
    myincome = income()
    mytax = tax()
    program.window.mainloop()

class gui:
    def __init__(self,*args, **kwargs):
        window = tk.Tk()
        window.config(bg='#c0c0c0')
        window.columnconfigure(0, weight=1

Tätä seuraa gui-luokka ja sen määrittelyt.

Saan seuraavan virheilmoituksen:
Virheilmoitus:

File "C:\Users\Käyttäjä\AppData\Local\Programs\Python\Python38\BASIC_GUIS\Net_incomeClass version.py", line 115, in create_main_frames
self.frm_main = tk.Frame(window)

NameError: name 'window' is not defined

Kysymykset:

1. mikä widgetti pitäisi määritellä ensimmäiseksi, esim. parent tai joskin vastaava, että siihen voisi upottaa ensimmäisen framin, jonka päälle muut widgetit tulevat ja mihin se pitäisi määrittää, jotta virheilmoitus poistuisi?

2. pitääkö muiden luokkien sisällä kutsua erikseen niissä määriteltyjä funktiota samoin kuin gui-luokassa create_widgets()?

Tarkoitus on yrittää opetella luokkajakoa useamman luokan sisältävissä ohjelmissa. Käytetty kieli on python.

Metabolix [23.12.2020 16:42:50]

#

Virheilmoitus johtuu siitä, että koodissasi window-muuttuja on __init__-funktion sisäinen muuttuja. Selvästi haluat käyttää sitä muuallakin, joten siitä pitäisi tehdä jäsenmuuttuja eli self.window.

Olio-ohjelmointi ei vaikuta järkevältä lähestymistavalta verotuksen laskemiseen ainakaan tuollaisessa muodossa, että tulo on luokka ja vero on luokka. Kannattaa varmaan miettiä ohjelman rakennetta vielä. Jos tarkoitus on laskea veroprosentin (ja työeläkemaksujen) perusteella käteen jäävä summa, tähän riittää yksinkertainen kertolasku: netto = brutto * (1 - vero - tyel), missä vero ja tyel sisältävät oikeat kertoimet (esim. veroprosentti 25% = kerroin 0.25, kuten prosentit kannattaa yleensä ohjelmissa käsitellä). Tietysti nämä arvot voi laittaa johonkin olioon tai luokkaan, mutta ei ole syytä tehdä asiasta turhan monimutkaista. Toisaalta koko verotuksen kaikki kiemurat käsittävä ohjelma sisältäisi varmasti jotain paljon monipuolisempaa kuin luokat income ja tax.

kayttaja-15306 [23.12.2020 17:28:58]

#

Tervehdys,

kiitos vastauksesta.

Tämä on vain yksinkertaisin sovellus, jonka keksin, jossa voisi nähdä ja ymmärtää konkreettisesti, miten luokkajako menee yksinkertaisella esimerkilla.

Ongelmana näyttää vain olevan se, etten osaa soveltaa saamiani ohjeita, ellen näe konkreettisesti, mihin se jäsenfuntkio pitäisi sijoitaa, jotta pääsisi tutkimaan, miten se menee käytännössä.

The Alchemist [23.12.2020 18:44:22]

#

Eipä mekään voida refaktoroida (järjestellä) sellaista koodia paremmaksi, mitä et ole ensin kirjoittanut ja laittanut esille. Muna vai kana? Kuten Metabolix äsken sanoi, niin jo sinun perus luokkajakosi on perseestä. Nyt sinä olet täällä paradoksaalisesti kysymässä, että miten voisit käyttää oikein väärin tehtyjä luokkia...

Koodaa vaikka se taskulaskin nyt aluksi; siinä on oikeasti yksinkertainen ohjelma. Verolaskuri ei todellakaan ole sitä.

The Alchemist [23.12.2020 19:56:15]

#

Tein nyt huvikseni taskulaskimen, kun en jostain syystä ole koskaan elämässäni sellaista vielä koodannut. (No okei, yhden kerran koulussa tuli käänteiselle puolalaiselle notaatiolle tehtyä yksi...)

Tämä on täydellinen esimerkki PyQt 5 -kirjastoa käyttäen. Jos sinulla on koneella kyseinen kirjasto asennettuna, niin koodin pitäisi toimia copy-pastella.

Tunnin työ, koodattu maha-asennosta sängyssä makoillessa... Ainakaan pariin vuoteen en ole PyQt:lla tehnyt mitään...

edit: poistin koodin tästä ja siirsin sen omaan ketjuunsa.

Metabolix [23.12.2020 20:13:03]

#

ma08 kirjoitti:

Tämä on vain yksinkertaisin sovellus, jonka keksin, jossa voisi nähdä ja ymmärtää konkreettisesti, miten luokkajako menee yksinkertaisella esimerkilla.

Minusta tässä nyt yksi ongelma on se, että yrität opetella samaan aikaan kahta asiaa: luokkia ja Tk:n käyttöä. Vielä yksinkertaisempi sovellus olisi sellainen, jossa ei käytetä Tk:ta vaan tehdään tekstipohjainen käyttöliittymä (input ja print). Opettele ensin Pythonin perusasiat eli luokat ja funktiot jne. ja yritä vasta sitten tehdä graafista käyttöliittymää niillä. Samalla opit tärkeän asian: kun teet koodisi fiksusti, voit tehdä ohjelman ensin ilman Tk:ta ja voit lisätä käyttöliittymän sitten myöhemmin.

Jos luokkien osalta tavoitteesi on nyt vain nähdä, ”miten se menee käytännössä”, voit lukea esimerkkejä vaikka Pythonin dokumentaatiosta.

On tärkeää myös opetella muuttamaan koodista olennaisia osia. Esimerkiksi nyt ihan oikein tunnistit ongelmakohdan: ”mihin se [widget] pitäisi määrittää, jotta virheilmoitus poistuisi”. Ongelman selvittämisessä voi lähteä liikkeelle minimaalisesta koodista, jossa on sama vika:

class X:
  def __init__(self):
    widget = 1
    self.funktio()

  def funktio(self):
    print(widget) # NameError: name 'widget' is not defined

X()

Kun tämän koodin korjaa (neuvoni mukaisesti tekemällä muuttujan self.widget), samaa korjausta voi soveltaa myös alkuperäiseen koodiin, jossa sama ongelma ilmeni.

The Alchemist [23.12.2020 20:26:10]

#

Omasta mielestäni graafisen käyttöliittymän kasaaminen ei ole oleellisesti vaikeampaa kuin tekstihelvettiohjelmien tekokaan.

Samat suunnittelun peruspilarit pätevät myös graafisen käyttöliittymän omaavan sovelluksen tekemiseen: sovelluksen logiikka eli suorittava koodi ja "tulostus" eli tiedon näyttäminen ruudulla ihmiselle luettavassa muodossa on pidettävä erillään.

Tekstipohjaista käyttöliittymää väsätessä menee aika yhtälailla sen tekstisotkun viilaukseen, ja se on vieläkin turhempaa kuin widgettien kanssa tuskailu.

kayttaja-15306 [24.12.2020 08:15:27]

#

Tervehdys,

kiitokset vastauksista.

Ohessa koko koodiräpellys.

Sain sen self.window asian oivallettua lopulta.

Nyt ongelmana on miten kytketään luokan income funktio käyttöliittymän painikkeeseen button2, jotta tämä virheilmoitus poistuisi:

Virheilmoitus:

File ... line 202, in create_buttons
    self.button2.bind("<Return>", self.income.cross_income)
AttributeError: 'gui' object has no attribute 'income'
from __future__ import division
import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext
from PIL import Image, ImageDraw
from bitmap import BitMap
from tkinter import messagebox as mb


def main():

    program = gui()
    myincome = income()
    mytax = tax()
    program.window.mainloop()

class gui:


    def __init__(self,*args, **kwargs):

        self.window = tk.Tk()
        self.window.config(bg='#c0c0c0')
        self.window.columnconfigure(0, weight=1)
        self.window.title('Netincome Calculator')

        """ Modified Style widget """

        style = ttk.Style()
        style.theme_use('alt')
        style.configure('TButton', background='#666565', foreground='white',
        font=('Helvetica New', 14), padx=20, pady=10, ipadx=40,
        borderwidth=5,relief='ridge')
        style.configure('TEntry', borderwidth=5,background='gray90')
        style.configure('TLabelframe', background='#c0c0c0',
        borderwidth=2, font=('Roboto', 13, 'bold'),
        foreground='blue2')
        style.configure('TLabel', background='#c0c0c0',
        anchor='n', font=('Inconsolta', 13, 'bold'), foreground='black')

        style.configure('Company.TLabel', foreground='blue2',
        font=('Roboto', 14, 'underline italic'), background='#c0c0c0')

        style.configure('Dr_left.TLabel', foreground='blue2',
        font=('Roboto', 20, 'underline italic'), background='#c0c0c0')

        style.configure('Frm_left.TLabel', foreground='white',
        font=('Roboto', 14, 'italic'), background='#4267b2')

        style.configure('Frm_right.TLabel', foreground='blue2',
        font=('Roboto', 14, 'italic'), background='gray80')


        style.map('TButton',
        foreground=[('disabled', 'yellow'),
                    ('pressed', '#c51162'),
                    ('active', 'blue')],
        background=[('disabled', 'magenta'),
                    ('pressed', '!focus', 'cyan'),
                    ('active', 'gray90')],
        highlightcolor=[('focus', 'green'),
                        ('!focus', 'red')]),
        relief=[('pressed', 'groove'),
                ('!pressed', 'ridge')]



        self.create_toplevel()
        self.create_toplevel_frames()
        self.create_main_frames()
        self.create_entry1()
        self.create_label_main()
        self.create_label_frame()
        self.create_text()
        self.create_labels()
        self.create_buttons()


    def create_toplevel(self):

        self.top = tk.Toplevel()
        self.top.configure(background='#c0c0c0')
        self.top.title('Business Card')
        self.top.columnconfigure(0, weight=1)
        self.top.grid_columnconfigure(0, weight=1)

    def create_toplevel_frames(self):

        """ Toplevel Frames """

        self.frm_main_top = tk.Frame(self.top)
        self.frm_main_top.grid(row=1, column=0, padx=50, pady=10)
        self.frm_main_top.configure(bg='#c0c0c0', borderwidth=5,
        relief='raised')
        self.frm_main_top.columnconfigure(0, weight=1)
        self.frm_main_top.grid_columnconfigure(0, weight=1)

        self.frm_left_top = tk.Frame(self.frm_main_top, width=420, height=270)
        self.frm_left_top.grid(row=0, column=0, sticky='ewns')
        self.frm_left_top.configure(bg='#4267b2')
        self.frm_left_top.grid_columnconfigure(0, weight=1)
        self.frm_left_top.grid_propagate(0)

        self.frm_right_top = tk.Frame(self.frm_main_top, width=420, height=270)
        self.frm_right_top.grid(row=0, column=1, sticky='ewnw')
        self.frm_right_top.configure(bg='gray80')
        self.frm_right_top.grid_columnconfigure(0, weight=1)
        self.frm_right_top.grid_propagate(0)

    def create_main_frames(self):

        """ Frames for main GUI """


        self.frm_main = tk.Frame(self.window)
        self.frm_main.grid(row=1, column=0, padx=50)
        self.frm_main.configure(bg='#c0c0c0', borderwidth=5)
        self.frm_main.columnconfigure(0, weight=1)
        self.frm_main.grid_columnconfigure(0, weight=1)

        self.frm_left = tk.Frame(self.frm_main, width=420, height=270)
        self.frm_left.grid(row=0, column=0, sticky='ewns')
        self.frm_left.configure(bg='gray80')
        self.frm_left.grid_columnconfigure(0, weight=1)

        self.frm_right = tk.Frame(self.frm_main, width=420, height=270)
        self.frm_right.grid(row=0, column=1, sticky='ewnw')
        self.frm_right.configure(bg='#4267b2')
        self.frm_right.grid_columnconfigure(0, weight=1)

        self.frm_middle = tk.Frame(self.window)
        self.frm_middle.grid(row=2, column=0)
        self.frm_middle.configure(bg='#c0c0c0', borderwidth=10)
        self.frm_middle.grid_columnconfigure(0, weight=1)
        self.frm_middle.grid_rowconfigure(0, weight=1)

    def create_entry1(self):

        self.frm_ent = tk.Frame(self.frm_middle)
        self.frm_ent.grid(row=1, column=0, padx=0, pady=0)
        self.frm_ent.configure(background='#c0c0c0')
        self.frm_ent.grid(row=1, column=0, padx=0, pady=0)

    def create_label_main(self):

        self.lab_widget = ttk.Label(self.frm_middle,text='Important Buttons',
        foreground='blue2', font=('Courier New', 13, 'italic bold'), underline='0')

    def create_label_frame(self):

        self.frm_btn = ttk.LabelFrame(self.frm_middle, style='TLabelframe',
        labelanchor='n', labelwidget=self.lab_widget)
        self.frm_btn.grid(row=2, column=0, padx=0, pady=5)

    def create_text(self):


        """ Text widget """

        self.txt=scrolledtext.ScrolledText(self.window, width=60, height=10,
        wrap='word', spacing1=1, spacing2=1, spacing3=1)
        self.txt.grid(row=1, column=0, padx=15, pady=15)
        self.txt.config(bg='light blue', font=('Verdana', 14),
        borderwidth=2, relief='ridge', foreground='black', state='normal')
        self.txt.grid_columnconfigure(0, weight=1)
        self.txt.grid_rowconfigure(0, weight=1)
        self.txt.grid_propagate(0)

        self.txt.insert('end', 'Welcome to Net Income Program!')
        self.txt.insert('end','\n\nThis program counts net icome for your income.')
        self.txt.tag_add('checker', '1.11', '1.21')
        self.txt.tag_add('checker', '3.20', '3.29')
        self.txt.tag_add('checker', '5.13', '5.32')
        self.txt.tag_configure('checker', background='purple',
        foreground='white', font=('Verdana', 14, 'bold italic'), underline='on')
        self.txt.tag_configure('dr', font=('Verdana', 14))

    def create_labels(self):


        """ Labels """

        self.label = ttk.Label(self.window, text='Number of units')
        self.label.grid(row=0, column=0, sticky='n')


    def create_buttons(self):

        self.button1 = ttk.Button(self.frm_btn, style='TButton',
        text='Close', underline=0)
        self.button1.grid(row=0, column=2, padx=10,
        pady=5, ipadx=50)
        self.button1.focus_force()
        self.button1.bind("<Return>", self.Click)
        self.button1.bind("<Button-1>", self.Click)

        self.button2 = ttk.Button(self.frm_btn, style='TButton',
        text='Net income', underline=0)
        self.button2.grid(row=0, column=0, padx=10, pady=5, ipadx=50)
        self.button2.focus_force()
        self.button2.bind("<Return>", self.income.cross_income)
        self.button2.bind("<Button-1>", self.net_income)

        self.button1_top = ttk.Button(self.top, style='TButton',
        text='Close', underline=0)
        self.button1_top.grid(row=2, column=0, padx=10,
        pady=5, ipadx=70)
        self.button1_top.focus_force()
        self.button1_top.bind("<Return>", self.Click)
        self.button1_top.bind("<Button-1>", self.Click)

    def Click(self, event):
        self.window.destroy()



class income:

    def __init__(self):

        def cross_income (self, event):

            try:

                number_of_units = float(ent.get())
                fee = 8
                cross_income = float(checking_fee * number_of_units)

                ent.delete(0, 'end')
                txt.delete(1.0, 'end')
                txt.insert('end', 'Total fee is ')
                txt.insert('end', round(cross_income, 2))
                txt.insert('end', ' euro.')

                return cross_income

            except ValueError:
                txt.delete(1.0, 'end')
                txt.insert('end', 'Give a number of units to calculate first.')

class tax:

    def __init__(self):

        def net_income(self, event):

            try:

                rate= 0.2625
                x = cross_income(self)
                net_income = (x - (x * rate))

                txt.insert('end', '\nYour net icome is ')
                txt.insert('end', round(net_income, 2))
                txt.insert('end', ' euro.')

                return net_income

            except ValueError:
                txt.delete(1.0, 'end')
                txt.insert('end', 'Give a number of units to calculate first.')

if __name__ == "__main__":
    main()

Sivun alkuun

Vastaus

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

Tietoa sivustosta