PythonTkinter + PyInstaller. Решение проблемы с кодировкой.

При изучении Tkinter столкнулся с досадной проблемой. Если собрать программу в exe с помощью PyInstaller, то в полях ввода текста вместо кириллицы вводится абракадабра в стиле "???????????", хотя текст, заданный из самой программы (Label, Button и т.п.), выглядит нормально. Причём исходный py-скрипт работает адекватно.
Перерыв кучу интернет-ресурсов, испробовал несколько приёмов решения проблемы с кодировкой, но ни один из них не сработал. Так как мой проект уже разросся до солидных размеров, а менять Tkinter на другой GUI мне очень не хотелось, я принялся за поиск решения.
В результате многочисленных проб и ошибок родился этот довольно грязный хак, который, однако, вполне справляется с поставленной задачей.

#!/usr/bin/python
# -*- coding: utf-8 -*-

from Tkinter import *

class FormatStringVar(StringVar):
    
    def __init__(self, root):
        StringVar.__init__(self)
        self.root = root
        # Устанавливаем callback-функцию. Она будет выполняться
        # каждый раз при изменении значения переменной
        self.trace_variable("w", self.callback)
        # CHARMAP
        # Составляем словарь необходимых символов
        _CHR = u"АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя"
        self._CHARMAP = dict()
        # от 'А' до 'Я' (192-223) и от 'а' до 'я' (224-255)
        i = 192
        for c in _CHR:
            self._CHARMAP[i] = c
            i += 1
        # Остальные символы, включая украинские
        self._CHARMAP[168] = u"Ё"
        self._CHARMAP[184] = u"ё"
        self._CHARMAP[178] = u"І"
        self._CHARMAP[179] = u"і"
        self._CHARMAP[175] = u"Ї"
        self._CHARMAP[191] = u"ї"
        self._CHARMAP[170] = u"Є"
        self._CHARMAP[186] = u"є"
        self._CHARMAP[165] = u"Ґ"
        self._CHARMAP[180] = u"ґ"
    
    # callback-функция
    def callback(self, name, index, mode):
        # получаем значение переменной
        old = self.root.globalgetvar(name)
        new = u""
        # для каждого символа в строке
        for i in old:
            # если символ входит в словарь
            if ord(i[-1]) in self._CHARMAP:
                # заменяем на правильное значение
                new += self._CHARMAP[ord(i[-1])]
            # если символа нет в словаре
            else: # оставляем символ
                new += i
        # записываем результат обратно в переменную
        self.root.globalsetvar(name, new)

# инициализируем Tkinter
root = Tk()
# устанавливаем в Tkinter кодировку UTF-8
root.tk.call('encoding', 'system', 'utf-8')

mainframe = Frame(root)
mainframe.pack()

# Инициализируем нашу переменную
entry_var = FormatStringVar(root)
# Привязываем переменную к полю ввода Entry
entry = Entry(mainframe, textvariable=entry_var)
entry.pack()

# Поехали!
root.mainloop()
  • +7
  • Vladar
  • 04 мая 2010, 15:19

Комментарии (2)

  • avatar
  • fog
  • 05 мая 2010, 00:24
  • #
  • 1
Уж сколько лет назад UTF-8 появился, а досихпор косяки всплывают… :-/
Не знаю может у кого-то что-то иполучилось так. Но я пошел другим путем.
Не буду долго рассказывать… просто при упаковке pyinstaller не добавляет папку C:\Python26\tcl\tcl8.5\encoding. это можно заметить если запустить прогу и залесть в C:\temp\_MEI7202\_MEI\tcl8.5, там нет папки encoding. Я просто кинул туда папку encoding и прога сразу подхватира кодеки. Дальше я подумал что гдето эксклюдится эта папка и нашел что это происходит в скрипте Build.py pyinstaller-a там есть такие строки

    #tcltree = Tree(tclroot, tclnm, excludes=['demos','encoding','*.lib'])
    #tktree = Tree(tkroot, tknm, excludes=['demos','encoding','*.lib'])

я их заменил на

    tcltree = Tree(tclroot, tclnm, excludes=['demos','*.lib'])
    tktree = Tree(tkroot, tknm, excludes=['demos','*.lib'])

и все… папка encoding пакуется — кирилица вводится.
Но все равно спасибо за этот класс некоторые вещи я не знал… видимо по тому что не надо было :)
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.