Пример #1
0
    def show_upload_files_wizard(self):
        self.set_edit_buttons_enabled(False)
        self.set_program_callbacks_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)

        self.upload_files_wizard = ProgramWizardUpload(self, context, self.program, self.last_upload_files_wizard_directory)
        self.upload_files_wizard.exec_()

        self.last_upload_files_wizard_directory = self.upload_files_wizard.last_directory

        if self.upload_files_wizard.upload_successful:
            self.widget_files.refresh_files()

        self.upload_files_wizard = None

        if self.tab_is_alive:
            self.set_program_callbacks_enabled(True)
            self.set_edit_buttons_enabled(True)
Пример #2
0
    def show_upload_files_wizard(self):
        self.set_edit_buttons_enabled(False)
        self.set_program_callbacks_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)

        self.upload_files_wizard = ProgramWizardUpload(self, context, self.program, self.last_upload_files_wizard_directory)
        self.upload_files_wizard.exec_()

        self.last_upload_files_wizard_directory = self.upload_files_wizard.last_directory

        if self.upload_files_wizard.upload_successful:
            self.widget_files.refresh_files()

        self.upload_files_wizard = None

        if self.tab_is_alive:
            self.set_program_callbacks_enabled(True)
            self.set_edit_buttons_enabled(True)
Пример #3
0
class ProgramInfoMain(QWidget, Ui_ProgramInfoMain):
    name_changed = pyqtSignal()

    def __init__(self, session, script_manager, image_version, executable_versions, program):
        QWidget.__init__(self)

        self.setupUi(self)

        self.session             = session
        self.script_manager      = script_manager
        self.image_version       = image_version
        self.executable_versions = executable_versions
        self.program             = program
        self.root_directory      = self.program.root_directory

        self.last_upload_files_wizard_directory = get_home_path()

        self.program.scheduler_state_changed_callback = self.scheduler_state_changed
        self.program.process_spawned_callback         = self.process_spawned

        if self.program.last_spawned_process != None:
            self.program.last_spawned_process.state_changed_callback = self.process_state_changed

        self.first_show_event            = True
        self.tab_is_alive                = True
        self.program_refresh_in_progress = False

        self.edit_general_wizard   = None
        self.edit_language_wizard  = None
        self.edit_arguments_wizard = None
        self.edit_stdio_wizard     = None
        self.edit_schedule_wizard  = None
        self.upload_files_wizard   = None
        self.download_wizard       = None

        self.button_refresh.clicked.connect(self.refresh_info)

        self.button_start_program.clicked.connect(self.start_program)
        self.button_kill_process.clicked.connect(self.kill_process)
        self.button_continue_schedule.clicked.connect(self.continue_schedule)
        self.button_send_stdin_pipe_input.clicked.connect(self.send_stdin_pipe_input)

        self.check_show_environment.stateChanged.connect(self.update_ui_state)

        self.button_edit_general.clicked.connect(self.show_edit_general_wizard)
        self.button_edit_language.clicked.connect(self.show_edit_language_wizard)
        self.button_edit_arguments.clicked.connect(self.show_edit_arguments_wizard)
        self.button_edit_stdio.clicked.connect(self.show_edit_stdio_wizard)
        self.button_edit_schedule.clicked.connect(self.show_edit_schedule_wizard)

        # create language info widget
        language_api_name = self.program.cast_custom_option_value('language', unicode, '<unknown>')

        try:
            language = Constants.get_language(language_api_name)
        except:
            language = None

        if language != None:
            language_info_classes = {
                Constants.LANGUAGE_C:          ProgramInfoC,
                Constants.LANGUAGE_CSHARP:     ProgramInfoCSharp,
                Constants.LANGUAGE_DELPHI:     ProgramInfoDelphi,
                Constants.LANGUAGE_JAVA:       ProgramInfoJava,
                Constants.LANGUAGE_JAVASCRIPT: ProgramInfoJavaScript,
                Constants.LANGUAGE_OCTAVE:     ProgramInfoOctave,
                Constants.LANGUAGE_PERL:       ProgramInfoPerl,
                Constants.LANGUAGE_PHP:        ProgramInfoPHP,
                Constants.LANGUAGE_PYTHON:     ProgramInfoPython,
                Constants.LANGUAGE_RUBY:       ProgramInfoRuby,
                Constants.LANGUAGE_SHELL:      ProgramInfoShell,
                Constants.LANGUAGE_VBNET:      ProgramInfoVBNET
            }

            if language_info_classes[language] != None:
                context = ProgramInfoContext(self.session, self.script_manager, self.executable_versions, self.program)

                self.widget_language = language_info_classes[language](context)
                self.layout_language.addWidget(self.widget_language)
            else:
                self.widget_language = None
        else:
            self.widget_language = None

        self.current_language_action = None

        self.button_language_action.setVisible(False)

        def is_alive():
            return self.tab_is_alive

        # create logs info widget
        context = ProgramInfoContext(self.session, self.script_manager, self.executable_versions, self.program)

        self.widget_logs = ProgramInfoLogs(context, self.update_ui_state, self.set_widget_enabled, is_alive, self.show_download_wizard, self.set_program_callbacks_enabled)
        self.layout_logs.addWidget(self.widget_logs)

        # create files info widget
        context = ProgramInfoContext(self.session, self.script_manager, self.executable_versions, self.program)

        self.widget_files = ProgramInfoFiles(context, self.update_ui_state, self.set_widget_enabled, is_alive, self.show_upload_files_wizard, self.show_download_wizard)
        self.layout_files.addWidget(self.widget_files)

        self.update_ui_state()

    # override QWidget.showEvent
    def showEvent(self, event):
        if self.first_show_event:
            QTimer.singleShot(1, self.widget_logs.refresh_logs)
            QTimer.singleShot(1, self.widget_files.refresh_files)

            self.first_show_event = False

        self.set_program_callbacks_enabled(True)

        QWidget.showEvent(self, event)

    # override QWidget.hideEvent
    def hideEvent(self, event):
        self.set_program_callbacks_enabled(False)

        QWidget.hideEvent(self, event)

    def scheduler_state_changed(self, program):
        self.update_ui_state()

    def process_spawned(self, program):
        self.program.last_spawned_process.state_changed_callback = self.process_state_changed

        self.update_ui_state()

    def process_state_changed(self, process):
        self.update_ui_state()

    def close_all_dialogs(self):
        self.tab_is_alive = False

        if self.edit_general_wizard != None:
            self.edit_general_wizard.close()

        if self.edit_language_wizard != None:
            self.edit_language_wizard.close()

        if self.edit_arguments_wizard != None:
            self.edit_arguments_wizard.close()

        if self.edit_stdio_wizard != None:
            self.edit_stdio_wizard.close()

        if self.edit_schedule_wizard != None:
            self.edit_schedule_wizard.close()

        if self.upload_files_wizard != None:
            self.upload_files_wizard.close()

        if self.download_wizard != None:
            self.download_wizard.close()

        if self.widget_language != None:
            self.widget_language.close_all_dialogs()

        self.widget_logs.close_all_dialogs()
        self.widget_files.close_all_dialogs()

    def set_program_callbacks_enabled(self, enable):
        self.program.enable_callbacks = enable

        if enable:
            self.refresh_program()

    def refresh_info(self):
        self.refresh_program()
        self.widget_logs.refresh_logs()
        self.widget_files.refresh_files()

    def refresh_program(self):
        def refresh_async():
            self.program.update()

        def cb_success():
            self.program_refresh_in_progress = False
            self.update_ui_state()

        def cb_error():
            pass # FIXME: report error

        self.program_refresh_in_progress = True
        self.update_ui_state()

        async_call(refresh_async, None, cb_success, cb_error)

    def update_ui_state(self):
        any_refresh_in_progress = self.program_refresh_in_progress or \
                                  self.widget_logs.refresh_in_progress or \
                                  self.widget_files.refresh_in_progress

        if any_refresh_in_progress:
            self.progress.setVisible(True)
            self.button_refresh.setText('Refreshing...')
            self.set_edit_buttons_enabled(False)
        else:
            self.progress.setVisible(False)
            self.button_refresh.setText('Refresh')
            self.set_edit_buttons_enabled(True)

        # general
        name              = self.program.cast_custom_option_value('name', unicode, '<unknown>')
        language_api_name = self.program.cast_custom_option_value('language', unicode, '<unknown>')
        description       = self.program.cast_custom_option_value('description', unicode, '')
        first_upload      = self.program.cast_custom_option_value('first_upload', int, 0)
        last_edit         = self.program.cast_custom_option_value('last_edit', int, 0)

        try:
            language_display_name = Constants.get_language_display_name(language_api_name)
        except:
            language_display_name = '<unknown>'

        self.label_name.setText(name)
        self.label_identifier.setText(self.program.identifier)
        self.label_language.setText(language_display_name)
        self.label_description.setText(description)
        self.label_first_upload.setText(timestamp_to_date_at_time(first_upload))
        self.label_last_edit.setText(timestamp_to_date_at_time(last_edit))

        php_start_mode_web_interface    = False
        python_start_mode_web_interface = False
        javascript_flavor_browser       = False

        if language_api_name == 'php':
            php_start_mode_api_name      = self.program.cast_custom_option_value('php.start_mode', unicode, '<unknown>')
            php_start_mode               = Constants.get_php_start_mode(php_start_mode_api_name)
            php_start_mode_web_interface = php_start_mode == Constants.PHP_START_MODE_WEB_INTERFACE
        elif language_api_name == 'python':
            python_start_mode_api_name      = self.program.cast_custom_option_value('python.start_mode', unicode, '<unknown>')
            python_start_mode               = Constants.get_python_start_mode(python_start_mode_api_name)
            python_start_mode_web_interface = python_start_mode == Constants.PYTHON_START_MODE_WEB_INTERFACE
        elif language_api_name == 'javascript':
            javascript_flavor_api_name = self.program.cast_custom_option_value('javascript.flavor', unicode, '<unknown>')
            javascript_flavor          = Constants.get_javascript_flavor(javascript_flavor_api_name)
            javascript_flavor_browser  = javascript_flavor == Constants.JAVASCRIPT_FLAVOR_BROWSER

        show_status    = not php_start_mode_web_interface and not python_start_mode_web_interface and not javascript_flavor_browser
        show_logs      = not php_start_mode_web_interface and not python_start_mode_web_interface and not javascript_flavor_browser
        show_arguments = not php_start_mode_web_interface and not python_start_mode_web_interface and not javascript_flavor_browser
        show_stdio     = not php_start_mode_web_interface and not python_start_mode_web_interface and not javascript_flavor_browser
        show_schedule  = not php_start_mode_web_interface and not python_start_mode_web_interface and not javascript_flavor_browser

        # status
        self.group_status.setVisible(show_status)

        process_running = False

        if self.program.last_spawned_process != None:
            date_at_time = timestamp_to_date_at_time(self.program.last_spawned_process.timestamp)

            if self.program.last_spawned_process.state == REDProcess.STATE_UNKNOWN:
                self.label_program_current_state.setText('Unknown since {0}'.format(date_at_time))
            elif self.program.last_spawned_process.state == REDProcess.STATE_RUNNING:
                self.label_program_current_state.setText('Running since {0}'.format(date_at_time))
                process_running = True
            elif self.program.last_spawned_process.state == REDProcess.STATE_ERROR:
                if self.program.last_spawned_process.exit_code == REDProcess.E_INTERNAL_ERROR:
                    self.label_program_current_state.setText('Internal error occurred on {0}'.format(date_at_time))
                elif self.program.last_spawned_process.exit_code == REDProcess.E_CANNOT_EXECUTE:
                    self.label_program_current_state.setText('Could not be executed on {0}'.format(date_at_time))
                elif self.program.last_spawned_process.exit_code == REDProcess.E_DOES_NOT_EXIST:
                    self.label_program_current_state.setText('Executable does not exist on {0}'.format(date_at_time))
                else:
                    self.label_program_current_state.setText('Unknown error occurred on {0}'.format(date_at_time))
            elif self.program.last_spawned_process.state == REDProcess.STATE_EXITED:
                if self.program.last_spawned_process.exit_code == 0:
                    self.label_program_current_state.setText('Not running, last run exited normally on {0}'.format(date_at_time))
                else:
                    self.label_program_current_state.setText('Not running, last run exited with an error (exit code: {0}) on {1}'
                                                            .format(self.program.last_spawned_process.exit_code, date_at_time))
            elif self.program.last_spawned_process.state == REDProcess.STATE_KILLED:
                self.label_program_current_state.setText('Not running, last run was killed (signal: {0}) on {1}'
                                                         .format(self.program.last_spawned_process.exit_code, date_at_time))
            elif self.program.last_spawned_process.state == REDProcess.STATE_STOPPED:
                self.label_program_current_state.setText('Stopped on {0}'.format(date_at_time)) # FIXME: show continue button?

            self.label_last_program_start.setText(timestamp_to_date_at_time(self.program.last_spawned_timestamp))
        else:
            self.label_program_current_state.setText('Not running')
            self.label_last_program_start.setText('Never started')

        scheduler_stopped            = self.program.scheduler_state == REDProgram.SCHEDULER_STATE_STOPPED
        scheduler_state_display_name = Constants.api_scheduler_state_display_name.get(self.program.scheduler_state, '<unknown>')

        if scheduler_stopped:
            scheduler_state_display_name += ', no automatic program start'

        self.label_current_scheduler_state.setText(scheduler_state_display_name)
        self.label_last_scheduler_state_change.setText(timestamp_to_date_at_time(self.program.scheduler_timestamp))

        if self.program.scheduler_message != None:
            self.label_last_scheduler_message.setText(self.program.scheduler_message)
        else:
            self.label_last_scheduler_message.setText('None')

        self.set_widget_enabled(self.button_start_program, not any_refresh_in_progress and not process_running)
        self.set_widget_enabled(self.button_kill_process, not any_refresh_in_progress and process_running)
        self.set_widget_enabled(self.button_continue_schedule, not any_refresh_in_progress and scheduler_stopped and self.program.start_mode != REDProgram.START_MODE_NEVER)

        # logs
        self.group_logs.setVisible(show_logs)

        self.widget_logs.any_refresh_in_progress = any_refresh_in_progress
        self.widget_logs.update_ui_state()

        # files
        self.widget_files.any_refresh_in_progress = any_refresh_in_progress
        self.widget_files.update_ui_state()

        # language
        self.group_language.setTitle('{0} Configuration'.format(language_display_name))

        if self.widget_language != None:
            self.widget_language.update_ui_state()

            language_action, language_action_name = self.widget_language.get_language_action()

            if language_action != self.current_language_action:
                if self.current_language_action != None:
                    self.button_language_action.setVisible(False)
                    self.button_language_action.clicked.disconnect(self.current_language_action)

                if language_action != None:
                    self.button_language_action.clicked.connect(language_action)
                    self.button_language_action.setText(language_action_name)
                    self.button_language_action.setVisible(True)

                self.current_language_action = language_action

        # arguments
        self.group_arguments.setVisible(show_arguments)

        arguments = []
        editable_arguments_offset = max(self.program.cast_custom_option_value('editable_arguments_offset', int, 0), 0)

        for argument in self.program.arguments.items[editable_arguments_offset:]:
            arguments.append(argument)

        self.label_arguments.setText('\n'.join(arguments))

        environment = []
        editable_environment_offset = max(self.program.cast_custom_option_value('editable_environment_offset', int, 0), 0)

        for variable in self.program.environment.items[editable_environment_offset:]:
            environment.append(variable)

        show_environment = self.check_show_environment.isChecked()

        self.label_environment_title.setVisible(show_environment)
        self.label_environment.setVisible(show_environment)
        self.label_environment.setText('\n'.join(environment))

        # stdio
        self.group_stdio.setVisible(show_stdio)

        stdin_redirection_pipe  = self.program.stdin_redirection  == REDProgram.STDIO_REDIRECTION_PIPE
        stdin_redirection_file  = self.program.stdin_redirection  == REDProgram.STDIO_REDIRECTION_FILE
        stdout_redirection_file = self.program.stdout_redirection == REDProgram.STDIO_REDIRECTION_FILE
        stderr_redirection_file = self.program.stderr_redirection == REDProgram.STDIO_REDIRECTION_FILE

        self.label_stdin_source.setText(Constants.api_stdin_redirection_display_names.get(self.program.stdin_redirection, '<unknown>'))
        self.label_stdin_pipe_input_title.setVisible(stdin_redirection_pipe)
        self.edit_stdin_pipe_input.setVisible(stdin_redirection_pipe)
        self.button_send_stdin_pipe_input.setVisible(stdin_redirection_pipe)
        self.label_stdin_file_title.setVisible(stdin_redirection_file)
        self.label_stdin_file.setVisible(stdin_redirection_file)

        self.set_widget_enabled(self.edit_stdin_pipe_input, not any_refresh_in_progress and process_running)
        self.set_widget_enabled(self.button_send_stdin_pipe_input, not any_refresh_in_progress and process_running)

        if stdin_redirection_file:
            self.label_stdin_file.setText(self.program.stdin_file_name)

        self.label_stdout_target.setText(Constants.api_stdout_redirection_display_names.get(self.program.stdout_redirection, '<unknown>'))
        self.label_stdout_file_title.setVisible(stdout_redirection_file)
        self.label_stdout_file.setVisible(stdout_redirection_file)

        if stdout_redirection_file:
            self.label_stdout_file.setText(self.program.stdout_file_name)

        self.label_stderr_target.setText(Constants.api_stderr_redirection_display_names.get(self.program.stderr_redirection, '<unknown>'))
        self.label_stderr_file_title.setVisible(stderr_redirection_file)
        self.label_stderr_file.setVisible(stderr_redirection_file)

        if stderr_redirection_file:
            self.label_stderr_file.setText(self.program.stderr_file_name)

        # schedule
        self.group_schedule.setVisible(show_schedule)

        start_mode_never    = self.program.start_mode == REDProgram.START_MODE_NEVER
        start_mode_always   = self.program.start_mode == REDProgram.START_MODE_ALWAYS
        start_mode_interval = self.program.start_mode == REDProgram.START_MODE_INTERVAL
        start_mode_cron     = self.program.start_mode == REDProgram.START_MODE_CRON

        start_mode_display_names  = Constants.api_start_mode_display_names.get(self.program.start_mode, '<unknown>')
        started_once_after_upload = self.program.cast_custom_option_value('started_once_after_upload', bool, False)

        if started_once_after_upload:
            start_mode_display_names += ' (was started once after upload)'

        self.label_start_mode.setText(start_mode_display_names)
        self.label_start_interval_title.setVisible(start_mode_interval)
        self.label_start_interval.setVisible(start_mode_interval)
        self.label_start_interval.setText('{0} seconds'.format(self.program.start_interval))
        self.label_start_fields_title.setVisible(start_mode_cron)
        self.label_start_fields.setVisible(start_mode_cron)

        if start_mode_cron:
            self.label_start_fields.setText(self.program.start_fields)

        self.label_continue_after_error_title.setVisible(not start_mode_never)
        self.label_continue_after_error.setVisible(not start_mode_never)

        if self.program.continue_after_error:
            self.label_continue_after_error.setText('Enabled')
        else:
            self.label_continue_after_error.setText('Disabled')

    def start_program(self):
        try:
            self.program.start()
        except (Error, REDError) as e:
            QMessageBox.critical(get_main_window(), 'Start Error',
                                 u'Could not start program [{0}]:\n\n{1}'
                                 .format(self.program.cast_custom_option_value('name', unicode, '<unknown>'), e))

    # FIXME: either send SIGINT before SIGKILL, or add a dedicated button for SIGINT
    def kill_process(self):
        if self.program.last_spawned_process != None:
            try:
                self.program.last_spawned_process.kill(REDProcess.SIGNAL_KILL)
            except (Error, REDError) as e:
                QMessageBox.critical(get_main_window(), 'Kill Error',
                                     u'Could not kill current process of program [{0}]:\n\n{1}'
                                     .format(self.program.cast_custom_option_value('name', unicode, '<unknown>'), e))

    def continue_schedule(self):
        try:
            self.program.continue_schedule()
        except (Error, REDError) as e:
            QMessageBox.critical(get_main_window(), 'Schedule Error',
                                 u'Could not continue schedule of program [{0}]:\n\n{1}'
                                 .format(self.program.cast_custom_option_value('name', unicode, '<unknown>'), e))

    def send_stdin_pipe_input(self):
        if self.program.last_spawned_process != None and self.program.last_spawned_process.stdin != None:
            try:
                self.program.last_spawned_process.stdin.write_async((self.edit_stdin_pipe_input.text() + u'\n').encode('utf-8'))
            except (Error, REDError) as e:
                QMessageBox.critical(get_main_window(), 'Pipe Input Error',
                                     u'Could not write to stdin of current process of program [{0}]:\n\n{1}'
                                     .format(self.program.cast_custom_option_value('name', unicode, '<unknown>'), e))
            else:
                self.edit_stdin_pipe_input.setText('')

    def set_widget_enabled(self, widget, enabled):
        # store current scroll position
        position = self.scroll_area.verticalScrollBar().value()

        widget.setEnabled(enabled)
        widget.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)

        # restore current scroll position, because en/disabling buttons
        # makes the scroll position jump for some reason
        self.scroll_area.verticalScrollBar().setValue(position)

    def set_edit_buttons_enabled(self, enabled):
        # store current scroll position
        position = self.scroll_area.verticalScrollBar().value()

        self.button_refresh.setEnabled(enabled)
        self.button_edit_general.setEnabled(enabled)
        self.button_edit_language.setEnabled(enabled)
        self.button_edit_arguments.setEnabled(enabled)
        self.button_edit_stdio.setEnabled(enabled)
        self.button_edit_schedule.setEnabled(enabled)

        self.button_refresh.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)
        self.button_edit_general.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)
        self.button_edit_language.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)
        self.button_edit_arguments.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)
        self.button_edit_stdio.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)
        self.button_edit_schedule.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)

        # restore current scroll position, because en/disabling buttons
        # makes the scroll position jump for some reason
        self.scroll_area.verticalScrollBar().setValue(position)

    def show_edit_general_wizard(self):
        self.set_edit_buttons_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)
        page    = ProgramPageGeneral()

        self.edit_general_wizard = ProgramWizardEdit(self, context, self.program, self.widget_files.available_files, self.widget_files.available_directories)
        self.edit_general_wizard.setPage(Constants.PAGE_GENERAL, page)

        if self.edit_general_wizard.exec_() == QDialog.Accepted:
            page.apply_program_changes()
            self.refresh_info()
            self.name_changed.emit()

        self.edit_general_wizard = None

        if self.tab_is_alive:
            self.set_edit_buttons_enabled(True)

    def show_edit_language_wizard(self):
        language_api_name = self.program.cast_custom_option_value('language', unicode, '<unknown>')

        try:
            language_page = Constants.get_language_page(language_api_name)
        except:
            return

        language_page_classes = {
            Constants.PAGE_C:          ProgramPageC,
            Constants.PAGE_CSHARP:     ProgramPageCSharp,
            Constants.PAGE_DELPHI:     ProgramPageDelphi,
            Constants.PAGE_JAVA:       ProgramPageJava,
            Constants.PAGE_JAVASCRIPT: ProgramPageJavaScript,
            Constants.PAGE_OCTAVE:     ProgramPageOctave,
            Constants.PAGE_PERL:       ProgramPagePerl,
            Constants.PAGE_PHP:        ProgramPagePHP,
            Constants.PAGE_PYTHON:     ProgramPagePython,
            Constants.PAGE_RUBY:       ProgramPageRuby,
            Constants.PAGE_SHELL:      ProgramPageShell,
            Constants.PAGE_VBNET:      ProgramPageVBNET
        }

        self.set_edit_buttons_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)
        page    = language_page_classes[language_page]()

        self.edit_language_wizard = ProgramWizardEdit(self, context, self.program, self.widget_files.available_files, self.widget_files.available_directories,)
        self.edit_language_wizard.setPage(language_page, page)

        if self.edit_language_wizard.exec_() == QDialog.Accepted:
            page.apply_program_changes()
            self.refresh_info()

        self.edit_language_wizard = None

        if self.tab_is_alive:
            self.set_edit_buttons_enabled(True)

    def show_edit_arguments_wizard(self):
        self.set_edit_buttons_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)
        page    = ProgramPageArguments()

        self.edit_arguments_wizard = ProgramWizardEdit(self, context, self.program, self.widget_files.available_files, self.widget_files.available_directories)
        self.edit_arguments_wizard.setPage(Constants.PAGE_ARGUMENTS, page)

        if self.edit_arguments_wizard.exec_() == QDialog.Accepted:
            page.apply_program_changes()
            self.refresh_info()

        self.edit_arguments_wizard = None

        if self.tab_is_alive:
            self.set_edit_buttons_enabled(True)

    def show_edit_stdio_wizard(self):
        self.set_edit_buttons_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)
        page    = ProgramPageStdio()

        self.edit_stdio_wizard = ProgramWizardEdit(self, context, self.program, self.widget_files.available_files, self.widget_files.available_directories)
        self.edit_stdio_wizard.setPage(Constants.PAGE_STDIO, page)

        if self.edit_stdio_wizard.exec_() == QDialog.Accepted:
            page.apply_program_changes()
            self.refresh_info()

        self.edit_stdio_wizard = None

        if self.tab_is_alive:
            self.set_edit_buttons_enabled(True)

    def show_edit_schedule_wizard(self):
        self.set_edit_buttons_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)
        page    = ProgramPageSchedule()

        self.edit_schedule_wizard = ProgramWizardEdit(self, context, self.program, self.widget_files.available_files, self.widget_files.available_directories)
        self.edit_schedule_wizard.setPage(Constants.PAGE_SCHEDULE, page)

        if self.edit_schedule_wizard.exec_() == QDialog.Accepted:
            page.apply_program_changes()
            self.refresh_info()

        self.edit_schedule_wizard = None

        if self.tab_is_alive:
            self.set_edit_buttons_enabled(True)

    def show_upload_files_wizard(self):
        self.set_edit_buttons_enabled(False)
        self.set_program_callbacks_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)

        self.upload_files_wizard = ProgramWizardUpload(self, context, self.program, self.last_upload_files_wizard_directory)
        self.upload_files_wizard.exec_()

        self.last_upload_files_wizard_directory = self.upload_files_wizard.last_directory

        if self.upload_files_wizard.upload_successful:
            self.widget_files.refresh_files()

        self.upload_files_wizard = None

        if self.tab_is_alive:
            self.set_program_callbacks_enabled(True)
            self.set_edit_buttons_enabled(True)

    def show_download_wizard(self, download_kind, download_directory, downloads):
        self.set_edit_buttons_enabled(False)
        self.set_program_callbacks_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)

        self.download_wizard = ProgramWizardDownload(self, context, self.program, download_kind, download_directory, downloads)
        self.download_wizard.exec_()
        self.download_wizard = None

        if self.tab_is_alive:
            self.set_program_callbacks_enabled(True)
            self.set_edit_buttons_enabled(True)
Пример #4
0
class ProgramInfoMain(QWidget, Ui_ProgramInfoMain):
    name_changed = pyqtSignal(object)
    status_changed = pyqtSignal(object)

    def __init__(self, session, script_manager, image_version, executable_versions, program):
        QWidget.__init__(self)

        self.setupUi(self)

        self.session             = session
        self.script_manager      = script_manager
        self.image_version       = image_version
        self.executable_versions = executable_versions
        self.program             = program
        self.root_directory      = self.program.root_directory

        self.last_upload_files_wizard_directory = get_home_path()

        self.program.scheduler_state_changed_callback      = self.scheduler_state_changed
        self.program.lite_scheduler_state_changed_callback = self.lite_scheduler_state_changed
        self.program.process_spawned_callback              = self.process_spawned
        self.program.lite_process_spawned_callback         = self.lite_process_spawned

        if self.program.last_spawned_process != None:
            self.program.last_spawned_process.state_changed_callback = self.process_state_changed

        if self.program.last_spawned_lite_process != None:
            self.program.last_spawned_lite_process.state_changed_callback = self.lite_process_state_changed

        self.first_show_event            = True
        self.tab_is_alive                = True
        self.program_refresh_in_progress = False

        self.edit_general_wizard   = None
        self.edit_language_wizard  = None
        self.edit_arguments_wizard = None
        self.edit_stdio_wizard     = None
        self.edit_schedule_wizard  = None
        self.upload_files_wizard   = None
        self.download_wizard       = None

        self.button_refresh.clicked.connect(self.refresh_info)

        self.button_start_program.clicked.connect(self.start_program)
        self.button_exit_process.clicked.connect(self.exit_process)
        self.button_kill_process.clicked.connect(self.kill_process)
        self.button_continue_schedule.clicked.connect(self.continue_schedule)
        self.button_send_stdin_pipe_input.clicked.connect(self.send_stdin_pipe_input)

        self.check_show_environment.stateChanged.connect(self.update_ui_state)

        self.button_edit_general.clicked.connect(self.show_edit_general_wizard)
        self.button_edit_language.clicked.connect(self.show_edit_language_wizard)
        self.button_edit_arguments.clicked.connect(self.show_edit_arguments_wizard)
        self.button_edit_stdio.clicked.connect(self.show_edit_stdio_wizard)
        self.button_edit_schedule.clicked.connect(self.show_edit_schedule_wizard)

        # create language info widget
        language_api_name = self.program.cast_custom_option_value('language', unicode, '<unknown>')

        try:
            language = Constants.get_language(language_api_name)
        except:
            language = None

        if language != None:
            language_info_classes = {
                Constants.LANGUAGE_C:          ProgramInfoC,
                Constants.LANGUAGE_CSHARP:     ProgramInfoCSharp,
                Constants.LANGUAGE_DELPHI:     ProgramInfoDelphi,
                Constants.LANGUAGE_JAVA:       ProgramInfoJava,
                Constants.LANGUAGE_JAVASCRIPT: ProgramInfoJavaScript,
                Constants.LANGUAGE_OCTAVE:     ProgramInfoOctave,
                Constants.LANGUAGE_PERL:       ProgramInfoPerl,
                Constants.LANGUAGE_PHP:        ProgramInfoPHP,
                Constants.LANGUAGE_PYTHON:     ProgramInfoPython,
                Constants.LANGUAGE_RUBY:       ProgramInfoRuby,
                Constants.LANGUAGE_SHELL:      ProgramInfoShell,
                Constants.LANGUAGE_VBNET:      ProgramInfoVBNET
            }

            if language_info_classes[language] != None:
                context = ProgramInfoContext(self.session, self.script_manager, self.executable_versions, self.program)

                self.widget_language = language_info_classes[language](context)
                self.layout_language.addWidget(self.widget_language)
            else:
                self.widget_language = None
        else:
            self.widget_language = None

        self.current_language_action = None

        self.button_language_action.setVisible(False)

        def is_alive():
            return self.tab_is_alive

        # create logs info widget
        context = ProgramInfoContext(self.session, self.script_manager, self.executable_versions, self.program)

        self.widget_logs = ProgramInfoLogs(context, self.update_ui_state, self.set_widget_enabled, is_alive, self.show_download_wizard, self.set_program_callbacks_enabled)
        self.layout_logs.addWidget(self.widget_logs)

        # create files info widget
        context = ProgramInfoContext(self.session, self.script_manager, self.executable_versions, self.program)

        self.widget_files = ProgramInfoFiles(context, self.update_ui_state, self.set_widget_enabled, is_alive, self.show_upload_files_wizard, self.show_download_wizard)
        self.layout_files.addWidget(self.widget_files)

        self.update_ui_state()

    # override QWidget.showEvent
    def showEvent(self, event):
        if self.first_show_event:
            QTimer.singleShot(1, self.widget_logs.refresh_logs)
            QTimer.singleShot(1, self.widget_files.refresh_files)

            self.first_show_event = False

        self.set_program_callbacks_enabled(True)

        QWidget.showEvent(self, event)

    # override QWidget.hideEvent
    def hideEvent(self, event):
        self.set_program_callbacks_enabled(False)

        QWidget.hideEvent(self, event)

    def scheduler_state_changed(self, program):
        self.update_ui_state()

    def lite_scheduler_state_changed(self, program):
        self.status_changed.emit(self.program)

    def process_spawned(self, program):
        self.program.last_spawned_process.state_changed_callback = self.process_state_changed

        self.update_ui_state()

    def lite_process_spawned(self, program):
        self.program.last_spawned_lite_process.state_changed_callback = self.lite_process_state_changed

        self.status_changed.emit(self.program)

    def process_state_changed(self, process):
        self.update_ui_state()

    def lite_process_state_changed(self, process):
        self.status_changed.emit(self.program)

    def close_all_dialogs(self):
        self.tab_is_alive = False

        if self.edit_general_wizard != None:
            self.edit_general_wizard.close()

        if self.edit_language_wizard != None:
            self.edit_language_wizard.close()

        if self.edit_arguments_wizard != None:
            self.edit_arguments_wizard.close()

        if self.edit_stdio_wizard != None:
            self.edit_stdio_wizard.close()

        if self.edit_schedule_wizard != None:
            self.edit_schedule_wizard.close()

        if self.upload_files_wizard != None:
            self.upload_files_wizard.close()

        if self.download_wizard != None:
            self.download_wizard.close()

        if self.widget_language != None:
            self.widget_language.close_all_dialogs()

        self.widget_logs.close_all_dialogs()
        self.widget_files.close_all_dialogs()

    def set_program_callbacks_enabled(self, enable):
        self.program.enable_callbacks = enable

        if enable:
            self.refresh_program()

    def refresh_info(self):
        self.refresh_program()
        self.widget_logs.refresh_logs()
        self.widget_files.refresh_files()

    def refresh_program(self):
        def refresh_async():
            self.program.update()

        def cb_success():
            self.program_refresh_in_progress = False
            self.update_ui_state()

        def cb_error():
            pass # FIXME: report error

        self.program_refresh_in_progress = True
        self.update_ui_state()

        async_call(refresh_async, None, cb_success, cb_error)

    def update_ui_state(self):
        any_refresh_in_progress = self.program_refresh_in_progress or \
                                  self.widget_logs.refresh_in_progress or \
                                  self.widget_files.refresh_in_progress

        if any_refresh_in_progress:
            self.progress.setVisible(True)
            self.button_refresh.setText('Refreshing...')
            self.set_edit_buttons_enabled(False)
        else:
            self.progress.setVisible(False)
            self.button_refresh.setText('Refresh')
            self.set_edit_buttons_enabled(True)

        # general
        name              = self.program.cast_custom_option_value('name', unicode, '<unknown>')
        language_api_name = self.program.cast_custom_option_value('language', unicode, '<unknown>')
        description       = self.program.cast_custom_option_value('description', unicode, '')
        first_upload      = self.program.cast_custom_option_value('first_upload', int, 0)
        last_edit         = self.program.cast_custom_option_value('last_edit', int, 0)

        try:
            language_display_name = Constants.get_language_display_name(language_api_name)
        except:
            language_display_name = '<unknown>'

        self.label_name.setText(name)
        self.label_identifier.setText(self.program.identifier)
        self.label_language.setText(language_display_name)
        self.label_description.setText(description)
        self.label_first_upload.setText(timestamp_to_date_at_time(first_upload))
        self.label_last_edit.setText(timestamp_to_date_at_time(last_edit))

        start_mode_web_interface = has_program_start_mode_web_interface(self.program)

        # status
        self.group_status.setVisible(not start_mode_web_interface)

        process_running = False

        if self.program.last_spawned_process != None:
            date_at_time = timestamp_to_date_at_time(self.program.last_spawned_process.timestamp)

            if self.program.last_spawned_process.state == REDProcess.STATE_UNKNOWN:
                self.label_program_current_state.setText('Unknown since {0}'.format(date_at_time))
            elif self.program.last_spawned_process.state == REDProcess.STATE_RUNNING:
                self.label_program_current_state.setText('Running since {0}'.format(date_at_time))
                process_running = True
            elif self.program.last_spawned_process.state == REDProcess.STATE_ERROR:
                if self.program.last_spawned_process.exit_code == REDProcess.E_INTERNAL_ERROR:
                    self.label_program_current_state.setText('Internal error occurred on {0}'.format(date_at_time))
                elif self.program.last_spawned_process.exit_code == REDProcess.E_CANNOT_EXECUTE:
                    self.label_program_current_state.setText('Could not be executed on {0}'.format(date_at_time))
                elif self.program.last_spawned_process.exit_code == REDProcess.E_DOES_NOT_EXIST:
                    self.label_program_current_state.setText('Executable does not exist on {0}'.format(date_at_time))
                else:
                    self.label_program_current_state.setText('Unknown error occurred on {0}'.format(date_at_time))
            elif self.program.last_spawned_process.state == REDProcess.STATE_EXITED:
                if self.program.last_spawned_process.exit_code == 0:
                    self.label_program_current_state.setText('Not running, last run exited normally on {0}'.format(date_at_time))
                else:
                    self.label_program_current_state.setText('Not running, last run exited with an error (exit code: {0}) on {1}'
                                                            .format(self.program.last_spawned_process.exit_code, date_at_time))
            elif self.program.last_spawned_process.state == REDProcess.STATE_KILLED:
                self.label_program_current_state.setText('Not running, last run was killed (signal: {0}) on {1}'
                                                         .format(self.program.last_spawned_process.exit_code, date_at_time))
            elif self.program.last_spawned_process.state == REDProcess.STATE_STOPPED:
                self.label_program_current_state.setText('Suspended on {0}'.format(date_at_time)) # FIXME: show resume button?

            self.label_last_program_start.setText(timestamp_to_date_at_time(self.program.last_spawned_timestamp))
        else:
            self.label_program_current_state.setText('Not running')
            self.label_last_program_start.setText('Never started')

        scheduler_stopped            = self.program.scheduler_state == REDProgram.SCHEDULER_STATE_STOPPED
        scheduler_state_display_name = Constants.api_scheduler_state_display_name.get(self.program.scheduler_state, '<unknown>')

        if scheduler_stopped:
            scheduler_state_display_name += ', no automatic program start'

        self.label_current_scheduler_state.setText(scheduler_state_display_name)
        self.label_last_scheduler_state_change.setText(timestamp_to_date_at_time(self.program.scheduler_timestamp))

        if self.program.scheduler_message != None:
            self.label_last_scheduler_message.setText(self.program.scheduler_message)
        else:
            self.label_last_scheduler_message.setText('None')

        self.set_widget_enabled(self.button_start_program, not any_refresh_in_progress and not process_running)
        self.set_widget_enabled(self.button_exit_process, not any_refresh_in_progress and process_running)
        self.set_widget_enabled(self.button_kill_process, not any_refresh_in_progress and process_running)
        self.set_widget_enabled(self.button_continue_schedule, not any_refresh_in_progress and scheduler_stopped and self.program.start_mode != REDProgram.START_MODE_NEVER)

        # logs
        self.group_logs.setVisible(not start_mode_web_interface)

        self.widget_logs.any_refresh_in_progress = any_refresh_in_progress
        self.widget_logs.update_ui_state()

        # files
        self.widget_files.any_refresh_in_progress = any_refresh_in_progress
        self.widget_files.update_ui_state()

        # language
        self.group_language.setTitle('{0} Configuration'.format(language_display_name))

        if self.widget_language != None:
            self.widget_language.update_ui_state()

            language_action, language_action_name = self.widget_language.get_language_action()

            if language_action != self.current_language_action:
                if self.current_language_action != None:
                    self.button_language_action.setVisible(False)
                    self.button_language_action.clicked.disconnect(self.current_language_action)

                if language_action != None:
                    self.button_language_action.clicked.connect(language_action)
                    self.button_language_action.setText(language_action_name)
                    self.button_language_action.setVisible(True)

                self.current_language_action = language_action

        # arguments
        self.group_arguments.setVisible(not start_mode_web_interface)

        arguments = []
        editable_arguments_offset = max(self.program.cast_custom_option_value('editable_arguments_offset', int, 0), 0)

        for argument in self.program.arguments.items[editable_arguments_offset:]:
            arguments.append(argument)

        self.label_arguments.setText('\n'.join(arguments))

        environment = []
        editable_environment_offset = max(self.program.cast_custom_option_value('editable_environment_offset', int, 0), 0)

        for variable in self.program.environment.items[editable_environment_offset:]:
            environment.append(variable)

        show_environment = self.check_show_environment.isChecked()

        self.label_environment_title.setVisible(show_environment)
        self.label_environment.setVisible(show_environment)
        self.label_environment.setText('\n'.join(environment))

        # stdio
        self.group_stdio.setVisible(not start_mode_web_interface)

        stdin_redirection_pipe  = self.program.stdin_redirection  == REDProgram.STDIO_REDIRECTION_PIPE
        stdin_redirection_file  = self.program.stdin_redirection  == REDProgram.STDIO_REDIRECTION_FILE
        stdout_redirection_file = self.program.stdout_redirection == REDProgram.STDIO_REDIRECTION_FILE
        stderr_redirection_file = self.program.stderr_redirection == REDProgram.STDIO_REDIRECTION_FILE

        self.label_stdin_source.setText(Constants.api_stdin_redirection_display_names.get(self.program.stdin_redirection, '<unknown>'))
        self.label_stdin_pipe_input_title.setVisible(stdin_redirection_pipe)
        self.edit_stdin_pipe_input.setVisible(stdin_redirection_pipe)
        self.button_send_stdin_pipe_input.setVisible(stdin_redirection_pipe)
        self.label_stdin_file_title.setVisible(stdin_redirection_file)
        self.label_stdin_file.setVisible(stdin_redirection_file)

        self.set_widget_enabled(self.edit_stdin_pipe_input, not any_refresh_in_progress and process_running)
        self.set_widget_enabled(self.button_send_stdin_pipe_input, not any_refresh_in_progress and process_running)

        if stdin_redirection_file:
            self.label_stdin_file.setText(self.program.stdin_file_name)

        self.label_stdout_target.setText(Constants.api_stdout_redirection_display_names.get(self.program.stdout_redirection, '<unknown>'))
        self.label_stdout_file_title.setVisible(stdout_redirection_file)
        self.label_stdout_file.setVisible(stdout_redirection_file)

        if stdout_redirection_file:
            self.label_stdout_file.setText(self.program.stdout_file_name)

        self.label_stderr_target.setText(Constants.api_stderr_redirection_display_names.get(self.program.stderr_redirection, '<unknown>'))
        self.label_stderr_file_title.setVisible(stderr_redirection_file)
        self.label_stderr_file.setVisible(stderr_redirection_file)

        if stderr_redirection_file:
            self.label_stderr_file.setText(self.program.stderr_file_name)

        # schedule
        self.group_schedule.setVisible(not start_mode_web_interface)

        start_mode_never    = self.program.start_mode == REDProgram.START_MODE_NEVER
        start_mode_always   = self.program.start_mode == REDProgram.START_MODE_ALWAYS
        start_mode_interval = self.program.start_mode == REDProgram.START_MODE_INTERVAL
        start_mode_cron     = self.program.start_mode == REDProgram.START_MODE_CRON

        start_mode_display_names  = Constants.api_start_mode_display_names.get(self.program.start_mode, '<unknown>')
        started_once_after_upload = self.program.cast_custom_option_value('started_once_after_upload', bool, False)

        if started_once_after_upload:
            start_mode_display_names += ' (was started once after upload)'

        self.label_start_mode.setText(start_mode_display_names)
        self.label_start_interval_title.setVisible(start_mode_interval)
        self.label_start_interval.setVisible(start_mode_interval)
        self.label_start_interval.setText('{0} seconds'.format(self.program.start_interval))
        self.label_start_fields_title.setVisible(start_mode_cron)
        self.label_start_fields.setVisible(start_mode_cron)

        if start_mode_cron:
            self.label_start_fields.setText(self.program.start_fields)

        self.label_continue_after_error_title.setVisible(not start_mode_never)
        self.label_continue_after_error.setVisible(not start_mode_never)

        if self.program.continue_after_error:
            self.label_continue_after_error.setText('Enabled')
        else:
            self.label_continue_after_error.setText('Disabled')

    def start_program(self):
        try:
            self.program.start()
        except (Error, REDError) as e:
            QMessageBox.critical(get_main_window(), 'Start Error',
                                 u'Could not start program [{0}]:\n\n{1}'
                                 .format(self.program.cast_custom_option_value('name', unicode, '<unknown>'), e))

    def exit_process(self):
        if self.program.last_spawned_process != None:
            try:
                self.program.last_spawned_process.kill(REDProcess.SIGNAL_TERMINATE)
            except (Error, REDError) as e:
                QMessageBox.critical(get_main_window(), 'Exit Error',
                                     u'Could not exit current process of program [{0}]:\n\n{1}'
                                     .format(self.program.cast_custom_option_value('name', unicode, '<unknown>'), e))
    def kill_process(self):
        if self.program.last_spawned_process != None:
            try:
                self.program.last_spawned_process.kill(REDProcess.SIGNAL_KILL)
            except (Error, REDError) as e:
                QMessageBox.critical(get_main_window(), 'Kill Error',
                                     u'Could not kill current process of program [{0}]:\n\n{1}'
                                     .format(self.program.cast_custom_option_value('name', unicode, '<unknown>'), e))

    def continue_schedule(self):
        try:
            self.program.continue_schedule()
        except (Error, REDError) as e:
            QMessageBox.critical(get_main_window(), 'Schedule Error',
                                 u'Could not continue schedule of program [{0}]:\n\n{1}'
                                 .format(self.program.cast_custom_option_value('name', unicode, '<unknown>'), e))

    def send_stdin_pipe_input(self):
        if self.program.last_spawned_process != None and self.program.last_spawned_process.stdin != None:
            try:
                self.program.last_spawned_process.stdin.write_async((self.edit_stdin_pipe_input.text() + u'\n').encode('utf-8'))
            except (Error, REDError) as e:
                QMessageBox.critical(get_main_window(), 'Pipe Input Error',
                                     u'Could not write to stdin of current process of program [{0}]:\n\n{1}'
                                     .format(self.program.cast_custom_option_value('name', unicode, '<unknown>'), e))
            else:
                self.edit_stdin_pipe_input.setText('')

    def set_widget_enabled(self, widget, enabled):
        # store current scroll position
        position = self.scroll_area.verticalScrollBar().value()

        widget.setEnabled(enabled)
        widget.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)

        # restore current scroll position, because en/disabling buttons
        # makes the scroll position jump for some reason
        self.scroll_area.verticalScrollBar().setValue(position)

    def set_edit_buttons_enabled(self, enabled):
        # store current scroll position
        position = self.scroll_area.verticalScrollBar().value()

        self.button_refresh.setEnabled(enabled)
        self.button_edit_general.setEnabled(enabled)
        self.button_edit_language.setEnabled(enabled)
        self.button_edit_arguments.setEnabled(enabled)
        self.button_edit_stdio.setEnabled(enabled)
        self.button_edit_schedule.setEnabled(enabled)

        self.button_refresh.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)
        self.button_edit_general.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)
        self.button_edit_language.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)
        self.button_edit_arguments.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)
        self.button_edit_stdio.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)
        self.button_edit_schedule.setAttribute(Qt.WA_TransparentForMouseEvents, not enabled)

        # restore current scroll position, because en/disabling buttons
        # makes the scroll position jump for some reason
        self.scroll_area.verticalScrollBar().setValue(position)

    def show_edit_general_wizard(self):
        self.set_edit_buttons_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)
        page    = ProgramPageGeneral()

        self.edit_general_wizard = ProgramWizardEdit(self, context, self.program, self.widget_files.available_files, self.widget_files.available_directories)
        self.edit_general_wizard.setPage(Constants.PAGE_GENERAL, page)

        if self.edit_general_wizard.exec_() == QDialog.Accepted:
            page.apply_program_changes()
            self.refresh_info()
            self.name_changed.emit(self.program)

        self.edit_general_wizard = None

        if self.tab_is_alive:
            self.set_edit_buttons_enabled(True)

    def show_edit_language_wizard(self):
        language_api_name = self.program.cast_custom_option_value('language', unicode, '<unknown>')

        try:
            language_page = Constants.get_language_page(language_api_name)
        except:
            return

        language_page_classes = {
            Constants.PAGE_C:          ProgramPageC,
            Constants.PAGE_CSHARP:     ProgramPageCSharp,
            Constants.PAGE_DELPHI:     ProgramPageDelphi,
            Constants.PAGE_JAVA:       ProgramPageJava,
            Constants.PAGE_JAVASCRIPT: ProgramPageJavaScript,
            Constants.PAGE_OCTAVE:     ProgramPageOctave,
            Constants.PAGE_PERL:       ProgramPagePerl,
            Constants.PAGE_PHP:        ProgramPagePHP,
            Constants.PAGE_PYTHON:     ProgramPagePython,
            Constants.PAGE_RUBY:       ProgramPageRuby,
            Constants.PAGE_SHELL:      ProgramPageShell,
            Constants.PAGE_VBNET:      ProgramPageVBNET
        }

        self.set_edit_buttons_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)
        page    = language_page_classes[language_page]()

        self.edit_language_wizard = ProgramWizardEdit(self, context, self.program, self.widget_files.available_files, self.widget_files.available_directories,)
        self.edit_language_wizard.setPage(language_page, page)

        if self.edit_language_wizard.exec_() == QDialog.Accepted:
            page.apply_program_changes()
            self.refresh_info()

        self.edit_language_wizard = None

        if self.tab_is_alive:
            self.set_edit_buttons_enabled(True)

    def show_edit_arguments_wizard(self):
        self.set_edit_buttons_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)
        page    = ProgramPageArguments()

        self.edit_arguments_wizard = ProgramWizardEdit(self, context, self.program, self.widget_files.available_files, self.widget_files.available_directories)
        self.edit_arguments_wizard.setPage(Constants.PAGE_ARGUMENTS, page)

        if self.edit_arguments_wizard.exec_() == QDialog.Accepted:
            page.apply_program_changes()
            self.refresh_info()

        self.edit_arguments_wizard = None

        if self.tab_is_alive:
            self.set_edit_buttons_enabled(True)

    def show_edit_stdio_wizard(self):
        self.set_edit_buttons_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)
        page    = ProgramPageStdio()

        self.edit_stdio_wizard = ProgramWizardEdit(self, context, self.program, self.widget_files.available_files, self.widget_files.available_directories)
        self.edit_stdio_wizard.setPage(Constants.PAGE_STDIO, page)

        if self.edit_stdio_wizard.exec_() == QDialog.Accepted:
            page.apply_program_changes()
            self.refresh_info()

        self.edit_stdio_wizard = None

        if self.tab_is_alive:
            self.set_edit_buttons_enabled(True)

    def show_edit_schedule_wizard(self):
        self.set_edit_buttons_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)
        page    = ProgramPageSchedule()

        self.edit_schedule_wizard = ProgramWizardEdit(self, context, self.program, self.widget_files.available_files, self.widget_files.available_directories)
        self.edit_schedule_wizard.setPage(Constants.PAGE_SCHEDULE, page)

        if self.edit_schedule_wizard.exec_() == QDialog.Accepted:
            page.apply_program_changes()
            self.refresh_info()

        self.edit_schedule_wizard = None

        if self.tab_is_alive:
            self.set_edit_buttons_enabled(True)

    def show_upload_files_wizard(self):
        self.set_edit_buttons_enabled(False)
        self.set_program_callbacks_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)

        self.upload_files_wizard = ProgramWizardUpload(self, context, self.program, self.last_upload_files_wizard_directory)
        self.upload_files_wizard.exec_()

        self.last_upload_files_wizard_directory = self.upload_files_wizard.last_directory

        if self.upload_files_wizard.upload_successful:
            self.widget_files.refresh_files()

        self.upload_files_wizard = None

        if self.tab_is_alive:
            self.set_program_callbacks_enabled(True)
            self.set_edit_buttons_enabled(True)

    def show_download_wizard(self, download_kind, download_directory, downloads):
        self.set_edit_buttons_enabled(False)
        self.set_program_callbacks_enabled(False)

        context = ProgramWizardContext(self.session, [], self.script_manager, self.image_version, self.executable_versions)

        self.download_wizard = ProgramWizardDownload(self, context, self.program, download_kind, download_directory, downloads)
        self.download_wizard.exec_()
        self.download_wizard = None

        if self.tab_is_alive:
            self.set_program_callbacks_enabled(True)
            self.set_edit_buttons_enabled(True)