Уведомления

Группа в Telegram: @pythonsu
  • Начало
  • » GUI
  • » Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable' [RSS Feed]

#1 Март 9, 2016 21:50:05

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'

Доброго дня!
Я - очередной новичок, изучающий Питон, поэтому вопросы могут быть несколько.. простыми для вас. Это на всякий случай, чтобы не удивлялись.
К сути. Я пишу интерфейс на Tk под Python 3.5.1. В моём коде есть инструкция, которая должна встраивать в виджет Text кучку виджетов Checkbutton. Делается это с помощью команды вида “self.window_create('end', window=cb)”. Так вот при попытке её выполнения вылетает эксепшен:

_tkinter.TclError: can't embed .19551680 in .19474248.19511280.19512568
Мне это сообщение ни о чём не говорило, поэтому я нарыл возможность отображения ошибок непосредственно через Tcl (с помощью tkinter.Tcl.eval(“set ::errorInfo”)), и увидел вот что:
can't read "::tcl_pkgPath": no such variable
    while executing
"foreach Dir $::tcl_pkgPath {
	    if {$Dir ni $::auto_path} {
		lappend ::auto_path $Dir
	    }
	}"
Самое удивительное, что эта ошибка появляется только в том случае, когда код исполняется из моего приложения - там этот Text находится внутри одного из окон. Если взять этот же класс и запустить его с ТЕМИ ЖЕ данными отдельно, всё прекрасно отрабатывает.
Итак, собственно вопрос: как же правильно скормить встроенному Tcl эту самую tcl_pkgPath? Tcl я не знаю совсем, а то, что нарыл по теме в сети, мне ничего не дало. Точнее, я узнал, что tcl_pkgPath нельзя задавать из самой программы, нужно для задания пути к библиотекам пользоваться auto_path, вот только из сообщения об ошибке явственно следует, если я правильно понимаю, что тут этот способ не поможет - переменная почему-то не инициализирована изначально.

Прошу помощи у знатоков Python/Tcl!
Всем большое спасибо.
PS: На всякий случай прикладываю упрощённый вариант кода. Отдельно этот код прекрасно работает. А вот если встроить его (даже именно в таком виде, не меняя ни буквы) в моё приложение, работать перестаёт. Если надо, могу дать ссылку на гитхаб, где оно лежит (хотя позориться пока не хотелось бы - код далёк от профессионального

from tkinter import *
class Tagslist(Frame):
    def __init__(self, parent=None, **options):
        Frame.__init__(self, master=parent)
        self.textbox = Text(self, **options)
        scroller = Scrollbar(self)
        scroller.config(command=self.textbox.yview)
        self.textbox.config(yscrollcommand=scroller.set)
        self.states_dict = {1: [1, 'tag1'],  2: [0, 'tag2'], 3: [1, 'tag3']}
        for key in self.states_dict:
            state = self.states_dict[key][0]
            self.states_dict[key][0] = IntVar()
            cb = Checkbutton(text=self.states_dict[key][1], variable=self.states_dict[key][0])
            try:
                self.textbox.window_create('end', window=cb)    # Вот тут оно и падает.
            except TclError as err:
                print(err)
                tcl = Tcl()
                errorInfo = tcl.eval("set ::errorInfo")          # Кстати, это работает на Windows, но на Linux вызывает ещё одно исключение.
                print(errorInfo)
            self.textbox.insert('end', '\n')
            self.states_dict[key][0].set(state)
        scroller.grid(row=0, column=1, sticky='sn')
        self.textbox.grid(row=0, column=0, sticky='news')
        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure('all', weight=1)
root = Tk()
f = Tagslist(root)
f.grid()
root.mainloop()

Отредактировано drevoborod (Март 9, 2016 21:54:33)

Офлайн

#2 Март 9, 2016 23:37:53

4kpt_IV
Зарегистрирован: 2016-01-08
Сообщения: 999
Репутация: +  49  -
Профиль   Отправить e-mail  

Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'

Поясните, пожалуйста, зачем вы хотите в Text виджет вставлять чекбоксы?

Офлайн

#3 Март 10, 2016 07:26:53

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'

4kpt_IV
Поясните, пожалуйста, зачем вы хотите в Text виджет вставлять чекбоксы?
Мне кажется, это отдельный вопрос, никак не связанный с моим. Но ответить могу, однако на мой вопрос ответ мне всё равно хотелось бы получить - мне важно понять, что же пошло не так, почему Tcl вдруг потерял свою переменную окружения именно при встраивании виджета внутрь одного из окон моей программы.
Что касается вашего вопроса, то задумка такая: иметь список чекбоксов, каждый из которых отвечает за свой тег (“тег” в значении “характеристика”, см. “облако тегов”, а не в значении “элемент разметки”, как в html). Этот список лежит внутри окна редактирования свойств юнита, то есть у юнита может быть несколько тегов. Чтобы эти теги было легко назначать, мы выводим их в виде списка с чекбоксами. Когда пользователь тыкает в чекбокс, к юниту привязывается один из тегов. Так как тегов может быть произвольное количество, нужно иметь возможность отображать их в виде скроллируемого списка. Я пока только изучаю tkinter, но, насколько мне известно, для встраивания в один контейнер со скроллом кучи виджетов подходят только Text и Canvas. Второго я пока не осилил - у него для создания скроллируемой области произвольного размера нужно динамически задавать координаты этой области, что я пока не стал изучать, решив, что в Text оно как-то значительно проще реализовано. И оказался прав - это просто, вот только не работает

PS: Вот, кстати, и реальный пример проблем со скроллбаром и Canvas c чекбаттонами ))
http://python.su/forum/topic/29501/

Отредактировано drevoborod (Март 10, 2016 15:52:01)

Офлайн

#4 Март 10, 2016 20:37:09

4kpt_IV
Зарегистрирован: 2016-01-08
Сообщения: 999
Репутация: +  49  -
Профиль   Отправить e-mail  

Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'

Связанный. Вам нужно использовать Canvas. Если нужен пример реализации - пишите. Поищу и выложу.

Офлайн

#5 Март 10, 2016 20:40:38

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'

Ок, спасибо, я уже сам понял, что изучить всё равно придётся и начал копать в эту сторону. Примеры реализации, если не сложно, покажите, пожалуйста.

Офлайн

#6 Март 10, 2016 20:54:56

4kpt_IV
Зарегистрирован: 2016-01-08
Сообщения: 999
Репутация: +  49  -
Профиль   Отправить e-mail  

Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'

Вот пример реализации по 2.7. Для перевода под 3.4 нужно помнять Tkinter на tkinter.

import Tkinter
#
def reconf_canvas(event):
    canv.configure(scrollregion=canv.bbox('all'))
#
root = Tkinter.Tk()
root.geometry("400x400+100+100")
#
canv = Tkinter.Canvas(root, width=200, height=200)
canv.grid(row=0, column=0)
#
frm = Tkinter.Frame(canv)
frm.pack()
#
scr = Tkinter.Scrollbar(root)
scr.grid(row=0, column=1, sticky="ns")
scr["command"] = canv.yview
canv["yscrollcommand"] = scr.set
canv.create_window((0,0), window=frm, anchor="nw")
frm.bind("<Configure>", reconf_canvas)
#
for i in xrange(20):
    but = Tkinter.Button(frm, text=u"Кнопка %01d" % i)
    but.pack()
#
root.mainloop()

Офлайн

#7 Март 11, 2016 14:53:31

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'

Большое спасибо! С использованием вашего примера сделал такой вот класс. Кроме всего прочего, он умеет принимать параметр orientation, в зависимости от значения которого чекбоксы и скролл располагаются горизонтально либо вертикально.
Словарь states_dict в реальности, конечно же, формируется за пределами класса.

import tkinter
class Tagslist(tkinter.Frame):
    """Список тегов со скроллом."""
    def __init__(self, parent=None, orientation="vertical", **options):
        super().__init__(master=parent, **options)
        scroller = tkinter.Scrollbar(self, orient=orientation)
        self.canvbox = tkinter.Canvas(self, width=(300 if orientation == "horizontal" else 100),
                              height=(30 if orientation == "horizontal" else 100))
        scroller.config(command=(self.canvbox.xview if orientation == "horizontal" else self.canvbox.yview))
        if orientation == "horizontal":
            self.canvbox.config(xscrollcommand=scroller.set)
            scroller.grid(row=1, column=0, sticky='ew')
            self.grid_rowconfigure(0, weight=1)
            self.grid_columnconfigure('all', weight=1)
        else:
            self.canvbox.config(yscrollcommand=scroller.set)
            scroller.grid(row=0, column=1, sticky='ns')
            self.grid_rowconfigure('all', weight=1)
            self.grid_columnconfigure(0, weight=1)
        self.content_frame = tkinter.Frame(self.canvbox)
        self.content_frame.pack(fill='both', expand=1)
        self.canvbox.create_window((0,0), window=self.content_frame, anchor='nw')
        self.content_frame.bind("<Configure>", lambda event: self.reconf_canvas())
        self.states_dict = {1: [1, 'default'], 2: [0, 'work'], 3: [0, 'personal']}    # Словарь id тегов с состояниями для данной таски и именами.
        for key in self.states_dict:
            state = self.states_dict[key][0]
            self.states_dict[key][0] = tkinter.IntVar()
            cb = tkinter.Checkbutton(self.content_frame, text=self.states_dict[key][1], variable=self.states_dict[key][0])
            cb.pack(side=('left' if orientation == "horizontal" else 'bottom'), anchor='w')
            self.states_dict[key][0].set(state)
        self.canvbox.grid(row=0, column=0, sticky='news')
    def reconf_canvas(self):
        """Изменение размера области прокрутки Canvas."""
        self.canvbox.configure(scrollregion=self.canvbox.bbox('all'))
if __name__ == "__main__":
    root = tkinter.Tk()
    f = Tagslist(root, orientation='horizontal')
    f.grid()
    print(f.states_dict)
    root.mainloop()

Отредактировано drevoborod (Март 11, 2016 14:55:36)

Офлайн

#8 Март 11, 2016 15:34:24

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'

…А потом разошёлся и сделал два класса с наследованием - чтобы можно было использовать скроллируемый Canvas независимо от того, что мы хотим положить внутрь

import tkinter
class ScrolledCanvas(tkinter.Frame):
    """Canvas со скроллом."""
    def __init__(self, parent=None, orientation="vertical", **options):
        super().__init__(master=parent, **options)
        scroller = tkinter.Scrollbar(self, orient=orientation)
        self.canvbox = tkinter.Canvas(self, width=(300 if orientation == "horizontal" else 100),
                              height=(30 if orientation == "horizontal" else 100))
        scroller.config(command=(self.canvbox.xview if orientation == "horizontal" else self.canvbox.yview))
        if orientation == "horizontal":
            self.canvbox.config(xscrollcommand=scroller.set)
            scroller.grid(row=1, column=0, sticky='ew')
            self.grid_rowconfigure(0, weight=1)
            self.grid_columnconfigure('all', weight=1)
        else:
            self.canvbox.config(yscrollcommand=scroller.set)
            scroller.grid(row=0, column=1, sticky='ns')
            self.grid_rowconfigure('all', weight=1)
            self.grid_columnconfigure(0, weight=1)
        self.content_frame = tkinter.Frame(self.canvbox)
        self.content_frame.pack(fill='both', expand=1)
        self.canvbox.create_window((0,0), window=self.content_frame, anchor='nw')
        self.content_frame.bind("<Configure>", lambda event: self.reconf_canvas())
        self.canvbox.grid(row=0, column=0, sticky='news')
    def reconf_canvas(self):
        """Изменение размера области прокрутки Canvas."""
        self.canvbox.configure(scrollregion=self.canvbox.bbox('all'))
class Tagslist(ScrolledCanvas):
    """Список тегов со скроллом."""
    def __init__(self, parent=None, states_dict=None, orientation="vertical", **options):
        super().__init__(parent=parent, orientation=orientation, **options)
        self.states_dict = states_dict      # нужно для того, чтобы из экземпляра потом можно было извлекать значения этой переменной.
        if self.states_dict is not None:
            for key in self.states_dict:
                state = self.states_dict[key][0]
                self.states_dict[key][0] = tkinter.IntVar()
                cb = tkinter.Checkbutton(self.content_frame, text=self.states_dict[key][1], variable=self.states_dict[key][0])
                cb.pack(side=('left' if orientation == "horizontal" else 'bottom'), anchor='w')
                self.states_dict[key][0].set(state)
if __name__ == "__main__":
    tags = {1: [1, 'default'], 2: [0, 'work'], 3: [0, 'personal']}    # Словарь id тегов с состояниями для данной таски и именами.
    root = tkinter.Tk()
    table = Tagslist(root, orientation='horizontal', states_dict=tags)
    table.grid()
    for key in table.states_dict:
        print(table.states_dict[key][1], ': state: ', table.states_dict[key][0].get(), ', id: ', key, sep='')
    root.mainloop()

Отредактировано drevoborod (Март 11, 2016 15:35:32)

Офлайн

#9 Март 14, 2016 21:35:42

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'

Слегка оптимизировал класс, отделил мух (вычисления) от котлет (прорисокви), и получилось вот что:

class ScrolledCanvas(tkinter.Frame):
    """Прокручиваемый Canvas."""
    def __init__(self, parent=None, orientation="vertical", **options):
        super().__init__(master=parent, relief='groove', bd=2, **options)
        scroller = tkinter.Scrollbar(self, orient=orientation)
        self.canvbox = tkinter.Canvas(self, width=(300 if orientation == "horizontal" else 200),
                              height=(30 if orientation == "horizontal" else 200))
        scroller.config(command=(self.canvbox.xview if orientation == "horizontal" else self.canvbox.yview))
        if orientation == "horizontal":
            self.canvbox.config(xscrollcommand=scroller.set)
        else:
            self.canvbox.config(yscrollcommand=scroller.set)
        scroller.pack(fill='x' if orientation == 'horizontal' else 'y', expand=1,
                      side='bottom' if orientation == 'horizontal' else 'right',
                      anchor='s' if orientation == 'horizontal' else 'e')
        self.content_frame = tkinter.Frame(self.canvbox)
        self.canvbox.create_window((0,0), window=self.content_frame, anchor='nw')
        self.content_frame.bind("<Configure>", lambda event: self.reconf_canvas())
        self.canvbox.pack(fill="x" if orientation == "horizontal" else "both", expand=1)
    def reconf_canvas(self):
        """Изменение размера области прокрутки Canvas."""
        self.canvbox.configure(scrollregion=self.canvbox.bbox('all'))

Офлайн

#10 Март 14, 2016 22:38:21

4kpt_IV
Зарегистрирован: 2016-01-08
Сообщения: 999
Репутация: +  49  -
Профиль   Отправить e-mail  

Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'

В общем хорошо.
Минусы:
1. Узнайте про PEP8
2. Вы задаете размеры канваса. Это не верно.
3. Однострочные логические выражения везде и вся это овер.
4. Ну и докстринги в на русском смотрятся как-то не очень

И… Ну это тындец. Я же уже писал

self.content_frame.bind("<Configure>", lambda event: self.reconf_canvas())

Офлайн

  • Начало
  • » GUI
  • » Ошибка Tcl 'can't read "::tcl_pkgPath": no such variable'[RSS Feed]

Board footer

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

Powered by DjangoBB

Lo-Fi Version