diff --git a/.travis.yml b/.travis.yml index eecb4a1695fd6..7945ac945076f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,20 +3,20 @@ language: python python: - 2.6 -env: - global: - - NOSE_ARGS="not slow" UPLOAD=true - matrix: include: + - python: 2.6 + env: NOSE_ARGS="not slow" CLIPBOARD=xclip - python: 2.7 - env: NOSE_ARGS="slow and not network" LOCALE_OVERRIDE="zh_CN.GB18030" FULL_DEPS=true JOB_TAG=_LOCALE + env: NOSE_ARGS="slow and not network" LOCALE_OVERRIDE="zh_CN.GB18030" FULL_DEPS=true JOB_TAG=_LOCALE - python: 2.7 - env: NOSE_ARGS="not slow" FULL_DEPS=true + env: NOSE_ARGS="not slow" FULL_DEPS=true GUI=gtk2 - python: 3.2 - env: NOSE_ARGS="not slow" FULL_DEPS=true + env: NOSE_ARGS="not slow" FULL_DEPS=true GUI=qt4 - python: 3.3 - env: NOSE_ARGS="not slow" FULL_DEPS=true + env: NOSE_ARGS="not slow" FULL_DEPS=true CLIPBOARD=xsel + exclude: + - python: 2.6 # allow importing from site-packages, # so apt-get python-x works for system pythons @@ -27,7 +27,7 @@ virtualenv: before_install: - echo "Waldo1" - echo $VIRTUAL_ENV - - df + - df -h - date # - export PIP_ARGS=-q # comment this this to debug travis install issues # - export APT_ARGS=-qq # comment this to debug travis install issues @@ -35,6 +35,8 @@ before_install: - export ZIP_FLAGS=-q # comment this to debug travis install issues - ci/before_install.sh - python -V + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start install: - echo "Waldo2" diff --git a/ci/install.sh b/ci/install.sh index 1b7ec3f647763..86226c530541c 100755 --- a/ci/install.sh +++ b/ci/install.sh @@ -41,6 +41,22 @@ fi time pip install $PIP_ARGS -r ci/requirements-${TRAVIS_PYTHON_VERSION}${JOB_TAG}.txt time sudo apt-get install libatlas-base-dev gfortran + +# install gui for clipboard testing +if [ -n "$GUI" ]; then + echo "Using GUI clipboard: $GUI" + [ -n "$pv" ] && py="py" + time sudo apt-get $APT_ARGS install python${pv}-${py}${GUI} +fi + + +# install a clipboard +if [ -n "$CLIPBOARD" ]; then + echo "Using clipboard: $CLIPBOARD" + time sudo apt-get $APT_ARGS install $CLIPBOARD +fi + + # Optional Deps if [ x"$FULL_DEPS" == x"true" ]; then echo "Installing FULL_DEPS" diff --git a/doc/source/install.rst b/doc/source/install.rst index 9dc8064da45e3..a7feea4bbf6ac 100644 --- a/doc/source/install.rst +++ b/doc/source/install.rst @@ -103,6 +103,15 @@ Optional Dependencies * Needed for Excel I/O * `boto `__: necessary for Amazon S3 access. + * One of `PyQt4 + `__, `PySide + `__, `pygtk + `__, `xsel + `__, or `xclip + `__: necessary to use + :func:`~pandas.io.parsers.read_clipboard`. Most package managers on Linux + distributions will have xclip and/or xsel immediately available for + installation. * One of the following combinations of libraries is needed to use the top-level :func:`~pandas.io.html.read_html` function: diff --git a/doc/source/release.rst b/doc/source/release.rst index 7b174611652de..478e7375b0b30 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -32,6 +32,7 @@ pandas 0.13 **New features** - Added ``isin`` method to DataFrame (:issue:`4211`) + - Clipboard functionality now works with PySide (:issue:`4282`) **Improvements to existing features** @@ -39,6 +40,7 @@ pandas 0.13 ``ValueError`` (:issue:`4303`, :issue:`4305`) - ``read_excel`` now supports an integer in its ``sheetname`` argument giving the index of the sheet to read in (:issue:`4301`). + - Added a test for ``read_clipboard()`` and ``to_clipboard()`` (:issue:`4282`) **API Changes** diff --git a/doc/source/v0.13.0.txt b/doc/source/v0.13.0.txt index e51206b3c2fe4..24d1b30d470ee 100644 --- a/doc/source/v0.13.0.txt +++ b/doc/source/v0.13.0.txt @@ -17,6 +17,8 @@ Enhancements - ``read_html`` now raises a ``URLError`` instead of catching and raising a ``ValueError`` (:issue:`4303`, :issue:`4305`) + - Added a test for ``read_clipboard()`` and ``to_clipboard()`` (:issue:`4282`) + - Clipboard functionality now works with PySide (:issue:`4282`) Bug Fixes ~~~~~~~~~ diff --git a/pandas/io/clipboard.py b/pandas/io/clipboard.py index fea7a51d344f9..08837474c11b4 100644 --- a/pandas/io/clipboard.py +++ b/pandas/io/clipboard.py @@ -31,5 +31,3 @@ def to_clipboard(obj): # pragma: no cover """ from pandas.util.clipboard import clipboard_set clipboard_set(str(obj)) - - diff --git a/pandas/io/tests/test_clipboard.py b/pandas/io/tests/test_clipboard.py new file mode 100644 index 0000000000000..9eadd16c207a9 --- /dev/null +++ b/pandas/io/tests/test_clipboard.py @@ -0,0 +1,50 @@ +import unittest + +import numpy as np +from numpy.random import randint + +import nose + +from pandas import DataFrame +from pandas import read_clipboard +from pandas.util import testing as tm +from pandas.util.testing import makeCustomDataframe as mkdf + + +try: + import pandas.util.clipboard +except OSError: + raise nose.SkipTest("no clipboard found") + + +class TestClipboard(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.data = {} + cls.data['string'] = mkdf(5, 3, c_idx_type='s', r_idx_type='i', + c_idx_names=[None], r_idx_names=[None]) + cls.data['int'] = mkdf(5, 3, data_gen_f=lambda *args: randint(2), + c_idx_type='s', r_idx_type='i', + c_idx_names=[None], r_idx_names=[None]) + cls.data['float'] = mkdf(5, 3, + data_gen_f=lambda r, c: float(r) + 0.01, + c_idx_type='s', r_idx_type='i', + c_idx_names=[None], r_idx_names=[None]) + cls.data['mixed'] = DataFrame({'a': np.arange(1.0, 6.0) + 0.01, + 'b': np.arange(1, 6), + 'c': list('abcde')}) + cls.data_types = cls.data.keys() + + @classmethod + def tearDownClass(cls): + del cls.data_types, cls.data + + def check_round_trip_frame(self, data_type): + data = self.data[data_type] + data.to_clipboard() + result = read_clipboard() + tm.assert_frame_equal(data, result) + + def test_round_trip_frame(self): + for dt in self.data_types: + self.check_round_trip_frame(dt) diff --git a/pandas/util/clipboard.py b/pandas/util/clipboard.py index 9f3ee0638352f..3008a5d606c90 100644 --- a/pandas/util/clipboard.py +++ b/pandas/util/clipboard.py @@ -44,6 +44,10 @@ import platform, os +class NoClipboardProgramError(OSError): + pass + + def winGetClipboard(): ctypes.windll.user32.OpenClipboard(0) pcontents = ctypes.windll.user32.GetClipboardData(1) # 1 is CF_TEXT @@ -138,23 +142,35 @@ def xselGetClipboard(): if xselExists: getcb = xselGetClipboard setcb = xselSetClipboard - try: - import gtk - getcb = gtkGetClipboard - setcb = gtkSetClipboard - except: + else: try: - import PyQt4.QtCore - import PyQt4.QtGui - app = QApplication([]) - cb = PyQt4.QtGui.QApplication.clipboard() + import gtk + except ImportError: + try: + import PyQt4 as qt4 + import PyQt4.QtCore + import PyQt4.QtGui + except ImportError: + try: + import PySide as qt4 + import PySide.QtCore + import PySide.QtGui + except ImportError: + raise NoClipboardProgramError('Pyperclip requires the' + ' gtk, PyQt4, or PySide' + ' module installed, or ' + 'either the xclip or ' + 'xsel command.') + app = qt4.QtGui.QApplication([]) + cb = qt4.QtGui.QApplication.clipboard() getcb = qtGetClipboard setcb = qtSetClipboard - except: - raise Exception('Pyperclip requires the gtk or PyQt4 module installed, or the xclip command.') + else: + getcb = gtkGetClipboard + setcb = gtkSetClipboard copy = setcb paste = getcb ## pandas aliases clipboard_get = paste -clipboard_set = copy \ No newline at end of file +clipboard_set = copy