Skip to content

Commit 340062e

Browse files
committed
Merge branch 'develop' into feature/drag_drop
2 parents 610b4a9 + 86d834c commit 340062e

File tree

8 files changed

+130243
-2
lines changed

8 files changed

+130243
-2
lines changed

examples/ThreadedExample.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import sys
2+
import time
3+
from pandasqt.compat import QtCore, QtGui, Qt, Slot, Signal
4+
import imgs
5+
6+
from pandasqt.views.OverlayProgressView import OverlayProgressWidget
7+
8+
from pandasqt.models.ProgressThread import ProgressWorker, createThread
9+
10+
11+
class ExampleWorker(ProgressWorker):
12+
def __init__(self, name, ticks):
13+
super(ExampleWorker, self).__init__(name)
14+
self.ticks = ticks
15+
16+
def run(self):
17+
count = 0
18+
while count < 100:
19+
time.sleep(1)
20+
count += self.ticks
21+
if count > 100:
22+
count = 100
23+
self.progressChanged.emit(count)
24+
break
25+
26+
self.progressChanged.emit(count)
27+
28+
29+
class Example(QtGui.QWidget):
30+
def __init__(self, parent=None):
31+
super(Example, self).__init__(parent)
32+
33+
self.initUI()
34+
35+
36+
def initUI(self):
37+
self.setGeometry(100, 100, 300, 300)
38+
39+
self.vlayout = QtGui.QVBoxLayout(self)
40+
41+
self.imgContainer = QtGui.QLabel(self)
42+
img = QtGui.QPixmap(':/europe.png')
43+
self.imgContainer.setPixmap(img)
44+
size = img.size()
45+
self.imgContainer.resize(size.width(), self.height())
46+
47+
self.vlayout.addWidget(self.imgContainer)
48+
self.vlayout.addWidget(QtGui.QLabel('FOOO',self))
49+
50+
threads = []
51+
52+
worker1 = ExampleWorker('foo', 10)
53+
worker2 = ExampleWorker('bar', 13)
54+
worker3 = ExampleWorker('spam', 25)
55+
56+
workers = [worker1, worker2, worker3]
57+
for worker in workers:
58+
thread = createThread(self, worker)
59+
threads.append(thread)
60+
worker.finished.connect(self.debugPrint)
61+
62+
self.pgFrame = OverlayProgressWidget(self.imgContainer, workers=workers)
63+
64+
for t in threads:
65+
t.start()
66+
67+
@Slot()
68+
def debugPrint(self):
69+
print 'THREAD %s ended' % (self.sender().name, )
70+
71+
72+
if __name__ == '__main__':
73+
74+
app = QtGui.QApplication(sys.argv)
75+
widget = Example()
76+
widget.show()
77+
app.exec_()

examples/europe.png

1.98 MB
Loading

examples/images.qrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<RCC>
2+
<qresource prefix="/">
3+
<file>europe.png</file>
4+
</qresource>
5+
</RCC>

examples/imgs.py

Lines changed: 129980 additions & 0 deletions
Large diffs are not rendered by default.

pandasqt/models/DataFrameModel.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class DataFrameModel(QtCore.QAbstractTableModel):
3535
changingDtypeFailed (Signal(columnName, index, dtype)):
3636
passed from related ColumnDtypeModel.
3737
emitted after a column has changed it's data type.
38-
dataChanged (Signal):
38+
dataChanged (Signal):
3939
Emitted, if data has changed, e.x. finished loading, new columns added or removed.
4040
It's not the same as layoutChanged.
4141
Usefull to reset delegates in the view.
@@ -63,6 +63,7 @@ class DataFrameModel(QtCore.QAbstractTableModel):
6363
dtypeChanged = Signal(int, object)
6464
changingDtypeFailed = Signal(object, QtCore.QModelIndex, object)
6565
dataChanged = Signal()
66+
dataFrameChanged = Signal()
6667

6768
def __init__(self, dataFrame=None, copyDataFrame=False):
6869
"""the __init__ method.
@@ -127,6 +128,7 @@ def setDataFrame(self, dataFrame, copyDataFrame=False):
127128
# )
128129
self.layoutChanged.emit()
129130
self.dataChanged.emit()
131+
self.dataFrameChanged.emit()
130132

131133
@Slot(int, object)
132134
def propagateDtypeChanges(self, column, dtype):
@@ -443,13 +445,16 @@ def setFilter(self, search):
443445

444446
self._search.setDataFrame(self._dataFrame)
445447
searchIndex, valid = self._search.search()
448+
446449
if valid:
447450
self._dataFrame = self._dataFrame[searchIndex]
448451
self.layoutChanged.emit()
449452
else:
450453
self.clearFilter()
451454
self.layoutChanged.emit()
452455

456+
self.dataFrameChanged.emit()
457+
453458
def clearFilter(self):
454459
"""clear all filters.
455460

pandasqt/models/ProgressThread.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from pandasqt.compat import QtCore, QtGui, Qt, Signal, Slot
2+
3+
class ProgressWorker(QtCore.QObject):
4+
5+
progressChanged = Signal(int) # set value of OverlayProgressView
6+
finished = Signal()
7+
8+
def __init__(self, name):
9+
"""Worker object that will be passed to the thread.
10+
11+
Args:
12+
name (str): name shown in progress ui.
13+
14+
"""
15+
super(ProgressWorker, self).__init__()
16+
self.name = name
17+
18+
@Slot()
19+
def doWork(self):
20+
"""start the thread"""
21+
self.run()
22+
# emit the result of the operation?
23+
self.finished.emit()
24+
25+
def run(self):
26+
"""Implement your job here. This is what the thread will do.
27+
28+
"""
29+
raise NotImplemented
30+
31+
32+
33+
def createThread(parent, worker, deleteWorkerLater=False):
34+
"""Create a new thread for given worker.
35+
36+
Args:
37+
parent (QObject): parent of thread and worker.
38+
worker (ProgressWorker): worker to use in thread.
39+
deleteWorkerLater (bool, optional): delete the worker if thread finishes.
40+
41+
Returns:
42+
QThread
43+
44+
"""
45+
thread = QtCore.QThread(parent)
46+
thread.started.connect(worker.doWork)
47+
worker.finished.connect(thread.quit)
48+
if deleteWorkerLater:
49+
thread.finished.connect(worker.deleteLater)
50+
51+
worker.moveToThread(thread)
52+
worker.setParent(parent)
53+
return thread

pandasqt/views/OverlayProgressView.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
from pandasqt.compat import QtCore, QtGui, Qt, Signal, Slot
2+
3+
class OverlayProgressWidget(QtGui.QFrame):
4+
def __init__(self, parent, workers=[], debug=True, margin=0):
5+
super(OverlayProgressWidget, self).__init__(parent)
6+
self._debug = debug
7+
self._workers = workers
8+
self._detailProgressBars = []
9+
self._addedBars = 0
10+
self._minHeight = 50
11+
self._width = parent.width() * 0.38
12+
self._margin = margin
13+
self._totalProgress = 0
14+
15+
self.initUi()
16+
17+
for worker in workers:
18+
self._addProgressBar(worker)
19+
20+
21+
def initUi(self):
22+
self.sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding)
23+
24+
self._pbHeight = 30
25+
26+
self.setMinimumWidth(self._width)
27+
#self.setMaximumWidth(self._width)
28+
self.setMinimumHeight(self._minHeight)
29+
30+
self.glayout = QtGui.QGridLayout(self)
31+
32+
self.totalProgressBar = QtGui.QProgressBar(self)
33+
self.totalProgressBar.setMinimumHeight(self._pbHeight)
34+
self.totalProgressBar.setMaximumHeight(self._pbHeight)
35+
36+
self.toggleButton = QtGui.QPushButton('Details', self)
37+
self.toggleButton.setCheckable(True)
38+
self.toggleButton.toggled.connect(self.showDetails)
39+
self.glayout.addWidget(self.totalProgressBar, 0, 0, 1, 1)
40+
self.glayout.addWidget(self.toggleButton, 0, 1, 1, 1)
41+
42+
#styleSheet = """.QProgressBar {
43+
#border: none;
44+
#border-radius: 3px;
45+
#text-align: center;
46+
#background-color: rgba(37, 37, 37, 50%);
47+
#color: white;
48+
#margin: 1px;
49+
#border-bottom-left-radius:5px;
50+
#border-top-left-radius:5px;
51+
#}
52+
53+
#.QProgressBar::chunk {
54+
#background-color: #05B8CC;
55+
#border-radius: 3px;
56+
#}
57+
58+
#.OverlayProgressWidget {
59+
#background-color: white;
60+
#}
61+
62+
#"""
63+
## set stylesheet for all progressbars in this widget
64+
#self.setStyleSheet(styleSheet)
65+
66+
parent = self.parent()
67+
xAnchor = parent.width() - self._width - self._margin
68+
yAnchor = self._margin
69+
self.setGeometry(xAnchor, yAnchor, self._width, self._minHeight)
70+
71+
@Slot(bool)
72+
def showDetails(self, toggled):
73+
for (progressBar, label) in self._detailProgressBars:
74+
progressBar.setVisible(toggled)
75+
label.setVisible(toggled)
76+
self.resizeFrame()
77+
78+
79+
def _addProgressBar(self, worker):
80+
progressBar = QtGui.QProgressBar(self)
81+
progressBar.setMinimumHeight(self._pbHeight - 5)
82+
progressBar.setMaximumHeight(self._pbHeight - 5)
83+
label = QtGui.QLabel(worker.name, self)
84+
if not self.toggleButton.isChecked():
85+
progressBar.hide()
86+
label.hide()
87+
row = self._addedBars + 1
88+
self.glayout.addWidget(progressBar, row, 0, 1, 1)
89+
self.glayout.addWidget(label, row, 1, 1, 1)
90+
self._addedBars += 1
91+
self._detailProgressBars.append((progressBar, label))
92+
worker.progressChanged.connect(progressBar.setValue)
93+
worker.progressChanged.connect(self.calculateTotalProgress)
94+
95+
worker.progressChanged.connect(self.debugProgressChanged)
96+
97+
def debugProgressChanged(self, value):
98+
print "debugProgressChanged", value
99+
100+
def addWorker(self, worker):
101+
self._workers.append(worker)
102+
self._addProgressBar(worker)
103+
self.resizeFrame()
104+
105+
def resizeFrame(self):
106+
size = self.glayout.sizeHint()
107+
self.resize(size.width(), size.height())
108+
109+
@Slot()
110+
def calculateTotalProgress(self):
111+
bars = len(self._detailProgressBars)
112+
if bars:
113+
progress = 0
114+
for (progressBar, label) in self._detailProgressBars:
115+
value = progressBar.value()
116+
progress += value
117+
progress = progress / bars
118+
else:
119+
progress = 100
120+
121+
self.totalProgressBar.setValue(progress)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def run_tests(self):
6565
install_requires=['pandas>=0.15.1', 'pytest', 'pytest-qt==1.2.2', 'pytest-cov', 'python-magic==0.4.6'],
6666
cmdclass={'test': PyTest},
6767
author_email='m.Ludwig@datalyze-solutions.com',
68-
description='catches exceptions inside qt applications and writes them to a message box and into a log file',
68+
description='Utilities to use pandas (the data analysis / manipulation library for Python) with Qt.',
6969
long_description=long_description,
7070

7171
include_package_data=True,

0 commit comments

Comments
 (0)