mirror of https://github.com/chriskiehl/Gooey.git
2 changed files with 257 additions and 0 deletions
Unified View
Diff Options
@ -0,0 +1,257 @@ |
|||||
|
""" |
||||
|
Primary orchestration and control point for Gooey. |
||||
|
""" |
||||
|
|
||||
|
import sys |
||||
|
from itertools import chain |
||||
|
|
||||
|
import wx |
||||
|
|
||||
|
from gooey.gui import events |
||||
|
from gooey.gui.components.header import FrameHeader |
||||
|
from gooey.gui.components.footer import Footer |
||||
|
from gooey.gui.util import wx_util |
||||
|
from gooey.gui.components.config import ConfigPage, TabbedConfigPage |
||||
|
from gooey.gui.components.sidebar import Sidebar |
||||
|
from gooey.gui.components.tabbar import Tabbar |
||||
|
from gooey.util.functional import getin, assoc, flatmap, compact |
||||
|
from gooey.python_bindings import constants |
||||
|
from gooey.gui.pubsub import pub |
||||
|
from gooey.gui import cli |
||||
|
from gooey.gui.components.console import Console |
||||
|
from gooey.gui.lang.i18n import _ |
||||
|
from gooey.gui.processor import ProcessController |
||||
|
from gooey.gui.util.wx_util import transactUI |
||||
|
from gooey.gui.components import modals |
||||
|
from gooey.gui import seeder |
||||
|
|
||||
|
|
||||
|
class GooeyApplication(wx.Frame): |
||||
|
""" |
||||
|
Main window for Gooey. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, buildSpec, *args, **kwargs): |
||||
|
super(GooeyApplication, self).__init__(None, *args, **kwargs) |
||||
|
self._state = {} |
||||
|
self.buildSpec = buildSpec |
||||
|
|
||||
|
self.header = FrameHeader(self, buildSpec) |
||||
|
self.configs = self.buildConfigPanels(self) |
||||
|
self.navbar = self.buildNavigation() |
||||
|
self.footer = Footer(self, buildSpec) |
||||
|
self.console = Console(self, buildSpec) |
||||
|
self.layoutComponent() |
||||
|
|
||||
|
self.clientRunner = ProcessController( |
||||
|
self.buildSpec.get('progress_regex'), |
||||
|
self.buildSpec.get('progress_expr'), |
||||
|
self.buildSpec.get('encoding') |
||||
|
) |
||||
|
|
||||
|
pub.subscribe(events.WINDOW_START, self.onStart) |
||||
|
pub.subscribe(events.WINDOW_RESTART, self.onStart) |
||||
|
pub.subscribe(events.WINDOW_STOP, self.onStopExecution) |
||||
|
pub.subscribe(events.WINDOW_CLOSE, self.onClose) |
||||
|
pub.subscribe(events.WINDOW_CANCEL, self.onCancel) |
||||
|
pub.subscribe(events.WINDOW_EDIT, self.onEdit) |
||||
|
pub.subscribe(events.CONSOLE_UPDATE, self.console.logOutput) |
||||
|
pub.subscribe(events.EXECUTION_COMPLETE, self.onComplete) |
||||
|
pub.subscribe(events.PROGRESS_UPDATE, self.footer.updateProgressBar) |
||||
|
# Top level wx close event |
||||
|
self.Bind(wx.EVT_CLOSE, self.onClose) |
||||
|
|
||||
|
if self.buildSpec['poll_external_updates']: |
||||
|
self.fetchExternalUpdates() |
||||
|
|
||||
|
if self.buildSpec.get('auto_start', False): |
||||
|
self.onStart() |
||||
|
|
||||
|
|
||||
|
def onStart(self, *args, **kwarg): |
||||
|
""" |
||||
|
Verify user input and kick off the client's program if valid |
||||
|
""" |
||||
|
with transactUI(self): |
||||
|
config = self.navbar.getActiveConfig() |
||||
|
config.resetErrors() |
||||
|
if config.isValid(): |
||||
|
self.clientRunner.run(self.buildCliString()) |
||||
|
self.showConsole() |
||||
|
else: |
||||
|
config.displayErrors() |
||||
|
self.Layout() |
||||
|
|
||||
|
|
||||
|
def onEdit(self): |
||||
|
"""Return the user to the settings screen for further editing""" |
||||
|
with transactUI(self): |
||||
|
if self.buildSpec['poll_external_updates']: |
||||
|
self.fetchExternalUpdates() |
||||
|
self.showSettings() |
||||
|
|
||||
|
|
||||
|
def buildCliString(self): |
||||
|
""" |
||||
|
Collect all of the required information from the config screen and |
||||
|
build a CLI string which can be used to invoke the client program |
||||
|
""" |
||||
|
config = self.navbar.getActiveConfig() |
||||
|
group = self.buildSpec['widgets'][self.navbar.getSelectedGroup()] |
||||
|
positional = config.getPositionalArgs() |
||||
|
optional = config.getOptionalArgs() |
||||
|
print(cli.buildCliString( |
||||
|
self.buildSpec['target'], |
||||
|
group['command'], |
||||
|
positional, |
||||
|
optional |
||||
|
)) |
||||
|
return cli.buildCliString( |
||||
|
self.buildSpec['target'], |
||||
|
group['command'], |
||||
|
positional, |
||||
|
optional |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def onComplete(self, *args, **kwargs): |
||||
|
""" |
||||
|
Display the appropriate screen based on the success/fail of the |
||||
|
host program |
||||
|
""" |
||||
|
with transactUI(self): |
||||
|
if self.clientRunner.was_success(): |
||||
|
self.showSuccess() |
||||
|
if self.buildSpec.get('show_success_modal', True): |
||||
|
wx.CallAfter(modals.showSuccess) |
||||
|
else: |
||||
|
if self.clientRunner.wasForcefullyStopped: |
||||
|
self.showForceStopped() |
||||
|
else: |
||||
|
self.showError() |
||||
|
wx.CallAfter(modals.showFailure) |
||||
|
|
||||
|
|
||||
|
def onStopExecution(self): |
||||
|
"""Displays a scary message and then force-quits the executing |
||||
|
client code if the user accepts""" |
||||
|
if self.buildSpec['show_stop_warning'] and modals.confirmForceStop(): |
||||
|
self.clientRunner.stop() |
||||
|
|
||||
|
|
||||
|
def fetchExternalUpdates(self): |
||||
|
""" |
||||
|
!Experimental! |
||||
|
Calls out to the client code requesting seed values to use in the UI |
||||
|
!Experimental! |
||||
|
""" |
||||
|
seeds = seeder.fetchDynamicProperties( |
||||
|
self.buildSpec['target'], |
||||
|
self.buildSpec['encoding'] |
||||
|
) |
||||
|
for config in self.configs: |
||||
|
config.seedUI(seeds) |
||||
|
|
||||
|
|
||||
|
def onCancel(self): |
||||
|
"""Close the program after confirming""" |
||||
|
if modals.confirmExit(): |
||||
|
self.onClose() |
||||
|
|
||||
|
|
||||
|
def onClose(self, *args, **kwargs): |
||||
|
"""Cleanup the top level WxFrame and shutdown the process""" |
||||
|
self.Destroy() |
||||
|
sys.exit() |
||||
|
|
||||
|
|
||||
|
def layoutComponent(self): |
||||
|
sizer = wx.BoxSizer(wx.VERTICAL) |
||||
|
sizer.Add(self.header, 0, wx.EXPAND) |
||||
|
sizer.Add(wx_util.horizontal_rule(self), 0, wx.EXPAND) |
||||
|
|
||||
|
sizer.Add(self.navbar, 1, wx.EXPAND) |
||||
|
sizer.Add(self.console, 1, wx.EXPAND) |
||||
|
sizer.Add(wx_util.horizontal_rule(self), 0, wx.EXPAND) |
||||
|
sizer.Add(self.footer, 0, wx.EXPAND) |
||||
|
self.SetMinSize((400, 300)) |
||||
|
self.SetSize(self.buildSpec['default_size']) |
||||
|
self.SetSizer(sizer) |
||||
|
self.console.Hide() |
||||
|
self.Layout() |
||||
|
self.SetIcon(wx.Icon(self.buildSpec['images']['programIcon'], wx.BITMAP_TYPE_ICO)) |
||||
|
|
||||
|
|
||||
|
|
||||
|
def buildNavigation(self): |
||||
|
""" |
||||
|
Chooses the appropriate layout navigation component based on user prefs |
||||
|
""" |
||||
|
if self.buildSpec['navigation'] == constants.TABBED: |
||||
|
navigation = Tabbar(self, self.buildSpec, self.configs) |
||||
|
else: |
||||
|
navigation = Sidebar(self, self.buildSpec, self.configs) |
||||
|
if self.buildSpec['navigation'] == constants.HIDDEN: |
||||
|
navigation.Hide() |
||||
|
return navigation |
||||
|
|
||||
|
|
||||
|
def buildConfigPanels(self, parent): |
||||
|
page_class = TabbedConfigPage if self.buildSpec['tabbed_groups'] else ConfigPage |
||||
|
|
||||
|
return [page_class(parent, widgets) |
||||
|
for widgets in self.buildSpec['widgets'].values()] |
||||
|
|
||||
|
|
||||
|
def showSettings(self): |
||||
|
self.navbar.Show(True) |
||||
|
self.console.Show(False) |
||||
|
self.header.setImage('settings_img') |
||||
|
self.header.setTitle(_("settings_title")) |
||||
|
self.header.setSubtitle(self.buildSpec['program_description']) |
||||
|
self.footer.showButtons('cancel_button', 'start_button') |
||||
|
|
||||
|
|
||||
|
def showConsole(self): |
||||
|
self.navbar.Show(False) |
||||
|
self.console.Show(True) |
||||
|
self.header.setImage('running_img') |
||||
|
self.header.setTitle(_("running_title")) |
||||
|
self.header.setSubtitle(_('running_msg')) |
||||
|
self.footer.showButtons('stop_button') |
||||
|
self.footer.progress_bar.Show(True) |
||||
|
if not self.buildSpec['progress_regex']: |
||||
|
self.footer.progress_bar.Pulse() |
||||
|
|
||||
|
|
||||
|
def showComplete(self): |
||||
|
self.navbar.Show(False) |
||||
|
self.console.Show(True) |
||||
|
self.footer.showButtons('edit_button', 'restart_button', 'close_button') |
||||
|
self.footer.progress_bar.Show(False) |
||||
|
|
||||
|
|
||||
|
def showSuccess(self): |
||||
|
self.showComplete() |
||||
|
self.header.setImage('check_mark') |
||||
|
self.header.setTitle(_('finished_title')) |
||||
|
self.header.setSubtitle(_('finished_msg')) |
||||
|
self.Layout() |
||||
|
|
||||
|
|
||||
|
def showError(self): |
||||
|
self.showComplete() |
||||
|
self.header.setImage('error_symbol') |
||||
|
self.header.setTitle(_('finished_title')) |
||||
|
self.header.setSubtitle(_('finished_error')) |
||||
|
|
||||
|
|
||||
|
def showForceStopped(self): |
||||
|
self.showComplete() |
||||
|
if self.buildSpec.get('force_stop_is_error', True): |
||||
|
self.showError() |
||||
|
else: |
||||
|
self.showSuccess() |
||||
|
self.header.setSubtitle(_('finished_forced_quit')) |
||||
|
|
||||
|
|
xxxxxxxxxx