Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions xicam/gui/bluesky/central.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def __init__(self, *args,
left_pane.addWidget(self.summary_widget)

layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
splitter = QSplitter(Qt.Horizontal)
layout.addWidget(splitter)
splitter.addWidget(left_pane)
Expand Down
2 changes: 1 addition & 1 deletion xicam/gui/bluesky/databroker_catalog_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def emit_opened_catalogs(name, my_list):

# connect the open_entries in the search model to sigOpen
self.centralWidget.search_model.open_entries.connect(emit_opened_catalogs)
self.centralWidget.summary_widget.open.connect(emit_opened_catalogs)
self.centralWidget.search_widget.search_results_widget.sigOpen.connect(emit_opened_catalogs)
layout.addWidget(self.centralWidget)


Expand Down
46 changes: 37 additions & 9 deletions xicam/gui/bluesky/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,18 @@
QLabel,
QLineEdit,
QVBoxLayout,
QFormLayout,
QWidget,
QTableView,
QMenu,
QAction
)

from xicam.core import msg

from .utils import ConfigurableQObject
from .top_utils import load_config, Callable
from xicam.gui.widgets.collapsiblewidget import CollapsiblePanel


MAX_SEARCH_RESULTS = 100 # TODO Use fetchMore instead of a hard limit.
Expand Down Expand Up @@ -335,25 +338,30 @@ def __init__(self, *args, **kwargs):
mongo_query_help_button.setText('?')
search_bar_layout.addWidget(mongo_query_help_button)
mongo_query_help_button.clicked.connect(self.show_mongo_query_help)
self.query_widget = CollapsiblePanel(title='Query', layout=search_bar_layout)

layout = QFormLayout()
layout.setContentsMargins(0, 0, 0, 0)
date_collapse = CollapsiblePanel(title='Dates', layout=layout)

self.since_widget = QDateTimeEdit()
self.since_widget.setCalendarPopup(True)
self.since_widget.setDisplayFormat('yyyy-MM-dd HH:mm')
since_layout = QHBoxLayout()
since_layout.addWidget(QLabel('Since:'))
since_layout.addWidget(self.since_widget)

self.until_widget = QDateTimeEdit()
self.until_widget.setCalendarPopup(True)
self.until_widget.setDisplayFormat('yyyy-MM-dd HH:mm')
until_layout = QHBoxLayout()
until_layout.addWidget(QLabel('Until:'))
until_layout.addWidget(self.until_widget)

date_collapse.addRow('Since:', self.since_widget)
date_collapse.addRow('Until:', self.until_widget)
# Show the Dates widget initially uncollapsed
date_collapse.toggleButton.setChecked(True)

layout = QVBoxLayout()
layout.addLayout(since_layout)
layout.addLayout(until_layout)
layout.addLayout(search_bar_layout)
# layout.addLayout(since_layout)
# layout.addLayout(until_layout)
layout.addWidget(date_collapse)
layout.addWidget(self.query_widget)
self.setLayout(layout)

def mark_custom_query(self, valid):
Expand Down Expand Up @@ -405,6 +413,8 @@ class SearchResultsWidget(QTableView):
"""
Table of search results
"""
sigOpen = Signal(str, list)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
Expand All @@ -416,6 +426,24 @@ def __init__(self, *args, **kwargs):
self.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
self.setAlternatingRowColors(True)

self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.menuRequested)
self.menu = QMenu()
standardActions = [
QAction("Open", self),
]
self.menu.addActions(standardActions)
standardActions[0].triggered.connect(self.open)

def menuRequested(self, position):
self.menu.exec_(self.viewport().mapToGlobal(position))

def open(self):
uids = [self.model().itemFromIndex(index).data(Qt.DisplayRole) for index in
self.selectionModel().selectedRows()]
runs = [self.model().search_state._results_catalog[uid] for uid in uids]
self.sigOpen.emit("blah?", runs)

def hide_hidden_columns(self):
hidden_columns = QSettings().value("catalog.columns.hidden") or set()
header = self.horizontalHeader()
Expand Down
17 changes: 2 additions & 15 deletions xicam/gui/bluesky/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@


class SummaryWidget(QWidget):
open = Signal([str, list])

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.uid_label = QLabel()
self.open_individually_button = QPushButton('Open')
self.open_individually_button.setEnabled(False)
self.open_individually_button.clicked.connect(self._open_individually)

self.copy_uid_button = QPushButton('Copy UID to Clipboard')
self.copy_uid_button.setEnabled(False)
self.copy_uid_button.clicked.connect(self._copy_uid)
Expand All @@ -29,7 +26,6 @@ def __init__(self, *args, **kwargs):
uid_layout.addWidget(self.uid_label)
uid_layout.addWidget(self.copy_uid_button)
layout = QVBoxLayout()
layout.addWidget(self.open_individually_button)
layout.addLayout(uid_layout)
layout.addWidget(self.streams)
self.setLayout(layout)
Expand All @@ -42,10 +38,6 @@ def cache_tab_titles(self, titles):
def _copy_uid(self):
QApplication.clipboard().setText(self.uid)

def _open_individually(self):
for entry in self.entries:
self.open.emit(None, [entry])


def set_entries(self, entries):
self.entries.clear()
Expand All @@ -54,15 +46,12 @@ def set_entries(self, entries):
self.uid_label.setText('')
self.streams.setText('')
self.copy_uid_button.setEnabled(False)
self.open_individually_button.setEnabled(False)
elif len(entries) == 1:
entry, = entries
run = entry()
self.uid = run.metadata['start']['uid']
self.uid_label.setText(self.uid[:8])
self.copy_uid_button.setEnabled(True)
self.open_individually_button.setEnabled(True)
self.open_individually_button.setText('Open')
num_events = (run.metadata['stop'] or {}).get('num_events')
if num_events:
self.streams.setText(
Expand All @@ -76,6 +65,4 @@ def set_entries(self, entries):
else:
self.uid_label.setText('(Multiple Selected)')
self.streams.setText('')
self.copy_uid_button.setEnabled(False)
self.open_individually_button.setText('Open individually')
self.open_individually_button.setEnabled(True)
self.copy_uid_button.setEnabled(False)
97 changes: 95 additions & 2 deletions xicam/gui/widgets/collapsiblewidget.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from qtpy.QtCore import Qt, Signal
from qtpy.QtWidgets import QGridLayout, QSplitter, QToolBar, QWidget
from qtpy.QtCore import Signal, QPropertyAnimation, Qt, QParallelAnimationGroup, QAbstractAnimation
from qtpy.QtWidgets import QSplitter, QToolBar, QWidget, QFrame, QScrollArea, QToolButton, QGridLayout, QSizePolicy, \
QLabel, QVBoxLayout, QFormLayout


# TODO: this could be more generic, defining a collapsible interface/mixin type class
Expand Down Expand Up @@ -83,3 +84,95 @@ def collapse(self, collapsed):
else:
sizes = [self.widgetSize, self.splitter.sizes()[1] - self.widgetSize]
self.splitter.setSizes(sizes)


class CollapsiblePanel(QWidget):
def __init__(self, parent=None, title='', layout=None, animationDuration=100):
"""
References:
# Adapted from c++ version
http://stackoverflow.com/questions/32476006/how-to-make-an-expandable-collapsable-section-widget-in-qt
"""
super(CollapsiblePanel, self).__init__(parent=parent)

self.animationDuration = animationDuration
self.toggleAnimation = QParallelAnimationGroup()
self.contentArea = QWidget()
self.headerLine = QFrame()
self.toggleButton = QToolButton()
self.mainLayout = QGridLayout()

toggleButton = self.toggleButton
toggleButton.setStyleSheet("QToolButton { border: none; }")
toggleButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
toggleButton.setArrowType(Qt.RightArrow)
toggleButton.setText(str(title))
toggleButton.setCheckable(True)
toggleButton.setChecked(False)

headerLine = self.headerLine
headerLine.setFrameShape(QFrame.HLine)
headerLine.setFrameShadow(QFrame.Sunken)
headerLine.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)

# self.contentArea.setStyleSheet("QScrollArea { background-color: white; border: none; }")
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
self.contentArea.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
# start out collapsed
self.contentArea.setMaximumHeight(0)
self.contentArea.setMinimumHeight(0)
# let the entire widget grow and shrink with its content
toggleAnimation = self.toggleAnimation
toggleAnimation.addAnimation(QPropertyAnimation(self, b"minimumHeight"))
toggleAnimation.addAnimation(QPropertyAnimation(self, b"maximumHeight"))
toggleAnimation.addAnimation(QPropertyAnimation(self.contentArea, b"minimumHeight"))
toggleAnimation.addAnimation(QPropertyAnimation(self.contentArea, b"maximumHeight"))
# don't waste space
mainLayout = self.mainLayout
mainLayout.setVerticalSpacing(0)
mainLayout.setContentsMargins(0, 0, 0, 0)
mainLayout.addWidget(self.toggleButton, 0, 0, 1, 1, Qt.AlignLeft)
mainLayout.addWidget(self.headerLine, 0, 2, 1, 1, Qt.AlignVCenter)
mainLayout.addWidget(self.contentArea, 1, 0, 1, 3)
self.setLayout(self.mainLayout)

# Initialize contentLayout
self.content_layout = None
self.setContentLayout(layout if layout is not None else QVBoxLayout())

def start_animation(checked):
collapsedHeight = self.sizeHint().height() - self.contentArea.maximumHeight()
contentHeight = self.content_layout.sizeHint().height()
for i in [0, 1]:
spoilerAnimation = self.toggleAnimation.animationAt(i)
spoilerAnimation.setDuration(self.animationDuration)
spoilerAnimation.setStartValue(collapsedHeight)
spoilerAnimation.setEndValue(collapsedHeight + contentHeight)
for i in [2, 3]:
contentAnimation = self.toggleAnimation.animationAt(i)
contentAnimation.setDuration(self.animationDuration)
contentAnimation.setStartValue(0)
contentAnimation.setEndValue(contentHeight)

arrow_type = Qt.DownArrow if checked else Qt.RightArrow
direction = QAbstractAnimation.Forward if checked else QAbstractAnimation.Backward
toggleButton.setArrowType(arrow_type)
self.toggleAnimation.setDirection(direction)
self.toggleAnimation.start()

self.toggleButton.toggled.connect(start_animation)

def setContentLayout(self, contentLayout):
# Not sure if this is equivalent to self.contentArea.destroy()
# self.contentArea.destroy()
self.content_layout = contentLayout
self.contentArea.setLayout(contentLayout)

def addWidget(self, widget):
self.content_layout.addWidget(widget)

def addRow(self, *args, **kwargs):
if not isinstance(self.contentArea.layout(), QFormLayout):
raise AttributeError('The assigned layout is not QFormLayout, however .addRow was used')
else:
self.content_layout.addRow(*args, **kwargs)