Coverage for src/scrilla/gui/widgets/menu.py: 0%

135 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-07-18 18:14 +0000

1# This file is part of scrilla: https://github.com/chinchalinchin/scrilla. 

2 

3# scrilla is free software: you can redistribute it and/or modify 

4# it under the terms of the GNU General Public License version 3 

5# as published by the Free Software Foundation. 

6 

7# scrilla is distributed in the hope that it will be useful, 

8# but WITHOUT ANY WARRANTY; without even the implied warranty of 

9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

10# GNU General Public License for more details. 

11 

12# You should have received a copy of the GNU General Public License 

13# along with scrilla. If not, see <https://www.gnu.org/licenses/> 

14# or <https://github.com/chinchalinchin/scrilla/blob/develop/main/LICENSE>. 

15 

16import sys 

17 

18from PySide6 import QtCore, QtWidgets, QtGui 

19 

20from scrilla import settings 

21 

22from scrilla.gui import utilities, definitions 

23from scrilla.gui.widgets import factories, functions 

24 

25# NOTE: widget_buttons and function_widgets must preserve order. 

26 

27def _to_class(name: str): 

28 if name == 'correlation': 

29 return functions.CorrelationWidget 

30 if name == 'plot_return_dist': 

31 return functions.DistributionWidget 

32 if name == 'yield_curve': 

33 return functions.YieldCurveWidget 

34 if name == 'discount_dividend': 

35 return functions.DiscountDividendWidget 

36 if name == 'risk_profile': 

37 return functions.RiskProfileWidget 

38 if name == 'optimize_portfolio': 

39 return functions.OptimizerWidget 

40 if name == 'efficient_frontier': 

41 return functions.EfficientFrontierWidget 

42 if name == 'moving_averages': 

43 return functions.MovingAverageWidget 

44 

45class MenuWidget(QtWidgets.QWidget): 

46 """ 

47 

48 .. notes:: 

49 * Widget Hierarchy:  

50 

51 .. links:: 

52 * [Stack Overflow: Weird Lambda Behavior in Loops](https://stackoverflow.com/questions/19768456/weird-lambda-behaviour-in-loops) 

53 """ 

54 

55 def __init__(self): 

56 super().__init__() 

57 self._init_menu_widgets() 

58 self._generate_menu_bar() 

59 self._generate_splash() 

60 self._arrange_menu_widgets() 

61 self._stage_menu_widgets() 

62 

63 def _generate_menu_bar(self): 

64 self.menu_bar = factories.atomic_widget_factory( 

65 component='menu-bar') 

66 self.menus = [] 

67 

68 for j, menu in enumerate(definitions.MENUBAR_WIDGET): 

69 self.menus.append(self.menu_bar.addMenu(menu)) 

70 for i, action in enumerate(definitions.MENUBAR_WIDGET[menu]): 

71 q_action = QtGui.QAction(action['name'], self) 

72 q_action.setShortcut(action['shortcut']) 

73 if menu == 'Functions': 

74 q_action.triggered.connect( 

75 (lambda i: lambda: self._show_widget(i))(i) 

76 ) 

77 elif menu == 'Account': 

78 if action['name'] == 'Add API Key': 

79 q_action.triggered.connect( 

80 (lambda action: lambda: 

81 self._show_api_key_dialog(action['options']))(action) 

82 ) 

83 elif menu == 'View': 

84 if action['name'] == 'Function Menu': 

85 q_action.triggered.connect(lambda: self.function_menu.setVisible( 

86 (not self.function_menu.isVisible()))) 

87 elif action['name'] == 'Splash Menu': 

88 q_action.triggered.connect(self._clear) 

89 

90 self.menus[j].addAction(q_action) 

91 

92 def _generate_splash(self): 

93 self.splash_container = factories.layout_factory(layout='vertical-box') 

94 self.source_button = factories.atomic_widget_factory( 

95 component='source-button', title=None) 

96 self.package_button = factories.atomic_widget_factory( 

97 component='package-button', title=None) 

98 self.documentation_button = factories.atomic_widget_factory( 

99 component='documentation-button', title=None) 

100 self.splash = factories.atomic_widget_factory( 

101 component='splash', title=None) 

102 

103 self.splash_button_panel = factories.layout_factory( 

104 layout='horizontal-box') 

105 

106 self.splash_button_panel.layout().addStretch() 

107 self.splash_button_panel.layout().addWidget(self.source_button) 

108 self.splash_button_panel.layout().addWidget(self.package_button) 

109 self.splash_button_panel.layout().addWidget(self.documentation_button) 

110 self.splash_button_panel.layout().addStretch() 

111 

112 def _init_menu_widgets(self): 

113 self.setObjectName('root') 

114 self.title = factories.atomic_widget_factory( 

115 component='title', title=settings.APP_NAME) 

116 

117 self.function_menu = factories.layout_factory(layout='vertical-box') 

118 self.function_menu.setObjectName('grand-child') 

119 self.function_title_container = factories.layout_factory( 

120 layout='horizontal-box') 

121 self.function_title = factories.atomic_widget_factory( 

122 component='heading', title='Functions') 

123 self.function_hide_button = factories.atomic_widget_factory( 

124 component='hide-button', title=None) 

125 

126 self.widget_buttons = [factories.atomic_widget_factory( 

127 component='button', title=function['name']) for function in definitions.FUNC_WIDGETS.values()] 

128 self.exit_button = factories.atomic_widget_factory( 

129 component='button', title="Exit") 

130 

131 self.function_widgets = [_to_class(function)( 

132 'great-grand-child', self) for function in definitions.FUNC_WIDGETS] 

133 

134 self.display_pane = factories.layout_factory(layout='vertical-box') 

135 self.display_pane.setObjectName('grand-child') 

136 

137 self.container_pane = factories.layout_factory(layout='horizontal-box') 

138 self.container_pane.setObjectName('child') 

139 

140 self.setLayout(QtWidgets.QVBoxLayout()) 

141 

142 def _arrange_menu_widgets(self): 

143 """Arranges child widget within their layouts.""" 

144 self.title.setAlignment(QtCore.Qt.AlignHCenter) 

145 

146 self.container_pane.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, 

147 QtWidgets.QSizePolicy.Expanding)) 

148 self.function_menu.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, 

149 QtWidgets.QSizePolicy.Expanding)) 

150 self.display_pane.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, 

151 QtWidgets.QSizePolicy.Expanding)) 

152 self.title.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, 

153 QtWidgets.QSizePolicy.Minimum)) 

154 

155 self.function_title_container.layout().addWidget(self.function_title) 

156 self.function_title_container.layout().addWidget(self.function_hide_button) 

157 

158 self.function_menu.layout().addWidget(self.function_title_container) 

159 for button in self.widget_buttons: 

160 self.function_menu.layout().addWidget(button) 

161 self.function_menu.layout().addStretch() 

162 self.function_menu.layout().addWidget(self.exit_button) 

163 

164 self.splash_container.layout().addWidget(self.splash) 

165 self.splash_container.layout().addStretch() 

166 self.splash_container.layout().addWidget(self.splash_button_panel) 

167 

168 self.display_pane.layout().addWidget(self.splash_container) 

169 for widget in self.function_widgets: 

170 self.display_pane.layout().addWidget(widget) 

171 

172 self.container_pane.layout().addWidget(self.function_menu) 

173 self.container_pane.layout().addWidget(self.display_pane) 

174 

175 self.layout().addWidget(self.menu_bar) 

176 self.layout().addWidget(self.title) 

177 self.layout().addWidget(self.container_pane) 

178 

179 def _stage_menu_widgets(self): 

180 for i, button in enumerate(self.widget_buttons): 

181 button.show() 

182 # see #NOTE 

183 button.clicked.connect((lambda i: lambda: self._show_widget(i))(i)) 

184 

185 for widget in self.function_widgets: 

186 widget.hide() 

187 

188 self.function_hide_button.clicked.connect( 

189 lambda: utilities.switch_visibility(self.function_menu)) 

190 self.exit_button.clicked.connect(self.close) 

191 self.source_button.clicked.connect( 

192 lambda: utilities.open_browser(utilities.get_metadata('source'))) 

193 self.package_button.clicked.connect( 

194 lambda: utilities.open_browser(utilities.get_metadata('package'))) 

195 self.documentation_button.clicked.connect( 

196 lambda: utilities.open_browser(utilities.get_metadata('documentation'))) 

197 self.exit_button.show() 

198 self.menu_bar.show() 

199 

200 def _clear(self): 

201 for widget in self.function_widgets: 

202 widget.hide() 

203 self.splash_container.show() 

204 

205 @QtCore.Slot() 

206 def _show_widget(self, widget_index): 

207 self.splash_container.hide() 

208 for widget in self.function_widgets: 

209 widget.hide() 

210 self.function_widgets[widget_index].show() 

211 self.title.setText(list(definitions.FUNC_WIDGETS.values())[ 

212 widget_index]['name']) 

213 

214 @staticmethod 

215 def _show_api_key_dialog(options): 

216 dialog = factories.dialog_widget_factory('api-key-dialog', options) 

217 dialog.exec_() 

218 

219 

220if __name__ == "__main__": 

221 app = QtWidgets.QApplication([]) 

222 

223 mainWidget = MenuWidget() 

224 mainWidget.resize(settings.GUI_WIDTH, settings.GUI_HEIGHT) 

225 mainWidget.show() 

226 

227 sys.exit(app.exec_())