Test Failed
Push — master ( 0ab505...25648c )
by Yoshihiro
03:11
created

ProcessingMenuClass.yahooresult()   A

Complexity

Conditions 2

Size

Total Lines 55
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 35
dl 0
loc 55
rs 9.0399
c 0
b 0
f 0
cc 2
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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