GitLabにプロジェクトをつくってみた

今日も見に来てくださって、ありがとうございます。

 先日、tkinterのサンプルをつくってみようと思ったところに、最近使い始めたGitLabが使えないかな、ということを思いついて、ちょっとやってみました。gitは分散型のバージョン管理システムを実現するためのプログラムで、GitLabは主にそのgitのリポジトリマネージャを提供しているサービスです。早速つくってみます。 https://gitlab.com/projects/new へ移動して、以下を順番に入力します。あ、ご自分のアカウントは、適当に作ってくださいね。

 URLにアクセスすると、上記のような画面になります。タブで「Blank project」が選ばれた状態になっています。他のテンプレートから作成する方法や、インポートしてくる方法や、外部のリポジトリを利用する方法などがあるようですが、どれもぴったりくるものがなかったので、初期値の「Blank project」のままでやることにします。
Project name:今回は「tkinter example project」としました。自由に入力できます。
Project URL:アカウントに応じた初期値が入っています。
Project slug:「Project name」を入力すると自動入力されました。
Project description(optional):「tkinterのサンプル集」と入力しました。
Visibility Level:は「Private」を選択。あとからProject Settingsで変更できます。
Initialize repository with a README:チェックします。初期化してください。
さ、実行しましょう。「Create project」をクリック。

おお、なんと、一瞬でできてしまいました。

ええと、まずは、先日作ったサンプルを登録してみましょうか。最近インストールしたのですけど、Git for Windowsを使ってやってみます。(必要な方は、ここからインストールしてください。インストールすると、BASHエミューレーションされたコマンドランツールとGUIのツールがインストールされます。Windowsエクスプローラーの右クリックのメニューにも機能が追加されます。)エクスプローラでリポジトリのクローンを作成するフォルダに右クリックすると以下のようなメニューがポップアップします。

「Git GUI Here」を選択します。

Git Guiが起動します。

先ほどリポジトリサービスを作成しましたので「Clone Existing Repository」を選択します。

Source Locationに作成したリポジトリを指定、Target Directoryにローカルのフォルダを指定します。
Source Location: https://gitlab.com/TMsTone/tkinter-example-project
Traget Directory:C:\Work\tkinter_example
「Clone」を実行します。(tkinter_exampleフォルダをつくってから実行すると、そのフォルダは既に存在します、と、叱られました。削除して再実行しようとしてもGit Guiがつかんでいるらしく削除できなかったので、Git Guiを終了して、フォルダを削除してから再実行しました。みなさんはフォルダがない状態で実行しましょう。)

指定したフォルダにサンプルのtkinter_example.pyファイルを作成して、Rescanしてみました。すると、Unstaged Changesの一覧にファイルが登場しました。

Gitもあんまりよく分かっていないのですけど、どうやら「Unstaged Changes」から「Staged Changes」に移さなければいけないようですね。と、いうことで、「Stage Changed」をクリックして見ます。

「Stage 1 untracked files?」と、聞いてきましたねぇ。何なのでしょうか、よくわかりませんが、「はい(Y)」をクリックして見ます。きっと、untrakedされるのでしょう。。。

「Staged Changeds」に行きましたねぇ。そして、「Commit」かな?
あ、「Please supply a commit message」と叱られました。コミットメッセージを入力して、「Commit」してみます。

次はやっぱり「Push」ですかねぇ。クリックしてみます。

「Push」をクリックします。

なんと、うまくいったようです。

GitLabの方へ行って、Filesで確認してみたところ、「tkinter_example.py」が追加されていました。
gitの本来の使い方は少し違うのでしょうけど、ひとりで使う分にはこれで充分でしょうか。もう少し慣れてきたら、gitの使い方について書こうかな。

実行時エラー ‘1004’: アプリケーション定義またはオブジェクト定義のエラーです。

今日も見に来てくださって、ありがとうございます。
お仕事で、ちょっとつまずいたので、まとめます。誰かのお役にたつとうれしいです。

 あ、お急ぎの方に、結論から言いますと、Microsoft Excel VBAマクロでセルに正しくない計算式をセットするとこのエラーを発生させてしまいます。
 例:

Range("A1").Value = "=SUM([3:[10)"

 業務でマクロ、普通に作られていますね。ある日、「実行時エラー ‘1004’:」なんて発生して動かなくなると、ショックですよねぇ。このエラー、Google先生に聞いてみたのですけど、どうして発生するのかという条件がありすぎるせいか、原因を特定するのが難しいエラーのようです。通常のエラーだと、マクロが発生個所を教えてくれるのですが、関数内で「On Error GoTo 」が記述されてあったので、このエラー、どこで発生したのか分かりませんでした。なので1行ずつ自分でステップ実行していって、発生個所を特定する必要がありました。
 今回エラーが発生したマクロのイメージは以下のような感じです。商品ごとに毎月の売り上げがあって、1年間の合計欄を作成するようなケースです。

Sub 年間合計欄をセット()
    ' 開始行、開始列から下にある12か月分の合計セル(=SUM())を商品店数分セットします。
    Dim i As Integer
    Dim 商品点数 As Integer
    Dim 開始行 As Long
    Dim 開始列 As String
    Dim 出力列 As String
    Dim 計算式 As String

    商品点数 = 5
    開始行 = 3
    開始列 = "C"
    For i = 1 To 商品点数
        出力列 = Chr(Asc(開始列) + i - 1)
        計算式 = "=SUM(" & 出力列 & Format(開始行) & ":" & 出力列 & Format(開始行 + 12 - 1) & ")"
        Range(出力列 + Format(開始行 + 12)).Value = 計算式
    Next
 End Sub

 ここでのポイントは、「出力列」の算出方法です。「開始列」”C”は、Asc関数で67になります。これは”C”をASCII文字コードに置き換えています。これに順次数字を加えていって、商品点数5の場合、”C”(67)、”D”(68)、”E”(69)、”F”(70)、”G”(71)が出力列として算出されます。できあがる一つ目の「計算式」は、この場合「=SUM(C3:C14)」になります。一見問題なさそうに見えますね。
 問題点は、列が”Z”(90)までは見つかりません。問題は列がその隣にうつった時、”AA”≠(91)のため発生するのです。今回これを回避するためにエクセルの機能を利用して以下のように関数を作成しました。

'*****************************************************************
'*関数名 :Number2Letter
'*機能概要:入力されたカラム位置の数値からカラム文字の英数字に変換します
'*引数   :カラム位置(1~16384)
'*戻り値 :カラム文字
'*****************************************************************
Function Number2Letter(iCol As Integer) As String
    Number2Letter = Split(Columns(iCol).Address(True, False), ":")(0)
End Function

 あるいは、数値をA~Zの26文字で表現する26進数に変換する、と考えると以下のような関数をつくってもよいかも知れませんね。

Function Number2Letter(iCol As Integer) As String
    If iCol < 1 Then Number2Letter = "": Exit Function
    Number2Letter = Number2Letter(Int((iCol - 1) / 26)) & Chr(Asc("A") + ((iCol - 1) Mod 26))
End Function

 これで、スッキリ解決です。

 ちなみに、今回の問題点をわかりやすくするために、単純化して書いてありますけど、実際にはちょっとした落とし穴があって、エラーは以下のような行で発生していました。簡単にしてますけど、”A:A”,”AA:AA”は複雑な変数で記載されていましたよ。

Range("A:A","AA:AA").Value = Range("A:A","AA:AA").Value

 同じ範囲のレンジを代入しているだけなので、エラーが起きる意味が分かりませんよね。今回、対象商品が増えてエラーが発生したので、すぐに「AA」が怪しいな、とあたりを付けて、「Range(“A:A”,”Z:Z”).Value」と「Range(“AA:AA”).Value」に分けて実行したところ、前者は成功して、後者が失敗したのです。それで、エクセルは「AA」セルのコピーを失敗する不具合があるのかな、と、一つ目の落とし穴に落ちたのでした。
 このValueへのセット、実は、文字列の式を値に変換するために代入しているのでした。そのため、文字列の式に誤りがあると、今回のエラーが発生するわけです。同様のエラーを発生させるステップは以下の通りです。文章だと意味がわかりにくいと思いますので実際に実行してみてくださいね。

Sub test()
    Range("A1").NumberFormat = "@" ' 文字列書式に変更
    Range("A1").Value = "=SUM(]3:]14)" ' 正しくない式を代入
    Range("A1").NumberFormat = "General" ' 書式を戻す
    Range("A1").Value = Range("A1").Value ' エラーが発生します
End Sub

 と、いうことで、処理データが増えたときにカラム方向へセルを拡張させるときには、気を付けましょう、というお話でした。

追記

 このページ、ぼくのブログの中では一番アクセスが多いので、きっとこの問題で悩んでいる人は多いのでしょうね。ということで、またしても発生しましたので原因を追記いたします。

 再現するには、イミディエイトウィンドウで、以下のように実行します。

? Sheet1.Cells(0,1).Value
実行時エラー’1004′

 セルの行列の数値を正しくセットしていない場合にも発生します。オフィス2013のエクセルの場合、行の値の範囲は、1~1048577、列の値の範囲は1~16384の制限があるようです。なので、VBAで変数を使って行列をセットしている場合は注意が必要です。

 不覚にもこのエラーがまた出てしまいましたが、原因は、if文の条件ミスでした。変数に値をセットできておらず、0で実行してしまったのでした。みなさんも、気をつけましょう。

さらに追記

 ここまで読んでくださって、ありがとうございます。さて、ここまで読まれたあなたは、問題が解決したでしょうか?いやいや、まだ解決していない、ということでもしよけらばコメントいただけますでしょうか。Excelの記事は数えるほどしかないホームページ ですが、前述しましたとおり、このページは中でもアクセス数が多いページです。訪れられた方の問題が少しでも多く解決するといいな、という気持ちでやっておりますので、上記以外の問題でも解決できるようにしたいと思います。新たな問題、お待ちしております。

PythonでGUI

 今日も見に来てくださって、ありがとうございます。

 Pythonを使ったGUIプログラミング、何を使ったらいいのでしょうね。ネットを調べてみると、いろいろとあって悩みます。標準搭載のGUIはTkinterなのですが、ビジネスでちょっと使ってみるGUIだと、これで充分ですね。ということで、helpに記載のあった以下の例題を実行してみます。

import tkinter
from tkinter.constants import *
tk = tkinter.Tk()
frame = tkinter.Frame(tk, relief=RIDGE, borderwidth=2)
frame.pack(fill=BOTH,expand=1)
label = tkinter.Label(frame, text="Hello, World")
label.pack(fill=X, expand=1)
button = tkinter.Button(frame,text="Exit",command=tk.destroy)
button.pack(side=BOTTOM)
tk.mainloop()

でました!
定番の、Hello, Worldです♪

 何から始めるのがいいか、ちょっと迷うところではありますが、チュートリアルとかはもう概ね経験済みなので、tkinterのソースを見てみようと思います。ぼくの環境だとインストールされているフォルダは「C:\ProgramData\Anaconda3\lib\tkinter」でした。このフォルダがモジュール名なので、最初に読み込まれる「__init__.py」をざっと見てみました。基本的には、何をやるにも「self.tk.call(…)」と、呼び出しているようです。self.tkの正体は、というと、最初に「_tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)」のように呼び出されています。「_tkinter」は何者かわかりませんが、最初に「import _tkinter」と読み込まれていました。今使っているのがAnaconda3の環境なので、このフォルダ以下を「_tkinter」で検索してみました。すると、 libsフォルダ、DLLsフォルダ から「_tkinter.lib」「_tkinter.pyd」が検索されました。これらのライブラリを使ってます、ということでしょうね。利用者側としてはひとまずこれくらいの理解でよいでしょうか。

 ライブラリを使ってできることは、tkのライブラリがどうなっているのか知る必要がある、ということですね。ライブラリを参照しに行く前に、とりあえず、定義されているclassについて見ていくことにしましょう。っと、一行ずつコピペしましたが、多いなぁ。内部的なクラス(Internal class)、と、書いてあったのは、線を引いていきましょうか。む~、まだよくわかりませんねぇ。親子関係になっているものは、インデントを付けてみましょうか。

class EventType(str, enum.Enum):
class Event:
class Variable:
  class StringVar(Variable):
  class IntVar(Variable):
  class DoubleVar(Variable):
  class BooleanVar(Variable):
class Misc:
class CallWrapper:
class XView:
class YView:
class Wm:
  class Tk(Misc, Wm):
class Pack:
class Place:
class Grid:
  class BaseWidget(Misc):
    class Widget(BaseWidget, Pack, Place, Grid):
    class Toplevel(BaseWidget, Wm):
      class Button(Widget):
      class Canvas(Widget, XView, YView):
      class Checkbutton(Widget):
      class Entry(Widget, XView):
      class Frame(Widget):
      class Label(Widget):
      class Listbox(Widget, XView, YView):
      class Menu(Widget):
      class Menubutton(Widget):
      class Message(Widget):
      class Radiobutton(Widget):
      class Scale(Widget):
      class Scrollbar(Widget):
      class Text(Widget, XView, YView):
class _setit:
        class OptionMenu(Menubutton):
class Image:
  class PhotoImage(Image):
  class BitmapImage(Image):
      class Spinbox(Widget, XView):
      class LabelFrame(Widget):
      class PanedWindow(Widget):

 インデントを付けてみて、なんとなくわかってきました。画面の要素はWidgetを継承していて、→BaseWidget→Miscとなっているので、Miscが一番の先祖になっているのですね。他に、イベントを管理するためのEventクラスがあって、Variableを継承している何らかの値を保持するクラスがあって、Widegetの中でも見え方に特徴のあるものがXViewやYViewを多重継承して見せ方に特徴を持たせてそう。他のWmはウィンドウマネージャーかなぁ、何となくGUIの全体を管理しそうな、、、そして、Pack、Place、Gridは位置決めに使うクラス、あとは、メニュー用とイメージ用、と、ざっとこんな感じでしょうか。
 それ以外にtkinterのフォルダを見てみました。

colorchooser.py ネイティブのカラーダイアログ(Chooserクラス)
commondialog.py 共通のダイアログ関連(Dialogクラス)
constants.py コンスタント値関連
dialog.py ダイアログ関連(Dialogクラス)
dnd.py ドラッグアンドドロップ関連
filedialog.py ファイル関連のダイアログ関連
font.py フォント関連
messagebox.py メッセージボックス関連
scrolledtext.py スクロールするテキスト関連
simpledialog.py 簡単なダイアログ関連
test テスト用かな
tix.py Tkの拡張ウィジェット(3.6から非推奨。使いません。ttkを使いましょう。)
ttk.py Tk8.5から新しく追加された、テーマ付きウィジェットに関するもの
__init.py
__main.py
__pycache

 全体感は、これで何となくわかりました。あとは、個別の利用方法ですかね。

 と、いうことで、最近gitLaboというサービスを触り始めたので、そちらの勉強もかねてもろもろが分かりやすくなるように簡単なサンプル集でもつくりますかねぇ。動くものがないと、理解できないですよね、きっと。
サンプルをつくっていく中で、いろいろと書きたいことが増えてくると思います。

2021年1月10日 追記

 こちらに、クラス図を追記いたしました。概要を把握するのにどうぞ。