うちの小学1年生の子の宿題に「計算カード」があります。学校からは「時間を計ってやりなさい」と言われているようです。
ところが、もちろん計算なんかせず、どれだけ早くめくれるかが目的になってしまっていました。まあ、そうなりますよね。妻が我慢できないようだったので、PythonのTkinterで計算カードのアプリを作ってみました。
結論から言うと、ボタンで答えを選ばないと次に進めないようにしたら、めくるだけのズルはできなくなりました。
要点
- くり下がりのない1けたの引き算(答えがマイナスにならない55通り)をランダムに出します
- 0〜9のボタンで答えを選び、正解か不正解かを表示します
- 最後に、かかった時間、点数(正解率)、間違えた問題を表示します
- 続編で、あまりのある割り算のアプリも作り、ダブルクリックで動くexeにしました
引き算の計算カードアプリ
作ったもの
- くり下がりのない1けたの引き算
- 答えがマイナスにならない組み合わせだけ(55通り)
- ランダムに出題
- 最後に、かかった時間、点数(正解率)、間違えた問題を表示
コード
import tkinter as tk
from tkinter import messagebox
from functools import partial
import numpy as np
import time
root = tk.Tk()
root.attributes("-topmost", True)
messagebox.showinfo('', 'はじめ!')
root.destroy()
q1=[0,1,2,3,4,5,6,7,8,9]
q2=[0,1,2,3,4,5,6,7,8,9]
q1=np.array(q1)
q2=np.array(q2)
ran=[]
for i in q1:
for ii in q2:
if i-ii>=0:
ran.append([i,ii])
ran=np.array(ran)
p=np.random.permutation(int(len(ran)))#シャッフル
ran=ran[p]
seikai=0
hazure=[]
def ans(a,i,ii):
b=i-ii
if a==b:
messagebox.showinfo('', 'せいかい!')
global seikai
seikai+=1
win.destroy()
else:
messagebox.showinfo('', 'ざんねん せいかいは{}です'.format(b))
global hazure
hazure.append(['{0}-{1}'.format(i,ii)])
win.destroy()
def but(i,ii):
global win
win=tk.Tk()
win.attributes("-topmost", True)#常に画面の最前に表示
win.title('ひきざんチャレンジ!')
win.geometry('500x350+100+200')
label=tk.Label(text='{0}-{1} ='.format(i,ii),font=("",30))
label.pack()
for n in range(10):
button=tk.Button(win,text='{}'.format(n),command=partial(ans,n,i,ii),width=10)
button.pack(fill = 'none', padx=10,side = 'top')
win.mainloop()
start = time.time()
for i in ran:
but(i[0],i[1])
stop =time.time()
result = stop -start
x=int(result)//60
y=int(result)%60
rp= tk.Tk()
rp.attributes("-topmost", True)
rp.geometry('300x600+100+50')
label0=tk.Label(text='{}ふん{}びょう'.format(x,y),font=("",30))
label0.pack()
label00=tk.Label(text='てんすう{}てん'.format(int(100*seikai/(seikai+len(hazure)))),font=("",30))
label00.pack()
if len(hazure)==0:
label1=tk.Label(text='ぜんもんせいかい!',font=("",30))
label1.pack()
else:
label2=tk.Label(text='にがてなものは',font=("",30))
label2.pack()
for h in hazure:
label3=tk.Label(text=h,font=("",20))
label3.pack()
label4=tk.Label(text='です、がんばりましょう!',font=("",20))
label4.pack()
messagebox.showinfo('', 'おわり')
rp.destroy()
変数がかぶっていたりする汚いコードですが、とりあえず動きます。元のコードでは0〜9のボタンを1つずつ10回書いていましたが、ここでは for でまとめて短くしました。
動かしてみる

まず、OKボタンでスタートします。同時に、時間の計測も始まります。

正解のボタンを押すと「せいかい!」と出ます。OKボタンを押して次の問題に進みます。

間違ったボタンを押すと、「ざんねん せいかいは〇です」と表示されます。

最後まで解くと、かかった時間と正解率、間違えた問題が表示されます。「おわり」のボタンでおしまいです。
続編:あまりのある割り算アプリも作った
その後、小学3年生の娘に「あまりのある割り算の問題を出してほしい」と頼まれたので、同じTkinterで割り算のアプリも作りました。
作るときに決めた条件
- 2けた÷1けたで、答え(商)は1けた、あまりは0以外
- 子供が使えるように、ダブルクリックで動くexeにする
- キーボードは使わず、マウスだけで答える
コード
元のコードには from functools import partial の1行が抜けていて、そのままだとエラーになります。下のコードでは足してあります。
import tkinter as tk
from tkinter import messagebox
from functools import partial
import time
import random
root = tk.Tk()
root.attributes("-topmost", True)
messagebox.showinfo('', 'はじめ!')
root.destroy()
q1,n,hazure,q2=[],[],[],[2,3,4,5,6,7,8,9]
ran,seikai,c=10,0,0
for i in range(10,100):
q1.append(i)
for ii in q1:
for iii in q2:
if ii%iii!=0:
if ii//iii<10:
n.append([ii,iii])
random.shuffle(n)
def sub(nn,a,b):
global c
if c==0:
global k
k=nn
c+=1
global win
win.destroy()
but(a,b,st,nn)
else:
c=0
kaito(a,b,k,nn)
def kaito(a,b,k,nn):
if a//b==k:
if a%b==nn:
messagebox.showinfo('', 'せいかい!')
global seikai
seikai+=1
global win
win.destroy()
else:
msg(a,b)
else:
msg(a,b)
def msg(a,b):
messagebox.showinfo('', 'ざんねん せいかいは{}あまり{}です'.format(a//b,a%b))
global hazure
hazure.append(['{0}÷{1}'.format(a,b)])
global win
win.destroy()
def but(a,b,f,sq):
global win
win=tk.Tk()
win.attributes("-topmost", True)#常に画面の最前に表示
win.title('あまりのあるわりざんチャレンジ!')
win.geometry('800x350+100+200')
label=tk.Label(text='{0}÷{1} ={3}あまり☆ \n※{2}にはいる数字のボタンを押そう'.format(a,b,f,sq),font=("",30))
label.pack()
for i in range(1,10):
button=tk.Button(win,text='{}'.format(i),command=partial(sub,i,a,b),width=10)
button.pack(fill = 'none', padx=10,side = 'top')
win.mainloop()
sq,st='□','次に☆'
start = time.time()
for i in range(ran):
but(n[i][0],n[i][1],sq,sq)
stop =time.time()
result = stop -start
x=int(result)//60
y=int(result)%60
rp= tk.Tk()
rp.attributes("-topmost", True)
rp.geometry('400x600+100+50')
label0=tk.Label(text='{}ふん{}びょう'.format(x,y),font=("",30))
label0.pack()
label00=tk.Label(text='てんすう{}てん'.format(int(100*seikai/(seikai+len(hazure)))),font=("",30))
label00.pack()
if len(hazure)==0:
label1=tk.Label(text='ぜんもんせいかい!',font=("",30))
label1.pack()
else:
label2=tk.Label(text='にがてなものは',font=("",30))
label2.pack()
for h in hazure:
label3=tk.Label(text=h,font=("",20))
label3.pack()
label4=tk.Label(text='です、がんばりましょう!',font=("",20))
label4.pack()
messagebox.showinfo('', 'おわり')
rp.destroy()
動かしてみる


OKを押すと時間の計測が始まり、問題が出ます。まず□に入る数字(商)を選び、次にあまりの数を選ぶと、正解か不正解かが表示されます。10問終わると、かかった時間と間違えた問題が出ます。
exeにしてダブルクリックで動かす
子供がダブルクリックだけで使えるように、実行ファイル(exe)にしました。コマンドプロンプトで「pyinstaller ファイル名」を実行するだけで変換できます。
できあがった「dist」フォルダの中にexeがあるので、それをダブルクリックすれば動きます。場所を移すときは、「dist」フォルダごと動かしてください。exeだけを移すと動かなくなります。
うまくいかなかったこと
娘には「使いにくい」と言われました。一度選んだ答えを取り消せないなど、直したいところはたくさんあります。それでも、宿題の自主勉強には使ってくれているので良しとしています。すぐに使わなくなりそうなので、これ以上は時間をかけていません。
まとめ
- 計算カードをめくるだけのズルは、ボタンで答えないと進めないアプリにしたらできなくなりました
- 引き算は55通りをランダムに出し、時間と点数と間違えた問題を表示します
- あまりのある割り算アプリも作り、pyinstallerでexeにしました
- 子供からは「使いにくい」とも言われたので、答えを取り消せるようにするのが次の課題です
Tkinterの基本は、Python Tkinter入門の記事にまとめています。
[…] 以前作った引き算アプリ「子供がズルをするので計算カードをpython(Tkinter)で作った」 […]