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\
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.
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.
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ä.
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ä.
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.
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.
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.
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()
Aihe on jo aika vanha, joten et voi enää vastata siihen.