Уведомления

Группа в Telegram: @pythonsu
  • Начало
  • » GUI
  • » Tkinter, bind к виджетам созданным в цикле, хрень с IntVar() [RSS Feed]

#1 Май 29, 2010 16:27:48

antib
От:
Зарегистрирован: 2010-02-23
Сообщения: 37
Репутация: +  0  -
Профиль   Отправить e-mail  

Tkinter, bind к виджетам созданным в цикле, хрень с IntVar()

В книге http://infohost.nmt.edu/tcc/help/pubs/tkinter/tkinter.pdf (pdf, 1,4Мб) на стр.115 (“30.7. The extra arguments trick”) имеет место фрагмент кода с десятью checkbutton'ами которые создаются в цикле и к каждой в том же цикле привязывается своё событие.
Пытаюсь повторить это, но получается, что сгенерированный в цикле handler существует для всех виджетов только в том виде, в каком он генерится в последнем цикле.

Создаю в цикле 5 чекбатонов, ниже вывожу для наглядности переменную данного чекбатона (0 или 1). При снятии или проставке “галочки” с чекбатона срабатывает handler, для проверки в консоль выводится аргумент с которым этот handler срабатывает и это всегда номер последнего шага в цикле, а нужен номер того шага, в котором был сгенерён данный виджет.

Не пойму где моё художество спотыкается. Мозг свело.

Вот пример как он есть в книге:

def __createWidgets ( self ):
...
self.cbList = [] # Create the checkbutton list
for i in range(10):
cb = Checkbutton ( self, ... )
self.cbList.append ( cb )
cb.grid( row=1, column=i )
def handler ( event, self=self, i=i ):
return self.__cbHandler ( event, i )
cb.bind ( "<Button-1>", handler )
...
def __cbHandler ( self, event, cbNumber ):
...
Вот что делаю я.
 
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Tkinter import *
class App(Frame):
def __init__(self,master=None):
Frame.__init__(self, master)
self.grid()
self.chcks=[]
self.dots=[]
self.status_=[]
#в цикле создаю чекбаттоны и их переменные
for step in range(1,6):
self.chk=Checkbutton()
self.chk.grid(row=0,column=step)
self.dot=IntVar()
self.dot.set(0)
self.chk["variable"]=self.dot
def handler (event, self=self, x=step):
#примитивная проверка
print ">>>>>>>>>>handler№ ", step
return self.__statset (event, step)
self.chk.bind ("<1>", handler)
self.statusbar=Label() #current dot display
self.statusbar.grid(row=1,column=step)
self.statusbar["textvariable"]=self.dot
self.dots.append(self.dot) #appending things into lists
self.chcks.append(self.chk)
self.status_.append(self.statusbar)
self.svar=StringVar() #summary variable
self.svar.set(self.dots[2].get())
self.summ=Label() #summary display
self.summ.grid(row=2,column=0)
self.summ["textvariable"]=self.svar

def __statset(self, num):
self.svar.set(num)

root=App()
root.mainloop()



ОС - linux mint, Python ver 3.3

Офлайн

#2 Май 30, 2010 12:08:15

igor.kaist
От:
Зарегистрирован: 2007-11-12
Сообщения: 1879
Репутация: +  3  -
Профиль   Отправить e-mail  

Tkinter, bind к виджетам созданным в цикле, хрень с IntVar()

Вы не создаете разные чекбатоны в цикле, вы несколько раз создаете один и тот же, каждый раз заменяя предыдущий. Сделайте список чекбатонов, примерно так:

self.chk=range(1,6)
for step in range(1,6):
self.chk[step]=Chekbutton()
self.chk[step].grid(row=0,column=step)
# и так далее



Офлайн

#3 Май 31, 2010 09:56:59

Griffon
От: Ukrain, Zaporozhie
Зарегистрирован: 2009-03-04
Сообщения: 324
Репутация: +  11  -
Профиль   Отправить e-mail  

Tkinter, bind к виджетам созданным в цикле, хрень с IntVar()

def handler ( event, self=self, i=i ):
return self.__cbHandler ( event, i )
Правим:
def handler (event):
self.__cbHandler (event, i)
upd: Насколько я помню, при выполнении grid ссылка на обьект сохраняется, и переменную можно смело освобождать, если не планируется потом ей оперировать.



Отредактировано (Май 31, 2010 10:03:11)

Офлайн

#4 Июнь 3, 2010 19:34:11

antib
От:
Зарегистрирован: 2010-02-23
Сообщения: 37
Репутация: +  0  -
Профиль   Отправить e-mail  

Tkinter, bind к виджетам созданным в цикле, хрень с IntVar()

Попробовал применить оба ваши совета, сообщений об ошибках теперь не выводится, но всё равно сгенерированный в цикле handler работает только как генерированный в последнем шаге цикла.

Griffon
upd: Насколько я помню, при выполнении grid ссылка на обьект сохраняется, и переменную можно смело освобождать, если не планируется потом ей оперировать.
Я попробовал чекбаттоны и их переменные создавать как советует igor.kaist, а метки отражающие эти переменный по-старому т.е. создаю переменную и присоединяю её к списку append'ом и в обоих случаях оно работало.

self.summ это Label который по идее должен выводить номер последней “сработавшей” чекбаттон (изначально я думал считать кол-во “проставленных” чекбатонов, вот почему оно было обозвано summ), и он всегда приравнивается 5, этот же результат выдает print в привязанном к каждому чекбаттон “событии” (или как это правильно назвать?)
Может быть так, что создаётся каждое событие правильно, но привязывается bind'ом каждый раз последнее т.е. с пятеркой, или оно привязывается каким создается?



#!/usr/bin/env python
# -*- coding: utf-8 -*-
#сообщения об ошибке больше нет, но созданный в цикле handler заменяется
#сгенерированным в последнем шаге цикла т.е. в консоли вечно "handler №5"
from Tkinter import *
class App(Frame):
def __init__(self,master=None):
Frame.__init__(self, master)
self.grid()
self.chks=[] #checkbuttons list
self.chks=range(1,7)
self.dots=[] #chekbutton's variables list
self.dots=range(1,7)
self.status_=[]# "statusbar" labels list
for step in range(1,6): #create checkbuttons and their vars
self.chks[step]=Checkbutton()
self.chks[step].grid(row=0,column=step)
self.dots[step]=IntVar()
self.chks[step]["variable"]=self.dots[step]

def handler (event, self=self): #примитивная проверка
print ">>>>>>>>>>handler№ ", step
return self.__statset (step) #set variable of self.summ Label
self.chks[step].bind ("<1>", handler)

self.statusbar=Label() #current dot display
self.statusbar.grid(row=1,column=step)
self.statusbar["textvariable"]=self.dots[step]
#self.dots.append(self.dots) #appending things into lists
#self.chks.append(self.chks)
self.status_.append(self.statusbar)
self.svar=StringVar()
self.summ=Label()
self.summ.grid(row=2,column=0)
self.summ["textvariable"]=self.svar

def __statset(self, num):
self.svar.set(num)

root=App()
root.mainloop()
При этом если я не создаю списки (range (1, …)) на 1 больше, чем нужно получаю ошибку list assigment index out of range.
Возможно я чего-то опять не понял или это создание списков таким образом (с range и циклом) работает не так как предполагается.
Вот пробую в консоли такое:
ls=[]  #типа список
ls=range(1,6) #заполняю его range'ом
ls
[1, 2, 3, 4, 5]
step=range(1,6)
type(step)
<type 'list'>
for step in range(1,6):
...: ls[step]=step
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
ls
[1, 1, 2, 3, 4]
Я полагал что список ls должен остаться неизменным, однако получается не то. Может стоит пользоваться чем-то другим? Или я что-то упустил?

upd: С примером про range в консоли я сам себя перемудрил - похоже тут путаются 1-й и нулевой элементы списка. Если это поможет наладить моё художество, то я пока не догадался как.

upd upd:
Проверяю считаются ли чекбатоны одним виджетом или разным. Для этого в handler (как ж его по-русски то обозвать?) ввожу строку


def handler (event, self=self): #примитивная проверка
print ">>>>>>>>>>handler№ ", step
print event.widget
return self.__statset (step) #set variable of self.summ Label
self.chks[step].bind ("<1>", handler)
Если я правильно понимаю вывод в консоль:
.149636876
>>>>>>>>>>handler№ 4
.149636556
>>>>>>>>>>handler№ 4
.148978060
>>>>>>>>>>handler№ 4
.148978060
>>>>>>>>>>handler№ 4
.148961996

то это определённо не один и тот же виджет. Но handler для всех одинаков.



ОС - linux mint, Python ver 3.3

Отредактировано (Июнь 3, 2010 20:26:54)

Офлайн

#5 Июнь 3, 2010 20:28:23

Griffon
От: Ukrain, Zaporozhie
Зарегистрирован: 2009-03-04
Сообщения: 324
Репутация: +  11  -
Профиль   Отправить e-mail  

Tkinter, bind к виджетам созданным в цикле, хрень с IntVar()

def handler (event, self=self, step=step) и будет тебе счастье.



Офлайн

#6 Июнь 3, 2010 20:30:22

antib
От:
Зарегистрирован: 2010-02-23
Сообщения: 37
Репутация: +  0  -
Профиль   Отправить e-mail  

Tkinter, bind к виджетам созданным в цикле, хрень с IntVar()

Спасибо. Стало мне счастье.



ОС - linux mint, Python ver 3.3

Офлайн

  • Начало
  • » GUI
  • » Tkinter, bind к виджетам созданным в цикле, хрень с IntVar()[RSS Feed]

Board footer

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

Powered by DjangoBB

Lo-Fi Version