widgets.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. from PyQt5.QtWidgets import *
  2. from PyQt5.QtCore import *
  3. import timetable_calendar
  4. from PyQt5.QtGui import *
  5. from typing import *
  6. import profiles
  7. import datetime
  8. import random
  9. def _get_contrast_text_color(background_value_float: float) -> QColor:
  10. if background_value_float > 0.5:
  11. return QColor('#000')
  12. else:
  13. return QColor('#FFF')
  14. class SectionFrame(QFrame):
  15. def __init__(self, *arguments) -> None:
  16. super().__init__(*arguments)
  17. self.setFrameStyle(QFrame.Shape.StyledPanel | QFrame.Shadow.Plain)
  18. class Spacer(QWidget):
  19. def __init__(self, *arguments) -> None:
  20. super().__init__(*arguments)
  21. self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
  22. class Header(QLabel):
  23. def __init__(self, text: str = '', color: QColor = None) -> None:
  24. super().__init__(text)
  25. self.setTextFormat(Qt.TextFormat.MarkdownText)
  26. self.setAlignment(Qt.AlignmentFlag.AlignCenter)
  27. self.setContentsMargins(4, 4, 4, 4)
  28. self.setAutoFillBackground(True)
  29. the_palette = self.palette()
  30. if color is None:
  31. the_palette.setColor(self.backgroundRole(), QApplication.palette().highlight().color())
  32. the_palette.setColor(self.foregroundRole(), QApplication.palette().highlightedText().color())
  33. else:
  34. the_palette.setColor(self.backgroundRole(), color)
  35. the_palette.setColor(self.foregroundRole(), _get_contrast_text_color(color.valueF()))
  36. self.setPalette(the_palette)
  37. class ReactiveCalendarWidget(QCalendarWidget):
  38. def paintCell(self, painter: QPainter, rect: QRect, date: Union[QDate, datetime.date]) -> None:
  39. super().paintCell(painter, rect, date)
  40. profile_id = timetable_calendar.get_profile_id(date)
  41. if profile_id is None:
  42. return
  43. profile = profiles.get(profile_id)
  44. if profile is None:
  45. print(f'Не найден профиль с ID: {profile_id}')
  46. return
  47. color: QColor = profile['color']
  48. step = min(rect.width(), rect.height()) / 3
  49. start_position = QPointF(rect.right() + 1, rect.bottom() + 1)
  50. path = QPainterPath(start_position)
  51. path.lineTo(start_position - QPointF(0, step))
  52. path.lineTo(start_position - QPointF(step, 0))
  53. path.lineTo(start_position)
  54. painter.fillPath(path, color)
  55. class VerticalScrollArea(QScrollArea):
  56. def resizeEvent(self, event: QResizeEvent) -> None:
  57. width = self.widget().minimumSizeHint().width()
  58. if self.verticalScrollBar().isVisible():
  59. width += self.verticalScrollBar().width()
  60. self.setMinimumWidth(width)
  61. return super().resizeEvent(event)
  62. class ClickableQWidget(QWidget):
  63. def __init__(self) -> None:
  64. super().__init__()
  65. self.click_callback = None
  66. def mousePressEvent(self, event: QMouseEvent) -> None:
  67. if self.click_callback is not None:
  68. self.click_callback()
  69. return super().mousePressEvent(event)
  70. def setClickCallback(self, callback: Callable[[], Any]) -> None:
  71. self.click_callback = callback
  72. class HighlightableWidget(ClickableQWidget):
  73. def __init__(self, unique_name: str = str(random.randint(0, 1024))) -> None:
  74. super().__init__()
  75. unique_name = unique_name.replace(' ', '-')
  76. unique_name = unique_name.replace('.', '-dot-')
  77. unique_name = unique_name.replace(',', '-comma-')
  78. highlight_color = QApplication.palette().base().color().name()
  79. stylesheet = f'ClickableQWidget#{unique_name}:hover {{ background-color: {highlight_color}; }}'
  80. self.setObjectName(unique_name)
  81. self.setAccessibleName(unique_name)
  82. self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground)
  83. self.setStyleSheet(stylesheet)
  84. class NotScrollableTimeEdit(QTimeEdit):
  85. def wheelEvent(self, event: QWheelEvent) -> None:
  86. return None
  87. class DisconnectableTimeEdit(QTimeEdit):
  88. def __init__(self, time: QTime = QTime(), parent: Union[QWidget, None] = None) -> None:
  89. super().__init__(time, parent)
  90. self.connected_function = None
  91. def disconnect_on_time_changed(self) -> None:
  92. if self.connected_function is not None:
  93. self.timeChanged.disconnect(self.connected_function)
  94. def connect_on_time_changed(self, function) -> None:
  95. self.connected_function = function
  96. self.timeChanged.connect(function)
  97. class DisconnectableLineEdit(QLineEdit):
  98. def __init__(
  99. self,
  100. contents: str = '',
  101. parent: Union[QWidget, None] = None,
  102. icon: QIcon = None,
  103. fade_initial_icon: bool = False
  104. ) -> None:
  105. super().__init__(contents, parent)
  106. self.connected_function = None
  107. self.icon = icon
  108. self.fade_amount = 1.0
  109. self.animation_timer: QTimer = None
  110. self.set_icon(icon, fade_initial_icon)
  111. def disconnect_on_text_changed(self) -> None:
  112. if self.connected_function is not None:
  113. self.textChanged.disconnect(self.connected_function)
  114. def connect_on_text_changed(self, function) -> None:
  115. self.connected_function = function
  116. self.textChanged.connect(function)
  117. def _perform_animation(self):
  118. self.fade_amount -= 0.001 / 2
  119. if self.fade_amount <= 0:
  120. self.set_icon(None)
  121. self.repaint()
  122. def set_icon(self, icon: Union[QIcon, None], fade: bool = False) -> None:
  123. if self.animation_timer is not None:
  124. self.animation_timer.stop()
  125. self.animation_timer = None
  126. self.fade_amount = 1.0
  127. if icon is None:
  128. self.setTextMargins(1, 1, 1, 1)
  129. else:
  130. self.setTextMargins(1, 1, 20, 1)
  131. self.icon = icon
  132. if not fade or icon is None:
  133. return
  134. self.animation_timer = QTimer(self)
  135. self.animation_timer.timeout.connect(self._perform_animation)
  136. self.animation_timer.start(1)
  137. print('Animation timer started!')
  138. def paintEvent(self, event: QPaintEvent) -> None:
  139. super().paintEvent(event)
  140. if self.icon is None:
  141. return
  142. pixmap = self.icon.pixmap(self.height() - 10, self.height() - 10)
  143. painter = QPainter(self)
  144. painter.setOpacity(self.fade_amount)
  145. painter.drawPixmap(self.width() - pixmap.width() - 5, 5, pixmap)
  146. class CachingDisconnectableLineEdit(DisconnectableLineEdit):
  147. def __init__(
  148. self,
  149. contents: str = '',
  150. cached_value: str = '',
  151. parent: Union[QWidget, None] = None,
  152. icon: QIcon = None
  153. ) -> None:
  154. super().__init__(contents, parent, icon)
  155. if cached_value is None or cached_value == '':
  156. self.cached_value = contents
  157. else:
  158. self.cached_value = cached_value
  159. def reset_contents(self):
  160. self.setText(self.cached_value)
  161. self.clearFocus()
  162. def setText(self, text: str) -> None:
  163. super().setText(text)
  164. self.cached_value = text
  165. def apply_changes(self):
  166. self.setText(self.text())