Start_python’s diary

ふたり暮らし

アラフィフ夫婦のフリーランスプラン

Kivy 日本語の表示と日本語を入力する(Python Kivyの取説・使い方 第12回)

日本語表示と日本語入力をする方法

1)kvファイルに日本語を対応させる

2)日本語を表示させる

3)日本語を入力する

の順番で解説していきます。

 

1)kvファイルに日本語を対応させる

まずラベル、テキストボックス、ボタンを作ります。

# フル画面を解除して画面の幅と高さを設定
from kivy.config import Config
Config.set('graphics', 'fullscreen', 0)
Config.set('graphics', 'width', 320)
Config.set('graphics', 'height', 568)
Config.set('graphics', 'resizable', 0)

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget


class MainScreen(BoxLayout):
    pass

class TestApp(App):
    def build(self):
        self.title = 'テスト'
        return MainScreen()

if __name__ == '__main__':
    TestApp().run()

kvファイル(test.kv)

<MainScreen>:
    BoxLayout:
        orientation: "vertical"

        Label:
            text: "日本語"

        TextInput:

        Button:
            text: "ボタン"

 

実行してみると、、、エラーが出ます。

UnicodeDecodeError: 'cp932' codec can't decode byte 0x9e in position 109: illegal multibyte sequence

 

こちらのサイトを参考に(丸パクリ)させていただきました。対策まで載ってます。

qiita.com

builder.py」を編集します。

C:\Program Files\Python37\Lib\site-packages\kivy\lang

のフォルダの中にありました。

f:id:Start_python:20191221171703p:plain

エラーは出なくなりましたが文字化けしています。

2)日本語を表示させる

# フル画面を解除して画面の幅と高さを設定
from kivy.config import Config
Config.set('graphics', 'fullscreen', 0)
Config.set('graphics', 'width', 320)
Config.set('graphics', 'height', 568)
Config.set('graphics', 'resizable', 0)

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget

from kivy.core.text import LabelBase, DEFAULT_FONT  # 追加分
from kivy.resources import resource_add_path  # 追加分

resource_add_path('c:/Windows/Fonts')  # 追加分
LabelBase.register(DEFAULT_FONT, 'msgothic.ttc')  # 追加分


class MainScreen(BoxLayout):
    pass

class TestApp(App):
    def build(self):
        self.title = 'テスト'
        return MainScreen()

if __name__ == '__main__':
    TestApp().run()

kvファイルはそのまま使います。

「♯ 追加分」の4行を追加します。パス名は環境によって変わる可能性があります。

f:id:Start_python:20191221174609p:plain

日本語が表示できました。

 

3)日本語を入力する

テキストボックスに日本語を入力すると、入力中の文字が表示されません。また変な改行がされてしまいます。これでは使いにくいので日本語入力に対応させます。

# フル画面を解除して画面の幅と高さを設定
from kivy.config import Config
Config.set('graphics', 'fullscreen', 0)
Config.set('graphics', 'width', 320)
Config.set('graphics', 'height', 568)
Config.set('graphics', 'resizable', 0)

import ctypes
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.resources import resource_add_path
from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.utils import platform
from kivy.base import EventLoop
from kivy.properties import StringProperty, ObjectProperty
from kivy.utils import escape_markup


resource_add_path('c:/Windows/Fonts')
LabelBase.register(DEFAULT_FONT, 'msgothic.ttc')

dll = ctypes.cdll.LoadLibrary('./ime_operator.dll')
dll.getComposition.restype = ctypes.c_char_p
dll.getEnterdString.restype = ctypes.c_char_p


class TextInputIME(TextInput):

    composition_string = StringProperty()
    sdl_composition = StringProperty()
    composition_window = ObjectProperty()

    def __init__(self, **kwargs):

        super(TextInputIME, self).__init__(**kwargs)

        self.disable_on_textedit = (False, False)
        self.is_openIME = False
        self.old_cursor_color = self.cursor_color
        self.old_composition = ''

        EventLoop.window.bind(on_textedit=self._on_textedit)

    def _on_textedit(self, _, value):

        self.sdl_composition = value
        self.is_openIME = bool(dll.getIsOpenIME())

        try:
            entered_text = dll.getEnterdString().decode('cp932')
            composition_string = dll.getComposition().decode('cp932')
        except UnicodeError:
            print('failed to decode IME information')

        if composition_string != '\n\n':
            self.composition_string = composition_string
        else:
            self.composition_string = ''

        if (entered_text != '\n\n' and self.is_openIME and self.old_composition != value):
            index = self.cursor_index()
            self.text = self.text[:index - 1] + entered_text + self.text[index:]
            self.composition_string = ''
            self.old_composition = value
            return None

        self.old_composition = value

    def insert_text(self, substring, from_undo=False):

        if substring == self.sdl_composition:
            return None
        else:
            return super(TextInputIME, self).insert_text(substring, from_undo)

    def keyboard_on_key_down(self, window, keycode, text, modifiers, dt=0):

        cursor_operations = {'left', 'up', 'right', 'down', 'backspace', 'tab'}
        self.composition_cursor_index = len(self.composition_string)

        if keycode[1] == 'left':
            self.composition_cursor_index -= 1

        if keycode[1] == 'right':
            self.composition_cursor_index += 1

        if keycode[1] in cursor_operations and self.composition_string:
            return None

        return super(
            TextInputIME,
            self).keyboard_on_key_down(
            window,
            keycode,
            text,
            modifiers)

    def on_composition_string(self, _, value):

        if self.composition_string:
            self.cursor_color = (0, 0, 0, 0)
        else:
            self.cursor_color = self.old_cursor_color

        if not dll.getIsOpenIME():
            return

        self.composition_window.text = '[u]' + value + '[/u]'


class CompositionLabel(Label):

    textinput = ObjectProperty()

    def __init__(self, **kwargs):
        super(CompositionLabel, self).__init__(**kwargs)


class MainScreen(Widget):
    pass

class TestApp(App):
    def build(self):
        self.title = 'テスト'
        return MainScreen()

if __name__ == '__main__':
    TestApp().run()

kvファイル(test.kv) 

<MainScreen>:
    BoxLayout:
        size: root.size
        orientation: "vertical"

        Label:
            text: "日本語"

        RelativeLayout:
            TextInputIME:
                id: textinput
                composition_window: cmp_window
                pos: root.pos
                size: root.size
            CompositionLabel:
                id: cmp_window
                textinput: textinput
                x: textinput.cursor_pos[0]
                y: textinput.cursor_pos[1] - self.height

        Button:
            text: "ボタン"


<CompositionLabel>:
    size_hint_x: None
    size_hint_y: None
    width: self.font_size * (len(self.text)-7)
    height: self.font_size
    color: 0,0,0,1
    markup: True
    canvas.before:
        Color:
            rgba: 1,1,1,len(self.text)-7
        Rectangle:
            pos: self.pos
            size: self.size

日本語入力できました。

f:id:Start_python:20191222161031g:plain

 

解説

まずこちらの過去記事の解説で紹介させていただきたサイトから
ime_operator.cpp」と「ime_operator.dll」を同じフォルダに保存します。

start-python.hateblo.jp

メインプログラムの前半部分(というかほとんどの部分)は「このままコピペしたら動くのでオッケー」ということで進めていきます。

class MainScreen(Widget):

ここの「Widget」が「BoxLayout」から変わっています。

 

kvファイルを見ていきます。

BoxLayout:
    size: root.size

「size: root.size」を入れないとWidgetレイアウトの大きさが「100×100」になってしまいます。

RelativeLayout:

FloatLayoutだと全てのレイアウトの絶対座標になり、RelativeLayoutならこのレイアウトだけの相対座標になります。「size: root.size」で真ん中のテキストボックスいっぱいのサイズが指定できます。

 

苦労した点

if composition_string != '\n\n':」

if (entered_text != '\n\n' and self.is_openIME and self.old_composition != value):」

'\n\n'」・・・「'¥n¥n'」の部分がなぜか「'/n/n'」になっていてなかなか気づけませんでした。

 そのときのエラーがこちらです。(わかりやすく色は変えています)

 

f:id:Start_python:20191222164056g:plain

 

まとめ

そのうちkivyのバージョンアップで日本語入力にも対応してくれると信じてますがそれまではこの方法で対応していきます。

 

 

次回はテキストボックスの読み込みと保存を作っていきます。

 

 

保存ファイル

lesson68.py

lesson69.py

test.kv

 

 

文責:Luke