Skip to content

Commit 863478c

Browse files
committed
Merge pull request #3848 from hayd/GH3837_use_paperclip
ENH use pyperclip for read and to_clipboard
2 parents 89e62c0 + a469d33 commit 863478c

File tree

5 files changed

+202
-119
lines changed

5 files changed

+202
-119
lines changed

LICENSES/OTHER

+30
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,33 @@ distributed under the License is distributed on an "AS IS" BASIS,
4848
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4949
See the License for the specific language governing permissions and
5050
limitations under the License.
51+
52+
Pyperclip v1.3 license
53+
----------------------
54+
55+
Copyright (c) 2010, Albert Sweigart
56+
All rights reserved.
57+
58+
BSD-style license:
59+
60+
Redistribution and use in source and binary forms, with or without
61+
modification, are permitted provided that the following conditions are met:
62+
* Redistributions of source code must retain the above copyright
63+
notice, this list of conditions and the following disclaimer.
64+
* Redistributions in binary form must reproduce the above copyright
65+
notice, this list of conditions and the following disclaimer in the
66+
documentation and/or other materials provided with the distribution.
67+
* Neither the name of the pyperclip nor the
68+
names of its contributors may be used to endorse or promote products
69+
derived from this software without specific prior written permission.
70+
71+
THIS SOFTWARE IS PROVIDED BY Albert Sweigart "AS IS" AND ANY
72+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
73+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
74+
DISCLAIMED. IN NO EVENT SHALL Albert Sweigart BE LIABLE FOR ANY
75+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
76+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
77+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
78+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
79+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
80+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

RELEASE.rst

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ pandas 0.11.1
7373
- ``melt`` now accepts the optional parameters ``var_name`` and ``value_name``
7474
to specify custom column names of the returned DataFrame (GH3649_),
7575
thanks @hoechenberger
76+
- clipboard functions use pyperclip (no dependencies on Windows, alternative
77+
dependencies offered for Linux) (GH3837_).
7678
- Plotting functions now raise a ``TypeError`` before trying to plot anything
7779
if the associated objects have have a dtype of ``object`` (GH1818_,
7880
GH3572_). This happens before any drawing takes place which elimnates any

pandas/core/generic.py

+10
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,16 @@ def to_hdf(self, path_or_buf, key, **kwargs):
492492
return pytables.to_hdf(path_or_buf, key, self, **kwargs)
493493

494494
def to_clipboard(self):
495+
"""
496+
Attempt to write text representation of object to the system clipboard
497+
498+
Notes
499+
-----
500+
Requirements for your platform
501+
- Linux: xclip, or xsel (with gtk or PyQt4 modules)
502+
- Windows:
503+
- OS X:
504+
"""
495505
from pandas.io import clipboard
496506
clipboard.to_clipboard(self)
497507

pandas/io/clipboard.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ def to_clipboard(obj): # pragma: no cover
2323
Notes
2424
-----
2525
Requirements for your platform
26-
- Linux: xsel command line tool
27-
- Windows: Python win32 extensions
26+
- Linux: xclip, or xsel (with gtk or PyQt4 modules)
27+
- Windows:
2828
- OS X:
2929
"""
3030
from pandas.util.clipboard import clipboard_set

pandas/util/clipboard.py

+158-117
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,160 @@
1-
"""
2-
Taken from the IPython project http://ipython.org
3-
4-
Used under the terms of the BSD license
5-
"""
6-
7-
import subprocess
8-
import sys
9-
10-
11-
def clipboard_get():
12-
""" Get text from the clipboard.
13-
"""
14-
if sys.platform == 'win32':
15-
try:
16-
return win32_clipboard_get()
17-
except Exception:
18-
pass
19-
elif sys.platform == 'darwin':
20-
try:
21-
return osx_clipboard_get()
22-
except Exception:
23-
pass
24-
return tkinter_clipboard_get()
25-
26-
27-
def clipboard_set(text):
28-
""" Get text from the clipboard.
29-
"""
30-
if sys.platform == 'win32':
31-
try:
32-
return win32_clipboard_set(text)
33-
except Exception:
34-
raise
35-
elif sys.platform == 'darwin':
36-
try:
37-
return osx_clipboard_set(text)
38-
except Exception:
39-
pass
40-
xsel_clipboard_set(text)
41-
42-
43-
def win32_clipboard_get():
44-
""" Get the current clipboard's text on Windows.
45-
46-
Requires Mark Hammond's pywin32 extensions.
47-
"""
48-
try:
49-
import win32clipboard
50-
except ImportError:
51-
message = ("Getting text from the clipboard requires the pywin32 "
52-
"extensions: http://sourceforge.net/projects/pywin32/")
53-
raise Exception(message)
54-
win32clipboard.OpenClipboard()
55-
text = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT)
56-
# FIXME: convert \r\n to \n?
57-
win32clipboard.CloseClipboard()
58-
return text
59-
60-
61-
def osx_clipboard_get():
62-
""" Get the clipboard's text on OS X.
63-
"""
64-
p = subprocess.Popen(['pbpaste', '-Prefer', 'ascii'],
65-
stdout=subprocess.PIPE)
66-
text, stderr = p.communicate()
67-
# Text comes in with old Mac \r line endings. Change them to \n.
68-
text = text.replace('\r', '\n')
69-
return text
70-
71-
72-
def tkinter_clipboard_get():
73-
""" Get the clipboard's text using Tkinter.
74-
75-
This is the default on systems that are not Windows or OS X. It may
76-
interfere with other UI toolkits and should be replaced with an
77-
implementation that uses that toolkit.
78-
"""
1+
# Pyperclip v1.3
2+
# A cross-platform clipboard module for Python. (only handles plain text for now)
3+
# By Al Sweigart [email protected]
4+
5+
# Usage:
6+
# import pyperclip
7+
# pyperclip.copy('The text to be copied to the clipboard.')
8+
# spam = pyperclip.paste()
9+
10+
# On Mac, this module makes use of the pbcopy and pbpaste commands, which should come with the os.
11+
# On Linux, this module makes use of the xclip command, which should come with the os. Otherwise run "sudo apt-get install xclip"
12+
13+
14+
# Copyright (c) 2010, Albert Sweigart
15+
# All rights reserved.
16+
#
17+
# BSD-style license:
18+
#
19+
# Redistribution and use in source and binary forms, with or without
20+
# modification, are permitted provided that the following conditions are met:
21+
# * Redistributions of source code must retain the above copyright
22+
# notice, this list of conditions and the following disclaimer.
23+
# * Redistributions in binary form must reproduce the above copyright
24+
# notice, this list of conditions and the following disclaimer in the
25+
# documentation and/or other materials provided with the distribution.
26+
# * Neither the name of the pyperclip nor the
27+
# names of its contributors may be used to endorse or promote products
28+
# derived from this software without specific prior written permission.
29+
#
30+
# THIS SOFTWARE IS PROVIDED BY Albert Sweigart "AS IS" AND ANY
31+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33+
# DISCLAIMED. IN NO EVENT SHALL Albert Sweigart BE LIABLE FOR ANY
34+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
37+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40+
41+
# Change Log:
42+
# 1.2 Use the platform module to help determine OS.
43+
# 1.3 Changed ctypes.windll.user32.OpenClipboard(None) to ctypes.windll.user32.OpenClipboard(0), after some people ran into some TypeError
44+
45+
import platform, os
46+
47+
def winGetClipboard():
48+
ctypes.windll.user32.OpenClipboard(0)
49+
pcontents = ctypes.windll.user32.GetClipboardData(1) # 1 is CF_TEXT
50+
data = ctypes.c_char_p(pcontents).value
51+
#ctypes.windll.kernel32.GlobalUnlock(pcontents)
52+
ctypes.windll.user32.CloseClipboard()
53+
return data
54+
55+
def winSetClipboard(text):
56+
GMEM_DDESHARE = 0x2000
57+
ctypes.windll.user32.OpenClipboard(0)
58+
ctypes.windll.user32.EmptyClipboard()
7959
try:
80-
import Tkinter
81-
except ImportError:
82-
message = ("Getting text from the clipboard on this platform "
83-
"requires Tkinter.")
84-
raise Exception(message)
85-
root = Tkinter.Tk()
86-
root.withdraw()
87-
text = root.clipboard_get()
88-
root.destroy()
89-
return text
90-
91-
92-
def win32_clipboard_set(text):
93-
# idiosyncratic win32 import issues
94-
import pywintypes as _
95-
import win32clipboard
96-
win32clipboard.OpenClipboard()
60+
# works on Python 2 (bytes() only takes one argument)
61+
hCd = ctypes.windll.kernel32.GlobalAlloc(GMEM_DDESHARE, len(bytes(text))+1)
62+
except TypeError:
63+
# works on Python 3 (bytes() requires an encoding)
64+
hCd = ctypes.windll.kernel32.GlobalAlloc(GMEM_DDESHARE, len(bytes(text, 'ascii'))+1)
65+
pchData = ctypes.windll.kernel32.GlobalLock(hCd)
9766
try:
98-
win32clipboard.EmptyClipboard()
99-
win32clipboard.SetClipboardText(_fix_line_endings(text))
100-
finally:
101-
win32clipboard.CloseClipboard()
102-
103-
104-
def _fix_line_endings(text):
105-
return '\r\n'.join(text.splitlines())
106-
107-
108-
def osx_clipboard_set(text):
109-
""" Get the clipboard's text on OS X.
110-
"""
111-
p = subprocess.Popen(['pbcopy', '-Prefer', 'ascii'],
112-
stdin=subprocess.PIPE)
113-
p.communicate(input=text)
114-
115-
116-
def xsel_clipboard_set(text):
117-
from subprocess import Popen, PIPE
118-
p = Popen(['xsel', '-bi'], stdin=PIPE)
119-
p.communicate(input=text)
67+
# works on Python 2 (bytes() only takes one argument)
68+
ctypes.cdll.msvcrt.strcpy(ctypes.c_char_p(pchData), bytes(text))
69+
except TypeError:
70+
# works on Python 3 (bytes() requires an encoding)
71+
ctypes.cdll.msvcrt.strcpy(ctypes.c_char_p(pchData), bytes(text, 'ascii'))
72+
ctypes.windll.kernel32.GlobalUnlock(hCd)
73+
ctypes.windll.user32.SetClipboardData(1,hCd)
74+
ctypes.windll.user32.CloseClipboard()
75+
76+
def macSetClipboard(text):
77+
outf = os.popen('pbcopy', 'w')
78+
outf.write(text)
79+
outf.close()
80+
81+
def macGetClipboard():
82+
outf = os.popen('pbpaste', 'r')
83+
content = outf.read()
84+
outf.close()
85+
return content
86+
87+
def gtkGetClipboard():
88+
return gtk.Clipboard().wait_for_text()
89+
90+
def gtkSetClipboard(text):
91+
cb = gtk.Clipboard()
92+
cb.set_text(text)
93+
cb.store()
94+
95+
def qtGetClipboard():
96+
return str(cb.text())
97+
98+
def qtSetClipboard(text):
99+
cb.setText(text)
100+
101+
def xclipSetClipboard(text):
102+
outf = os.popen('xclip -selection c', 'w')
103+
outf.write(text)
104+
outf.close()
105+
106+
def xclipGetClipboard():
107+
outf = os.popen('xclip -selection c -o', 'r')
108+
content = outf.read()
109+
outf.close()
110+
return content
111+
112+
def xselSetClipboard(text):
113+
outf = os.popen('xsel -i', 'w')
114+
outf.write(text)
115+
outf.close()
116+
117+
def xselGetClipboard():
118+
outf = os.popen('xsel -o', 'r')
119+
content = outf.read()
120+
outf.close()
121+
return content
122+
123+
124+
if os.name == 'nt' or platform.system() == 'Windows':
125+
import ctypes
126+
getcb = winGetClipboard
127+
setcb = winSetClipboard
128+
elif os.name == 'mac' or platform.system() == 'Darwin':
129+
getcb = macGetClipboard
130+
setcb = macSetClipboard
131+
elif os.name == 'posix' or platform.system() == 'Linux':
132+
xclipExists = os.system('which xclip') == 0
133+
if xclipExists:
134+
getcb = xclipGetClipboard
135+
setcb = xclipSetClipboard
136+
else:
137+
xselExists = os.system('which xsel') == 0
138+
if xselExists:
139+
getcb = xselGetClipboard
140+
setcb = xselSetClipboard
141+
try:
142+
import gtk
143+
getcb = gtkGetClipboard
144+
setcb = gtkSetClipboard
145+
except:
146+
try:
147+
import PyQt4.QtCore
148+
import PyQt4.QtGui
149+
app = QApplication([])
150+
cb = PyQt4.QtGui.QApplication.clipboard()
151+
getcb = qtGetClipboard
152+
setcb = qtSetClipboard
153+
except:
154+
raise Exception('Pyperclip requires the gtk or PyQt4 module installed, or the xclip command.')
155+
copy = setcb
156+
paste = getcb
157+
158+
## pandas aliases
159+
clipboard_get = paste
160+
clipboard_set = copy

0 commit comments

Comments
 (0)