Passed
Pull Request — master (#57)
by Yoshihiro
04:03
created

neditor.pm   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 482
Duplicated Lines 0 %

Test Coverage

Coverage 98.31%

Importance

Changes 0
Metric Value
eloc 260
dl 0
loc 482
ccs 174
cts 177
cp 0.9831
rs 9.6
c 0
b 0
f 0
wmc 35

18 Methods

Rating   Name   Duplication   Size   Complexity  
A ProcessingMenuClass.font_dialog() 0 30 1
B ProcessingMenuClass.yahooresult() 0 72 2
A ProcessingMenuClass.yahoo() 0 14 2
A ProcessingMenuClass.__init__() 0 7 1
B ProcessingMenuClass.ruby_huri() 0 37 6
A ProcessingMenuClass.is_hiragana() 0 13 1
A ProcessingMenuClass.yahoocall() 0 27 2
A ProcessingMenuClass.pyttsx3_onend() 0 13 1
A ProcessingMenuClass.on_double_click_yahoo() 0 47 4
A ProcessingMenuClass.font_size_input() 0 10 1
A ProcessingMenuClass.count_moji() 0 23 2
A ProcessingMenuClass.find_wikipedia() 0 23 2
A ProcessingMenuClass.open_becoming_novelist_page() 0 6 1
A ProcessingMenuClass.pyttsx3_onword() 0 54 3
A ProcessingMenuClass.pyttsx3_onreadend() 0 10 1
A ProcessingMenuClass.externalLoop() 0 6 1
A ProcessingMenuClass.font_size_Change() 0 21 3
A ProcessingMenuClass.read_text() 0 16 1
1
#!/usr/bin/env python3
2 1
import textwrap
3 1
import webbrowser
4 1
import tkinter as tk
5 1
import tkinter.ttk as ttk
6 1
import tkinter.messagebox as messagebox
7 1
import xml.etree.ElementTree as ET
8
9 1
import jaconv
10 1
import pyttsx3
11 1
import requests
12
13 1
from . import main
14
15
16 1
class ProcessingMenuClass(main.MainClass):
17
    """処理メニューバーのクラス.
18
19
    ・処理メニューバーにあるプログラム群
20
21
    Args:
22
        app (instance): MainProcessingClass のインスタンス
23
        tokenizer (instance): Tokenizer のインスタンス
24
        wiki_wiki (instance): wikipediaapi.Wikipedia のインスタンス
25
        locale_var (str): ロケーション
26
        master (instance): toplevel のインスタンス
27
    """
28 1
    font_size = 0
29
    """フォントのサイズ."""
30 1
    yahoo_appid = ""
31
    """Yahoo! Client ID"""
32
33 1
    def __init__(self, app, tokenizer, wiki_wiki, locale_var, master=None):
34 1
        super().__init__(locale_var, master)
35
        # yahooの校正支援
36 1
        self.KOUSEI = "{urn:yahoo:jp:jlp:KouseiService}"
37 1
        self.app = app
38 1
        self.tokenizer = tokenizer
39 1
        self.wiki_wiki = wiki_wiki
40
41 1
    def ruby_huri(self):
42
        """ルビをふり.
43
44
        ・選択文字列に小説家になろうのルビを振る。
45
        """
46 1
        hon = ""
47
        # 選択文字列を切り取る
48 1
        set_ruby = self.app.text.get('sel.first', 'sel.last')
49
        # 選択文字列を削除する
50 1
        self.app.text.delete('sel.first', 'sel.last')
51
        # 形態素解析を行う
52 1
        for token in self.tokenizer.tokenize(set_ruby):
53
            # ルビの取得
54 1
            ruby = ""
55 1
            ruby = jaconv.kata2hira(token.reading)
56
            # 解析している文字のひらがなの部分を取得
57 1
            hira = ""
58 1
            for i in token.surface:
59 1
                if self.is_hiragana(i):
60
                    hira += i
61
            # ルビがないときと、記号の時の処理
62 1
            if ruby.replace(
63
                hira, ''
64
            ) == "" or token.part_of_speech.split(
65
                ","
66
            )[0] == self.dic.get_dict("symbol"):
67
                hon += token.surface
68
            else:
69
                # ルビ振りを行う
70 1
                hon += "|{0}≪{1}≫{2}".format(
71
                    token.surface.replace(hira, ''),
72
                    ruby.replace(hira, ''),
73
                    hira
74
                )
75
76
        # テキストを表示する
77 1
        self.app.text.insert('insert', hon)
78
79 1
    @staticmethod
80
    def is_hiragana(char):
81
        """文字がひらがなか判断.
82
83
        ・与えられた文字がひらがなかどうか判断する。
84
85
        Args:
86
            char (str): 判断する文字
87
88
        Returns:
89
            bool: ひらがなならTrue、違うならFalse
90
        """
91 1
        return (0x3040 < ord(char) < 0x3097)
92
93 1
    def count_moji(self):
94
        """文字数と行数を表示.
95
96
        ・文字数と行数をカウントして表示する。
97
        """
98
        # 行数の取得
99 1
        new_line = int(self.app.text.index('end-1c').split('.')[0])
100
        # 文字列の取得
101 1
        moji = self.app.text.get('1.0', 'end')
102
        # 20文字で区切ったときの行数を数える
103 1
        gen_mai = 0
104 1
        for val in moji.splitlines():
105 1
            gen_mai += len(textwrap.wrap(val, 20))
106
        # メッセージボックスの表示
107 1
        messagebox.showinfo(
108
            self.app.dic.get_dict("Number of characters etc"),
109
            self.app.dic.get_dict(
110
                "Characters : {0} Lines : {1}\n Manuscript papers : {2}"
111
            )
112
            .format(
113
                len(moji)-new_line,
114
                new_line,
115
                -(-gen_mai//20)))
116
117 1
    def find_wikipedia(self):
118
        """意味を検索.
119
120
        ・Wikipedia-APIライブラリを使ってWikipediaから選択文字の意味を
121
        検索する。
122
        """
123
        # wikipediaから
124 1
        select_text = self.app.text.selection_get()
125 1
        page_py = self.wiki_wiki.page(select_text)
126
        # ページがあるかどうか判断
127 1
        if page_py.exists():
128 1
            messagebox.showinfo(
129
                self.app.dic.get_dict(
130
                    "Meaning of [{0}]"
131
                ).format(select_text),
132
                page_py.summary
133
            )
134
        else:
135 1
            messagebox.showwarning(
136
                self.app.dic.get_dict(
137
                    "Meaning of [{0}]"
138
                ).format(select_text),
139
                self.app.dic.get_dict("Can't find.")
140
            )
141
142 1
    def open_becoming_novelist_page(self):
143
        """小説家になろうのユーザーページを開く.
144
145
        ・インターネットブラウザで小説家になろうのユーザーページを開く。
146
        """
147 1
        webbrowser.open("https://syosetu.com/user/top/")
148
149 1
    def read_text(self):
150
        """テキストを読み上げる.
151
152
        ・pyttsx3ライブラリを使ってテキストボックスに書かれているものを読み上げる。
153
        """
154 1
        self.gyou_su = 0
155 1
        self.text_len = 0
156 1
        self.app.text.focus()
157 1
        self.read_texts = True
158 1
        self.engine = pyttsx3.init()
159 1
        self.engine.connect('started-word', self.pyttsx3_onword)
160 1
        self.engine.connect('finished-utterance', self.pyttsx3_onend)
161 1
        self.engine.setProperty('rate', 150)
162 1
        self.engine.say(self.app.text.get('1.0', 'end - 1c'))
163 1
        self.engine.startLoop(False)
164 1
        self.externalLoop()
165
166 1
    def externalLoop(self):
167
        """文章読み上げ繰り返し処理.
168
169
        ・文章読み上げを繰り返し続ける。
170
        """
171 1
        self.engine.iterate()
172
173 1
    def pyttsx3_onword(self, name, location, length):
174
        """文章を読み上げ中の処理.
175
176
        ・文章読み始めるときに止めるダイアログを出してから読み上げる。
177
        読み上げている最中は読み上げている行を選択状態にする。
178
179
        Args:
180
            name (str): 読み上げに関連付けられた名前
181
            location (int): 現在の場所
182
            length (int): 不明
183
        """
184
        # 今読んでいる場所と選択位置を比較する
185 1
        if location > self.text_len:
186
            # すべての選択一度解除する
187 1
            self.app.text.tag_remove('sel', '1.0', 'end')
188
            # 現在読んでいる場所を選択する
189 1
            self.app.text.tag_add(
190
                'sel',
191
                "{0}.0".format(self.gyou_su),
192
                "{0}.0".format(self.gyou_su+1)
193
            )
194
            # 次の行の長さをtextlenに入力する
195 1
            self.text_len += len(
196
                self.app.text.get(
197
                    '{0}.0'.format(self.gyou_su),
198
                    '{0}.0'.format(self.gyou_su+1)
199
                )
200
            )
201
            # カーソルを文章の一番後ろに持ってくる
202 1
            self.app.text.mark_set('insert', '{0}.0'.format(self.gyou_su+1))
203 1
            self.app.text.see('insert')
204 1
            self.app.text.focus()
205
            # 行を1行増やす
206 1
            self.gyou_su += 1
207
        # 読み初めての処理
208 1
        if self.read_texts:
209
            # 読むのを中止するウインドウを作成する
210 1
            self.sub_read_win = tk.Toplevel(self.app)
211 1
            button = ttk.Button(
212
                self.sub_read_win,
213
                text=self.app.dic.get_dict("Stop"),
214
                width=str(self.app.dic.get_dict("Stop")),
215
                padding=(100, 5),
216
                command=self.pyttsx3_onreadend
217
            )
218 1
            button.grid(row=1, column=1)
219
            # 最前面に表示し続ける
220 1
            self.sub_read_win.attributes("-topmost", True)
221
            # サイズ変更禁止
222 1
            self.sub_read_win.resizable(False, False)
223 1
            self.sub_read_win.title(
224
                self.app.dic.get_dict("Read aloud")
225
            )
226 1
            self.read_texts = False
227
228 1
    def pyttsx3_onreadend(self):
229
        """中止するボタンを押したときの処理.
230
231
        ・中止ボタンを押したときに読み上げをやめ、中止ウインドウ
232
        を削除する。
233
        """
234 1
        self.engine.stop()
235 1
        self.engine.endLoop()
236 1
        self.sub_read_win.destroy()
237 1
        self.app.text.tag_remove('sel', '1.0', 'end')
238
239 1
    def pyttsx3_onend(self, name, completed):
240
        """文章を読み終えた時の処理.
241
242
        ・文章を読み終えたら中止ウインドウを削除する。
243
244
        Args:
245
            name (str): 読み上げに関連付けられた名前
246
            completed (bool): 文章が読み上げ終わった(True)
247
        """
248 1
        self.engine.stop()
249 1
        self.engine.endLoop()
250 1
        self.sub_read_win.destroy()
251 1
        self.app.text.tag_remove('sel', '1.0', 'end')
252
253 1
    def yahoo(self):
254
        """Yahoo! 校正支援.
255
256
        ・Yahoo! 校正支援を呼び出し表示する。
257
258
        Args:
259
            event (instance): tkinter.Event のインスタンス
260
        """
261 1
        html = self.yahoocall(
262
            self.app.text.get('1.0', 'end -1c')
263
        )
264 1
        if not self.yahoo_appid == "":
265 1
            self.yahooresult(html)
266 1
            self.yahoo_tree.bind("<Double-1>", self.on_double_click_yahoo)
267
268 1
    def yahoocall(self, sentence=""):
269
        """yahooの校正支援を呼び出し.
270
271
        ・Yahoo! 校正支援をClient IDを使って呼び出す。
272
273
        Args:
274
            sentence (str): 校正をしたい文字列
275
276
        Returns:
277
            str: 校正結果
278
        """
279 1
        if self.yahoo_appid == "":
280 1
            messagebox.showerror(
281
                self.app.dic.get_dict("Yahoo! Client ID"),
282
                self.app.dic.get_dict(
283
                    "Yahoo! Client ID is not find."
284
                    "\nRead Readme.html and set it again."
285
                )
286
            )
287 1
            return
288 1
        url = "https://jlp.yahooapis.jp/KouseiService/V1/kousei"
289 1
        data = {
290
            "appid": self.yahoo_appid.rstrip('\n'),
291
            "sentence": sentence,
292
        }
293 1
        html = requests.post(url, data)
294 1
        return html.text
295
296 1
    def yahooresult(self, html):
297
        """校正支援を表示する画面を制作.
298
299
        ・校正結果を表示するダイアログを作成する。
300
301
        Args:
302
            html (str): 校正結果
303
        """
304 1
        xml = ET.fromstring(html)
305
        # サブウインドウの表示
306 1
        sub_win = tk.Toplevel(self.app)
307
        # ツリービューの表示
308 1
        self.yahoo_tree = ttk.Treeview(sub_win)
309 1
        self.yahoo_tree["columns"] = (1, 2, 3, 4, 5)
310
        # 表スタイルの設定(headingsはツリー形式ではない、通常の表形式)
311 1
        self.yahoo_tree["show"] = "headings"
312 1
        self.yahoo_tree.column(1, width=100)
313 1
        self.yahoo_tree.column(2, width=80)
314 1
        self.yahoo_tree.column(3, width=75)
315 1
        self.yahoo_tree.column(4, width=150)
316 1
        self.yahoo_tree.column(5, width=120)
317 1
        self.yahoo_tree.heading(
318
            1,
319
            text=self.dic.get_dict("Number of characters from the beginning")
320
        )
321 1
        self.yahoo_tree.heading(
322
            2,
323
            text=self.dic.get_dict("Number of target characters")
324
        )
325 1
        self.yahoo_tree.heading(
326
            3,
327
            text=self.dic.get_dict("Target notation")
328
        )
329 1
        self.yahoo_tree.heading(
330
            4,
331
            text=self.dic.get_dict("Paraphrase candidate string")
332
        )
333 1
        self.yahoo_tree.heading(
334
            5,
335
            text=self.dic.get_dict("Detailed information on the pointed out")
336
        )
337
        # 情報を取り出す
338 1
        for child in list(xml):
339 1
            StartPos = (child.findtext(self.KOUSEI+"StartPos"))
340 1
            Length = (child.findtext(self.KOUSEI+"Length"))
341 1
            Surface = (child.findtext(self.KOUSEI+"Surface"))
342 1
            ShitekiWord = (child.findtext(self.KOUSEI+"ShitekiWord"))
343 1
            ShitekiInfo = (child.findtext(self.KOUSEI+"ShitekiInfo"))
344 1
            self.yahoo_tree.insert(
345
                "",
346
                "end",
347
                values=(StartPos,
348
                        Length,
349
                        Surface,
350
                        ShitekiWord,
351
                        ShitekiInfo
352
                        )
353
                )
354
355 1
        self.yahoo_tree.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W))
356
        # スクロールバーを表示する
357 1
        SCRLBAR_Y = ttk.Scrollbar(
358
            sub_win,
359
            orient=tk.VERTICAL,
360
            command=self.yahoo_tree.yview
361
        )
362 1
        self.yahoo_tree.configure(yscroll=SCRLBAR_Y.set)
363 1
        SCRLBAR_Y.grid(row=0, column=1, sticky=(tk.N, tk.S))
364
        # 最前面に表示し続ける
365 1
        sub_win.attributes("-topmost", True)
366 1
        sub_win.title(
367
            self.app.dic.get_dict("Sentence structure")
368
        )
369
370 1
    def on_double_click_yahoo(self, event=None):
371
        """Yahoo! 校正支援リストをダブルクリック.
372
373
        ・Yahoo! 校正支援ダイアログのリストをダブルクリックすると
374
        その該当箇所を選択する。
375
376
        Args:
377
            event (instance): tkinter.Event のインスタンス
378
        """
379 1
        i = 0
380 1
        textlen = 0
381 1
        textforlen = 0
382 1
        curItem = self.yahoo_tree.focus()
383 1
        value = self.yahoo_tree.item(curItem)
384
        # 出てくる場所を取得
385 1
        val = int(value.get("values")[0])
386
        # 出てくる文字数を取得
387 1
        lenge = value.get("values")[1]
388
        # 何行目になるか確認する
389 1
        while True:
390 1
            if val > textlen:
391 1
                i += 1
392 1
                textforlen = textlen
393 1
                textlen += len(
394
                    self.app.text.get(
395
                        '{0}.0'.format(i),
396
                        '{0}.0'.format(i+1)
397
                    )
398
                )
399
            else:
400 1
                break
401 1
        if i == 0:
402
            i = 1
403
        # 選択状態を一旦削除
404 1
        self.app.text.tag_remove('sel', '1.0', 'end')
405
        # 選択状態にする
406 1
        self.app.text.tag_add(
407
            'sel',
408
            "{0}.{1}".format(i, val-textforlen),
409
            "{0}.{1}".format(i, val-textforlen+lenge)
410
        )
411
        # カーソルの移動
412 1
        self.app.text.mark_set('insert', '{0}.{1}'.format(i, val-textforlen))
413 1
        self.app.text.see('insert')
414
        # フォーカスを合わせる
415 1
        self.app.text.focus()
416 1
        return
417
418 1
    def font_dialog(self, event=None):
419
        """フォントサイズダイアログを作成.
420
421
        ・フォントサイズダイアログを作成し表示する。
422
423
        Args:
424
            event (instance): tkinter.Event のインスタンス
425
        """
426 1
        self.app.sub_wins = tk.Toplevel(self.app)
427 1
        self.app.intSpin = ttk.Spinbox(self.app.sub_wins, from_=12, to=72)
428 1
        self.app.intSpin.grid(
429
            row=0,
430
            column=0,
431
            columnspan=2,
432
            padx=5,
433
            pady=5,
434
            sticky=tk.W+tk.E,
435
            ipady=3
436
        )
437 1
        button = ttk.Button(
438
            self.app.sub_wins,
439
            text=self.app.dic.get_dict("Resize"),
440
            width=str(self.app.dic.get_dict("Resize")),
441
            padding=(10, 5),
442
            command=self.font_size_Change
443
        )
444 1
        button.grid(row=1, column=1)
445 1
        self.app.intSpin.set(ProcessingMenuClass.font_size)
446 1
        self.app.sub_wins.title(
447
            self.app.dic.get_dict("Font size")
448
        )
449
450 1
    def font_size_Change(self):
451
        """フォントのサイズの変更.
452
453
        ・サイズ変更を押されたときにサイズを変更する。
454
        上は72ptまで下は12ptまでにする。
455
        """
456
        # 比較のため数値列に変更
457 1
        font_size = int(self.app.intSpin.get())
458 1
        if font_size < 12:  # 12より下の値を入力した時、12にする
459 1
            font_size = 12
460 1
        elif 72 < font_size:  # 72より上の値を入力した時、72にする
461 1
            font_size = 72
462
        # 文字列にもどす
463 1
        self.font_size_input(str(font_size))
464 1
        self.app.sub_wins.destroy()
465
        # フォントサイズの変更
466 1
        self.app.text.configure(font=(self.app.font, self.font_size))
467
        # ラインナンバーの変更
468 1
        self.app.spc.update_line_numbers()
469
        # ハイライトのやり直し
470 1
        self.app.hpc.all_highlight()
471
472 1
    @classmethod
473
    def font_size_input(cls, font_size):
474
        """フォントサイズを入力.
475
476
        ・フォントサイズをクラス変数に入力する。
477
478
        Args:
479
            font_size (str): フォントサイズ
480
        """
481
        cls.font_size = font_size
482