|
Groovy/FAQ/Пользовательский интерфейс
Материал из Wiki.crossplatform.ru
[править] Parsing Program Arguments
//----------------------------------------------------------------------------------
// The are several Java options builder packages available. Some popular ones:
// Apache Jakarta Commons CLI: http://jakarta.apache.org/commons/cli/
// jopt-simple: http://jopt-simple.sourceforge.net
// args4j: https://args4j.dev.java.net/ (requires Java 5 with annotations)
// jargs: http://jargs.sourceforge.net/
// te-code: http://te-code.sourceforge.net/article-20041121-cli.html
// Most of these can be used from Groovy with some Groovy code benefits.
// Groovy also has the CliBuilder built right in.
// CliBuilder example
def cli = new CliBuilder()
cli.v(longOpt: 'verbose', 'verbose mode')
cli.D(longOpt: 'Debug', 'display debug info')
cli.o(longOpt: 'output', 'use/specify output file')
def options = cli.parse(args)
if (options.v) // ...
if (options.D) println 'Debugging info available'
if (options.o) {
println 'Output file flag was specified'
println "Output file is ${options.o}"
}
// ...
// jopt-simple example 1 (short form)
cli = new joptsimple.OptionParser("vDo::")
options = cli.parse(args)
if (options.wasDetected('o')) {
println 'Output file flag was specified.'
println "Output file is ${options.argumentsOf('o')}"
}
// ...
// jopt-simple example 2 (declarative form)
op = new joptsimple.OptionParser()
VERBOSE = 'v'; op.accepts( VERBOSE, "verbose mode" )
DEBUG = 'D'; op.accepts( DEBUG, "display debug info" )
OUTPUT = 'o'; op.accepts( OUTPUT, "use/specify output file" ).withOptionalArg().
describedAs( "file" ).ofType( File.class )
options = op.parse(args)
params = options.nonOptionArguments()
if (options.wasDetected( DEBUG )) println 'Debugging info available'
// ...
//----------------------------------------------------------------------------------
[править] Testing Whether a Program Is Running Interactively
//----------------------------------------------------------------------------------
// Groovy like Java can be run in a variety of scenarios, not just interactive vs
// non-interative, e.g. within a servlet container. Sometimes InputStreams and other
// mechanisms are used to hide away differences between the different containers
// in which code is run; other times, code needs to be written purpose-built for
// the container in which it is running. In most situations where the latter applies
// the container will have specific lifecycle mechanisms to allow the code to
// access specific needs, e.g. javax.servlet.ServletRequest.getInputStream()
// rather than System.in
//----------------------------------------------------------------------------------
[править] Clearing the Screen
//----------------------------------------------------------------------------------
// Idiomatically Groovy encourages GUI over text-based applications where a rich
// interface is desirable. Libraries for richer text-based interfaces include:
// jline: http://jline.sourceforge.net
// jcurses: http://sourceforge.net/projects/javacurses/
// java-readline: http://java-readline.sourceforge.net
// enigma console: http://sourceforge.net/projects/enigma-shell/
// Note: Run examples using these libraries from command line not inside an IDE.
// If you are using a terminal/console that understands ANSI codes
// (excludes WinNT derivatives) you can just print the ANSI codes
print ((char)27 + '[2J')
// jline has constants for ANSI codes
import jline.ANSIBuffer
print ANSIBuffer.ANSICodes.clrscr()
// Also available through ConsoleReader.clearScreen()
// Using jcurses
import jcurses.system.*
bg = CharColor.BLACK
fg = CharColor.WHITE
screenColors = new CharColor(bg, fg)
Toolkit.clearScreen(screenColors)
//----------------------------------------------------------------------------------
[править] Determining Terminal or Window Size
//----------------------------------------------------------------------------------
// Not idiomatic for Groovy to use text-based applications here.
// Using jcurses: http://sourceforge.net/projects/javacurses/
// use Toolkit.screenWidth and Toolkit.screenHeight
// 'barchart' example
import jcurses.system.Toolkit
numCols = Toolkit.screenWidth
rand = new Random()
if (numCols < 20) throw new RuntimeException("You must have at least 20 characters")
values = (1..5).collect { rand.nextInt(20) } // generate rand values
max = values.max()
ratio = (numCols - 12)/max
values.each{ i ->
printf('%8.1f %s\n', [i as double, "*" * ratio * i])
}
// gives, for example:
// 15.0 *******************************
// 10.0 *********************
// 5.0 **********
// 14.0 *****************************
// 18.0 **************************************
// Run from command line not inside an IDE which may give false width/height values.
//----------------------------------------------------------------------------------
[править] Changing Text Color
//----------------------------------------------------------------------------------
// Idiomatically Groovy encourages GUI over text-based applications where a rich
// interface is desirable. See 15.3 for richer text-based interface libraries.
// Note: Run examples using these libraries from command line not inside an IDE.
// If you are using a terminal/console that understands ANSI codes
// (excludes WinNT derivatives) you can just print the ANSI codes
ESC = "${(char)27}"
redOnBlack = ESC + '[31;40m'
reset = ESC + '[0m'
println (redOnBlack + 'Danger, Will Robinson!' + reset)
// jline has constants for ANSI codes
import jline.ANSIBuffer
redOnBlack = ANSIBuffer.ANSICodes.attrib(31) + ANSIBuffer.ANSICodes.attrib(40)
reset = ANSIBuffer.ANSICodes.attrib(0)
println redOnBlack + 'Danger, Will Robinson!' + reset
// Using JavaCurses
import jcurses.system.*
import jcurses.widgets.*
whiteOnBlack = new CharColor(CharColor.BLACK, CharColor.WHITE)
Toolkit.clearScreen(whiteOnBlack)
redOnBlack = new CharColor(CharColor.BLACK, CharColor.RED)
Toolkit.printString("Danger, Will Robinson!", 0, 0, redOnBlack)
Toolkit.printString("This is just normal text.", 0, 1, whiteOnBlack)
// Blink not supported by JavaCurses
// Using jline constants for Blink
blink = ANSIBuffer.ANSICodes.attrib(5)
reset = ANSIBuffer.ANSICodes.attrib(0)
println (blink + 'Do you hurt yet?' + reset)
// Using jline constants for Coral snake rhyme
def ansi(code) { ANSIBuffer.ANSICodes.attrib(code) }
redOnBlack = ansi(31) + ansi(40)
redOnYellow = ansi(31) + ansi(43)
greenOnCyanBlink = ansi(32) + ansi(46) + ansi(5)
reset = ansi(0)
println redOnBlack + "venom lack"
println redOnYellow + "kill that fellow"
println greenOnCyanBlink + "garish!" + reset
//----------------------------------------------------------------------------------
[править] Reading from the Keyboard
//----------------------------------------------------------------------------------
// Default Java libraries buffer System.in by default.
// Using JavaCurses:
import jcurses.system.Toolkit
print 'Press a key: '
println "\nYou pressed the '${Toolkit.readCharacter().character}' key"
// Also works for special keys:
import jcurses.system.InputChar
print "Press the 'End' key to finish: "
ch = Toolkit.readCharacter()
assert ch.isSpecialCode()
assert ch.code == InputChar.KEY_END
// See also jline Terminal#readCharacter() and Terminal#readVirtualKey()
//----------------------------------------------------------------------------------
[править] Ringing the Terminal Bell
//----------------------------------------------------------------------------------
print "${(char)7}"
// Using jline constant
print "${jline.ConsoleOperations.KEYBOARD_BELL}"
// Also available through ConsoleReader.beep()
// Using JavaCurses (Works only with terminals that support 'beeps')
import jcurses.system.Toolkit
Toolkit.beep()
//----------------------------------------------------------------------------------
[править] Using POSIX termios
//----------------------------------------------------------------------------------
// I think you would need to resort to platform specific calls here,
// E.g. on *nix systems call 'stty' using execute().
// Some things can be set through the packages mentioned in 15.3, e.g.
// echo can be turned on and off, but others like setting the kill character
// didn't appear to be supported (presumably because it doesn't make
// sense for a cross-platform toolkit).
//----------------------------------------------------------------------------------
[править] Checking for Waiting Input
//----------------------------------------------------------------------------------
// Consider using Java's PushbackInputStream or PushbackReader
// Different functionality to original cookbook but can be used
// as an alternative for some scenarios.
//----------------------------------------------------------------------------------
[править] Reading Passwords
//----------------------------------------------------------------------------------
// If using Java 6, use Console.readPassword()
// Otherwise use jline (use 0 instead of mask character '*' for no echo):
password = new jline.ConsoleReader().readLine(new Character('*'))
//----------------------------------------------------------------------------------
[править] Editing Input
//----------------------------------------------------------------------------------
// In Groovy (like Java) normal input is buffered so you can normally make
// edits before hitting 'Enter'. For more control over editing (including completion
// and history etc.) use one of the packages mentioned in 15.3, e.g. jline.
//----------------------------------------------------------------------------------
[править] Managing the Screen
//----------------------------------------------------------------------------------
// Use javacurses or jline (see 15.3) for low level screen management.
// Java/Groovy would normally use a GUI for such functionality.
// Here is a slight variation to cookbook example. This repeatedly calls
// the command feedin on the command line, e.g. "cmd /c dir" on windows
// or 'ps -aux' on Linux. Whenever a line changes, the old line is "faded
// out" using font colors from white through to black. Then the new line
// is faded in using the reverse process.
import jcurses.system.*
color = new CharColor(CharColor.BLACK, CharColor.WHITE)
Toolkit.clearScreen(color)
maxcol = Toolkit.screenWidth
maxrow = Toolkit.screenHeight
colors = [CharColor.WHITE, CharColor.CYAN, CharColor.YELLOW, CharColor.GREEN,
CharColor.RED, CharColor.BLUE, CharColor.MAGENTA, CharColor.BLACK]
done = false
refresh = false
waittime = 8000
oldlines = []
def fade(line, row, colorList) {
for (i in 0..<colorList.size()) {
Toolkit.printString(line, 0, row, new CharColor(CharColor.BLACK, colorList[i]))
sleep 10
}
}
while(!done) {
if (waittime > 9999 || refresh) {
proc = args[0].execute()
lines = proc.text.split('\n')
for (r in 0..<maxrow) {
if (r >= lines.size() || r > oldlines.size() || lines[r] != oldlines[r]) {
if (oldlines != [])
fade(r < oldlines.size() ? oldlines[r] : ' ' * maxcol, r, colors)
fade(r < lines.size() ? lines[r] : ' ' * maxcol, r, colors.reverse())
}
}
oldlines = lines
refresh = false
waittime = 0
}
waittime += 200
sleep 200
}
// Keyboard handling would be similar to 15.6.
// Something like below but need to synchronize as we are in different threads.
Thread.start{
while(!done) {
ch = Toolkit.readCharacter()
if (ch.isSpecialCode() || ch.character == 'q') done = true
else refresh = true
}
}
//----------------------------------------------------------------------------------
[править] Controlling Another Program with Expect
//----------------------------------------------------------------------------------
// These examples uses expectj, a pure Java Expect-like module.
// http://expectj.sourceforge.net/
defaultTimeout = -1 // infinite
expect = new expectj.ExpectJ("logfile.log", defaultTimeout)
command = expect.spawn("program to run")
command.expect('Password', 10)
// expectj doesn't support regular expressions, but see readUntil
// in recipe 18.6 for how to manually code this
command.expect('invalid')
command.send('Hello, world\r')
// kill spawned process
command.stop()
// expecting multiple choices
// expectj doesn't support multiple choices, but see readUntil
// in recipe 18.6 for how to manually code this
//----------------------------------------------------------------------------------
[править] Creating Menus with Tk
//----------------------------------------------------------------------------------
// Methods not shown for the edit menu items, they would be the same as for the
// file menu items.
import groovy.swing.SwingBuilder
def print() {}
def save() {}
frame = new SwingBuilder().frame(title:'Demo') {
menuBar {
menu(mnemonic:'F', 'File') {
menuItem (actionPerformed:this.&print, 'Print')
separator()
menuItem (actionPerformed:this.&save, 'Save')
menuItem (actionPerformed:{System.exit(0)}, 'Quit immediately')
}
menu(mnemonic:'O', 'Options') {
checkBoxMenuItem ('Create Debugging Info', state:true)
}
menu(mnemonic:'D', 'Debug') {
group = buttonGroup()
radioButtonMenuItem ('Log Level 1', buttonGroup:group, selected:true)
radioButtonMenuItem ('Log Level 2', buttonGroup:group)
radioButtonMenuItem ('Log Level 3', buttonGroup:group)
}
menu(mnemonic:'F', 'Format') {
menu('Font') {
group = buttonGroup()
radioButtonMenuItem ('Times Roman', buttonGroup:group, selected:true)
radioButtonMenuItem ('Courier', buttonGroup:group)
}
}
menu(mnemonic:'E', 'Edit') {
menuItem (actionPerformed:{}, 'Copy')
menuItem (actionPerformed:{}, 'Cut')
menuItem (actionPerformed:{}, 'Paste')
menuItem (actionPerformed:{}, 'Delete')
separator()
menu('Object ...') {
menuItem (actionPerformed:{}, 'Circle')
menuItem (actionPerformed:{}, 'Square')
menuItem (actionPerformed:{}, 'Point')
}
}
}
}
frame.pack()
frame.show()
//----------------------------------------------------------------------------------
[править] Creating Dialog Boxes with Tk
//----------------------------------------------------------------------------------
// Registration Example
import groovy.swing.SwingBuilder
def cancel(event) {
println 'Sorry you decided not to register.'
dialog.dispose()
}
def register(event) {
if (swing.name?.text) {
println "Welcome to the fold $swing.name.text"
dialog.dispose()
} else println "You didn't give me your name!"
}
def dialog(event) {
dialog = swing.createDialog(title:'Entry')
def panel = swing.panel {
vbox {
hbox {
label(text:'Name')
textField(columns:20, id:'name')
}
hbox {
button('Register', actionPerformed:this.®ister)
button('Cancel', actionPerformed:this.&cancel)
}
}
}
dialog.getContentPane().add(panel)
dialog.pack()
dialog.show()
}
swing = new SwingBuilder()
frame = swing.frame(title:'Registration Example') {
panel {
button(actionPerformed:this.&dialog, 'Click Here For Registration Form')
glue()
button(actionPerformed:{System.exit(0)}, 'Quit')
}
}
frame.pack()
frame.show()
// Error Example, slight variation to original cookbook
import groovy.swing.SwingBuilder
import javax.swing.WindowConstants as WC
import javax.swing.JOptionPane
def calculate(event) {
try {
swing.result.text = evaluate(swing.expr.text)
} catch (Exception ex) {
JOptionPane.showMessageDialog(frame, ex.message)
}
}
swing = new SwingBuilder()
frame = swing.frame(title:'Calculator Example',
defaultCloseOperation:WC.EXIT_ON_CLOSE) {
panel {
vbox {
hbox {
label(text:'Expression')
hstrut()
textField(columns:12, id:'expr')
}
hbox {
label(text:'Result')
glue()
label(id:'result')
}
hbox {
button('Calculate', actionPerformed:this.&calculate)
button('Quit', actionPerformed:{System.exit(0)})
}
}
}
}
frame.pack()
frame.show()
//----------------------------------------------------------------------------------
[править] Responding to Tk Resize Events
//----------------------------------------------------------------------------------
// Resizing in Groovy follows Java rules, i.e. is dependent on the layout manager.
// You can set preferred, minimum and maximum sizes (may be ignored by some layout managers).
// You can setResizable(false) for some components.
// You can specify a weight value for some layout managers, e.g. GridBagLayout
// which control the degree of scaling which occurs during resizing.
// Some layout managers, e.g. GridLayout, automaticaly resize their contained widgets.
// You can capture resize events and do everything manually yourself.
//----------------------------------------------------------------------------------
[править] Removing the DOS Shell Window with Windows Perl/Tk
//----------------------------------------------------------------------------------
// Removing DOS console on Windows:
// If you are using java.exe to start your Groovy script, use javaw.exe instead.
// If you are using groovy.exe to start your Groovy script, use groovyw.exe instead.
//----------------------------------------------------------------------------------
[править] Program: Small termcap program
//----------------------------------------------------------------------------------
// additions to original cookbook:
// random starting position
// color changes after each bounce
import jcurses.system.*
color = new CharColor(CharColor.BLACK, CharColor.WHITE)
Toolkit.clearScreen(color)
rand = new Random()
maxrow = Toolkit.screenWidth
maxcol = Toolkit.screenHeight
rowinc = 1
colinc = 1
row = rand.nextInt(maxrow)
col = rand.nextInt(maxcol)
chars = '*-/|\\_'
colors = [CharColor.RED, CharColor.BLUE, CharColor.YELLOW,
CharColor.GREEN, CharColor.CYAN, CharColor.MAGENTA]
delay = 20
ch = null
def nextChar(){
ch = chars[0]
chars = chars[1..-1] + chars[0]
color = new CharColor(CharColor.BLACK, colors[0])
colors = colors[1..-1] + colors[0]
}
nextChar()
while(true) {
Toolkit.printString(ch, row, col, color)
sleep delay
row = row + rowinc
col = col + colinc
if (row in [0, maxrow]) { nextChar(); rowinc = -rowinc }
if (col in [0, maxcol]) { nextChar(); colinc = -colinc }
}
//----------------------------------------------------------------------------------
[править] Program: tkshufflepod
//----------------------------------------------------------------------------------
// Variation to cookbook. Let's you reshuffle lines in a multi-line string
// by drag-n-drop.
import java.awt.*
import java.awt.datatransfer.*
import java.awt.dnd.*
import javax.swing.*
import javax.swing.ScrollPaneConstants as SPC
class DragDropList extends JList implements
DragSourceListener, DropTargetListener, DragGestureListener {
def dragSource
def dropTarget
def dropTargetCell
int draggedIndex = -1
def localDataFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType)
def supportedFlavors = [localDataFlavor] as DataFlavor[]
public DragDropList(model) {
super()
setModel(model)
setCellRenderer(new DragDropCellRenderer(this))
dragSource = new DragSource()
dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this)
dropTarget = new DropTarget(this, this)
}
public void dragGestureRecognized(DragGestureEvent dge) {
int index = locationToIndex(dge.dragOrigin)
if (index == -1 || index == model.size() - 1) return
def trans = new CustomTransferable(model.getElementAt(index), this)
draggedIndex = index
dragSource.startDrag(dge, Cursor.defaultCursor, trans, this)
}
public void dragDropEnd(DragSourceDropEvent dsde) {
dropTargetCell = null
draggedIndex = -1
repaint()
}
public void dragEnter(DragSourceDragEvent dsde) { }
public void dragExit(DragSourceEvent dse) { }
public void dragOver(DragSourceDragEvent dsde) { }
public void dropActionChanged(DragSourceDragEvent dsde) { }
public void dropActionChanged(DropTargetDragEvent dtde) { }
public void dragExit(DropTargetEvent dte) { }
public void dragEnter(DropTargetDragEvent dtde) {
if (dtde.source != dropTarget) dtde.rejectDrag()
else dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE)
}
public void dragOver(DropTargetDragEvent dtde) {
if (dtde.source != dropTarget) dtde.rejectDrag()
int index = locationToIndex(dtde.location)
if (index == -1 || index == draggedIndex + 1) dropTargetCell = null
else dropTargetCell = model.getElementAt(index)
repaint()
}
public void drop(DropTargetDropEvent dtde) {
if (dtde.source != dropTarget) {
dtde.rejectDrop()
return
}
int index = locationToIndex(dtde.location)
if (index == -1 || index == draggedIndex) {
dtde.rejectDrop()
return
}
dtde.acceptDrop(DnDConstants.ACTION_MOVE)
def dragged = dtde.transferable.getTransferData(localDataFlavor)
boolean sourceBeforeTarget = (draggedIndex < index)
model.remove(draggedIndex)
model.add((sourceBeforeTarget ? index - 1 : index), dragged)
dtde.dropComplete(true)
}
}
class CustomTransferable implements Transferable {
def object
def ddlist
public CustomTransferable(object, ddlist) {
this.object = object
this.ddlist = ddlist
}
public Object getTransferData(DataFlavor df) {
if (isDataFlavorSupported(df)) return object
}
public boolean isDataFlavorSupported(DataFlavor df) {
return df.equals(ddlist.localDataFlavor)
}
public DataFlavor[] getTransferDataFlavors() {
return ddlist.supportedFlavors
}
}
class DragDropCellRenderer extends DefaultListCellRenderer {
boolean isTargetCell
def ddlist
public DragDropCellRenderer(ddlist) {
super()
this.ddlist = ddlist
}
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean hasFocus) {
isTargetCell = (value == ddlist.dropTargetCell)
boolean showSelected = isSelected && !isTargetCell
return super.getListCellRendererComponent(list, value, index, showSelected, hasFocus)
}
public void paintComponent(Graphics g) {
super.paintComponent(g)
if (isTargetCell) {
g.setColor(Color.black)
g.drawLine(0, 0, size.width.intValue(), 0)
}
}
}
lines = '''
This is line 1
This is line 2
This is line 3
This is line 4
'''.trim().split('\n')
def listModel = new DefaultListModel()
lines.each{ listModel.addElement(it) }
listModel.addElement(' ') // dummy
def list = new DragDropList(listModel)
def sp = new JScrollPane(list, SPC.VERTICAL_SCROLLBAR_ALWAYS, SPC.HORIZONTAL_SCROLLBAR_NEVER)
def frame = new JFrame('Line Shuffle Example')
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
frame.contentPane.add(sp)
frame.pack()
frame.setVisible(true)
//----------------------------------------------------------------------------------
|