Skip to content

Commit d7e4fd3

Browse files
committed
Merge pull request pandas-dev#5013 from alefnula/qtpandas
ENH: PySide support for qtpandas.
2 parents 702e3bb + 048fb0d commit d7e4fd3

File tree

4 files changed

+104
-11
lines changed

4 files changed

+104
-11
lines changed

doc/source/release.rst

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Experimental Features
7777
(:issue:`4897`).
7878
- Add msgpack support via ``pd.read_msgpack()`` and ``pd.to_msgpack()`` / ``df.to_msgpack()`` for serialization
7979
of arbitrary pandas (and python objects) in a lightweight portable binary format (:issue:`686`)
80+
- Added PySide support for the qtpandas DataFrameModel and DataFrameWidget.
8081

8182
Improvements to existing features
8283
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

doc/source/v0.13.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,8 @@ Experimental
604604

605605
os.remove('foo.msg')
606606

607+
- Added PySide support for the qtpandas DataFrameModel and DataFrameWidget.
608+
607609
.. _whatsnew_0130.refactoring:
608610

609611
Internal Refactoring

doc/source/visualization.rst

+76
Original file line numberDiff line numberDiff line change
@@ -594,3 +594,79 @@ Andrews curves charts:
594594
595595
@savefig andrews_curve_winter.png
596596
andrews_curves(data, 'Name', colormap='winter')
597+
598+
599+
****************************************
600+
Visualizing your data in Qt applications
601+
****************************************
602+
603+
There is an experimental support for visualizing DataFrames in PyQt4 and PySide
604+
applications. At the moment you can display and edit the values of the cells
605+
in the DataFrame. Qt will take care of displaying just the portion of the
606+
DataFrame that is currently visible and the edits will be immediately saved to
607+
the underlying DataFrame
608+
609+
To demonstrate this we will create a simple PySide application that will switch
610+
between two editable DataFrames. For this will use the ``DataFrameModel`` class
611+
that handles the access to the DataFrame, and the ``DataFrameWidget``, which is
612+
just a thin layer around the ``QTableView``.
613+
614+
.. code-block:: python
615+
616+
import numpy as np
617+
import pandas as pd
618+
from pandas.sandbox.qtpandas import DataFrameModel, DataFrameWidget
619+
from PySide import QtGui, QtCore
620+
621+
# Or if you use PyQt4:
622+
# from PyQt4 import QtGui, QtCore
623+
624+
class MainWidget(QtGui.QWidget):
625+
def __init__(self, parent=None):
626+
super(MainWidget, self).__init__(parent)
627+
628+
# Create two DataFrames
629+
self.df1 = pd.DataFrame(np.arange(9).reshape(3, 3),
630+
columns=['foo', 'bar', 'baz'])
631+
self.df2 = pd.DataFrame({
632+
'int': [1, 2, 3],
633+
'float': [1.5, 2.5, 3.5],
634+
'string': ['a', 'b', 'c'],
635+
'nan': [np.nan, np.nan, np.nan]
636+
}, index=['AAA', 'BBB', 'CCC'],
637+
columns=['int', 'float', 'string', 'nan'])
638+
639+
# Create the widget and set the first DataFrame
640+
self.widget = DataFrameWidget(self.df1)
641+
642+
# Create the buttons for changing DataFrames
643+
self.button_first = QtGui.QPushButton('First')
644+
self.button_first.clicked.connect(self.on_first_click)
645+
self.button_second = QtGui.QPushButton('Second')
646+
self.button_second.clicked.connect(self.on_second_click)
647+
648+
# Set the layout
649+
vbox = QtGui.QVBoxLayout()
650+
vbox.addWidget(self.widget)
651+
hbox = QtGui.QHBoxLayout()
652+
hbox.addWidget(self.button_first)
653+
hbox.addWidget(self.button_second)
654+
vbox.addLayout(hbox)
655+
self.setLayout(vbox)
656+
657+
def on_first_click(self):
658+
'''Sets the first DataFrame'''
659+
self.widget.setDataFrame(self.df1)
660+
661+
def on_second_click(self):
662+
'''Sets the second DataFrame'''
663+
self.widget.setDataFrame(self.df2)
664+
665+
if __name__ == '__main__':
666+
import sys
667+
668+
# Initialize the application
669+
app = QtGui.QApplication(sys.argv)
670+
mw = MainWidget()
671+
mw.show()
672+
app.exec_()

pandas/sandbox/qtpandas.py

+25-11
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
44
@author: Jev Kuznetsov
55
'''
6-
from PyQt4.QtCore import (
7-
QAbstractTableModel, Qt, QVariant, QModelIndex, SIGNAL)
8-
from PyQt4.QtGui import (
9-
QApplication, QDialog, QVBoxLayout, QTableView, QWidget)
6+
try:
7+
from PyQt4.QtCore import QAbstractTableModel, Qt, QVariant, QModelIndex
8+
from PyQt4.QtGui import (
9+
QApplication, QDialog, QVBoxLayout, QTableView, QWidget)
10+
except ImportError:
11+
from PySide.QtCore import QAbstractTableModel, Qt, QModelIndex
12+
from PySide.QtGui import (
13+
QApplication, QDialog, QVBoxLayout, QTableView, QWidget)
14+
QVariant = lambda value=None: value
1015

1116
from pandas import DataFrame, Index
1217

@@ -57,9 +62,17 @@ def flags(self, index):
5762
return flags
5863

5964
def setData(self, index, value, role):
60-
self.df.set_value(self.df.index[index.row()],
61-
self.df.columns[index.column()],
62-
value.toPyObject())
65+
row = self.df.index[index.row()]
66+
col = self.df.columns[index.column()]
67+
if hasattr(value, 'toPyObject'):
68+
# PyQt4 gets a QVariant
69+
value = value.toPyObject()
70+
else:
71+
# PySide gets an unicode
72+
dtype = self.df[col].dtype
73+
if dtype != object:
74+
value = None if value == '' else dtype.type(value)
75+
self.df.set_value(row, col, value)
6376
return True
6477

6578
def rowCount(self, index=QModelIndex()):
@@ -75,17 +88,18 @@ def __init__(self, dataFrame, parent=None):
7588
super(DataFrameWidget, self).__init__(parent)
7689

7790
self.dataModel = DataFrameModel()
78-
self.dataModel.setDataFrame(dataFrame)
79-
8091
self.dataTable = QTableView()
8192
self.dataTable.setModel(self.dataModel)
82-
self.dataModel.signalUpdate()
8393

8494
layout = QVBoxLayout()
8595
layout.addWidget(self.dataTable)
8696
self.setLayout(layout)
97+
# Set DataFrame
98+
self.setDataFrame(dataFrame)
8799

88-
def resizeColumnsToContents(self):
100+
def setDataFrame(self, dataFrame):
101+
self.dataModel.setDataFrame(dataFrame)
102+
self.dataModel.signalUpdate()
89103
self.dataTable.resizeColumnsToContents()
90104

91105
#-----------------stand alone test code

0 commit comments

Comments
 (0)