2021년 2월 25일 목요일

[Jython] JavaFx Icon Library

 JavaFx Icon Library

import base64
from javafx.embed.swing import SwingFXUtils
from java.io import *
from java.lang import *
from java.awt.image import *
from javax.imageio import *

exit_png  = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGT0lEQVR42qWXe0xTVxzHv7dAsVRBQZ5T8IHCggZ06NSF6DKNOqPLotuMMdmWGcXNqSM6jYuZcTPO7Z9lS/aIzPkYOECdwSHNVJI5iA9QDA8ZAwQs0EKBQhFKub337Jx7b28LLObWHSjn9qT8vp/f9/zO795y0DA6sl8iWj7nz4jbVsaxmdPyYfOPy0jx3XafFaLtXznlo8oQgydD5+qT1rdnt2oHaP5+Kfmjoh07fnrsV5YR8UmYm7ERyRmbYIybj4d1Lnxj3ICy+03YccoPgMZvXyTXKy3IPPV0gNDoeMxatAapq99FZOIi6AURIYIAMiyixeLG1QoXTOmbcauqkcXSDlD39WLyZ5V1HIAxPAbxacuRtnY7ImJnIjR8GqZARAAvQnCLcAtQXgQ9AwQXy4ZxJe1NVNQ2I/NnPxyo/uoFUlprw87TMsDLmV8iacl6TI1JRDgnwiCKEAWvoCAS8Gxm7+m12w0MjRDk3RzGpXmbUPl3C94/7YcD948vJLfqu/GBArDhAo9XJ/IIABOgLzehorI4TwUFmrG8DmVdBmIAeckbUd3YymJpB7hzNJWUN9qx66wMsL6Ax8rAAQQGBctCAlGylUXdovLe4wCdh3mCwtsu5Ca+jtpmM3ad8QPgr8PzSWVLH3afM6sAyzkKEBisikmWi7Ig75a3wbtO0PeEoKRqBGcSNqDe3I7dZ83aAW4cTCE1bQ7s+cULsFRwyA4oe80L3m1gjkiuiJxaD9ZeERUNPE7GrcMjqxV7zvkBUJyVTOo7n2BvTpsKkD7STx2YoGYoW++7HUSFY2BN9Bg2WAR8F7kWrV2d+CinTTvA5V1zyKMeJ7LOewFSB/sRQB3gVZHRla/WhCCD1LTysPUTfD15DTp6u5CVqwHgdlSUYUlXl7Ngx2zS6nBhnw9Acp8dgXrDOPsFJXPPVrD1wWGCB81u8LQQTxhXwebowb5ftQGwTh7buC7EYhlyY3+eFyCxpxe6oBDpyPG+wqLHEdYXOAnA3C2gpVOQxI7pX4F9yM5iaQOIWbUKN0duwkrvKh8XeAESbL3gdAa18tnMq0dQfs9AXLRH/NMuoH+IgKPpHOFWYIB34EC+D4CS6X+Oxbm5aDp9ElccpcgqbFcBYtt7oNOHSE1ILjhOFibE24DourWXoNVGs2fqhMMhdwZc4iAOXhgDsLikhKbAy53EbqcPAR0g3d1Afz/I4CDqysuwoqwcPdRaBhDxmAIEGRQxohahSI+e5yQM0L1v7XJjyMWE5Fv4fucyuHVOHLrYPgbAZAKcTqChAaS5GXA4Rjkh0ojZBQXYabFIAJOaurFyRhFt8m0o7trrLUDJerkezDRzdhNiDwWc8mywx7EUumAXPrk0FiA/H6Smhh7YpnHbQKitdXepA7fvqQ4E19uQEZoNe90d/OA4iPSkeWrfZ9m320XY+gQ5AMd+6Q+Ns7NnCfRGFw7/1qGtBtK3bMHDO6UoDu/AgaIOtQZ0dTboAg24VVuDBXNTqEOcehIsTNwhShnLxhMZgk7brIsQEubGp5c7tJ2CKfHxOB/QCESH4EihF0CstgGBBrUJMeuH6V4z4W6HJ3NOEpUdkMc75oX0uUFksbT3gfyEIcvEmEk4esWiArgqu8CxIlSajp3ebLoHBNp0AM+DoGS7dyMloK2P0jAlmuBooUV7J/xwgZFMfi4Un/3uBRgotwJBRiosSuKOQfpQ4rORkjjHZIkk4kF5q2E+ImN1LJb2e0HmPAOZmhCGz4usKkD1VTOGxUl44hSpkCcM8clZFvb5I/3dWJeCmGmBOFbkB8B7yXoSMyscx656AW7ktoDTh0mVPVqH84p77VBr4bWqFEyfEURj+QHwdmIQiZsTgePFXoDrOS20EYVCTd5z0IFxhecLte7B80iYpWextANsnRlApidF4rhpNADHAMZE4RQYDqNPNqe4sPpeMmYnTqCx/HBg83QdSUiJwgkfgGvMAX2o1N9ZZE79GsSp597zBYrzsAiDWHM/FbOTjPjC5IcDYwcFIDfye+njBRfmyVSqeKJUO0dU0+lNqJ9eltAr2uthGsiLGvXl4pkBrlMAehmmJuq7DQSlNDK9u+HaYH506dNi/W8AJchjCmGis4nuSIkzP7pfa6xnAmDD8EZnDp3u0leRsyC68Vnj/As+l/pOB/618gAAAABJRU5ErkJggg=="
clock_png = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAJVUlEQVR42pWXeXCNaRbG6TLV0z0zqobhj6lS1dX80TVLL6Zr0BhjkEQsLWIXTaxNBkE0ErFkT2wdume0Rlta2yNB9g2xZZNVkLghEXGz5+YuuVvuM+f5fDeu8M/cqqfe+573nN9zvu2t7+vV69Xvnfr4Kb7GdC+zCJQhfZq27MSED2Wtd6///9e7/OT4D8lw8simB716Jvdpvjw53vJwFRzGEMAcrsjREgjb7ZnQp48JZ46XV5znjBmXL0z3TqwVwcv7KrymX1VGzhn39r5yfsLEODdd+qidjlxvoG0LYAl7yRM2PehFXveRV55wX96ZuxBdTcGwawPRpd0CR8NmoGUTYNqAjMNBNs9JqS1r/30Tl8/UQlPUCUcz4GiC1LwcOWc84fQz+K26A59ZF3E9aR+gXw80f6PwusgWD3rR03km3tPFT7ZaK/1h1ayHtcofds1aoMYP7eXrscz3Etb4FSIvqwMNj4C0y504+mMHone1viHGU2VdK3l3M/RYvbJQ6uPQVrZe4ZFr06wDvehJbzbQTxfnCUu5H8wlX8NauhyoWIyKpI2YMikRJ36oRU0ZEHfWiKiYVlEbIqN1Ir0qg8t/nbLOPOaz7vgPNZjimYRn19YqXFvpCsWLnvRmAwN15zxhzF2CzruL0JXvg5qklfD0vIE76XrkpJqwd68YR7Vh9+4WxJ8uQGFWHJ7f+y/qiw7iRfEhZeScca4zL1LyWXc324w7aXqFd//iWnQV+KAzbzF055UGBioNtP3iYTOmz4EleyYars7HlImJuJ7YjqxEHl0zwiPb5dTnob7sDBoenELT4/Noe5YAQ1MiwkNWKyPnjGtl/XnpaaTG5yEsol2pJ4e8qcJtTJwPS9YMtJ/2sDkb+H3wzCELDefc4Ehxx4q5Z3FwnwZpCR2IiGpBzK4mubmSYai7jKjw1TC3paGjIRk6bTJMLemICPVXRs4pY3MKQratRGt1HKoKkxAT06RwspM6FO6KeWfRFTcaW2cO/orebODXosH39/z1UnKIn33l0lzkZhiwS4xjohvxpDQLpsZUxMaG4LvvIuAw3YCxJQMdjWlinClnYJ2MGdA1pKG9IVXi6TBIQ8YWmdclorokE7tiGhUeuauW3UXwVwFJ9FS9lU2mr+jjyW5J7eePPcOxI80IDW9CeX4+OpszYTdcx749Ifhyug/2fxsqj+YtmFqzYWjOEqNs6JuypIGMbnU0vhQbNTVloODmPYVH7oXjtZjqnmwQvz+4bnC/GjcyymflwkxkXpFTFtGAn47WwK7LQVREACztOWJ6R5rYAc/JM7FXRrlj0dl2Q67/NTHLfkN6kanlOiIjNigc8sglf5X4ePzzwBzXBt719jh9JTasGMcOaREaqkVR3kNYOq7BLOamthyYdTdlR8vD7phgjHObil3RwYA1X4kbxEjf/KaMrTfQKfXkkEcu+fShH32dDfSdNfFC/alDVYjd9wI7QhpgMRTD1C5H2HoT+hYxab0lZncAWyGiIoMweoyHnB3ZZu1FMHfcVdaZ11OsJ4c8csmnD/3US6/8+s+emICEUzXyvEsD27WyxxagU3cLxrY7igytt2W8DYs+V0yLER62GcNHjEV4qGzXXSXSRK6S4ypnLTnkkUs+fehHX2cDA2d7JODcTxpERtZj+7YXAIphNxXAZroHq7EQnR35MLbnwqTLk1ihrD9AdOQWfP75KAQH+cNhKVLWmEMxn3WsJ4c8csmnD/2c+4DSwBz3eJw8WCUJzyWxXgoKcGC/PHoHwhErd77VUCRQOSsdhQIslvVK7Nm9E599NgyBm9fI/VCmrJl0BYqYzzrWk0MeueTTh349GkjA4f0PJKEO24LrpKBIjqoUXeZy2DvL5BrKadYXy1GVytoj2WbDMHL0OOzYESDzKjnSUmmgqFvMZx3rySGPXPLpQ7/XGpjrFo/vd5XLDfa8uwG7uQRWAVvE1CxAjrBV4NvYaExwnyrXU54EaNBlud+d4yrGWE+OswHy6UO/1xqYPe6cNmxLrtzZT+RmeYbKBw/FrFyOrFxAL2U13ZeYHP2+KNndeFqr4bA+VOLOnJ5iPTnkkRsVUQ360M+1gf5e/ziSsWHZNUSGPUJISC3i4zRiUAGH7SFs5gewmCoU2S3SmL1K7uoq+f8I1s6K7rWeYh3rySGPXPI3LLsO+rk+BX1Hf7zRz3fKFewMzEdEeC22BtYI/DF+PLwHXWJopZn5pWyWSlWPFDnjr0nirGM9OeSRSz596Oe6D3BHGjLvXxct61dcQ8j2B9i58ynO/PJEOc1wVMNufazIZnmlLptcAvsTZXSNO3NZx3pyyCOXfPrQz3Un5J48YMwnQTsWyVtQwJrbCAt7gm1bnyI/l02o6nqqGNqt1YopUIujR79XRs4Z5zrznDWsJ4c8csmnD/16vm3z/eyjaSMuaBdPT0OAf4Hsck8RFFiN/HwC63DixH+U0eGoEdXK/3qcOHlIGTln3DWPdawPD3uK9Wvy4CvcaSMu8ub7yPk+6PrjG+qA6K/dN80dfQkLZmRird9dpfOtQRqcPiVHb6tXzIDnqrT4+dQhZXwVq1fymM861pND3hzhkq8e/Ttv+5j4nT5lQkV1wip4fpqMuV7pWLX8FkJDHssjpMGWTVWIv/QE1Zo61bRB1KiOWiXOdeYxn3WsJ8fz0xQ0ZSyGPsWtgj5v/ZRZMH7QH00p04AXi9GaswKzR8RjjkcqlizMxqaAEoSFamQ7fYygLZXY/E0lNm18Jc4Z5zrzmM861s8ekYDW6/K2XecLU8qXoM/b/PtUHxl/zFqyVA7KX5pYAuPNBVgz8Thmj7mChd7JWOqbiY0b7sl1LZfrKtdWjLolc8a5zjzms471xhwfuTqL5SStgbV4Cejj+mXk/P229aQbYNgBSyFfmyfh+dExmiPrhkQO/WBtxLwvLpgXjI3DQnmGF81KxiKfVCya7yLOJc515jGfdawnh6/hlgJfoCMY9KFfzwb6Pdo/5lbLkfG4ETI88YOB702S2DDRn0R/EY36++AlW6f97UDO3OE/Ny4YeR4LvnCRzBnnOvOYr9axfhh55JJPH+dHyWs3oOjPopGiT0SD1Nfm90W/Ue9cbh5DVfhY0TgXjVXjQ9W8AWrd+ypnkModqfq8cSP2UYP91NPT8xr1VneuvuoePvAt6q+uv/uWT/o+Kref6qPw/wfHogZ+jQNQQQAAAABJRU5ErkJggg=="

def FileToBase64(filename):
    bi      = ImageIO.read(File(filename)) # -> BufferedImage
    baos    = ByteArrayOutputStream()
    ImageIO.write(bi, "png", baos)
    image   = baos.toByteArray()
    image64 = base64.b64encode(image)
    print(image64)
       
def Base64ToFile(image64): 
    image_b = base64.b64decode(image64)
    image_o = ImageIO.read(ByteArrayInputStream(image_b))
    ImageIO.write(image_o, "png", File(filename+".png"))

def Base64ToImage(image64):
    image_b = base64.b64decode(image64)
    return ImageIO.read(ByteArrayInputStream(image_b))

def GetExitImage(): 
    image = Base64ToImage(clock_png)
    return SwingFXUtils.toFXImage(image,None)

2021년 2월 15일 월요일

[Jython] JavaFx Wrapper Library

 JavaFx Wrapper Library

from javafx.application import Application
from javafx.scene import (Scene, Node)
from javafx.scene.layout import (VBox, HBox, Priority )
from javafx.scene.input import ( Clipboard, ClipboardContent )
from javafx.scene.image import ( Image, ImageView )
from javafx.scene.control import ( Menu, MenuItem, SeparatorMenuItem  )
from javafx.geometry import ( Insets, Orientation, Pos  )
from javafx.embed.swing import SwingFXUtils
from java.io import *
from java.lang import *
from java.awt.image import *

#
# Platform
#

class Class:
    def __init__(self,**kwargs): self.__dict__.update(kwargs)
    
def GetHandler(ctrl,handler):
    return lambda e: handler(Class(sender=ctrl, event=e))

def RunLater(handler,ctrl=None):
    from javafx.application import Platform
    Platform.runLater(handler)
    
def StartThread(handler,args):
    import threading
    thread = threading.Thread(target=handler,args=args)
    thread.daemon = True
    thread.start()

def Exec():
    from java.lang import Runtime
    from java.io import BufferedReader
    from java.io import InputStreamReader

    process = Runtime.getRuntime().exec(cmd);
    inp = BufferedReader(InputStreamReader(process.getInputStream(),"euc-kr"))
    out = ""
    line = inp.readLine()
    while line:
        out = out + line
        line = inp.readLine()

def Execute(cmd):
    '''
    import os
    os.system(cmd)
    '''
    import subprocess
    try:
        out = subprocess.check_output(cmd)
        return 0,out
    except:
        return -1,""

def DragOver(event):
    from javafx.scene.input import TransferMode
    if event.getDragboard().hasFiles():
        event.acceptTransferModes(TransferMode.COPY_OR_MOVE)
    event.consume()

def DragDropped(handler):
    from javafx.scene.input import DragEvent
    from javafx.scene.input import Dragboard
    def _DragDropped(event): #closure
        db = event.getDragboard()
        if db.hasFiles():
            handler( db.getFiles() ) # List<File>
            event.setDropCompleted(True)
        else:
            event.setDropCompleted(False)
        event.consume()
    return _DragDropped

def SetFileDropHandler(ctrl,handler):  
    ctrl.setOnDragOver( DragOver )
    ctrl.setOnDragDropped( DragDropped(handler) )

def ClipboardClear(): Clipboard.getSystemClipboard().clear();
def GetClipboardText():
    if Clipboard.getSystemClipboard().hasString():
        return Clipboard.getSystemClipboard().getString()
def GetClipboardHtmlText():
    if Clipboard.getSystemClipboard().hasHtml():
        return Clipboard.getSystemClipboard().getHtml()
def GetClipboardFiles():
    if Clipboard.getSystemClipboard().hasFiles():
        return Clipboard.getSystemClipboard().getFiles()
def GetClipboardImage():
    return SwingFXUtils.fromFXImage( Clipboard.getSystemClipboard().getImage()  )

def putClipboardText(text):
    content = ClipboardContent()
    content.putString(text);
    Clipboard.getSystemClipboard().setContent(content)
def putClipboardHtmlText(text):
    content = ClipboardContent()
    content.putString(text)
    content.putHtml(text)
    Clipboard.getSystemClipboard().setContent(content)
def putClipboardFiles(files):
    content = ClipboardContent()
    content.putFiles(files)
    Clipboard.getSystemClipboard().setContent(content)
def putClipboardImage(image):
    content = ClipboardContent()
    content.putImage(SwingFXUtils.toFXImage(image, null))
    Clipboard.getSystemClipboard().setContent(content)
 
#
# Dialog
#

def AlertDialog(message, title=None, dlg_type='alert', stage=None):
    from javafx.scene.control import Alert
    from javafx.scene.control.Alert import AlertType
    from javafx.stage import Modality
    alert = Alert( AlertType.CONFIRMATION if dlg_type == 'yesno' else AlertType.INFORMATION)
    if stage: alert.initOwner(stage)
    if title: alert.setTitle(title)
    alert.setHeaderText("")
    alert.setContentText(message)
    alert.initModality(Modality.APPLICATION_MODAL)
    result = alert.showAndWait()
    if result.get() == ButtonType.OK: return True
    else: return False

def YesNoDialog(message, title=None, dlg_type='alert', stage=None):
    return AlertDialog(message, title, 'yesno', stage )
    
def FileDialog(initialFile, save, stage=None):
    from javafx.stage import FileChooser
    from java.io import File
    dlg = FileChooser()
    if initialFile:
        f = File(initialFile)
        if f.exists():
            if f.isDirectory(): dlg.setInitialDirectory(f)
            if f.isFile():      dlg.setInitialFileName(f.getAbsolutePath());
    dlg.setTitle("Select File");
    if save: return dlg.showSaveDialog(stage);
    else:    return dlg.showOpenDialog(stage);

def FileOpenDialog(initialFile, stage=None):
    return EzFileDialog(initialFile, False, stage)

def FileSaveDialog(initialFile, stage=None):
    return EzFileDialog(initialFile, True, stage)

def DirectoryOpenDialog(initialDirectory, stage=None):
    from javafx.stage import DirectoryChooser
    from java.io import File
    dlg = DirectoryChooser()
    if initialDirectory:
        f = File(initialDirectory)
        if f.exists() and f.isDirectory():
            dlg.setInitialDirectory(f);
    dlg.setTitle("Select Folder");
    return dlg.showDialog(stage)
       
#
# Control
#

class wControl():
    def Initialize(self,fontsize=None,width=None,height=None,tooltip=None,menu=None):
        if width: self.set_width(width)
        if height: self.set_width(height)
        if fontsize: self.set_width(fontsize)
        if tooltip: self.set_width(tooltip)
        if menu: self.set_width(menu)
    def set_width(self,width): self.ctrl.setPrefSize(width, -1); 
    def set_height(self,height): self.ctrl.setPrefSize(-1, height); 
    def set_fontsize(self,size): self.ctrl.setStyle("-fx-font-size: " + str(size) + ";")
    def set_bgcolor(self,color): self.ctrl.setStyle("-fx-background-color: #" + color + ";") # {D8BFD8}
    def set_menu(self,menu): self.ctrl.setContextMenu(menu) #ezmenu
    def set_tooltip(self,text): 
        from javafx.scene.control import Tooltip
        self.ctrl.setTooltip(Tooltip(text))           
    def set_icon(self,filename,size,top=False): 
        from javafx.scene.control import ContentDisplay;
        iv = ImageView(Image(FileInputStream(filename)))
        if size: iv.setFitHeight(size); iv.setFitWidth(size)
        self.ctrl.setGraphic(iv)
        if top: self.ctrl.setContentDisplay(ContentDisplay.TOP)
    def set_icon_image(self,image,size,top=False): 
        from javafx.scene.control import ContentDisplay;
        iv = ImageView(image)
        if size: iv.setFitHeight(size); iv.setFitWidth(size)
        self.ctrl.setGraphic(iv)
        if top: self.ctrl.setContentDisplay(ContentDisplay.TOP)
    def set_filedrop(self,handler): SetFileDropHandler(self.ctrl,handler)
    def show(self): pass #TODO
    def hide(self): pass #TODO
    def set_margin(self): pass #TODO

class wLabel(wControl):
    def __init__(self,text,handler=None,**kwargs):
        from javafx.scene.control import Label
        self.ctrl = Label()
        self.Initialize(**kwargs)
        self.ctrl.setText(text)
        if handler: self.ctrl.setOnAction(GetHandler(self,handler))
        #ctrl.setAlignment(Pos.CENTER);

class wImageView(wControl):
    def __init__(self,parent=None,imagefile=None,handler=None,scroll=True,width=None,height=None,stretch=None,**kwargs):
        from javafx.scene.control import ScrollPane
        from javafx.scene.control.ScrollPane import ScrollBarPolicy        
        if scroll: parent = self.scroll = ScrollPane()
        else: parent = self.scroll = None
        if imagefile: self.ctrl = self.image = ImageView(Image(FileInputStream(imagefile)))
        else: self.ctrl = self.image = ImageView()
        if width: self.ctrl.setFitWidth(width)
        if height: self.ctrl.setFitHeight(height)
        if handler: self.ctrl.setOnAction(handler)
        self.ctrl.setPreserveRatio(True);
        self.ctrl.fitWidthProperty().bind(parent.widthProperty())
        self.ctrl.fitHeightProperty().bind(parent.heightProperty())  
        self.Initialize(**kwargs)
        if self.scroll:
            self.scroll.setVbarPolicy(ScrollBarPolicy.AS_NEEDED)
            self.scroll.setHbarPolicy(ScrollBarPolicy.ALWAYS)
            self.scroll.setPannable(True)
            self.scroll.setFitToHeight(True)
            #box = wHBox(1,1)
            #box.add_item(self.image,True)      
            self.scroll.setContent(self.image)
            self.ctrl = self.scroll

class wButton(wControl):
    def __init__(self,text=None,handler=None,iconfile=None,iconimage=None,**kwargs):   
        from javafx.scene.control import Button
        self.ctrl = Button()
        self.Initialize(**kwargs)
        if text: self.ctrl.setText(text)
        if handler: self.ctrl.setOnAction(GetHandler(self,handler))
        if iconfile: self.set_icon(iconfile,16)
        if iconimage: self.set_icon_image(iconimage,16)
        
class wToggleButton(wControl):
    def __init__(self,text,handler=None,**kwargs):
        from javafx.scene.control import ToggleButton
        self.ctrl = ToggleButton()
        self.Initialize(**kwargs)
        self.ctrl.setText(text)
        if handler: self.ctrl.setOnAction(GetHandler(self,handler))
    def is_selected(self): return self.ctrl.isSelected()
    def set_selected(self,v): return self.ctrl.setSelected(v)
        
class wCheckBox(wControl):
    def __init__(self,text,handler=None,**kwargs):
        from javafx.scene.control import CheckBox
        self.ctrl = CheckBox()
        self.Initialize(**kwargs)
        self.ctrl.setText(text)
        if handler: self.ctrl.setOnAction(GetHandler(self,handler))
    def is_selected(self): return self.ctrl.isSelected()
    def set_selected(self,v): return self.ctrl.setSelected(v)

class wText(wControl):
    def get_text(self): return self.ctrl.getText()
    def set_text(self,text): self.ctrl.setText(text)
    def insert(self,index,text): self.ctrl.insertText(index,text)
    def append(self,text): self.ctrl.appendText(text)
    def clear(self): self.ctrl.clear()
    def copy(self): self.ctrl.copy()
    def paste(self): self.ctrl.paste()
        
class wTextField(wText):
    def __init__(self,text=None,handler=None,**kwargs):
        from javafx.scene.control import TextField
        self.ctrl = TextField()
        self.Initialize(**kwargs)
        SetFileDropHandler( self.ctrl, self.on_drop )  
        if text: self.ctrl.setText(text)
        if handler: self.ctrl.setOnAction(GetHandler(self,handler))
    def on_drop(self,files): self.ctrl.setText( files.get(0).getPath() )
        
class wTextArea(wText):
    def __init__(self,text=None,**kwargs):
        from javafx.scene.control import TextArea
        self.ctrl = TextArea()
        self.Initialize(**kwargs)
        if text: self.ctrl.setText(text)

class wChoiceBox(wControl):
    def __init__(self,items=None,handler=None,**kwargs):
        from javafx.beans.value import ChangeListener
        from javafx.beans.value import ObservableValue
        from javafx.scene.control import ChoiceBox
        self.ctrl = ChoiceBox()
        self.Initialize(**kwargs)
        if items: self.ctrl.getItems().addAll(items);
        self.ctrl.getSelectionModel().selectFirst() 
        if handler: self.ctrl.setOnAction(GetHandler(self,handler))
    def add_item(self,item): self.ctrl.getItems().add(item);
    def get_selected_item(self): return self.ctrl.getSelectionModel().getSelectedItem()
    def get_selected_index(self): return self.ctrl.getSelectionModel().getSelectedIndex()

class wComboBox(wControl):
    def __init__(self,items=None,handler=None,**kwargs):
        from javafx.beans.value import ChangeListener
        from javafx.beans.value import ObservableValue
        from javafx.scene.control import ComboBox
        self.ctrl = ComboBox()
        self.Initialize(**kwargs)
        if items: self.ctrl.getItems().addAll(items);
        self.ctrl.getSelectionModel().selectFirst() 
        if handler: self.ctrl.setOnAction(GetHandler(self,handler))
    def add_item(self,item): self.ctrl.getItems().add(item);
    def get_selected_item(self): return self.ctrl.getSelectionModel().getSelectedItem()
    def get_selected_index(self): return self.ctrl.getSelectionModel().getSelectedIndex()

class wListBox(wControl):
    def __init__(self,items=None,handler=None,**kwargs):
        from javafx.beans.value import ChangeListener
        from javafx.beans.value import ObservableValue
        from javafx.scene.control import ListView
        self.ctrl = ListView()
        self.Initialize(**kwargs)
        if items: self.ctrl.getItems().addAll(items);
        self.ctrl.getSelectionModel().selectFirst() 
        if handler: self.ctrl.getSelectionModel().selectedItemProperty().addListener(GetHandler(self,handler))
    def add_item(self,item): self.ctrl.getItems().add(item);
    def get_selected_item(self): return self.ctrl.getSelectionModel().getSelectedItem()
    def get_selected_index(self): return self.ctrl.getSelectionModel().getSelectedIndex()
        
class wTableRow():
    def __init__(self,items=None):
        self.values = []
        if items:
            for item in items: self.add_item(item)
    def add_item(self,item):
        from javafx.beans.property import SimpleStringProperty
        self.values.append(SimpleStringProperty(item))

class wTableView(wControl):
    def __init__(self,columns=None,widths=None,aligns=None,rwidths=None,handler=None,**kwargs):
        from javafx.scene.control import TableView
        self.ctrl = TableView()
        self.Initialize(**kwargs)
        if handler: self.ctrl.getSelectionModel().selectedItemProperty().addListener(GetHandler(self,handler))
        if columns: self.set_column(columns)
        if widths: self.set_column_width(widths) 
        if rwidths: self.set_column_width_percent(rwidths) 
        if aligns: self.set_column_align(aligns) 
    def set_column(self,labels):
        from javafx.scene.control import TableColumn
        for label in labels: self.add_column(label)
    def add_column(self,label):
        from javafx.scene.control import TableColumn
        index = self.ctrl.getColumns().size()
        column = TableColumn(label)
        column.setCellValueFactory(lambda row : row.getValue().values[index])
        self.ctrl.getColumns().add(column);
    def add_row(self,row): self.ctrl.getItems().add(wTableRow(row))
    def add_items(self,row): self.ctrl.getItems().add(wTableRow(row))
    def set_column_width(self,widths):
        from javafx.scene.control import TableView
        self.ctrl.setColumnResizePolicy( TableView.UNCONSTRAINED_RESIZE_POLICY )
        for i in range(len(widths)):
            column = self.ctrl.getColumns().get(i)
            column.setPrefWidth( widths[i] )
    def set_column_width_percent(self,widths):
        from javafx.scene.control import TableView
        self.ctrl.setColumnResizePolicy( TableView.CONSTRAINED_RESIZE_POLICY )
        for i in range(len(widths)):
            column = self.ctrl.getColumns().get(i)
            column.setMaxWidth( widths[i] * 100000 )
    def set_column_align(self,aligns):
        for i in range(len(aligns)):
            column = self.ctrl.getColumns().get(i)
            if aligns[i] < 0: column.setStyle( "-fx-alignment: CENTER-LEFT;")
            elif aligns[i] == 0: column.setStyle( "-fx-alignment: CENTER;")
            elif aligns[i] > 0: column.setStyle( "-fx-alignment: CENTER-RIGHT;")
    def clear_selection(self): self.ctrl.getSelectionModel().clearSelection()
    def select_first(self): self.ctrl.getSelectionModel().selectFirst()
    def select_last(self): self.ctrl.getSelectionModel().selectLast();
    def enable_multi_selection(self):
        from javafx.scene.control import SelectionMode
        self.ctrl.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE)
    def get_selected_index(self): return self.ctrl.getSelectionModel().getSelectedIndex()
    def get_selected_item(self):
        index = self.ctrl.getSelectionModel().getSelectedIndex()
        return self.ctrl.getItems().get(index).values
    def get_selected_items(self):
        items = []
        for item in self.ctrl.getSelectionModel().getSelectedItems():
            items.append( item.values )
        return items;
    def remove_selected_items(self): self.ctrl.getItems().removeAll(self.ctrl.getSelectionModel().getSelectedItems());

class wTreeView(wControl):
    def __init__(self,name,handler=None,**kwargs):
        from javafx.scene.control import TreeView
        from javafx.scene.control import TreeItem
        self.root = TreeItem(name)
        self.ctrl = TreeView(self.root)
        self.Initialize(**kwargs)
        if handler: self.ctrl.getSelectionModel().selectedItemProperty().addListener(GetHandler(self,handler))
    def add_root_item(self,label): return self.add_item(label)
    def add_item(self,label,parent=None):
        from javafx.scene.control import TreeItem
        item = TreeItem(label)
        if parent: parent.getChildren().add(item)
        else: self.root.getChildren().add(item)
        return item
    def get_selected_index(self): return self.ctrl.getSelectionModel().getSelectedIndex()
    def get_selected_item(self): return self.ctrl.getSelectionModel().getSelectedItem()
    def get_selected_item_text(self): return self.ctrl.getSelectionModel().getSelectedItem().getValue()
    def get_selected_item_path(self,delim=""):
        item = self.ctrl.getSelectionModel().getSelectedItem()
        return self.get_item_path(item)
    def get_item_path(self,item,delim=""):
        item = self.ctrl.getSelectionModel().getSelectedItem()
        path = item.getValue()
        while item.getParent():
            item = item.getParent()
            path = item.getValue() + delim + path
        return path
    def get_parent_item(self,item): return item.getParent()
    def get_item_value(self,item): return item.getValue()

class wProgressBar(wControl):
    def __init__(self,**kwargs):   
        from javafx.scene.control import ProgressBar
        self.ctrl = ProgressBar()
        self.Initialize(**kwargs)
    def get_value(self): return self.ctrl.getProgress()
    def set_value(self,v): self.ctrl.setProgress(v)


#
# Container
#

class wBox():
    def get_item(self,index): return self.ctrl.getChildren().get(index)
    def add_item(self,item,expand=False): 
        self.ctrl.getChildren().add(item)
        if expand: 
            item.setMaxHeight( Double.MAX_VALUE )
            self.set_expand(item)
    def align_left(self): self.set_align_left()
    def align_right(self): self.set_align_right()
    def addSeparator(self):
        from javafx.scene.control import Separator
        sep = Separator()
        sep.setOrientation(self.separatorOrientation)
        self.ctrl.getChildren().add( sep )

class wVBox(wBox):
    def __init__(self,gap=0,pad=0):
        self.ctrl = VBox( gap )
        self.set_expand = lambda x : VBox.setVgrow(x, Priority.ALWAYS)
        self.set_align_left = lambda : self.ctrl.setAlignment(Pos.CENTER_LEFT)
        self.set_align_right = lambda : self.ctrl.setAlignment(Pos.CENTER_RIGHT)
        self.separatorOrientation = Orientation.HORIZONTAL
        self.ctrl.setAlignment(Pos.CENTER)
        self.ctrl.setSpacing( gap )
        self.ctrl.setPadding( Insets( pad, pad, pad, pad ) )

class wHBox(wBox):
    def __init__(self,gap=0,pad=0):
        self.ctrl = HBox( gap )
        self.set_expand = lambda x : HBox.setHgrow(x, Priority.ALWAYS)
        self.set_align_left = lambda : self.ctrl.setAlignment(Pos.CENTER_LEFT)
        self.set_align_right = lambda : self.ctrl.setAlignment(Pos.CENTER_RIGHT)
        self.separatorOrientation = Orientation.VERTICAL
        self.ctrl.setAlignment(Pos.CENTER)
        self.ctrl.setSpacing( gap )
        self.ctrl.setPadding( Insets( pad, pad, pad, pad ) )

class wBorderPane():
    def __init__(self):
        from javafx.scene.layout import BorderPane
        from javafx.geometry import Insets
        self.ctrl = BorderPane()
        self.ctrl.setPadding(Insets(0, 0, 0, 0))
    def set_top(self,item):    self.ctrl.setTop(item)
    def set_bottom(self,item): self.ctrl.setBottom(item)
    def set_left(self,item):   self.ctrl.setLeft(item)
    def set_right(self,item):  self.ctrl.setRight(item)
    def set_center(self,item): self.ctrl.setCenter(item)

class wTabPane():
    def __init__(self,labels=None,items=None):
        from javafx.scene.control import TabPane
        self.ctrl = TabPane()
        if labels and items:
            for i in range(0,len(items)):
                self.add_item( labels[i], items[i] )        
    def add_item(self, label, item):
        from javafx.scene.control import Tab
        tab = Tab()
        tab.setText(label)
        tab.setClosable(False)
        tab.setContent(item.ctrl)
        self.ctrl.getTabs().add(tab)
       
class wSplitPane(object):
    def add_item(self,item): self.ctrl.getItems().add(item.ctrl)
    def add_items(self,items):
        if items:
            for item in items:
                self.add_item(item)
        
class wVSplitPane(wSplitPane):
    def __init__(self,first=None,items=None):
        from javafx.scene.control import SplitPane
        self.ctrl = SplitPane()
        self.ctrl.setOrientation(Orientation.VERTICAL);
        if first: self.ctrl.setDividerPositions(first, 1-first);
        if items: self.add_items(items)

class wHSplitPane(wSplitPane):
    def __init__(self,first=None,items=None):
        from javafx.scene.control import SplitPane
        self.ctrl = SplitPane()
        self.ctrl.setOrientation(Orientation.HORIZONTAL);
        if first: self.ctrl.setDividerPositions(first, 1-first);
        if items: self.add_items(items)

#
# Window
#
      
def wMenu(name,menu_table):
    from javafx.scene.control import ContextMenu
    if name: menu = Menu()
    else: menu = ContextMenu(name)
    for m in menu_table:
        if name == '-': menu.getItems().add(SeparatorMenuItem());
        if item:
            if type(item) == list:
                menu.getItems().add(wMenu(name,item))
            else:
                item = MenuItem(m['name'])
                item.setOnAction(m['item'])
                menu.getItems().add(item);
    return menu

def wContextMenu(menu_table):
    return wMenu(None,menu_table)
    
class wMenuBar():
    def __init__(self,menutable=None,fontsize=None): 
        from javafx.scene.control import ( MenuBar, Menu, MenuItem )
        self.ctrl = self.menubar = MenuBar()
        if fontsize: self.menubar.setStyle("-fx-font: " + fontsize + " arial;")  
        if menutable: self.add_ezmenubar(menutable)
    def add_separater(self):
        from javafx.scene.control import SeparatorMenuItem
        self.menu.getItems().add(SeparatorMenuItem())
    def add_ezmenuitem(self,menu,menuitem):
        #print( 'ezmenuitem', menu, menuitem )
        from javafx.scene.control import SeparatorMenuItem
        remain = len(menuitem)
        if remain == 1 or type(menuitem[1]) != str:
            if menuitem[0] == '-': 
                menu.getItems().add(SeparatorMenuItem())
            else:
                item = MenuItem(menuitem[0])
                if remain > 1 and callable(menuitem[1]): item.setOnAction(menuitem[1])
                menu.getItems().add(item)
        else:
            for m in menu.getItems():
                if m.getText() == menuitem[0]: #type(m) == Menu
                    self.add_ezmenuitem( m, menuitem[1:] )
                    break
            else:
                m = Menu(menuitem[0])
                menu.getItems().add( m )
                self.add_ezmenuitem( m, menuitem[1:] )

    def add_ezmenubar(self,menutable):
        #print( 'ezmenubar', menutable )
        for menuitem in menutable:
            for menu in self.menubar.getMenus():
                if menu.getText() == menuitem[0]:
                    self.add_ezmenuitem(menu, menuitem[1:])
                    break
            else:
                menu = Menu(menuitem[0])
                self.menubar.getMenus().add(menu)
                self.add_ezmenuitem(menu, menuitem[1:])

class wToolBar(): #Label, Button, CheckBox, ChoiceBox, ComboBox, ProgressBar, ToggleButton, TextField
    def __init__(self,tooltable):
        from javafx.scene.control import ToolBar
        self.ctrl = ToolBar()
        for v in tooltable:
            print('wToolBar',v)
            if v[0] == '-': self.add_separater()
            else:
                icon = v[2] if len(v) > 2 and v[2] is not None else None
                self.add_button( v[0], v[1], icon )   
    def add_separater(self,parent=None):
        from javafx.scene.control import Separator     
        self.ctrl.getItems().add(Separator())
    def add_button(self,text=None,handler=None,icon=None):
        print( 'add_button', text, handler, icon)
        self.ctrl.getItems().add(wButton(text,handler,icon).ctrl)

class wStatusBar(): #Label, Button, CheckBox, ChoiceBox, ComboBox, ProgressBar, ToggleButton, TextField
    def __init__(self,status_table):
        from javafx.scene.control import Separator     
        self.hbox = wHBox(1,1);
        self.ctrl = self.hbox.ctrl
        for v in status_table:
            if type(v) == str:
                if v == '-': self.hbox.add_item(Separator())
                if v == '_': self.add_spacer()
            else:
                self.hbox.add_item(v.ctrl);
        self.add_spacer()
    def add_spacer(self):
        from javafx.scene.layout import Region     
        space = Region(); 
        HBox.setHgrow(space, Priority.ALWAYS)
        self.hbox.add_item(space)
        
class wLayout():
    def __init__(self,layout):
        vbox = wVBox()
        self.ctrl = vbox.ctrl
        for h in layout:
            height = h[1] if len(h) > 1 else 0 #expand
            hbox = wHBox()
            for ctrl in h[0]:
                width = ctrl[1] if len(ctrl) > 1 else 0
                if height > 0: ctrl[0].set_height(height)
                if width > 0: ctrl[0].set_width(width)
                hbox.add_item( ctrl[0].ctrl, expand=True if width == 0 else False )
            vbox.add_item( hbox.ctrl, expand=True if height == 0 else False )
        
class WindowVars():
    def __init__(self):
        self.ctrl = None
        self.stage = None
        self.pane = None
        self.createdHandler = None
        self.closeHandler = None
        self.title = None
        self.icon = None
        self.width = 800
        self.height = 600
        self.menu = None
        self.tool = None
        self.status = None
        self.content = None

__vars = WindowVars()

class wWindow(Application):
    def __init__(self): print('__init__()', self)
    def start(self, stage): 
        print('start()', self)
        from javafx.application import Platform
        __vars.stage = stage
        if __vars.title: __vars.stage.setTitle(__vars.title)
        if __vars.icon: __vars.stage.getIcons().add(Image(FileInputStream(__vars.icon)))   
        if __vars.closeHandler: __vars.stage.setOnCloseRequest(__vars.closeHandler)
        Platform.setImplicitExit(True)
        __vars.pane = wBorderPane()
        vbox = wVBox()
        if __vars.menu: vbox.add_item(wMenuBar(__vars.menu).ctrl)
        if __vars.tool: vbox.add_item(wToolBar(__vars.tool).ctrl)
        __vars.pane.set_top(vbox.ctrl)
        if __vars.status: __vars.pane.set_bottom(wStatusBar(__vars.status()).ctrl)           
        if __vars.content: __vars.pane.set_center(wLayout(__vars.content()).ctrl)
        __vars.scene = Scene(__vars.pane.ctrl, __vars.width, __vars.height)
        __vars.stage.setScene(__vars.scene)
        if __vars.createdHandler: __vars.createdHandler()
        __vars.stage.show()      
    def run(self): 
        print('Run()', self)
        self.launch(self.class, [])
    def get_stage(self): return __vars.stage     
    def set_title(self,title): print('SetTitle()'); __vars.title = title
    def set_size(self,width,height): __vars.width = width;  __vars.height = height    
    def set_icon(self,icon): __vars.icon = icon
    def set_on_create(self,handler): __vars.createdHandler = handler
    def set_on_close(self,handler): __vars.closeHandler = handler
    def close(self): __vars.stage.close()
    def set_menubar(self,menu): __vars.menu = menu
    def set_toolbar(self,tool): __vars.tool = tool
    def set_statusbar(self,status): __vars.status = status
    def set_content(self,content): __vars.content = content

#
# Application
#

def on_exit(event): appWin.close()
def on_check(arg): stc1.append( str(arg.sender.is_selected()) + "\n" )
def on_combo(arg): stc1.append( arg.sender.get_selected_item() + "\n" )
def on_list(arg): stc1.append( arg.sender.get_selected_item() + "\n" )
def on_table(arg): 
    for row in arg.sender.get_selected_items():
        for value in row: stc1.append( str(value) + " " )
        stc1.append("\n")

def on_run(arg):
    StartThread(th_handler,None)
def update_status(i):
    progress.set_value(i)
    label1.set_value(i)
def th_handler(args=None):
    import time
    for i in range(100):
        RunLater(lambda : update_status(i), progress.ctrl)   
        time.sleep(0.1)     
            
main_menu = \
   [[ 'File', 'Exit', on_exit, "./icon/help.png" ],
    [ 'File', '-'],
    [ 'File', "Menu", 'About', on_exit ], 
    [ 'File', "Menu", 'About2', on_exit ], 
    [ 'Help', 'About', on_exit ]]        

main_toolbar = \
    [[ 'Exit', on_exit, "./icon/help.png" ],
     [ '-', None ],
     [ 'About', on_exit ]]

def main_status():
    import fx_icon
    global progress, label1, appWin
    exit_icon = fx_icon.GetExitImage()
    progress= wProgressBar()
    label1  = wLabel(text="Ready", width=100)
    button1 = wButton(text="Run", handler = on_run, iconimage=exit_icon, width=48)
    button2 = wButton(text="Image", handler = on_exit, width=64)
    button3 = wButton(text=None, iconfile="./icon/help.png", handler = lambda e: appWin.close(), width=32, height=16)
    return [ progress, label1, button1, button2, button3 ] 

def main_content():
    global table_view, listbox, image2, stc1
    combobox = wComboBox(items=("apple","grape"),handler=on_combo)
    checkbox = wCheckBox("Click",handler=on_check)
    textbox  = wTextField()
    button   = wButton(iconfile="./icon/help.png", handler=on_exit)
            
    stc1 = wTextArea()
    listbox  = wListBox(items=("apple","grape","pear","melon"),handler=on_list)
    split = wHSplitPane(items=(stc1,listbox))
    
    table_view = wTableView(handler=on_table)
    table_view.enable_multi_selection()
    table_view.set_column(["One","Two"])
    table_view.add_items(("1111111111","222222222"))
    table_view.add_items(("33","44"))
    table_view.add_items(("5555","466664"))

    image2 = wImageView(imagefile='D:/Lenna.png',scroll=True)
    tab = wTabPane()
    tab.add_item("List",table_view)
    tab.add_item("Edit/Web",split)    
    tab.add_item("Image",wImageView(imagefile='D:/Lenna.png',stretch='none'))    
    tab.add_item("Image2",image2)    

    h1 = [[ combobox, -1 ],
          [ checkbox, -1 ],
          [ textbox, 0 ],
          [ button, 16 ]]
          
    h2 = [[tab, 0 ]]
    
    return [[h1, -1], [h2, 0]]       
    
if __name__ == "__main__":
    global appWin
    appWin = wWindow()
    appWin.set_title("FxWin")
    appWin.set_size(640,400)
    appWin.set_icon("./icon/Lenna.png")
    appWin.set_menubar(main_menu)
    appWin.set_toolbar(main_toolbar)
    appWin.set_statusbar(main_status)
    appWin.set_content(main_content)
    appWin.run()