🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

967閲覧

Python3 Tkinter 決まった形式の文字列のみ入力可能にしたい

person

総合スコア224

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/11/24 08:00

"No.*"という形式の文字列のみエントリに入力できるようにしたいです。
後からエントリの文字列を拾って比較するというよりは、形式が違えば入力そのものをできないようにしたいです。

とりあえず、期待動作できるようにはなりましたが、ほかにいい方法はありますか?

スマートな方法であれば、そちらを採用したいです。
また、以下のやり方で良くない部分があれば指摘してほしいです。

Python

1import tkinter as tk 2from tkinter import ttk 3 4def func(p): 5 # エントリにインサートする文字列を "No.*" に制限(*は0文字以上の任意の文字)。 6 # IndexError を起こさないために、len(p) >= n and p[n-1] == "*" とする。 7 # 上記について、and は前者がFalseの場合、後者を見ないため。 8 # ちなみに、or は前者がTrueの場合、後者を見ない。 9 10 jouken = \ 11 p == "" or \ 12 len(p) == 1 and p[0] == "N" or \ 13 len(p) == 2 and p[1] == "o" or \ 14 len(p) == 3 and p[2] == "." or \ 15 len(p) > 3 and True 16 17 return jouken 18 19if __name__ == "__main__": 20 win = tk.Tk() 21 win.geometry("300x300+0+0") 22 23 sv = tk.StringVar() 24 25 vcmd = win.register(func) 26 27 entry = ttk.Entry(win, textvariable=sv, validatecommand=(vcmd, "%P"), validate="key") 28 entry.grid() 29 30 win.mainloop() 31 32# 参考:https://python.keicode.com/advanced/tkinter-widget-entry-validate.php

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

以下のやり方で良くない部分があれば指摘してほしいです。

良くないというか、改善可能なポイントはあります。

  • len() が1文字入力毎に、毎回最大4回呼ばれる (改善可能な点)
  • 検査する文字列を変更する時に、コードを変更することになる。(保守性の問題)

  • 入力文字が prefix で始まる → 入力文字の startswith メソッドで検査
  • prefix の入力中 ("N", "No") は、prefix.startswith で検査

python

1 2def func(p, prefix="No."): 3 return p.startswith(prefix) or prefix.startswith(p) 4 5 6if __name__ == "__main__": 7 8 import unittest 9 10 class TestInputValidataion(unittest.TestCase): 11 def test_inputNo_ok(self): 12 self.assertTrue(func("")) 13 self.assertTrue(func("N")) 14 self.assertTrue(func("No")) 15 self.assertTrue(func("No.")) 16 self.assertTrue(func("No.100")) 17 18 def test_inputNo_ng(self): 19 self.assertFalse(func("no.")) 20 self.assertFalse(func("No,")) 21 self.assertFalse(func(" No.")) 22 23 unittest.main()
  • 単体テストを作っておくことで、変更時のテストがやり易くなります。

続けて番号入力も検査するなら、他には正規表現もありますが、
入力中の状態の表現が少し複雑になりがちなので、
prefix.startswith での検査方法と合わせて使うと良いです。

  • check point: プレフィクスの "No." の表現を

 別のモノに変えたい場合、変更が一カ所で済む。

投稿2020/11/24 08:42

編集2020/11/24 08:44
teamikl

総合スコア8715

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

person

2020/11/24 12:11

回答ありがとうございます。 関数(len())が複数回呼ばれる点については考えていませんでした。 変数に入れると、定義した変数分メモリなどを消費すると思ったので使わなかったのですが、関数を複数回呼ぶと微小とはいえ時間がかかりそうですね。以後、気をつけようと思います。 startswith()については盲点でした。 nto様のご指摘にあるカーソル移動時の入力問題についてもこれで対応できそうなので、startswith()を使うことにします。
guest

0

現状だと、No.と入力した後、1~3文字目にカーソルを合わせると
その他の文字に自由に変更できる様な状態になってしまっている為
厳格に先頭の「No.」を固定させたいならばもう少し条件の見直しが必要かもしれません。

python

1 if len(p) <= 3: 2 if p in ['N', 'No', 'No.']: 3 return True 4 else: 5 return False 6 else: 7 if p[0:3] == 'No.': 8 return True 9 else: 10 return False 11

投稿2020/11/24 08:35

編集2020/11/24 08:37
nto

総合スコア1438

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

person

2020/11/24 12:06

回答ありがとうございます。 確かにカーソル移動させた時に違う文字が打てたり、その後末尾から文字を消していくと消せなくなったりするのは問題ですね。 teamikl様の回答にあるstartswith()を使って、そうならないようにします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問