熊本大学医学部附属病院 医療情報部
助手 佐藤純三
mailto:jsato@fc.kuh.kumamoto-u.ac.jp
2001.3.1
これは2月22日に(株)日本電子計算で行われたMathematica SIGの記録です。MathematicaとJavaをつなぐJ/Linkについてお話しした、概要をお伝えします。いくつかサンプルを用意しましたので、J/Link環境を整えてからお試し下さい。著者はApple MRJ2.2.3(JDK1.1.8相当)とJ/Link 1.0.1の組み合わせで作成しました。JDKもJ/Linkも最新版ではないことを予め御了承願います。
J/Linkの1つ目の使い方は、MathematicaからJavaを操作することです。LoadClass[]やJavaNew[]といった関数を使って、JavaクラスをMathematicaに認識させ、オブジェクトを生成して活用します。Mathematica言語で記述するので、他の様々な外部パッケージと同様に、
<<JLink`
により利用可能になります。
フレームを1枚用意して、換気扇を回すJavaアプリケーションを作ってみました。
クラスFanAppの中にmain()があり、FanFrameクラスを使って初期化を行っています。
/*
FanApp.java
Title:FanApp
Author:RoboDog
Description:
*/
package Fan;
import javax.swing.*;
public class FanApp{
FanFrame frame;
public FanFrame getFrame() {
return frame;
}
public FanApp() {
try {
//For native Look and Feel,uncomment the following code.
/*
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}
*/
frame=new FanFrame();
frame.initComponents();
frame.setVisible(true);
frame.doSomething();
} catch (Exception e) {
e.printStackTrace();
}
}
//Main entry point
static public void main(String[] args){
new FanApp();
}
}
/*
FanFrame.java
Title:FanApp
Author:RoboDog
Description:
*/
package Fan;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FanFrame extends JFrame{
Icon iconFan[]={
new ImageIcon("./image/fan00.gif"),
new ImageIcon("./image/fan01.gif"),
new ImageIcon("./image/fan02.gif"),
new ImageIcon("./image/fan03.gif"),
new ImageIcon("./image/fan04.gif"),
new ImageIcon("./image/fan05.gif")
};
boolean isRunning=false;
//IMPORTANT:Source code between BEGIN/END comment pair will be regenerated
//every time the form is saved.All manual changes will be overwritten.
//BEGIN GENERATED CODE
//member declarations
javax.swing.JButton jButton1=new javax.swing.JButton();
javax.swing.JLabel jLabel1=new javax.swing.JLabel();
//END GENERATED CODE
public FanFrame() {}
public void initComponents() throws Exception {
//IMPORTANT:Source code between BEGIN/END comment pair will be regenerated
//every time the form is saved.All manual changes will be overwritten.
//BEGIN GENERATED CODE
//the following code sets the frame's initial state
jButton1.setText("Start");
jButton1.setLocation(new java.awt.Point(20,150));
jButton1.setVisible(true);
jButton1.setSize(new java.awt.Dimension(132,32));
jLabel1.setLocation(new java.awt.Point(20,10));
jLabel1.setVisible(true);
jLabel1.setSize(new java.awt.Dimension(132,132));
setLocation(new java.awt.Point(0,0));
setTitle("Fan.FanFrame");
getContentPane().setLayout(null);
setSize(new java.awt.Dimension(173,188));
getContentPane().add(jButton1);
getContentPane().add(jLabel1);
jButton1.addActionListener(
new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
jButton1ActionPerformed(e);
}
}
);
addWindowListener(
new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
thisWindowClosing(e);
}
}
);
//END GENERATED CODE
jLabel1.setIcon(iconFan[0]);
repaint();
}
int fanCnt=0;
public void rotate() {
fanCnt++;
if (fanCnt>5) {
fanCnt=0;
}
jLabel1.setIcon(iconFan[fanCnt]);
repaint();
}
Thread th=null;
public void doSomething() {
th=new Thread() {
public void run() {
while (true) {
try {sleep(100);//msec
if (isRunning)
rotate();
} catch (InterruptedException e) {
break;
}
}
}
};
th.start();
}
public void startRotation() {
isRunning=true;
jButton1.setText("Stop");
}
public void stopRotation() {
isRunning=false;
jButton1.setText("Start");
}
private boolean mShown=false;
public void addNotify() {
super.addNotify();
if (mShown) return;
//resize frame to account for menubar
JMenuBar jMenuBar=getJMenuBar();
if (jMenuBar≠null) {
int jMenuBarHeight=jMenuBar.getPreferredSize().height;
Dimension dimension=getSize();
dimension.height+=jMenuBarHeight;
setSize(dimension);
}
mShown=true;
}
//Close the window when the close box is clicked
void thisWindowClosing(java.awt.event.WindowEvent e) {
setVisible(false);
dispose();
System.exit(0);
}
public void jButton1ActionPerformed(java.awt.event.ActionEvent e) {
if (isRunning) {
stopRotation();
} else {
startRotation();
}
}
}
Java環境の初期化と,アプリケ-ションの読み込みをします.
![[Graphics:Images/jlink-tutorial_gr_1.gif]](Images/jlink-tutorial_gr_1.gif)
![[Graphics:Images/jlink-tutorial_gr_2.gif]](Images/jlink-tutorial_gr_2.gif)
![[Graphics:Images/jlink-tutorial_gr_4.gif]](Images/jlink-tutorial_gr_4.gif)
アプリケ-ションFanAppのもつメソッドを調べてみると、getFrame()というのが見つかります。
これはJavaソースの中で自分で用意しておいたものです。
![[Graphics:Images/jlink-tutorial_gr_6.gif]](Images/jlink-tutorial_gr_6.gif)
boolean equals(Object) |
Fan.FanFrame getFrame() |
Class getClass() |
void notify() |
void notifyAll() |
void wait(long) throws InterruptedException |
void wait(long, int) throws InterruptedException |
void wait() throws InterruptedException |
int hashCode() |
String toString() |
static void main(String[]) |
表示されているフレームを取り出してみましょう。
![[Graphics:Images/jlink-tutorial_gr_7.gif]](Images/jlink-tutorial_gr_7.gif)
同様にFanFrameクラスにはstartRotaion(),stopRotation()というメソッドが見つかります.
![[Graphics:Images/jlink-tutorial_gr_10.gif]](Images/jlink-tutorial_gr_10.gif)
boolean action(java.awt.Event, Object) |
boolean contains(int, int) |
boolean contains(java.awt.Point) |
boolean gotFocus(java.awt.Event, Object) |
boolean handleEvent(java.awt.Event) |
boolean imageUpdate(java.awt.Image, int, int, int, int, int) |
boolean inside(int, int) |
boolean isEnabled() |
boolean isFocusTraversable() |
boolean isValid() |
boolean isVisible() |
boolean keyDown(java.awt.Event, int) |
boolean keyUp(java.awt.Event, int) |
boolean lostFocus(java.awt.Event, Object) |
boolean mouseDown(java.awt.Event, int, int) |
boolean mouseDrag(java.awt.Event, int, int) |
boolean mouseEnter(java.awt.Event, int, int) |
boolean mouseExit(java.awt.Event, int, int) |
boolean mouseMove(java.awt.Event, int, int) |
boolean mouseUp(java.awt.Event, int, int) |
boolean prepareImage(java.awt.Image, int, int, java.awt.image.ImageObserver) |
boolean prepareImage(java.awt.Image, java.awt.image.ImageObserver) |
boolean isAncestorOf(java.awt.Component) |
boolean isResizable() |
boolean isShowing() |
boolean postEvent(java.awt.Event) |
boolean equals(Object) |
Object getTreeLock() |
String getWarningString() |
Class getClass() |
void notify() |
void notifyAll() |
void wait(long) throws InterruptedException |
void dispatchEvent(java.awt.AWTEvent) |
void wait(long, int) throws InterruptedException |
void wait() throws InterruptedException |
float getAlignmentX() |
float getAlignmentY() |
int checkImage(java.awt.Image, int, int, java.awt.image.ImageObserver) |
int checkImage(java.awt.Image, java.awt.image.ImageObserver) |
int countComponents() |
int getComponentCount() |
int getCursorType() |
int hashCode() |
int getDefaultCloseOperation() |
java.awt.Color getBackground() |
java.awt.Color getForeground() |
java.awt.Component add(java.awt.Component) |
java.awt.Component add(java.awt.Component, int) |
java.awt.Component add(String, java.awt.Component) |
java.awt.Component getComponentAt(int, int) |
java.awt.Component getComponentAt(java.awt.Point) |
java.awt.Component getComponent(int) |
java.awt.Component[] getComponents() |
java.awt.Component locate(int, int) |
java.awt.Component getFocusOwner() |
java.awt.Component getGlassPane() |
java.awt.Container getParent() |
java.awt.Container getContentPane() |
java.awt.Cursor getCursor() |
java.awt.Dimension getSize() |
java.awt.Dimension size() |
java.awt.Dimension getMaximumSize() |
java.awt.Dimension getMinimumSize() |
java.awt.Dimension getPreferredSize() |
java.awt.Dimension minimumSize() |
java.awt.Dimension preferredSize() |
java.awt.Font getFont() |
java.awt.FontMetrics getFontMetrics(java.awt.Font) |
java.awt.Graphics getGraphics() |
java.awt.image.ColorModel getColorModel() |
java.awt.Image createImage(int, int) |
java.awt.Image createImage(java.awt.image.ImageProducer) |
java.awt.Image getIconImage() |
java.awt.Insets getInsets() |
java.awt.Insets insets() |
java.awt.LayoutManager getLayout() |
java.awt.MenuBar getMenuBar() |
java.awt.peer.ComponentPeer getPeer() |
java.awt.Point getLocation() |
java.awt.Point getLocationOnScreen() |
java.awt.Point location() |
java.awt.Rectangle bounds() |
java.awt.Rectangle getBounds() |
java.awt.Toolkit getToolkit() |
String getName() |
String toString() |
String getTitle() |
java.util.Locale getLocale() |
javax.accessibility.AccessibleContext getAccessibleContext() |
javax.swing.JLayeredPane getLayeredPane() |
javax.swing.JMenuBar getJMenuBar() |
javax.swing.JRootPane getRootPane() |
void addComponentListener(java.awt.event.ComponentListener) |
void addFocusListener(java.awt.event.FocusListener) |
void add(java.awt.PopupMenu) |
void addKeyListener(java.awt.event.KeyListener) |
void addMouseListener(java.awt.event.MouseListener) |
void addMouseMotionListener(java.awt.event.MouseMotionListener) |
void removeComponentListener(java.awt.event.ComponentListener) |
void removeFocusListener(java.awt.event.FocusListener) |
void removeKeyListener(java.awt.event.KeyListener) |
void removeMouseListener(java.awt.event.MouseListener) |
void removeMouseMotionListener(java.awt.event.MouseMotionListener) |
void addContainerListener(java.awt.event.ContainerListener) |
void removeContainerListener(java.awt.event.ContainerListener) |
void setCursor(int) |
void setIconImage(java.awt.Image) |
void setResizable(boolean) |
void setTitle(String) |
void addWindowListener(java.awt.event.WindowListener) |
void removeWindowListener(java.awt.event.WindowListener) |
void setCursor(java.awt.Cursor) |
void addNotify() |
void doSomething() |
void initComponents() throws Exception |
void jButton1ActionPerformed(java.awt.event.ActionEvent) |
void rotate() |
void startRotation() |
void stopRotation() |
void disable() |
void enable() |
void enable(boolean) |
void hide() |
void list() |
void list(java.io.PrintStream) |
void list(java.io.PrintWriter) |
void move(int, int) |
void nextFocus() |
void paintAll(java.awt.Graphics) |
void printAll(java.awt.Graphics) |
void repaint() |
void repaint(int, int, int, int) |
void repaint(long) |
void repaint(long, int, int, int, int) |
void requestFocus() |
void reshape(int, int, int, int) |
void resize(int, int) |
void resize(java.awt.Dimension) |
void setBackground(java.awt.Color) |
void setBounds(int, int, int, int) |
void setBounds(java.awt.Rectangle) |
void setEnabled(boolean) |
void setFont(java.awt.Font) |
void setForeground(java.awt.Color) |
void setLocale(java.util.Locale) |
void setLocation(int, int) |
void setLocation(java.awt.Point) |
void setName(String) |
void setSize(int, int) |
void setSize(java.awt.Dimension) |
void setVisible(boolean) |
void show(boolean) |
void transferFocus() |
void add(java.awt.Component, Object) |
void add(java.awt.Component, Object, int) |
void deliverEvent(java.awt.Event) |
void doLayout() |
void invalidate() |
void layout() |
void list(java.io.PrintStream, int) |
void list(java.io.PrintWriter, int) |
void paintComponents(java.awt.Graphics) |
void paint(java.awt.Graphics) |
void printComponents(java.awt.Graphics) |
void print(java.awt.Graphics) |
void removeAll() |
void remove(int) |
void removeNotify() |
void validate() |
void dispose() |
void remove(java.awt.MenuComponent) |
void setMenuBar(java.awt.MenuBar) |
void pack() |
void show() |
void toBack() |
void toFront() |
void remove(java.awt.Component) |
void setContentPane(java.awt.Container) |
void setDefaultCloseOperation(int) |
void setGlassPane(java.awt.Component) |
void setJMenuBar(javax.swing.JMenuBar) |
void setLayeredPane(javax.swing.JLayeredPane) |
void setLayout(java.awt.LayoutManager) |
void update(java.awt.Graphics) |
Javaアプリケ-ションでStartボタンを押す代わりに,MathematicaからStartさせてみましょう.
ボタンのタイトルがStopに変わって、換気扇が回りはじめます。
![[Graphics:Images/jlink-tutorial_gr_11.gif]](Images/jlink-tutorial_gr_11.gif)
停止も同じ要領です。
![[Graphics:Images/jlink-tutorial_gr_13.gif]](Images/jlink-tutorial_gr_13.gif)
J/Linkを終了しMathKernelも停止するにはUninstallJava[]とQuit[]を使います。
![[Graphics:Images/jlink-tutorial_gr_15.gif]](Images/jlink-tutorial_gr_15.gif)
![[Graphics:Images/jlink-tutorial_gr_17.gif]](Images/jlink-tutorial_gr_17.gif)
上のFanAppではJava Swingのパッケ-ジからJFrame,JButton等を使いましたが,Java Swingの持つ豊富なGUI部品群もいろいろ工夫して使えます.下の図はJTabbedPaneの例です。
J/Linkを通じてJavaのパッケージが自由に利用できるようになると、画像処理のエンジンをMathematicaにして、インターフェースをSwingなどに任せた「統合環境」が作れるわけです。現在Digital Image ProcessingのMathematica Packageを利用するJava PhotoEditorを制作中です。
今度はJavaコードの中からMathematicaに式を評価させるプログラムの例です。
基本的にリンクオブジェクトのevaluate()メソッドを呼び出すことにより、任意の表現をMathKernelに送って評価してもらう、ということになります。
![[Graphics:Images/jlink-tutorial_gr_19.gif]](Images/jlink-tutorial_gr_19.gif)
以下の例はWRIの古典的なサンプル、addTwoのJavaバージョンですが、
この中でMathematicaのMessage[]関数を呼び出しているところが今回のテーマです。
アプレットのソ-スです.
/*
CJLinkApplet.java
Title: JLinkApplet
Author: RoboDog
Description: Java Applet Which Uses JLink
*/
package JLinkApplet;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import javax.swing.*;
import com.wolfram.jlink.*;// JLink Staff
public class CJLinkApplet extends JApplet
{
public static String sContext = "JLinkApplet`CJLinkApplet`";
// IMPORTANT: Source code between BEGIN/END comment pair will be regenerated
// every time the form is saved. All manual changes will be overwritten.
// BEGIN GENERATED CODE
// member declarations
// END GENERATED CODE
boolean isStandalone = false;
public CJLinkApplet()
{
/* THIS PART CAN BE WRITTEN IN THE MATHEMATICA PACKAGE
// let MathKernel define symbols
KernelLink link = Install.getStdLink();
if ( link != null )
{
try {
link.evaluate( sContext + "addTwo::usage=\"addTwo[i,j] returns the sum of two integers: i + j\"" );
link.discardAnswer();
link.evaluate( sContext + "addTwo::ovflw=\"the resulting value overflow\"" );
link.discardAnswer();
link.evaluate( sContext + "addTwo::test=\"`1`\"" );
link.discardAnswer();
} catch( MathLinkException e ) {
}
}
*/
}
public static void evaluateMessage( KernelLink link, String messageName, String message )
{
try {
if ( message == null )
{
link.evaluate( "Message[" + messageName + "]" );
} else {
link.evaluate( "Message[" + messageName + ",\"" + message + "\"]" );
}
link.discardAnswer();
} catch ( MathLinkException e ) {}
}
public static void addTwo( int i, int j )
{
long sum = i + j;
KernelLink link = Install.getStdLink();
if ( link != null )
{
// this method is called via MathLink
//link.message("addTwo::test", "addTwo is called from Mathematica.");
evaluateMessage(link, sContext + "addTwo::test", "Hello from addTwo().");
// check the result.
if ( i>0 && j>0 && sum<0 || i<0 && j<0 && sum>0 )
{
// put ovflw message.
//link.message(sContext + "addTwo::ovflw", (String)null);
evaluateMessage(link, sContext + "addTwo::ovflw", (String)null);
// put $Failed
link.beginManual();
try {
link.putSymbol("$Failed");
} catch ( MathLinkException e ) {
System.out.println("An exception occurred in putSymbol(): " + e.getMessage());
return;
}
return;
} else {
// put result
link.beginManual();
try {
link.put( sum );
} catch ( MathLinkException e ) {
System.out.println("An exception occurred in put(): " + e.getMessage());
return;
}
return;
}
} else {
// this method is called within Java
return;
}
}
// Retrieve the value of an applet parameter
public String getParameter(String key, String def)
{
return isStandalone ? System.getProperty(key, def) :
(getParameter(key) != null ? getParameter(key) : def);
}
// Get info on the applet parameters
public String[][] getParameterInfo()
{
return null;
}
// Get applet information
public String getAppletInfo()
{
return "Applet Information: This applet is a J/Link savvy program.";
}
// Initialize the applet
public void init()
{
try {
initComponents();
}
catch (Exception e) {
e.printStackTrace();
}
}
public void initComponents() throws Exception
{
// IMPORTANT: Source code between BEGIN/END comment pair will be regenerated
// every time the form is saved. All manual changes will be overwritten.
// BEGIN GENERATED CODE
// the following code sets the frame's initial state
setLocation(new java.awt.Point(0, 0));
getContentPane().setLayout(null);
setSize(new java.awt.Dimension(400, 270));
// END GENERATED CODE
}
// Standard method to start the applet
public void start()
{
}
// Standard method to stop the applet
public void stop()
{
}
// Standard method to destroy the applet
public void destroy()
{
}
}
addTwo()という「外部関数」のusageなどは、Javaソースコードに記述してもよいし、Mathematica Packageに記述しても構いません。
通常はコンパイルの手間を省くため、以下の通りMathematica Packageに記述しておくと便利です。
(* :Title:JLinkApplet`CJLinkApplet`*)
(* :
Context:JLinkApplet`CJLinkApplet`*)
(* :Author:Junzo Sato*)
(* :
Summary:*)
(* :Package Version:1.0*)
(* :Release Note:*)
(* :
Mathematica Version:4.0*)
(* :Copyright:Copyright © 2000,
Junzo Sato.All rights reserved.*)
(* :
History:Ver 1.0 Dec 2000 by Junzo Sato.*)
(* :Keywords:*)
(* :
Reference:*)
(* :Limitation:*)
(* :Discussion:*)
BeginPackage["JLinkApplet`CJLinkApplet`"]
Unprotect[Evaluate[$Context<>"*"]]
addTwo::usage="addTwo[i,j] returns the sum of two integers: i+j";
addTwo::ovflw="the resulting value overflow";
addTwo::test="the message is ... `1`";
Begin["`Private`"]
End[]
(* don't protect symbols if you will add more definitions to them later.*)
\
(*Protect[Evaluate[$Context<>"*"]]*)
EndPackage[]
Needs["JLink`"]
始めはパッケージのロードからです。このパッケージの中でNeeds["JLink`"]が実行されていることに注意して下さい。
![[Graphics:Images/jlink-tutorial_gr_20.gif]](Images/jlink-tutorial_gr_20.gif)
続いてJ/Linkのリンクオブジェクトを生成します。
![[Graphics:Images/jlink-tutorial_gr_21.gif]](Images/jlink-tutorial_gr_21.gif)
アプレットの生成にはJavaNew[]を使います。
![[Graphics:Images/jlink-tutorial_gr_23.gif]](Images/jlink-tutorial_gr_23.gif)
返ってきたJavaObejctの正体はJLink`Objects`以下につくられた変数です。
![[Graphics:Images/jlink-tutorial_gr_25.gif]](Images/jlink-tutorial_gr_25.gif)
用意したアプレットのContextを調べてみると、addTwo以外にもアプレット関連の変数が見受けられるのがわかります。
![[Graphics:Images/jlink-tutorial_gr_27.gif]](Images/jlink-tutorial_gr_27.gif)
ABORT CENTERUALIGNMENT FRAMEBITS PROPERTIES SOMEBITS
addTwo ERROR HEIGHT RIGHTUALIGNMENT TOPUALIGNMENT
ALLBITS evaluateMessage LEFTUALIGNMENT sContext WIDTH
BOTTOMUALIGNMENT
addTwoのusageをみてみましょう。わざわざFull ContextでaddTwoを指定する必要はありませんが、以下ではアプレットのメソッドを呼び出していることをはっきりさせるために敢えてFull Contextを使用します。
![[Graphics:Images/jlink-tutorial_gr_28.gif]](Images/jlink-tutorial_gr_28.gif)
|
addTwoのusageをみてみましょう。
![[Graphics:Images/jlink-tutorial_gr_32.gif]](Images/jlink-tutorial_gr_32.gif)
addTwo::testのMessageは引数の文字列を以下のように表示させるものです。
![[Graphics:Images/jlink-tutorial_gr_35.gif]](Images/jlink-tutorial_gr_35.gif)
このMessageをJavaコードから呼び出させてみましょう。同時にaddTwoに大きな数を与えてオーバーフローも起こさせます。
addTwo::testとaddTwo::ovflwが以下のように実行されます。
![[Graphics:Images/jlink-tutorial_gr_37.gif]](Images/jlink-tutorial_gr_37.gif)
有効な数を与えてもaddTwo::testが返ってきます。
![[Graphics:Images/jlink-tutorial_gr_41.gif]](Images/jlink-tutorial_gr_41.gif)
引き数に無効な値を入れた時はJavaの例外が投げられて、J/Linkが自動的にJava::excptnとして表示してくれます。
![[Graphics:Images/jlink-tutorial_gr_44.gif]](Images/jlink-tutorial_gr_44.gif)
最後に余談ですが、ロードしたappletにフレームを付け足したい場合などは以下のようにして、
MathAppletFrameなどを利用することが可能です。
![[Graphics:Images/jlink-tutorial_gr_47.gif]](Images/jlink-tutorial_gr_47.gif)
![[Graphics:Images/jlink-tutorial_gr_49.gif]](Images/jlink-tutorial_gr_49.gif)
![[Graphics:Images/jlink-tutorial_gr_50.gif]](Images/jlink-tutorial_gr_50.gif)
J/Linkとカーネルを終了しましょう。
![[Graphics:Images/jlink-tutorial_gr_51.gif]](Images/jlink-tutorial_gr_51.gif)
![[Graphics:Images/jlink-tutorial_gr_53.gif]](Images/jlink-tutorial_gr_53.gif)
以上でJ/Linkの2つの特徴的な使い方、「MathematicaからJava」と「JavaからMathematica」の概要を掴んでいただけたかと思います。
MathLink Program作成の立場からいうと、C/C++で作っていた頃よりtemplateを書く手間がないだけ楽になったと言えるのではないでしょうか。演算速度追究型のアプリケーションを必要としているのでないかぎり、J/Linkを使って行く方が今後のMathLink Programmingの主流になっていくと思われます。現にWRIではJ/LinkがこれまでのMathLink Programmingにとって替わることを意図しているそうです。いまから「Mathematicaを拡張してみたい」、「自分のアプリからKernelを使いたい」という方はJ/Linkを念頭において開発されるとよいでしょう。では、この辺で。御購読ありがとうございました。