Kivy ファイル選択ダイアログからテキストファイルを読み込む(Python Kivyの取説・使い方 第14回)
はじめに
今回は納得できるまでの結果は出ませんでした。まだまだ課題が残ってますが、Kivyでのポップアップ(Popup)とファイル選択(FileChooser)をやってみました。
プログラムのコード
# フル画面を解除して画面の幅と高さを設定 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 import os from kivy.uix.popup import Popup from kivy.core.window import Window 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 LoadDialog(Widget): load = ObjectProperty(None) cancel = ObjectProperty(None) class MainScreen(Widget): text = StringProperty() def on_command(self, **kwargs): content = LoadDialog(load=self.load, cancel=self.dismiss_popup) self._popup = Popup(title='Load file', content=content, size_hint=(0.9, 0.9)) self._popup.open() def dismiss_popup(self): self._popup.dismiss() def load(self, path, filename): self.ids['label1'].text = str(filename[0]) self.dismiss_popup() with open(os.path.join(path, filename[0])) as file: self.ids['textinput'].text = str(file.read()) 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: id: label1 text_size: root.width, None text: root.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: "OPEN" on_press: root.on_command() <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 <LoadDialog>: BoxLayout: size: root.size pos: root.pos orientation: "vertical" FileChooserListView: #FileChooserIconView: id: filechooser BoxLayout: size_hint_y: None height: 30 Button: text: "Cancel" on_release: root.cancel() Button: text: "Load" on_release: root.load(filechooser.path,filechooser.selection)
解説
import os
from kivy.uix.popup import Popup
from kivy.core.window import Window
これらのモジュールを新しくインポートしています。
class LoadDialog(Widget):
kvファイルで作った<LoadDialog>用にクラスを作ります。
def on_command(self, **kwargs):
content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
self._popup = Popup(title='Load file', content=content, size_hint=(0.9, 0.9))
self._popup.open()
ボタンが押されたときの処理です。ポップアップを開きます。
def load(self, path, filename):
self.ids['label1'].text = str(filename[0])
self.dismiss_popup()
with open(os.path.join(path, filename[0])) as file:
self.ids['textinput'].text = str(file.read())
ポップアップ(ファイル選択)内の「Load」ボタンが押されたときの処理です。
上のラベル部分にパスを含むファイル名を表示します。真ん中のテキストボックス部分にテキストファイルのテキストを表示します。
kvファイル
text_size: root.width, None
ラベルに表示する文字が長いときは自動で折り返します。(これを入れないと長い文字を表示させると左右にはみだしてしまいます)
FileChooserIconView:
フォルダ内をアイコンで表示します(上の画像左)
FileChooserListView:
フォルダ内をリストで表示します(上の画像右)
今後の課題
なんとなくポップアップとファイル選択はわかりました。問題点を1つずつ解決していくとかなり時間がかかりそうなので今回はこのあたりで終わりたいと思います。
実際に使うとエラーが多く発生します。
・「pywintypes.error: (32, 'GetFileAttributesEx', 'プロセスはファイルにアクセスできません。別のプロセスが使用中です。')」のエラーが出ます。
・テキストファイル以外を開こうとするとエラーになります。
・ファイルを選択せずに「Load」を押すとエラーになります。
こう出来たらいいのにもまだまだあります。
・フォルダを開いたときにCドライブの絶対パスになってしまうのを相対パスで開きたいです。
・表示するファイルはテキストファイルのみにしたいです。
・保存するときのフォルダを選択できるようにしたいです。
次回は(ラベルとボタンなどの)境界線を移動させてみたいを思います。
参考サイト
保存ファイル
lesson71.py
test.kv
文責:Luke