tkinterで4択クイズアプリを開発
下記サイトの写経です。
import csv import random import tkinter from tkinter import messagebox # https://daeudaeu.com/tkinter_quiz/ CSV_FILE = "quiz.csv" class Quiz(): def __init__(self, master): # master : クイズ画面を配置するウィジェット self.master = master self.quiz_list = [] self.now_quiz = None self.choice_value = tkinter.IntVar() self.getQuiz() self.createWidgets() self.showQuiz() def getQuiz(self): with open(CSV_FILE, encoding="utf-8") as f: csv_data = csv.reader(f) for quiz in csv_data: self.quiz_list.append(quiz) def createWidgets(self): self.frame = tkinter.Frame( self.master, width=400, height=200, ) self.frame.pack() self.button = tkinter.Button( self.master, text="OK", command=self.checkAnswer ) self.button.pack(pady=5) def showQuiz(self): num_quiz = random.randrange(len(self.quiz_list)) quiz = self.quiz_list[num_quiz] self.problem = tkinter.Label( self.frame, text=quiz[0] ) self.problem.grid( column=0, row=0, columnspan=4, pady=10 ) self.choices = [] for i in range(4): choice = tkinter.Radiobutton( self.frame, text=quiz[i+1], variable=self.choice_value, value=i ) choice.grid( row=1, column=i, padx=10, pady=10 ) self.choices.append(choice) self.quiz_list.remove(quiz) self.now_quiz = quiz def checkAnswer(self): if self.choice_value.get() == int(self.now_quiz[5]): messagebox.showinfo("結果", "正解です!!") else: messagebox.showinfo("結果", "不正解です…") self.deleteQuiz() if self.quiz_list: self.showQuiz() else: self.endAppli() def deleteQuiz(self): self.problem.destroy() for choice in self.choices: choice.destroy() def endAppli(self): self.problem = tkinter.Label( self.frame, text="クイズは全て出題済みです" ) self.problem.grid( column=0, row=0, padx=10, pady=10 ) self.button.config( command=self.master.destroy ) app = tkinter.Tk() quiz = Quiz(app) app.mainloop()
tkinter で「ストップウォッチ」アプリを作成
下記サイトの写経です。 daeudaeu.com
import tkinter import time # https://daeudaeu.com/stopwatch/ INTERVAL = 5 start_time = 0 start_flg = False after_id = 0 app = tkinter.Tk() app.title("stop watch") app.geometry("250x200") def update_time(): global start_time global app, label global after_id after_id = app.after(INTERVAL, update_time) now_time = time.time() elapsed_time = now_time - start_time elapsed_time_str = "{:.2f}".format(elapsed_time) label.config(text=elapsed_time_str) def start(): global app global start_time global start_flg global after_id if not start_flg: start_flg = True start_time = time.time() after_id = app.after(INTERVAL, update_time) def stop(): global start_flg global after_id if start_flg: app.after_cancel(after_id) start_flg = False def reset(): if not start_flg: label.config(text="0.00") # 時刻表示 label = tkinter.Label( app, text="0.00", width=6, font=("", 50, "bold"), ) label.pack(pady=5) #スタートボタン start_button = tkinter.Button( app, text="START", command=start ) start_button.pack(pady=5) #ストップボタン start_button = tkinter.Button( app, text="STOP", command=stop ) start_button.pack(pady=5) #リセットボタン start_button = tkinter.Button( app, text="RESET", command=reset ) start_button.pack(pady=5) app.mainloop()
達人に学ぶSQL徹底指南書 1-4 HAVING句の力
データの歯抜けを探す
テーブル定義
create table tbl1_4_1_SeqTbl( seq int, name varchar(10) ) insert into dbo.tbl1_4_1_SeqTbl values (1, 'ディック'), (2, 'アン'), (3, 'ライル'), (5, 'カー'), (6, 'マリー'), (8, 'ベン')
-- seqの歯抜けチェック select '歯抜けあり' as gap from dbo.tbl1_4_1_SeqTbl having count(*) <> max(seq);
--歯抜けの最小値を探す select min(seq + 1) as gap from dbo.tbl1_4_1_SeqTbl where (seq+1) not in (select seq from dbo.tbl1_4_1_SeqTbl) --gap --4 --not exists版 select min(seq + 1) as gap from dbo.tbl1_4_1_SeqTbl a where not exists (select * from dbo.tbl1_4_1_SeqTbl b where a.seq + 1 = b.seq) --gap --4
最頻値を求める
テーブル定義
create table tbl1_4_3_Graduates( name nvarchar(10), income int ) insert into dbo.tbl1_4_3_Graduates values ('a', 400000), ('b', 30000), ('c', 20000), ('d', 20000), ('e', 20000), ('f', 15000), ('g', 15000), ('h', 10000), ('i', 10000), ('j', 10000)
--最頻値を求める(allの利用) select income, count(*) as cnt from dbo.tbl1_4_3_Graduates group by income having count(*) >= all( select count(*) from dbo.tbl1_4_3_Graduates group by income ) --income cnt --10000 3 --20000 3
--最頻値を求める(maxの利用) select income, count(*) as cnt from dbo.tbl1_4_3_Graduates group by income having count(*) >= ( select max(cnt) from( select count(*) as cnt from dbo.tbl1_4_3_Graduates group by income ) tmp ) --income cnt --10000 3 --20000 3
中央値(メジアン)を求める
--メジアンを求める select avg(distinct income) as median from( select t1.income from dbo.tbl1_4_3_Graduates t1, dbo.tbl1_4_3_Graduates t2 group by t1.income having sum(case when t2.income >= t1.income then 1 else 0 end) >= count(*) / 2 and sum(case when t2.income <= t1.income then 1 else 0 end) >= count(*) / 2 ) tmp --median --17500
NULLを含まない集合を探す
テーブル定義
create table tbl1_4_5_Students( id int, dpt nvarchar(10), submit_date date ) insert into dbo.tbl1_4_5_Students values (100, '理学部', '2005-10-10'), (101, '理学部', '2005-09-22'), (102, '文学部', null), (103, '文学部', '2005-09-10'), (200, '文学部', '2005-09-22'), (201, '工学部', null), (202, '経済学部', '2005-09-25')
--提出日にnullを含まない(=全員提出)学部を選択する select dpt from dbo.tbl1_4_5_Students group by dpt having count(*) = sum( case when submit_date is not null then 1 else 0 end )
関係除算でバスケット解析
テーブル定義
create table tbl1_4_6_Items( item nvarchar(10) ) insert into dbo.tbl1_4_6_Items values ('ビール'), ('紙オムツ'), ('自転車') create table tbl1_4_6_ShopItems( shopname nvarchar(10), item nvarchar(10) ) insert into dbo.tbl1_4_6_ShopItems values ('仙台', 'ビール'), ('仙台', '紙オムツ'), ('仙台', '自転車'), ('仙台', 'カーテン'), ('東京', 'ビール'), ('東京', '紙オムツ'), ('東京', '自転車'), ('大阪', 'テレビ'), ('大阪', '紙オムツ'), ('大阪', '自転車')
--Itemsテーブルの商品を全て備えた店を探すSQL(他の物があってもよい) select si.shopname from dbo.tbl1_4_6_ShopItems si, dbo.tbl1_4_6_Items i where si.item = i.item group by si.shopname having count(si.item) = (select count(item) from dbo.tbl1_4_6_Items) --shopname --仙台 --東京
--Itemsテーブルの商品を全て備えた店を探すSQL(他の物があるとダメ) select si.shopname from dbo.tbl1_4_6_ShopItems si left outer join dbo.tbl1_4_6_Items i on si.item = i.item group by si.shopname having count(si.item) = (select count(item) from dbo.tbl1_4_6_Items) and count(i.item) = (select count(item) from dbo.tbl1_4_6_Items) --shopname --東京
達人に学ぶSQL徹底指南書 1-3 3値論理とNULL
3値論理の真理表
(t = true, u = unknown, f = false)
- NOT
x | NOT x |
---|---|
t | f |
u | u |
f | t |
- AND
AND | t | u | f |
---|---|---|---|
t | t | u | f |
u | u | u | f |
f | f | f | f |
- OR
OR | t | u | f |
---|---|---|---|
t | t | t | t |
u | t | u | u |
f | t | u | f |
排中律の不成立
テーブル定義
create table tbl1_3_1_Students( name nvarchar(10), age int ) insert into tbl1_3_1_Students values ('ブラウン', 22), ('ラリー', 19), ('ジョン', null), ('ボギー', 21)
select * from tbl1_3_1_Students where age = 20 or age <> 20; --ジョンの行が出力されない --name age --ブラウン 22 --ラリー 19 --ボギー 21
select * from tbl1_3_1_Students where age = 20 or age <> 20 or age is null; --3値論理での条件網羅 --name age --ブラウン 22 --ラリー 19 --ジョン NULL --ボギー 21
not in とnot existsの違い
テーブル定義
create table tbl1_3_2_ClassA( name nvarchar(10), age int, city nvarchar(10), ) insert into tbl1_3_2_ClassA values ('ブラウン', 22, '東京'), ('ラリー', 19, '埼玉'), ('ボギー', 21, '千葉') create table tbl1_3_2_ClassB( name nvarchar(10), age int, city nvarchar(10), ) insert into tbl1_3_2_ClassB values ('斎藤', 22, '東京'), ('田尻', 23, '東京'), ('山田', NULL, '東京'), ('和泉', 18, '千葉'), ('武田', 20, '千葉'), ('石川', 19, '神奈川')
-- Bクラスの東京在住の生徒と年齢が一致しないAクラスの生徒を選択するSQL? select * from tbl1_3_2_ClassA where age not in ( select age from tbl1_3_2_ClassB where city ='東京' ) -- 結果がない
where age not in (...)はwhere age <> x1 and age <> x2 and ... と同義であるが、このときage <> nullという条件式があるとunknownが発生し、どのxに対しても"where unknown"という評価になり、結果が出力されない。
-- 本当にやりたかったこと select * from tbl1_3_2_ClassA a where not exists ( select * from tbl1_3_2_ClassB b where a.age = b.age and b.city ='東京' ) --name age city --ラリー 19 埼玉 --ボギー 21 千葉
exists述語はtrueかfalseしか返さないため、unknownはfalseに評価される。
限定述語と極値関数の違い
ClassBのテーブルで東京在住の人の全ての人の年齢よりも低い年齢のClassAのテーブルの人を抽出する。
-- 限定述語 all を使う select * from dbo.tbl1_3_2_ClassA where age < all( select age from dbo.tbl1_3_2_ClassB where city = '東京' ) -- 結果の出力なし
限定述語の where age < all(...)はwhere age < x1 and age < x2 and ... と同義であるため、age < nullという条件式があると上記同様"where unknown"と評価され、結果が出力されない。
-- 極値関数 min を使う select * from dbo.tbl1_3_2_ClassB where age < ( select min(age) from dbo.tbl1_3_2_ClassB where city = '東京' ) --name age city --和泉 18 千葉 --武田 20 千葉 --石川 19 神奈川
極値関数はnullを集計に含めないため、nullを除いたageで最小値が求められる。ただし集計の対象がない場合はnullになるため注意。
達人に学ぶSQL徹底指南書 1-2 自己結合の使い方
重複順列・順列・組み合わせ
テーブル定義
use PracticeSQL
create table tbl1_2_1_Products(
prod_name nvarchar(10),
price int
)
insert into tbl1_2_1_Products values
('りんご', 100),
('みかん', 50),
('バナナ', 80)
-- prod_nameの重複順列 select p1.prod_name as name1, p2.prod_name as name2 from tbl1_2_1_Products p1, tbl1_2_1_Products p2 --name1 name2 --りんご りんご --みかん りんご --バナナ りんご --りんご みかん --みかん みかん --バナナ みかん --りんご バナナ --みかん バナナ --バナナ バナナ
-- prod_nameの順列 select p1.prod_name as name1, p2.prod_name as name2 from tbl1_2_1_Products p1, tbl1_2_1_Products p2 where p1.prod_name <> p2.prod_name --name1 name2 --みかん りんご --バナナ りんご --りんご みかん --バナナ みかん --りんご バナナ --みかん バナナ
-- prod_nameの組み合わせ select p1.prod_name as name1, p2.prod_name as name2 from tbl1_2_1_Products p1, tbl1_2_1_Products p2 where p1.prod_name > p2.prod_name --name1 name2 --りんご みかん --りんご バナナ --みかん バナナ
内容重複行の抽出
テーブル定義
use PracticeSQL
create table tbl1_2_2_Products(
rowid int identity(1, 1),
prod_name nvarchar(10),
price int
)
insert into tbl1_2_2_Products(prod_name, price) values
('りんご', 100),
('みかん', 50),
('みかん', 50),
('みかん', 50),
('バナナ', 80)
--重複行の抽出 select * from tbl1_2_2_Products p1 where rowid < ( select max(p2.rowid) from tbl1_2_2_Products p2 where p1.prod_name = p2.prod_name and p1.price = p2.price ) --rowid prod_name price --2 みかん 50 --3 みかん 50
部分的に不一致なキーの検索
テーブル定義
use PracticeSQL
create table tbl1_2_3_Addresses(
name nvarchar(10),
family_id int,
address nvarchar(30)
)
insert into tbl1_2_3_Addresses values
('前田義明', 100, '東京都港区虎ノ門3-2-29'),
('前田由美', 100, '東京都港区虎ノ門3-2-92'),
('加藤茶', 200, '東京都新宿区西新宿2-8-1'),
('加藤勝', 200, '東京都新宿区西新宿2-8-1'),
('ホームズ', 300, 'ベイカー街221B'),
('ワトソン', 400, 'ベイカー街221B')
--family_idが同じでaddressが異なるレコードを抽出 select a1.name, a1.address from tbl1_2_3_Addresses a1, tbl1_2_3_Addresses a2 where a1.family_id = a2.family_id and a1.address <> a2.address --name address --前田義明 東京都港区虎ノ門3-2-29 --前田由美 東京都港区虎ノ門3-2-92
ランキング
テーブル定義
use PracticeSQL
create table tbl1_2_4_Products(
name nvarchar(10),
price int
)
insert into tbl1_2_4_Products values
('りんご', 50),
('みかん', 100),
('ぶどう', 50),
('スイカ', 80),
('レモン', 30),
('バナナ', 50)
-- ランキング算出(OLAP) select name, price, RANK() over (order by price desc) as rank_1, DENSE_RANK() over(order by price desc) as rank_2 from tbl1_2_4_Products --name price rank_1 rank_2 --みかん 100 1 1 --スイカ 80 2 2 --ぶどう 50 3 3 --りんご 50 3 3 --バナナ 50 3 3 --レモン 30 6 4
-- RANK(自己非等値結合) select P1.name, P1.price, (select count(P2.price) from tbl1_2_4_Products P2 where P2.price > P1.price) + 1 as rank_1 from tbl1_2_4_Products P1 --name price rank_1 --りんご 50 3 --みかん 100 1 --ぶどう 50 3 --スイカ 80 2 --レモン 30 6 --バナナ 50 3
-- DENSE_RANK(自己非等値結合) select P1.name, P1.price, (select count(distinct P2.price) from tbl1_2_4_Products P2 where P2.price > P1.price) + 1 as rank_1 from tbl1_2_4_Products P1 --name price rank_1 --りんご 50 3 --みかん 100 1 --ぶどう 50 3 --スイカ 80 2 --レモン 30 4 --バナナ 50 3
--RANK(外部結合) select P1.name, max(P1.price) as price, count(P2.name) + 1 as rank_1 from tbl1_2_4_Products P1 left outer join tbl1_2_4_Products P2 on P1.price < P2.price group by P1.name --name price rank_1 --スイカ 80 2 --バナナ 50 3 --ぶどう 50 3 --みかん 100 1 --りんご 50 3 --レモン 30 6
達人に学ぶSQL徹底指南書 1-1 CASE式のススメ②
主キーの交換
テーブル定義
use PracticeSQL
create table tbl1_1_5(
p_key nvarchar(3),
col_1 int,
col_2 nvarchar(3)
)
insert into tbl1_1_5(p_key, col_1, col_2) values
('a', 1, 'あ'),
('b', 2, 'い'),
('c', 3, 'う')
-- 主キーの入れ替え(aとbの交換) update tbl1_1_5 set p_key = case when p_key = 'a' then 'b' when p_key = 'b' then 'a' else p_key end select * from tbl1_1_5 --p_key col_1 col_2 --b 1 あ --a 2 い --c 3 う
テーブル同士のマッチング(IN, EXISTS)
テーブル定義
use PracticeSQL
create table tbl1_1_6_CourseMaster(
course_id int,
course_name nvarchar(10)
)
insert into tbl1_1_6_CourseMaster(course_id, course_name) values
(1, '経理入門'),
(2, '財務知識'),
(3, '簿記検定'),
(4, '税理士')
create table tbl1_1_6_OpenCourses(
open_month int,
course_id int
)
insert into tbl1_1_6_OpenCourses(open_month, course_id) values
(200706, 1),
(200706, 3),
(200706, 4),
(200707, 4),
(200708, 2),
(200708, 4)
-- 講座と開催月の対応表出力(in) select course_name, case when course_id in ( select course_id from tbl1_1_6_OpenCourses where open_month = 200706 ) then 'o' else 'x' end as '6月', case when course_id in ( select course_id from tbl1_1_6_OpenCourses where open_month = 200707 ) then 'o' else 'x' end as '7月', case when course_id in ( select course_id from tbl1_1_6_OpenCourses where open_month = 200708 ) then 'o' else 'x' end as '8月' from tbl1_1_6_CourseMaster --course_name 6月 7月 8月 --経理入門 o x x --財務知識 x x o --簿記検定 o x x --税理士 o o o
-- 講座と開催月の対応表出力(exists) select cm.course_name, case when exists ( select course_id from tbl1_1_6_OpenCourses oc where open_month = 200706 and oc.course_id = cm.course_id ) then 'o' else 'x' end as '6月', case when exists ( select course_id from tbl1_1_6_OpenCourses oc where open_month = 200707 and oc.course_id = cm.course_id ) then 'o' else 'x' end as '7月', case when exists ( select course_id from tbl1_1_6_OpenCourses oc where open_month = 200708 and oc.course_id = cm.course_id ) then 'o' else 'x' end as '8月' from tbl1_1_6_CourseMaster cm --course_name 6月 7月 8月 --経理入門 o x x --財務知識 x x o --簿記検定 o x x --税理士 o o o
CASE式の中で集約関数をつかう
main_club_flgを掛け持ちでなくてもYとするようにすればよい気もする。
テーブル定義
use PracticeSQL
create table tbl1_1_7_StudentClub(
std_id int,
club_id int,
club_name nvarchar(10),
main_club_flg nvarchar(1)
)
insert into tbl1_1_7_StudentClub values
(100, 1, '野球', 'Y'),
(100, 2, '吹奏楽', 'N'),
(200, 2, '吹奏楽', 'N'),
(200, 3, 'バドミントン', 'Y'),
(200, 4, 'サッカー', 'N'),
(300, 4, 'サッカー', 'N'),
(400, 5, '水泳', 'N'),
(500, 6, '囲碁', 'N')
-- 複数のクラブ掛け持ちの人はメインクラブのclub_idを、 -- そうでない人は所属クラブのclub_idを出力 select std_id, case when count(*) = 1 then max(club_id) else max( case when main_club_flg = 'Y' then club_id else null end ) end as main_club_id from tbl1_1_7_StudentClub group by std_id --std_id main_club_id --100 1 --200 3 --300 4 --400 5 --500 6
達人に学ぶSQL徹底指南書 1-1 CASE式のススメ①
既存のコード体系を新しい体系に変換して集計する
テーブル定義
use PracticeSQL
create table tbl1_1(
pref_name nvarchar(50),
population int
)
insert into tbl1_1(pref_name, population) values
('徳島', 100),
('香川', 200),
('愛媛', 150),
('高知', 200),
('福岡', 300),
('佐賀', 100),
('長崎', 200),
('東京', 400),
('群馬', 50)
-- 県コードを地方コードに再分類する select case when pref_name = '徳島' then '四国' when pref_name = '香川' then '四国' when pref_name = '愛媛' then '四国' when pref_name = '高知' then '四国' when pref_name = '福岡' then '九州' when pref_name = '佐賀' then '九州' when pref_name = '長崎' then '九州' else 'その他' end district, sum(population) as 人口合計 from tbl1_1 group by case when pref_name = '徳島' then '四国' when pref_name = '香川' then '四国' when pref_name = '愛媛' then '四国' when pref_name = '高知' then '四国' when pref_name = '福岡' then '九州' when pref_name = '佐賀' then '九州' when pref_name = '長崎' then '九州' else 'その他' end --district 人口合計 --その他 450 --九州 600 --四国 650
異なる条件の集計を1つのSQLで行う
テーブル定義
use PracticeSQL
create table tbl1_1_2(
pref_name nvarchar(50),
gender int,
population int
)
insert into tbl1_1_2(pref_name, gender, population) values
('徳島', 1, 60),
('徳島', 2, 40),
('香川', 1, 100),
('香川', 2, 100),
('愛媛', 1, 100),
('愛媛', 2, 50),
('高知', 1, 100),
('高知', 2, 100),
('福岡', 1, 100),
('福岡', 2, 200),
('佐賀', 1, 80),
('佐賀', 2, 20),
('長崎', 1, 125),
('長崎', 2, 125),
('東京', 1, 250),
('東京', 2, 150)
-- 男女別人口の集計 select pref_name, sum(case when gender = '1' then population else 0 end) as cnt_m, sum(case when gender = '2' then population else 0 end) as cnt_f from tbl1_1_2 group by pref_name --pref_name cnt_m cnt_f --愛媛 100 50 --香川 100 100 --高知 100 100 --佐賀 80 20 --長崎 125 125 --東京 250 150 --徳島 60 40 --福岡 100 200
CHECK制約の練習
(本当はCASE式を使うのだが、SQLServerだと通らない…)
use PracticeSQL --給与は20万円以下 create table tbl1_1_3( gender int, salary int constraint check_salary check( salary <= 200000 ) ) insert into tbl1_1_3 values (1, 199999) insert into tbl1_1_3 values (2, 200001) --(1 行処理されました) --メッセージ 547、レベル 16、状態 0、行 12 --INSERT ステートメントは CHECK 制約 "check_salary" と競合しています。競合が発生したのは、データベース "PracticeSQL"、テーブル "dbo.tbl1_1_3", column 'salary' です。 --ステートメントは終了されました。
条件を分岐させたUPDATE
2回ロジックを通すと給与Down⇒給与Upみたいなことになるので、CASE式で処理するのが吉らしい
テーブル定義
use PracticeSQL
create table tbl1_1_4(
name nvarchar(50),
salary int
)
insert into tbl1_1_4(name, salary) values
('相田', 300000),
('神崎', 270000),
('木村', 220000),
('斎藤', 290000)
--次のようなupdateを行う -- 1.現在の給料が30万円以上の社員は、10%の減給とする -- 2.現在の給料が25万以上28万未満の社員は、20%の昇給とする update tbl1_1_4 set salary = case when 300000 <= salary then salary * 0.9 when 250000 <= salary and salary < 280000 then salary * 1.2 else salary end --name salary --相田 270000 --神崎 324000 --木村 220000 --斎藤 290000