Уведомления

Группа в Telegram: @pythonsu

#1 Сен. 2, 2017 19:29:52

Feelgood
Зарегистрирован: 2016-08-04
Сообщения: 64
Репутация: +  0  -
Профиль   Отправить e-mail  

Вызов из другого класса

Класс Child функция insert_data. Как правильно обратится к переменной с и сonn чтоб выполнялось добавление данных в БД.

 import tkinter as tk
from tkinter import ttk
import sqlite3
class Main(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        self.init_main()
    def init_main(self):
        toolbar = tk.Frame(bg='#d7d8e0', bd=2)
        toolbar.pack(side=tk.TOP, fill=tk.X)
        self.add_img = tk.PhotoImage(file="add.gif")
        btn_open_dialog = tk.Button(toolbar, text='Добавить позицию', command=self.open_dialog, bg='#d7d8e0', bd=0,
                                    compound=tk.TOP, image=self.add_img)
        btn_open_dialog.pack(side=tk.LEFT)
        self.tree = ttk.Treeview(self, columns=('ID', 'description', 'costs', 'total', 'date'),
                            height=15, show='headings')
        self.tree.column("ID", width=30, anchor=tk.CENTER)
        self.tree.column("description", width=250, anchor=tk.CENTER)
        self.tree.column("costs", width=150, anchor=tk.CENTER)
        self.tree.column("total", width=100, anchor=tk.CENTER)
        self.tree.column("date", width=100, anchor=tk.CENTER)
        self.tree.heading("ID", text='ID')
        self.tree.heading("description", text='Наименование')
        self.tree.heading("costs", text='Статья дохода/расхода')
        self.tree.heading("total", text='Сумма')
        self.tree.heading("date", text='Дата')
        self.tree.pack(side=tk.TOP, fill=tk.X)
        self.viewing_records()
    def run_query(self):
        conn = sqlite3.connect('finance.db')
        c = conn.cursor()
        c.execute(
            '''CREATE TABLE IF NOT EXISTS finance (id integer primary key, description text, costs text, total real,
            date current_timestamp)''')
        c.execute('''select * from finance''')
        conn.commit()
        return c
    def viewing_records(self):
        c = self.run_query()
        for row in c.fetchall():
            self.tree.insert('', 'end', values=row)
    def open_dialog(self):
        Child()
class Child(tk.Toplevel):
    def __init__(self):
        super().__init__(root)
        self.init_child()
    def init_child(self):
        self.title('Добавить доходы/расходы')
        self.geometry('400x220+400+300')
        self.resizable(False, False)
        label_description = tk.Label(self, text='Наименование:')
        label_description.place(x=50, y=50)
        label_select = tk.Label(self, text='Статья дохода/расхода:')
        label_select.place(x=50, y=80)
        label_sum = tk.Label(self, text='Сумма:')
        label_sum.place(x=50, y=110)
        self.entry_description = ttk.Entry(self)
        self.entry_description.place(x=200, y=50)
        self.entry_money = ttk.Entry(self)
        self.entry_money.place(x=200, y=110)
        self.combobox = ttk.Combobox(self, values=[u"Доход", u"Расход"])
        self.combobox.current(0)
        self.combobox.place(x=200, y=80)
        btn_cancel = ttk.Button(self, text='Отмена', command=self.destroy)
        btn_cancel.place(x=300, y=170)
        btn_ok = ttk.Button(self, text='OK')
        btn_ok.place(x=220, y=170)
        btn_ok.bind('<Button-1>', lambda event: self.insert_data())
        self.grab_set()
        self.focus_set()
        self.wait_window()
    def insert_data(self):
        c.execute('''INSERT INTO finance(description, costs, total) VALUES (?, ?, ?)''',
                  (self.entry_description.get(), self.combobox.get(), self.entry_money.get()))
        conn.commit()
        '''conn.close()'''
        self.destroy()
if __name__ == "__main__":
    root = tk.Tk()
    app = Main(root)
    app.pack()
    root.title("Домашние финансы")
    root.geometry("650x450+300+200")
    root.resizable(False, False)
    root.mainloop()

Офлайн

#2 Сен. 3, 2017 15:49:40

Feelgood
Зарегистрирован: 2016-08-04
Сообщения: 64
Репутация: +  0  -
Профиль   Отправить e-mail  

Вызов из другого класса

Ребят помогите! Может что-то нужно уточнить?

Офлайн

#3 Сен. 4, 2017 11:10:37

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10003
Репутация: +  857  -
Профиль   Отправить e-mail  

Вызов из другого класса

Надо сделать класс для управления базой данных. Потом ты его снаружи инициализируешь и инициализированный объект подаёшь в __init__ класса Main, где он сохраняется внутри класса. Когда создаёшь дочернее окно, в __init__ класса Child ты точно так же подаёшь этот сохранённый объект, где он сохраняется внутри класса.

Классы не должны знать друг про друга, но каждый класс для работы с базой данных принимает её снаружи, уже проинициализированную. Это мы делаем шаблон ООП интерфейс, где принимается какой-то интерфейс снаружи и внутри класса выполняются действия с этим интерфейсом, но при этом содержимое этого интерфейса нам неизвестно. Так ты можешь подавать любые базы данных снаружи (SQLite, MySQL, PostgreSQL), при этом что это за база, класс не знает, но может из неё что-то читать и что-то в неё сохранять через заданные методы интерфейса.

Это пример

  
>>> class Doing:
...     def __init__(self, intobj):
...         self.obj = intobj
...     def do_action(self):
...         self.obj.do()
...     def repeat_action(self):
...         self.obj.repeat()
... 
>>> class Cat:
...     def do(self):
...         print('meow')
...     def repeat(self):
...         print('meow meow meow')
... 
>>> class Dog:
...     def do(self):
...         print('woof')
...     def repeat(self):
...         print('woof woof woof')
... 
>>> class DB:
...     def do(self):
...         print('insert a into table')
...     def repeat(self):
...         print('insert a, a into table')
... 
>>> def do(obj):
...     for i in range(5):
...         obj.do_action()
...     obj.repeat_action()
... 
>>> do(Doing(Cat()))
meow
meow
meow
meow
meow
meow meow meow
>>> do(Doing(Dog()))
woof
woof
woof
woof
woof
woof woof woof
>>> do(Doing(DB()))
insert a into table
insert a into table
insert a into table
insert a into table
insert a into table
insert a, a into table
>>>



Отредактировано py.user.next (Сен. 4, 2017 11:28:40)

Офлайн

#4 Сен. 5, 2017 22:41:40

Feelgood
Зарегистрирован: 2016-08-04
Сообщения: 64
Репутация: +  0  -
Профиль   Отправить e-mail  

Вызов из другого класса

Огромное спасибо! Общая смысловая линия понятна, но вот с реализацией проблема…

Офлайн

#5 Сен. 6, 2017 02:02:16

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10003
Репутация: +  857  -
Профиль   Отправить e-mail  

Вызов из другого класса

Вытаскивай создание базы SQLite наружу из класса окна.

Feelgood
 conn = sqlite3.connect('finance.db')
Вот это должно происходить в классе DB, который ты должен сделать.

Feelgood
 app = Main(root)
Потом ты снаружи создаёшь базу и подаёшь её (уже готовую) в конструктор окна.
 app = Main(root, DB('finance.db'))

А окно внутри себя её просто сохраняет.
Feelgood
  
class Main(tk.Frame):
    def __init__(self, root, db):
        super().__init__(root)
        self.db = db
        self.init_main()

Потом ты просто у этой переменной self.db вызываешь методы cursor(), execute(), commit(). При этом это не прямые методы базы (базы ведь бывают разные и эти методы могут по-разному называться в разных базах), а это просто методы класса DB, который при их вызове вызывает прямые методы базы, которая внутри него открыта.
То есть для того же метода execute() можно сделать свой язык запросов, более удобный и универсальный, чем в SQLite или любой другой базе, которая может быть подана таким образом.


Потом при создании окна Child, во-первых, ты должен само окно Child прикрепить к Main (если оно дочернее, оно должно быть прикреклено к родительскому), а во-вторых, точно так же подать в дочернее окно базу из окна Main. Мы не используем базу из родительского окна напрямую через него, потому что не факт, что такая конструкция будет всегда сохраняться (сегодня это дочернее окно прикреплено к этому родительскому, завтра - к другому; сегодня в родительском окне база прикреплена, завтра - её убрали оттуда), из дочернего окна внутрь родительского мы не лазим (прямо такое правило себе заведи). Когда создаётся дочернее окно, оно не должно знать внутреннее устройство своего родительского окна, поэтому к базе внутри родителя мы не обращаемся, а передаём эту базу в конструктор дочернего окна, как будто это просто созданная где-то снаружи база, считаем её вообще отдельной базой. То есть суть в том, что окна не знают, где и как создавалась база, которая подаётся снаружи; они только знают, как с этой поданной снаружи базой обращаться через её методы. При этом в данном случае эти базы совпадают, а вообще всё делается так (готовится заранее), будто эти базы разные. Меньше привязок должно быть к условиям каким-то (где какое окно, какое окно под каким; где базы одинаковые, а где разные, - это всё ненужные ограничения, которые сковывают программу и делают изменения невозможными).



Отредактировано py.user.next (Сен. 6, 2017 02:23:39)

Офлайн

#6 Ноя. 21, 2017 21:59:33

Feelgood
Зарегистрирован: 2016-08-04
Сообщения: 64
Репутация: +  0  -
Профиль   Отправить e-mail  

Вызов из другого класса

py.user.next
Простите за наглость, я самоучка и учусь “гавнокодить”
Очень прошу показать реализацию моей проблемы!
Как говорится: “лучше один раз увидеть, чем тысячу раз услышать”)

Офлайн

#7 Ноя. 22, 2017 10:19:52

vic57
Зарегистрирован: 2015-07-07
Сообщения: 913
Репутация: +  127  -
Профиль  

Вызов из другого класса

примерно так

 import tkinter as tk
import sqlite3
class DB():
    def __init__(self,dbname):
        self.name = dbname
        self.data = ''
    def query(self,q_str):
        try:
            conn = sqlite3.connect(self.name)
            c = conn.cursor()
            c.execute(q_str)
            self.data = c.fetchall()
            conn.commit()
            conn.close()
        except Exception as e:
            return str(e)
        else:
            s = ''
            for row in self.data: s += '\t'.join([str(i) for i in row]) +'\n'
            return s
                
                
class W(tk.Frame):
    def __init__(self,parent):
        tk.Frame.__init__(self,parent)
        self.db = DB('test.db')
        self.pack(fill=tk.BOTH,expand=True)
        self.text = tk.Text(self)
        self.text.pack(fill=tk.BOTH,expand=True)
        frame = tk.Frame(self,bg="#ccc")
        self.entry = tk.Entry(frame,width = 64)
        self.entry.bind('<Return>',self.query)
        self.entry.grid(row=0,column=0)
        self.btn = tk.Button(frame,text = 'Query')
        self.btn.grid(row=0,column=1)
        self.btn.bind('<Button-1>',self.query)
        frame.pack(fill=tk.BOTH,expand=True)
        
    def query(self,event):
        q = self.entry.get()
        a = self.db.query(q)
        self.text.delete(1.0,tk.END)
        self.text.insert(1.0,a)
if __name__ == "__main__":
    root = tk.Tk()
    app = W(root)
    root.geometry("600x400+100+100")
    root.mainloop()

Офлайн

#8 Ноя. 22, 2017 10:33:25

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10003
Репутация: +  857  -
Профиль   Отправить e-mail  

Вызов из другого класса

Feelgood
Очень прошу показать реализацию моей проблемы!
Вот без самих окон
  
import sqlite3
 
 
class SQLite:
    def __init__(self, filename):
        self.conn = sqlite3.connect(filename)
        self.cur = self.conn.cursor()
 
    def execute(self, cmd, subvars=None):
        if subvars is None:
            self.cur.execute(cmd)
        else:
            newcmd = translate_universal_to_sqlite3(cmd)
            self.cur.execute(newcmd, subvars)
        self.conn.commit()
 
    def __del__(self):
        self.conn.close()
 
 
class DB:
    def __init__(self, db):
        self.db = db
 
    def execute(self, cmd):
        self.db.execute(cmd)
 
 
def main():
    root = tk.Tk()
    db = DB(SQLite('file.sqlite'))
    win1 = Main(root, db)
    win2 = Child(win1, db)
    root.mainloop()
 
if __name__ == '__main__':
    main()
Почему есть DB и SQLite. DB - это интерфейс, который связан с какой-то базой. Окна не знают, с какой базой связан DB; они только знают, что у него есть метод execute() и используют только его. SQLite - это уже адаптер для SQLite-баз, у которого есть определённые методы и определённый синтаксис команд в упрощённом виде. А вот адаптер для SQLite-баз уже взаимодействует с библиотечным модулем питона для открытия SQLite-баз, у которого есть свои особенности использования, довольно неудобные. То есть к этому адаптеру можно подключить совсем другой модуль для работы с SQLite-базами, при этом работа с самим адаптером не изменится*.
Таким образом, имея интерфейс DB, ты не заморачиваешь окна определённой базой данных и сможешь в любой момент сменить её на MySQL или на PostgreSQL. При этом окна менять не надо будет вообще, так как они не знают этих особенностей, работают только с интерфейсом DB.
Таким образом, имея адаптер SQLite, ты не заморачиваешься с нюансами библиотечного модуля и не зависишь от него. Ты можешь его заменить на любой другой удобный модуль. При этом менять что-то в интерфейсе DB тоже не нужно, потому что он не знает про внутреннее устройство адаптера SQLite, а просто использует его методы.


* Сам адаптер служит инструментом для упрощения взаимодействия с модулем питона. Я его ввёл, чтобы мы могли иметь одинаковый синтаксис для подстановки переменных при выполнении execute(). В разных модулях питона для работы с базами, даже с одними и теми же базами, часто используется разные синтаксис подстановки переменных в execute(). Одни модули используют вопросики, другие модули используют %s, третьи могут использовать %s и %d. Вот для сведения всех этих трёх способов к одному мы и используем адаптер, который внутри должен универсальный способ записи подстановок переводить в конкретный для того модуля, который подключён к адаптеру в данный момент.



Отредактировано py.user.next (Ноя. 22, 2017 11:11:14)

Офлайн

#9 Дек. 7, 2017 22:27:14

Feelgood
Зарегистрирован: 2016-08-04
Сообщения: 64
Репутация: +  0  -
Профиль   Отправить e-mail  

Вызов из другого класса

py.user.next, можете построчно рассказать что делает эта функция, так как есть пробелы
Спасибо

 def execute(self, cmd, subvars=None):
        if subvars is None:
            self.cur.execute(cmd)
        else:
            newcmd = translate_universal_to_sqlite3(cmd)
            self.cur.execute(newcmd, subvars)
        self.conn.commit()

Отредактировано Feelgood (Дек. 7, 2017 22:27:47)

Офлайн

#10 Дек. 7, 2017 22:38:00

Feelgood
Зарегистрирован: 2016-08-04
Сообщения: 64
Репутация: +  0  -
Профиль   Отправить e-mail  

Вызов из другого класса

Вот моя первая версия приложения и всё работало, верю что гавно код, но оставалось было решить только одну проблему, отображение введенной информации сразу после нажатие кнопки ОК, а не после закрытия и вновь открытия приложения.

 import tkinter as tk
from tkinter import ttk
import sqlite3
conn = sqlite3.connect('finance.db')
c = conn.cursor()
c.execute(
    '''CREATE TABLE IF NOT EXISTS finance (id integer primary key, description text, costs text, total real, date current_timestamp)''')
c.execute('''select * from finance''')
class Main:
    def __init__(self, master):
        self.master = master
        self.master.title('Домашние финансы')
        self.master.geometry('650x450+300+200')
        self.master.resizable(False, False)
        toolbar = tk.Frame(master)
        toolbar.pack(side=tk.TOP, fill=tk.X)
        toolbar.config(bg='#d7d8e0', bd=2)
        self.add_img = tk.PhotoImage(file="add.gif")
        self.btnOpenDialog = tk.Button(toolbar, text='Добавить позицию', command=self.open_dialog, image=self.add_img)
        self.btnOpenDialog.pack(side=tk.LEFT)
        self.btnOpenDialog.config(bg='#d7d8e0', bd=0, compound=tk.TOP)
        self.tree = ttk.Treeview(self.master, columns=('ID', 'description', 'costs', 'total', 'date'),
                                 height=15, show='headings')
        self.tree.column("ID", width=30, anchor=tk.CENTER)
        self.tree.column("description", width=250, anchor=tk.CENTER)
        self.tree.column("costs", width=150, anchor=tk.CENTER)
        self.tree.column("total", width=100, anchor=tk.CENTER)
        self.tree.column("date", width=100, anchor=tk.CENTER)
        self.tree.heading("ID", text='ID')
        self.tree.heading("description", text='Наименование')
        self.tree.heading("costs", text='Статья дохода/расхода')
        self.tree.heading("total", text='Сумма')
        self.tree.heading("date", text='Дата')
        for row in c.fetchall():
            self.tree.insert('', 'end', values=row)
        self.tree.yview()
        self.tree.pack(side=tk.TOP, fill=tk.X)
        self.master.mainloop()
    def open_dialog(self):
        Child(self.master)
class Child:
    def __init__(self, master):
        self.slave = tk.Toplevel(master)
        self.slave.title('Добавить доходы/расходы')
        self.slave.geometry('400x220+400+300')
        self.slave.resizable(False, False)
        self.labelDescription = tk.Label(self.slave, text='Наименование:')
        self.labelDescription.place(x=50, y=50)
        self.labelSelect = tk.Label(self.slave, text='Статья дохода/расхода:')
        self.labelSelect.place(x=50, y=80)
        self.labelSum = tk.Label(self.slave, text='Сумма:')
        self.labelSum.place(x=50, y=110)
        self.entryDescription = ttk.Entry(self.slave)
        self.entryDescription.place(x=200, y=50)
        self.entryMoney = ttk.Entry(self.slave)
        self.entryMoney.place(x=200, y=110)
        self.combobox = ttk.Combobox(self.slave, values=[u"Доход", u"Расход"])
        self.combobox.current(0)
        self.combobox.place(x=200, y=80)
        self.btnCancel = ttk.Button(self.slave, text='Отмена', command=self.slave.destroy)
        self.btnCancel.place(x=300, y=170)
        self.btnOk = ttk.Button(self.slave, text='OK')
        self.btnOk.place(x=220, y=170)
        self.btnOk.bind('<Button-1>', lambda event: self.insert_data(self.entryDescription.get(),
                                                                     self.combobox.get(),
                                                                     self.entryMoney.get()))
        self.slave.grab_set()
        self.slave.focus_set()
        self.slave.wait_window()
    def insert_data(self, description, costs, total):
        c.execute('''INSERT INTO finance(description, costs, total) VALUES (?, ?, ?)''', (description, costs, total))
        conn.commit()
        '''conn.close()'''
        self.slave.destroy()
root = tk.Tk()
Main(root)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version