質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.47%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

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

Q&A

解決済

1回答

4680閲覧

Python tkinterでcanvas上にカレンダーを表示させるプログラムを作りたいのですが…

flower

総合スコア29

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

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

0グッド

0クリップ

投稿2016/07/17 08:29

###前提・実現したいこと
Pyhonでキーボードから「1994/1」のように「年/月」という形式の文字列をコンソールに入力すると、キャンバス上にその月のカレンダーを表示するプログラムを作っているのですが、難しくて何も進めることができておりません。
また、できれば以下のようなこともできるようにしたいです。

・土曜日や日曜日を別の色で描画する。
・キャンバス上に「進む」「戻る」を表すボタンを描画し、クリックするとそれぞれ「次の月」「前の月」のカレンダーに切り替わる

###発生している問題・エラーメッセージ
エラーメッセージ以前に問題が解けません。

###課題で与えられたヒント

ヒント1 まず、指定された月の「一日(ついたち)の曜日」を調べる必要があります。 「ツェラーの公式」を利用すると便利なので、調べてみましょう。 また、上記の公式を関数として利用できるようにしておくと便利です。 なお、小数点以下切り捨てで割り算を行いたい場合、 a / b と割り算をするところを、 割り算ごとに int(a / b) のように書きましょう。 または、 a // b とすると、割った結果に床関数( floor )を適用するため、 「 a / b 以下の最大の整数」を計算できます。 ヒント2 「年」と「月」の値を引数に取り、その月の「日数」を返す関数を作りましょう。 ただし、「2月」だけは閏年の関係で年ごとに日数が異なるため、注意が必要です。 閏年の計算は次のようにできます。 •「年」が400で割り切れれば閏年 •そうでなく、「年」が100で割り切れれば閏年でない •そうでなく、「年」が4で割り切れれば閏年 •そうでない場合は閏年でない 上記についても、「閏年かどうかを判別する関数」というのを別途定義するのが良いかもしれません。 ヒント3 カレンダーは最大で6行になります。 プログラムの実現方法は無数にありますが、 たとえば2015年5月(2015/5/1は金曜日)のカレンダーを 表示しようとする場合、先に次のような形式のデータを 作っておくとやりやすいかもしれません。 calendar_data = [ 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] 上記は、先頭に余計な 0 が含まれている以外は描画しようと しているカレンダー(日曜日始まり)の内容と同じです。 なお、わかりやすいように7列ごとに改行をしていますが、 実際にはただの1次元リストです。 上記のようなデータを作ったら、キャンバス上のそれぞれの値ごとに 決まった位置に日付を描画していきます。 ただし、値が 0 になっている箇所は その月以外の日付ですので、描画を行いません。 この「決まった位置」は、次のように計算できます(先頭の要素が「0行0列目」です)。 •行番号は int(要素のインデックス / 7) で計算できる •列番号は 要素のインデックス % 7 で計算できる 例えば、上記の例で「2015年5月6日」の要素のインデックスは 10 ですので、 以下のようになります。 • int(10 / 7) = 1 • 10 % 7 = 3 つまり、「2015年5月6日」は「1行3列目」にくることになります。 この情報を元に、キャンバス上の適切な位置に「 6 」という日付を 描画すればいいことになります。 さらに、上記の6行7列の すべてにこれを繰り返せば、カレンダーを描画できます。 このような方式で進める場合は、キャンバスのことを一回忘れて、 キーボードから入力した年月から上記のデータを作ることにまず専念しましょう。 ヒント4 ヒント3のデータを作るには、以下のように考えると良いでしょう。 •先頭の 0 ◦一日(ついたち)が日曜日の場合、なにもしない(日曜日始まりの場合、週の先頭になる) ◦一日(ついたち)が月曜日の場合、リストの先頭に1個の 0 を追加する ◦一日(ついたち)が火曜日の場合、リストの先頭に2個の 0 を追加する ◦... ◦一日(ついたち)が土曜日の場合、リストの先頭に6個の 0 を追加する •中央の日付 ◦リストに 1 から対象の月の日数までの整数を順番に追加する

ヒントを読んでもあまりつかめません…
疑似コードのみでもいいのでどうか回答よろしくお願いします。

###補足情報(言語/FW/ツール等のバージョンなど)
使用ツール:Python3.5.1

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

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

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

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

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

guest

回答1

0

ベストアンサー

課題であれば、ご自身で解決されないと意味が無いと思いますので、
今回の回答は、どうやって課題を解決すれば良いかについて書かせていただきます。

いきなりtkinterでなく、コンソールで作ってみる

フォームエディターがあるような開発環境ならともかく、
そうでないGUIプログラムはtkinterに限らずですが、
初心者のかたにはまだ早いと思います。

まずは、print()関数を使って計算結果を表示するところから始めたほうが良いと思います。
Pythonの場合は、標準で対話環境(REPLと呼ばれる)ものがありますので、
それを活用しても良いです。

いっきに完成させることを考えないで、ちいさな処理から作っていく

初心者の方にありがちなのが、
1から完成までを一気に駆け上がろうとしてしまうことです。

この課題の場合は、ヒントがたくさん出ていますので、
それをひとつずつ解決していきましょう。

小さいコードを部分ごとに実行して、
それぞれがどういう動きをするのかを理解していってください。
分からないことや、こうしたらどうなるだろう、というのを
くりかえし試して覚えていって下さい。

それらがだいたい理解できてから、
全体を組み上げる作業に入ってください。

こういう過程を踏まないと、課題をこなす意味がありません。
こうすれば動くというコードがあったとしても、
それの意味をちゃんと理解しないと意味が無いのです。
こういった訓練を経て身に着けた知識を総動員して、
やっとアプリが作れるようになります。

ヒント1の「解き方」

ボリュームがそれなりに大きくてきりがないので、
今回はヒント1を例にして、進め方のコツのようなものを説明してみます。

ツェラーの公式は、ググるなりウィキペディアを見るなりして調べます。

あとは、それを関数にまとめてみましょう。
これはつまり「指定された月の初日の曜日」を返す関数を作ってみるということです。

lang

1def weekday_of_first_day_of_month(year, month): 2 # TODO 指定された月の初日の曜日を数値で返す 3 4print(weekday_of_first_day_of_month(2016, 7)) # => 6 (金) 5print(weekday_of_first_day_of_month(2012, 3)) # => 5 (木)

...

これを完成させるには長い時間がかかると思いますが、
それは必要なことなので、じっくり取り組んでみて下さい。

ただ、ちょっと難易度が高すぎるように思いますので、
可能であれば、もっと難易度が低い課題から取り組まれた方が良いかも知れません。

投稿2016/07/17 13:38

argius

総合スコア9390

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

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

flower

2016/07/22 23:30

’’’ from tkinter import * tk = Tk() c = Canvas(tk, width = 700, height = 700) c.pack() def weekday_of_first_day_of_month(year, month): a = list(str(year)) A = int(a[0]) B = int(a[1]) C = int(a[2]) D = int(a[3]) E = int(month) F = 1 W = (10*C+D)+(10*C+D)//4+(10*A+B)//4-2*(10*A+B)+13*(E+1)//5+F w = W % 7 # ABCD年E月F日 # 指定された月の初日の曜日を数値で返す # 1234560で日月火水木金土 # Wを7で割った余りが曜日 return w i = input("(西暦)何年? ") j = input("何月? ") weekday_of_first_day_of_month(int(i),int(j)) youbi = ["日","月","火","水","木","金","土"] days1 = [31,28,31,30,31,30,31,31,30,31,30,31,30,31] days2 = [31,29,31,30,31,30,31,31,30,31,30,31,30,31] for a in range(7): c.create_rectangle(10,30,400,(a+1)*100) c.create_text(75+a*100,75,text = youbi[a],font = 20) for b in range(7): c.create_rectangle(10,30,(a+1)*100,(b+1)*100) day1 = weekday_of_first_day_of_month(a,b) if day1 == 0: day1 = 6 else: day1 = day1 - 1 month = [year[m-1]-d for d in range(year[m-1])] day = 0 week = 0 while month!=[]: if day+day1 == 7: day = 0 day1 = 0 week = week + 1 x = 75 + day1*100 + day*100 y = 125 + week*100 day2 = month.pop() c.create_text(x,y,text=day2,fill="black",font=40) day = day + 1 ’’’ これでできるはずなのですが、うまく実行出来ませんでした… けど、ヒントを与えてくださったおかげで課題を進めることができました。 ありがとうございました!m(_ _)m
argius

2016/07/23 04:13

質問を読むと全く何も分からない、のように見受けられたので心配しましたが、 そこまで出来るならあとは時間の問題だと思います。 ↑のコードは90%ほどはできているみたいですね。 残りの10%はバグつぶしなので一番大変ですが... whileとmonth.popのところは、 for day2 in month:にするほうが一般的な書き方だと思います。 最後に2つ、追加でアドバイスさせて下さい。 1. 変数は、それぞれ何の値が格納されているのかが分かるようにしましょう。 weekday_of_first_day_of_month関数の中のコメントはそれができていますね。 分かりやすい変数名や関数名を付けるのが一番良いのですが、 それはまだ難しいかも知れませんので、 練習用にそれぞれの変数に日本語の説明を付けてみると良いです。 コードに直接コメントを書くと見づらくなってしまうかも知れませんので、 別ファイルに変数を整理してまとめてみるのも良いでしょう。 2. 主要な処理でないところは後で追加するようにすると良いです。 今回の場合は、指定した年月の日にちを並べるところが主要な処理で、 曜日の漢字や罫線は装飾です。 装飾は後から少しずつ追加するようにした方が、 主要部分を作るのに集中しやすくなります。 がんばってください!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問