Пример #1
0
class LostFolderDialog(object):
    def __init__(self, parent, path, restoreFolder, dialog_id=0):
        super(LostFolderDialog, self).__init__()
        self._path = path
        self._restoreFolder = restoreFolder
        self._dialog_id = dialog_id

        self._dialog = QDialog(
            parent, Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint)
        self._dialog.setAttribute(Qt.WA_MacFrameworkScaled)
        self._dialog.setWindowIcon(QIcon(':/images/icon.png'))
        self._ui = lost_folder_dialog.Ui_Dialog()
        self._ui.setupUi(self._dialog)

        self._ui.textLabel.setText(self._ui.textLabel.text().replace(
            '{PATH}', path))

        self._connect_slots()

    def _connect_slots(self):
        ui = self._ui
        ui.tryAgainButton.clicked.connect(self._on_tryAgain)
        ui.restoreFolderButton.clicked.connect(self._on_restoreFolder)

    def show(self):
        self._dialog.raise_()
        self._dialog.exec_()

    def _on_tryAgain(self):
        if op.isdir(self._path):
            self._dialog.accept()

    def _on_restoreFolder(self):
        self._dialog.accept()
        self._restoreFolder(self._dialog_id, 0)
Пример #2
0
 def accept(self):
     if self.result == 'XML':
         app = ET.Element('App')
         app.attrib['title'] = self.title
         child = ET.fromstring(self.formwidget.get())
         app.append(child)
         self.data = ET.tostring(app)
     else:
         self.data = self.formwidget.get()
     QDialog.accept(self)
Пример #3
0
    def accept(self) -> None:
        """
		Called when the user clicks the "save" button.
		
		:return: None
		:rtype: NoneType
		"""

        self._project.setBackend(self.ui.backendEdit.currentText())
        self._project.setName(self.ui.nameEdit.text())
        self._project.setProjectDir(self.ui.locationEdit.text())
        self._project.setDescription(self.ui.descriptionEdit.toPlainText())
        self._project.setExecutableFile(self.ui.appEdit.text())
        QDialog.accept(self)
Пример #4
0
    def accept(self):
        """
        Applies given information when the user clicks "OK"
        """
        warningShown = False

        for tabNum in range(1, 2):  # TODO: Change to 4 when done
            for planetNum in range(1, 6):
                planet = 'self.mainWindow.ui.pie' + str(planetNum) + 'series'
                spinBox = 'self.ui.planet' + str(planetNum) + 'Num'
                button = 'self.ui.planet' + str(planetNum) + 'Enable'
                if tabNum in (2, 3):
                    planet += '_' + str(tabNum)
                    spinBox += '_' + str(tabNum)
                    button += '_' + str(tabNum)

                exec('checked = ' + button + '.isChecked()', locals())
                exec('pE = ' + planet + '.enabled', locals())

                if locals()['checked']:
                    exec('pieces = ' + spinBox + '.value()', locals())
                    pcs = locals()['pieces']

                    exec('samePcNum = ' + planet + '.sum() == ' + str(pcs),
                         locals())

                    if not locals()['pE']:
                        exec(planet + '.setEnabled(' + str(pcs) + ')')
                    elif not locals()['samePcNum']:
                        if not warningShown:
                            result = self.showChangeWarning()
                            warningShown = True
                            if result == QMessageBox.Cancel:
                                return

                        exec(planet + '.clear()')
                        exec(planet + '.setEnabled(' + str(pcs) + ')')
                    # Only other case is planet already being enabled with the same num of pcs, nothing to do.
                else:
                    if locals()['pE']:
                        if not warningShown:
                            result = self.showChangeWarning()
                            warningShown = True
                            if result == QMessageBox.Cancel:
                                return

                        toDelete = []
                        for wo in self.mainWindow.machines[tabNum -
                                                           1].workOrders:
                            if wo.planetNum == planetNum:
                                toDelete.append(wo)
                        for wo in toDelete:
                            self.mainWindow.deleteWorkOrder(wo)
                        toDelete.clear()

                        exec(planet + '.setDisabled()')

        return QDialog.accept(self)
    def accept(self):
        """
		This method is called when the user clicks the "OK" button.

		It will validate all of the user's input and show error messages if
		any information is invalid.

		:emits: setApiCompiler if the api compiler setting was successfully accpeted
		:return: None
		:rtype: NoneType
		"""

        self.ui.error_label.setText("")
        errors = []
        interpExe = self.ui.interpreterLocation.text()

        # Check for valid target application executable
        if not interpExe:
            errors.append("Need to select target application executable")
        else:
            if not isExecutable(interpExe):
                errors.append("Target application must be an executable file.")
            elif not appBitnessMatches(interpExe):
                pyBit = getPythonBitness()
                appBit = getExeBitness(interpExe)
                errors.append(
                    "{} bit Python cannot control {} bit application".format(
                        pyBit, appBit))

        # Construct a set for documentation type
        setDocType = set()
        if self.ui.checkBoxDocx.isChecked():
            setDocType.add(CompilationProfile.DocType.Doc)
        if self.ui.checkBoxHtml.isChecked():
            setDocType.add(CompilationProfile.DocType.Html)
        if self.ui.checkBoxPdf.isChecked():
            setDocType.add(CompilationProfile.DocType.Pdf)
        if self.ui.checkBoxEPub.isChecked():
            setDocType.add(CompilationProfile.DocType.EPub)

        if len(setDocType) == 0:
            errors.append("You must select at least one documentation type.")

        # Construct a set for component resolution type
        setcompResOpts = set()

        if self.ui.checkBoxTokenExactMatch.isChecked():
            setcompResOpts.add(MatchOption.ExactToken)
        if self.ui.checkBoxTokenCloseMatch.isChecked():
            setcompResOpts.add(MatchOption.CloseToken)
        if self.ui.checkBoxPywinautoBestMatch.isChecked():

            setcompResOpts.add(MatchOption.PWABestMatch)

        if len(setcompResOpts) == 0:
            errors.append(
                "You must select at least one component resolution type.")

        apiFolderDir = self.ui.apiLocation.text()
        interpExeDir = self.ui.interpreterLocation.text()

        theCompilationProfile = CompilationProfile(setDocType, setcompResOpts,
                                                   apiFolderDir, interpExeDir)

        # if there are any errors, show them, then return.
        if len(errors) != 0:
            errMsg = "Errors:\n"
            for err in errors:
                errMsg += "\t" + err + "\n"
            self.ui.error_label.setText(errMsg)
            return

        # TODO: figure out why FindExecutable: There is no association for the file get printed
        self.setApiCompiler.emit(theCompilationProfile)
        c = Compiler(theCompilationProfile).compileAPI()
        return QDialog.accept(self)
Пример #6
0
class InsertLinkDialog(object):

    def __init__(self, parent, dp=None, signal_server_address=''):
        self._dialog = QDialog(parent)
        self._dp = dp
        self._parent = parent

        self._link = ''
        self._password = ''
        self._is_shared = True
        self._password_mode = False
        self._signal_server_address = signal_server_address

        self._dialog.setWindowIcon(QIcon(':/images/icon.png'))
        self._ui = Ui_insert_link_dialog()
        self._ui.setupUi(self._dialog)

        self._init_ui()

        self._cant_validate = tr("Cannot validate share link")

    def _init_ui(self):
        self._dialog.setAttribute(Qt.WA_MacFrameworkScaled)

        self._hide_error()

        self._ok_button = self._ui.ok_button
        self._ui.cancel_button.clicked.connect(self._dialog.reject)
        self._ok_button.clicked.connect(self._ok_clicked)
        self._ui.link_line_edit.textChanged.connect(self._text_changed)

        self._set_fonts()

    def _set_fonts(self):
        controls = []
        controls.extend([c for c in self._dialog.findChildren(QLabel)])
        controls.extend(
            [c for c in self._dialog.findChildren(QLineEdit)])
        controls.extend(
            [c for c in self._dialog.findChildren(QPushButton)])

        for control in controls:
            font = control.font()
            font_size = control.font().pointSize() * self._dp
            if font_size > 0:
                control.setFont(QFont(font.family(), font_size))

    def _ok_clicked(self):
        validated = self._validate()
        if validated is None:
            self._change_mode()
        elif validated:
            if self._password_mode:
                self._password = self._ui.link_line_edit.text()
            else:
                self._link = self._ui.link_line_edit.text()
            self._dialog.accept()

    def _text_changed(self, *args, **kwargs):
        self._hide_error()

    def _validate(self):
        if self._is_shared:
            return self._validate_shared()
        else:
            return self._validate_network_file()

    def _validate_shared(self):
        if self._password_mode:
            return self._validate_password()

        if not self._validate_scheme():
            return False

        return self._check_share_link()

    def _validate_scheme(self):
        share_url = self._ui.link_line_edit.text()
        pr = urlparse(share_url)

        success = pr.scheme in ('http', 'https', 'pvtbox') and pr.path
        share_hash = pr.path.split('/')[-1]
        success = success and share_hash and len(share_hash) == 32
        if not success:
            self._show_error()
        return success

    def _check_share_link(self):
        self._lock_screen()
        share_url = self._link if self._password_mode \
            else self._ui.link_line_edit.text()
        pr = urlparse(share_url)
        share_hash = pr.path.split('/')[-1]
        param_str = ''
        if self._password_mode:
            password = self._ui.link_line_edit.text()
            password = base64.b64encode(
                bytes(password, 'utf-8')).decode('utf-8')
            params = {"passwd": password}
            query = urlencode(params, encoding='utf-8')
            param_str = '?{}'.format(query)
        url = 'https://{}/ws/webshare/{}{}'.format(
            self._signal_server_address, share_hash, param_str)
        logger.debug("url %s", url)

        error = ''
        try:
            response = urlopen(url, timeout=1)
            status = response.status
        except HTTPError as e:
            logger.warning("Request to signal server returned error %s", e)
            status = e.code
            response = str(e.read(), encoding='utf-8')
        except URLError as e:
            logger.warning("Request to signal server returned url error %s", e)
            self._show_error(self._cant_validate)
            self._unlock_screen()
            return False

        logger.debug("request status %s", status)
        if status == 400:
            if self._password_mode:
                self._link += param_str
            success = True
        else:
            success, error = self._parse_response(response)

        if success is False:
            self._show_error(error)
        self._unlock_screen()
        return success

    def _parse_response(self, response):
        try:
            data = json.loads(response)
            err_code = data.get("errcode", '')
            info = data.get("info", '')

            if err_code == 'SHARE_WRONG_PASSWORD':
                success = None if not self._password_mode else False
                error = ''
            elif err_code == 'LOCKED_CAUSE_TOO_MANY_BAD_LOGIN':
                success = False
                error = tr('Locked after too many incorrect attempts')
            elif err_code == 'SHARE_NOT_FOUND':
                success = False
                error = ''
            else:
                success = False
                error = info if info else self._cant_validate
        except Exception as e:
            logger.warning("Can't parse response (%s). reason: %s",
                           response, e)
            success = False
            error = self._cant_validate

        return  success, error

    def _validate_password(self):
        if not self._ui.link_line_edit.text():
            self._show_error()
            return False

        return self._check_share_link()

    def _validate_network_file(self):
        # place code to validate network file link here
        return False

    def _change_mode(self):
        assert not self._password_mode, \
            "Must not be in password mode in changing mode"

        logger.debug("Changing to password mode")
        self._password_mode = True
        self._dialog.setWindowTitle(tr("Insert password"))
        self._link = self._ui.link_line_edit.text()
        self._ui.link_line_edit.setText('')
        self._ui.link_line_edit.setPlaceholderText(tr("Insert password here"))
        self._ui.link_line_edit.setEchoMode(QLineEdit.Password)
        self._hide_error()

    def _show_error(self, error_text=''):
        if not error_text:
            link_text = self._ui.link_line_edit.text()
            error_text = tr("Please insert share link") \
                if not self._password_mode and not link_text \
                else tr("Invalid link") if not self._password_mode \
                else tr("Password can not be empty") if not link_text \
                else tr("Wrong password")
        self._ui.error_label.setText(error_text)
        self._ui.link_line_edit.setFocus()

    def _hide_error(self):
        self._ui.error_label.setText('')
        self._ui.link_line_edit.setFocus()

    def _lock_screen(self):
        self._ok_button.setText(tr("Processing..."))
        self._dialog.setEnabled(False)
        self._dialog.repaint()

    def _unlock_screen(self):
        self._ok_button.setText(tr("Ok"))
        self._dialog.setEnabled(True)
        self._dialog.repaint()

    def show(self):
        logger.debug("Opening insert link dialog")

        if self._dialog.exec_() == QDialog.Rejected:
            self._link = ''
            self._password = ''
            self._is_shared = True

        logger.verbose("link (%s), password (%s)", self._link, self._password)
        return self._link, self._is_shared
class batch_file_viewer(QTableWidget):
    def __init__(self, nf_settings_path):
        super(batch_file_viewer,self).__init__(parent = None)
        self.nf_settings_parser = custom_config_parser()
        self.nf_settings_parser.load(nf_settings_path)
        self.setRowCount(20)
        self.setColumnCount(2)
        # Fill all places so there are no "None" types in the table
        for row in range(self.rowCount()):
            for column in range(self.columnCount()):
                item = QTableWidgetItem()
                item.setText('')
                self.setItem(row, column, item)

        self.original_background = item.background()
        self.clipboard = QGuiApplication.clipboard()

        self.cellChanged.connect(self.check_cell)  # Needs to be after "filling for loop" above
        self.header = self.horizontalHeader()
        self.header.setSectionResizeMode(0, QHeaderView.Stretch)
        self.setHorizontalHeaderLabels(["MS files", "Label"])
        self.header.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.header.customContextMenuRequested.connect( self.right_click_menu )
        self.saved_text = ''
        self.store_re_text = ''

    def right_click_menu(self, point):
        column = self.header.logicalIndexAt(point.x())
        # show menu about the column if column 1
        if column == 1:
            menu = QMenu(self)
            menu.addAction('Auto label (experimental)', self.auto_label)
            menu.popup(self.header.mapToGlobal(point))
        elif column == 0:
            menu = QMenu(self)
            menu.addAction('Remove empty rows', self.remove_empty_rows)
            menu.popup(self.header.mapToGlobal(point))

    def keyPressEvent(self, event):
        """Add functionallity to keyboard"""
        # Mac OS specifics
        is_mac_os_delete = (event.key() == QtCore.Qt.Key_H and event.modifiers() == QtCore.Qt.ControlModifier)
        if event.key() == QtCore.Qt.Key_Delete or is_mac_os_delete:  # 16777223
            for item in self.selectedItems():
                item.setText('')
        elif event.key() == 16777221 or event.key() == 16777220:  # *.221 is right enter
            if len(self.selectedIndexes()) == 0:  # Quick check if anything is selected
                pass
            else:
                index = self.selectedIndexes()[0]  # Take last
                if index.row() + 1 > self.rowCount() - 1:
                    self.addRow_with_items()
                self.setCurrentCell(index.row() + 1, index.column())
        else:
            modifiers = QGuiApplication.keyboardModifiers()
            if modifiers == QtCore.Qt.ControlModifier and event.key() == 67:  # Copy
                self.saved_text = ''
                new_row = False
                for index in self.selectedIndexes():
                    if index.column() == 0:
                        if new_row:
                            self.saved_text += '\n'
                        new_row = True
                        self.saved_text += self.item(index.row(), index.column()).text()
                        self.saved_text += '\t'
                    elif index.column() == 1:
                        self.saved_text += self.item(index.row(), index.column()).text()
                        self.saved_text += '\n'
                        new_row = False

                self.clipboard.setText(self.saved_text)
            elif modifiers == QtCore.Qt.ControlModifier and event.key() == 86:  # Paste
                clipboard_text = self.clipboard.text()
                clipboard_text = clipboard_text.split('\n')
                paste_index = self.selectedIndexes()[0]
                row = paste_index.row()
                for text, index in zip(clipboard_text, range(len(clipboard_text))):
                    text = text.split('\t')
                    column = paste_index.column()
                    for input in text:
                        if input == '':
                            continue
                        if column > self.columnCount() - 1:
                            pass
                        else:
                            if self.item(row, column) is None:
                                self.addRow_with_items()
                            self.item(row, column).setText(input)
                        column += 1
                    row += 1
            else:
                super().keyPressEvent(event)  # Propagate to built in methods
                # This interferes with copy and paste if it is not else

    def check_cell(self, row, column):
        """Triggered when cell is changed"""
        self.blockSignals(True)
        self.pick_color(row, column)
        self.blockSignals(False)

    def addRow_with_items(self):
        item = QTableWidgetItem()
        item.setText('')
        self.setRowCount(self.rowCount() + 1)
        self.setItem(self.rowCount() - 1, 0, item)    # Note: new rowcount here
        label = QTableWidgetItem()
        label.setText('')
        self.setItem(self.rowCount() - 1, 1, label)

    def pick_color(self, row, column):
        """Triggered by check_cell"""
        msfile = self.item(row, 0)
        label = self.item(row, 1)
        if label is None or msfile is None:  # NOTE: item == None will give NotImplementedError. Must use "is"
            return  # This might remove some weird errors in the future

        # Fix for adding empty spaces
        if msfile.text() == ' ':
            msfile.setText('')

        # Ms file
        if not os.path.isfile(msfile.text()):
            msfile.setForeground(QColor('red'))
        elif msfile.text().split('.')[-1] == "mzML":
            msfile.setForeground(QColor(0,255,150))
        elif msfile.text().split('.')[-1] != "mzML":
            msfile.setForeground(QColor(30,150,255))

        workflow = self.nf_settings_parser.get('params.workflow')
        if msfile.text() == '':
            label.setBackground(self.original_background)
            label.setForeground(QColor('white'))
        elif label.text() == '' and os.path.isfile(msfile.text()) and workflow == "Full":
            label.setBackground(QColor('red'))
        elif os.path.isfile(msfile.text()) and label.text() != '':
            label.setBackground(self.original_background)
            label.setForeground(QColor(0,255,150))
        else:
            label.setBackground(self.original_background)
            label.setForeground(QColor('white'))

    def update(self):
        for row in range(self.rowCount()):
            for column in range(self.columnCount()):
                self.pick_color(row, column)

    def auto_assign(self, file):
        """Triggered when using file chooser button"""
        for row in range(self.rowCount()):
            item = self.item(row, 0)
            if item.text() == '':
                self.item(row, 0).setText(file)
                return

        # If we get here, add more rows
        self.blockSignals(True)
        self.addRow_with_items()
        self.item(self.rowCount() - 1, 0).setText(file)
        self.pick_color(self.rowCount() - 1, 0)
        self.blockSignals(False)

    def auto_label(self):
        all_labels = []
        # Get current labels, we want unique
        for row in range(self.rowCount()):
            label = self.item(row, 1).text()
            all_labels.append(label)

        self.dialog = QDialog()
        lay = QVBoxLayout()
        self.dialog.setLayout(lay)
        self.line = QLineEdit()
        self.line.setText(self.store_re_text)
        bbox = QDialogButtonBox()
        bbox.addButton("Help", QDialogButtonBox.HelpRole)
        bbox.addButton("Apply", QDialogButtonBox.AcceptRole)
        bbox.addButton("Cancel", QDialogButtonBox.RejectRole)
        bbox.accepted.connect(self.apply_auto)
        bbox.rejected.connect(self.cancel_auto)
        bbox.helpRequested.connect(self.help_auto)
        lay.addWidget(self.line)
        lay.addWidget(bbox)
        self.dialog.resize(300, 100)
        ans = self.dialog.exec_()  # This will block until the dialog closes
        if ans != 1:
            return
        re_text = self.line.text()
        self.store_re_text = re_text

        # Assign labels
        c = 0
        for row in range(self.rowCount()):
            label = self.item(row, 1).text()
            file = self.item(row, 0).text()
            if label == '' and file != '':
                file_name = os.path.basename(file)
                search = re.search(re_text, file_name)
                if search is not None:
                    c += 1
                    # idk how to check how many captured groups, so lets do this a hacky way
                    for i in range(10,-1,-1):
                        try:
                            added_label = search.group(i)
                            break
                        except Exception as e:
                            pass
                else:
                    added_label = ''  # Do not add label if you don't know what to add
                self.item(row, 1).setText(added_label)
        if c == 0:
            ERROR("No files matched the regex pattern")
            self.auto_label()

    def apply_auto(self):
        re_text = self.line.text()
        test_string = "This is a string to check if you put in a correct regex code"
        try:
            match = re.search(re_text, test_string)
        except Exception as e:
            ERROR("This is not a valid regex string. Error is: \n" + str(e))
            return
        self.dialog.accept()

    def cancel_auto(self):
        self.dialog.reject()

    def help_auto(self):
        help_dialog = QDialog()
        lay = QVBoxLayout()
        help_string = '''<html>
        <center>
        Auto labeler uses regular expressions to determine which files belong to a certain groupself. <br>
        Write the regular expression in the box and press apply. <br>
        The labeler will then label your file depending on the naming convention you applied. <br>

        Example: You have 100s of files named like this <br>
        patient_X_<date>.RAW <br>
        Solution can be found <a href=\"https://regex101.com/r/dpCBo9/1/\">here</a> <br>

        NOTE: The auto labeler will always take the LAST capturing group
        </html>
        '''
        help_label = QLabel(help_string)
        help_label.setOpenExternalLinks(True)
        lay.addWidget(help_label)
        help_dialog.setLayout(lay)
        ans = help_dialog.exec_()  # This will block until the dialog closes

    def remove_empty_rows(self):
        row = 0
        for _ in range(5000):
            file = self.item(row, 0)
            label = self.item(row, 1)
            if file is None or label is None:
                self.removeRow(row)
            elif file.text() == '' and label.text() == '':
                self.removeRow(row)
            else:
                row += 1
        if self.rowCount() == 0:
            item = QTableWidgetItem()
            self.setRowCount(self.rowCount() + 1)
            row = self.rowCount() - 1
            self.setItem(row, 0, item)    # Note: new rowcount here
            label = QTableWidgetItem()
            label.setText('')
            self.setItem(row, 1, label)
Пример #8
0
class Settings(object):
    class _MigrationFailed(ExpectedError):
        pass

    def __init__(self,
                 cfg,
                 main_cfg,
                 start_service,
                 exit_service,
                 parent=None,
                 size=None,
                 migrate=False,
                 dp=1,
                 get_offline_dirs=lambda: None,
                 set_offline_dirs=lambda o, no: None):
        super(Settings, self).__init__()
        self._cfg = cfg
        self._main_cfg = main_cfg
        self._start_service = start_service
        self._exit_service = exit_service
        self._parent = parent
        self._size = size
        self._dp = dp
        self._get_offline_dirs = get_offline_dirs
        self._set_offline_dirs = set_offline_dirs

        self._dialog = QDialog(parent)
        self._dialog.setWindowIcon(QIcon(':/images/icon.png'))
        self._dialog.setAttribute(Qt.WA_MacFrameworkScaled)
        self._ui = settings.Ui_Dialog()
        self._ui.setupUi(self._dialog)
        self._max_root_len = get_max_root_len(self._cfg)
        self._migrate = migrate
        self._migration = None
        self._migration_cancelled = False

        try:
            self._ui.account_type.setText(
                license_display_name_from_constant(self._cfg.license_type))
            self._ui.account_type.setVisible(True)
            self._ui.account_type_header.setVisible(True)
            self._ui.account_upgrade.setVisible(True)
        except KeyError:
            pass
        upgrade_license_types = (FREE_LICENSE, FREE_TRIAL_LICENSE)
        if self._cfg.license_type in upgrade_license_types:
            self._ui.account_upgrade.setText('<a href="{}">{}</a>'.format(
                GET_PRO_URI.format(self._cfg.host), tr('Upgrade')))
            self._ui.account_upgrade.setTextFormat(Qt.RichText)
            self._ui.account_upgrade.setTextInteractionFlags(
                Qt.TextBrowserInteraction)
            self._ui.account_upgrade.setOpenExternalLinks(True)
            self._ui.account_upgrade.setAlignment(Qt.AlignLeft)
        else:
            self._ui.account_upgrade.setText("")

        self._ui.centralWidget.setFrameShape(QFrame.NoFrame)
        self._ui.centralWidget.setLineWidth(1)

        self._ui.language_comboBox.addItem(tr('English'))
        self._ui.language_comboBox.setEnabled(False)

        self._connect_slots()
        self._set_fonts()
        self._ui.tabWidget.setCurrentIndex(0)

        self._smart_sync_dialog = None

        self.logged_out = Signal(bool)
        self.logging_disabled_changed = Signal(bool)

        # FIXMe: without line below app crashes on exit after settings opened
        self._dialog.mousePressEvent = self.on_mouse_press_event

    def on_mouse_press_event(self, ev):
        pass

    def _connect_slots(self):
        ui = self._ui

        ui.logout_button.clicked.connect(self._logout)

        ui.download_auto_radioButton.clicked.connect(
            lambda: ui.download_limit_edit.setEnabled(
                False) or ui.download_limit_edit.clear())
        ui.download_limit_radioButton.clicked.connect(
            lambda: ui.download_limit_edit.setEnabled(True))

        ui.upload_auto_radioButton.clicked.connect(
            lambda: ui.upload_limit_edit.setEnabled(
                False) or ui.upload_limit_edit.clear())
        ui.upload_limit_radioButton.clicked.connect(
            lambda: ui.upload_limit_edit.setEnabled(True))

        ui.buttonBox.accepted.connect(self._dialog.accept)
        ui.buttonBox.rejected.connect(self._dialog.reject)

        ui.smart_sync_button.clicked.connect(
            self._on_smart_sync_button_clicked)

        ui.location_button.clicked.connect(
            self._on_sync_folder_location_button_clicked)

        ui.location_button.enterEvent = lambda _: \
            ui.location_button.setIcon(QIcon(
                ':/images/settings/pencil_hovered.svg'))
        ui.location_button.leaveEvent = lambda _: \
            ui.location_button.setIcon(QIcon(
                ':/images/settings/pencil.svg'))
        ui.smart_sync_button.enterEvent = lambda _: \
            ui.smart_sync_button.setIcon(QIcon(
                ':/images/settings/folder_sync_hovered.svg'))
        ui.smart_sync_button.leaveEvent = lambda _: \
            ui.smart_sync_button.setIcon(QIcon(
                ':/images/settings/folder_sync.svg'))
        ui.logout_button.enterEvent = lambda _: \
            ui.logout_button.setIcon(QIcon(
                ':/images/settings/logout_hovered.svg'))
        ui.logout_button.leaveEvent = lambda _: \
            ui.logout_button.setIcon(QIcon(
                ':/images/settings/logout.svg'))

    def _set_fonts(self):
        ui = self._ui
        controls = [ui.tabWidget, ui.language_comboBox]
        controls.extend([c for c in ui.tabWidget.findChildren(QLabel)])
        controls.extend([c for c in ui.tabWidget.findChildren(QLineEdit)])
        controls.extend([c for c in ui.tabWidget.findChildren(QPushButton)])
        controls.extend([c for c in ui.tabWidget.findChildren(QCheckBox)])
        controls.extend([c for c in ui.tabWidget.findChildren(QRadioButton)])

        for control in controls:
            font = control.font()
            font_size = control.font().pointSize() * self._dp
            if font_size > 0:
                control_font = QFont(font.family(), font_size)
                control_font.setBold(font.bold())
                control.setFont(control_font)

    def _logout(self):
        userAnswer = msgbox(tr('Keep local files on device?'),
                            buttons=[
                                (tr('Clear all'), 'Wipe'),
                                (tr('Keep'), 'Keep'),
                            ],
                            parent=self._dialog,
                            default_index=1,
                            enable_close_button=True)

        if userAnswer == '':
            return

        wipe_all = userAnswer == 'Wipe'
        if not wipe_all:
            self._cfg.set_settings({'user_password_hash': ""})

        self.logged_out.emit(wipe_all)

        self._dialog.reject()

    def show(self, on_finished):
        def finished():
            if self._dialog.result() == QDialog.Accepted:
                self._apply_settings()
            self._dialog.finished.disconnect(finished)
            on_finished()

        self._setup_to_ui()
        if self._migrate:
            self._ui.tabWidget.setCurrentIndex(1)  # Account page
            QTimer.singleShot(100,
                              self._on_sync_folder_location_button_clicked)
        self._dialog.finished.connect(finished)
        self._dialog.raise_()
        self._dialog.setModal(True)
        self._dialog.show()

    def _setup_to_ui(self):
        ui = self._ui
        cfg = self._cfg

        portable = is_portable()

        if cfg.get_setting('lang', None) is None:
            self._ui.language_comboBox.setCurrentIndex(0)
        else:
            lang = cfg.lang if cfg.lang in get_available_languages() else 'en'
            assert lang in get_available_languages()
            for i in range(1, ui.language_comboBox.count()):
                if ui.language_comboBox.itemText(i) == lang:
                    ui.language_comboBox.setCurrentIndex(i)
                    break

        ui.location_edit.setText(
            FilePath(cfg.sync_directory) if cfg.sync_directory else '')
        ui.location_button.setEnabled(not portable)
        if portable:
            ui.location_button.setToolTip(tr("Disabled in portable version"))
        ui.email_label.setText(cfg.user_email if cfg.user_email else '')

        def set_limit(limit, auto_btn, manual_btn, edit):
            edit.setValidator(QRegExpValidator(QRegExp("\\d{1,9}")))
            if limit:
                manual_btn.setChecked(True)
                edit.setText(str(limit))
            else:
                auto_btn.setChecked(True)
                auto_btn.click()

        set_limit(limit=cfg.download_limit,
                  auto_btn=ui.download_auto_radioButton,
                  manual_btn=ui.download_limit_radioButton,
                  edit=ui.download_limit_edit)
        set_limit(limit=cfg.upload_limit,
                  auto_btn=ui.upload_auto_radioButton,
                  manual_btn=ui.upload_limit_radioButton,
                  edit=ui.upload_limit_edit)

        ui.autologin_checkbox.setChecked(self._main_cfg.autologin)
        ui.autologin_checkbox.setEnabled(not portable)
        if portable:
            ui.autologin_checkbox.setToolTip(
                tr("Disabled in portable version"))
        ui.tracking_checkbox.setChecked(cfg.send_statistics)
        ui.autoupdate_checkbox.setChecked(self._main_cfg.autoupdate)
        ui.download_backups_checkBox.setChecked(cfg.download_backups)
        ui.is_smart_sync_checkBox.setChecked(cfg.smart_sync)
        ui.disable_logging_checkBox.setChecked(self._main_cfg.logging_disabled)

        # Disable smart sync for free license
        if not cfg.license_type or cfg.license_type == FREE_LICENSE:
            ui.is_smart_sync_checkBox.setText(
                tr("SmartSync+ is not available for your license"))
            ui.is_smart_sync_checkBox.setChecked(False)
            ui.is_smart_sync_checkBox.setCheckable(False)
            ui.smart_sync_button.setEnabled(False)

        ui.startup_checkbox.setChecked(is_in_system_startup())
        ui.startup_checkbox.setEnabled(not portable)
        if portable:
            ui.startup_checkbox.setToolTip(tr("Disabled in portable version"))

    def _apply_settings(self):
        service_settings, main_settings = self._get_configs_from_ui()
        if main_settings['logging_disabled'] != \
                self._main_cfg.logging_disabled:
            self.logging_disabled_changed.emit(
                main_settings['logging_disabled'])
        self._cfg.set_settings(service_settings)
        self._main_cfg.set_settings(main_settings)
        if self._ui.startup_checkbox.isChecked():
            if not is_in_system_startup():
                add_to_system_startup()
        else:
            if is_in_system_startup():
                remove_from_system_startup()

    def _config_is_changed(self):
        service_settings, main_settings = self._get_configs_from_ui()
        for param, value in service_settings.items():
            if self._cfg.get_setting(param) != value:
                return True
        for param, value in main_settings.items():
            if self._main_cfg.get_setting(param) != value:
                return True

        return False

    def _get_configs_from_ui(self):
        ui = self._ui
        return {
            'lang': (str(ui.language_comboBox.currentText())
                     if ui.language_comboBox.currentIndex() > 0 else None),
            'upload_limit': (0 if ui.upload_auto_radioButton.isChecked()
                             or not ui.upload_limit_edit.text() else int(
                                 ui.upload_limit_edit.text())),
            'download_limit': (0 if ui.download_auto_radioButton.isChecked()
                               or not ui.download_limit_edit.text() else int(
                                   ui.download_limit_edit.text())),
            'send_statistics':
            bool(ui.tracking_checkbox.isChecked()),
            'download_backups':
            bool(ui.download_backups_checkBox.isChecked()),
            'smart_sync':
            bool(ui.is_smart_sync_checkBox.isChecked()),
            'autologin':
            bool(ui.autologin_checkbox.isChecked()),
        }, {
            'autologin': bool(ui.autologin_checkbox.isChecked()),
            'autoupdate': bool(ui.autoupdate_checkbox.isChecked()),
            'logging_disabled': bool(ui.disable_logging_checkBox.isChecked()),
            'download_backups': bool(ui.download_backups_checkBox.isChecked()),
        }

    def _on_smart_sync_button_clicked(self):
        self._get_offline_dirs()
        root = str(self._ui.location_edit.text())
        self._smart_sync_dialog = SmartSyncDialog(self._dialog)
        offline, online = self._smart_sync_dialog.show(root_path=root,
                                                       hide_dotted=True)
        if offline or online:
            logger.info("Directories set to be offline: (%s)",
                        ", ".join(map(lambda s: u"'%s'" % s, offline)))
            self._set_offline_dirs(offline, online)

    def offline_dirs(self, offline_dirs):
        root = str(self._ui.location_edit.text())
        pc = PathConverter(root)
        offline_dirs_abs_paths = set(
            map(lambda p: pc.create_abspath(p), offline_dirs))
        if self._smart_sync_dialog:
            self._smart_sync_dialog.set_offline_paths(offline_dirs_abs_paths)

    def _on_sync_folder_location_button_clicked(self):
        selected_folder = QFileDialog.getExistingDirectory(
            self._dialog, tr('Choose Pvtbox folder location'),
            get_parent_dir(FilePath(self._cfg.sync_directory)))
        selected_folder = ensure_unicode(selected_folder)

        try:
            if not selected_folder:
                raise self._MigrationFailed("Folder is not selected")

            if len(selected_folder + "/Pvtbox") > self._max_root_len:
                if not self._migrate:
                    msgbox(tr("Destination path too long. "
                              "Please select shorter path."),
                           tr("Path too long"),
                           parent=self._dialog)
                raise self._MigrationFailed("Destination path too long")

            free_space = get_free_space(selected_folder)
            selected_folder = get_data_dir(dir_parent=selected_folder,
                                           create=False)
            if FilePath(selected_folder) == FilePath(self._cfg.sync_directory):
                raise self._MigrationFailed("Same path selected")

            if FilePath(selected_folder) in FilePath(self._cfg.sync_directory):
                msgbox(tr("Can't migrate into existing Pvtbox folder.\n"
                          "Please choose other location"),
                       tr("Invalid Pvtbox folder location"),
                       parent=self._dialog)
                raise self._MigrationFailed(
                    "Can't migrate into existing Pvtbox folder")

            if self._size and free_space < self._size:
                logger.debug(
                    "No disk space in %s. Free space: %s. Needed: %s.",
                    selected_folder, free_space, self._size)
                msgbox(tr(
                    "Insufficient disk space for migration to\n{}.\n"
                    "Please clean disk", selected_folder),
                       tr("No disk space"),
                       parent=self._dialog)
                raise self._MigrationFailed(
                    "Insufficient disk space for migration")

            self._migration_cancelled = False
            dialog = QProgressDialog(self._dialog)
            dialog.setWindowTitle(tr('Migrating to new Pvtbox folder'))
            dialog.setWindowIcon(QIcon(':/images/icon.svg'))
            dialog.setModal(True)
            dialog.setMinimum(0)
            dialog.setMaximum(100)
            dialog.setMinimumSize(400, 80)
            dialog.setAutoClose(False)

            def progress(value):
                logger.debug("Migration dialog progress received: %s", value)
                dialog.setValue(value)

            def migration_failed(error):
                logger.warning("Migration failed with error: %s", error)
                msgbox(error,
                       tr('Migration to new Pvtbox folder error'),
                       parent=dialog)
                dialog.cancel()
                self._migration_cancelled = True
                done()

            def cancel():
                logger.debug("Migration dialog cancelled")
                self._migration_cancelled = True
                self._migration.cancel()

            def done():
                logger.debug("Migration done")
                try:
                    self._migration.progress.disconnect(progress)
                    self._migration.failed.disconnect(migration_failed)
                    self._migration.done.disconnect(done)
                    dialog.canceled.disconnect(cancel)
                except Exception as e:
                    logger.warning("Can't disconnect signal %s", e)
                dialog.hide()
                dialog.done(QDialog.Accepted)
                dialog.close()

            self._migration = SyncDirMigration(self._cfg, parent=self._dialog)
            self._migration.progress.connect(progress, Qt.QueuedConnection)
            self._migration.failed.connect(migration_failed,
                                           Qt.QueuedConnection)
            self._migration.done.connect(done, Qt.QueuedConnection)
            dialog.canceled.connect(cancel)
            self._exit_service()
            old_dir = self._cfg.sync_directory
            self._migration.migrate(old_dir, selected_folder)

            def on_finished():
                logger.info("Migration dialog closed")
                if not self._migration_cancelled:
                    logger.debug("Setting new location")
                    self._ui.location_edit.setText(FilePath(selected_folder))

                    disable_file_logging(logger)
                    shutil.rmtree(op.join(old_dir, '.pvtbox'),
                                  ignore_errors=True)
                    set_root_directory(FilePath(selected_folder))
                    enable_file_logging(logger)

                    make_dir_hidden(get_patches_dir(selected_folder))

                self._start_service()

            dialog.finished.connect(on_finished)
            dialog.show()

        except self._MigrationFailed as e:
            logger.warning("Sync dir migration failed. Reason: %s", e)
        finally:
            if self._migrate:
                self._dialog.accept()
    def accept(self) -> None:
        """
		When the user clicks "OK", the ports and name are set on the Action.
		
		:return: None
		:rtype: NoneType
		"""
        apim = sm.StateMachine.instance._project.getAPIModel()

        # get copies of input and output ports for validation.
        inputPorts = []
        il = self.ui.inputLayout
        for i in range(il.count() - 1):
            pew = il.itemAt(i).widget()
            inputPorts.append(pew.previewPort())

        outputPorts = []
        il = self.ui.outputLayout
        for i in range(il.count() - 1):
            pew = il.itemAt(i).widget()
            outputPorts.append(pew.previewPort())

        # clear error message
        self.ui.errorLabel.setText("")
        errors = []
        #################################
        # VALIDATE ACTION NAME AND PORTS
        #################################
        name = self.ui.actionName.text().strip()

        # Make sure name of action pipeline is unique
        if name in [ap.getName() for ap in apim.getActionPipelines()]:
            errors.append(
                "An action pipeline with the name '%s' already exists." % name)

        # Make sure name is a valid identifier
        if not name.isidentifier():
            errors.append(
                "The name of the action pipeline must be a valid Python identifier."
            )

        # Name must start with a lower-case character
        if name[0].isupper():
            errors.append(
                "The action name must start with a lower-case letter.")

        # Make sure all inputs have unique names.
        inputNames = set()
        for port in inputPorts:
            if port.getName() in inputNames:
                errors.append(
                    "Duplicate name '{}' in input ports is not allowed".format(
                        port.getName()))
            inputNames.add(port.getName())

        # Make sure all outputs have unique names.
        outputNames = set()
        for port in outputPorts:
            if port.getName() in outputNames:
                errors.append(
                    "Duplicate name '{}' in output ports is not allowed.".
                    format(port.getName()))
            outputNames.add(port.getName())

        # Make sure that optional inputs come after required inputs
        optionalFound = False
        for port in inputPorts:
            if port.isOptional():
                optionalFound = True
            if optionalFound and not port.isOptional():
                errors.append(
                    "Optional inputs must come after required inputs.")
                break

        # Make sure that optional inputs have a default value
        for port in inputPorts:
            if port.isOptional():
                if port.getDefaultValue() == "":
                    errors.append(
                        "All optional ports must have default values.")

        # Make sure the type of the port is not NoneType
        for port in inputPorts + outputPorts:
            if port.getDataType() == type(None):
                errors.append("Data type of port cannot be NoneType.")
                break

        # If type is simple, and port is optional, check the type of the default value.
        checkable_types = [int, float, str, bool]
        for port in inputPorts:
            if port.isOptional() and port.getDataType() != type(None):
                default = port.getDefaultValue()
                t = port.getDataType()
                bad = False
                try:
                    result = t(default)
                except ValueError:
                    bad = True
                else:
                    if t == bool and result is False:
                        bad = True

                if t == str:
                    validEnds = default.startswith('"') and default.endswith(
                        '"') or default.startswith("'") and default.endswith(
                            "'")
                    if not validEnds:
                        errors.append(
                            "String values must be enclosed by quotes.")
                    else:
                        if default[0] in default[1:-1]:
                            errors.append(
                                "Avoid using the same quote character in the default "
                                "value as the one that encloses the default value."
                            )

                if bad:
                    errors.append(
                        "The default value is not the correct type for port '{}'."
                        .format(port.getName()))

        if len(errors) != 0:
            errMsg = "Errors:\n"
            for err in errors:
                errMsg += "\t" + err + "\n"
            self.ui.errorLabel.setText(errMsg)
            self.ui.errorLabel.show()
            return
        else:
            self.ui.errorLabel.hide()

        #################################
        # EDIT ACTION NAME AND PORTS
        #################################
        self._action.setName(name)

        # add new input ports to the action
        il = self.ui.inputLayout
        newPorts = []
        for i in range(il.count() - 1):
            pew = il.itemAt(i).widget()
            pew.updatePort()
            port = pew.getPort()
            newPorts.append(port)
            if port.getAction() is None:
                self._action.addInputPort(port)

        # remove old input ports from the action.
        for port in self._action.getInputPorts():
            if port not in newPorts:
                self._action.removePort(port)

        # remove old input ports from the action.
        for port in self._action.getInputPorts():
            if port not in newPorts:
                self._action.removePort(port)

        # add new output ports to the action.
        ol = self.ui.outputLayout
        newPorts = []
        for i in range(ol.count() - 1):
            pew = ol.itemAt(i).widget()
            pew.updatePort()
            port = pew.getPort()
            newPorts.append(port)
            if port.getAction() is None:
                self._action.addOutputPort(port)

        # remove old output ports from the action.
        for port in self._action.getOutputPorts():
            if port not in newPorts:
                self._action.removePort(port)

        return QDialog.accept(self)
Пример #10
0
    def accept(self) -> None:
        """
		This method is called when the user clicks the "OK" button.
		
		It will validate all of the user's input and show error messages if
		any information is invalid.
		
		:emits: projectCreated if a project was successfully created
		:return: None
		:rtype: NoneType
		"""

        oldPath = self.ui.oldPathEdit.text()
        oldName = self.ui.oldNameEdit.text()
        oldDescription = self.ui.oldDescriptionEdit.toPlainText()
        oldExe = self.ui.oldAppEdit.text()
        newPath = self.ui.newPathEdit.text()
        newName = self.ui.newNameEdit.text()
        newDescription = self.ui.newDescriptionEdit.toPlainText()
        newExe = self.ui.newAppEdit.text()

        # clear error messages
        self.ui.oldErrorLabel.setText("")
        self.ui.newErrorLabel.setText("")

        # detect any errors
        oldErrors = []
        newErrors = []

        # check existing project for errors (as much as we can)
        if not self._oldProject:
            oldErrors.append("must select an existing project")

        # check new project details for errors
        if not newName:
            newErrors.append("new project must have a name")

        if not newDescription:
            newErrors.append("new project must have a description")

        if newExe != oldExe:
            newErrors.append(
                "new project must use the same executable as the old project")
        if not newPath:
            newErrors.append("Must specify location of new project")
        else:
            isAlreadyProject = False
            for file in os.listdir(newPath):
                if file[-4:] == ".fcl":
                    isAlreadyProject = True
                    break
            if isAlreadyProject:
                newErrors.append(
                    "The selected project directory already belongs to a different project. Please select another."
                )

        # if there are any errors, show them, then return.
        if len(newErrors) != 0:
            errMsg = "Errors:\n"
            for err in newErrors:
                errMsg += "\t" + err + "\n"
            self.ui.newErrorLabel.setText(errMsg)
        if len(oldErrors) != 0:
            errMsg = "Errors:\n"
            for err in oldErrors:
                errMsg += "\t" + err + "\n"
            self.ui.oldErrorLabel.setText(errMsg)

        # if there are no errors, create a new project, emit the projectCreated signal, and
        # call the super-class's accept method to perform the default behavior.
        if len(newErrors) == 0 and len(oldErrors) == 0:
            self._newProject.setName(newName)
            self._newProject.setDescription(newDescription)
            self._newProject.setProjectDir(newPath)
            self.projectCreated.emit(self._newProject)
            return QDialog.accept(self)
Пример #11
0
 def accept(self):
     self.accepted.emit(self.ui.volumeInput.value(),
                        self.ui.pHInput.value())
     QDialog.accept(self)
Пример #12
0
class TutorialDialog(object):
    def __init__(self, parent, dp=None):
        self._dialog = QDialog(parent)
        self._dp = dp
        self._dialog.setWindowIcon(QIcon(':/images/icon.png'))
        self._ui = Ui_Dialog()
        self._ui.setupUi(self._dialog)

        self._current_index = 0
        self._ui.slides.setCurrentIndex(self._current_index)
        self._slides_count = self._ui.slides.count()

        self._ui.next_button.clicked.connect(self._on_next_button_clicked)
        self._ui.prev_button.clicked.connect(self._on_prev_button_clicked)

        self._point_enabled_style = "background-color:#f9af61; " \
                                    "border: 2px solid; " \
                                    "border-radius: 3px; " \
                                    "border-color:#f9af61;"
        self._point_disabled_style = "background-color:#cccccc; " \
                                     "border: 2px solid; " \
                                     "border-radius: 3px; " \
                                     "border-color:#cccccc;"
        self._points = [self._ui.point]
        self._init_points()

        self._setup_buttons()

        self._set_labels_fonts()
        self._set_controls_font([self._ui.next_button, self._ui.prev_button])

        self._slide_show = SlideShow(self._dialog, self._ui.slides)
        self._slide_show.current_index_changed.connect(
            self._on_current_index_changed)
        self._slide_show.clicked.connect(self._on_next_button_clicked)
        self._slide_show.key_pressed.connect(self._on_key_pressed)

        self._dialog.keyPressEvent = self._on_key_pressed

    def _on_next_button_clicked(self):
        if self._current_index + 1 >= self._slides_count:
            self._close()
            return

        self._slide_show.setCurrentIndex(self._current_index + 1)

    def _on_prev_button_clicked(self):
        if self._current_index - 1 < 0:
            return

        self._slide_show.setCurrentIndex(self._current_index - 1)

    def _on_key_pressed(self, ev):
        if ev.key() == Qt.Key_Left:
            self._on_prev_button_clicked()
        elif ev.key() == Qt.Key_Right:
            self._on_next_button_clicked()

    def _on_current_index_changed(self, new_index):
        self._current_index = new_index
        self._setup_buttons()

    def _setup_buttons(self):
        if self._current_index == 0:
            self._ui.prev_button.setDisabled(True)
            self._ui.prev_button.setStyleSheet(
                "border: 0; color:#ffffff; text-align:left;")
        else:
            self._ui.prev_button.setDisabled(False)
            self._ui.prev_button.setStyleSheet(
                "border: 0; color:#222222; text-align:left;")
        if self._current_index + 1 == self._slides_count:
            self._ui.next_button.setText(tr("GOT IT"))
        else:
            self._ui.next_button.setText(tr("NEXT"))

        self._setup_points()

    def _init_points(self):
        self._points[0].setText(' ')
        self._points[0].setFont(QFont("Noto Sans", 2))
        for i in range(1, self._slides_count):
            new_point = QLabel()
            new_point.setText(' ')
            new_point.setFont(QFont("Noto Sans", 2))
            new_point.setStyleSheet(self._point_disabled_style)
            self._points.append(new_point)
            self._ui.points_layout.addSpacing(8)
            self._ui.points_layout.addWidget(new_point)

    def _setup_points(self):
        for i, point in enumerate(self._points):
            style = self._point_enabled_style if i == self._current_index \
                else self._point_disabled_style
            point.setStyleSheet(style)

    def _set_labels_fonts(self):
        self._set_controls_font(self._ui.slides.findChildren(QLabel))

    def _set_controls_font(self, controls):
        if not self._dp or self._dp == 1:
            return

        for control in controls:
            font = control.font()
            font_size = font.pointSize() * self._dp
            control_font = QFont(font.family(),
                                 font_size,
                                 italic=font.italic())
            control_font.setBold(font.bold())
            control.setFont(control_font)

    def _close(self):
        self._slide_show.current_index_changed.disconnect(
            self._on_current_index_changed)
        self._dialog.accept()
        self._dialog.close()

    def show(self):
        logger.debug("Opening tutorial dialog")

        # Execute dialog
        self._dialog.exec_()
Пример #13
0
    def accept(self) -> None:
        """
		This method is called when the user clicks the "OK" button.
		
		It will validate all of the user's input and show error messages if
		any information is invalid.
		
		:emits: projectCreated if a project was successfully created
		:return: None
		:rtype: NoneType
		"""

        name = self.ui.project_name_edit.text()
        description = self.ui.description_edit.toPlainText()
        projectDir = self.ui.project_folder_edit.text()
        appExe = self.ui.executable_file_edit.text()

        # clear error message
        self.ui.error_label.setText("")

        # detect any errors
        errors = []
        if not self.ui.project_name_edit.text():
            errors.append("Need project name")
        if not self.ui.description_edit.toPlainText():
            errors.append("Need project description")

        # Check for valid project directory.
        if not projectDir:
            errors.append("Need to select project folder")
        else:
            isAlreadyProject = False
            for file in os.listdir(projectDir):
                if file[-4:] == ".fcl":
                    isAlreadyProject = True
                    break

            if isAlreadyProject:
                errors.append(
                    "The selected project directory already belongs to a different project. Please select another."
                )

        # Check for valid target application executable
        if not appExe:
            errors.append("Need to select target application executable")
        else:
            if not isExecutable(appExe):
                errors.append("Target application must be an executable file.")
            elif not appBitnessMatches(appExe):
                pyBit = getPythonBitness()
                appBit = getExeBitness(appExe)
                errors.append(
                    "{} bit Python cannot control {} bit application".format(
                        pyBit, appBit))

        # Check for valid framework
        frameworkOption = self._radioBtnGroup.checkedButton()
        framework = frameworkOption.text()
        if frameworkOption == self.ui.option_Other:
            framework = self.ui.other_edit.text()
        if not framework:
            errors.append("Must specify which framework is being used")

        # check for valid backend
        backendWidget = frameworkOption.parentWidget()
        backend = ""
        if isinstance(backendWidget, QGroupBox):
            backend = backendWidget.title()
        else:
            # TODO: Get best backend for target application. (using function from TGUIIL)
            pass

        # if there are any errors, show them, then return.
        if len(errors) != 0:
            errMsg = "Errors:\n"
            for err in errors:
                errMsg += "\t" + err + "\n"
            self.ui.error_label.setText(errMsg)
            return

        # if there are no errors, create a new project, emit the projectCreated signal, and
        # call the super-class's accept method to perform the default behavior.
        else:
            newProject = Project(name,
                                 description,
                                 appExe,
                                 backend,
                                 projectDir=projectDir)
            self.projectCreated.emit(newProject)
            return QDialog.accept(self)