Skip to content

Commit

Permalink
Added custom title bar to PymeadDialog
Browse files Browse the repository at this point in the history
  • Loading branch information
mlau154 committed Jan 28, 2024
1 parent 46cd08e commit 0975d34
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 13 deletions.
4 changes: 2 additions & 2 deletions pymead/gui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -1282,9 +1282,9 @@ def multi_airfoil_analysis_setup(self):
self.dialog = MultiAirfoilDialog(
parent=self, geo_col=self.geo_col, settings_override=self.multi_airfoil_analysis_settings
)
self.dialog.show()
self.dialog.accepted.connect(self.multi_airfoil_analysis_accepted)
self.dialog.rejected.connect(self.multi_airfoil_analysis_rejected)
self.dialog.exec_()

def multi_airfoil_analysis_accepted(self):

Expand Down Expand Up @@ -1468,9 +1468,9 @@ def plot_airfoil_from_airfoiltools(self):
def setup_optimization(self):
self.dialog = OptimizationSetupDialog(self, settings_override=self.opt_settings,
geo_col=self.geo_col)
self.dialog.show()
self.dialog.accepted.connect(self.optimization_accepted)
self.dialog.rejected.connect(self.optimization_rejected)
self.dialog.exec_()

def optimization_accepted(self):
exit_the_dialog = False
Expand Down
1 change: 1 addition & 0 deletions pymead/gui/gui_settings/themes/dark_theme.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"theme-name": "dark",
"background-color": "#3e3f40",
"main-color": "#dce1e6",
"dock-widget-background-color": "#2a2a2b",
Expand Down
1 change: 1 addition & 0 deletions pymead/gui/gui_settings/themes/light_theme.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"theme-name": "light",
"background-color": "#ffffff",
"main-color": "#000000",
"dock-widget-background-color": "#faf9f6",
Expand Down
71 changes: 68 additions & 3 deletions pymead/gui/input_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
import PyQt5.QtWidgets
import numpy as np
import pyqtgraph as pg
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QFormLayout, QDoubleSpinBox, QComboBox, QSpinBox, \
QTabWidget, QLabel, QMessageBox, QCheckBox, QVBoxLayout, QWidget, QGridLayout, QPushButton, QListView, QRadioButton
from PyQt5.QtCore import QEvent, Qt, QObject
from PyQt5.QtWidgets import (QDialog, QDialogButtonBox, QFormLayout, QDoubleSpinBox, QComboBox, QSpinBox, \
QTabWidget, QLabel, QMessageBox, QCheckBox, QVBoxLayout, QWidget, QGridLayout, QPushButton, QListView, QRadioButton,
QSizeGrip)
from PyQt5.QtCore import QEvent, Qt, QObject, QRect
from PyQt5.QtGui import QStandardItem, QStandardItemModel, QFont
import tempfile
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QStandardPaths
Expand All @@ -24,6 +25,8 @@
from pymead.gui.scientificspinbox_master.ScientificDoubleSpinBox import ScientificDoubleSpinBox
from pymead.gui.file_selection import *
from pymead.gui.separation_lines import QHSeperationLine
from pymead.gui.side_grip import SideGrip
from pymead.gui.title_bar import DialogTitleBar
from pymead.utils.widget_recursion import get_parent
from pymead.utils.read_write_files import load_data, save_data, load_documents_path
from pymead.utils.dict_recursion import recursive_get
Expand Down Expand Up @@ -2041,10 +2044,14 @@ def valuesFromWidgets(self):


class PymeadDialog(QDialog):

_gripSize = 2

"""This subclass of QDialog forces the selection of a WindowTitle and matches the visual format of the GUI"""
def __init__(self, parent, window_title: str, widget: PymeadDialogWidget or PymeadDialogVTabWidget):
super().__init__(parent=parent)
self.setWindowTitle(window_title)
self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)
if self.parent() is not None:
self.setFont(self.parent().font())

Expand All @@ -2055,6 +2062,24 @@ def __init__(self, parent, window_title: str, widget: PymeadDialogWidget or Pyme
self.layout.addWidget(widget)
self.layout.addWidget(self.create_button_box())

# mandatory for cursor updates
self.setMouseTracking(True)

self.title_bar = DialogTitleBar(self, theme=self.parent().themes[self.parent().current_theme])

self.sideGrips = [
SideGrip(self, Qt.LeftEdge),
SideGrip(self, Qt.TopEdge),
SideGrip(self, Qt.RightEdge),
SideGrip(self, Qt.BottomEdge),
]
# corner grips should be "on top" of everything, otherwise the side grips
# will take precedence on mouse events, so we are adding them *after*;
# alternatively, widget.raise_() can be used
self.cornerGrips = [QSizeGrip(self) for _ in range(4)]

self.resize(self.width(), self.title_bar.height() + self.height())

# def setInputs(self):
# self.w.setInputs()

Expand All @@ -2076,6 +2101,46 @@ def create_button_box(self):
buttonBox.rejected.connect(self.reject)
return buttonBox

@property
def gripSize(self):
return self._gripSize

def setGripSize(self, size):
if size == self._gripSize:
return
self._gripSize = max(2, size)
self.updateGrips()

def updateGrips(self):
self.setContentsMargins(self.gripSize, self.gripSize + self.title_bar.height(), self.gripSize, self.gripSize)

outRect = self.rect()
# an "inner" rect used for reference to set the geometries of size grips
inRect = outRect.adjusted(self.gripSize, self.gripSize, -self.gripSize, -self.gripSize)

# top left
self.cornerGrips[0].setGeometry(QRect(outRect.topLeft(), inRect.topLeft()))
# top right
self.cornerGrips[1].setGeometry(QRect(outRect.topRight(), inRect.topRight()).normalized())
# bottom right
self.cornerGrips[2].setGeometry(QRect(inRect.bottomRight(), outRect.bottomRight()))
# bottom left
self.cornerGrips[3].setGeometry(QRect(outRect.bottomLeft(), inRect.bottomLeft()).normalized())

# left edge
self.sideGrips[0].setGeometry(0, inRect.top(), self.gripSize, inRect.height())
# top edge
self.sideGrips[1].setGeometry(inRect.left(), 0, inRect.width(), self.gripSize)
# right edge
self.sideGrips[2].setGeometry(inRect.left() + inRect.width(), inRect.top(), self.gripSize, inRect.height())
# bottom edge
self.sideGrips[3].setGeometry(self.gripSize, inRect.top() + inRect.height(), inRect.width(), self.gripSize)

def resizeEvent(self, event):
self.title_bar.resize(self.width(), self.title_bar.height())
super().resizeEvent(event)
self.updateGrips()


class XFOILDialog(PymeadDialog):
def __init__(self, parent: QWidget, current_airfoils: typing.List[str], settings_override: dict = None):
Expand Down
137 changes: 129 additions & 8 deletions pymead/gui/title_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,30 @@


class TitleBarButton(QToolButton):
def __init__(self, parent, operation: str):
def __init__(self, parent, operation: str, theme: dict = None):
super().__init__(parent)

if operation not in ("minimize", "maximize", "normal", "close"):
raise ValueError("Invalid operation")

self.operation = operation
self.theme = theme

def hoverColor(self) -> str:
return self.parent().parent().themes[self.parent().parent().current_theme][f"{self.operation}-hover-color"]
if self.theme is not None:
return self.theme[f"{self.operation}-hover-color"]
else:
return self.parent().parent().themes[self.parent().parent().current_theme][f"{self.operation}-hover-color"]

def setColorDefault(self):
self.setIcon(QIcon(os.path.join(ICON_DIR, f"{self.operation}-{self.parent().parent().current_theme}-mode.svg")))
theme_name = self.theme["theme-name"] if self.theme is not None else self.parent().parent().current_theme
self.setIcon(QIcon(os.path.join(ICON_DIR, f"{self.operation}-{theme_name}-mode.svg")))
self.setStyleSheet(f"""QToolButton {{ border: none }}""")

def setColorHover(self):
if self.parent().parent().current_theme == "dark":
self.setIcon(QIcon(os.path.join(ICON_DIR, f"{self.operation}-light-mode.svg")))
theme_name = self.theme["theme-name"] if self.theme is not None else self.parent().parent().current_theme
if theme_name == "dark":
self.setIcon(QIcon(os.path.join(ICON_DIR, f"{self.operation}-{theme_name}-mode.svg")))
self.setStyleSheet(f"""QToolButton {{ background-color: {self.hoverColor()} }}""")

def enterEvent(self, a0):
Expand Down Expand Up @@ -171,6 +177,121 @@ def openGitHubPage(self):
if not QDesktopServices.openUrl(url):
self.sigMessage.emit("Could not open pymead's GitHub page", "error")

# def resizeEvent(self, event):
# self.title.resize(self.minButton.x(), self.height())
# self.updateTitle()

class DialogTitleBar(QWidget):
clickPos = None

sigMessage = pyqtSignal(str, str)

def __init__(self, parent, theme: dict):
super().__init__(parent)

self.guiWindowMaximized = False

self.setAutoFillBackground(True)

self.lay = QHBoxLayout(self)
self.lay.setContentsMargins(1, 1, 1, 1)

self.title = QLabel("Custom Title Bar", self)
self.title.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self.title.setFixedHeight(30)
self.title.setMinimumWidth(400)
self.title.setAutoFillBackground(True)

# style = self.style()
# ref_size = self.fontMetrics().height()
# ref_size += style.pixelMetric(style.PM_ButtonMargin) * 2
# self.setMaximumHeight(ref_size + 2)

# Add the pymead logo as a button that, when clicked, opens the pymead GitHub page in the machine's default
# browser
pymeadLogoButton = QToolButton(self)
pymeadLogoButton.setIcon(QIcon(os.path.join(ICON_DIR, "pymead-logo.png")))
pymeadLogoButton.setFixedSize(30, 30)
pymeadLogoButton.setIconSize(QSize(30, 30))
# pymeadLogoButton.setEnabled(False)

gripFillerWidget = QWidget(self)
gripFillerWidget.setFixedWidth(0)

self.lay.addWidget(gripFillerWidget)
self.lay.addWidget(pymeadLogoButton)
self.lay.addWidget(self.title)

# self.lay.addStretch()

self.minimizeButton = None
self.normalButton = None
self.maximizeButton = None
self.closeButton = None

btn_size = QSize(30, 30)
for target, picture, hover_color, tool_tip in zip(
('close',),
(f"close-{theme['theme-name']}-mode.svg",),
(theme["close-hover-color"],),
("Close",)
):
btn = TitleBarButton(self, operation=target, theme=theme)
btn.setFocusPolicy(Qt.NoFocus)
self.lay.addWidget(btn)
btn.setFixedSize(btn_size)
# btn.setIconSize(btn_size)

btn.setIcon(QIcon(os.path.join(ICON_DIR, picture)))
btn.setToolTip(tool_tip)

btn.setStyleSheet('''QToolButton { border: none }''')

signal = getattr(self, target + 'Clicked')
btn.clicked.connect(signal)

setattr(self, target + 'Button', btn)

gripFillerWidget = QWidget(self)
gripFillerWidget.setFixedWidth(0)

self.lay.addWidget(gripFillerWidget)

self.updateTitle(parent.windowTitle())
parent.windowTitleChanged.connect(self.updateTitle)

def updateTitle(self, title=None):
if title is None:
title = self.window().windowTitle()
width = self.title.width()
width -= self.style().pixelMetric(QStyle.PM_LayoutHorizontalSpacing) * 2
self.title.setText(self.fontMetrics().elidedText(
title, Qt.ElideRight, width))
# self.title.setStyleSheet("QLabel { color: blue; }")

def windowStateChanged(self, state):
self.normalButton.setVisible(state == Qt.WindowMaximized)
self.maximizeButton.setVisible(state != Qt.WindowMaximized)
self.guiWindowMaximized = state == Qt.WindowMaximized

def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and not self.guiWindowMaximized:
self.clickPos = event.windowPos().toPoint()
super().mousePressEvent(event)
event.accept()

def mouseMoveEvent(self, event):
if self.clickPos is not None and not self.guiWindowMaximized:
self.window().move(event.globalPos() - self.clickPos)
super().mouseMoveEvent(event)
event.accept()

def mouseReleaseEvent(self, event):
self.clickPos = None
super().mouseReleaseEvent(event)
event.accept()

def closeClicked(self):
self.window().close()

def openGitHubPage(self):
url = QUrl("https://github.com/mlau154/pymead")
if not QDesktopServices.openUrl(url):
self.sigMessage.emit("Could not open pymead's GitHub page", "error")

0 comments on commit 0975d34

Please sign in to comment.