首页 > Sktech > 习作BM_整合Swing控件到Processing的一个尝试
2018
01-26

习作BM_整合Swing控件到Processing的一个尝试

习作BM_整合Swing控件到Processing的一个尝试 - 第1张  | Processing编程艺术


Swing是Java的原生标准GUI库.
因为Processing运行在Java运行时环境里, 所以Processing是不需要引入任何外部支持就可以使用Swing的.
虽然据说业界对Swing诟病较多(主要是丑?), 且认为其过时,
但我觉得在为输入手段贫乏的Processing程序快速搭建能获取用户输入的小工具方面,
Swing比起其他各种三方库还是十分优秀且易用的.
(官方有针对Swing的详细教程, 可以在这里找到.)


思路:
– 正常Swing程序一般先继承Swing框架(JFrame)类, 然后给框架添加各种容器(JPannel)实例, 再往容器里添加控件.
– Processing程序则强制继承Processing的自有框架(PApplet)类, 其实其内部已经有了Swing框架(或更原始的awt框架, 即frame变量), 所以直接给Processing程序添加容器(这里选用JWindow)即可. Processing的frame变量可以直接做所有Swing控件的宿主.
– Swing为常用弹出对话框做好了呼出即用的预制件, 虽然在弹出时会中断程序, 但Processing为我们准备好了傻瓜式创建线程的方法. 这里尝试调用四种已经预制好了的对话框: 警告信息, 文件选择, 文本输入, 选择列表.
– Processing的draw()方法实时处理数据, 而Swing控件跑在另一个线程, 所以不宜对draw()要处理的数据做直接操作, 要想办法规避. 这里用了较原始的用if语句测等待位的方法.


Processing动作
– [Q]键:退出程序
– [Quit]:推出程序
– [browse]:呼出文件选择对话框选择保存路径
– [save]:保存当前窗口为图片到被选择的路径 (如果没有选择路径则弹出警告信息)
– [operate]:显示或隐藏工具面板
– [FileName]:设定保存图片时的文件名前缀
– [Adding]:设置点击工具面板添加按钮时一次添加几个抖动方块.

Swing工具面板动作
– [add]: 往Processing画面添加抖动方块
– [remove]: 移除Processing画面的抖动方块 (每次只移除一个, 不受Adding设置的影响)
– [clear]: 删除Processing画面的所有抖动方块
– [stroke]: 设置抖动方块是否显示框线
– [fill]: 设置抖动方块是否显示填充
– [+]: 拖动工具面板


其他:
– 这个习作是在win7电脑上做的, 用的Window皮肤, 用其他操作系统的话可以尝试设置注释里给出的其他皮肤, 或者通过注释里的for语句查看有哪些可用皮肤.
– 这里只给各类按钮加入了动作事件监听器. 如果需要追加更高级事件, 比如鼠标事件和键盘事件, 则会在某些地方和Processing自己的事件机制重合, 要用的话继续在Processing的框架里做会十分不方便.
– 线程不安全是一个问题. 官方建议所有对Swing控件的操作都要包裹在一个Runnable的实现里传递给SwingUtilities类的invokeLater()方法, 包括界面的创建及实时的刷新, 这里偷懒没有遵守, 感觉只要不用到JTree和JList这两个控件就很少会遇到问题, 但如果长时间运行需要稳定性的话还是应该严格遵守.
– 在我的电脑上呼出文件选择对话框后会同时报出log4cplus的异常, 但不影响运行. 据说安装了autocad而没有正确配置这个工具的电脑都会这样, 具体原因不详.
– 3.x的窗口类型会随渲染方式改变, 新出的surface变量只是一个接口, 用来转接其他类型(“For Java2D, this will be an AWT Frame object. For OpenGL, the window.”). 原来的frame变量是一个空变量, 不包含任何窗口信息, 但可以使用(官方原话是”A dummy frame to keep compatibility with 2.x code and encourage users to update”…), 所以用3.0以上运行的这个例子话工具面板不能自动调整位置, 会直接被定位到屏幕宽度. 3.0的core源码我还没下到, 但应该是有从surface的getNative()方法获取原始窗口的办法的.


在2.0内制作, 用3.0以上运行可能会报错:


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.File;

//- --- --- ---
//- public
//- --- --- ---
//- ** for swing
static JWindow psThatSettingWindow;
boolean psFrameVisibility=false;
boolean psThisAddButtonWaiting=false;
boolean psThisRemoveButtonWaiting=false;
boolean psThisClearButtonWaiting=false;
boolean psThisStrokeChecker=true;
boolean psThisFillChecker=true;
String psMessageDialogCaster="nc";
Object[] pbAddingComboModel={"Single","Double","Tripple"};
//-
//- ** local
ArrayList<EcElement> pbTheElementList;
ArrayList<PVector> pbTheCubeList;
String pbImagePath="nc";
String pbImageFileName="nc";
int pbAmountPerAdding=1;
int pbAddingAmount=1;
//-
//-

void setup() {size(320, 240);noStroke();frameRate(16);textAlign(LEFT, TOP);ellipseMode(CENTER);
  frame.setTitle("Test Swing with Processing!!");
  //--
  //-- ** Swing Theme
  try{
    /* alternate available:
     *   "javax.swing.plaf.metal.MetalLookAndFeel"
     *   "javax.swing.plaf.nimbus.NimbusLookAndFeel"
     *   "com.sun.java.swing.plaf.motif.MotifLookAndFeel"
     *   "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"
     *   "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"
     * or use the next line to check what is available on your system:
     *   for(UIManager.LookAndFeelInfo it:UIManager.getInstalledLookAndFeels()){println(it.getClassName());}
     */
    UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
  }catch(Exception e){println("--well yeah something went wrong but i dont think we needa know that");}
  //--
  //-- ** Local UI setting
  pbTheCubeList=new ArrayList<PVector>();
  pbTheElementList=new ArrayList<EcElement>();
  //--
  EcButton lpBrowseButton=new EcButton(50,20,"browse",0);pbTheElementList.add(lpBrowseButton);
  EcButton lpSaveButton=new EcButton(40,20,"save",1);pbTheElementList.add(lpSaveButton);
  EcButton lpOperateButton=new EcButton(50,20,"operate",2);pbTheElementList.add(lpOperateButton);
  EcButton lpQuitButton=new EcButton(50,20,"quit",3);pbTheElementList.add(lpQuitButton);
  EcTextBox lpPathBox=new EcTextBox(200,20,"path:",4);pbTheElementList.add(lpPathBox);
  EcButton lpFileNameButton=new EcButton(60,20,"FileName",5);pbTheElementList.add(lpFileNameButton);
  EcButton lpAddingButton=new EcButton(60,20,"Adding",6);pbTheElementList.add(lpAddingButton);
  //--
  lpPathBox.ccTargetLayout(null, 10,10);
  lpBrowseButton.ccTargetLayout(lpPathBox, 3, 0);
  lpSaveButton.ccTargetLayout(lpBrowseButton, 3, 0);
  lpQuitButton.ccTargetLayout(null, 5, 200);
  lpOperateButton.ccTargetLayout(lpQuitButton, 5, 0);
  lpFileNameButton.ccTargetLayout(lpOperateButton, 5, 0);
  lpAddingButton.ccTargetLayout(lpFileNameButton, 55, 0);
  //--
  //-- ** Swing UI setting
  fsCreateSwingWindow();
  //--
}//+++


void draw(){background(0);
  //--
  //-- ** receving request from swing window
  if(psThisAddButtonWaiting){
    for(int i=0;i<pbAddingAmount;i++){
      pbTheCubeList.add(new PVector(random(width),random(height)));
    }
    psThisAddButtonWaiting=false;
  }
  if(psThisRemoveButtonWaiting){
    if(!pbTheCubeList.isEmpty()){pbTheCubeList.remove(0);}
    psThisRemoveButtonWaiting=false;
  }
  if(psThisClearButtonWaiting){
    pbTheCubeList.clear();
    psThisClearButtonWaiting=false;
  }
  //--
  //-- ** receving setting from swing window
  if(psThisStrokeChecker){stroke(0xCC);}else{noStroke();}
  if(psThisFillChecker){fill(0x99);}else{noFill();}
  //--
  //-- ** draw local elements
  for(PVector it:pbTheCubeList){
    rect(it.x,it.y,ceil(random(4,8)),ceil(random(4,8)));
  }noStroke();
  for(EcElement it:pbTheElementList){it.ccUpdate();}
  //--
}//+++


void keyPressed(){switch(key){
  //--
  case 'q':fsPover();
  default:break;
  //--
}}//+++


void mousePressed(){
  int lpID=3001;
  for(EcElement it:pbTheElementList){
    if(it instanceof EcButton){
      lpID=((EcButton) it).ccTellClickedID();
      if(lpID<3000){break;}
    }
  }
  //--
  switch(lpID){
    //--
    case 0:
      thread("fsGetPathByFileChooser");
      break;
    //--
    case 1:{
        File lpFile=new File(pbImagePath);
        if(lpFile.isDirectory()){
          saveFrame(pbImagePath+"\\"+pbImageFileName+"-######.png");
        }else{
          psMessageDialogCaster="File path illegal.";
          thread("fsMessageDialog");
        }
    } break;
    case 2:
      psFrameVisibility=!psFrameVisibility;
      psThatSettingWindow.setVisible(psFrameVisibility);
      psThatSettingWindow.setEnabled(psFrameVisibility);
      psThatSettingWindow.setLocation(frame.getLocation().x+width+10, frame.getLocation().y);
      break;
    case 5:
      thread("fsSetFileNameByInputDialog");
      break;
    case 6:
      thread("fsComboDialog");
      break;
    case 3:
      fsPover();
      break;
    default:break;
  }
  //--
}//+++

//< <<< <<< <<< <<< <<< Overrided

//* *** *** *** *** ***
//*
//* Operate
//*
//* *** *** *** *** ***
//- --- --- ---

public void fsComboDialog(){
  String lpSelected = (String)JOptionPane.showInputDialog(frame,"how much cube youd like to add per click","Adding mode setting Dialog",
    JOptionPane.PLAIN_MESSAGE,null,pbAddingComboModel,pbAddingComboModel[pbAddingAmount-1]);
  if(lpSelected==null){return;}
  if(lpSelected.equals(pbAddingComboModel[0])){pbAddingAmount=1;}//...ALL IS NO SAFE AND CLEAN BUT ANYWAY!!
  if(lpSelected.equals(pbAddingComboModel[1])){pbAddingAmount=2;}
  if(lpSelected.equals(pbAddingComboModel[2])){pbAddingAmount=3;}
}//+++

public void fsSetFileNameByInputDialog(){
  String lpInputed=(String)JOptionPane.showInputDialog(frame,  "Replace current file name:","File Name Input Dialog",
    1,null,null,pbImageFileName);
  if(lpInputed!=null){if(!lpInputed.isEmpty()){
    pbImageFileName=lpInputed;
  }}
}//+++

public void fsMessageDialog(){JOptionPane.showMessageDialog(frame, psMessageDialogCaster);}

public void fsGetPathByFileChooser(){
  final JFileChooser lpChooser=new JFileChooser();
    lpChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
  int lpChooserFlag=lpChooser.showOpenDialog(frame);
  if(lpChooserFlag==JFileChooser.APPROVE_OPTION){
    File lpFile=lpChooser.getSelectedFile();
    EcElement lpElement=pbTheElementList.get(4);//...THIS IS NO SAFE AND CLEAN BUT ANYWAY!!
    pbImagePath=lpFile.toString();
    ((EcTextBox)lpElement).ccSetText(pbImagePath);
  }else{println("--again, this should be shown nowhere cuz we dont need a cancel noice");}
  //--
}//+++

void fsCreateSwingWindow(){
  //-- ** presetting
  psThatSettingWindow=new JWindow(frame);
  JPanel lpPanel=new JPanel();
    lpPanel.setLayout(new GridLayout(0, 1));
  //-- **AddButonn
  JButton lpThisAddButton= new JButton("add");
  ActionListener lpThisAddButtonAction=new ActionListener(){@Override public void actionPerformed(ActionEvent ae) {
    //-- ** ** action start from here::
    psThisAddButtonWaiting=true;
  }};lpThisAddButton.addActionListener(lpThisAddButtonAction);
  //--  
  //-- **RemoveButonn
  JButton lpThisRemoveButton= new JButton("remove");
  ActionListener lpThisRemoveButtonAction=new ActionListener(){@Override public void actionPerformed(ActionEvent ae) {
    //-- ** ** action start from here::
    psThisRemoveButtonWaiting=true;
  }};lpThisRemoveButton.addActionListener(lpThisRemoveButtonAction);
  //--  
  //-- **ClearButonn
  JButton lpThisClearButton= new JButton("clear");
  ActionListener lpThisClearButtonAction=new ActionListener(){@Override public void actionPerformed(ActionEvent ae) {
    //-- ** ** action start from here::
    psThisClearButtonWaiting=true;
  }};lpThisClearButton.addActionListener(lpThisClearButtonAction);
  //--  
  //-- **StrokeChecker
  JCheckBox lpThisStrokeChecker=new JCheckBox("stroke");
  ActionListener lpThisStrokeCheckerActionListener=new ActionListener() {@Override public void actionPerformed(ActionEvent ae) {
    //-- ** ** action start from here::
    Object lpSouce=ae.getSource();
    if(lpSouce instanceof JCheckBox){
      psThisStrokeChecker=((JCheckBox)lpSouce).isSelected();
    }
  }};
  lpThisStrokeChecker.addActionListener(lpThisStrokeCheckerActionListener);
  lpThisStrokeChecker.setSelected(true);
  //--
  //-- **FillChecker
  JCheckBox lpThisFillChecker=new JCheckBox("fill");
  ActionListener lpThisFillCheckerActionListener=new ActionListener() {@Override public void actionPerformed(ActionEvent ae) {
    //-- ** ** action start from here::
    Object lpSouce=ae.getSource();
    if(lpSouce instanceof JCheckBox){
      psThisFillChecker=((JCheckBox)lpSouce).isSelected();
    }
  }};
  lpThisFillChecker.addActionListener(lpThisFillCheckerActionListener);
  lpThisFillChecker.setSelected(true);
  //--
  //-- ** layuout setting
  Container lpPane=psThatSettingWindow.getContentPane();
    lpPane.add(new JLabel("[+] operate"),BorderLayout.PAGE_START);
    lpPane.add(lpPanel,BorderLayout.CENTER);
    lpPanel.add(lpThisAddButton);  
    lpPanel.add(lpThisRemoveButton);  
    lpPanel.add(lpThisClearButton);
    lpPanel.add(lpThisStrokeChecker);  
    lpPanel.add(lpThisFillChecker);  
  //--
  //-- ** window setting
  psThatSettingWindow.addMouseMotionListener(new MouseMotionListener() {
    @Override public void mouseDragged(java.awt.event.MouseEvent me) {
      psThatSettingWindow.setLocation(me.getXOnScreen()-5, me.getYOnScreen()-5);
    }
    //--
    @Override public void mouseMoved(java.awt.event.MouseEvent me) {;}
  });
  psThatSettingWindow.pack();
  psThatSettingWindow.setVisible(false);
  //--
}//+++

void fsPover(){
  exit();
}//+++
//< <<< <<< <<< <<< <<< operate

//* *** *** *** *** ***
//*
//* Class
//*
//* *** *** *** *** ***

class EcElement{
  int cmX, cmY, cmW, cmH;
  int cmID;
  String cmName;
  EcElement(){
    cmX=cmY=cmW=cmH=9;
    cmID=3001;
    cmName="n/c";
  }
  //--
  void ccUpdate(){
    fill(0x99);rect(cmX,cmY,cmW,cmH);
  }
  //--
  boolean ccIsMouseOver(){
    return (mouseX>cmX)&&(mouseX<(cmX+cmW))&&
           (mouseY>cmY)&&(mouseY<(cmY+cmH));
  }
  //--
  void ccTargetLayout(EcElement pxFollow, int pxOffsetX, int pxOffsetY){
    if(pxFollow==null){cmX=pxOffsetX;cmY=pxOffsetY;return;}
    cmX=pxFollow.cmX+pxOffsetX+(pxOffsetY==0?pxFollow.cmW:0);
    cmY=pxFollow.cmY+pxOffsetY+(pxOffsetX==0?pxFollow.cmH:0);
  }
  //--
}//+++

class EcButton extends EcElement{
  EcButton(int pxW, int pxH, String pxName, int pxID){
    super();
    cmW=pxW;cmH=pxH;
    cmName=pxName;cmID=pxID;
  }
  //--
  @Override void ccUpdate(){
    fill(ccIsMouseOver()?color(0xEE,0xEE,0x11,0xCC):color(0xEE,0xCC));
      rect(cmX,cmY,cmW,cmH);
    fill(0x11);textAlign(CENTER,CENTER);
      text(cmName,cmX+cmW/2,cmY+cmH/2);
    textAlign(LEFT,TOP);
  }
  //--
  int ccTellClickedID(){return ccIsMouseOver()?cmID:9999;}
}//+++


class EcTextBox extends EcElement{
  String cmText;
  String cmBoxText;
  int cmMax;
  boolean cmIsCutted;
  EcTextBox(int pxW, int pxH, String pxName, int pxID){
    super();
    cmW=pxW;cmH=pxH;
    cmName=pxName;cmID=pxID;
    cmText="n/c";
    cmBoxText=cmText;
    cmMax=cmW/8;
    cmIsCutted=false;
  }
  //--
  @Override void ccUpdate(){
    //--
    stroke(0x11,0xEE,0x11);fill(0x33,0xCC);
      rect(cmX, cmY, cmW, cmH);
    noStroke();fill(0x11,0xEE,0x11);
      text(cmBoxText,cmX+2,cmY+2);
    //--
    if(ccIsMouseOver()&&cmIsCutted){
      fill(0xEE);
      text(cmText,mouseX-64,mouseY+16);
    }
  }
  //--
  void ccSetText(String pxText){
    cmText=pxText;
    int lpLength=cmText.length();
    if(lpLength>cmMax){
      cmBoxText="~"+cmText.substring(lpLength-cmMax, lpLength);
      cmIsCutted=true;
    }else{
      cmBoxText=cmText;
      cmIsCutted=false;
    }
  }
  //--
}//+++

 



最后编辑:
作者:constrain
nullpointerexception

留下一个回复

你的email不会被公开。