IUP基礎 - hiz1/lua GitHub Wiki

基礎

Luaファイルの場所に、iup.dll・iuplua53.dllをコピー。

※ package.cpathで別のフォルダ指定してそこにdll置いてもダメっぽい。

Hello, world

package.cpath = package.cpath .. ";.\\?53.dll"
require("iuplua")

iup.Message("Hello World 1","Hello world from IUP.")

-- to be able to run this script inside another context
if (iup.MainLoopLevel()==0) then
  iup.MainLoop()
  iup.Close()
end

サイズは文字列で指定。"100x20"など。"QUARTERxQUARTER"でスクリーンの縦横4分の1サイズ。
true/falseも文字列で指定。"YES"/"NO"

コントロールの生成は、コンストラクタの後にオブジェクト書く。

button = iup.button{title = "OK", size="40x12"}

コールバックはコンストラクタ引数(action)に関数渡すか、コントロールのメソッド作成。

button = iup.button{title = "OK", size="40x12", action = function(self) return iup.CLOSE end}

function button:action()
    -- Exits the main loop
    return iup.CLOSE  
end

バッチファイル実行

以下のようなバッチファイルを用意。

@echo off
D:
cd D:\Develop\lua\iup\test001
lua53 main.lua %1

バッチファイルのショートカットを作成し、実行時の大きさを「最小化」に設定。

ダイアログ

ファイル選択ダイアログ(保存)呼び出しは、

local filedlg = iup.filedlg{
  dialogtype = "SAVE", 
  filter = "*.txt", 
  filterinfo = "Text Files",
}
filedlg:popup(iup.CENTER, iup.CENTER)
if (tonumber(filedlg.status) ~= -1) then
  -- ファイル選択後の処理(filedlg.valueで選択したファイルパスの取得)
end
filedlg:destroy()

ファイル選択ダイアログ(読み込み)呼び出しは、

local filedlg = iup.filedlg{
  dialogtype = "OPEN", 
  filter = "*.txt", 
  filterinfo = "Text Files",
}
filedlg:popup(iup.CENTER, iup.CENTER)
if (tonumber(filedlg.status) ~= -1) then
  -- ファイル選択後の処理(filedlg.valueで選択したファイルパスの取得)
end
filedlg:destroy()

カスタムダイアログ呼び出しは、

local line_count = multitext.linecount
local lbl_goto = iup.label{title = "Line Number [1-"..line_count.."]:"}
local txt_goto = iup.text{mask = iup.MASK_UINT, visiblecolumns = 20} --unsigned integer numbers only

local bt_goto_ok = iup.button{title = "OK", text_linecount = 0, padding = "10x2"} 
bt_goto_ok.text_linecount = line_count
function bt_goto_ok:action()
  local line_count = tonumber(self.text_linecount)
  local line = tonumber(txt_goto.value)
  if (line < 1 or line >= line_count) then
    iup.Message("Error", "Invalid line number.")
    return
  end
  goto_dlg.status = 1
  return iup.CLOSE
end

local bt_goto_cancel = iup.button{title = "Cancel", padding = "10x2"}
function bt_goto_cancel:action()
  goto_dlg.status = 0
  return iup.CLOSE
end

local box = iup.vbox{
  lbl_goto,
  txt_goto,
  iup.hbox{
    iup.fill{},
    bt_goto_ok,
    bt_goto_cancel,
    normalizesize="HORIZONTAL", 
  },
  margin = "10x10", 
  gap = "5",
}
goto_dlg = iup.dialog{
  box,
  title = "Go To Line", 
  dialogframe = "Yes", 
  defaultenter = bt_goto_ok, 
  defaultesc = bt_goto_cancel,
  parentdialog = iup.GetDialog(self)
}

goto_dlg:popup(iup.CENTERPARENT, iup.CENTERPARENT)

if (tonumber(goto_dlg.status) == 1) then
  -- OKボタン押下後の処理(txt_goto.valueで入力内容取得)
end
goto_dlg:destroy()

ツールバー・ステータスバー

ツールバー・ステータスバ-は特別なコントロールではない。
単純にダイアログの上下に置かれる横幅100%のコントロール。

ツールバー

標準アイコンを使うために、以下のモジュールをrequireする。

require("iupluaimglib")

iupluaimglib53.dll・iupimglib.dllをluaファイルのフォルダにコピー。

btn_open = iup.button{image = "IUP_FileOpen", flat = "Yes", action = item_open.action, canfocus="No", tip = "Open (Ctrl+O)"}
btn_save = iup.button{image = "IUP_FileSave", flat = "Yes", action = item_saveas.action, canfocus="No", tip = "Save (Ctrl+S)"}
btn_find = iup.button{image = "IUP_EditFind", flat = "Yes", action = item_find.action, canfocus="No", tip = "Find (Ctrl+F)"}

toolbar_hb = iup.hbox{
  btn_open,
  btn_save,
  iup.label{separator="VERTICAL"},
  btn_find,
  margin = "5x5",
  gap = 2,
  }
vbox = iup.vbox{
  toolbar_hb,
  multitext
}
dlg = iup.dialog{
  vbox,
  title = "Simple Notepad",
  size = "QUARTERxQUARTER",
  menu = menu
}

ステータスバー

lbl_statusbar = iup.label{title = "Lin 1, Col 1", expand = "HORIZONTAL", padding = "10x5"}

vbox = iup.vbox{
  toolbar_hb,
  multitext,
  lbl_statusbar,
}

ホットキー

ショートカットキー

ダイアログのコールバックk_anyを定義。

function dlg:k_any(c)
  if (c == iup.K_cO) then
    item_open:action()
  elseif (c == iup.K_cS) then
    item_saveas:action()
  elseif (c == iup.K_cF) then
    item_find:action()
  elseif (c == iup.K_cG) then
    item_goto:action()
  end
end

ホットキー

ホットキーの直前に&を記述。

item_open = iup.item{title="&Open...\tCtrl+O"}
item_saveas = iup.item{title="Save &As...\tCtrl+S"}
item_font = iup.item{title="&Font (Ctrl+F)"}
item_about = iup.item{title="&About..."}
item_find = iup.item{title="&Find...\tCtrl+F"}
item_goto = iup.item{title="&Go To..."}
item_exit = iup.item{title="E&xit"}

コンフィグファイル

初期化

config = iup.config{}
config.app_name = "simple_notepad"
config:Load()

終了時の保存

function dlg:destroy_cb()
  config:DialogClosed(iup.GetDialog(self), "MainWindow")
  config:Save()
  config:destroy()
end

書き込み

config:SetVariable("MainWindow", "Font", fontdlg.value)

読み込み

config:SetVariable("MainWindow", "Font", fontdlg.value)

最近のファイル一覧更新

config:RecentUpdate(filename)

最近のファイル一覧表示

recent_menu = iup.menu{}
file_menu = iup.menu{
  item_open,
  item_saveas,
  iup.submenu{title="Recent &Files", recent_menu},
  iup.separator{},
  item_exit
}
config:RecentInit(recent_menu, 10)

選択したファイルの読み込み

function config:recent_cb()
  local filename = self.title
  local str = read_file(filename)
  if (str) then
    multitext.value = str
  end
end

前回の表示位置・サイズでダイアログ表示

config:DialogShow(dlg, "MainWindow")

クリップボード

クリップボード用メニューアイテム作成

item_copy = iup.item{title="&Copy\tCtrl+C"}
item_paste = iup.item{title="&Paste\tCtrl+V"}
item_cut = iup.item{title="Cu&t\tCtrl+X"}
item_delete = iup.item{title="&Delete\tDel"}
item_select_all = iup.item{title="Select &All\tCtrl+A"}

クリップボード用コールバック作成

function item_paste:action()
  local clipboard = iup.clipboard{}
  multitext.insert = clipboard.text
  clipboard:destroy()
  return iup.IGNORE  -- avoid system processing for hot keys, to correctly parse line feed
end

function item_cut:action()
  local clipboard = iup.clipboard{text = multitext.selectedtext}
  multitext.selectedtext = ""
  clipboard:destroy()
end

function item_delete:action()
  multitext.selectedtext = ""
end

function item_select_all:action()
  iup.SetFocus(multitext)
  multitext.selection = "ALL"
end

メニュー設定

edit_menu = iup.menu{
  item_cut,
  item_copy,
  item_paste,
  item_delete,
  iup.separator{},
  item_find, 
  item_goto,
  iup.separator{},
  item_select_all
  }
function edit_menu:open_cb()
  local clipboard = iup.clipboard{}

  if (not clipboard.textavailable) then
    item_paste.active = "NO"
  else
    item_paste.active = "YES"
  end

  if (not multitext.selectedtext) then
    item_cut.active = "NO"
    item_delete.active = "NO"
    item_copy.active = "NO"
  else
    item_cut.active = "YES"
    item_delete.active = "YES"
    item_copy.active = "YES"
  end

  clipboard:destroy()
end

ダイナミックレイアウト

ステータスバー・ツールバーの表示・非表示を切り替える場合
※ floatingがNOだと非表示にしてもその場所にスペースが空いてしまう。

function toggle_bar_visibility(item, bar)
  if (item.value == "ON") then
    bar.floating = "YES"
    bar.visible = "NO"
    item.value = "OFF"
  else
    bar.floating = "NO"
    bar.visible = "YES"
    item.value = "ON"
  end
  iup.Refresh(bar)  -- refresh the dialog layout
end

ファイルドロップ

function multitext:dropfiles_cb(filename)
  if (save_check(self)) then
   open_file(self, filename)
  end
end

dlg = iup.dialog{
  vbox,
  title = "Simple Notepad",
  size = "HALFxHALF",
  menu = menu,
  multitext = multitext,
  dropfiles_cb = multitext.dropfiles_cb,
}

終了時保存チェック

function save_check(ih)
  local dlg = iup.GetDialog(ih)
  local multitext = dlg.multitext
  
  if (multitext.dirty) then
    local resp = iup.Alarm("Warning", "File not saved! Save it now?", "Yes", "No", "Cancel")
    if resp == 1 then -- save the changes and continue
      save_file(multitext)
    elseif resp == 3 then  -- cancel
      return false
    else  -- ignore the changes and continue
    end
  end
  return true
end

function multitext:valuechanged_cb()
  self.dirty = "YES"
end

function item_exit:action()
  if not save_check(self) then
    return iup.IGNORE  -- to abort the CLOSE_CB callback
  end
  
  config:DialogClosed(iup.GetDialog(self), "MainWindow")
  config:Save()
  config:destroy()
  return iup.CLOSE
end

dlg = iup.dialog{
  vbox,
  title = "Simple Notepad",
  size = "HALFxHALF",
  menu = menu,
  close_cb = item_exit.action
}

#コマンドライン引数

-- open a file from the command line (allow file association in Windows)
if (arg and arg[1]) then
  filename = arg[1]
  open_file(dlg, filename)
end

外部ヘルプ

function item_help:action()
  iup.Help("http://www.tecgraf.puc-rio.br/iup")
end

グラフィック

IMライブラリが別途必要。

ダウンロードリンクからDLLダウンロードできる。

im-3.12-Lua53_Win32_dllw4_lib.zip・im-3.12_Win32_dllw4_lib.zipをダウンロードしてim.dll・imlua53.dll・zlib1.dllをコピーする。

Canvas Drawと一緒に使う場合、v2は完全な互換性がある。v3で一緒に使う場合、 Bitmapしか使えない。
imFileImageLoadBitmapで画像ファイルを読み込むか、imImageをimConvertToBitmapでBitmapに変換する必要あり。

IMは画像編集ソフト作るので無ければimFileImageLoadBitmapだけ使えれば問題なさそう。後はCanvas Drawでなんとかする。

require

require("iuplua")
require("iupluaimglib")
require("imlua")
require("iupluaim")

画像ファイル読み込み

function read_file(filename)
  local image, err = im.FileImageLoadBitmap(filename, 0)
  if (err) then
    show_error(im.ErrorStr(err), true)
  else
    -- we are going to support only RGB images with no alpha
    image:RemoveAlpha()
    if (image:ColorSpace() ~= im.RGB) then
      local new_image = im.ImageCreateBased(image, nil, nil, im.RGB, nil)            

      im.ConvertColorSpace(image, new_image)
      image:Destroy()

      image = new_image
    end
  end
  return image
end

function write_file(filename, image)
  local format = image:GetAttribString("FileFormat")
  local err = im.FileImageSave(filename, format, image)
  if (err and err ~= im.ERR_NONE) then
    show_error(im.ErrorStr(err), true)
    return false
  end
  return true
end

画像新規作成

local image = im.ImageCreate(width, height, im.RGB, im.BYTE)

キャンバス作成

    config = iup.config{}
    config.app_name = "simple_paint"
    config:Load()

    canvas = iup.canvas{
      config = config,
      dirty = nil,
    }

    dlg = iup.dialog{
      vbox,
      title = "Simple Paint",
      size = "HALFxHALF",
      canvas = canvas
    }

OpenGL

luaGLライブラリが別途必要。

ダウンロードリンクからDLLダウンロード。

iupgl.dll・iupluagl53.dll・luagl.dll・luagl_base.dll・luaglu.dllをコピー。

キャンバス作成

canvas = iup.glcanvas{
  config = config,
  dirty = nil,
  buffer = "DOUBLE",
}

キャンバス描画

function canvas:action()
  local image = canvas.image
  local canvas_width, canvas_height = string.match(canvas.drawsize,"(%d*)x(%d*)")

  canvas:MakeCurrent()

  -- OpenGL configuration
  gl.PixelStore(gl.UNPACK_ALIGNMENT, 1)           -- image data alignment is 1

  gl.Viewport(0, 0, canvas_width, canvas_height)

  gl.MatrixMode(gl.PROJECTION)
  gl.LoadIdentity()
  gl.Ortho(0, canvas_width, 0, canvas_height, -1, 1)

  gl.MatrixMode(gl.MODELVIEW)
  gl.LoadIdentity()

  -- draw the background 
  local background = config:GetVariableDef("Canvas", "Background", "255 255 255")
  local r, g, b = string.match(background, "(%d*) (%d*) (%d*)")
  gl.ClearColor(r / 255, g / 255, b / 255, 1)
  gl.Clear(gl.COLOR_BUFFER_BIT)

  -- draw the image at the center of the canvas 
  if (image) then
    local x = (canvas_width - image:Width()) / 2
    local y = (canvas_height - image:Height()) / 2
    gl.RasterPos(x, y)  -- this will not work for negative values, an OpenGL limitation 
    gl.DrawPixelsRaw(image:Width(), image:Height(), gl.RGB, gl.UNSIGNED_BYTE, canvas.gldata)  -- no zoom support, must use texture
  end

  canvas:SwapBuffers()
end

CD(Canvas Draw)

CDライブラリが別途必要。

ダウンロードリンクからDLLダウンロードできる。

CDのDLL・luacdのDLLを全部コピー、cdim.dll・cdluaim53.dllをコピー。

キャンバス作成

function canvas:map_cb()
  cd_canvas = cd.CreateCanvas(cd.IUPDBUFFER, canvas)
  canvas.cdCanvas = cd_canvas
end
function canvas:unmap_cb()
  local cd_canvas = canvas.cdCanvas
  cd_canvas:Kill()
end
canvas = iup.canvas{
  config = config,
  dirty = nil,
}

キャンバス描画

function canvas:action()
  local image = canvas.image
  local canvas_width, canvas_height = string.match(canvas.drawsize,"(%d*)x(%d*)")
  local cd_canvas = canvas.cdCanvas

  cd_canvas:Activate()

  -- draw the background 
  local background = config:GetVariableDef("Canvas", "Background", "255 255 255")
  local r, g, b = string.match(background, "(%d*) (%d*) (%d*)")
  cd_canvas:Background(cd.EncodeColor(r, g, b))
  cd_canvas:Clear()

  -- draw the image at the center of the canvas 
  if (image) then
    local x = math.floor((canvas_width - image:Width()) / 2)
    local y = math.floor((canvas_height - image:Height()) / 2)
    image:cdCanvasPutImageRect(cd_canvas, x, y, image:Width(), image:Height(), 0, 0, 0, 0)
  end

  cd_canvas:Flush()
end

ツールボックス

function tool_action_cb(ih, state)
  if (state == 1) then
    local tool_index = tonumber(ih.toolindex)
    toolbox.toolindex = tool_index

    if (tool_index == 0) then
      canvas.cursor = "ARROW"
    else
      canvas.cursor = "CROSS"
    end
    if (tool_index == 8) then
      tool_get_text()
    end  
  end
end
gbox = iup.gridbox{
  iup.toggle{toolindex=0, image=load_image_PaintPointer(), value="ON", flat="Yes", tip="Pointer", action = tool_action_cb},
  iup.toggle{toolindex=1, image=load_image_PaintColorPicker(), flat="Yes", tip="Color Picker", action = tool_action_cb},
  iup.toggle{toolindex=2, image=load_image_PaintPencil(), flat="Yes", tip="Pencil", action = tool_action_cb},
  iup.toggle{toolindex=3, image=load_image_PaintLine(), flat="Yes", tip="Line", action = tool_action_cb},
  iup.toggle{toolindex=4, image=load_image_PaintRect(), flat="Yes", tip="Hollow Rectangle", action = tool_action_cb},
  iup.toggle{toolindex=5, image=load_image_PaintBox(), flat="Yes", tip="Box (Filled Rectangle)", action = tool_action_cb},
  iup.toggle{toolindex=6, image=load_image_PaintEllipse(), flat="Yes", tip="Hollow Ellipse", action = tool_action_cb},
  iup.toggle{toolindex=7, image=load_image_PaintOval(), flat="Yes", tip="Oval (Filled Ellipse)", action = tool_action_cb},
  iup.toggle{toolindex=8, image=load_image_PaintText(), flat="Yes", tip="Text", action = tool_action_cb},    
  iup.toggle{toolindex=9, image=load_image_PaintFill(), flat="Yes", tip="Fill Color", action = tool_action_cb},
  gapcol = 2,
  gaplin = 2,
  margin = "5x10",
  numdiv = 2
}
vbox = iup.vbox{
  iup.radio{gbox},