challenge クリップボードへの転送

クリップボード(や同等の機能)へテキストを転送するプログラムをお願いします。 また可能でしたらクリップボードのデータを取り出すプログラムもお願いします。

システムに依存する内容ですが、応用範囲が広いと思いましたので出題させてもらいました。

Posted feedbacks - Flatten

Nested Hidden

Rでは、ファイル名に"clipboard"を指定するとクリップボードへの入出力が可能です。(Windows限定かもしれません)

1
2
3
4
> readLines("clipboard")
[1] "クリップボード(や同等の機能)へテキストを転送するプログラムをお願いします。 また可能でしたらクリップボードのデータを取り出すプログラムもお願いします。 "
[2] ""
[3] "システムに依存する内容ですが、応用範囲が広いと思いましたので出題させてもらいました。 "

私が使ってるライブラリそのものですが。私はこのコードを ocean/clipboard.py として保存してるので、

import ocean.clipboard as c
c.get() # クリップボードの中身を取得
c.set("hoge") # クリップボードに設定
c.clear() # クリップボードを空にする

として使います。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
from __future__ import with_statement
from contextlib import contextmanager
import ctypes

GMEM_DDESHARE = 0x2000
GMEM_MOVEABLE = 0x0002
CF_TEXT = 1

user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32

@contextmanager
def _GlobalLock(h):
    p = kernel32.GlobalLock(h)
    if not p:
        raise WindowsError("GlobalLock() failed")
    try:
        yield p
    finally:
        kernel32.GlobalUnlock(h)

@contextmanager
def _Clipboard():
    if not user32.OpenClipboard(None):
        raise WindowsError("OpenClipboard() failed")
    try:
        yield
    finally:
        user32.CloseClipboard()

def set(s):
    s = s.encode("mbcs")
    # memory block
    h = kernel32.GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len(s) + 1)
    if not h:
        raise WindowsError("GlobalAlloc() failed")
    try:
        with _GlobalLock(h) as p:
            p = (ctypes.c_char * (len(s) + 1)).from_address(p)
            for i, c in enumerate(s + "\0"):
                p[i] = c
        # clipboard
        with _Clipboard():
            user32.EmptyClipboard()
            user32.SetClipboardData(CF_TEXT, h)
    except:
        kernel32.GlobalFree(h)
        raise

def get():
    with _Clipboard():
        h = user32.GetClipboardData(CF_TEXT)
        if h:
            with _GlobalLock(h) as p:
                return ctypes.c_char_p(p).value.decode("mbcs")

def clear():
    with _Clipboard():
        user32.EmptyClipboard()

そのものずばりのCPANモジュール Clipboardを使って。
1
2
3
4
5
6
use strict;
use Clipboard;
# クリップボードに転送
Clipboard->copy('foo');
# クリップボードから取り出し
print Clipboard->paste;

ちょっと簡略化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
--- clipboard.py    Mon Feb 11 19:47:02 2008
+++ clipboard.py    Mon Jul  7 02:51:50 2008
@@ -37,9 +37,7 @@
         raise WindowsError("GlobalAlloc() failed")
     try:
         with _GlobalLock(h) as p:
-            p = (ctypes.c_char * (len(s) + 1)).from_address(p)
-            for i, c in enumerate(s + "\0"):
-                p[i] = c
+            (ctypes.c_char * (len(s) + 1)).from_address(p).value = s
         # clipboard
         with _Clipboard():
             user32.EmptyClipboard()

なでしこでは、変数「クリップボード」でクリップボードの読み書きが出来ます。

また、命令としても、クリップボードからの取り出しには「クリップボード取得」、転送には「コピー」が使用できます。

1
2
3
4
5
クリップボードは「変数経由」
クリップボード取得して表示

「命令経由」をコピー
クリップボードを表示

サクラエディタはJScriptやVBScriptで書いたマクロを使えるんだぜ、というアピール。
"hoge.js" などの名前で保存し、「ツール」の「キーマクロの読み込み」でキーマクロに設定したのち、Ctrl + Shift + L で実行します。
1
2
3
4
5
6
Editor.SelectAll();
Editor.Copy();
for (var i=0; i<3; i++) {
  Editor.GoFileEnd();
  Editor.Paste();
}

標準入力或いは、指定のファイルからクリップボードに転送します。 '-show' をつけるとクリップボードのテキストを表示

1
2
3
4
5
6
7
8
require 'win32/clipboard'
include Win32

if ARGV.include?('-show')
  print Clipboard.get_data
else
  Clipboard.set_data(ARGF.read)
end

"hogehoge" をクリップボードに転送。 Firefox3で確認。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function copyToClipboard (copytext) {
  const supstr = Components.classes["@mozilla.org/supports-string;1"].
                   createInstance(Components.interfaces.nsISupportsString);
  const transferable = Components.classes["@mozilla.org/widget/transferable;1"].
                  createInstance(Components.interfaces.nsITransferable);
  const iClipboard = Components.interfaces.nsIClipboard;
  const clipboard = Components.classes["@mozilla.org/widget/clipboard;1"].getService(iClipboard);

  supstr.data = copytext;

  transferable.addDataFlavor("text/unicode");
  transferable.setTransferData("text/unicode", supstr, copytext.length * 2);

  return clipboard.setData(transferable, null, iClipboard.kGlobalClipboard);
}

copyToClipboard('hogehoge');

とりあえず、文字列の場合は以下の通り。 その他、画像やJavaのシリアライズしたオブジェクトなど、さまざまなデータを送れます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;

public class Sample188 {
    public static void main(String[] args) {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

        // クリップボードにデータを設定
        StringSelection setData = new StringSelection("クリップボードにコピーされる。");
        clipboard.setContents(setData, setData);

        // クリップボードからデータを取得
        try {
            String str = (String) clipboard.getData(DataFlavor.stringFlavor);
            System.out.println(str);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Squeak Smalltalk で。

1
2
Clipboard clipboardText: 'クリップボードへ'.
Clipboard clipboardText  "クリップボードから"

クラス化してみました。

【使い方】
clip = new ClipBoard()
// 中身を表示
println clip.text
// セット
clip.text = "新しい内容"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.awt.Toolkit
import java.awt.datatransfer.Clipboard
import java.awt.datatransfer.DataFlavor
import java.awt.datatransfer.StringSelection
import java.awt.datatransfer.Transferable

public class ClipBoard {
    def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
    
    public void setText(sendStr) {

        def ss = new StringSelection(sendStr)
        clipboard.setContents(ss, ss)
    }

    public getText() {

        Transferable recive = clipboard.getContents(null)

        def recieveStr = null

        try {
            recieveStr = (String) recive.getTransferData(DataFlavor.stringFlavor)
        } catch (Exception e) {
            recieveStr = ""
        }
        return recieveStr
    }
}

MIT/GNU Scheme で書きました。
残念ながら Scheme 処理系に共通したクリップボード読み書きの方法はありません。
1
2
3
4
;; クリップボードへ書き込み (※ str は文字列)
(win32-clipboard-write-text str)
;; クリップボードから読み込み
(win32-clipboard-read-text)

X window上でMacOSXのpbpaste/pbcopyのような動作を実現できるxclipを使ってみました。
今回の例では、中ボタンでのペーストではなくCTR+V系にしました。
コマンド呼び出しのライブラリには、KMRCLを利用しています。
ちなみに、日本語の文字化け問題には対応していません…。

動作:
(pbcopy "foo")
; クリップボードへ

(pbpaste) 
;=> foo
1
2
3
4
5
6
7
8
9
(defun pbcopy (string)
  (declare (string string))
  (kmrcl:run-shell-command
   "echo ~A|xclip -selection clipboard" string))

(defun pbpaste ()
  (values
   (kmrcl:command-output
    "~A" "xclip -selection clipboard -o")))

Windows用です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import Graphics.Win32.GDI.Clip
import Foreign.Ptr
import Foreign.C.String

main = do { openClipboard nullPtr
          ; b <- isClipboardFormatAvailable cF_TEXT
          ; if b
            then do { p <- getClipboardData cF_TEXT
                    ; s <- peekCString (castPtr p)
                    ; putStrLn s
                    }
            else return ()
          ; closeClipboard
          }

生憎、今のところそのような便利ライブラリはなさそうなので生のWin32APIを使わざるを得ません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
module win32.clipboard;
private import std.c.windows.windows;
private import std.contracts, std.conv;

export extern(Windows) {
    HGLOBAL GlobalAlloc(UINT uFlags, DWORD dwBytes);
    HGLOBAL GlobalFree(HGLOBAL hMem);
    LPVOID GlobalLock(HGLOBAL hMem);
    BOOL GlobalUnlock(HGLOBAL hMem);
    enum UINT GMEM_MOVEABLE = 2;
    
    BOOL CloseClipboard();
    BOOL EmptyClipboard();
    UINT EnumClipboardFormats(UINT format);
    HANDLE GetClipboardData(UINT uFormat);
    BOOL OpenClipboard(HWND hWndNewOwner);
    HANDLE SetClipboardData(UINT uFormat, HANDLE hMem);
    
    enum UINT CF_UNICODETEXT = 13;
}

public static class Clipboard {    
    static void setText(string text) {
        enforce(OpenClipboard(null));
        scope(exit) CloseClipboard();
        
        auto wtext = to!(wstring)(text);
        auto data = enforce(GlobalAlloc(GMEM_MOVEABLE, wchar.sizeof * (wtext.length + 1)));
        scope(failure) GlobalFree(data);
        
        {
            auto pdata = cast(wchar*)GlobalLock(data);
            scope(exit) GlobalUnlock(data);
            
            pdata[0..wtext.length] = wtext;
            pdata[wtext.length] = '\0';
        }
        
        enforce(EmptyClipboard());
        enforce(SetClipboardData(CF_UNICODETEXT, data));
    }
    
    static string getText() {
        enforce(OpenClipboard(null));
        scope(exit) CloseClipboard();
        
        auto data = GetClipboardData(CF_UNICODETEXT);
        if(!data) return null;
        
        auto pdata = cast(const(wchar)*)GlobalLock(data);
        auto wtext = pdata[0..strlen(pdata)];
        GlobalUnlock(data);
        
        return to!(string)(wtext);
    }
}

private uint strlen(const(wchar)* s) {
    uint len;
    while(*s++) len++;
    return len;
}

debug(Clipboard) {
import std.stdio;

void main() {
    Clipboard.setText("test テスト");
    writeln(Clipboard.getText);
}

}

すみません。 不注意で関係のないところへぶら下げてしまいました。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
using System;

class Program {
    [STAThread]//クリップボードを利用する場合に必須の属性です。
    static void Main(string[] args) {
        Console.Write("クリップボードのセットする文字列を入力して下さい。:");
        string setStr = Console.ReadLine();//セットする文字列を入力

        System.Windows.Forms.Clipboard.SetText(setStr);//クリップボードにセット

        string getStr = System.Windows.Forms.Clipboard.GetText();//クリップボードから文字列を取得

        Console.WriteLine("クリップボードの内容は\n\n{0}\n\nです。", getStr);
        Console.ReadLine();
    }
}

こちらは、クリップボードへテキストを乗っけるコードです…標準入力とファイルに対応しています…

System.Win32.Mem.globalUnlockってエラーハンドリングにバグがあるようなのでc_GlobalUnlockを使っています…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
module Main where

import Control.Monad
import Foreign.Ptr
import Foreign.Storable
import Graphics.Win32.GDI.Types
import Graphics.Win32.GDI.Clip
import Data.Word
import System
import System.IO
import System.Win32.Types
import System.Win32.Mem

when_ f m = when f (m >> return ())

withGlobalAlloc :: GlobalAllocFlags -> DWORD -> (HGLOBAL -> IO (HGLOBAL, a) ) -> IO a
withGlobalAlloc fl cb f = do
    h <- globalAlloc fl cb
    (h', ret) <- f h
    when_ (h' /= nullHANDLE) $ globalFree h
    return ret
    
withGlobalLock :: HGLOBAL -> (Addr -> IO a) -> IO a
withGlobalLock h f = do
    addr <- globalLock h
    ret <- f addr
    c_GlobalUnlock h
    return ret
    
withClipboard :: HWND -> (IO b) -> IO b
withClipboard hwnd fn = do
    openClipboard hwnd
    ret <- fn
    closeClipboard
    return ret
    
publishToClipboard :: HGLOBAL -> IO (HGLOBAL, ())
publishToClipboard h = withClipboard nullHANDLE (setData h)
    where
        setData h = do
            h' <- catch (c_SetClipboardData cF_TEXT h) (\_ -> return nullHANDLE)
            return (if h' == nullHANDLE then h else nullHANDLE, ())
            
copyStr :: String -> Addr -> IO ()
copyStr str pv = zipWithM_ (pokeByteOff pch) [0..] $ str
    where
        pch = castPtr pv :: Ptr Word8

setTextToClipboard str =  withGlobalAlloc gMEM_MOVEABLE (fromIntegral $ length str) $ _setTextToClipboard str
    where
        _setTextToClipboard :: String -> HGLOBAL -> IO (HGLOBAL, ())
        _setTextToClipboard str h = do
            withGlobalLock h (copyStr str)
            publishToClipboard h

main = do
    args <- liftM (take 1) $ getArgs >>= mapM (\path -> openFile path ReadMode)
    str <- hGetContents $ head $ args ++ [stdin]
    setTextToClipboard str

なでしこでは「コピー」でクリップボードにコピー、「クリップボード取得」でクリップボードを取得できます。
1
2
3
4
# クリップボードにコピー
「Hello World」をコピー
# クリップボードから取得
クリップボード取得して表示

Tkのclipboardコマンドで実現できます。

1
2
3
4
5
package require Tk
clipboard clear
clipboard append "テキスト"; # 転送
clipboard get; # 取得
#=> テキスト

windows用のGUI版の場合です。コンソール版では
「wd」が再定義されていてうまくいきません。

   clipcopy 'test test test'
   clippaste ''
test test test
1
2
3
4
5
6
clipcopy=:3 :0
  wd 'clipcopy "',y,'"'
)
clippaste=:3 :0
  wd 'clippaste'
)

clisp扱いでいいのかな?
1
2
(copy-to-clipboard "hoge") ;転送
(get-clipboard-data) ;取得

wxPythonのClipboard機能を試してみました。
プラットフォームに依存しないのがウリです。
windows xp, Centos5.1 gnome, Mac OS X Leopardで動作を確認しました。

らくちんすぎます。

コメント:
コードは、リンク先にあるwxpython.orgのコードを動くようにしただけです。

解説:
実行するとロードが跳ね上がります。
idle eventをhandleしてそこでTheClipboardの
内容をtext ctrlに貼り付けるだけ。
次のidle eventが処理されるので一瞬に
して消えます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import wx

class MyFrame(wx.Frame):
  def __init__(self, parent, ID, title):
    wx.Frame.__init__(self, parent, ID, title, wx.DefaultPosition, wx.Size(200, 150))
    self.text = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE|wx.HSCROLL)
    wx.EVT_IDLE(self,self.Paste)

  def Paste(self, evt):
    do = wx.TextDataObject()
    success = str(wx.TheClipboard)
    if success:
      self.text.SetValue(do.GetText())
    else:
      self.text.SetValue("There is no data in the clipboard in the required format")

class MyApp(wx.App):
  def OnInit(self):
    frame = MyFrame(None, -1, "Hello from wxPython")
    frame.Show(True)
    self.SetTopWindow(frame)
    return True

app = MyApp(0)
app.MainLoop()

クリップボードの扱いを最初に覚えたDelphiで。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
program doukaku188;

{$APPTYPE CONSOLE}

uses
  ClipBrd;

var
  S: String;
begin
  Clipboard.AsText := 'テキスト'; { copy  }
  S := Clipboard.AsText;          { paste }
  WriteLn(S);
end.

Javaと同じ処理になりますが...。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import    java.awt.BorderLayout
import    java.awt.Container
import    java.awt.Dimension
import    java.awt.FlowLayout
import    java.awt.Toolkit
import    java.awt.datatransfer.Clipboard
import    java.awt.datatransfer.DataFlavor
import    java.awt.datatransfer.StringSelection
import    java.awt.event.ActionEvent
import    java.awt.event.ActionListener
import    javax.swing.JButton
import    javax.swing.JFrame
import    javax.swing.JMenu
import    javax.swing.JMenuBar
import    javax.swing.JMenuItem
import    javax.swing.JPanel
import    javax.swing.JTextArea

class SButton(l:String,c:ActionEvent=>Unit) extends JButton with ActionListener {
    
    setText(l)
    addActionListener(this)
    
    override def actionPerformed(e:ActionEvent) = c(e)
}

class SMenuItem(l:String,c:ActionEvent=>Unit) extends JMenuItem with ActionListener {
    
    setText(l)
    addActionListener(this)
    
    override def actionPerformed(e:ActionEvent) = c(e)
}

class ClipboardFrame(title:String) extends JFrame {
    
    val    mb:JMenuBar = new JMenuBar
    val    fm:JMenu = new JMenu("file")
    val    em:JMenu = new JMenu("edit")
    val    c:Container = getContentPane
    val    t:JTextArea = new JTextArea(20,40);
    val    p:JPanel = new JPanel;
    val    clipboard:Clipboard = Toolkit.getDefaultToolkit.getSystemClipboard;
    
    c.setLayout(new BorderLayout)
    c.add(t,BorderLayout.CENTER)
    
    p.setLayout(new FlowLayout)
    p.add(new SButton("copy",copy))
    p.add(new SButton("paste",paste))
    c.add(p,BorderLayout.SOUTH)
    
    fm.add(new SMenuItem("quit",quit))
    mb.add(fm)
    em.add(new SMenuItem("copy",copy))
    em.add(new SMenuItem("paste",paste))
    mb.add(em)
    setJMenuBar(mb)
    
    setResizable(false)
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
    setTitle(title)
    
    pack
    
    def this() = this("clipboad")
    
    def quit(e:ActionEvent):Unit = {
        System.exit(0)
    }
    
    def copy(e:ActionEvent):Unit = {
        val    s:StringSelection = new StringSelection(t.getSelectedText);
        clipboard.setContents(s,s);
    }
    
    def paste(e:ActionEvent):Unit = {
        t.replaceSelection(clipboard.getData(DataFlavor.stringFlavor).asInstanceOf[String])
    }
}

object Main extends Application {
    (new ClipboardFrame).setVisible(true)
}

Mac OS Xには pbcopy, pbpasteの名前でシェルから クリップポードを操作するコマンドが 存在します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/usr/bin/env ruby -wKU

if (ARGV.length != 1)
  print "Usage: handle_pb [-show] text\n"
end

if ARGV.include?('-show') 
  
  print %x{ pbpaste }
else
  ENV['PB_COPY_TEXT'] = ARGV[0]
  
  # デフォルトではpbcopyは日本語の場合Shift-JISしか扱えない
  %x{ echo $PB_COPY_TEXT | nkf -s | pbcopy } 
end

Mac OS Xには pbcopy, pbpasteの名前でシェルからクリップポードを操作するコマンドが存在します。(デフォルトではpbcopy,pbpasteは日本語の場合Shift-JISしか扱えないそうです)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/usr/bin/env ruby -wKU

if (ARGV.length != 1)
  print "Usage: handle_pb [-show] text\n"
end

if ARGV[0] == '-show'
  
  print %x{ pbpaste | nkf -w }
else
  ENV['PB_COPY_TEXT'] = ARGV[0]
  
  # デフォルトではpbcopyは日本語の場合Shift-JISしか扱えない
  %x{ echo $PB_COPY_TEXT | nkf -s | pbcopy } 
end

#!/usr/bin/ruby -wKSでnkfは取り除けました。 勘違いです。すいません。


#!/usr/bin/ruby -wKSでnkfは取り除けました。 勘違いです。すいません。


HSP2.xxでは動かない…かもしれません。
1
2
3
4
5
#include "hspext.as"
clipset "テスト"
sdim clip,3
clipget clip
mes clip

Index

Feed

Other

Link

Pathtraq

loading...