Форум сайта python.su
Я написал программу, которая обеспечивает создание и перемещение объектов по экрану (планировщик помещения). Каждый объект активируется для движения нажатием левой кнопки мыши на него. Есть несколько проблем. После активации объекта двигаться начинает не он, а объект, созданный перед ним (имеющий порядковый номер, который я сделал тегом, на единицу меньше), а если сохранить положение фигур и снова открыть файл, то смещение произведенное предметом с меньшим тегом, будет отображено как смещение предмета, который я и активировал. Есть еще проблема с функцией изменения положения объекта в пространстве (с горизонтального в вертикальное и обратно), в данном случае виджет может как не двигаться, так и совершить одно действие и прекратить в принципе реагировать на команды, при этом перестают реагировать все виджеты. Код указан ниже. В чем здесь проблема?
from tkinter import * import tkinter.ttk as ttk from time import sleep from tkinter.filedialog import * import fileinput from tkinter.messagebox import * import os import sqlite3 #список с параметрами виджетов spisok_widget=[] #имя файла для автоматического сохранения name_save='' tag=0 zvet='white' figura='прямоугольник' #открытие базы данных с сохраненными параметрами объектов def _open(): okno_sozd=Tk() okno_sozd.title('') okno_sozd.geometry('+0+0') l = Listbox(okno_sozd,height=9,width=33) scroll=Scrollbar(okno_sozd, command=l.yview) l.configure(yscrollcommand=scroll.set) l.grid(row=0,column=0) scroll.grid(row=0,column=1) for i in os.listdir(): if i[len(i)-2:len(i)]=='db': l.insert(END, i) def zakritie(event): okno_sozd.destroy() def obnovlen(event): index=l.curselection() dannie_widget=l.get(index) okno_sozd.destroy() #определяем имя файла для автоматического сохранения global name_save name_save=dannie_widget con=sqlite3.connect(dannie_widget) cur=con.cursor() cur.execute('SELECT*FROM perechen') b=cur.fetchall() con.commit() cur.close() con.close() global spisok_widget for elem in b: spisok_widget.append(list(elem)) if elem[6]=='прямоугольник': widget=c.create_rectangle(elem[1],elem[2],elem[1]+elem[3], elem[2]+elem[4],fill=elem[5],outline='black',tag=elem[0]) if elem[6]=='овал': widget=c.create_oval(elem[1],elem[2],elem[1]+elem[3], elem[2]+elem[4],fill=elem[5],outline='black',tag=elem[0]) c.tag_bind(widget,'<Button-1>',identifikacia) ramka=Frame(okno_sozd) ramka.grid(row=1,column=0) knopka_da=Button(ramka,text='Открыть') knopka_net=Button(ramka,text='Отменить') knopka_da.pack(side=LEFT) knopka_net.pack(side=RIGHT) knopka_da.bind('<Button-1>',obnovlen) knopka_net.bind('<Button-1>',zakritie) #сохранение параметров объектов в создаваемом файле def _save_kak(): okno_sozd=Tk() okno_sozd.title('') okno_sozd.geometry('+0+0') #okno_sozd.overrideredirect(1) vvod_text=Label(okno_sozd,text='Введите наименование файла') st=StringVar() pole=Entry(okno_sozd,textvariable=st) ramka=Frame(okno_sozd) vvod_text.pack() pole.pack() ramka.pack() knopka_da=Button(ramka,text='Сохранить') knopka_net=Button(ramka,text='Отменить') knopka_da.pack(side=LEFT) knopka_net.pack(side=RIGHT) def zakritie(event): okno_sozd.destroy() def obnovlen(event): name=pole.get()+'.db' #определяем имя файла для автоматического сохранения global name_save name_save=name okno_sozd.destroy() con = sqlite3.connect(name) cur = con.cursor() sql='''CREATE TABLE perechen(tag INTEGER PRIMARY KEY AUTOINCREMENT, x INTEGER, y INTEGER, razmer1 INTEGER,razmer2 INTEGER,zvet TEXT,figura TEXT)''' cur.executescript(sql) cur.close() con.close() con=sqlite3.connect(name) cur=con.cursor() global spisok_widget for elem in spisok_widget: a=tuple(elem) sql_a='INSERT INTO perechen VALUES(?,?,?,?,?,?,?)' cur.execute(sql_a,a) con.commit() cur.close() con.close() knopka_da.bind('<Button-1>',obnovlen) knopka_net.bind('<Button-1>',zakritie) #сохранение параметров объектов в последний используемый файл def _save(): if name_save!='': con=sqlite3.connect(name_save) cur=con.cursor() cur.execute('SELECT*FROM perechen') b=cur.fetchall() global spisok_widget for elem in spisok_widget: a=tuple(elem) n=0 for elem in b: if a[0]==elem[0]: n=1 if n!=1: sql_a='INSERT INTO perechen VALUES(?,?,?,?,?,?,?)' cur.execute(sql_a,a) elif n==1: cur.execute('UPDATE perechen SET x=? WHERE tag=?',\ (a[1],a[0])) cur.execute('UPDATE perechen SET y=? WHERE tag=?',\ (a[2],a[0])) cur.execute('UPDATE perechen SET razmer1=? WHERE tag=?',\ (a[3],a[0])) cur.execute('UPDATE perechen SET razmer2=? WHERE tag=?',\ (a[4],a[0])) cur.execute('UPDATE perechen SET zvet=? WHERE tag=?',\ (a[5],a[0])) cur.execute('UPDATE perechen SET figura=? WHERE tag=?',\ (a[6],a[0])) con.commit() cur.close() con.close() #создание функций меню def close_win (): #def quit(): if askyesno("Выход", "Желаете выйти"): root.destroy() def new_proekt(): if askyesno("Новый проект", "Желаете создать новый проект"): root.destroy() def about(): showinfo("О программе", '''Программа предоставляет возможность спроектировать расстановку различных предметов(в том числе мебели) в помещении''') #создание окна, холста, меню, виджетов для ввода информации root=Tk() root.title('Планировка помещения') m = Menu(root) root.config(menu=m) fm = Menu(m) m.add_cascade(label="Меню",menu=fm) fm.add_command(label="Открыть",command=_open) fm.add_command(label="Сохранить",command=_save) fm.add_command(label="Сохранить как...",command=_save_kak) fm.add_command(label="Новый проект",command=new_proekt) fm.add_command(label="Выход",command=close_win) hm = Menu(m) m.add_cascade(label="Помощь",menu=hm) hm.add_command(label="О программе",command=about) #масштаб 1 пиксель - 1 см c = Canvas(width=1000,height=700,bg='white') ramka=Frame(root,width=200,height=700) c.pack(side=LEFT) ramka.pack(side=RIGHT) c.focus_set() nadp_predmet=Label(ramka,text='Выбор объекта') nadp_predmet.pack() spisok_predmet=ttk.Combobox(ramka,height=5,values=['комната','окно','дверь', 'мебель','другой предмет']) forma_predmet=ttk.Combobox(ramka,height=2,values=['прямоугольник','овал']) spisok_predmet.pack() forma_predmet.pack() spisok_predmet.set('комната') forma_predmet.set('прямоугольник') nadp_predmet=Label(ramka,text='Размеры объекта') nadp_predmet.pack() ramka_razmer=Frame(ramka) ramka_razmer.pack() dk=IntVar() razmer1_komnata=Entry(ramka_razmer,width=4,textvariable=dk) nadp_razmer1=Label(ramka_razmer,text='см',width=2) sk=IntVar() razmer2_komnata=Entry(ramka_razmer,width=4,textvariable=sk) nadp_razmer2=Label(ramka_razmer,text='см',width=2) knopka_vvoda=Button(ramka,text='Ввод данных') knopka_vvoda.pack() razmer1_komnata.grid(row=0,column=0) nadp_razmer1.grid(row=0,column=1) razmer2_komnata.grid(row=0,column=2) nadp_razmer2.grid(row=0,column=3) def obnovl_predmet(e=None): global tag global zvet global figura global spisok_widget predmet=spisok_predmet.get() figura=forma_predmet.get() if predmet=='комната': tag=0;zvet='white' else: tag=spisok_widget[-1][0]+1 if predmet=='окно': zvet='gray80' elif predmet=='дверь' or predmet=='другой предмет': zvet='black' elif predmet=='мебель': zvet='gray' spisok_predmet.bind('<<ComboboxSelected>>', obnovl_predmet) forma_predmet.bind('<<ComboboxSelected>>', obnovl_predmet) #создание объектов x=20 y=20 def dannie_komnata(event): global tag global zvet global figura global spisok_widget razmer1=float(razmer1_komnata.get()) razmer2=float(razmer2_komnata.get()) if figura=='прямоугольник': widget_komnata=c.create_rectangle(x,y,x+razmer1,y+razmer2, fill=zvet,outline='black',tag=tag) elif figura=='овал': widget_komnata=c.create_oval(x,y,x+razmer1,y+razmer2, fill=zvet,outline='black',tag=tag) elem_spisok_widget=[tag,x,y,razmer1,razmer2,zvet,figura] spisok_widget.append(elem_spisok_widget) c.tag_bind(widget_komnata,'<Button-1>',identifikacia) knopka_vvoda.bind('<Button-1>',dannie_komnata) #создание функций движения объектов def sdvig_vverch(event): global tag global spisok_widget c.move(tag,0,-2) for elem in spisok_widget: if elem[0]==tag: elem[2]=elem[2]-2 c.update() sleep(0.02) def sdvig_vniz(event): global tag global spisok_widget c.move(tag,0,2) for elem in spisok_widget: if elem[0]==tag: elem[2]=elem[2]+2 c.update() sleep(0.02) def sdvig_vpravo(event): global tag global spisok_widget c.move(tag,2,0) for elem in spisok_widget: if elem[0]==tag: elem[1]=elem[1]+2 c.update() sleep(0.02) def sdvig_vlevo(event): global tag global spisok_widget c.move(tag,-2,0) for elem in spisok_widget: if elem[0]==tag: elem[1]=elem[1]-2 c.update() sleep(0.02) #функция изменения положения фигуры с горизонтального в вертикальное и обратно def izmen_polozhenie(event): global tag global spisok_widget c.delete(tag) for elem in spisok_widget: if elem[0]==tag: elem[3],elem[4]=elem[4],elem[3] if elem[6]=='прямоугольник': widget=c.create_rectangle(elem[1],elem[2],elem[1]+elem[3], elem[2]+elem[4],fill=elem[5],outline='black',tag=elem[0]) elif elem[6]=='овал': widget=c.create_oval(elem[1],elem[2],elem[1]+elem[3], elem[2]+elem[4],fill=elem[5],outline='black',tag=elem[0]) #идентификация виджета по тегу def identifikacia(event): c.focus_set() global tag item=c.find_closest(event.x,event.y) tag=int(c.gettags(item)[0]) c.bind('<Up>',sdvig_vverch) c.bind('<Down>',sdvig_vniz) c.bind('<Right>',sdvig_vpravo) c.bind('<Left>',sdvig_vlevo) c.bind('s',izmen_polozhenie) root.mainloop()
Офлайн
Konstantin1984+Вашим кодом можно восхищаться бесконечно, но мне кажется одна мааааленькая проблемка у вас налицо - декомпозиция. У вас есть проблемы очень базовые, например “После активации объекта двигаться начинает не он, а объект, созданный перед ним”. Это значит, что у вас нет понимания принципиальной логики работы программы. Вы нафигачили 350 строк кода - тут у вас и запросы к БД и менюшки какие-то, вот это всё - второстепенное, это надстройка над базовым функционалом. Если у вас есть список неких объектов, то вовсе не имеет значения откуда этот список взялся - из БД или вы его захардкодили. Сначала научитесь работать с одним объектом. Потом со списком объектов. Вот уже потом можно придумать хоть сколько способов заполнять этот список. Программа пишется ровно так же. Сначала вы делаете простейший рабочий прототип. Потом постепенно вносите улучшения. Смысл вашей программы, насколько я понял, заставить предсказуемо двигаться некие объекты. Если вам не удалось этого достичь - зачем нужны все тонны остального бреда?
В чем здесь проблема?
Офлайн
Вот урезанная версия, минимум кода, чтобы проверить программу.
from tkinter import * from time import sleep #список с параметрами виджетов spisok_widget=[] #создание окна, холста, меню, виджетов для ввода информации root=Tk() root.title('Движение объектов') c=Canvas(width=1000,height=700,bg='white') knopka_vvoda=Button(root,text='Создать фигуру') c.pack(side=LEFT) knopka_vvoda.pack(side=RIGHT) #создание объектов x=20 y=20 def dannie_komnata(event): global tag global spisok_widget if len(spisok_widget)==0: tag=0 else: tag=spisok_widget[-1][0]+1 razmer1=400 razmer2=100 widget_komnata=c.create_rectangle(x,y,x+razmer1,y+razmer2, fill='gray',outline='black',tag=tag) elem_spisok_widget=[tag,x,y,razmer1,razmer2] spisok_widget.append(elem_spisok_widget) c.tag_bind(widget_komnata,'<Button-1>',identifikacia) knopka_vvoda.bind('<Button-1>',dannie_komnata) #идентификация виджета по тегу def identifikacia(event): c.focus_set() global tag item=c.find_closest(event.x,event.y) tag=int(c.gettags(item)[0]) #создание функций движения объектов def sdvig_vverch(event): global tag global spisok_widget c.move(tag,0,-2) for elem in spisok_widget: if elem[0]==tag: elem[2]=elem[2]-2 c.update() sleep(0.02) def sdvig_vniz(event): global tag global spisok_widget c.move(tag,0,2) for elem in spisok_widget: if elem[0]==tag: elem[2]=elem[2]+2 c.update() sleep(0.02) def sdvig_vpravo(event): global tag global spisok_widget c.move(tag,2,0) for elem in spisok_widget: if elem[0]==tag: elem[1]=elem[1]+2 c.update() sleep(0.02) def sdvig_vlevo(event): global tag global spisok_widget c.move(tag,-2,0) for elem in spisok_widget: if elem[0]==tag: elem[1]=elem[1]-2 c.update() sleep(0.02) #функция изменения положения фигуры с горизонтального в вертикальное и обратно def izmen_polozhenie(event): global tag global spisok_widget for elem in spisok_widget: if elem[0]==tag: elem[3],elem[4]=elem[4],elem[3] c.coords(tag,elem[1],elem[2],elem[1]+elem[3],elem[2]+elem[4]) c.bind('<Up>',sdvig_vverch) c.bind('<Down>',sdvig_vniz) c.bind('<Right>',sdvig_vpravo) c.bind('<Left>',sdvig_vlevo) c.bind('s',izmen_polozhenie) root.mainloop()
Офлайн
Konstantin1984+Минимум кода это как-то так:
Вот урезанная версия, минимум кода, чтобы проверить программу.
from tkinter import Tk, Variable, Canvas, Button from time import sleep STEP = 15 #Шаг перемещения фигур (можно изменить) DIRECTION = {'Down': (0, STEP), 'Left': (-STEP, 0), 'Right': (STEP, 0), 'Up': (0, -STEP)} #Не изменять!!! X, Y, W, H = 70, 20, 400, 100 #Начальное положение и размер новых фигур def dannie_komnata(): '''Создание новой фигуры.''' item = c.create_rectangle(X, Y, X+W, Y+H, fill='gray', outline='black') c.tag_bind(item, '<Button-1>', identifikacia) def identifikacia(event): '''Идентификация выбранного виджета.''' c.focus_set() item = c.find_closest(event.x, event.y) item_selected.set(item[0]) def sdvig(e): '''Нажатие на клавиши со стрелками перемещает выбранную фигуру в направлении стрелки.''' c.move(item_selected.get(), *DIRECTION.get(e.keysym, (0, 0))) def izmen_polozhenie(event): '''Функция изменения положения фигуры с горизонтального в вертикальное и обратно.''' x1, y1, x2, y2 = c.coords(item_selected.get()) c.coords(item_selected.get(), x1, y1, y2-y1+x1, x2-x1+y1) #создание окна, холста, меню, виджетов для ввода информации root = Tk() root.title('Движение объектов') item_selected = Variable() c = Canvas(root, width=1000, height=600, bg='white') c.pack(side='left') c.bind('<Key>', sdvig) c.bind('s', izmen_polozhenie) Button(root, text='Создать фигуру', command=dannie_komnata).pack(side='right') root.mainloop()
Офлайн
Konstantin1984+ вы упорно отказываетесь читать документацию?
coords(item, *coords)https://effbot.org/tkinterbook/canvas.htm
Returns the coordinates for an item.
item
Item specifier (tag or id).
….
The tags option takes either a single tag string, or a tuple of strings.
…
Konstantin1984+естественно, у вас ИД обьекта 1, а тег 0, предполагаю что обьект с ИД 0 это сам канвас(или ХЗ что еще), вот оно и берет что первое находит, а именно обьект с ИД=0, а когда вы делаете tag+1 то оно ищет обьект с ИД =1 ну и находит вашу фигуру.
Самое интересное, что при замене в методах “c.move” и “с.coords” аргумента “tag” на “tag+1” программа начинает работать.
[code python][/code]
Отредактировано PEHDOM (Май 20, 2020 21:06:31)
Офлайн
Всем спасибо!
Офлайн
Уважаемый PEHDOM, я не очень понял фразу про отличия tag от id.
id - это ведь идентификатор? А идентификатор в моем случае - это целое выражение “c.create_rectangle(x,y,x+razmer1,y+razmer2, fill='gray',outline='black',tag=tag)”,
а tag принимает конкретное цифровое значение. Какая тут может быть путаница между id и tag?
Офлайн
Konstantin1984+Черт, я даже не знаю что вам сказать. Все дело в том что программа работает так как запрограмировали прграмисты а не так как вы хотите чтобы она рабтала. И идентификатор в данном случае это то что решили програмисты ткинтера, а не вы. Обратимся к документации, которую вы почемуто упорно игнорируете:
Уважаемый PEHDOM, я не очень понял фразу про отличия tag от id.
id - это ведь идентификатор? А идентификатор в моем случае - это целое выражение “c.create_rectangle(x,y,x+razmer1,y+razmer2, fill='gray',outline='black',tag=tag)”.
create_rectangle(bbox, **options)
Draws a rectangle on the canvas.
….
Returns:
The item id.
Konstantin1984+tag же это просто метка , вы можете назначить один и тот же тег нескольким обьектам, или у одного объекта может быть несколько тегов.
tag принимает конкретное цифровое значение. Какая тут может быть путаница между id и tag?
[code python][/code]
Отредактировано PEHDOM (Май 21, 2020 20:08:18)
Офлайн
Konstantin1984+У вас есть имя и фамилия, а есть номер паспорта. Разница существенна.
Какая тут может быть путаница между id и tag?
Офлайн
Спасибо! Что-то я упустил при прочтении материалов.
Офлайн