"""
Utilities to create widgets
"""
__all__ = [
'Dialog',
]
import os
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QSizePolicy
from madgui.util.layout import HBoxLayout, VBoxLayout, Stretch
from madgui.util.qt import present, notifyEvent
# short-hands:
Button = QDialogButtonBox
def perpendicular(orientation):
"""Get perpendicular orientation."""
return (Qt.Horizontal | Qt.Vertical) ^ orientation
def expand(widget, orientation):
"""Expand widget in specified direction."""
policy = widget.sizePolicy()
if orientation == Qt.Horizontal:
policy.setHorizontalPolicy(QSizePolicy.Minimum)
else:
policy.setVerticalPolicy(QSizePolicy.Minimum)
widget.setSizePolicy(policy)
class SerializeButtons(QDialogButtonBox):
"""
:ivar QWidget widget: the content area widget
:ivar str folder: folder for exports/imports
"""
def __init__(self, widget, folder, *args, **kwargs):
super().__init__(*args, **kwargs)
self.widget = widget
self.folder = folder
self.addButton(Button.Open).clicked.connect(self.onImport)
self.addButton(Button.Save).clicked.connect(self.onExport)
self.addButton(Button.Ok).clicked.connect(self.onAccept)
self.button(Button.Open).setAutoDefault(False)
self.button(Button.Save).setAutoDefault(False)
self.button(Button.Ok).setDefault(True)
expand(self, perpendicular(self.orientation()))
def updateButtons(self):
self.button(Button.Save).setEnabled(
hasattr(self.exporter, 'exportTo'))
self.button(Button.Open).setEnabled(
hasattr(self.exporter, 'importFrom') and
not getattr(self.exporter, 'readonly', None))
@property
def exporter(self):
return getattr(self.widget, 'exporter', None)
def onImport(self):
"""Import data from JSON/YAML file."""
from madgui.widget.filedialog import getOpenFileName
filename = getOpenFileName(
self.window(), 'Import values', self.folder,
self.exporter.importFilters)
if filename:
self.exporter.importFrom(filename)
self.folder, _ = os.path.split(filename)
def onExport(self):
"""Export data to YAML file."""
from madgui.widget.filedialog import getSaveFileName
filename = getSaveFileName(
self.window(), 'Export values', self.folder,
self.exporter.exportFilters)
if filename:
self.exporter.exportTo(filename)
self.folder, _ = os.path.split(filename)
def onAccept(self):
self.window().accept()
def addCustomButton(self, buttonTag, apply):
"""
Add a custom button
@param buttonTag is a string that appears on the button
@param apply is the function performed when clicked on
"""
custom = self.addButton(buttonTag, Button.ActionRole)
custom.clicked.connect(apply)
[docs]class Dialog(QDialog):
"""
Utility to wrap an application widget into a window with size-grip and
optional buttons, and manage their lifetime via an owner.
We distinguish between an `owner` and `parent` widget as follows:
- dialogs always stay on top of the parent and share their taskbar entry
- dialogs do not stay on top of the owner, nor share their taskbar entry,
However: they will be kept alive as long as the owner, and when the
owner is closed, will be closed as well.
The second option is preferrable for most non-modal windows!
- we want to allow the mainwindow in the foreground when focussed
- we want to allow choosing between all windows from the taskbar
"""
# TODO: reset button
def __init__(self, owner=None, widget=None, *, parent=None, tight=True):
super().__init__(parent)
self.setSizeGripEnabled(True)
self.finished.connect(self.close)
self.event_filter = owner and notifyEvent(owner, 'Close', self.close)
if owner:
self.setFontSize(owner.font().pointSize())
if widget is not None:
self.setWidget(widget, VBoxLayout([widget], tight=tight))
self.show()
[docs] def setFontSize(self, pointsize):
# Note that setStyleSheet is used because it propagates the change to
# subwidgets better than setFont.
self.setStyleSheet("font-size:{}pt;".format(pointsize))
# TODO: update enabled-state of apply-button?
[docs] def closeEvent(self, event):
# Prevent errors when closing the parent after the child:
if self.event_filter:
self.event_filter.uninstall()
# send closeEvent to children!
if hasattr(self.widget(), 'closeEvent'):
self.widget().closeEvent(event)
super().closeEvent(event)
present = present