File wellmet/qt_gui/qt_pairwise.py changed (mode: 100644) (index 3678c21..39ccfc0) |
1 |
1 |
#!/usr/bin/env python |
#!/usr/bin/env python |
2 |
2 |
# coding: utf-8 |
# coding: utf-8 |
3 |
3 |
|
|
|
4 |
|
import gc |
4 |
5 |
import pyqtgraph as pg |
import pyqtgraph as pg |
5 |
6 |
from pyqtgraph.Qt import QtGui, QtWidgets |
from pyqtgraph.Qt import QtGui, QtWidgets |
6 |
7 |
from pyqtgraph.Qt import QtCore |
from pyqtgraph.Qt import QtCore |
7 |
8 |
|
|
8 |
9 |
import numpy as np |
import numpy as np |
9 |
10 |
from scipy import spatial # for distance matrix |
from scipy import spatial # for distance matrix |
10 |
|
#from ..wireframe import ConvexSpline |
|
|
11 |
|
from ..wireframe import ConvexSpline |
11 |
12 |
from .. import reader |
from .. import reader |
12 |
13 |
|
|
13 |
14 |
|
|
|
... |
... |
class MatrixWindow(QtWidgets.QMainWindow): |
200 |
201 |
self.dockables = [] |
self.dockables = [] |
201 |
202 |
|
|
202 |
203 |
|
|
203 |
|
#self.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock) |
|
204 |
204 |
|
|
205 |
205 |
|
|
|
206 |
|
dock = QtWidgets.QDockWidget("Contacts", self) |
|
207 |
|
dock.setWidget(ContactWidget(self, dock)) |
|
208 |
|
self.dockables.append(dock) |
|
209 |
|
self.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock) |
206 |
210 |
|
|
207 |
211 |
|
|
208 |
212 |
|
|
209 |
213 |
for dock in self.dockables: |
for dock in self.dockables: |
210 |
214 |
self.view.addAction(dock.toggleViewAction()) |
self.view.addAction(dock.toggleViewAction()) |
211 |
215 |
#dock.setFloating(True) |
#dock.setFloating(True) |
212 |
|
|
|
213 |
|
#self.w.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.items) |
|
214 |
|
|
|
215 |
|
|
|
216 |
|
|
|
|
216 |
|
|
|
217 |
|
|
217 |
218 |
csv_filter = "CSV files (*.csv)" |
csv_filter = "CSV files (*.csv)" |
218 |
219 |
npy_filter = "NumPy binary files (*.npy)" |
npy_filter = "NumPy binary files (*.npy)" |
219 |
220 |
txt_filter = "Text files (*.txt)" |
txt_filter = "Text files (*.txt)" |
|
... |
... |
class MView(pg.ImageView): |
463 |
464 |
|
|
464 |
465 |
def __getattr__(self, attr): |
def __getattr__(self, attr): |
465 |
466 |
if attr == 'red': |
if attr == 'red': |
466 |
|
return data[0] |
|
|
467 |
|
return self.data[0] |
467 |
468 |
if attr == 'green': |
if attr == 'green': |
468 |
|
return data[1] |
|
|
469 |
|
return self.data[1] |
469 |
470 |
if attr == 'blue': |
if attr == 'blue': |
470 |
|
return data[2] |
|
|
471 |
|
return self.data[2] |
471 |
472 |
raise AttributeError(attr) |
raise AttributeError(attr) |
472 |
473 |
|
|
473 |
474 |
|
|
474 |
475 |
def on_slice_changed(self, nsim): |
def on_slice_changed(self, nsim): |
475 |
|
#č resize nejde. Asi pyqtgraph drží referenci |
|
|
476 |
|
#č resize nejde. Ani s gc. Asi pyqtgraph drží referenci |
476 |
477 |
#č i když já pekně vím, že ta data kopíruje a pro Qt normalizuje |
#č i když já pekně vím, že ta data kopíruje a pro Qt normalizuje |
477 |
478 |
self.data = np.empty((3, nsim, nsim)) |
self.data = np.empty((3, nsim, nsim)) |
478 |
479 |
#č zelené-červené barvy přepíše distance matrix |
#č zelené-červené barvy přepíše distance matrix |
|
... |
... |
class MView(pg.ImageView): |
481 |
482 |
self.nsim = nsim |
self.nsim = nsim |
482 |
483 |
|
|
483 |
484 |
|
|
484 |
|
def update(self): |
|
485 |
|
self.setImage(self.data.T, levelMode='rgba') |
|
|
485 |
|
def update(self, **kwargs): |
|
486 |
|
self.setImage(self.data.T, levelMode='rgba', **kwargs) |
486 |
487 |
|
|
487 |
488 |
def on_mouse_moved(self, evt): |
def on_mouse_moved(self, evt): |
488 |
489 |
pos = evt[0] ## using signal proxy turns original arguments into a tuple |
pos = evt[0] ## using signal proxy turns original arguments into a tuple |
|
... |
... |
class DistanceMatrix: |
578 |
579 |
|
|
579 |
580 |
|
|
580 |
581 |
|
|
|
582 |
|
|
|
583 |
|
|
|
584 |
|
|
|
585 |
|
|
|
586 |
|
class ContactWidget(pg.LayoutWidget): |
|
587 |
|
def __init__(self, w, parent=None): |
|
588 |
|
super().__init__(parent) |
|
589 |
|
self.w = w |
|
590 |
|
|
|
591 |
|
w.image_view.mouse_moved.connect(self.on_mouse_moved) |
|
592 |
|
w.image_view.mouse_clicked.connect(self.on_mouse_dragged) |
|
593 |
|
w.image_view.mouse_double_clicked.connect(self.on_double_click) |
|
594 |
|
w.image_view.mouse_right_dragged.connect(self.on_mouse_dragged) |
|
595 |
|
|
|
596 |
|
w.space_changed.connect(self.on_space_changed) |
|
597 |
|
w.slice_changed.connect(self.on_nsim_changed) |
|
598 |
|
#w.redraw_called.connect(self.hide) |
|
599 |
|
|
|
600 |
|
n = w.slider.value() |
|
601 |
|
size = n * (n - 1) // 2 |
|
602 |
|
self.condensed_contacts = np.zeros(size, dtype=np.int8) |
|
603 |
|
self.mask = np.tri(n, k=-1, dtype=bool).T |
|
604 |
|
self.nsim = n |
|
605 |
|
|
|
606 |
|
self.setup() |
|
607 |
|
self.kwargs = {} |
|
608 |
|
self.setup_CS() |
|
609 |
|
|
|
610 |
|
def on_nsim_changed(self, n): |
|
611 |
|
try: |
|
612 |
|
del self.condensed_qontacts |
|
613 |
|
except AttributeError: |
|
614 |
|
pass |
|
615 |
|
size = n * (n - 1) // 2 |
|
616 |
|
gc.collect() |
|
617 |
|
self.condensed_contacts.resize(size) |
|
618 |
|
self.condensed_contacts[:] = 0 |
|
619 |
|
self.mask = np.tri(n, k=-1, dtype=bool).T |
|
620 |
|
self.nsim = n |
|
621 |
|
|
|
622 |
|
self.on_space_changed() |
|
623 |
|
|
|
624 |
|
def on_space_changed(self, *args, **kwargs): |
|
625 |
|
self.setup_CS() |
|
626 |
|
|
|
627 |
|
|
|
628 |
|
def on_mouse_moved(self, x, y): |
|
629 |
|
if (x == -1) or (x == y): |
|
630 |
|
self.status_label.setText("") |
|
631 |
|
return None |
|
632 |
|
|
|
633 |
|
m = self.nsim |
|
634 |
|
i, j = min(x, y), max(x, y) |
|
635 |
|
entry = m * i + j - ((i + 2) * (i + 1)) // 2 |
|
636 |
|
try: |
|
637 |
|
val = self.condensed_qontacts[entry] |
|
638 |
|
except AttributeError: |
|
639 |
|
val = self.condensed_contacts[entry] |
|
640 |
|
|
|
641 |
|
if val > 0: |
|
642 |
|
self.status_label.setText("There IS contact between %d and %d" % (x, y)) |
|
643 |
|
elif val < 0: |
|
644 |
|
self.status_label.setText("There is NO contact between %d and %d" % (x, y)) |
|
645 |
|
else: |
|
646 |
|
self.status_label.setText("") |
|
647 |
|
|
|
648 |
|
|
|
649 |
|
def on_mouse_dragged(self, x, y): |
|
650 |
|
if (x == -1) or (x == y): |
|
651 |
|
return None |
|
652 |
|
|
|
653 |
|
m = self.nsim |
|
654 |
|
i, j = min(x, y), max(x, y) |
|
655 |
|
entry = m * i + j - ((i + 2) * (i + 1)) // 2 |
|
656 |
|
val = self.condensed_contacts[entry] |
|
657 |
|
if val == 0: |
|
658 |
|
self.condensed_contacts[entry] = val = self.get_contact(i, j) |
|
659 |
|
self.show() |
|
660 |
|
|
|
661 |
|
if val > 0: |
|
662 |
|
self.status_label.setText("There IS contact between %d and %d" % (x, y)) |
|
663 |
|
else: |
|
664 |
|
self.status_label.setText("There is NO contact between %d and %d" % (x, y)) |
|
665 |
|
|
|
666 |
|
def on_double_click(self, x, y): |
|
667 |
|
if (x == -1) or (x == y): |
|
668 |
|
return None |
|
669 |
|
|
|
670 |
|
m = self.nsim |
|
671 |
|
i, j = min(x, y), max(x, y) |
|
672 |
|
entry = m * i + j - ((i + 2) * (i + 1)) // 2 |
|
673 |
|
self.condensed_contacts[entry] = val = self.get_contact(i, j) |
|
674 |
|
self.show() |
|
675 |
|
|
|
676 |
|
if val > 0: |
|
677 |
|
self.status_label.setText("There IS contact between %d and %d" % (x, y)) |
|
678 |
|
else: |
|
679 |
|
self.status_label.setText("There is NO contact between %d and %d" % (x, y)) |
|
680 |
|
|
|
681 |
|
|
|
682 |
|
def discover(self): |
|
683 |
|
nsim = self.nsim |
|
684 |
|
if nsim > 0: |
|
685 |
|
self.stopbtn.setEnabled(True) |
|
686 |
|
self.stopbtn.setCheckable(True) |
|
687 |
|
contacts = self.condensed_contacts |
|
688 |
|
if self.param.getValues()['check_all'][0]: |
|
689 |
|
for i in range(nsim-1, -1, -1): |
|
690 |
|
# keep the GUI responsive :) |
|
691 |
|
self.show() |
|
692 |
|
self.w.app.processEvents() |
|
693 |
|
if self.stopbtn.isChecked(): |
|
694 |
|
break |
|
695 |
|
preentry = nsim * i - ((i + 2) * (i + 1)) // 2 |
|
696 |
|
for j in range(i+1, nsim): |
|
697 |
|
contacts[preentry + j] = self.get_contact(i, j) |
|
698 |
|
else: |
|
699 |
|
failsi = self.w.get_sample_box().failsi |
|
700 |
|
for i in range(nsim-1, -1, -1): |
|
701 |
|
# keep the GUI responsive :) |
|
702 |
|
self.show() |
|
703 |
|
self.w.app.processEvents() |
|
704 |
|
if self.stopbtn.isChecked(): |
|
705 |
|
break |
|
706 |
|
preentry = nsim * i - ((i + 2) * (i + 1)) // 2 |
|
707 |
|
if failsi[i]: #č první je červený |
|
708 |
|
for j in range(i+1, nsim): |
|
709 |
|
contacts[preentry + j] = self.get_contact(i, j) |
|
710 |
|
else: |
|
711 |
|
for j in range(i+1, nsim): |
|
712 |
|
if failsi[j]: |
|
713 |
|
contacts[preentry + j] = self.get_contact(i, j) |
|
714 |
|
self.stopbtn.setChecked(False) |
|
715 |
|
self.stopbtn.setEnabled(False) |
|
716 |
|
self.show() |
|
717 |
|
|
|
718 |
|
|
|
719 |
|
def get_contact(self, i, j): |
|
720 |
|
return -1 + 2 * self.CS.is_couple((i, j), **self.kwargs) |
|
721 |
|
|
|
722 |
|
|
|
723 |
|
def setup(self): |
|
724 |
|
self.toolbar = QtWidgets.QToolBar(self) |
|
725 |
|
|
|
726 |
|
action = self.toolbar.addAction("discover", self.discover) |
|
727 |
|
btn = self.toolbar.widgetForAction(action) |
|
728 |
|
btn.setAutoRaise(False) |
|
729 |
|
btn.setToolTip("Searches for a common facets between Voronoi cells") |
|
730 |
|
|
|
731 |
|
action = self.toolbar.addAction("stop", self.stop) |
|
732 |
|
self.stopbtn = btn = self.toolbar.widgetForAction(action) |
|
733 |
|
btn.setAutoRaise(False) |
|
734 |
|
btn.setCheckable(True) |
|
735 |
|
btn.setChecked(False) |
|
736 |
|
btn.setEnabled(False) |
|
737 |
|
|
|
738 |
|
|
|
739 |
|
action = self.toolbar.addAction("Qhull check", self.check) |
|
740 |
|
btn = self.toolbar.widgetForAction(action) |
|
741 |
|
btn.setAutoRaise(False) |
|
742 |
|
btn.setToolTip("Compare with exact Qhull wireframe") |
|
743 |
|
|
|
744 |
|
action = self.toolbar.addAction("show", self.show) |
|
745 |
|
btn = self.toolbar.widgetForAction(action) |
|
746 |
|
btn.setAutoRaise(False) |
|
747 |
|
|
|
748 |
|
action = self.toolbar.addAction("hide", self.hide) |
|
749 |
|
btn = self.toolbar.widgetForAction(action) |
|
750 |
|
btn.setAutoRaise(False) |
|
751 |
|
|
|
752 |
|
action = self.toolbar.addAction("clear", self.clear) |
|
753 |
|
btn = self.toolbar.widgetForAction(action) |
|
754 |
|
btn.setAutoRaise(False) |
|
755 |
|
|
|
756 |
|
self.addWidget(self.toolbar, row=0, col=0) |
|
757 |
|
|
|
758 |
|
|
|
759 |
|
#оӵ остальной (люкет) уллапала |
|
760 |
|
|
|
761 |
|
### Create ParameterTree widget |
|
762 |
|
self.ptree = pg.parametertree.ParameterTree() |
|
763 |
|
self._set_param() |
|
764 |
|
self.ptree.setParameters(self.param, showTop=False) |
|
765 |
|
|
|
766 |
|
self.addWidget(self.ptree, row=1, col=0) |
|
767 |
|
|
|
768 |
|
self.status_label = QtWidgets.QLabel() |
|
769 |
|
#self.status_label.setText() |
|
770 |
|
self.addWidget(self.status_label, row=2, col=0) |
|
771 |
|
|
|
772 |
|
def stop(self): |
|
773 |
|
self.stopbtn.setChecked(True) |
|
774 |
|
|
|
775 |
|
def check(self): |
|
776 |
|
pass |
|
777 |
|
|
|
778 |
|
def clear(self): |
|
779 |
|
self.condensed_contacts[:] = 0 |
|
780 |
|
try: |
|
781 |
|
del self.condensed_qontacts |
|
782 |
|
del self.qframe |
|
783 |
|
except AttributeError: |
|
784 |
|
pass |
|
785 |
|
|
|
786 |
|
self.w.image_view.blue[self.mask] = 0 |
|
787 |
|
self.w.image_view.update() |
|
788 |
|
|
|
789 |
|
def show(self): |
|
790 |
|
self.w.image_view.blue[self.mask] = self.condensed_contacts |
|
791 |
|
try: |
|
792 |
|
self.w.image_view.blue[self.mask] -= self.condensed_qontacts |
|
793 |
|
except AttributeError: |
|
794 |
|
pass |
|
795 |
|
self.w.image_view.update(autoRange=False) |
|
796 |
|
|
|
797 |
|
def hide(self): |
|
798 |
|
self.w.image_view.blue[self.mask] = 0 |
|
799 |
|
self.w.image_view.update() |
|
800 |
|
|
|
801 |
|
|
|
802 |
|
def _set_param(self): |
|
803 |
|
params = list() |
|
804 |
|
params.append({'name': 'check_all', 'title': 'all contacts', |
|
805 |
|
'type': 'bool', 'value': True, |
|
806 |
|
'tip': 'allows to skip "green" contacts'}) |
|
807 |
|
params.append({'name': 'method', 'type': 'list', 'value': 'DirectHull', \ |
|
808 |
|
'values': ['SBall', 'BrickHull', 'DirectHull', 'CompleteHull', 'QHull', 'Grick']}) |
|
809 |
|
|
|
810 |
|
params.append({'name': 'ndir', 'type': 'int', \ |
|
811 |
|
'limits': (1, float('inf')), 'value': 1000, 'default': 1000, \ |
|
812 |
|
'title': "number of random directions", \ |
|
813 |
|
'tip': "Used only for Grick or for random scheme in DirectHull (or CompleteHull)"}) |
|
814 |
|
|
|
815 |
|
params.append({'name': 'integrator', 'title': "integrator", 'type': 'list', \ |
|
816 |
|
'values': ['MC', 'IS', '1DS'], 'value': '1DS' }) |
|
817 |
|
params.append({'name': 'tol', 'type': 'float', \ |
|
818 |
|
'title': "tolerance", \ |
|
819 |
|
'limits': (0, float('inf')), 'value': 0.9, 'default': 0.9,\ |
|
820 |
|
'tip': "Applied when Ghull integrates non Gaussian convex hulls"}) |
|
821 |
|
|
|
822 |
|
params.append({'name': 'use_MC', 'title': "use MC", 'type': 'bool', 'value': False, \ |
|
823 |
|
'tip': "Used for shot(), fire() and boom() functions"}) |
|
824 |
|
|
|
825 |
|
params.append({'name': 'budget', 'type': 'int', \ |
|
826 |
|
'limits': (1, float('inf')), 'value': 1000, 'default': 1000,\ |
|
827 |
|
'tip': "Number of simulations for optimal importance sampling"}) |
|
828 |
|
|
|
829 |
|
params.append({'name': 'Update as the box runned', 'type': 'bool', 'value': False }) # 'tip': "This is a checkbox" |
|
830 |
|
params.append({'name': 'index', 'title': "replace previous", 'type': 'bool', 'value': True }) |
|
831 |
|
|
|
832 |
|
self.param = pg.parametertree.Parameter.create(name='params', type='group', children=params) |
|
833 |
|
|
|
834 |
|
|
|
835 |
|
def setup_CS(self): |
|
836 |
|
sample_box = self.w.get_sample_box() |
|
837 |
|
sample_space = getattr(sample_box, self.w.space) |
|
838 |
|
self.CS = ConvexSpline(sample_space) |
|
839 |
|
|
|
840 |
|
# |
|
841 |
|
# |
|
842 |
|
# #č bez semplu se neobejde |
|
843 |
|
# nsim = self.sb_item.slider.value() |
|
844 |
|
# sample = self.sb_item.sample_box.f_model[:nsim] |
|
845 |
|
# |
|
846 |
|
# # ['SBall', 'BrickHull', 'DirectHull', 'QHull', 'Grick'] |
|
847 |
|
# hull_model = self.param.getValues()['method'][0] |
|
848 |
|
# if hull_model == 'SBall': |
|
849 |
|
# return khull.GBall(sample) |
|
850 |
|
# elif hull_model == 'BrickHull': |
|
851 |
|
# space = self.param.getValues()['check_all'][0] |
|
852 |
|
# return khull.BrickHull(sample, space) |
|
853 |
|
# elif hull_model == 'DirectHull': |
|
854 |
|
# space = self.param.getValues()['space'][0] |
|
855 |
|
# direct_plan = self.get_scheme() |
|
856 |
|
# return khull.DirectHull(sample, direct_plan, space) |
|
857 |
|
# elif hull_model == 'CompleteHull': |
|
858 |
|
# space = self.param.getValues()['space'][0] |
|
859 |
|
# direct_plan = self.get_scheme() |
|
860 |
|
# return khull.CompleteHull(sample, direct_plan, space) |
|
861 |
|
# elif hull_model == 'QHull': |
|
862 |
|
# space = self.param.getValues()['space'][0] |
|
863 |
|
# #č tento widget pokažde generuje obálku znovu |
|
864 |
|
# return khull.QHull(sample, space, incremental=False) |
|
865 |
|
# elif hull_model == 'Grick': |
|
866 |
|
# direct_plan = self.get_scheme() |
|
867 |
|
# ndir = self.param.getValues()['ndir'][0] |
|
868 |
|
# return khull.Grick(sample, direct_plan, nrandom=ndir, auto_update=True) |
|
869 |
|
# else: |
|
870 |
|
# raise ValueError("HullEstimationWidget: co to je za obálku?") |
|
871 |
|
|
|
872 |
|
|
|
873 |
|
|
|
874 |
|
|