プログラムの概要
このプログラムでは、エクセルで以下のことができます。① エクセルに問題と回答選択肢(解説)を入力します。
A列:問題番号
B列:問題文
C列:選択肢(正答)
D~F列:選択肢(誤答)
G列:解説文
②プログラムを実行するとフォームが表示されます。
③「スタート」をクリック
④入力した問題がランダムに実行されます。回答選択肢の位置もランダムです。
⑤答えを選ぶ
⑥正答か誤答かがフィードバックされる。(入力していれば、解説も表示される)
⑦次の問題に進むかどうかを聞かれる
⑧「はい」→④に戻り、くり返し
「いいえ」→問題の終了
⑨日付け、正答率、問題番号と正誤(誤答=0、正答=1)のデータが記録される。
「データ処理」ボタンを押すと問題番号順に正誤の結果が並び替えられる。
(重複データは削除される。正答と誤答のどちらを優先するかも変更はできる)
プログラム作成の手順
本プログラムは、VBA (Visual Basic for Applications) というプログラミング言語で書かれています。エクセルでこのプログラムを実行するためには、以下のように設定を変更する必要があります。Excelメニュー[ファイル]→[オプション] → [リボンのユーザー設定]→ [メインタブ]内の[開発]にチェックをつけ[OK] → Excelのメニューに[開発]タブが追加される。
ファイルの保存は [名前を付けて保存] → ファイルの種類で[Excel マクロ有効ブック(*.xlsm)]を選択して保存する。セキュリティ警告の「コンテンツの有効化」をクリックする(詳しくは以下のリンク参照)。これで準備完了です。
Excelマクロ機能の基本操作(2007/2010/2013版)
Excelマクロ機能の基本操作(2007/2010/2013版)(3ページ目)
[開発タブ] → [Visual Basic] とクリックします。
尚、本プログラムは、以下のHPを主に参考に作成しています。
女子大生のためのExcelVBA講座
Sheetの準備
Sheet 2, Sheet 3 を新規作成します。このシートには何も入力しなくて良いです。
フォームの作成
[Sheet 1]を右クリック → [挿入] → [ユーザーフォーム] を選択する。以下のHPの手順に沿って作成してください。
Lesson4:ユーザフォームの作成
上のリンクでは、ボタンは5つですが、本プログラムは4つです。また、解説用のテキストボックスも加え、以下のようになります。
オレンジでは、各要素のオブジェクト名や大きさを示しています。オブジェクト名を間違えないように、入力してください。
コードウィンドウを開くために、作成した [スタート] ボタンをダブルクリックします。
コードウィンドウで「全体を選択 [Alt] + [A]」し、デリーとします。そして、以下のコードを貼りつけます。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Option Explicit | |
Dim CorrectAns, CmntRow | |
Private Sub UserForm_Initialize() | |
info.Visible = False | |
End Sub | |
Private Sub ToggleButton5_Click() | |
setQuizData | |
'保存用シートへのデータ貼りつけ用の最終列取得の次の列i | |
Dim i | |
i = Sheet3.Cells(1, Columns.Count).End(xlToLeft).Column | |
If Sheet3.Cells(1, i).Value <> "" Then i = i + 1 'A1が空白ならiを1とする | |
'記録用シートに日付を入力する | |
Sheet2.Range("A1").Value = Date | |
Sheet2.Range("B1").Value = "%" '後に正答率を入力する | |
Do | |
While info.Visible = False | |
DoEvents | |
Wend | |
Dim nextQuiz | |
nextQuiz = MsgBox("次の問題に進みますか?", vbInformation + vbYesNo) | |
If nextQuiz = vbYes Then | |
info.Visible = False | |
setQuizData | |
Else | |
Exit Do | |
End If | |
Loop | |
Sheet2.Range("A1").CurrentRegion.Copy 'Sheet2のデータをコピー | |
'Sheet3に貼りつけ | |
Sheet3.Cells(1, i).PasteSpecial Paste:=xlPasteValues, _ | |
Operation:=xlNone, SkipBlanks:=False, Transpose:=False | |
Sheet3.Cells(1, i).NumberFormatLocal = "mm/dd" '表示形式を00月00日へ | |
Sheet3.Cells(1, i + 1).NumberFormatLocal = "0%" '表示形式をパーセントへ | |
Sheet2.Cells.Clear '記録用シートの初期化 | |
Call getAverage(i) | |
MsgBox "問題集を終了します", vbInformation + vbOKOnly | |
Unload Me | |
End Sub | |
Private Sub getAverage(ByVal lBeginCol As Long) | |
Const TARGET_SHEET_NAME As String = "Sheet3" | |
Const COL_OFFSET As Long = 2 | |
Dim sHeader As String | |
Dim lCol As Long | |
Dim lEndRow As Long | |
Dim lTargetCol As Long | |
lCol = lBeginCol | |
With ThisWorkbook.Worksheets(TARGET_SHEET_NAME) | |
sHeader = .Cells(1, lCol).Value | |
Do Until sHeader = "" | |
lEndRow = .Cells(1, lCol).End(xlDown).Row | |
lTargetCol = lCol + 1 | |
.Cells(1, lTargetCol).Value = WorksheetFunction.Average(.Range(.Cells(2, lTargetCol), .Cells(lEndRow, lTargetCol))) | |
lCol = lCol + COL_OFFSET | |
sHeader = .Cells(1, lCol).Value | |
Loop | |
End With | |
End Sub | |
Private Sub setQuizData() | |
Randomize '乱数ジェネレータを初期化 | |
Dim rowNo | |
rowNo = Int(Rnd * Sheet1.UsedRange.Rows.Count + 1) | |
quizText.Text = Sheet1.Cells(rowNo, 2) | |
CmntText.Text = "" | |
'rowNoは問題の行数 | |
'解説を表示するためにrowNoを記録しておく | |
CmntRow = rowNo | |
'問題ナンバーを入力する行番号mを定義 | |
Dim m | |
m = Sheet2.Cells(Rows.Count, "A").End(xlUp).Row + 1 | |
ans1.Value = False | |
ans2.Value = False | |
ans3.Value = False | |
ans4.Value = False | |
ans1.Caption = "" | |
ans2.Caption = "" | |
ans3.Caption = "" | |
ans4.Caption = "" | |
'変数の説明 | |
'ansFlag: いくつ選択肢を設定したのかを記憶しておく箱 | |
'ansNo: 1から4の間で発生させた乱数の値を記憶しておく箱 | |
'colNo: Sheet1の3列目から6列目に格納されている選択肢の、何番目までを設定したのかを記憶しておく箱 | |
Dim ansFlag, ansNo, colNo | |
ansFlag = 0 | |
ansNo = 0 | |
colNo = 3 | |
While ansFlag < 4 'ansFlagが4より小さいあいだ処理をくり返す | |
ansNo = Int(Rnd * 4 + 1) '0~1までの乱数Rnd に4をかけ、1を足し、小数点以下を切り捨てるInt | |
If UserForm1.Controls("ans" & ansNo).Caption = "" Then | |
UserForm1.Controls("ans" & ansNo).Caption = Sheet1.Cells(rowNo, colNo) | |
ansFlag = ansFlag + 1 | |
Sheet2.Range("A" & m).Value = Sheet1.Cells(rowNo, 1) '記録シートに問題番号を入力 | |
'正答(Sheet1の3列目)がどのトグルボタンに設定されたかをCorrectAnsに記憶 | |
If colNo = 3 Then | |
CorrectAns = ansNo | |
End If | |
colNo = colNo + 1 | |
End If | |
Wend | |
End Sub | |
Private Sub answerJudg(tName) | |
Dim n | |
n = Sheet2.Cells(Rows.Count, "B").End(xlUp).Row + 1 | |
If UserForm1.Controls("ans" & tName).Value = False Then | |
Exit Sub | |
End If | |
If CorrectAns = tName Then | |
info.Caption = "○ 正解" | |
CmntText = Sheet1.Cells(CmntRow, 7) | |
Sheet2.Range("B" & n).Value = "1" '記録用シートに正答を記録する | |
Else | |
info.Caption = "× 不正解" | |
CmntText = Sheet1.Cells(CmntRow, 7) | |
Sheet2.Range("B" & n).Value = "0" '記録用シートに誤答を記録する | |
End If | |
info.Visible = True | |
End Sub | |
Private Sub ans1_Click() | |
answerJudg (1) | |
End Sub | |
Private Sub ans2_Click() | |
answerJudg (2) | |
End Sub | |
Private Sub ans3_Click() | |
answerJudg (3) | |
End Sub | |
Private Sub ans4_Click() | |
answerJudg (4) | |
End Sub |
Module 1は、簡単に以下のコードです。
Option Explicit
Sub UserForm_Open()
UserForm1.Show
End Sub
Module 2 は、以下のコードを貼りつけてください。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Option Explicit | |
Private Const DATA_BEGIN_ROW As Long = 2 | |
Public Sub sortAndSerialize() | |
Dim ws As Worksheet | |
Dim lCol As Long | |
Dim sDate As String | |
Dim sCorrectAnswerRate As String | |
Set ws = ThisWorkbook.ActiveSheet | |
With ws | |
lCol = ActiveCell.Column | |
sDate = .Cells(1, lCol).Value | |
Do Until sDate = "" | |
If Not IsDate(sDate) Then | |
Exit Do | |
End If | |
sCorrectAnswerRate = .Cells(1, lCol + 1).Value | |
If sCorrectAnswerRate <> "" And (Not IsDate(sCorrectAnswerRate)) Then | |
'並べ替え | |
Call sortDatas(ws, lCol) | |
'欠番挿入、重複番号削除 | |
Call toSerialize(ws, lCol) | |
'日付移動、問題番号列削除 | |
Call deleteQNoCol(ws, lCol) | |
End If | |
lCol = lCol + 1 | |
sDate = .Cells(1, lCol).Value | |
Loop | |
End With | |
End Sub | |
Private Sub sortDatas(ByRef ws As Worksheet, ByVal lQNoCol As Long) | |
Dim lEndRow As Long | |
With ws | |
lEndRow = .Cells(.Rows.Count, lQNoCol).End(xlUp).Row | |
With .Sort.SortFields | |
.Clear | |
.Add Key:=ws.Range(ws.Cells(DATA_BEGIN_ROW, lQNoCol), ws.Cells(lEndRow, lQNoCol)), _ | |
SortOn:=xlSortOnValues, _ | |
Order:=xlAscending, _ | |
DataOption:=xlSortNormal | |
'複数の異なる回答時:1を残す場合 | |
' .Add Key:=ws.Range(ws.Cells(DATA_BEGIN_ROW, lQNoCol + 1), ws.Cells(lEndRow, lQNoCol + 1)), _ | |
SortOn:=xlSortOnValues, _ | |
Order:=xlAscending, _ | |
DataOption:=xlSortNormal | |
'複数の異なる回答時:0を残す場合 | |
.Add Key:=ws.Range(ws.Cells(DATA_BEGIN_ROW, lQNoCol + 1), ws.Cells(lEndRow, lQNoCol + 1)), _ | |
SortOn:=xlSortOnValues, _ | |
Order:=xlDescending, _ | |
DataOption:=xlSortNormal | |
End With | |
With .Sort | |
.SetRange ws.Range(ws.Cells(DATA_BEGIN_ROW, lQNoCol), ws.Cells(lEndRow, lQNoCol + 1)) | |
.Header = xlNo | |
.MatchCase = False | |
.Orientation = xlTopToBottom | |
.Apply | |
End With | |
End With | |
End Sub | |
Private Sub toSerialize(ByRef ws As Worksheet, ByVal lCol As Long) | |
Dim lCurrentRow As Long | |
Dim sCurrentQNo As String | |
Dim lCurrentQNo As Long | |
Dim lPrevQNo As Long | |
Dim lInsertRows As Long | |
lCurrentRow = DATA_BEGIN_ROW | |
lPrevQNo = 0 | |
With ws | |
sCurrentQNo = CStr(.Cells(lCurrentRow, lCol).Value) | |
Do Until sCurrentQNo = "" | |
lCurrentQNo = CLng(sCurrentQNo) | |
If lCurrentQNo > lPrevQNo + 1 Then | |
'欠番あり | |
lInsertRows = lCurrentQNo - lPrevQNo - 1 | |
.Range(.Cells(lCurrentRow, lCol), .Cells(lCurrentRow + lInsertRows - 1, lCol + 1)).Insert Shift:=xlDown | |
.Cells(lCurrentRow + lInsertRows, lCol).AutoFill Destination:=.Range(.Cells(lCurrentRow, lCol), .Cells(lCurrentRow + lInsertRows, lCol)), Type:=xlFillSeries | |
With .Range(.Cells(lCurrentRow, lCol + 1), .Cells(lCurrentRow + lInsertRows - 1, lCol + 1)) | |
.NumberFormatLocal = "G/標準" | |
End With | |
lCurrentRow = lCurrentRow + lInsertRows + 1 | |
ElseIf lPrevQNo = lCurrentQNo Then | |
'同番 | |
.Range(.Cells(lCurrentRow - 1, lCol), .Cells(lCurrentRow - 1, lCol + 1)).Delete Shift:=xlUp | |
Else | |
lCurrentRow = lCurrentRow + 1 | |
End If | |
sCurrentQNo = CStr(.Cells(lCurrentRow, lCol).Value) | |
lPrevQNo = .Cells(lCurrentRow - 1, lCol).Value | |
Loop | |
End With | |
End Sub | |
Private Sub deleteQNoCol(ByRef ws As Worksheet, ByVal lDateCol As Long) | |
With ws | |
.Cells(1, lDateCol + 1).Insert Shift:=xlDown | |
.Cells(1, lDateCol).Copy Destination:=.Cells(1, lDateCol + 1) | |
.Columns(lDateCol).Delete | |
End With | |
End Sub | |
Sheet2に「スタートボタン」、Sheet3に「データ処理ボタン」をそれぞれ作成します。
そして、スタートボタンにUserForm_Open()、データ処理ボタンにsortAndSerialize()のマクロを登録します。
以上でプログラムは完成です。
問題をSheet1に入力し、スタートボタンを押すと、クイズプログラムを実行できます。資格試験などに活用してみてください。