Пример #1
0
class GalaxyOwnPlanetSelectorWidget(QFrame):

    planetSelected = pyqtSignal(int)

    def __init__(self, parent: QWidget):
        super(GalaxyOwnPlanetSelectorWidget, self).__init__(parent)
        self.world = XNovaWorld_instance()
        # setup frame
        self.setFrameShadow(QFrame.Raised)
        self.setFrameShape(QFrame.StyledPanel)
        # layout
        self._layout = QVBoxLayout()
        self._layout.setContentsMargins(6, 6, 6, 6)
        self._layout.setSpacing(0)
        self.setLayout(self._layout)
        # label
        self._lbl = QLabel(self.tr('Select planet:'), self)
        # combo
        self._cb = QComboBox(self)
        self._cb.setEditable(False)
        self._cb.setInsertPolicy(QComboBox.InsertAtBottom)
        self._cb.currentIndexChanged.connect(self.on_cb_currentChanged)
        # finalize layout
        self._layout.addWidget(self._lbl, 0, Qt.AlignHCenter)
        self._layout.addWidget(self._cb, 0, Qt.AlignCenter)
        # ...
        self.fill_planets()

    def fill_planets(self):
        planets = self.world.get_planets()
        for pl in planets:
            st = '{0} {1}'.format(pl.name, pl.coords.coords_str())
            dt = QVariant(pl.planet_id)
            self._cb.addItem(st, dt)

    @pyqtSlot(int)
    def on_cb_currentChanged(self, index: int):
        if index == -1:
            return
        planet_id = int(self._cb.currentData(Qt.UserRole))
        self.planetSelected.emit(planet_id)
class RenameWidget(FlexiFrame):
    """
    Display combo boxes for file renaming and file extension case handling, and
    an example file name
    """

    def __init__(
        self,
        preset_type: PresetPrefType,
        prefs: Preferences,
        exiftool_process: exiftool.ExifTool,
        parent,
    ) -> None:
        super().__init__(parent=parent)
        self.setBackgroundRole(QPalette.Base)
        self.setAutoFillBackground(True)
        self.exiftool_process = exiftool_process
        self.prefs = prefs
        self.preset_type = preset_type
        if preset_type == PresetPrefType.preset_photo_rename:
            self.file_type = FileType.photo
            self.pref_defn = DICT_IMAGE_RENAME_L0
            self.generation_type = NameGenerationType.photo_name
            self.index_lookup = self.prefs.photo_rename_index
            self.pref_conv = PHOTO_RENAME_MENU_DEFAULTS_CONV
            self.generation_type = NameGenerationType.photo_name
        else:
            self.file_type = FileType.video
            self.pref_defn = DICT_VIDEO_RENAME_L0
            self.generation_type = NameGenerationType.video_name
            self.index_lookup = self.prefs.video_rename_index
            self.pref_conv = VIDEO_RENAME_MENU_DEFAULTS_CONV
            self.generation_type = NameGenerationType.video_name

        self.sample_rpd_file = make_sample_rpd_file(
            sample_job_code=self.prefs.most_recent_job_code(missing=_("Job Code")),
            prefs=self.prefs,
            generation_type=self.generation_type,
        )

        layout = QFormLayout()
        self.layout().addLayout(layout)

        self.getCustomPresets()

        self.renameCombo = PresetComboBox(
            prefs=self.prefs,
            preset_names=self.preset_names,
            preset_type=preset_type,
            parent=self,
            edit_mode=False,
        )
        self.setRenameComboIndex()
        self.renameCombo.activated.connect(self.renameComboItemActivated)

        # File extensions
        self.extensionCombo = QComboBox()
        self.extensionCombo.addItem(_(ORIGINAL_CASE), ORIGINAL_CASE)
        self.extensionCombo.addItem(_(UPPERCASE), UPPERCASE)
        self.extensionCombo.addItem(_(LOWERCASE), LOWERCASE)
        if preset_type == PresetPrefType.preset_photo_rename:
            pref_value = self.prefs.photo_extension
        else:
            pref_value = self.prefs.video_extension
        try:
            index = [ORIGINAL_CASE, UPPERCASE, LOWERCASE].index(pref_value)
        except ValueError:
            if preset_type == PresetPrefType.preset_photo_rename:
                t = "Photo"
            else:
                t = "Video"
            logging.error(
                "%s extension case value is invalid. Resetting to lower case.", t
            )
            index = 2
        self.extensionCombo.setCurrentIndex(index)
        self.extensionCombo.currentIndexChanged.connect(self.extensionChanged)

        self.example = QLabel()
        self.updateExampleFilename()

        layout.addRow(_("Preset:"), self.renameCombo)
        layout.addRow(_("Extension:"), self.extensionCombo)
        layout.addRow(_("Example:"), self.example)

        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)

    def setRenameComboIndex(self) -> None:
        """
        Set the value being displayed in the combobox to reflect the
        current renaming preference.

        Takes into account built-in renaming presets and custom presets.
        """

        index = self.index_lookup(self.preset_pref_lists)
        if index == -1:
            # Set to the "Custom..." value
            cb_index = self.renameCombo.count() - 1
        else:
            # Set to appropriate combobox idex, allowing for possible separator
            cb_index = self.renameCombo.getComboBoxIndex(index)
        logging.debug(
            "Setting %s combobox chosen value to %s",
            self.file_type.name,
            self.renameCombo.itemText(cb_index),
        )
        self.renameCombo.setCurrentIndex(cb_index)

    def pref_list(self) -> List[str]:
        """
        :return: the user's file naming preference according to whether
         this widget is handling photos or videos
        """
        if self.preset_type == PresetPrefType.preset_photo_rename:
            return self.prefs.photo_rename
        else:
            return self.prefs.video_rename

    @pyqtSlot(int)
    def renameComboItemActivated(self, index: int) -> None:
        """
        Respond to user activating the Rename preset combo box.

        :param index: index of the item activated
        """

        user_pref_list = None

        preset_class = self.renameCombo.currentData()
        if preset_class == PresetClass.start_editor:

            prefDialog = PrefDialog(
                self.pref_defn,
                self.pref_list(),
                self.generation_type,
                self.prefs,
                self.sample_rpd_file,
            )

            if prefDialog.exec():
                user_pref_list = prefDialog.getPrefList()
                if not user_pref_list:
                    user_pref_list = None

            # Regardless of whether the user clicked OK or cancel, refresh the rename combo
            # box entries
            self.getCustomPresets()
            self.renameCombo.resetEntries(self.preset_names)
            self.setUserPrefList(user_pref_list=user_pref_list)
            self.setRenameComboIndex()
        else:
            assert (
                preset_class == PresetClass.custom
                or preset_class == PresetClass.builtin
            )
            index = self.renameCombo.getPresetIndex(self.renameCombo.currentIndex())
            user_pref_list = self.combined_pref_lists[index]
            self.setUserPrefList(user_pref_list=user_pref_list)

        self.updateExampleFilename()

    def getCustomPresets(self) -> None:
        """
        Get the custom presets from the user preferences and store them in lists
        """

        self.preset_names, self.preset_pref_lists = self.prefs.get_preset(
            preset_type=self.preset_type
        )
        self.combined_pref_lists = self.pref_conv + tuple(self.preset_pref_lists)

    def setUserPrefList(self, user_pref_list: List[str]) -> None:
        """
        Update the user preferences with a new preference value
        :param user_pref_list: the photo or video rename preference list
        """

        if user_pref_list is not None:
            logging.debug("Setting new %s rename preference value", self.file_type.name)
            if self.preset_type == PresetPrefType.preset_photo_rename:
                self.prefs.photo_rename = user_pref_list
            else:
                self.prefs.video_rename = user_pref_list

    def updateExampleFilename(
        self,
        downloads_today: Optional[List[str]] = None,
        stored_sequence_no: Optional[int] = None,
    ) -> None:
        """
        Update filename shown to user that serves as an example of the
        renaming rule in practice on sample data.

        :param downloads_today: if specified, update the downloads today value
        :param stored_sequence_no: if specified, update the stored sequence value
        """

        if downloads_today:
            self.sample_rpd_file.sequences.downloads_today_tracker.downloads_today = (
                downloads_today
            )
        if stored_sequence_no is not None:
            self.sample_rpd_file.sequences.stored_sequence_no = stored_sequence_no

        if self.preset_type == PresetPrefType.preset_photo_rename:
            self.name_generator = gn.PhotoName(self.prefs.photo_rename)
            logging.debug("Updating example photo name in rename panel")
        else:
            self.name_generator = gn.VideoName(self.prefs.video_rename)
            logging.debug("Updating example video name in rename panel")

        self.example.setText(self.name_generator.generate_name(self.sample_rpd_file))

    def updateSampleFile(self, sample_rpd_file: Union[Photo, Video]) -> None:
        self.sample_rpd_file = make_sample_rpd_file(
            sample_rpd_file=sample_rpd_file,
            sample_job_code=self.prefs.most_recent_job_code(missing=_("Job Code")),
            prefs=self.prefs,
            generation_type=self.generation_type,
        )
        self.updateExampleFilename()

    @pyqtSlot(int)
    def extensionChanged(self, index: int) -> None:
        """
        Respond to user changing the case of file extensions in file name generation.

        Save new preference value, and update example file name.
        """

        value = self.extensionCombo.currentData()
        if self.preset_type == PresetPrefType.preset_photo_rename:
            self.prefs.photo_extension = value
        else:
            self.prefs.video_extension = value
        self.sample_rpd_file.generate_extension_case = value
        self.updateExampleFilename()
Пример #3
0
class InputData(QWidget):

    submitted = pyqtSignal(object)

    def __init__(self,
                 msg,
                 num_of_fields,
                 default_value=None,
                 numeric=False,
                 placeholders=None,
                 dropdown=False,
                 dropdown_text=None,
                 dropdown_options=None):
        super(InputData, self).__init__()

        self.display_msg = msg

        self.number_of_input_fields = num_of_fields

        self.numeric = numeric

        if default_value is not None:
            self.default = default_value
        else:
            self.default = []
            for i in range(num_of_fields):
                self.default.append("")

        if placeholders is not None:
            self.placeholders = placeholders
        else:
            self.placeholders = []
            for i in range(num_of_fields):
                self.placeholders.append("Unspecified field")

        if dropdown:
            self.dropdown = True
            self.dropdown_text = dropdown_text
            self.dropdown_options = dropdown_options
        else:
            self.dropdown = False

        self.textboxes = []

        self.init_ui()

    def init_ui(self):
        _, _, width, height = QDesktopWidget().screenGeometry().getCoords()
        self.setGeometry(int(0.2 * width), int(0.2 * height), 300, 200)

        self.setWindowTitle("Ola senor !")
        self.setWindowIcon(QIcon("img/question_mark_icon.png"))

        v_layout = QVBoxLayout()
        self.explanation_text_label = QLabel(self.display_msg)
        v_layout.addWidget(self.explanation_text_label)

        if self.dropdown:
            v_layout.addWidget(QLabel(self.dropdown_text))
            self.combobox = QComboBox()
            for name, data in self.dropdown_options.items():
                self.combobox.addItem(name, data)
            v_layout.addWidget(self.combobox)

        for i in range(self.number_of_input_fields):
            textbox = QLineEdit(self.default[i])
            textbox.setPlaceholderText(self.placeholders[i])
            self.textboxes.append(textbox)
            v_layout.addWidget(textbox)
        # self.textbox_input_value = QLineEdit(self.default)
        # v_layout.addWidget(self.textbox_input_value)
        self.submit_btn = QPushButton("OK")
        self.submit_btn.clicked.connect(self.submit_data)
        v_layout.addWidget(self.submit_btn)

        self.setLayout(v_layout)

        self.show()

    def submit_data(self):

        send_value = []
        for i, numeric_required in enumerate(self.numeric):
            if numeric_required:
                if is_numeric(self.textboxes[i].text()):
                    send_value.append(self.textboxes[i].text())
                else:
                    show_error_message("Warning",
                                       "Input data has to be numeric")
                    return
            else:
                send_value.append(self.textboxes[i].text())

        if self.dropdown:
            send_value.append(self.combobox.currentData())

        self.submitted.emit(send_value)
        self.close()
Пример #4
0
class App(QDialog):
    def __init__(self):
        super().__init__()
        self.title = 'Consensus Plotter'
        self.left = 10
        self.top = 10
        self.width = 320
        self.height = 100
        self.initUI()
        self.setWindowIcon(QIcon(resource_path('app_icon.ico')))

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.createGridLayout()

        windowLayout = QVBoxLayout()
        windowLayout.addWidget(self.horizontalGroupBox)
        self.setLayout(windowLayout)

        self.show()

    def createGridLayout(self):
        self.horizontalGroupBox = QGroupBox("ROC Plot options")
        layout = QGridLayout()
        layout.setColumnStretch(1, 4)
        layout.setColumnStretch(2, 4)

        self.target_list = QComboBox()
        self.target_list.addItem('Select Target')
        self.target_list.addItem('CDK5')
        self.target_list.addItem('GSK3b')
        self.target_list.addItem('CK1')
        self.target_list.addItem('DYRK1a')

        #trg_lst = get_target

        self.method_list = QComboBox()
        self.method_list.addItem('Select Method', None)

        self.plot_button = QPushButton('Plot')
        self.fig_button = QPushButton('New Figure')
        self.invert_opt = QCheckBox('Invert ROC plot')
        self.new_window = QCheckBox('Plot on new')
        self.weighted = QCheckBox('weighted ')
        self.exp_value = QLineEdit('1.0')
        self.best_auc = QLineEdit('10')
        self.clear_plot = QPushButton('Clear Plot')

        self.best_roc = QPushButton('Best ROC Areas')
        self.populate_methods()

        layout.addWidget(self.target_list, 0, 0)
        layout.addWidget(self.method_list, 1, 0, 1, 2)
        layout.addWidget(self.plot_button, 1, 2)
        layout.addWidget(self.invert_opt, 0, 2)
        layout.addWidget(self.exp_value, 2, 0)
        layout.addWidget(self.clear_plot, 2, 2)
        layout.addWidget(self.new_window, 1, 3)
        layout.addWidget(self.weighted, 1, 4)
        layout.addWidget(self.best_roc, 2, 3)
        layout.addWidget(self.best_auc, 2, 4)

        #layout.addWidget(self.fig_button, 0, 2)

        self.plot_button.clicked.connect(self.plot_fig)
        self.clear_plot.clicked.connect(self.clear_fig)
        self.best_roc.clicked.connect(self.plot_best_roc)
        self.fig_button.clicked.connect(self.new_fig)
        self.target_list.currentIndexChanged.connect(self.selectionchange)
        self.horizontalGroupBox.setLayout(layout)

    def plot_best_roc(self):
        #find_best_auc(self.target_list.currentText(),self.invert_opt.isChecked(),float(self.exp_value.text()),int(self.best_auc.text()))
        #cumulative_best_roc()
        calculate_enrichment_factors()

    def populate_methods(self):
        self.method_list.clear()
        #self.target_list.addItem('Select Method', None)
        if self.target_list.currentText() != 'Select Target':
            mthds = get_targets_methods(self.target_list.currentText())
            for mtd, idx in zip(mthds, range(len(mthds))):
                self.method_list.addItem(mtd, idx)

    def clear_fig(self):
        cear_plot_fig()

    def new_fig(self):
        new_fig_mpl()

    def plot_fig(self):
        #if self.target_list.currentData() != None:
        method = self.method_list.currentData()
        target = self.target_list.currentText()
        if method != None and 'Exponential Mean' != self.method_list.currentText() and 'Mean' != self.method_list.currentText() \
                and 'Linear Reduction Mean'!= self.method_list.currentText() :
            if self.new_window.isChecked():
                plot_remote(target, method, self.invert_opt.isChecked())
                plot_remote_show()
            else:
                plot_remote_v2(target, method, self.invert_opt.isChecked())
                plot_remote_show()
        if self.method_list.currentText() == 'Mean':
            get_mean_roc(target, self.invert_opt.isChecked(),
                         self.weighted.isChecked())
            plot_remote_show()
        if self.method_list.currentText() == 'Exponential Mean':
            get_mean_exp_roc(target, self.invert_opt.isChecked(),
                             float(self.exp_value.text()),
                             self.weighted.isChecked())
            plot_remote_show()
        if 'Linear Reduction Mean' == self.method_list.currentText():
            #get_mean_exp_roc_v2(target, self.invert_opt.isChecked(), float(self.exp_value.text()))
            #plot_remote_show()
            calculate_enrichment_factors()

    def selectionchange(self, i):
        print("Items in the list are :")
        self.populate_methods()
Пример #5
0
class FreedsonAdult1998Factory(BaseAlgorithmFactory):
    config_preset_input = QComboBox
    config_sedentary_input = QSpinBox
    config_light_input = QSpinBox
    config_moderate_input = QSpinBox
    config_vigorous_input = QSpinBox

    def __init__(self):
        super().__init__()

    def create(self, params: dict):
        # Create instance of algorithm
        return FreedsonAdult1998(params)

    def params(self):
        return {
            'sedentary_cutoff': self.config_sedentary_input.value(),
            'light_cutoff': self.config_light_input.value(),
            'moderate_cutoff': self.config_moderate_input.value(),
            'vigorous_cutoff': self.config_vigorous_input.value()
        }

    def name(self):
        return 'Freedson Adult 1998'

    def unique_id(self):
        return 1

    def info(self):
        my_info = {
            'description':
            """ \
        It is a uniaxial accelerometer that assesses accelerations ranging from 0.05-2.0 G and is band limited with a 
        frequency response from 0.25-2.5 Hz.
                
        The acceleration signal is filtered by an analog bandpass filter and digitized by an 8 bit A/D converter at a 
        sampling rate of 10 samples per second.
        
        Each digitized signal is summed over a user specified time interval (epoch), and at the end of each epoch 
        the activity count is stored internally and the accumulator is reset to zero. In the current study, a 60-s
         epoch was used and activity counts were expressed as the average counts per minute over the 6 min of exercise. 
        
        
        Cut points (intensity buckets): 
        * https://actigraph.desk.com/customer/portal/articles/2515802
        
        Counts (accelerator sum over 60 s)
        * https://actigraph.desk.com/customer/portal/articles/2515580-What-are-counts-
        
        Notes:
        --> Only Y axis used on Actigraph devices.
        8 bits = 256 = 2g
        
        epoch = 60 seconds
                        
        """,
            'name':
            self.name(),
            'author':
            'Dominic Létourneau',
            'version':
            '0.1',
            'reference':
            ("Freedson PS1, Melanson E, Sirard J., Calibration of the Computer Science and "
             "Applications, Inc. accelerometer., Med Sci Sports Exerc. 1998 May;30(5):777-81"
             ),
            'unique_id':
            self.unique_id()
        }

        return my_info

    def required_sensors(self):
        return [SensorType.ACCELEROMETER]

    def build_config_widget(self,
                            parent_widget: QWidget,
                            default_params: dict = None):
        # Initialize inputs
        self.config_preset_input = QComboBox()
        # self.config_preset_input.addItem('')
        self.config_preset_input.addItem('Valeurs originales',
                                         [99, 1951, 5724, 9498])
        self.config_preset_input.addItem('Personnalisées', [-1, -1, -1, -1])
        # self.config_preset_input.addItem('Child', [99, 573, 1002, 0])
        self.config_preset_input.currentIndexChanged.connect(
            self.config_preset_changed)

        base_layout = QVBoxLayout()
        preset_frame = QFrame()
        preset_frame.setStyleSheet(
            'QFrame{background-color: rgba(200,200,200,50%);}'
            'QLabel{background-color: rgba(0,0,0,0%);}')
        preset_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
        frame_layout = QGridLayout()
        item_label = QLabel('Preset')
        frame_layout.addWidget(item_label, 0, 0)
        frame_layout.addWidget(self.config_preset_input, 0, 1)
        # frame_layout.addRow('Preset', self.config_preset_input)
        preset_frame.setLayout(frame_layout)
        base_layout.addWidget(preset_frame)

        layout = QGridLayout()
        layout.setAlignment(Qt.AlignTop)
        self.config_sedentary_input = QSpinBox()
        self.config_sedentary_input.setRange(0, 15000)
        item_label = QLabel('Cut-off Sedentary')
        layout.addWidget(item_label, 0, 0)
        layout.addWidget(self.config_sedentary_input, 0, 1)
        # layout.addRow("Cut-off Sedentary", self.config_sedentary_input)
        self.config_light_input = QSpinBox()
        self.config_light_input.setRange(0, 15000)
        item_label = QLabel('Cut-off Light')
        layout.addWidget(item_label, 1, 0)
        layout.addWidget(self.config_light_input, 1, 1)
        # layout.addRow("Cut-off Light", self.config_light_input)
        self.config_moderate_input = QSpinBox()
        self.config_moderate_input.setRange(0, 15000)
        item_label = QLabel('Cut-off Moderate')
        layout.addWidget(item_label, 2, 0)
        layout.addWidget(self.config_moderate_input, 2, 1)
        # layout.addRow("Cut-off Moderate", self.config_moderate_input)
        self.config_vigorous_input = QSpinBox()
        self.config_vigorous_input.setRange(0, 15000)
        item_label = QLabel('Cut-off Vigorous')
        layout.addWidget(item_label, 3, 0)
        layout.addWidget(self.config_vigorous_input, 3, 1)
        # layout.addRow("Cut-off Vigorous", self.config_vigorous_input)
        base_layout.addLayout(layout)

        base_widget = QWidget(parent_widget)
        base_widget.setLayout(base_layout)

        # Set default values
        if default_params is None:
            self.config_preset_changed()
        else:
            self.config_sedentary_input = default_params['sedentary_cutoff']
            self.config_light_input = default_params['light_cutoff']
            self.config_moderate_input = default_params['moderate_cutoff']
            self.config_vigorous_input = default_params['vigorous_cutoff']

        return base_widget

    def config_preset_changed(self):
        params = self.config_preset_input.currentData()
        if params is not None and len(params) == 4:
            if params[0] != -1:
                self.config_sedentary_input.setValue(params[0])
                self.config_light_input.setValue(params[1])
                self.config_moderate_input.setValue(params[2])
                self.config_vigorous_input.setValue(params[3])
            self.config_sedentary_input.setEnabled(params[0] == -1)
            self.config_light_input.setEnabled(params[0] == -1)
            self.config_moderate_input.setEnabled(params[0] == -1)
            self.config_vigorous_input.setEnabled(params[0] == -1)

    def build_display_widget(self, parent_widget: QWidget, results,
                             recordsets):

        layout = QVBoxLayout()
        # Add Scroll area
        scroll = QScrollArea(parent=parent_widget)

        scroll.setLayout(layout)
        view = OpenIMUBarGraphView(scroll)
        view.set_title('Active minutes')
        layout.addWidget(view)

        for result in results:
            data = result['result']
            view.set_category_axis(data.keys())
            values = []

            for key in data:
                values.append(data[key])

            label = result['result_name']
            view.add_set(label, values)

        # if len(results) == len(recordsets):
        #     for i, _ in enumerate(results):
        #         view.set_category_axis(results[i].keys())
        #         values = []
        #
        #         for key in results[i]:
        #             values.append(results[i][key])
        #
        #         label = recordsets[i].name
        #         view.add_set(label, values)

        # Update view
        view.update()

        return scroll

    def build_data_table(self, results):
        data_table = {}
        headers = []
        data = []
        data_names = []
        # Results are stored in json, as a list of dict
        if isinstance(results, list):
            for result in results:
                if isinstance(result, dict):
                    result_data = result['result']
                    result_name = result['result_name']
                    headers.append(result_name)
                    if not data_names:
                        data_names = list(result_data.keys())
                    data.append(list(result_data.values()))

            data_table = {
                'headers': headers,
                'data_names': data_names,
                'data': data
            }

        return data_table
Пример #6
0
class ResultW(QFrame):
    """
    Stores the result of a comparison that can be replayed at any time.
    Contains a QLabel, QPushButton (visualize) and QPushButton (copy to clipboard).
    """
    template_button_pressed_signal = pyqtSignal()
    visualize_button_pressed_signal = pyqtSignal()

    def __init__(self, text, result, replays):
        super().__init__()
        self.result = result
        self.replays = replays

        self.label = QLabel(self)
        self.label.setText(text)
        self.visualize_button = QPushButton(self)
        self.visualize_button.setText("Visualize")
        self.visualize_button.clicked.connect(
            lambda: self.visualize_button_pressed_signal.emit())

        if len(replays) == 1:
            self.set_layout_single()
        # at the moment, this only happens for replay stealing and when
        # visualizing multiple replays
        else:
            self.set_layout_multiple()

    def set_layout_single(self):
        self.actions_combobox = QComboBox()
        self.actions_combobox.addItem("More")
        self.actions_combobox.addItem("View Frametimes", "View Frametimes")
        self.actions_combobox.addItem("View Replay Data", "View Replay Data")
        self.actions_combobox.setInsertPolicy(QComboBox.NoInsert)
        self.actions_combobox.activated.connect(self.action_combobox_activated)

        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.label, 0, 0, 1, 4)
        layout.addItem(SPACER, 0, 4, 1, 1)
        if isinstance(self.result, AnalysisResult):
            layout.addWidget(self.visualize_button, 0, 5, 1, 3)
            layout.addWidget(self.actions_combobox, 0, 8, 1, 1)
        else:
            template_button = self.new_template_button()

            layout.addWidget(self.visualize_button, 0, 5, 1, 2)
            layout.addWidget(template_button, 0, 7, 1, 1)
            layout.addWidget(self.actions_combobox, 0, 8, 1, 1)

        self.setLayout(layout)

    def set_layout_multiple(self):
        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.label, 0, 0, 1, 1)
        layout.addItem(SPACER, 0, 1, 1, 1)
        if isinstance(self.result, AnalysisResult):
            layout.addWidget(self.visualize_button, 0, 2, 1, 2)
        else:
            template_button = self.new_template_button()
            layout.addWidget(self.visualize_button, 0, 2, 1, 1)
            layout.addWidget(template_button, 0, 3, 1, 1)

        self.setLayout(layout)

    def action_combobox_activated(self):
        if self.actions_combobox.currentData() == "View Frametimes":
            self.frametime_window = FrametimeWindow(self.result,
                                                    self.replays[0])
            self.frametime_window.show()
        if self.actions_combobox.currentData() == "View Replay Data":
            self.replay_data_window = ReplayDataWindow(self.replays[0])
            self.replay_data_window.show()
        self.actions_combobox.setCurrentIndex(0)

    def new_template_button(self):
        template_button = QPushButton(self)
        template_button.setText("Copy Template")
        template_button.clicked.connect(
            lambda: self.template_button_pressed_signal.emit())
        return template_button
Пример #7
0
class AddGlyphsDialog(QDialog):

    # TODO: implement Frederik's Glyph Construction Builder
    def __init__(self, currentGlyphs=None, parent=None):
        super().__init__(parent)
        self.setWindowModality(Qt.WindowModal)
        self.setWindowTitle(self.tr("Add Glyphs…"))
        self.currentGlyphs = currentGlyphs
        self.currentGlyphNames = [glyph.name for glyph in currentGlyphs]

        layout = QGridLayout(self)
        self.markColorWidget = ColorVignette(self)
        self.markColorWidget.setFixedWidth(56)
        self.importCharDrop = QComboBox(self)
        self.importCharDrop.addItem(self.tr("Import glyph names…"))
        glyphSets = settings.readGlyphSets()
        for name, glyphNames in glyphSets.items():
            self.importCharDrop.addItem(name, glyphNames)
        self.importCharDrop.currentIndexChanged[int].connect(self.importGlyphs)
        self.addGlyphsEdit = QPlainTextEdit(self)
        self.addGlyphsEdit.setFocus(True)

        self.addUnicodeBox = QCheckBox(self.tr("Add Unicode"), self)
        self.addUnicodeBox.setChecked(True)
        self.addAsTemplateBox = QCheckBox(self.tr("Add as template"), self)
        self.addAsTemplateBox.setChecked(True)
        self.sortFontBox = QCheckBox(self.tr("Sort font"), self)
        self.overrideBox = QCheckBox(self.tr("Override"), self)
        buttonBox = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        buttonBox.accepted.connect(self.accept)
        buttonBox.rejected.connect(self.reject)

        l = 0
        layout.addWidget(self.markColorWidget, l, 0)
        layout.addWidget(self.importCharDrop, l, 3, 1, 2)
        l += 1
        layout.addWidget(self.addGlyphsEdit, l, 0, 1, 5)
        l += 1
        layout.addWidget(self.addUnicodeBox, l, 0)
        layout.addWidget(self.addAsTemplateBox, l, 1)
        layout.addWidget(self.sortFontBox, l, 2)
        layout.addWidget(self.overrideBox, l, 3)
        layout.addWidget(buttonBox, l, 4)
        self.setLayout(layout)

    @classmethod
    def getNewGlyphNames(cls, parent, currentGlyphs=None):
        dialog = cls(currentGlyphs, parent)
        result = dialog.exec_()
        markColor = dialog.markColorWidget.color()
        if markColor is not None:
            markColor = markColor.getRgbF()
        params = dict(
            addUnicode=dialog.addUnicodeBox.isChecked(),
            asTemplate=dialog.addAsTemplateBox.isChecked(),
            markColor=markColor,
            override=dialog.overrideBox.isChecked(),
            sortFont=dialog.sortFontBox.isChecked(),
        )
        newGlyphNames = []
        for name in dialog.addGlyphsEdit.toPlainText().split():
            if name not in dialog.currentGlyphNames:
                newGlyphNames.append(name)
        return (newGlyphNames, params, result)

    def importGlyphs(self, index):
        if index == 0:
            return
        glyphNames = self.importCharDrop.currentData()
        editorNames = self.addGlyphsEdit.toPlainText().split()
        currentNames = set(self.currentGlyphNames) ^ set(editorNames)
        changed = False
        for name in glyphNames:
            if name not in currentNames:
                changed = True
                editorNames.append(name)
        if changed:
            self.addGlyphsEdit.setPlainText(" ".join(editorNames))
            cursor = self.addGlyphsEdit.textCursor()
            cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)
            self.addGlyphsEdit.setTextCursor(cursor)
        self.importCharDrop.setCurrentIndex(0)
        self.addGlyphsEdit.setFocus(True)
Пример #8
0
class NewEntryDialog(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.submitted = False
        self.cover_bytes = b''
        self.setWindowTitle('Add new entry')
        self.container = QWidget()
        self.horizontal_layout = QHBoxLayout()
        self.container_left_layout = QVBoxLayout()
        self.container_right_layout = QVBoxLayout()
        self.horizontal_layout.addLayout(self.container_left_layout)
        self.horizontal_layout.addLayout(self.container_right_layout)

        label_type = QLabel(_('Entry type:'))
        self.combo_type = QComboBox()
        label_type.setBuddy(self.combo_type)
        for entry in EntryType:
            self.combo_type.addItem(entry.value, entry)

        label_status = QLabel(_('Entry status:'))
        self.combo_status = QComboBox()
        label_status.setBuddy(self.combo_status)
        for entry in EntryStatus:
            self.combo_status.addItem(entry.value, entry)

        label_progress = QLabel(_('Progress status:'))
        self.combo_progress = QComboBox()
        label_progress.setBuddy(self.combo_progress)
        for entry in ProgressStatus:
            self.combo_progress.addItem(entry.value, entry)

        label_org_name = QLabel(_('Entry original name:'))
        self.line_org_name = QLineEdit()
        label_org_name.setBuddy(self.line_org_name)

        label_eng_name = QLabel(_('Entry english name:'))
        self.line_eng_name = QLineEdit()
        label_eng_name.setBuddy(self.line_eng_name)

        label_synonyms = QLabel(_('Synonym names:'))
        self.line_synonyms = QLineEdit()
        label_synonyms.setBuddy(self.line_synonyms)

        label_description = QLabel(_('Description:'))
        self.description_textbox = QPlainTextEdit()
        label_description.setBuddy(self.description_textbox)

        self.nsfw = QCheckBox(_('NSFW'))

        only_int = QIntValidator(bottom=0)
        label_current_progress = QLabel(_('Current progress:'))
        self.current_progress_textbox = QLineEdit()
        label_current_progress.setBuddy(self.current_progress_textbox)
        self.current_progress_textbox.setValidator(only_int)
        label_current_progress.setBuddy(self.current_progress_textbox)

        label_max_progress = QLabel(_('Max progress:'))
        self.max_progress_textbox = QLineEdit()
        label_max_progress.setBuddy(self.max_progress_textbox)
        self.max_progress_textbox.setValidator(only_int)
        label_max_progress.setBuddy(self.max_progress_textbox)

        label_release_date = QLabel(_('Release date:'))
        self.release_date1 = QDateEdit()
        self.release_date2 = QDateEdit()
        label_release_date.setBuddy(self.release_date1)
        label_release_date.setBuddy(self.release_date2)

        self.cover = QLabel("Cover:")
        self.load_cover_button = QPushButton(_('Load cover'))
        self.load_cover_button.clicked.connect(self.load_image)

        self.accept_button = QPushButton(_('Add'))

        self.container_left_layout.addWidget(self.cover)
        self.container_left_layout.addWidget(self.load_cover_button)

        self.container_right_layout.addWidget(label_type)
        self.container_right_layout.addWidget(self.combo_type)
        self.container_right_layout.addWidget(label_status)
        self.container_right_layout.addWidget(self.combo_status)
        self.container_right_layout.addWidget(label_progress)
        self.container_right_layout.addWidget(self.combo_progress)

        self.container_right_layout.addWidget(label_org_name)
        self.container_right_layout.addWidget(self.line_org_name)
        self.container_right_layout.addWidget(label_eng_name)
        self.container_right_layout.addWidget(self.line_eng_name)
        self.container_right_layout.addWidget(label_synonyms)
        self.container_right_layout.addWidget(self.line_synonyms)

        self.container_right_layout.addWidget(label_description)
        self.container_right_layout.addWidget(self.description_textbox)

        self.container_right_layout.addWidget(self.nsfw)

        self.container_right_layout.addWidget(label_current_progress)
        self.container_right_layout.addWidget(self.current_progress_textbox)
        self.container_right_layout.addWidget(label_max_progress)
        self.container_right_layout.addWidget(self.max_progress_textbox)

        self.container_right_layout.addWidget(label_release_date)
        date_layout = QHBoxLayout()
        date_layout.addWidget(self.release_date1)
        date_layout.addWidget(self.release_date2)
        self.container_right_layout.addLayout(date_layout)

        self.container_right_layout.addWidget(self.accept_button)

        self.root_layout = QVBoxLayout(self)
        self.main_layout = QVBoxLayout(self)
        self.root_layout.addLayout(self.main_layout)
        self.container.setLayout(self.horizontal_layout)
        self.main_layout.addWidget(self.container)
        self.setLayout(self.root_layout)

        self.accept_button.clicked.connect(self.submit)

    def submit(self):
        self.submitted = True
        self.close()

    def load_image(self):
        fname = QFileDialog.getOpenFileName(self, 'Select entry cover')
        image_path = fname[0]
        self.cover_bytes = open(image_path, 'rb').read()
        qp = QPixmap()
        qp.loadFromData(self.cover_bytes)
        self.cover.setPixmap(qp)
        self.cover.setMaximumHeight(500)
        self.cover.setMaximumWidth(500)

    def get_values(self):
        entry_status = self.combo_status.currentData()
        entry_type = self.combo_type.currentData()
        entry_progress = self.combo_progress.currentData()
        date1 = self.release_date1.date().toPyDate()
        date2 = self.release_date2.date().toPyDate()
        try:
            current_progress = int(self.current_progress_textbox.text())
        except:
            current_progress = 0
        try:
            max_progress = int(self.max_progress_textbox.text())
        except:
            max_progress = -1

        entry = GenericEntry(self.cover_bytes, self.line_eng_name.text(),
                             self.line_org_name.text(),
                             self.line_synonyms.text(), date1, date2,
                             entry_status,
                             self.description_textbox.toPlainText(),
                             self.nsfw.isChecked(), entry_type,
                             current_progress, max_progress, entry_progress)
        return entry
Пример #9
0
class ViewSettingsPage(QWidget):
    def __init__(self, share_dir, parent=None):
        QWidget.__init__(self, parent)
        layout = QFormLayout()
        self.show_notation = QCheckBox(self)
        layout.addRow(_("Show fields notation on the board"),
                      self.show_notation)
        self.show_border = QCheckBox(self)
        layout.addRow(_("Show board borders with notation"), self.show_border)
        self.show_possible_moves = QCheckBox(self)
        layout.addRow(_("Show possible moves"), self.show_possible_moves)
        self.theme = QComboBox(self)
        self.themes = dict()
        for theme in Theme.list_themes(share_dir):
            self.themes[theme.id] = theme
            self.theme.addItem(theme.name, theme.id)
        layout.addRow(_("Theme"), self.theme)

        self.enable_sound = QCheckBox(self)
        layout.addRow(_("Enable sounds"), self.enable_sound)

        self.setLayout(layout)

    def get_theme(self):
        theme_name = self.theme.currentData()
        return self.themes[theme_name]

    def get_enable_sound(self):
        return self.enable_sound.checkState() == Qt.Checked

    def load(self, settings):
        show_notation = settings.value("show_notation", type=bool)
        self.show_notation.setCheckState(
            Qt.Checked if show_notation else Qt.Unchecked)

        show_border = settings.value("show_border", False, type=bool)
        self.show_border.setCheckState(
            Qt.Checked if show_border else Qt.Unchecked)

        show_possible_moves = settings.value("show_possible_moves", type=bool)
        self.show_possible_moves.setCheckState(
            Qt.Checked if show_possible_moves else Qt.Unchecked)

        theme = settings.value("theme")
        theme_idx = self.theme.findData(theme)
        if theme_idx < 0:
            theme_idx = self.theme.findText("default")
        if theme_idx >= 0:
            self.theme.setCurrentIndex(theme_idx)

        enable_sound = settings.value("enable_sound", type=bool)
        self.enable_sound.setCheckState(
            Qt.Checked if enable_sound else Qt.Unchecked)

    def save(self, settings):
        settings.setValue("show_notation",
                          self.show_notation.checkState() == Qt.Checked)
        settings.setValue("show_border",
                          self.show_border.checkState() == Qt.Checked)
        settings.setValue("show_possible_moves",
                          self.show_possible_moves.checkState() == Qt.Checked)
        settings.setValue("theme", self.theme.currentData())
        settings.setValue("enable_sound", self.get_enable_sound())
Пример #10
0
class TopicsTab(QWidget):
    configChanged = pyqtSignal()
    def __init__(self):
        super(QWidget, self).__init__()
        self.config = None
        self.count = 0
        self.topicRows = {}

        self.nameEdit = QLineEdit()
        self.dataTypeComboBox = QComboBox()
        self.fillDataTypes()
        self.opTypeComboBox = QComboBox()
        self.opTypeComboBox.addItem('sub', 'Subscribe')
        self.opTypeComboBox.addItem('pub', 'Publish')
        self.addButton = QPushButton('Add')
        self.addButton.clicked.connect(self.addClicked)

        self.mainLayout = QVBoxLayout()
        rowLayout = QHBoxLayout()
        rowLayout.addWidget(self.nameEdit)
        rowLayout.addWidget(self.dataTypeComboBox)
        rowLayout.addWidget(self.opTypeComboBox)
        rowLayout.addWidget(self.addButton)
        rowContainer = QWidget()
        rowContainer.setLayout(rowLayout)
        rowContainer.setObjectName('titleRow')
        self.mainLayout.addWidget(rowContainer)
        self.setLayout(self.mainLayout)


    def fillDataTypes(self):
        rosTypes = Interfaces.getRosMessageTypes()
        for type in rosTypes:
            concatType = type['typeDir'] + '/' + type['type']
            self.dataTypeComboBox.addItem(concatType, concatType)


    def addTopicRow(self, name, type, opType):
        rowLayout = QHBoxLayout()
        rowLayout.addWidget(QLabel(name))
        rowLayout.addWidget(QLabel(type))
        rowLayout.addWidget(QLabel(opType))
        removeButton = QPushButton('Remove')
        removeButton.clicked.connect(self.removeTopicClicked)
        removeButton.setObjectName(str(self.count))
        rowLayout.addWidget(removeButton)
        rowContainer = QWidget()
        rowContainer.setLayout(rowLayout)
        rowContainer.setObjectName('row' + str(self.count))
        self.mainLayout.addWidget(rowContainer)
        self.topicRows[self.count] = rowContainer
        self.count += 1


    def addClicked(self):
        if self.config is not None:
            self.config.addTopic(self.count, self.nameEdit.text(), self.dataTypeComboBox.currentData(), self.opTypeComboBox.currentData())
            self.addTopicRow(self.nameEdit.text(), self.dataTypeComboBox.currentData(), self.opTypeComboBox.currentData())
            self.nameEdit.setText('')
            self.configChanged.emit()

    def removeTopicClicked(self):
        if self.config is not None:
            itemToRemove = None
            for i in range(self.mainLayout.count()):
                if self.mainLayout.itemAt(i).widget().objectName() == 'row' + self.sender().objectName():
                    itemToRemove = self.mainLayout.itemAt(i)
                    break
            if itemToRemove is not None:
                self.mainLayout.removeItem(itemToRemove)
                itemToRemove.widget().setParent(None)
                self.mainLayout.update()
                self.configChanged.emit()
            self.config.removeTopic(int(self.sender().objectName()))
            del self.topicRows[int(self.sender().objectName())]


    def clearAllRows(self):
        clearList = []
        for i in range(self.mainLayout.count()):
            item = self.mainLayout.itemAt(i)
            if item.widget().objectName() != 'titleRow':
                clearList.append(item)

        for item in clearList:
            self.mainLayout.removeItem(item)
            item.widget().setParent(None)

        self.mainLayout.update()
        self.count = 0


    def setConfig(self, config):
        self.config = config
        self.clearAllRows()
        for topic in self.config.getTopics():
            topic['id'] = self.count
            self.addTopicRow(topic['name'], topic['type'], topic['opType'])
Пример #11
0
class Settings_Net(QWidget):
    def __init__(self, parent: QWidget):
        super(Settings_Net, self).__init__(parent)
        self._layout = QVBoxLayout()
        self.setLayout(self._layout)
        # construct layout
        min_width = 150
        self.user_agents = dict()
        self.user_agents['chrome_win7_x64'] = (
            'Chrome 41, Windows 7 x64', 'Mozilla/5.0 (Windows NT 6.1; WOW64) '
            'AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/41.0.2227.0 Safari/537.36')
        self.user_agents['chrome_linux_64'] = (
            'Chrome 41, Linux x86_64', 'Mozilla/5.0 (X11; Linux x86_64) '
            'AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/41.0.2227.0 Safari/537.36')
        self.user_agents['chrome_android'] = (
            'Chrome 47, Android 4.3 Galaxy-S3',
            'Mozilla/5.0 (Linux; Android 4.3; GT-I9300 Build/JSS15J) '
            'AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/47.0.2526.83 Mobile Safari/537.36')
        self.user_agents['firefox_win32'] = (
            'Firefox 40, Windows 7 32-bit',
            'Mozilla/5.0 (Windows NT 6.1; rv:40.0) '
            'Gecko/20100101 Firefox/40.1')
        self.user_agents['firefox_android'] = (
            'Firefox, Android 4.3 Galaxy-S3',
            'Mozilla/5.0 (Android 4.3; Mobile; rv:43.0) '
            'Gecko/43.0 Firefox/43.0')
        self.user_agents['edge_win10'] = (
            'Microsoft Edge, Windows 10 x64',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
            'AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/42.0.2311.135 Safari/537.36 Edge/12.246')
        # server URL
        self._l_surl = QHBoxLayout()
        self._l_ua = QHBoxLayout()
        self._lbl_surl = QLabel(self.tr('Server URL:'), self)
        self._lbl_surl.setMinimumWidth(min_width)
        self._le_surl = QLineEdit(self)
        self._l_surl.addWidget(self._lbl_surl)
        self._l_surl.addWidget(self._le_surl)
        self._layout.addLayout(self._l_surl)
        # emulate browser combo box
        self._l_eb = QHBoxLayout()
        self._lbl_eb = QLabel(self.tr('Emulate browser:'), self)
        self._lbl_eb.setMinimumWidth(min_width)
        self._cb_eb = QComboBox(self)
        self._cb_eb.setEditable(False)
        self._cb_eb.setInsertPolicy(QComboBox.InsertAtBottom)
        ua_keylist = [i for i in self.user_agents.keys()]
        ua_keylist.sort()
        for key_id in ua_keylist:
            b_tuple = self.user_agents[key_id]
            display_string = b_tuple[0]
            self._cb_eb.addItem(display_string, QVariant(str(key_id)))
        self._cb_eb.addItem(self.tr('<Custom>'), QVariant('custom'))
        self._l_eb.addWidget(self._lbl_eb)
        self._l_eb.addWidget(self._cb_eb)
        self._layout.addLayout(self._l_eb)
        # custom user-agent string
        self._lbl_ua = QLabel(self.tr('User-agent string:'), self)
        self._lbl_ua.setMinimumWidth(min_width)
        self._le_ua = QLineEdit(self)
        self._l_ua.addWidget(self._lbl_ua)
        self._l_ua.addWidget(self._le_ua)
        self._layout.addLayout(self._l_ua)
        # proxy settings
        self._l_proxy = QHBoxLayout()
        self._lbl_proxy = QLabel(self.tr('Proxy type:'), self)
        self._lbl_proxy.setMinimumWidth(min_width)
        self._cb_proxy = QComboBox(self)
        self._cb_proxy.setEditable(False)
        self._cb_proxy.addItem(self.tr('No proxy'), QVariant('none'))
        self._cb_proxy.addItem(self.tr('HTTP proxy'), QVariant('http'))
        self._cb_proxy.addItem(self.tr('SOCKS5 proxy'), QVariant('socks5'))
        self._l_proxy.addWidget(self._lbl_proxy)
        self._l_proxy.addWidget(self._cb_proxy)
        self._layout.addLayout(self._l_proxy)
        self._l_proxy_s = QHBoxLayout()
        self._lbl_proxy_s = QLabel(self.tr('Proxy addr:port:'), self)
        self._lbl_proxy_s.setMinimumWidth(min_width)
        self._le_proxy_addr = QLineEdit(self)
        self._l_proxy_s.addWidget(self._lbl_proxy_s)
        self._l_proxy_s.addWidget(self._le_proxy_addr)
        self._layout.addLayout(self._l_proxy_s)
        # all connections
        self._cb_eb.currentIndexChanged.connect(
            self.on_cb_eb_current_index_changed)
        self._cb_proxy.currentIndexChanged.connect(
            self.on_cb_proxy_current_index_changed)
        # finalize
        self._layout.addStretch()

    @pyqtSlot(int)
    def on_cb_eb_current_index_changed(self, index: int):
        key_id = str(self._cb_eb.currentData(Qt.UserRole))
        if key_id == 'custom':
            self._le_ua.setEnabled(True)
            return
        self._le_ua.setEnabled(False)
        if key_id in self.user_agents:
            b_tuple = self.user_agents[key_id]
            ua_str = b_tuple[1]
            self._le_ua.setText(ua_str)

    @pyqtSlot(int)
    def on_cb_proxy_current_index_changed(self, index: int):
        if index == 0:
            self._le_proxy_addr.setEnabled(False)
        else:
            self._le_proxy_addr.setEnabled(True)

    def ua_select(self, key_id: str):
        cnt = self._cb_eb.count()
        for i in range(cnt):
            item_key_id = str(self._cb_eb.itemData(i, Qt.UserRole))
            if item_key_id == key_id:
                self._cb_eb.setCurrentIndex(i)
                break

    def load_from_config(self, cfg: configparser.ConfigParser):
        # defaults
        xnova_url = 'uni4.xnova.su'
        user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0'
        user_agent_id = 'custom'
        proxy = ''
        if 'net' in cfg:
            xnova_url = cfg['net']['xnova_url']
            user_agent = cfg['net']['user_agent']
            user_agent_id = cfg['net']['user_agent_id']
            proxy = cfg['net']['proxy']
        self._le_surl.setText(xnova_url)
        self._le_surl.setEnabled(
            False)  # cannot be edited by user, for safety!
        # deal with user-agent
        self._le_ua.setText(user_agent)
        if user_agent_id == 'custom':
            self._le_ua.setEnabled(True)
        else:
            self._le_ua.setEnabled(False)
        self.ua_select(user_agent_id)
        # deal with proxy
        if proxy == '':
            self._le_proxy_addr.setText('')
            self._cb_proxy.setCurrentIndex(0)
            self._le_proxy_addr.setEnabled(False)
        elif proxy.startswith('http://'):
            self._cb_proxy.setCurrentIndex(1)
            proxy_addr = proxy[7:]
            self._le_proxy_addr.setText(proxy_addr)
            self._le_proxy_addr.setEnabled(True)
        elif proxy.startswith('socks5://'):
            self._cb_proxy.setCurrentIndex(2)
            proxy_addr = proxy[9:]
            self._le_proxy_addr.setText(proxy_addr)
            self._le_proxy_addr.setEnabled(True)
        else:
            raise ValueError('Invalid proxy setting: ' + proxy)

    def save_to_config(self, cfg: configparser.ConfigParser):
        # ensure there is a 'net' section
        if 'net' not in cfg:
            cfg.add_section('net')
        # skip server url
        # deal with user-agent
        user_agent_id = ''
        user_agent = ''
        idx = self._cb_eb.currentIndex()
        if idx >= 0:
            user_agent_id = str(self._cb_eb.itemData(idx, Qt.UserRole))
            cfg['net']['user_agent_id'] = user_agent_id
        user_agent = self._le_ua.text().strip()
        if user_agent != '':
            cfg['net']['user_agent'] = user_agent
        # deal with proxy
        idx = self._cb_proxy.currentIndex()
        proxy_addr = self._le_proxy_addr.text().strip()
        if idx == 0:
            cfg['net']['proxy'] = ''
        elif idx == 1:
            cfg['net']['proxy'] = 'http://' + proxy_addr
        elif idx == 2:
            cfg['net']['proxy'] = 'socks5://' + proxy_addr
        logger.debug('Saved network config')
Пример #12
0
class fullScreenEditor(QWidget):
    def __init__(self, index, parent=None):
        QWidget.__init__(self, parent)
        self._background = None
        self._index = index
        self._theme = findThemePath(settings.fullScreenTheme)
        self._themeDatas = loadThemeDatas(self._theme)
        self.setMouseTracking(True)
        self._geometries = {}

        # Text editor
        self.editor = MDEditView(self,
                                index=index,
                                spellcheck=settings.spellcheck,
                                highlighting=True,
                                dict=settings.dict)
        self.editor.setFrameStyle(QFrame.NoFrame)
        self.editor.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.editor.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.editor.installEventFilter(self)
        self.editor.setMouseTracking(True)
        self.editor.setVerticalScrollBar(myScrollBar())
        self.scrollBar = self.editor.verticalScrollBar()
        self.scrollBar.setParent(self)

        # Top Panel
        self.topPanel = myPanel(parent=self)
        # self.topPanel.layout().addStretch(1)

        # Spell checking
        if enchant:
            self.btnSpellCheck = QPushButton(self)
            self.btnSpellCheck.setFlat(True)
            self.btnSpellCheck.setIcon(QIcon.fromTheme("tools-check-spelling"))
            self.btnSpellCheck.setCheckable(True)
            self.btnSpellCheck.setChecked(self.editor.spellcheck)
            self.btnSpellCheck.toggled.connect(self.editor.toggleSpellcheck)
        else:
            self.btnSpellCheck = None

        # Navigation Buttons
        self.btnPrevious = QPushButton(self)
        self.btnPrevious.setFlat(True)
        self.btnPrevious.setIcon(QIcon.fromTheme("arrow-left"))
        self.btnPrevious.clicked.connect(self.switchPreviousItem)
        self.btnNext = QPushButton(self)
        self.btnNext.setFlat(True)
        self.btnNext.setIcon(QIcon.fromTheme("arrow-right"))
        self.btnNext.clicked.connect(self.switchNextItem)
        self.btnNew = QPushButton(self)
        self.btnNew.setFlat(True)
        self.btnNew.setIcon(QIcon.fromTheme("document-new"))
        self.btnNew.clicked.connect(self.createNewText)

        # Path and New Text Buttons
        self.wPath = myPath(self)

        # Close
        self.btnClose = QPushButton(self)
        self.btnClose.setIcon(qApp.style().standardIcon(QStyle.SP_DialogCloseButton))
        self.btnClose.clicked.connect(self.close)
        self.btnClose.setFlat(True)

        # Top panel Layout
        if self.btnSpellCheck:
            self.topPanel.layout().addWidget(self.btnSpellCheck)
        self.topPanel.layout().addSpacing(15)
        self.topPanel.layout().addWidget(self.btnPrevious)
        self.topPanel.layout().addWidget(self.btnNext)
        self.topPanel.layout().addWidget(self.btnNew)

        self.topPanel.layout().addStretch(1)
        self.topPanel.layout().addWidget(self.wPath)
        self.topPanel.layout().addStretch(1)

        self.topPanel.layout().addWidget(self.btnClose)
        self.updateTopBar()

        # Left Panel
        self._locked = False
        self.leftPanel = myPanel(vertical=True, parent=self)
        self.locker = locker(self)
        self.locker.lockChanged.connect(self.setLocked)
        self.leftPanel.layout().addWidget(self.locker)

        # Bottom Panel
        self.bottomPanel = myPanel(parent=self)

        self.bottomPanel.layout().addSpacing(24)
        self.lstThemes = QComboBox(self)
        self.lstThemes.setAttribute(Qt.WA_TranslucentBackground)
        paths = allPaths("resources/themes")
        for p in paths:
            lst = [i for i in os.listdir(p) if os.path.splitext(i)[1] == ".theme"]
            for t in lst:
                themeIni = os.path.join(p, t)
                name = loadThemeDatas(themeIni)["Name"]
                # self.lstThemes.addItem(os.path.splitext(t)[0])
                self.lstThemes.addItem(name)
                self.lstThemes.setItemData(self.lstThemes.count()-1, os.path.splitext(t)[0])

        self.lstThemes.setCurrentIndex(self.lstThemes.findData(settings.fullScreenTheme))
        # self.lstThemes.setCurrentText(settings.fullScreenTheme)
        self.lstThemes.currentTextChanged.connect(self.setTheme)
        self.lstThemes.setMaximumSize(QSize(300, QFontMetrics(qApp.font()).height()))
        themeLabel = QLabel(self.tr("Theme:"), self)
        self.bottomPanel.layout().addWidget(themeLabel)
        self.bottomPanel.layout().addWidget(self.lstThemes)
        self.bottomPanel.layout().addStretch(1)

        self.lblProgress = QLabel(self)
        self.lblProgress.setMaximumSize(QSize(200, 14))
        self.lblProgress.setMinimumSize(QSize(100, 14))
        self.lblWC = QLabel(self)
        self.lblClock = myClockLabel(self)
        self.bottomPanel.layout().addWidget(self.lblWC)
        self.bottomPanel.layout().addWidget(self.lblProgress)
        self.bottomPanel.layout().addSpacing(15)
        self.bottomPanel.layout().addWidget(self.lblClock)
        self.updateStatusBar()

        self.bottomPanel.layout().addSpacing(24)

        # Add Widget Settings
        if self.btnSpellCheck:
            self.topPanel.addWidgetSetting(self.tr("Spellcheck"), 'top-spellcheck', (self.btnSpellCheck, ))
        self.topPanel.addWidgetSetting(self.tr("Navigation"), 'top-navigation', (self.btnPrevious, self.btnNext))
        self.topPanel.addWidgetSetting(self.tr("New Text"), 'top-new-doc', (self.btnNew, ))
        self.topPanel.addWidgetSetting(self.tr("Title"), 'top-title', (self.wPath, ))
        self.topPanel.addSetting(self.tr("Title: Show Full Path"), 'title-show-full-path', True)
        self.topPanel.setSettingCallback('title-show-full-path', lambda var, val: self.updateTopBar())
        self.bottomPanel.addWidgetSetting(self.tr("Theme selector"), 'bottom-theme', (self.lstThemes, themeLabel))
        self.bottomPanel.addWidgetSetting(self.tr("Word count"), 'bottom-wc', (self.lblWC, ))
        self.bottomPanel.addWidgetSetting(self.tr("Progress"), 'bottom-progress', (self.lblProgress, ))
        self.bottomPanel.addSetting(self.tr("Progress: Auto Show/Hide"), 'progress-auto-show', True)
        self.bottomPanel.addWidgetSetting(self.tr("Clock"), 'bottom-clock', (self.lblClock, ))
        self.bottomPanel.addSetting(self.tr("Clock: Show Seconds"), 'clock-show-seconds', True)
        self.bottomPanel.setAutoHideVariable('autohide-bottom')
        self.topPanel.setAutoHideVariable('autohide-top')
        self.leftPanel.setAutoHideVariable('autohide-left')

        # Connection
        self._index.model().dataChanged.connect(self.dataChanged)

        # self.updateTheme()
        self.showFullScreen()
        # self.showMaximized()
        # self.show()

    def __del__(self):
        # print("Leaving fullScreenEditor via Destructor event", flush=True)
        self.showNormal()
        self.close()

    def setLocked(self, val):
        self._locked = val
        self.btnClose.setVisible(not val)

    def setTheme(self, themeName):
        themeName = self.lstThemes.currentData()
        settings.fullScreenTheme = themeName
        self._theme = findThemePath(themeName)
        self._themeDatas = loadThemeDatas(self._theme)
        self.updateTheme()

    def updateTheme(self):
        # Reinit stored geometries for hiding widgets
        self._geometries = {}
        rect = self.geometry()
        self._background = generateTheme(self._themeDatas, rect)

        setThemeEditorDatas(self.editor, self._themeDatas, self._background, rect)

        # Colors
        if self._themeDatas["Foreground/Color"] == self._themeDatas["Background/Color"] or \
                        self._themeDatas["Foreground/Opacity"] < 5:
            self._fgcolor = QColor(self._themeDatas["Text/Color"])
            self._bgcolor = QColor(self._themeDatas["Background/Color"])
        else:
            self._bgcolor = QColor(self._themeDatas["Foreground/Color"])
            self._bgcolor.setAlpha(self._themeDatas["Foreground/Opacity"] * 255 / 100)
            self._fgcolor = QColor(self._themeDatas["Text/Color"])
            if self._themeDatas["Text/Color"] == self._themeDatas["Foreground/Color"]:
                self._fgcolor = QColor(self._themeDatas["Background/Color"])

        # ScrollBar
        r = self.editor.geometry()
        w = qApp.style().pixelMetric(QStyle.PM_ScrollBarExtent)
        r.setWidth(w)
        r.moveRight(rect.right() - rect.left())
        self.scrollBar.setGeometry(r)
        # self.scrollBar.setVisible(False)
        self.hideWidget(self.scrollBar)
        p = self.scrollBar.palette()
        b = QBrush(self._background.copy(self.scrollBar.geometry()))
        p.setBrush(QPalette.Base, b)
        self.scrollBar.setPalette(p)

        self.scrollBar.setColor(self._bgcolor)

        # Left Panel
        r = self.locker.geometry()
        r.moveTopLeft(QPoint(
                0,
                self.geometry().height() / 2 - r.height() / 2
        ))
        self.leftPanel.setGeometry(r)
        self.hideWidget(self.leftPanel)
        self.leftPanel.setColor(self._bgcolor)

        # Top / Bottom Panels
        r = QRect(0, 0, 0, 24)
        r.setWidth(rect.width())
        # r.moveLeft(rect.center().x() - r.width() / 2)
        self.topPanel.setGeometry(r)
        # self.topPanel.setVisible(False)
        self.hideWidget(self.topPanel)
        r.moveBottom(rect.bottom() - rect.top())
        self.bottomPanel.setGeometry(r)
        # self.bottomPanel.setVisible(False)
        self.hideWidget(self.bottomPanel)
        self.topPanel.setColor(self._bgcolor)
        self.bottomPanel.setColor(self._bgcolor)

        # Lst theme
        # p = self.lstThemes.palette()
        p = self.palette()
        p.setBrush(QPalette.Button, self._bgcolor)
        p.setBrush(QPalette.ButtonText, self._fgcolor)
        p.setBrush(QPalette.WindowText, self._fgcolor)

        for panel in (self.bottomPanel, self.topPanel, self.leftPanel):
            for i in range(panel.layout().count()):
                item = panel.layout().itemAt(i)
                if item.widget():
                    item.widget().setPalette(p)
        # self.lstThemes.setPalette(p)
        # self.lblWC.setPalette(p)

        self.update()
        self.editor.centerCursor()

    def paintEvent(self, event):
        if self._background:
            painter = QPainter(self)
            painter.setClipRegion(event.region())
            painter.drawPixmap(event.rect(), self._background, event.rect())
            painter.end()

    def resizeEvent(self, event):
        self.updateTheme()

    def keyPressEvent(self, event):
        if event.key() in [Qt.Key_Escape, Qt.Key_F11] and \
                not self._locked:
            # print("Leaving fullScreenEditor via keyPressEvent", flush=True)
            self.showNormal()
            self.close()
        elif (event.modifiers() & Qt.AltModifier) and \
                event.key() in [Qt.Key_PageUp, Qt.Key_PageDown, Qt.Key_Left, Qt.Key_Right]:
            if event.key() in [Qt.Key_PageUp, Qt.Key_Left]:
                success = self.switchPreviousItem()
            if event.key() in [Qt.Key_PageDown, Qt.Key_Right]:
                success = self.switchNextItem()
            if not success:
                QWidget.keyPressEvent(self, event)
        else:
            QWidget.keyPressEvent(self, event)

    def mouseMoveEvent(self, event):
        r = self.geometry()

        for w in [self.scrollBar, self.topPanel,
                  self.bottomPanel, self.leftPanel]:
            # w.setVisible(w.geometry().contains(event.pos()))
            if self._geometries[w].contains(event.pos()):
                self.showWidget(w)
            else:
                self.hideWidget(w)

    def hideWidget(self, widget):
        if widget not in self._geometries:
            self._geometries[widget] = widget.geometry()

        if hasattr(widget, "_autoHide") and not widget._autoHide:
            return

        # Hides widget in the bottom right corner
        widget.move(self.geometry().bottomRight() + QPoint(1, 1))

    def showWidget(self, widget):
        if widget in self._geometries:
            widget.move(self._geometries[widget].topLeft())

    def eventFilter(self, obj, event):
        if obj == self.editor and event.type() == QEvent.Enter:
            for w in [self.scrollBar, self.topPanel,
                      self.bottomPanel, self.leftPanel]:
                # w.setVisible(False)
                self.hideWidget(w)
        return QWidget.eventFilter(self, obj, event)

    def dataChanged(self, topLeft, bottomRight):
        # This is called sometimes after self has been destroyed. Don't know why.
        if not self or not self._index:
            return
        if topLeft.row() <= self._index.row() <= bottomRight.row():
            self.updateStatusBar()

    def updateTopBar(self):
        item = self._index.internalPointer()
        previousItem = self.previousTextItem(item)
        nextItem = self.nextTextItem(item)
        self.btnPrevious.setEnabled(previousItem is not None)
        self.btnNext.setEnabled(nextItem is not None)
        self.wPath.setItem(item)

    def updateStatusBar(self):
        if self._index:
            item = self._index.internalPointer()

        wc = item.data(Outline.wordCount)
        goal = item.data(Outline.goal)
        pg = item.data(Outline.goalPercentage)

        if goal:
            if settings.fullscreenSettings.get("progress-auto-show", True):
                self.lblProgress.show()
            self.lblWC.setText(self.tr("{} words / {}").format(wc, goal))
        else:
            if settings.fullscreenSettings.get("progress-auto-show", True):
                self.lblProgress.hide()
            self.lblWC.setText(self.tr("{} words").format(wc))
            pg = 0
        rect = self.lblProgress.geometry()
        rect = QRect(QPoint(0, 0), rect.size())
        self.px = QPixmap(rect.size())
        self.px.fill(Qt.transparent)
        p = QPainter(self.px)
        drawProgress(p, rect, pg, 2)
        p.end()
        self.lblProgress.setPixmap(self.px)

        self.locker.setWordCount(wc)
        # If there's a goal, then we update the locker target's number of word accordingly
        # (also if there is a word count, we deduce it.
        if goal and not self.locker.isLocked():
            if wc and goal - wc > 0:
                self.locker.spnWordTarget.setValue(goal - wc)
            elif not wc:
                self.locker.spnWordTarget.setValue(goal)

    def setCurrentModelIndex(self, index):
        self._index = index
        self.editor.setCurrentModelIndex(index)
        self.updateTopBar()
        self.updateStatusBar()

    def switchPreviousItem(self):
        item = self._index.internalPointer()
        previousItem = self.previousTextItem(item)
        if previousItem:
            self.setCurrentModelIndex(previousItem.index())
            return True
        return False

    def switchNextItem(self):
        item = self._index.internalPointer()
        nextItem = self.nextTextItem(item)
        if nextItem:
            self.setCurrentModelIndex(nextItem.index())
            return True
        return False

    def switchToItem(self, item):
        item = self.firstTextItem(item)
        if item:
            self.setCurrentModelIndex(item.index())
        
    def createNewText(self):
        item = self._index.internalPointer()
        newItem = outlineItem(title=qApp.translate("outlineBasics", "New"), _type=settings.defaultTextType)
        self._index.model().insertItem(newItem, item.row() + 1, item.parent().index())
        self.setCurrentModelIndex(newItem.index())

    def previousModelItem(self, item):
        parent = item.parent()
        if not parent:
            # Root has no sibling
            return None

        row = parent.childItems.index(item)
        if row > 0:
            return parent.child(row - 1)
        return self.previousModelItem(parent)

    def nextModelItem(self, item):
        parent = item.parent()
        if not parent:
            # Root has no sibling
            return None

        row = parent.childItems.index(item)
        if row + 1 < parent.childCount():
            return parent.child(row + 1)
        return self.nextModelItem(parent)

    def previousTextItem(self, item):
        previous = self.previousModelItem(item)

        while previous:
            last = self.lastTextItem(previous)
            if last:
                return last
            previous = self.previousModelItem(previous)
        return None
        
    def nextTextItem(self, item):
        if item.isFolder() and item.childCount() > 0:
            next = item.child(0)
        else:
            next = self.nextModelItem(item)

        while next:
            first = self.firstTextItem(next)
            if first:
                return first
            next = self.nextModelItem(next)
        return None

    def firstTextItem(self, item):
        if item.isText():
            return item
        for child in item.children():
            first = self.firstTextItem(child)
            if first:
                return first
        return None

    def lastTextItem(self, item):
        if item.isText():
            return item
        for child in reversed(item.children()):
            last = self.lastTextItem(child)
            if last:
                return last
        return None
Пример #13
0
class addTileDialog(QDialog):
    output = None
    ready = False

    def __init__(self):
        super().__init__()
        self.tilename = QComboBox()
        for tile in tilelist.tilelist:
            self.tilename.addItem(tile.__name__, tile)
        self.tilename.currentIndexChanged.connect(self.setTable)
        okbtn = QPushButton('Ok')
        okbtn.clicked.connect(self.onOk)
        clbtn = QPushButton('Cancel')
        clbtn.clicked.connect(self.onCancel)
        self.table = QTableWidget(2, 2)
        self.setTable(self.tilename.currentIndex())
        hbox = QHBoxLayout()
        hbox.addWidget(clbtn)
        hbox.addWidget(okbtn)
        vbox = QVBoxLayout()
        vbox.addWidget(self.tilename)
        vbox.addWidget(self.table)
        vbox.addLayout(hbox)
        self.setLayout(vbox)
        self.resize(400, 300)
        self.table.setColumnWidth(1, 250)
        self.setModal(True)
        self.show()

    def setTable(self, index):
        try:
            raw = 2 + len(self.tilename.itemData(index).extraText)
        except TypeError:
            raw = 2
        self.table.setRowCount(raw)
        self.table.setItem(0, 0, QTableWidgetItem('X position'))
        self.table.setCellWidget(0, 1, QSpinBox())
        self.table.setItem(1, 0, QTableWidgetItem('Y position'))
        self.table.setCellWidget(1, 1, QSpinBox())
        if self.tilename.itemData(index).extraText != None:
            i = 0
            for line in self.tilename.itemData(index).extraText:
                self.table.setItem(2 + i, 0, QTableWidgetItem(line))
                self.table.setCellWidget(2 + i, 1, QLineEdit(''))
                i += 1

    def getParams(self):
        x = self.table.cellWidget(0, 1).value()
        y = self.table.cellWidget(1, 1).value()
        extra = None
        if self.tilename.currentData().extraTypes != None:
            i = 0
            extra = []
            for t in self.tilename.currentData().extraTypes:
                if t == 'bool':
                    extra.append(bool(self.table.cellWidget(2 + i, 1).text()))
                elif t == 'int':
                    extra.append(int(self.table.cellWidget(2 + i, 1).text()))
                elif t == 'float':
                    extra.append(float(self.table.cellWidget(2 + i, 1).text()))
                elif t == 'str':
                    extra.append(str(self.table.cellWidget(2 + i, 1).text()))
                else:
                    extra.append(str(self.table.cellWidget(2 + i, 1).text()))
                i += 1
        return (x, y, extra)

    def getTileParams(self):
        return self.output

    def onOk(self):
        self.output = (self.tilename.currentData(), self.getParams())
        self.ready = True
        self.accept()

    def onCancel(self):
        self.output = (self.tilename.currentData(), self.getParams())
        self.ready = True
        self.reject()
Пример #14
0
class JdeRobotCommConfigDialog(QDialog):
    configChanged = pyqtSignal()

    def __init__(self, name):
        super(QDialog, self).__init__()
        self.setWindowTitle(name)
        self.config = None

        self.gridLayout = QGridLayout()

        # add header
        self.gridLayout.addWidget(QLabel('Server Type'), 0, 0)
        self.gridLayout.addWidget(QLabel('Name'), 0, 1)
        self.gridLayout.addWidget(QLabel('Topic'), 0, 2)
        self.gridLayout.addWidget(QLabel('Proxy Name'), 0, 3)
        self.gridLayout.addWidget(QLabel('IP'), 0, 4)
        self.gridLayout.addWidget(QLabel('Port'), 0, 5)
        self.gridLayout.addWidget(QLabel('Interface'), 0, 6)
        self.gridLayout.addWidget(QLabel(''), 0, 7)

        # add new config input fields
        fixedWidthFont = QFontDatabase.systemFont(QFontDatabase.FixedFont)
        self.serverTypeCombo = QComboBox()
        self.serverTypeCombo.setFont(fixedWidthFont)
        self.gridLayout.addWidget(self.serverTypeCombo, 1, 0)
        self.nameEdit = QLineEdit()
        self.nameEdit.setFont(fixedWidthFont)
        self.gridLayout.addWidget(self.nameEdit, 1, 1)
        self.topicEdit = QLineEdit()
        self.topicEdit.setFont(fixedWidthFont)
        self.topicEdit.setEnabled(False)
        self.gridLayout.addWidget(self.topicEdit, 1, 2)
        self.proxyNameEdit = QLineEdit()
        self.proxyNameEdit.setFont(fixedWidthFont)
        self.gridLayout.addWidget(self.proxyNameEdit, 1, 3)
        self.ipEdit = QLineEdit()
        self.ipEdit.setFont(fixedWidthFont)
        self.gridLayout.addWidget(self.ipEdit, 1, 4)
        self.portEdit = QLineEdit()
        self.portEdit.setFont(fixedWidthFont)
        self.gridLayout.addWidget(self.portEdit, 1, 5)
        self.interfaceCombo = QComboBox()
        self.gridLayout.addWidget(self.interfaceCombo, 1, 6)
        self.addButton = QPushButton('Add')
        self.gridLayout.addWidget(self.addButton, 1, 7)
        self.addButton.clicked.connect(self.addClicked)

        self.rowCount = 2

        # add server types to the combobox
        self.serverTypeCombo.addItem('ICE', 'ice')
        self.serverTypeCombo.addItem('ROS', 'ros')
        self.serverTypeCombo.currentIndexChanged.connect(self.serverTypeChanged)

        # add interfaces to the combobox
        interfaces = Interfaces.getInterfaces()
        for interfaceName in interfaces:
            self.interfaceCombo.addItem(interfaceName, interfaceName)

        self.resize(700, 100)
        self.setLayout(self.gridLayout)

    def clearAll(self):
        deleteList = []
        for i in range(self.rowCount):
            if i == 0 or i == 1:
                continue
            else:
                for j in range(8):
                    item = self.gridLayout.itemAtPosition(i, j)
                    deleteList.append(item)

        for item in deleteList:
            self.gridLayout.removeItem(item)
            item.widget().setParent(None)

        self.rowCount = 2


    def setConfig(self, config):
        self.config = config
        self.clearAll()
        if self.config is not None:
            for interface in self.config.getInterfaces():
                interface['id'] = self.rowCount
                self.addConfigRow(interface)


    def addConfigRow(self, configData):
        self.gridLayout.addWidget(QLabel(configData['serverType']), self.rowCount, 0)
        self.gridLayout.addWidget(QLabel(configData['name']), self.rowCount, 1)
        self.gridLayout.addWidget(QLabel(configData['topic']), self.rowCount, 2)
        self.gridLayout.addWidget(QLabel(configData['proxyName']), self.rowCount, 3)
        self.gridLayout.addWidget(QLabel(configData['ip']), self.rowCount, 4)
        self.gridLayout.addWidget(QLabel(configData['port']), self.rowCount, 5)
        self.gridLayout.addWidget(QLabel(configData['interface']), self.rowCount, 6)
        deleteButton = QPushButton('Delete')
        deleteButton.clicked.connect(self.deleteClicked)
        # we will find the item to be deleted based on the index on the config list
        deleteButton.setObjectName(str(self.rowCount))
        self.gridLayout.addWidget(deleteButton, self.rowCount, 7)
        self.rowCount += 1

    def serverTypeChanged(self):
        if self.serverTypeCombo.currentData() == 'ros':
            self.topicEdit.setEnabled(True)
            self.proxyNameEdit.setEnabled(False)
            self.ipEdit.setEnabled(False)
            self.portEdit.setEnabled(False)
        elif self.serverTypeCombo.currentData() == 'ice':
            self.topicEdit.setEnabled(False)
            self.proxyNameEdit.setEnabled(True)
            self.ipEdit.setEnabled(True)
            self.portEdit.setEnabled(True)
        self.configChanged.emit()

    def deleteClicked(self):
        if self.config is not None:
            id = int(self.sender().objectName())
            self.config.removeInterface(id)
            # reset the config to redraw all configs
            self.setConfig(self.config)
            self.configChanged.emit()

    def addClicked(self):
        configData = {}
        configData['serverType'] = self.serverTypeCombo.currentData()
        configData['name'] = self.nameEdit.text()
        configData['topic'] = self.topicEdit.text()
        configData['proxyName'] = self.proxyNameEdit.text()
        configData['ip'] = self.ipEdit.text()
        configData['port'] = self.portEdit.text()
        configData['interface'] = self.interfaceCombo.currentData()

        self.nameEdit.setText('')
        self.topicEdit.setText('')
        self.proxyNameEdit.setText('')
        self.ipEdit.setText('')
        self.portEdit.setText('')

        if self.config is not None:
            configData['id'] = self.rowCount
            self.config.addInterface(configData)
            self.addConfigRow(configData)
            self.configChanged.emit()
Пример #15
0
class _StylePreview(QGroupBox):
    preview_text_changed = pyqtSignal([])

    def __init__(
        self,
        api: Api,
        model: AssStylesModel,
        selection_model: QItemSelectionModel,
        parent: QWidget,
    ) -> None:
        super().__init__("Preview", parent)
        self._api = api
        self._selection_model = selection_model

        self._renderer = AssRenderer()

        self._editor = QPlainTextEdit()
        self._editor.setPlainText(api.cfg.opt["styles"]["preview_test_text"])
        self._editor.setFixedWidth(400)
        self._editor.setTabChangesFocus(True)
        self._editor.setFixedHeight(get_text_edit_row_height(self._editor, 2))

        self._background_combobox = QComboBox()
        for i, path in enumerate(get_assets("style_preview_bk")):
            self._background_combobox.addItem(path.name, path.resolve())
            if path.name == api.cfg.opt["styles"]["preview_background"]:
                self._background_combobox.setCurrentIndex(i)

        self._preview_box = QLabel(self)
        self._preview_box.setLineWidth(1)
        self._preview_box.setFrameShape(QFrame.StyledPanel)
        self._preview_box.setFrameShadow(QFrame.Sunken)
        self._preview_box.setSizePolicy(QSizePolicy.Ignored,
                                        QSizePolicy.Ignored)

        layout = QVBoxLayout(self)
        layout.addWidget(self._editor)
        layout.addWidget(self._background_combobox)
        layout.addWidget(self._preview_box)

        self.update_preview()
        self._editor.textChanged.connect(self._on_text_change)
        self._background_combobox.currentIndexChanged.connect(
            self._on_background_change)

        model.dataChanged.connect(self.update_preview)
        model.rowsInserted.connect(self.update_preview)
        model.rowsRemoved.connect(self.update_preview)
        selection_model.selectionChanged.connect(self.update_preview)

    def _on_background_change(self) -> None:
        self.update_preview()
        self._api.cfg.opt["styles"][
            "preview_background"] = self._background_combobox.currentData(
            ).name

    def _on_text_change(self) -> None:
        self.preview_text_changed.emit()
        self.update_preview()
        self._api.cfg.opt["styles"]["preview_test_text"] = self.preview_text

    @property
    def preview_text(self) -> str:
        return self._editor.toPlainText()

    @property
    def _selected_style(self) -> Optional[AssStyle]:
        try:
            idx = self._selection_model.selectedIndexes()[0].row()
        except IndexError:
            return None
        else:
            return self._api.subs.styles[idx]

    def update_preview(self) -> None:
        selected_style = self._selected_style
        if not selected_style:
            self._preview_box.clear()
            return

        resolution = (self._preview_box.width(), self._preview_box.height())
        if resolution[0] <= 0 or resolution[1] <= 0:
            self._preview_box.clear()
            return

        fake_file = AssFile()
        fake_style = copy(selected_style)
        fake_style.name = "Default"
        if (self._api.video.has_current_stream
                and self._api.video.current_stream.is_ready):
            fake_style.scale(resolution[1] /
                             self._api.video.current_stream.height)
        fake_file.styles.append(fake_style)

        fake_event = AssEvent(
            start=0,
            end=1000,
            text=self.preview_text.replace("\n", "\\N"),
            style_name=fake_style.name,
        )
        fake_file.events.append(fake_event)

        image = PIL.Image.new(mode="RGBA", size=resolution)

        background_path = self._background_combobox.currentData()
        if background_path and background_path.exists():
            background = PIL.Image.open(background_path)
            for y in range(0, resolution[1], background.height):
                for x in range(0, resolution[0], background.width):
                    image.paste(background, (x, y))

        self._renderer.set_source(fake_file, resolution)
        try:
            aspect_ratio = self._api.video.current_stream.aspect_ratio
        except ResourceUnavailable:
            aspect_ratio = Fraction(1)
        subs_image = self._renderer.render(
            time=0,
            aspect_ratio=aspect_ratio,
        )
        image = PIL.Image.composite(subs_image, image, subs_image)

        image = PIL.ImageQt.ImageQt(image)
        image = QImage(image)
        self._preview_box.setPixmap(QPixmap.fromImage(image))
Пример #16
0
class GeneralPage(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        layout = QFormLayout()

        self.server_url = MandatoryField(_("Server URL"), QLineEdit(self))
        self.server_url.add_to_form(layout)

        self.use_local_server = QCheckBox(self)
        layout.addRow(_("Use local server"), self.use_local_server)

        self.local_server_path = MandatoryField(
            _("Local server start command"), QLineEdit(self))
        self.local_server_path.add_to_form(layout)

        self.use_local_server.stateChanged.connect(self._on_use_local_server)

        self.proxy_usage = QComboBox(self)
        self.proxy_usage.addItem(_("Use system settings"), PROXY_SYSTEM)
        self.proxy_usage.addItem(_("Do not use proxy"), PROXY_NONE)
        self.proxy_usage.addItem(_("Manual configuration"), PROXY_CUSTOM)
        layout.addRow(_("Proxy usage"), self.proxy_usage)
        self.proxy_usage.currentIndexChanged.connect(self._on_use_proxy)

        self.proxy_address = QLineEdit(self)
        layout.addRow(_("Proxy"), self.proxy_address)
        self.proxy_address.setEnabled(False)

        self.log_level = QComboBox(self)
        self.log_level.addItem(_("Debug"), logging.DEBUG)
        self.log_level.addItem(_("Information"), logging.INFO)
        self.log_level.addItem(_("Warning"), logging.WARN)
        self.log_level.addItem(_("Error"), logging.ERROR)

        layout.addRow(_("Logging level"), self.log_level)

        self.setLayout(layout)

    def _on_use_local_server(self):
        use = self.use_local_server.checkState() == Qt.Checked
        self.local_server_path.widget.setEnabled(use)
        self.local_server_path.is_mandatory = use

    def _on_use_proxy(self):
        usage = self.proxy_usage.currentData()
        self.proxy_address.setEnabled(usage == PROXY_CUSTOM)

    def load(self, settings):
        url = settings.value("server_url", DEFAULT_SERVER_URL)
        self.server_url.widget.setText(url)

        use_local_server = settings.value("use_local_server", type=bool)
        self.use_local_server.setCheckState(
            Qt.Checked if use_local_server else Qt.Unchecked)

        path = settings.value("local_server_path", "hcheckersd --local")
        self.local_server_path.widget.setText(path)
        self.local_server_path.widget.setEnabled(use_local_server)
        self.local_server_path.widget.is_mandatory = use_local_server

        level = settings.value("log_level", logging.INFO, type=int)
        level_idx = self.log_level.findData(level)
        self.log_level.setCurrentIndex(level_idx)

        proxy_usage = settings.value("proxy_usage", PROXY_SYSTEM, type=int)
        self.proxy_usage.setCurrentIndex(proxy_usage)

        proxy = settings.value("proxy_address", EXAMPLE_PROXY)
        self.proxy_address.setText(proxy)

    def save(self, settings):
        settings.setValue("server_url", self.server_url.widget.text())
        use_local_server = self.use_local_server.checkState() == Qt.Checked
        settings.setValue("use_local_server", use_local_server)
        settings.setValue("local_server_path",
                          self.local_server_path.widget.text())
        level = self.log_level.currentData()
        settings.setValue("log_level", level)
        settings.setValue("proxy_usage", self.proxy_usage.currentData())
        settings.setValue("proxy_address", self.proxy_address.text())
Пример #17
0
class DomainWidget(QWidget):
    tab_active = pyqtSignal()
    go_to_data_tab = pyqtSignal()

    def __init__(self, iface: QgisInterface) -> None:
        super().__init__()
        self.iface = iface

        # Import/Export
        ## Import from 'namelist.wps'
        import_from_namelist_button = QPushButton("Import from namelist")
        import_from_namelist_button.setObjectName(
            'import_from_namelist_button')

        ## Export to namelist
        export_geogrid_namelist_button = QPushButton("Export to namelist")
        export_geogrid_namelist_button.setObjectName(
            'export_geogrid_namelist_button')

        vbox_import_export = QVBoxLayout()
        vbox_import_export.addWidget(import_from_namelist_button)
        vbox_import_export.addWidget(export_geogrid_namelist_button)

        self.gbox_import_export = QGroupBox("Import/Export")
        self.gbox_import_export.setLayout(vbox_import_export)

        # Group: Map Type
        self.group_box_map_type = QGroupBox("Map Type")
        vbox_map_type = QVBoxLayout()
        hbox_map_type = QHBoxLayout()

        self.projection = QComboBox()
        self.projection.setObjectName('projection')
        projs = {
            'undefined':
            '-',  # do not use a default projection - let the user pick the projection.
            'lat-lon': 'Latitude/Longitude',
            'lambert': 'Lambert'
        }
        for proj_id, proj_label in projs.items():
            self.projection.addItem(proj_label, proj_id)

        hbox_map_type.addWidget(QLabel('GCS/Projection:'))
        # TODO: when the user select the type of GCS/Projection
        # we should automatically change the GCS/Projection for the whole
        # project. This will do an on-the-fly remapping of any of the other CRS
        # which are different from the one supported by our tool/WRF.
        # The Project CRS can be accessed in QGIS under the menu `Project` > `Project Properties` > `CRS.`
        # -> This is only really possible for Lat/Lon as this one is a CRS, where the others are projections
        #    that only become a full CRS with the additional parameters like truelat1.
        # TODO: fields should be cleared when the user changes the CRS/Projection.
        hbox_map_type.addWidget(self.projection)
        vbox_map_type.addLayout(hbox_map_type)

        ## Lambert only: show 'True Latitudes' field
        truelat_grid = QGridLayout()
        self.truelat1 = add_grid_lineedit(truelat_grid,
                                          0,
                                          'True Latitude 1',
                                          LAT_VALIDATOR,
                                          unit='°',
                                          required=True)
        self.truelat2 = add_grid_lineedit(truelat_grid,
                                          1,
                                          'True Latitude 2',
                                          LAT_VALIDATOR,
                                          unit='°',
                                          required=True)
        self.widget_true_lats = QWidget()
        self.widget_true_lats.setLayout(truelat_grid)
        vbox_map_type.addWidget(self.widget_true_lats)

        self.domain_pb_set_projection = QPushButton("Set Map CRS")
        self.domain_pb_set_projection.setObjectName('set_projection_button')
        vbox_map_type.addWidget(self.domain_pb_set_projection)
        self.group_box_map_type.setLayout(vbox_map_type)

        # Group: Horizontal Resolution
        self.group_box_resol = QGroupBox("Horizontal Grid Spacing")
        hbox_resol = QHBoxLayout()
        self.resolution = MyLineEdit(required=True)
        self.resolution.setValidator(RESOLUTION_VALIDATOR)
        self.resolution.textChanged.connect(
            lambda _: update_input_validation_style(self.resolution))
        self.resolution.textChanged.emit(self.resolution.text())
        hbox_resol.addWidget(self.resolution)
        self.resolution_label = QLabel()
        hbox_resol.addWidget(self.resolution_label)

        self.group_box_resol.setLayout(hbox_resol)

        # Group: Automatic Domain Generator
        self.group_box_auto_domain = QGroupBox("Grid Extent Calculator")
        vbox_auto_domain = QVBoxLayout()
        hbox_auto_domain = QHBoxLayout()
        domain_pb_set_canvas_extent = QPushButton("Set to Canvas Extent")
        domain_pb_set_canvas_extent.setObjectName('set_canvas_extent_button')
        domain_pb_set_layer_extent = QPushButton("Set to Active Layer Extent")
        domain_pb_set_layer_extent.setObjectName('set_layer_extent_button')
        vbox_auto_domain.addLayout(hbox_auto_domain)
        vbox_auto_domain.addWidget(domain_pb_set_canvas_extent)
        vbox_auto_domain.addWidget(domain_pb_set_layer_extent)
        self.group_box_auto_domain.setLayout(vbox_auto_domain)

        # Group: Manual Domain Configuration

        ## Subgroup: Centre Point
        grid_center_point = QGridLayout()
        self.center_lon = add_grid_lineedit(grid_center_point,
                                            0,
                                            'Longitude',
                                            LON_VALIDATOR,
                                            '°',
                                            required=True)
        self.center_lat = add_grid_lineedit(grid_center_point,
                                            1,
                                            'Latitude',
                                            LAT_VALIDATOR,
                                            '°',
                                            required=True)
        group_box_centre_point = QGroupBox("Center Point")
        group_box_centre_point.setLayout(grid_center_point)

        ## Subgroup: Advanced configuration
        grid_dims = QGridLayout()
        self.cols = add_grid_lineedit(grid_dims,
                                      0,
                                      'Horizontal',
                                      DIM_VALIDATOR,
                                      required=True)
        self.rows = add_grid_lineedit(grid_dims,
                                      1,
                                      'Vertical',
                                      DIM_VALIDATOR,
                                      required=True)
        group_box_dims = QGroupBox("Grid Extent")
        group_box_dims.setLayout(grid_dims)

        vbox_manual_domain = QVBoxLayout()
        vbox_manual_domain.addWidget(group_box_centre_point)
        vbox_manual_domain.addWidget(group_box_dims)

        self.group_box_manual_domain = QGroupBox("Advanced Configuration")
        # TODO: make this section collapsable (default state collapsed)
        # and change the checkbox to arrows like `setArrowType(Qt.RightArrow)`
        # TODO: the style should be disabled when the 'advanced configuration' box is disabled (default).
        self.group_box_manual_domain.setCheckable(True)
        self.group_box_manual_domain.setChecked(False)
        self.group_box_manual_domain.setLayout(vbox_manual_domain)

        for field in [
                self.resolution, self.center_lat, self.center_lon, self.rows,
                self.cols, self.truelat1, self.truelat2
        ]:
            # editingFinished is only emitted on user input, not via programmatic changes.
            # This is important as we want to avoid re-drawing the bbox many times when several
            # fields get changed while using the automatic domain generator.
            field.editingFinished.connect(self.on_change_any_field)

        # Group Box: Parent Domain
        self.group_box_parent_domain = QGroupBox("Enable Parenting")
        self.group_box_parent_domain.setObjectName('group_box_parent_domain')
        self.group_box_parent_domain.setCheckable(True)
        self.group_box_parent_domain.setChecked(False)

        # TODO: As it is for the single domain case the generation of the domain should be automatic.
        # For now leave placeholder values of '3' for Child to Parent Ratio and '2' for padding.
        # use `calc_parent_lonlat_from_child` in `routines.py` to calculate the coordinate of the domain given the child domain coordinate,
        # grid_ratio and padding.
        hbox_parent_num = QHBoxLayout()
        hbox_parent_num.addWidget(QLabel('Number of Parent Domains:'))
        self.parent_spin = QSpinBox()
        self.parent_spin.setObjectName('parent_spin')
        self.parent_spin.setRange(1, MAX_PARENTS)
        hbox_parent_num.addWidget(self.parent_spin)

        self.group_box_parent_domain.setLayout(hbox_parent_num)

        self.parent_domains = []  # type: list
        self.parent_vbox = QVBoxLayout()
        self.parent_vbox.setSizeConstraint(QLayout.SetMinimumSize)

        go_to_data_tab_btn = QPushButton('Continue to Datasets')
        go_to_data_tab_btn.clicked.connect(self.go_to_data_tab)

        # Tabs
        dom_mgr_layout = QVBoxLayout()
        dom_mgr_layout.addWidget(self.gbox_import_export)
        dom_mgr_layout.addWidget(self.group_box_map_type)
        dom_mgr_layout.addWidget(self.group_box_resol)
        dom_mgr_layout.addWidget(self.group_box_auto_domain)
        dom_mgr_layout.addWidget(self.group_box_manual_domain)
        dom_mgr_layout.addWidget(self.group_box_parent_domain)
        dom_mgr_layout.addLayout(self.parent_vbox)
        dom_mgr_layout.addWidget(go_to_data_tab_btn)
        self.setLayout(dom_mgr_layout)

        QMetaObject.connectSlotsByName(self)

        # trigger event for initial layout
        self.projection.currentIndexChanged.emit(
            self.projection.currentIndex())

    @property
    def project(self) -> Project:
        return self._project

    @project.setter
    def project(self, val: Project) -> None:
        ''' Sets the currently active project. See tab_simulation. '''
        self._project = val
        self.populate_ui_from_project()

    def populate_ui_from_project(self) -> None:
        project = self.project
        try:
            domains = project.data['domains']
        except KeyError:
            return

        main_domain = domains[0]

        idx = self.projection.findData(main_domain['map_proj'])
        self.projection.setCurrentIndex(idx)
        try:
            truelat1 = main_domain['truelat1']
            self.truelat1.set_value(truelat1)

            truelat2 = main_domain['truelat2']
            self.truelat2.set_value(truelat2)
        except KeyError:
            pass

        self.resolution.set_value(main_domain['cell_size'][0])

        lon, lat = main_domain['center_lonlat']
        self.center_lat.set_value(lat)
        self.center_lon.set_value(lon)

        cols, rows = main_domain['domain_size']
        self.rows.set_value(rows)
        self.cols.set_value(cols)

        if len(domains) > 1:
            self.group_box_parent_domain.setChecked(True)
            self.parent_spin.setValue(len(domains) - 1)
            # We call the signal handler explicitly as we need the widgets ready immediately
            # and otherwise this is delayed until the signals are processed (queueing etc.).
            self.on_parent_spin_valueChanged(len(domains) - 1)

            for idx, parent_domain in enumerate(domains[1:]):
                fields, _ = self.parent_domains[idx]
                fields = fields['inputs']

                field_to_key = {
                    'ratio': 'parent_cell_size_ratio',
                    'top': 'padding_top',
                    'left': 'padding_left',
                    'right': 'padding_right',
                    'bottom': 'padding_bottom'
                }

                for field_name, key in field_to_key.items():
                    field = fields[field_name]
                    val = parent_domain[key]
                    field.set_value(val)

        self.draw_bbox_and_grids(zoom_out=True)

    @pyqtSlot()
    def on_import_from_namelist_button_clicked(self) -> None:
        file_path, _ = QFileDialog.getOpenFileName(caption='Open WPS namelist')
        if not file_path:
            return
        nml = read_namelist(file_path, schema_name='wps')
        project = convert_wps_nml_to_project(nml, self.project)
        Broadcast.open_project_from_object.emit(project)

    @pyqtSlot()
    def on_export_geogrid_namelist_button_clicked(self):
        if not self.update_project():
            raise UserError('Domain configuration invalid, check fields')
        file_path, _ = QFileDialog.getSaveFileName(caption='Save WPS namelist as', \
                                                   directory='namelist.wps')
        if not file_path:
            return
        wps_namelist = convert_project_to_wps_namelist(self.project)
        write_namelist(wps_namelist, file_path)

    @pyqtSlot()
    def on_set_projection_button_clicked(self):
        crs = self.create_domain_crs()

        qgsProject = QgsProject.instance()  # type: QgsProject
        qgsProject.setCrs(get_qgis_crs(crs.proj4))

    def create_domain_crs(self) -> CRS:
        proj = self.get_proj_kwargs()
        if proj is None:
            raise UserError('Incomplete projection definition')

        origin_valid = all(
            map(lambda w: w.is_valid(), [self.center_lat, self.center_lon]))
        if origin_valid:
            origin = LonLat(self.center_lon.value(), self.center_lat.value())
        else:
            origin = LonLat(0, 0)

        if proj['map_proj'] == 'lambert':
            crs = CRS.create_lambert(proj['truelat1'], proj['truelat2'],
                                     origin)
        elif proj['map_proj'] == 'lat-lon':
            crs = CRS.create_lonlat()
        else:
            assert False, 'unknown proj: ' + proj['map_proj']
        return crs

    @pyqtSlot()
    def on_set_canvas_extent_button_clicked(self):
        if not self.resolution.is_valid():
            return
        canvas = self.iface.mapCanvas()  # type: QgsMapCanvas

        settings = canvas.mapSettings()  # type: QgsMapSettings
        map_crs = settings.destinationCrs(
        )  # type: QgsCoordinateReferenceSystem

        extent = canvas.extent()  # type: QgsRectangle
        self.set_domain_to_extent(map_crs, extent)

    @pyqtSlot()
    def on_set_layer_extent_button_clicked(self):
        if not self.resolution.is_valid():
            return
        layer = self.iface.activeLayer()  # type: QgsMapLayer
        if layer is None:
            print('foo')
            raise UserError('No layer selected, use the "Layers" panel')

        layer_crs = layer.crs()  # type: QgsCoordinateReferenceSystem

        extent = layer.extent()  # type: QgsRectangle
        self.set_domain_to_extent(layer_crs, extent)

    def set_domain_to_extent(self, crs: QgsCoordinateReferenceSystem,
                             extent: QgsRectangle) -> None:
        resolution = self.resolution.value()

        bbox = rect_to_bbox(extent)

        extent_crs = CRS(crs.toProj4())
        domain_crs = self.create_domain_crs()
        domain_srs = domain_crs.srs

        domain_bbox = domain_crs.transform_bbox(bbox, domain_srs)

        # TODO disallow creation of bounding box outside projection range (e.g. for lat-lon 360-180)

        xmin, xmax, ymin, ymax = domain_bbox.minx, domain_bbox.maxx, domain_bbox.miny, domain_bbox.maxy

        center_x = xmin + (xmax - xmin) / 2
        center_y = ymin + (ymax - ymin) / 2
        center_lonlat = domain_crs.to_lonlat(Coordinate2D(center_x, center_y))
        self.center_lat.set_value(center_lonlat.lat)
        self.center_lon.set_value(center_lonlat.lon)
        self.resolution.set_value(resolution)
        cols = ceil((xmax - xmin) / resolution)
        rows = ceil((ymax - ymin) / resolution)
        self.cols.set_value(cols)
        self.rows.set_value(rows)

        self.on_change_any_field(zoom_out=True)

    @pyqtSlot()
    def on_group_box_parent_domain_clicked(self):
        if self.group_box_parent_domain.isChecked():
            self.add_parent_domain()
        else:
            self.parent_spin.setValue(1)
            while self.parent_domains:
                self.remove_last_parent_domain()

    def add_parent_domain(self):
        idx = len(self.parent_domains) + 1
        fields, group_box_parent = create_parent_group_box('Parent ' +
                                                           str(idx),
                                                           '?',
                                                           self.proj_res_unit,
                                                           required=True)
        self.parent_vbox.addWidget(group_box_parent)
        # "If you add a child widget to an already visible widget you must
        #  explicitly show the child to make it visible."
        # (http://doc.qt.io/qt-5/qwidget.html#QWidget)
        group_box_parent.show()
        self.parent_domains.append((fields, group_box_parent))
        # After adding/removing widgets, we need to tell Qt to recompute the sizes.
        # This always has to be done on the widget where the child widgets have been changed,
        # here self.subtab_parenting (which contains self.parent_vbox).
        self.adjustSize()

        for field in fields['inputs'].values():
            field.editingFinished.connect(self.on_change_any_field)

    def remove_last_parent_domain(self):
        _, group_box_parent = self.parent_domains.pop()
        group_box_parent.deleteLater()
        self.parent_vbox.removeWidget(group_box_parent)
        self.on_change_any_field()

    @pyqtSlot(int)
    def on_parent_spin_valueChanged(self, value: int) -> None:
        count = len(self.parent_domains)
        for _ in range(value, count):
            self.remove_last_parent_domain()
        for _ in range(count, value):
            self.add_parent_domain()

    @pyqtSlot(int)
    def on_projection_currentIndexChanged(self, index: int) -> None:
        proj_id = self.projection.currentData()
        is_lambert = proj_id == 'lambert'
        is_undefined = proj_id == 'undefined'
        is_lat_lon = proj_id == 'lat-lon'

        self.domain_pb_set_projection.setDisabled(is_undefined)
        self.group_box_resol.setDisabled(is_undefined)
        self.group_box_auto_domain.setDisabled(is_undefined)
        self.group_box_manual_domain.setDisabled(is_undefined)
        self.group_box_parent_domain.setDisabled(is_undefined)

        self.widget_true_lats.setVisible(is_lambert)

        if is_undefined:
            self.proj_res_unit = ''
        elif is_lat_lon:
            self.proj_res_unit = '°'
        elif is_lambert:
            self.proj_res_unit = 'm'
        self.resolution_label.setText(self.proj_res_unit)

        # If the projection is changed the parent domains are removed
        self.group_box_parent_domain.setChecked(False)
        for _ in self.parent_domains:
            self.remove_last_parent_domain()

        self.adjustSize()

    def get_proj_kwargs(self) -> dict:
        proj_id = self.projection.currentData()
        kwargs = {'map_proj': proj_id}
        if proj_id == 'lambert':
            valid = all(
                map(lambda w: w.is_valid(), [self.truelat1, self.truelat2]))
            if not valid:
                return None
            kwargs = {
                'map_proj': proj_id,
                'truelat1': self.truelat1.value(),
                'truelat2': self.truelat2.value(),
            }
        return kwargs

    def update_project(self) -> bool:
        proj_kwargs = self.get_proj_kwargs()
        if proj_kwargs is None:
            return False

        valid = all(
            map(lambda w: w.is_valid(), [
                self.center_lat, self.center_lon, self.resolution, self.cols,
                self.rows
            ]))
        if not valid:
            return False
        center_lonlat = LonLat(lon=self.center_lon.value(),
                               lat=self.center_lat.value())
        resolution = self.resolution.value()
        domain_size = (self.cols.value(), self.rows.value())

        parent_domains = []

        for parent_domain in self.parent_domains:
            fields, _ = parent_domain
            inputs = fields['inputs']
            valid = all(map(lambda w: w.is_valid(), inputs.values()))
            if not valid:
                return False
            ratio, top, left, right, bottom = [
                inputs[name].value()
                for name in ['ratio', 'top', 'left', 'right', 'bottom']
            ]

            parent_domains.append({
                'parent_cell_size_ratio': ratio,
                'padding_left': left,
                'padding_right': right,
                'padding_bottom': bottom,
                'padding_top': top
            })

        self.project.set_domains(cell_size=(resolution, resolution),
                                 domain_size=domain_size,
                                 center_lonlat=center_lonlat,
                                 parent_domains=parent_domains,
                                 **proj_kwargs)
        return True

    def on_change_any_field(self, zoom_out=False):
        if not self.update_project():
            return

        domains = self.project.data['domains']

        # update main domain size as it may have been adjusted
        main_domain_size = domains[0]['domain_size']
        self.cols.set_value(main_domain_size[0])
        self.rows.set_value(main_domain_size[1])

        for (fields, _), domain in zip(self.parent_domains, domains[1:]):
            # update the parent resolutions
            res_label = fields['other']['resolution']
            res_label.setText(
                HORIZONTAL_RESOLUTION_LABEL.format(
                    resolution=domain['cell_size'][0],
                    unit=self.proj_res_unit))

            # update any padding as it may have been adjusted
            for name in ['left', 'right', 'top', 'bottom']:
                field = fields['inputs'][name]
                field.set_value(domain['padding_' + name])

        self.draw_bbox_and_grids(zoom_out)

    def draw_bbox_and_grids(self, zoom_out: bool) -> None:
        project = self.project

        update_domain_grid_layers(project)
        update_domain_outline_layers(self.iface.mapCanvas(),
                                     project,
                                     zoom_out=zoom_out)
Пример #18
0
class AiEditorWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self._setup()

    edited = pyqtSignal()

    def _setup(self):
        self.tabs = QTabWidget(self)

        general = QWidget(self.tabs)
        layout = QFormLayout()
        general.setLayout(layout)

        def make_spinbox(title, low, high, tab):
            widget = QSpinBox(tab)
            widget.setRange(low, high)
            widget.valueChanged.connect(self.edited)
            tab.layout().addRow(title, widget)
            return widget

        self.title = MandatoryField(_("Title"), QLineEdit(general))
        self.title.add_to_form(layout)
        self.title.widget.editingFinished.connect(self.edited)

        self.depth = make_spinbox(_("Default depth (half-steps)"), 0, 20,
                                  general)
        self.start_depth = make_spinbox(_("Minimum depth"), 0, 20, general)
        self.max_combination_depth = make_spinbox(_("Forced mode depth"), 0,
                                                  24, general)
        self.dynamic_depth = make_spinbox(_("Static search mode threshold"), 0,
                                          24, general)

        self.deeper_if_bad = QCheckBox(general)
        self.deeper_if_bad.stateChanged.connect(self.edited)
        layout.addRow(_("Think better if situation seem bad"),
                      self.deeper_if_bad)

        self.moves_bound_low = make_spinbox(_("`Few moves' mode bound"), 1, 5,
                                            general)
        self.moves_bound_high = make_spinbox(_("`Too many moves' mode bound"),
                                             5, 50, general)

        self.use_positional_score = QCheckBox(general)
        layout.addRow(_("Use positional score"), self.use_positional_score)
        self.use_positional_score.stateChanged.connect(self.edited)

        self.use_timeout = QCheckBox(general)
        layout.addRow(_("Continue thinking while there is time"),
                      self.use_timeout)
        self.use_timeout.stateChanged.connect(self.edited)
        self.use_timeout.stateChanged.connect(self._on_use_timeout)

        self.timeout = make_spinbox(_("Timeout (seconds)"), 1, 120, general)
        self.timeout.setEnabled(False)

        self.random_opening_depth = make_spinbox(_("Random opening depth"), 1,
                                                 5, general)
        self.random_opening_depth.setToolTip(
            _("Number of first moves to be considered as opening; during these moves, AI will select it's move randomly from several best options"
              ))
        self.random_opening_options = make_spinbox(_("Random opening options"),
                                                   1, 5, general)
        self.random_opening_options.setToolTip(
            _("From how many best options to select during the opening"))

        self.accept_draw = QComboBox(self)
        self.accept_draw.addItem(_("Always accept"), ALWAYS_ACCEPT)
        self.accept_draw.addItem(_("Always decline"), ALWAYS_DECLINE)
        self.accept_draw.addItem(_("Accept if AI is losing"), ACCEPT_IF_LOSING)
        self.accept_draw.currentIndexChanged.connect(self.edited)

        layout.addRow(_("Accept draws"), self.accept_draw)

        self.tabs.addTab(general, _("General"))

        evaluator = QWidget(self.tabs)
        layout = QFormLayout()
        evaluator.setLayout(layout)

        self.mobility_weight = make_spinbox(_("Mobility"), -500, 500,
                                            evaluator)
        self.backyard_weight = make_spinbox(_("Back row"), -100, 100,
                                            evaluator)
        self.center_weight = make_spinbox(_("Center"), -100, 100, evaluator)
        self.opposite_side_weight = make_spinbox(_("Opposite side"), -100, 100,
                                                 evaluator)
        self.backed_weight = make_spinbox(_("Backed"), -100, 100, evaluator)
        self.asymetry_weight = make_spinbox(_("Asymetry"), -100, 100,
                                            evaluator)
        self.pre_king_weight = make_spinbox(_("Pre-king"), 1, 100, evaluator)
        self.king_coef = make_spinbox(_("King"), 1, 100, evaluator)
        self.attacked_man_coef = make_spinbox(_("Attacked man"), -300, 300,
                                              evaluator)
        self.attacked_king_coef = make_spinbox(_("Attacked king"), -300, 300,
                                               evaluator)

        self.tabs.addTab(evaluator, _("Board evaluation"))

        extra = QWidget(self.tabs)
        layout = QVBoxLayout()
        extra.setLayout(layout)

        self.extra = QTextEdit(general)
        self.extra.textChanged.connect(self.edited)
        layout.addWidget(self.extra)
        self.tabs.addTab(extra, _("Extra options"))

        layout = QVBoxLayout()
        layout.addWidget(self.tabs)

        hbox = QHBoxLayout()

        save = QPushButton(_("Save..."), self)
        save.setIcon(QIcon.fromTheme("document-save"))
        save.setToolTip(_("Save AI settings to JSON file"))
        save.clicked.connect(self._on_save)
        hbox.addWidget(save)

        load = QPushButton(_("Load..."), self)
        load.setIcon(QIcon.fromTheme("document-open"))
        load.setToolTip(_("Load AI settings from JSON file"))
        load.clicked.connect(self._on_load)
        hbox.addWidget(load)

        layout.addLayout(hbox)

        self.setLayout(layout)

    def _on_use_timeout(self):
        use = self.use_timeout.checkState() == Qt.Checked
        self.timeout.setEnabled(use)

    def _on_save(self):
        path, mask = QFileDialog.getSaveFileName(self, _("Save file"), ".",
                                                 JSON_MASK)
        if path:
            ai = self.get_ai()
            json_data = ai.params()
            with open(path, 'w') as f:
                f.write(json.dumps(json_data))

    def _on_load(self):
        path, mask = QFileDialog.getOpenFileName(self, _("Load file"), ".",
                                                 JSON_MASK)
        if path:
            try:
                with open(path) as f:
                    text = f.read()
                    json_data = json.loads(text)
                    ai = AI()
                    ai.title = self.get_ai().title
                    ai.load_json(json_data)
                    self.set_ai(ai)
            except Exception as e:
                logging.exception(e)

    def set_ai(self, ai):
        self.title.widget.setText(ai.title)
        self.depth.setValue(ai.depth)
        if ai.start_depth is not None:
            self.start_depth.setValue(ai.start_depth)
        self.max_combination_depth.setValue(ai.max_combination_depth)
        self.dynamic_depth.setValue(ai.dynamic_depth)
        self.deeper_if_bad.setCheckState(
            Qt.Checked if ai.deeper_if_bad else Qt.Unchecked)
        self.moves_bound_low.setValue(ai.moves_bound_low)
        self.moves_bound_high.setValue(ai.moves_bound_high)
        self.use_positional_score.setCheckState(
            Qt.Checked if ai.use_positional_score else Qt.Unchecked)
        self.use_timeout.setCheckState(
            Qt.Checked if ai.use_timeout else Qt.Unchecked)
        self.timeout.setValue(1 if ai.timeout is None else ai.timeout)
        self.random_opening_depth.setValue(ai.random_opening_depth)
        self.random_opening_options.setValue(ai.random_opening_options)

        self.mobility_weight.setValue(ai.mobility_weight)
        self.backyard_weight.setValue(ai.backyard_weight)
        self.center_weight.setValue(ai.center_weight)
        self.opposite_side_weight.setValue(ai.opposite_side_weight)
        self.backed_weight.setValue(ai.backed_weight)
        self.asymetry_weight.setValue(ai.asymetry_weight)
        self.pre_king_weight.setValue(ai.pre_king_weight)
        self.king_coef.setValue(ai.king_coef)
        self.attacked_man_coef.setValue(-ai.attacked_man_coef)
        self.attacked_king_coef.setValue(-ai.attacked_king_coef)

        policy = ai.accept_draw
        policy_idx = self.accept_draw.findData(policy)
        self.accept_draw.setCurrentIndex(policy_idx)

        self.extra.setText("" if ai.extra is None else ai.extra)

    def get_ai(self):
        ai = AI()
        ai.title = self.title.widget.text()
        ai.depth = self.depth.value()
        ai.start_depth = self.start_depth.value()
        ai.max_combination_depth = self.max_combination_depth.value()
        ai.dynamic_depth = self.dynamic_depth.value()
        ai.deeper_if_bad = self.deeper_if_bad.checkState() == Qt.Checked
        ai.moves_bound_low = self.moves_bound_low.value()
        ai.moves_bound_high = self.moves_bound_high.value()
        ai.use_positional_score = self.use_positional_score.checkState(
        ) == Qt.Checked
        ai.use_timeout = self.use_timeout.checkState() == Qt.Checked
        ai.timeout = self.timeout.value()
        ai.random_opening_depth = self.random_opening_depth.value()
        ai.random_opening_options = self.random_opening_options.value()

        ai.mobility_weight = self.mobility_weight.value()
        ai.backyard_weight = self.backyard_weight.value()
        ai.center_weight = self.center_weight.value()
        ai.opposite_side_weight = self.opposite_side_weight.value()
        ai.backed_weight = self.backed_weight.value()
        ai.asymetry_weight = self.asymetry_weight.value()
        ai.pre_king_weight = self.pre_king_weight.value()
        ai.king_coef = self.king_coef.value()
        ai.attacked_man_coef = -self.attacked_man_coef.value()
        ai.attacked_king_coef = -self.attacked_king_coef.value()

        ai.accept_draw = self.accept_draw.currentData()

        ai.extra = self.extra.toPlainText()
        return ai
Пример #19
0
class ConfigDialog(QDialog):
    configChanged = pyqtSignal()
    def __init__(self, title, config):
        QDialog.__init__(self)
        if config is not None:
            self.type = config.type
        else:
            self.type = JDEROBOTCOMM

        self.setWindowTitle(title)
        commSelectionBox = QGroupBox('Select Communication Interface')
        commSelectionBox.setObjectName('commInterface')
        # add new config input fields
        fixedWidthFont = QFontDatabase.systemFont(QFontDatabase.FixedFont)
        self.commTypeCombo = QComboBox()
        self.commTypeCombo.setFont(fixedWidthFont)
        self.commTypeCombo.setMaximumWidth(220)
        boxLayout = QVBoxLayout()
        boxLayout.addWidget(self.commTypeCombo)
        commSelectionBox.setLayout(boxLayout)
        vLayout = QFormLayout()
        vLayout.addWidget(commSelectionBox)

        self.configsLayout = QVBoxLayout()
        self.configsBox = QGroupBox('')
        self.configsBox.setLayout(self.configsLayout)
        vLayout.addWidget(self.configsBox)

        self.setLayout(vLayout)
        self.resize(700, 500)
        #self.setStyleSheet('QGroupBox#commInterface { border: 1px solid black; border-radius: 4px; padding:15px;} QGroupBox::title#commInterface {background-color:transparent; padding-left:25px; padding-top:5px;} ')

        self.rosConfigsUI = RosConfigDialog('ROS Communication')
        self.rosConfigsUI.configChanged.connect(self.configChangedHandler)
        self.configsLayout.addWidget(self.rosConfigsUI)
        self.rosConfigsUI.setVisible(False)
        self.jderobotCommConfigsUI = JdeRobotCommConfigDialog('JdeRobot Communication')
        self.jderobotCommConfigsUI.configChanged.connect(self.configChangedHandler)
        self.configsLayout.addWidget(self.jderobotCommConfigsUI)
        self.jderobotCommConfigsUI.setVisible(True)

        self.rosConfig = None
        self.jdeRobotCommConfig = None

        self.commTypeCombo.addItem('JdeRobot Communication', 'jderobotcomm')
        self.commTypeCombo.addItem('ROS Node', 'ros')
        self.commTypeCombo.currentIndexChanged.connect(self.commTypeComboChanged)

        if config is not None:
            if config.type == ROS:
                self.rosConfig = config
                self.commTypeCombo.setCurrentIndex(1)
                self.loadRosConfigs()
            elif config.type == JDEROBOTCOMM:
                self.jdeRobotCommConfig = config
                self.commTypeCombo.setCurrentIndex(0)
                self.loadJdeRobotCommConfigs()
        else:
            self.loadJdeRobotCommConfigs()



    def commTypeComboChanged(self):
        if self.commTypeCombo.currentData() == 'ros':
            self.loadRosConfigs()
        elif self.commTypeCombo.currentData() == 'jderobotcomm':
            self.loadJdeRobotCommConfigs()


    def loadRosConfigs(self):
        self.type = ROS
        self.jderobotCommConfigsUI.setVisible(False)
        self.rosConfigsUI.setVisible(True)
        if self.rosConfig is None:
            self.rosConfig = RosConfig()
        self.rosConfigsUI.setConfig(self.rosConfig)
        self.configChanged.emit()

    def loadJdeRobotCommConfigs(self):
        self.type = JDEROBOTCOMM
        self.rosConfigsUI.setVisible(False)
        self.jderobotCommConfigsUI.setVisible(True)
        if self.jdeRobotCommConfig is None:
            self.jdeRobotCommConfig = JdeRobotConfig()
        self.jderobotCommConfigsUI.setConfig(self.jdeRobotCommConfig)
        self.configChanged.emit()

    def configChangedHandler(self):
        self.configChanged.emit()

    def getConfig(self):
        if self.type == ROS:
            return self.rosConfig
        elif self.type == JDEROBOTCOMM:
            return self.jdeRobotCommConfig
Пример #20
0
class FlowBoardEditorToolBar(QToolBar):

    makeNewBoard = pyqtSignal(int)  # argument: board size
    toolChanged = pyqtSignal()

    def __init__(self):
        super(FlowBoardEditorToolBar, self).__init__()
        self.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))

        boardbox = QBoxLayout(QBoxLayout.TopToBottom)
        boardbox.setSpacing(2)

        self._sizelist = QComboBox()
        for s in range(5, 16):
            self._sizelist.addItem("{0}x{0}".format(s), s)
        self._sizelist.setCurrentIndex(2)
        boardbox.addWidget(self._sizelist)
        self._sizelist.currentIndexChanged.connect(self._sizelistChanged)

        self._clearbutton = QPushButton("clear")
        boardbox.addWidget(self._clearbutton)
        self._clearbutton.clicked.connect(self._clearClicked)

        boardboxwidget = QWidget()
        boardboxwidget.setLayout(boardbox)
        self.addWidget(boardboxwidget)

        self._toolchooser = FlowToolChooser()
        self.addWidget(self._toolchooser)
        self._toolchooser.changed.connect(self._toolChanged)

    @property
    def selectedSize(self):
        return self._sizelist.currentData()

    @property
    def selectedEndpointKey(self):
        t = self._toolchooser.selected
        return t.endpointKey if isinstance(t, FlowToolEndpoint) else None

    def selectSize(self, size):
        if size != self.selectedSize:
            i = self._sizelist.findData(size)
            if i >= 0:
                self._sizelist.setCurrentIndex(i)

    @property
    def tools(self):
        return self._toolchooser

    @pyqtSlot(FlowBoard)
    def updateBoard(self, board):
        self._clearbutton.setEnabled(not board.isEmpty())
        ek = self.selectedEndpointKey
        if ek is not None and board.hasCompleteEndpoints(ek):
            self._toolchooser.selectFirstOpenEndpoint(board)

    @pyqtSlot(int)
    def _sizelistChanged(self, _):
        self.makeNewBoard.emit(self.selectedSize)

    @pyqtSlot(bool)
    def _clearClicked(self, _):
        self.makeNewBoard.emit(self.selectedSize)

    @pyqtSlot()
    def _toolChanged(self):
        self.toolChanged.emit()
Пример #21
0
class fullScreenEditor(QWidget):
    def __init__(self, index, parent=None):
        QWidget.__init__(self, parent)
        self._background = None
        self._index = index
        self._theme = findThemePath(settings.fullScreenTheme)
        self._themeDatas = loadThemeDatas(self._theme)
        self.setMouseTracking(True)
        self._geometries = {}

        # Text editor
        self.editor = textEditView(self,
                                   index=index,
                                   spellcheck=settings.spellcheck,
                                   highlighting=True,
                                   dict=settings.dict)
        self.editor.setFrameStyle(QFrame.NoFrame)
        self.editor.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.editor.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.editor.installEventFilter(self)
        self.editor.setMouseTracking(True)
        self.editor.setVerticalScrollBar(myScrollBar())
        self.scrollBar = self.editor.verticalScrollBar()
        self.scrollBar.setParent(self)

        # Top Panel
        self.topPanel = myPanel(parent=self)
        # self.topPanel.layout().addStretch(1)

        # Spell checking
        if enchant:
            self.btnSpellCheck = QPushButton(self)
            self.btnSpellCheck.setFlat(True)
            self.btnSpellCheck.setIcon(QIcon.fromTheme("tools-check-spelling"))
            self.btnSpellCheck.setCheckable(True)
            self.btnSpellCheck.setChecked(self.editor.spellcheck)
            self.btnSpellCheck.toggled.connect(self.editor.toggleSpellcheck)
            self.topPanel.layout().addWidget(self.btnSpellCheck)

        self.topPanel.layout().addStretch(1)

        # Formatting
        self.textFormat = textFormat(self)
        self.topPanel.layout().addWidget(self.textFormat)
        self.topPanel.layout().addStretch(1)

        self.btnClose = QPushButton(self)
        self.btnClose.setIcon(qApp.style().standardIcon(QStyle.SP_DialogCloseButton))
        self.btnClose.clicked.connect(self.close)
        self.btnClose.setFlat(True)
        self.topPanel.layout().addWidget(self.btnClose)

        # Left Panel
        self._locked = False
        self.leftPanel = myPanel(vertical=True, parent=self)
        self.locker = locker(self)
        self.locker.lockChanged.connect(self.setLocked)
        self.leftPanel.layout().addWidget(self.locker)

        # Bottom Panel
        self.bottomPanel = myPanel(parent=self)

        self.bottomPanel.layout().addSpacing(24)
        self.lstThemes = QComboBox(self)
        self.lstThemes.setAttribute(Qt.WA_TranslucentBackground)
        paths = allPaths("resources/themes")
        for p in paths:
            lst = [i for i in os.listdir(p) if os.path.splitext(i)[1] == ".theme"]
            for t in lst:
                themeIni = os.path.join(p, t)
                name = loadThemeDatas(themeIni)["Name"]
                # self.lstThemes.addItem(os.path.splitext(t)[0])
                self.lstThemes.addItem(name)
                self.lstThemes.setItemData(self.lstThemes.count()-1, os.path.splitext(t)[0])

        self.lstThemes.setCurrentIndex(self.lstThemes.findData(settings.fullScreenTheme))
        # self.lstThemes.setCurrentText(settings.fullScreenTheme)
        self.lstThemes.currentTextChanged.connect(self.setTheme)
        self.lstThemes.setMaximumSize(QSize(300, QFontMetrics(qApp.font()).height()))
        self.bottomPanel.layout().addWidget(QLabel(self.tr("Theme:"), self))
        self.bottomPanel.layout().addWidget(self.lstThemes)
        self.bottomPanel.layout().addStretch(1)

        self.lblProgress = QLabel(self)
        self.lblProgress.setMaximumSize(QSize(200, 14))
        self.lblProgress.setMinimumSize(QSize(100, 14))
        self.lblWC = QLabel(self)
        self.bottomPanel.layout().addWidget(self.lblWC)
        self.bottomPanel.layout().addWidget(self.lblProgress)
        self.updateStatusBar()

        self.bottomPanel.layout().addSpacing(24)

        # Connection
        self._index.model().dataChanged.connect(self.dataChanged)

        # self.updateTheme()
        self.showFullScreen()
        # self.showMaximized()
        # self.show()

    def setLocked(self, val):
        self._locked = val
        self.btnClose.setVisible(not val)

    def setTheme(self, themeName):
        themeName = self.lstThemes.currentData()
        settings.fullScreenTheme = themeName
        self._theme = findThemePath(themeName)
        self._themeDatas = loadThemeDatas(self._theme)
        self.updateTheme()

    def updateTheme(self):
        # Reinit stored geometries for hiding widgets
        self._geometries = {}
        rect = self.geometry()
        self._background = generateTheme(self._themeDatas, rect)

        setThemeEditorDatas(self.editor, self._themeDatas, self._background, rect)

        # Colors
        if self._themeDatas["Foreground/Color"] == self._themeDatas["Background/Color"] or \
                        self._themeDatas["Foreground/Opacity"] < 5:
            self._bgcolor = QColor(self._themeDatas["Text/Color"])
            self._fgcolor = QColor(self._themeDatas["Background/Color"])
        else:
            self._bgcolor = QColor(self._themeDatas["Foreground/Color"])
            self._bgcolor.setAlpha(self._themeDatas["Foreground/Opacity"] * 255 / 100)
            self._fgcolor = QColor(self._themeDatas["Text/Color"])
            if self._themeDatas["Text/Color"] == self._themeDatas["Foreground/Color"]:
                self._fgcolor = QColor(self._themeDatas["Background/Color"])

        # ScrollBar
        r = self.editor.geometry()
        w = qApp.style().pixelMetric(QStyle.PM_ScrollBarExtent)
        r.setWidth(w)
        r.moveRight(rect.right() - rect.left())
        self.scrollBar.setGeometry(r)
        # self.scrollBar.setVisible(False)
        self.hideWidget(self.scrollBar)
        p = self.scrollBar.palette()
        b = QBrush(self._background.copy(self.scrollBar.geometry()))
        p.setBrush(QPalette.Base, b)
        self.scrollBar.setPalette(p)

        self.scrollBar.setColor(self._bgcolor)

        # Left Panel
        r = self.locker.geometry()
        r.moveTopLeft(QPoint(
                0,
                self.geometry().height() / 2 - r.height() / 2
        ))
        self.leftPanel.setGeometry(r)
        self.hideWidget(self.leftPanel)
        self.leftPanel.setColor(self._bgcolor)

        # Top / Bottom Panels
        r = QRect(0, 0, 0, 24)
        r.setWidth(rect.width())
        # r.moveLeft(rect.center().x() - r.width() / 2)
        self.topPanel.setGeometry(r)
        # self.topPanel.setVisible(False)
        self.hideWidget(self.topPanel)
        r.moveBottom(rect.bottom() - rect.top())
        self.bottomPanel.setGeometry(r)
        # self.bottomPanel.setVisible(False)
        self.hideWidget(self.bottomPanel)
        self.topPanel.setColor(self._bgcolor)
        self.bottomPanel.setColor(self._bgcolor)

        # Lst theme
        # p = self.lstThemes.palette()
        p = self.palette()
        p.setBrush(QPalette.Button, self._bgcolor)
        p.setBrush(QPalette.ButtonText, self._fgcolor)
        p.setBrush(QPalette.WindowText, self._fgcolor)

        for panel in (self.bottomPanel, self.topPanel):
            for i in range(panel.layout().count()):
                item = panel.layout().itemAt(i)
                if item.widget():
                    item.widget().setPalette(p)
        # self.lstThemes.setPalette(p)
        # self.lblWC.setPalette(p)

        self.update()

    def paintEvent(self, event):
        if self._background:
            painter = QPainter(self)
            painter.setClipRegion(event.region())
            painter.drawPixmap(event.rect(), self._background, event.rect())
            painter.end()

    def resizeEvent(self, event):
        self.updateTheme()

    def keyPressEvent(self, event):
        if event.key() in [Qt.Key_Escape, Qt.Key_F11] and \
                not self._locked:
            self.close()
        else:
            QWidget.keyPressEvent(self, event)

    def mouseMoveEvent(self, event):
        r = self.geometry()

        for w in [self.scrollBar, self.topPanel,
                  self.bottomPanel, self.leftPanel]:
            # w.setVisible(w.geometry().contains(event.pos()))
            if self._geometries[w].contains(event.pos()):
                self.showWidget(w)
            else:
                self.hideWidget(w)

    def hideWidget(self, widget):
        if widget not in self._geometries:
            self._geometries[widget] = widget.geometry()
        widget.move(self.geometry().bottomRight())

    def showWidget(self, widget):
        if widget in self._geometries:
            widget.move(self._geometries[widget].topLeft())

    def eventFilter(self, obj, event):
        if obj == self.editor and event.type() == QEvent.Enter:
            for w in [self.scrollBar, self.topPanel,
                      self.bottomPanel, self.leftPanel]:
                # w.setVisible(False)
                self.hideWidget(w)
        return QWidget.eventFilter(self, obj, event)

    def dataChanged(self, topLeft, bottomRight):
        if not self._index:
            return
        if topLeft.row() <= self._index.row() <= bottomRight.row():
            self.updateStatusBar()

    def updateStatusBar(self):
        if self._index:
            item = self._index.internalPointer()

        wc = item.data(Outline.wordCount.value)
        goal = item.data(Outline.goal.value)
        pg = item.data(Outline.goalPercentage.value)

        if goal:
            rect = self.lblProgress.geometry()
            rect = QRect(QPoint(0, 0), rect.size())
            self.px = QPixmap(rect.size())
            self.px.fill(Qt.transparent)
            p = QPainter(self.px)
            drawProgress(p, rect, pg, 2)
            p.end()
            self.lblProgress.setPixmap(self.px)
            self.lblWC.setText(self.tr("{} words / {}").format(wc, goal))
        else:
            self.lblProgress.hide()
            self.lblWC.setText(self.tr("{} words").format(wc))

        self.locker.setWordCount(wc)
        # If there's a goal, then we update the locker target's number of word accordingly
        # (also if there is a word count, we deduce it.
        if goal and not self.locker.isLocked():
            if wc and goal - wc > 0:
                self.locker.spnWordTarget.setValue(goal - wc)
            elif not wc:
                self.locker.spnWordTarget.setValue(goal)
Пример #22
0
class Main_Window(QMainWindow):
    def __init__(self):
        super(Main_Window, self).__init__()
        self.mtz_name = ""
        self.pdb_name = ""
        self.fcf_name = ""
        self.cif_name = ""
        self.saved_Data = {}
        self.resolution = 1.8
        self.scaling_factor = 1
        self.scatfact_table = 'electron'
        self.initUI()
        self.home()
        self.show()

    def initUI(self):
        self.setGeometry(300, 300, 600, 400)
        self.setFixedSize(self.size())
        self.setWindowTitle('Plot_Fcalc_Fobs')
        self.setWindowIcon(QIcon('gear-tools.png'))
        self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint
                            | QtCore.Qt.WindowMinimizeButtonHint)

    #Operations about MTZ file
    def load_mtz(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        self.mtz_name, _ = QFileDialog.getOpenFileName(
            self,
            "Choose an MTZ file",
            "",
            "MTZ Files (*.mtz);;All Files (*)",
            options=options)
        self.textbox1.setText(self.mtz_name)

    def mtz_text_editor_enter(self):
        tmp = self.textbox1.text()
        if os.path.isfile(tmp):
            self.mtz_name = tmp
            msgBox = QMessageBox()
            msgBox.setText("Successfully found MTZ file.")
            msgBox.setWindowTitle("MTZ File")
            msgBox.exec_()
        else:
            msgBox = QMessageBox()
            msgBox.setText("File does not exist, please enter another name")
            msgBox.setWindowTitle("File")
            msgBox.exec_()

    def read_mtz(self, f):
        #read an mtz file
        return any_reflection_file(str(f))

    def show_mtz(self):
        try:
            subprocess.call(['viewhkl', str(self.mtz_name)])
        except:
            msgBox = QMessageBox()
            msgBox.setIcon(QMessageBox.Critical)
            msgBox.setText(
                "An Error Has Ocurred While Trying To Show This File!")
            msgBox.setWindowTitle("Error")
            msgBox.exec_()

    #Operations about PDB file
    def load_pdb(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        self.pdb_name, _ = QFileDialog.getOpenFileName(
            self,
            "Choose a PDB file",
            "",
            "PDB Files (*.pdb);;All Files (*)",
            options=options)
        self.textbox2.setText(self.pdb_name)

    def pdb_text_editor_enter(self):
        tmp = self.textbox2.text()
        if os.path.isfile(tmp):
            self.pdb_name = tmp
            msgBox = QMessageBox()
            msgBox.setText("Successfully found PDB file.")
            msgBox.setWindowTitle("PDB File")
        else:
            msgBox = QMessageBox()
            msgBox.setText("File does not exist, please enter another name")
            msgBox.setWindowTitle("File")
            msgBox.exec_()

    def read_pdb(self, f):
        #read a pdb file
        try:
            if isinstance(f, str):
                structures = pdb.input(
                    file_name=f,
                    raise_sorry_if_format_error=True).xray_structure_simple()
            else:
                raise TypeError, 'read_pdb: Cannot deal is type {}'.format(
                    type(f))
        except libtbx.utils.Sorry as e:
            print e
            print "Error parsing pdb file, check if the data tag does not contain any spaces."
            exit()
        return structures

    def show_pdb(self):
        self.F_Window = File_Window(str(self.pdb_name))

    #Operations about FCF file
    def load_fcf(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        self.fcf_name, _ = QFileDialog.getOpenFileName(
            self,
            "Choose an FCF file",
            "",
            "FCF Files (*.fcf);;All Files (*)",
            options=options)
        self.textbox3.setText(self.fcf_name)

    def fcf_text_editor_enter(self):
        tmp = self.textbox3.text()
        if os.path.isfile(tmp):
            self.fcf_name = tmp
            msgBox = QMessageBox()
            msgBox.setText("Successfully found FCF file.")
            msgBox.setWindowTitle("FCF File")
        else:
            msgBox = QMessageBox()
            msgBox.setText("File does not exist, please enter another name")
            msgBox.setWindowTitle("File")
            msgBox.exec_()

    def read_fcf(self, f):
        #read an fcf file
        return any_reflection_file(str(f))

    def show_fcf(self):
        self.F_Window = File_Window(str(self.fcf_name))

    #Operations about CIF file
    def load_cif(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        self.cif_name, _ = QFileDialog.getOpenFileName(
            self,
            "Choose a CIF file",
            "",
            "CIF Files (*.cif);;All Files (*)",
            options=options)
        self.textbox4.setText(self.cif_name)

    def cif_text_editor_enter(self):
        tmp = self.textbox4.text()
        if os.path.isfile(tmp):
            self.cif_name = tmp
            msgBox = QMessageBox()
            msgBox.setText("Successfully found CIF file.")
            msgBox.setWindowTitle("CIF File")
        else:
            msgBox = QMessageBox()
            msgBox.setText("File does not exist, please enter another name")
            msgBox.setWindowTitle("File")
            msgBox.exec_()

    def read_cif(self, f):
        #read a cif file  One cif file can contain multiple structures
        try:
            if isinstance(f, str):
                structures = cif.reader(
                    file_path=f,
                    raise_if_errors=True).build_crystal_structures()
            else:
                raise TypeError, 'read_cif: Cannot deal is type {}'.format(
                    type(f))
        except libtbx.utils.Sorry as e:
            print e
            print "Error parsing cif file, check if the data tag does not contain any spaces."
            exit()
        return structures

    def read_cif_reflections(self, f):
        #read an cif reflection file
        return any_reflection_file(str(f))

    def show_cif(self):
        self.F_Window = File_Window(str(self.cif_name))

    #Set resolution
    def set_resolution(self):
        try:
            self.resolution = float(self.textbox5.text())
            msgBox = QMessageBox()
            msgBox.setText("Successfully set the resolution to {}".format(
                self.resolution))
            msgBox.setWindowTitle("Resolution")
            msgBox.exec_()
        except:
            msgBox = QMessageBox()
            msgBox.setIcon(QMessageBox.Critical)
            msgBox.setText(
                "An Error Has Ocurred while trying to set resolution! Please check if you input the proper number."
            )
            msgBox.setWindowTitle("Error")
            msgBox.exec_()

    def set_wilson_scaling_factor(self):
        try:
            self.scaling_factor = float(self.textbox6.text())
            msgBox = QMessageBox()
            msgBox.setText("Successfully set scaling factor to {}".format(
                self.scaling_factor))
            msgBox.setWindowTitle("Scaling Factor")
            msgBox.exec_()
        except:
            msgBox = QMessageBox()
            msgBox.setIcon(QMessageBox.Critical)
            msgBox.setText(
                "An Error Has Ocurred while trying to set the wilson scaling factor! Please check if you input the proper number."
            )
            msgBox.setWindowTitle("Error")
            msgBox.exec_()

    def save_fobs_fcalc(self):
        try:
            options = QFileDialog.Options()
            options |= QFileDialog.DontUseNativeDialog
            save_name, _ = QFileDialog.getSaveFileName(
                self,
                "Save Fobs Fcalc",
                "",
                "JSON Files (*.json);;All Files (*)",
                options=options)
            with open(save_name, 'w') as fp:
                json.dump(self.saved_Data, fp, indent=4)
        except:
            msgBox = QMessageBox()
            msgBox.setIcon(QMessageBox.Critical)
            msgBox.setText(
                "An Error Has Ocurred while trying to save fobs and fcalc!")
            msgBox.setWindowTitle("Error")
            msgBox.exec_()

    def f_calc_structure_factors(self, structure, **kwargs):
        """Takes cctbx structure and returns f_calc miller array
		Takes an optional options dictionary with keys:
		input:
			**kwargs:
				'd_min': minimum d-spacing for structure factor calculation
				'algorithm': which algorithm to use ('direct', 'fft', 'automatic')
			structure: <cctbx.xray.structure.structure object>
		output:
			f_calc: <cctbx.miller.array object> with calculated structure factors
				in the f_calc.data() function
		
		"""

        dmin = kwargs.get('dmin', 1.0)
        algorithm = kwargs.get('algorithm', "automatic")
        anomalous = kwargs.get('anomalous', False)
        table = kwargs.get('scatfact_table', 'wk1995')
        return_as = kwargs.get('return_as', "series")
        verbose = kwargs.get('verbose', False)

        if dmin <= 0.0:
            raise ValueError, "d-spacing must be greater than zero."

        if algorithm == "automatic":
            if structure.scatterers().size() <= 100:
                algorithm = "direct"
            else:
                algorithm = None

        structure.scattering_type_registry(table=table)

        f_calc_manager = structure.structure_factors(anomalous_flag=anomalous,
                                                     d_min=dmin,
                                                     algorithm=algorithm)
        f_calc = f_calc_manager.f_calc()

        if verbose:
            print "\nScattering table:", structure.scattering_type_registry_params.table
            structure.scattering_type_registry().show()
        print "Minimum d-spacing: %g" % f_calc.d_min()

        if return_as == "miller":
            return f_calc
        elif return_as == "series":
            fcalc = pd.Series(index=f_calc.indices(),
                              data=np.abs(f_calc.data()))
            phase = pd.Series(index=f_calc.indices(),
                              data=np.angle(f_calc.data()))
            return fcalc, phase
        elif return_as == "df":
            dffcal = pd.DataFrame(index=f_calc.index)
            dffcal['fcalc'] = np.abs(f_calc.data())
            dffcal['phase'] = np.angle(f_calc.data())
            return dffcal
        else:
            raise ValueError, "Unknown argument for 'return_as':{}".format(
                return_as)

    def calc_structure_factors(self,
                               structures,
                               dmin=1.0,
                               table='electron',
                               prefix='',
                               verbose=True,
                               **kwargs):
        """Wrapper around f_calc_structure_factors()
		Takes a structure object in which there is only one strcture

		dmin can be a dataframe and it will take the minimum dspacing (as specified by col 'd') or a float
		if combine is specified, function will return a dataframe combined with the given one, otherwise a
		dictionary of dataframes

		prefix is a prefix for the default names fcalc/phases to identify different structures
		"""
        fcalc = self.f_calc_structure_factors(structures,dmin=dmin,scatfact_table=table,\
                return_as="miller",verbose=verbose,**kwargs)

        return fcalc

    def home(self):
        #quit button
        btn1 = QPushButton("Quit", self)
        btn1.clicked.connect(QtCore.QCoreApplication.instance().quit)
        btn1.resize(100, 50)
        btn1.move(490, 340)
        #plot button
        btn2 = QPushButton("Plot", self)
        btn2.clicked.connect(self.plot_Fcalc_Fobs)
        btn2.resize(100, 50)
        btn2.move(380, 340)

        #load mtz file button
        btn3 = QPushButton("Choose MTZ", self)
        btn3.clicked.connect(self.load_mtz)
        btn3.resize(100, 35)
        btn3.move(490, 5)
        #show the mtz file button
        btn3_1 = QPushButton("Show MTZ", self)
        btn3_1.clicked.connect(self.show_mtz)
        btn3_1.resize(100, 35)
        btn3_1.move(385, 5)
        #show the path of the mtz file
        self.textbox1 = QLineEdit(self)
        self.textbox1.resize(375, 35)
        self.textbox1.move(5, 5)
        self.textbox1.setText(self.mtz_name)
        self.textbox1.returnPressed.connect(self.mtz_text_editor_enter)

        #load pdb file button
        btn4 = QPushButton("Choose PDB", self)
        btn4.clicked.connect(self.load_pdb)
        btn4.resize(100, 35)
        btn4.move(490, 45)
        #show the pdb file button
        btn4_1 = QPushButton("Show PDB", self)
        btn4_1.clicked.connect(self.show_pdb)
        btn4_1.resize(100, 35)
        btn4_1.move(385, 45)
        #show the path of the pdb file
        self.textbox2 = QLineEdit(self)
        self.textbox2.resize(375, 35)
        self.textbox2.move(5, 45)
        self.textbox2.setText(self.pdb_name)
        self.textbox2.returnPressed.connect(self.pdb_text_editor_enter)

        #load fcf file button
        btn5 = QPushButton("Choose FCF", self)
        btn5.clicked.connect(self.load_fcf)
        btn5.resize(100, 35)
        btn5.move(490, 85)
        #show the fcf file button
        btn5_1 = QPushButton("Show FCF", self)
        btn5_1.clicked.connect(self.show_fcf)
        btn5_1.resize(100, 35)
        btn5_1.move(385, 85)
        #show the path of the fcf file
        self.textbox3 = QLineEdit(self)
        self.textbox3.resize(375, 35)
        self.textbox3.move(5, 85)
        self.textbox3.setText(self.fcf_name)
        self.textbox3.returnPressed.connect(self.fcf_text_editor_enter)

        #load cif file button
        btn6 = QPushButton("Choose CIF", self)
        btn6.clicked.connect(self.load_cif)
        btn6.resize(100, 35)
        btn6.move(490, 125)
        #show the cif file button
        btn6_1 = QPushButton("Show CIF", self)
        btn6_1.clicked.connect(self.show_cif)
        btn6_1.resize(100, 35)
        btn6_1.move(385, 125)
        #show the path of the cif file
        self.textbox4 = QLineEdit(self)
        self.textbox4.resize(375, 35)
        self.textbox4.move(5, 125)
        self.textbox4.setText(self.cif_name)
        self.textbox4.returnPressed.connect(self.cif_text_editor_enter)

        #checkbox to choose the way to calculate Fcalc
        #1 get Fcalc by calculating the structure factor from pdb file
        self.cb1 = QCheckBox('Calc Fcalc from PDB', self)
        self.cb1.move(10, 170)
        self.cb1.resize(145, 20)
        self.cb1.setAutoExclusive(1)
        #2 get Fcalc by calculating the structure factor from cif file
        self.cb2 = QCheckBox('Calc Fcalc from CIF', self)
        self.cb2.move(160, 170)
        self.cb2.resize(145, 20)
        self.cb2.setAutoExclusive(1)
        #3 get Fcalc from mtz file
        self.cb3 = QCheckBox('Get Fcalc from MTZ', self)
        self.cb3.move(305, 170)
        self.cb3.resize(145, 20)
        self.cb3.toggle()
        self.cb3.setAutoExclusive(1)
        #4 get Fcalc from fcf file
        self.cb4 = QCheckBox('Get Fcalc from FCF', self)
        self.cb4.move(455, 170)
        self.cb4.resize(145, 20)
        self.cb4.setAutoExclusive(1)
        #set a checkbox group to group the options for fobs
        self.cb_group_fcalc = QButtonGroup(self)
        self.cb_group_fcalc.addButton(self.cb1)
        self.cb_group_fcalc.addButton(self.cb2)
        self.cb_group_fcalc.addButton(self.cb3)
        self.cb_group_fcalc.addButton(self.cb4)
        self.cb_group_fcalc.setExclusive(1)

        #checkbox to choose the way to get Fobs
        #1 get Fobs from mtz file
        self.cb1_1 = QCheckBox('Get Fobs from MTZ', self)
        self.cb1_1.move(10, 200)
        self.cb1_1.resize(145, 20)
        self.cb1_1.toggle()
        #2 get Fobs from fcf file
        self.cb2_1 = QCheckBox('Get Fobs from FCF', self)
        self.cb2_1.move(160, 200)
        self.cb2_1.resize(145, 20)
        #3 get Fobs from CIF file
        self.cb3_1 = QCheckBox('Get Fobs from CIF', self)
        self.cb3_1.move(305, 200)
        self.cb3_1.resize(145, 20)
        #set a checkbox group to group the options for fobs
        self.cb_group_fobs = QButtonGroup(self)
        self.cb_group_fobs.addButton(self.cb1_1)
        self.cb_group_fobs.addButton(self.cb2_1)
        self.cb_group_fobs.addButton(self.cb3_1)
        self.cb_group_fobs.setExclusive(1)

        #input textbox to set resolution
        self.textbox5 = QLineEdit(self)
        self.textbox5.move(10, 230)
        self.textbox5.resize(60, 35)
        self.textbox5.setValidator(
            QDoubleValidator(self, bottom=0, top=500, decimals=2))
        self.textbox5.setText(str(self.resolution))
        btn7 = QPushButton("Set Resolution", self)
        btn7.move(75, 230)
        btn7.resize(100, 35)
        btn7.clicked.connect(self.set_resolution)

        #Overall wilson plot scaling factor
        self.textbox6 = QLineEdit(self)
        self.textbox6.move(10, 270)
        self.textbox6.resize(60, 36)
        self.textbox6.setValidator(
            QDoubleValidator(self, bottom=-500, top=500, decimals=4))
        self.textbox6.setText(str(self.scaling_factor))
        btn8 = QPushButton("Set Wilson\nFactor", self)
        btn8.move(75, 270)
        btn8.resize(100, 36)
        btn8.clicked.connect(self.set_wilson_scaling_factor)

        #Save the Fobs and Fcalc as text file
        btn9 = QPushButton("Save", self)
        btn9.move(270, 340)
        btn9.resize(100, 50)
        btn9.clicked.connect(self.save_fobs_fcalc)

        #Combobox to select scattering factors
        self.combo = QComboBox(self)
        self.combo.addItem('electron', 'electron')
        self.combo.addItem('xray-wk1995', 'wk1995')
        self.combo.addItem('xray-it1992', 'it1992')
        self.combo.move(185, 230)
        self.combo.resize(150, 35)

    def plot_Fcalc_Fobs(self):
        self.resolution = float(self.textbox5.text())
        self.scaling_factor = float(self.textbox6.text())
        #try:
        if self.cb1_1.checkState():  #1 get Fobs from mtz file
            mtz_file = self.read_mtz(self.textbox1.text())
            Fobs = mtz_file.as_miller_arrays()[2]
            Fobs_ds = Fobs.d_spacings().data()
            Fobs_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fobs.indices()),\
                 data=np.array([Fobs.data()*self.scaling_factor,Fobs_ds]).transpose())
        elif self.cb2_1.checkState():  #2 get Fobs from fcf file
            fcf_file = self.read_fcf(self.textbox3.text())
            model = dict(fcf_file.file_content().model().items()[0][1])
            if model['_shelx_refln_list_code'] is '6':
                Fobs = fcf_file.as_miller_arrays(
                )[1]  #This Fobs is Fobs^2 but in order to be more convinient for me just call it Fobs
                Fobs_ds = Fobs.d_spacings().data()
                Fobs_data = np.array(Fobs.data())
                Fobs_data[Fobs_data < 0] = 0
                Fobs_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fobs.indices()),\
                    data=np.array([np.sqrt(Fobs_data)*self.scaling_factor,Fobs_ds]).transpose())
            elif model['_shelx_refln_list_code'] is '4':
                Fobs = fcf_file.as_miller_arrays(
                )[1]  #This Fobs is Fobs^2 but in order to be more convinient for me just call it Fobs
                Fobs_ds = Fobs.d_spacings().data()
                Fobs_data = np.array(Fobs.data())
                Fobs_data[Fobs_data < 0] = 0
                Fobs_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fobs.indices()),\
                    data=np.array([np.sqrt(Fobs_data)*self.scaling_factor,Fobs_ds]).transpose())
            elif model['_shelx_refln_list_code'] is '3':
                Fobs = fcf_file.as_miller_arrays()[1]
                Fobs_ds = Fobs.d_spacings().data()
                Fobs_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fobs.indices()),\
                    data=np.array([Fobs.data()*self.scaling_factor,Fobs_ds]).transpose())
        elif self.cb3_1.checkState():
            cif_file = self.read_cif_reflections(str(self.textbox4.text()))
            Fobs = cif_file.as_miller_arrays()[0]
            if type(Fobs.data()[0]) is complex:
                Fobs = cif_file.as_miller_arrays()[1]
            Fobs_ds = Fobs.d_spacings().data()
            Fobs_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fobs.indices()),\
                 data=np.array([Fobs.data()*self.scaling_factor,Fobs_ds]).transpose())

    #-------------------------------------------------------------------------------
        if self.cb1.checkState(
        ):  #1 get Fcalc by calculating the structure factor from pdb file
            self.scatfact_table = str(self.combo.currentData())
            pdb_structure = self.read_pdb(str(self.textbox2.text()))
            Fcalc = self.calc_structure_factors(pdb_structure,
                                                dmin=self.resolution,
                                                table=self.scatfact_table)
            Fcalc_data = Fcalc.data()
            Fcalc_indices = Fcalc.indices()
            Fcalc_ds = Fcalc.d_spacings().data()
            Fcalc_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fcalc_indices),\
                 data=np.array([np.abs(Fcalc_data),Fcalc_ds]).transpose())

        elif self.cb2.checkState(
        ):  #2 get Fcalc by calculating the structure factor from cif file
            self.scatfact_table = str(self.combo.currentData())
            cif_structures = self.read_cif(str(self.textbox4.text()))
            for name, cif_structure in cif_structures.items():
                Fcalc = self.calc_structure_factors(cif_structure,
                                                    dmin=self.resolution,
                                                    table=self.scatfact_table)
                break  #abandon any more structures in the cif file, if there is any, only read the first one
            Fcalc_P1 = Fcalc.expand_to_p1()
            Fcalc_data = Fcalc_P1.data()
            Fcalc_indices = Fcalc_P1.indices()
            Fcalc_ds = Fcalc_P1.d_spacings().data()
            Fcalc_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fcalc_indices),\
                 data=np.array([np.abs(Fcalc_data),Fcalc_ds]).transpose())

        elif self.cb3.checkState():  #3 get Fcalc from mtz file
            if not self.cb1_1.checkState():
                mtz_file = self.read_mtz(self.textbox1.text())
            Fcalc = mtz_file.as_miller_arrays()[3]
            Fcalc_ds = Fcalc.d_spacings().data()
            Fcalc_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fcalc.indices()),\
                 data=np.array([np.abs(Fcalc.data()),Fcalc_ds]).transpose())

        elif self.cb4.checkState():  #4 get Fcalc from fcf file
            if not self.cb2_1.checkState():
                fcf_file = self.read_fcf(self.textbox3.text())
                model = dict(fcf_file.file_content().model().items()[0][1])

            if model['_shelx_refln_list_code'] is '6':
                Fcalc = fcf_file.as_miller_arrays()[0]
                Fcalc_ds = Fcalc.d_spacings().data()
                Fcalc_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fcalc.indices()),\
                    data=np.array([np.abs(Fcalc.data()),Fcalc_ds]).transpose())
            elif model['_shelx_refln_list_code'] is '4':
                Fcalc = fcf_file.as_miller_arrays()[0]
                Fcalc_ds = Fcalc.d_spacings().data()
                Fcalc_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fcalc.indices()),\
                    data=np.array([np.sqrt(Fcalc.data()),Fcalc_ds]).transpose())
            elif model['_shelx_refln_list_code'] is '3':
                Fcalc = fcf_file.as_miller_arrays()[0]
                Fcalc_ds = Fcalc.d_spacings().data()
                Fcalc_DF = pd.DataFrame(index=pd.MultiIndex.from_tuples(Fcalc.indices()),\
                    data=np.array([np.abs(Fcalc.data()),Fcalc_ds]).transpose())

    #-------------------------------------------------------------------------------

        merged_DF = Fobs_DF.merge(Fcalc_DF,
                                  how='inner',
                                  left_index=True,
                                  right_index=True,
                                  suffixes=('_Fobs', '_Fcalc'))
        merged_fobs = merged_DF['0_Fobs'].values.tolist()
        merged_fcalc = merged_DF['0_Fcalc'].values.tolist()
        fig, ax = plt.subplots()
        x2, y2 = pd.Series(merged_fobs,
                           name="F_obs"), pd.Series(merged_fcalc,
                                                    name="F_model")
        self.saved_Data['Fobs'] = merged_fobs
        self.saved_Data['Fcalc'] = merged_fcalc
        index = merged_DF.index.tolist()
        ds = merged_DF['1_Fobs'].values.tolist()
        af = AnnoteFinder(merged_fobs, merged_fcalc, zip(index, ds), ax=ax)
        fig.canvas.mpl_connect('button_press_event', af)
        self.fit_window = FitWindow(ax=ax, data=zip(merged_fobs, merged_fcalc))
        sns.regplot(x=x2, y=y2, x_ci=None, ci=None, marker='+', ax=ax)
        plt.show()
Пример #23
0
class SubwindowBrowserSources(QWidget):
    """Show connections settings sub window."""
    def createWindow(self, mainWindow, tab=''):
        """Create window."""
        try:
            parent = None
            super().__init__(parent)
            # self.setWindowFlags(Qt.WindowStaysOnTopHint)

            self.setWindowIcon(
                QIcon(hwctool.settings.getResFile('browser.png')))
            self.setWindowModality(Qt.ApplicationModal)
            self.mainWindow = mainWindow
            self.passEvent = False
            self.controller = mainWindow.controller
            self.__dataChanged = False

            self.createButtonGroup()
            self.createTabs(tab)

            mainLayout = QVBoxLayout()

            mainLayout.addWidget(self.tabs)
            mainLayout.addLayout(self.buttonGroup)

            self.setLayout(mainLayout)

            self.resize(
                QSize(int(mainWindow.size().width() * 0.8),
                      self.sizeHint().height()))
            relativeChange = QPoint(mainWindow.size().width() // 2,
                                    mainWindow.size().height() // 3) -\
                QPoint(self.size().width() // 2,
                       self.size().height() // 3)
            self.move(mainWindow.pos() + relativeChange)

            self.setWindowTitle(_("Browser Sources"))

            self.controller.websocketThread.unregister_hotkeys(force=True)

        except Exception as e:
            module_logger.exception("message")

    def createTabs(self, tab):
        """Create tabs."""
        self.tabs = QTabWidget()

        self.createFormGroupIntro()

        # Add tabs
        self.tabs.addTab(self.formGroupIntro, _("Intros"))
        table = dict()
        table['intro'] = 0
        self.tabs.setCurrentIndex(table.get(tab, -1))

    def addHotkey(self, ident, label):
        element = HotkeyLayout(
            self, ident, label,
            hwctool.settings.config.parser.get("Intros", ident))
        self.hotkeys[ident] = element
        return element

    def connectHotkeys(self):
        for ident, key in self.hotkeys.items():
            if ident == 'hotkey_debug':
                for ident2, key2 in self.hotkeys.items():
                    if ident == ident2:
                        continue
                    key.modified.connect(key2.check_dublicate)
            key.modified.connect(self.hotkeyChanged)

    def hotkeyChanged(self, key, ident):
        self.changed()

        if (ident == 'hotkey_player1' and self.cb_single_hotkey.isChecked()):
            self.hotkeys['hotkey_player2'].setData(
                self.hotkeys['hotkey_player1'].getKey())

        if not key:
            return

        if ((ident == 'hotkey_player1'
             and key == self.hotkeys['hotkey_player2'].getKey()['name']) or
            (ident == 'hotkey_player2'
             and key == self.hotkeys['hotkey_player1'].getKey()['name'])):
            self.cb_single_hotkey.setChecked(True)

        if (ident in ['hotkey_player1', 'hotkey_player2']
                and key == self.hotkeys['hotkey_debug'].getKey()['name']):
            self.hotkeys['hotkey_debug'].clear()

    def singleHotkeyChanged(self):
        checked = self.cb_single_hotkey.isChecked()
        self.hotkeys['hotkey_player2'].setDisabled(checked)
        if checked:
            self.hotkeys['hotkey_player2'].setData(
                self.hotkeys['hotkey_player1'].getKey())
        elif (self.hotkeys['hotkey_player1'].getKey() ==
              self.hotkeys['hotkey_player2'].getKey()):
            self.hotkeys['hotkey_player2'].clear()

    def createFormGroupIntro(self):
        """Create forms for websocket connection to intro."""
        self.formGroupIntro = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Style"))
        layout = QHBoxLayout()
        styleqb = StyleComboBox(
            hwctool.settings.casting_html_dir + "/src/css/intro", "intro")
        styleqb.connect2WS(self.controller, 'intro')
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            hwctool.settings.casting_html_dir + "/intro.html"))
        layout.addWidget(styleqb, 2)
        layout.addWidget(button, 1)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        self.hotkeyBox = QGroupBox(_("Hotkeys"))
        layout = QVBoxLayout()
        try:
            keyboard.unhook_all()
        except AttributeError:
            pass

        self.cb_single_hotkey = QCheckBox(
            _("Use a single hotkey for both players"))
        self.cb_single_hotkey.stateChanged.connect(self.singleHotkeyChanged)
        layout.addWidget(self.cb_single_hotkey)

        self.hotkeys = dict()
        layout.addLayout(self.addHotkey("hotkey_player1", _("Player 1")))
        layout.addLayout(self.addHotkey("hotkey_player2", _("Player 2")))
        layout.addLayout(self.addHotkey("hotkey_debug", _("Debug")))

        self.cb_single_hotkey.setChecked(self.hotkeys['hotkey_player1'].getKey(
        ) == self.hotkeys['hotkey_player2'].getKey())
        self.connectHotkeys()
        label = QLabel(
            _("Player 1 is always the player your observer"
              " camera is centered on at start of a game."))
        layout.addWidget(label)
        self.hotkeyBox.setLayout(layout)
        mainLayout.addWidget(self.hotkeyBox)

        self.introBox = QGroupBox(_("Animation"))
        layout = QFormLayout()
        self.cb_animation = QComboBox()
        animation = hwctool.settings.config.parser.get("Intros", "animation")
        currentIdx = 0
        idx = 0
        options = dict()
        options['Fly-In'] = _("Fly-In")
        options['Slide'] = _("Slide")
        options['Fanfare'] = _("Fanfare")
        for key, item in options.items():
            self.cb_animation.addItem(item, key)
            if (key == animation):
                currentIdx = idx
            idx += 1
        self.cb_animation.setCurrentIndex(currentIdx)
        self.cb_animation.currentIndexChanged.connect(self.changed)
        label = QLabel(_("Animation:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_animation)
        self.sb_displaytime = QDoubleSpinBox()
        self.sb_displaytime.setRange(0, 10)
        self.sb_displaytime.setDecimals(1)
        self.sb_displaytime.setValue(
            hwctool.settings.config.parser.getfloat("Intros", "display_time"))
        self.sb_displaytime.setSuffix(" " + _("Seconds"))
        self.sb_displaytime.valueChanged.connect(self.changed)
        layout.addRow(QLabel(_("Display Duration:") + " "),
                      self.sb_displaytime)
        self.sl_sound = QSlider(Qt.Horizontal)
        self.sl_sound.setMinimum(0)
        self.sl_sound.setMaximum(20)
        self.sl_sound.setValue(
            hwctool.settings.config.parser.getint("Intros", "sound_volume"))
        self.sl_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_sound.setTickInterval(1)
        self.sl_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(_("Sound Volume:") + " "), self.sl_sound)
        self.introBox.setLayout(layout)
        mainLayout.addWidget(self.introBox)

        self.ttsBox = QGroupBox(_("Text-to-Speech"))
        layout = QFormLayout()

        self.cb_tts_active = QCheckBox()
        self.cb_tts_active.setChecked(
            hwctool.settings.config.parser.getboolean("Intros", "tts_active"))
        self.cb_tts_active.stateChanged.connect(self.changed)
        label = QLabel(_("Activate Text-to-Speech:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_tts_active)

        self.icons = {}
        self.icons['MALE'] = QIcon(hwctool.settings.getResFile('male.png'))
        self.icons['FEMALE'] = QIcon(hwctool.settings.getResFile('female.png'))
        self.cb_tts_voice = QComboBox()

        currentIdx = 0
        idx = 0
        tts_voices = self.controller.tts.getVoices()
        tts_voice = hwctool.settings.config.parser.get("Intros", "tts_voice")
        for voice in tts_voices:
            self.cb_tts_voice.addItem(self.icons[voice['ssmlGender']],
                                      '   ' + voice['name'], voice['name'])
            if (voice['name'] == tts_voice):
                currentIdx = idx
            idx += 1
        self.cb_tts_voice.setCurrentIndex(currentIdx)
        self.cb_tts_voice.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(_("Voice:") + " "), self.cb_tts_voice)
        self.ttsBox.setStyleSheet("QComboBox { combobox-popup: 0; }")
        self.ttsBox.setLayout(layout)
        mainLayout.addWidget(self.ttsBox)

        self.sb_tts_pitch = QDoubleSpinBox()
        self.sb_tts_pitch.setRange(-20, 20)
        self.sb_tts_pitch.setDecimals(2)
        self.sb_tts_pitch.setValue(
            hwctool.settings.config.parser.getfloat("Intros", "tts_pitch"))
        self.sb_tts_pitch.valueChanged.connect(self.changed)
        layout.addRow(QLabel(_("Pitch:") + " "), self.sb_tts_pitch)

        self.sb_tts_rate = QDoubleSpinBox()
        self.sb_tts_rate.setRange(0.25, 4.00)
        self.sb_tts_rate.setSingleStep(0.1)
        self.sb_tts_rate.setDecimals(2)
        self.sb_tts_rate.setValue(
            hwctool.settings.config.parser.getfloat("Intros", "tts_rate"))
        self.sb_tts_rate.valueChanged.connect(self.changed)
        layout.addRow(QLabel(_("Rate:") + " "), self.sb_tts_rate)

        self.cb_tts_scope = QComboBox()
        self.cb_tts_scope.setMaximumWidth(400)
        scope = hwctool.settings.config.parser.get("Intros", "tts_scope")
        currentIdx = 0
        idx = 0
        options = self.controller.tts.getOptions()
        for key, item in options.items():
            self.cb_tts_scope.addItem(item['desc'], key)
            if (key == scope):
                currentIdx = idx
            idx += 1
        self.cb_tts_scope.setCurrentIndex(currentIdx)
        self.cb_tts_scope.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(_("Line:") + " "), self.cb_tts_scope)

        self.sl_tts_sound = QSlider(Qt.Horizontal)
        self.sl_tts_sound.setMinimum(0)
        self.sl_tts_sound.setMaximum(20)
        self.sl_tts_sound.setValue(
            hwctool.settings.config.parser.getint("Intros", "tts_volume"))
        self.sl_tts_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_tts_sound.setTickInterval(1)
        self.sl_tts_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(_("Sound Volume:") + " "), self.sl_tts_sound)

        text = _(
            "Text-to-Speech provided by Google-Cloud is paid for "
            "by StarCraft Casting Tool Patrons. To keep this service up "
            "consider becoming a <a href='{patreon}'>Patron</a> yourself. "
            "You can test all voices at {tts}.")

        patreon = 'https://www.patreon.com/StarCraftCastingTool'

        url = 'https://cloud.google.com/text-to-speech/'
        tts = "<a href='{}'>cloud.google.com/text-to-speech</a>"
        tts = tts.format(url)

        label = QLabel(text.format(patreon=patreon, tts=tts))
        label.setAlignment(Qt.AlignJustify)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setMargin(5)
        layout.addRow(label)

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupIntro.setLayout(mainLayout)

    def createButtonGroup(self):
        """Create buttons."""
        try:
            layout = QHBoxLayout()

            layout.addWidget(QLabel(""))

            buttonCancel = QPushButton(_('Cancel'))
            buttonCancel.clicked.connect(self.closeWindow)
            layout.addWidget(buttonCancel)

            buttonSave = QPushButton(_('&Save && Close'))
            buttonSave.setToolTip(_("Shortcut: {}").format("Ctrl+S"))
            self.shortcut = QShortcut(QKeySequence("Ctrl+S"), self)
            self.shortcut.setAutoRepeat(False)
            self.shortcut.activated.connect(self.saveCloseWindow)
            buttonSave.clicked.connect(self.saveCloseWindow)
            layout.addWidget(buttonSave)

            self.buttonGroup = layout
        except Exception as e:
            module_logger.exception("message")

    def changed(self, *values):
        """Handle changed data."""
        self.__dataChanged = True

    def saveData(self):
        """Save the data to config."""
        if (self.__dataChanged):
            self.saveWebsocketdata()
            self.__dataChanged = False
            # self.controller.refreshButtonStatus()

    def saveWebsocketdata(self):
        """Save Websocket data."""
        for ident, key in self.hotkeys.items():
            string = hwctool.settings.config.dumpHotkey(key.getKey())
            hwctool.settings.config.parser.set("Intros", ident, string)
        hwctool.settings.config.parser.set("Intros", "display_time",
                                           str(self.sb_displaytime.value()))
        hwctool.settings.config.parser.set("Intros", "sound_volume",
                                           str(self.sl_sound.value()))
        hwctool.settings.config.parser.set(
            "Intros", "animation",
            self.cb_animation.currentData().strip())
        hwctool.settings.config.parser.set(
            "Intros", "tts_voice",
            self.cb_tts_voice.currentData().strip())
        hwctool.settings.config.parser.set(
            "Intros", "tts_scope",
            self.cb_tts_scope.currentData().strip())
        hwctool.settings.config.parser.set("Intros", "tts_active",
                                           str(self.cb_tts_active.isChecked()))
        hwctool.settings.config.parser.set("Intros", "tts_volume",
                                           str(self.sl_tts_sound.value()))
        hwctool.settings.config.parser.set("Intros", "tts_pitch",
                                           str(self.sb_tts_pitch.value()))
        hwctool.settings.config.parser.set("Intros", "tts_rate",
                                           str(self.sb_tts_rate.value()))

    def openHTML(self, file):
        """Open file in browser."""
        self.controller.openURL(hwctool.settings.getAbsPath(file))

    def saveCloseWindow(self):
        """Save and close window."""
        self.saveData()
        self.closeWindow()

    def closeWindow(self):
        """Close window without save."""
        self.passEvent = True
        self.close()

    def closeEvent(self, event):
        """Handle close event."""
        try:
            if (not self.__dataChanged):
                self.controller.updateHotkeys()
                event.accept()
                return
            if (not self.passEvent):
                if (self.isMinimized()):
                    self.showNormal()
                buttonReply = QMessageBox.question(
                    self, _('Save data?'), _("Do you want to save the data?"),
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                if buttonReply == QMessageBox.Yes:
                    self.saveData()
            self.controller.updateHotkeys()
            event.accept()
        except Exception as e:
            module_logger.exception("message")
Пример #24
0
class SettingsWindow(QDialog):
    """
    Class describing the application settings window.
    """
    def __init__(self, parent: QWidget = None):
        super().__init__(parent)

        # window settings
        self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
        self.setWindowTitle(self.tr("Settings window"))

        # language
        self.group_box_lang = QGroupBox(self.tr("Language"))
        self.form_layout_lang = QFormLayout(self.group_box_lang)

        self.label_app_lang = QLabel(self.tr("Application"))
        self.form_layout_lang.setWidget(0, QFormLayout.LabelRole, self.label_app_lang)
        self.combo_box_app_lang = QComboBox()
        self.form_layout_lang.setWidget(0, QFormLayout.FieldRole, self.combo_box_app_lang)

        self.label_sch_lang = QLabel(self.tr("Schedule"))
        self.form_layout_lang.setWidget(1, QFormLayout.LabelRole, self.label_sch_lang)
        self.combo_box_sch_lang = QComboBox()
        self.form_layout_lang.setWidget(1, QFormLayout.FieldRole, self.combo_box_sch_lang)

        languages = [("English", "en_US"), ("Русский", "ru_RU")]

        for lang_name, lang_code in languages:
            self.combo_box_app_lang.addItem(lang_name, lang_code)
            self.combo_box_sch_lang.addItem(lang_name, lang_code)

            if Settings.ApplicationLang == lang_code:
                self.combo_box_app_lang.setCurrentText(lang_name)

            if Settings.ScheduleLang == lang_code:
                self.combo_box_sch_lang.setCurrentText(lang_name)

        # schedule
        self.group_box_schedule = QGroupBox(self.tr("Schedule"))
        self.form_layout_schedule = QFormLayout(self.group_box_schedule)

        self.label_short_name = QLabel(self.tr("Short name"))
        self.form_layout_schedule.setWidget(0, QFormLayout.LabelRole, self.label_short_name)
        self.check_box_short_name = QCheckBox()
        self.form_layout_schedule.setWidget(0, QFormLayout.FieldRole, self.check_box_short_name)

        self.check_box_short_name.setChecked(Settings.ShortName)

        # navigate
        self.layout_navigate = QHBoxLayout()
        self.layout_navigate.addStretch(1)

        self.push_button_ok = QPushButton(self.tr("OK"))
        self.layout_navigate.addWidget(self.push_button_ok)

        self.push_button_apply = QPushButton(self.tr("Apply"))
        self.layout_navigate.addWidget(self.push_button_apply)

        self.push_button_cancel = QPushButton(self.tr("Cancel"))
        self.layout_navigate.addWidget(self.push_button_cancel)

        # layout setup
        self.layout_main = QVBoxLayout()

        self.layout_main.addWidget(self.group_box_lang)
        self.layout_main.addWidget(self.group_box_schedule)
        self.layout_main.addLayout(self.layout_navigate)

        self.setLayout(self.layout_main)

        # connection
        self.combo_box_app_lang.currentTextChanged.connect(self.application_lang_changed)
        self.combo_box_sch_lang.currentTextChanged.connect(self.schedule_lang_changed)

        self.check_box_short_name.clicked.connect(self.short_name_checked)

        self.push_button_ok.clicked.connect(self.close)
        self.push_button_apply.clicked.connect(self.close)
        self.push_button_cancel.clicked.connect(self.close)

    def changeEvent(self, event: QEvent) -> None:
        if event.type() == QEvent.LanguageChange:
            self.setWindowTitle(self.tr("Settings window"))

            self.group_box_lang.setTitle(self.tr("Language"))
            self.label_app_lang.setText(self.tr("Application"))
            self.label_sch_lang.setText(self.tr("Schedule"))

            self.push_button_ok.setText(self.tr("OK"))
            self.push_button_apply.setText(self.tr("Apply"))
            self.push_button_cancel.setText(self.tr("Cancel"))
        else:
            super().changeEvent(event)

    def application_lang_changed(self) -> None:
        """
        Method to change the application language.
        """
        Settings.ApplicationLang = self.combo_box_app_lang.currentData()

        translator = QTranslator()
        translator.load(":/translations/application_" +
                        Settings.ApplicationLang)

        qApp.removeTranslator(Settings.ApplicationTranslator)
        qApp.installTranslator(translator)
        Settings.ApplicationTranslator = translator

    def schedule_lang_changed(self) -> None:
        """
        Method to change the language of the schedule.
        """
        Settings.ScheduleLang = self.combo_box_sch_lang.currentData()

        translator = QTranslator()
        translator.load(":/translations/schedule_" +
                        Settings.ScheduleLang)

        qApp.removeTranslator(Settings.ScheduleTranslator)
        qApp.installTranslator(translator)
        Settings.ScheduleTranslator = translator

    def short_name_checked(self) -> None:
        """
        Method to change the display mode of names.
        """
        Settings.ShortName = int(self.check_box_short_name.isChecked())
Пример #25
0
class MetToolsDownloadManager(QWidget):
    def __init__(self, iface) -> None:
        super().__init__()

        self.iface = iface
        self.options = get_options()
        self.msg_bar = MessageBar(iface)

        vbox = QVBoxLayout()
        self.setLayout(vbox)

        hbox = QHBoxLayout()
        vbox.addLayout(hbox)

        hbox.addWidget(QLabel('Dataset: '))
        self.cbox_dataset = QComboBox()
        self.cbox_dataset.addItem('-')
        for index, (dataset_name,
                    dataset_label) in enumerate(met_datasets.items()):
            self.cbox_dataset.addItem(dataset_name, dataset_name)
            self.cbox_dataset.setItemData(index + 1, dataset_label,
                                          Qt.ToolTipRole)
        self.cbox_dataset.currentIndexChanged.connect(self.on_dataset_changed)
        hbox.addWidget(self.cbox_dataset)

        hbox_product_name = QHBoxLayout()
        vbox.addLayout(hbox_product_name)
        hbox_product_name.addWidget(QLabel('Product: '))
        self.cbox_product = QComboBox()
        self.cbox_product.currentIndexChanged.connect(self.on_product_changed)
        hbox_product_name.addWidget(self.cbox_product)

        hbox_start_datetime = QHBoxLayout()
        vbox.addLayout(hbox_start_datetime)
        self.dedit_start_date = QDateTimeEdit()
        self.dedit_start_date.setCalendarPopup(True)
        hbox_start_datetime.addWidget(QLabel('Start: '))
        hbox_start_datetime.addWidget(self.dedit_start_date)

        hbox_end_datetime = QHBoxLayout()
        vbox.addLayout(hbox_end_datetime)
        self.dedit_end_date = QDateTimeEdit()
        self.dedit_end_date.setCalendarPopup(True)
        hbox_end_datetime.addWidget(QLabel('End: '))
        hbox_end_datetime.addWidget(self.dedit_end_date)

        gbox_extent = QGroupBox('Extent')
        vbox.addWidget(gbox_extent)
        vbox_extent = QVBoxLayout()
        gbox_extent.setLayout(vbox_extent)

        hbox_extent = QHBoxLayout()
        vbox_extent.addLayout(hbox_extent)
        self.radio_global = QRadioButton('Global')
        self.radio_global.toggled.connect(self.on_extent_radio_button_clicked)
        hbox_extent.addWidget(self.radio_global)
        self.radio_subset = QRadioButton('Subset')
        self.radio_subset.toggled.connect(self.on_extent_radio_button_clicked)
        hbox_extent.addWidget(self.radio_subset)

        self.widget_extent = QWidget()
        vbox_extent.addWidget(self.widget_extent)
        grid_extent = QGridLayout()
        self.widget_extent.setLayout(grid_extent)
        self.widget_extent.hide()
        self.top = add_grid_lineedit(grid_extent,
                                     0,
                                     'North Latitude',
                                     LAT_VALIDATOR,
                                     '°',
                                     required=True)
        self.right = add_grid_lineedit(grid_extent,
                                       1,
                                       'East Longitude',
                                       LON_VALIDATOR,
                                       '°',
                                       required=True)
        self.left = add_grid_lineedit(grid_extent,
                                      2,
                                      'West Longitude',
                                      LON_VALIDATOR,
                                      '°',
                                      required=True)
        self.bottom = add_grid_lineedit(grid_extent,
                                        3,
                                        'South Latitude',
                                        LAT_VALIDATOR,
                                        '°',
                                        required=True)
        self.extent_from_active_layer = QPushButton('Set from Active Layer')
        grid_extent.addWidget(self.extent_from_active_layer, 4, 1)
        self.extent_from_active_layer.clicked.connect(
            self.on_extent_from_active_layer_button_clicked)
        self.radio_global.setChecked(True)

        self.tree = QListWidget()
        vbox_tree = QVBoxLayout()
        vbox.addLayout(vbox_tree)
        vbox_tree.addWidget(self.tree)

        self.btn_download = QPushButton('Download')
        self.btn_download.clicked.connect(self.on_download_button_clicked)
        vbox.addWidget(self.btn_download)

        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, PROGRESS_BAR_MAX)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.hide()
        vbox.addWidget(self.progress_bar)

    def on_dataset_changed(self, index: int):
        self.cbox_product.clear()
        dataset_name = self.cbox_dataset.currentData()
        if dataset_name is None:
            return
        auth = (self.options.rda_username, self.options.rda_password)
        self.products = get_met_products(dataset_name, auth)
        for product in self.products.keys():
            self.cbox_product.addItem(product, product)

    def on_product_changed(self, index: int):
        if index == -1:
            return

        self.tree.clear()
        product_name = self.cbox_product.currentData()
        current_avail_vars = self.products[product_name]
        dates = []
        for name in current_avail_vars.keys():
            item = QListWidgetItem(current_avail_vars[name]['label'])
            item.setData(Qt.UserRole, name)
            item.setCheckState(Qt.Checked)
            self.tree.addItem(item)
            dates.append(current_avail_vars[name]['start_date'])
            dates.append(current_avail_vars[name]['end_date'])
        date_min = min(dates)
        date_max = max(dates)

        for dt_input in [self.dedit_start_date, self.dedit_end_date]:
            dt_input.setDateTimeRange(
                QDateTime(QDate(date_min.year, date_min.month, date_min.day),
                          QTime(date_min.hour, date_min.minute)),
                QDateTime(QDate(date_max.year, date_max.month, date_max.day),
                          QTime(date_max.hour, date_max.minute)))

        min_dt = self.dedit_start_date.minimumDateTime()
        max_dt = self.dedit_start_date.maximumDateTime()
        self.dedit_start_date.setDateTime(min_dt)
        self.dedit_end_date.setDateTime(max_dt)

    def on_download_button_clicked(self):
        param_names = []
        for index in range(self.tree.count()):
            item = self.tree.item(index)
            if item.checkState() == Qt.Checked:
                param_name = item.data(Qt.UserRole)
                param_names.append(param_name)

        dataset_name = self.cbox_dataset.currentData()
        product_name = self.cbox_product.currentData()
        start_date = self.dedit_start_date.dateTime().toPyDateTime()
        end_date = self.dedit_end_date.dateTime().toPyDateTime()
        if dataset_name is None or product_name is None:
            raise UserError('Dataset/Product not selected')

        args = [
            self.options.met_dir, dataset_name, product_name, start_date,
            end_date
        ]
        if is_met_dataset_downloaded(*args):
            reply = QMessageBox.question(self.iface.mainWindow(
            ), 'Existing dataset', (
                'You already downloaded data with the selected dataset/product/date/time combination. '
                'If you continue, this data will be removed.\n'
                'Location: {}'.format(get_met_dataset_path(*args))),
                                         QMessageBox.Ok, QMessageBox.Cancel)
            if reply == QMessageBox.Cancel:
                return

        lat_north = self.top.value()
        lat_south = self.bottom.value()
        lon_west = self.left.value()
        lon_east = self.right.value()
        auth = (self.options.rda_username, self.options.rda_password)

        thread = TaskThread(lambda: download_met_dataset(
            self.options.met_dir, auth, dataset_name, product_name,
            param_names, start_date, end_date, lat_south, lat_north, lon_west,
            lon_east),
                            yields_progress=True)
        thread.started.connect(self.on_started_download)
        thread.progress.connect(self.on_progress_download)
        thread.finished.connect(self.on_finished_download)
        thread.succeeded.connect(self.on_successful_download)
        thread.failed.connect(reraise)
        thread.start()

    def on_started_download(self) -> None:
        self.btn_download.hide()
        self.progress_bar.show()

    def on_progress_download(self, progress: float, status: str) -> None:
        bar_value = int(progress * PROGRESS_BAR_MAX)
        self.progress_bar.setValue(bar_value)
        self.progress_bar.repaint()  # otherwise just updates in 1% steps
        if status == 'submitted':
            self.msg_bar.info(
                'Met dataset download request submitted successfully, waiting until available for download...'
            )
        elif status == 'ready':
            self.msg_bar.info(
                'Met dataset download request is now ready, downloading...')
        logger.debug(f'Met data download: {progress*100:.1f}% - {status}')

    def on_finished_download(self) -> None:
        self.btn_download.show()
        self.progress_bar.hide()

    def on_successful_download(self) -> None:
        self.msg_bar.success('Meteorological dataset downloaded successfully.')
        Broadcast.met_datasets_updated.emit()

    def on_extent_radio_button_clicked(self):
        if self.radio_global.isChecked():
            self.top.set_value(90)
            self.bottom.set_value(-90)
            self.left.set_value(-180)
            self.right.set_value(180)
            self.top.setDisabled(True)
            self.bottom.setDisabled(True)
            self.left.setDisabled(True)
            self.right.setDisabled(True)
            self.widget_extent.hide()

        elif self.radio_subset.isChecked():
            self.widget_extent.show()
            self.top.setDisabled(False)
            self.bottom.setDisabled(False)
            self.left.setDisabled(False)
            self.right.setDisabled(False)

    def on_extent_from_active_layer_button_clicked(self):
        layer = self.iface.activeLayer()  # type: Optional[QgsMapLayer]
        if layer is None:
            return
        layer_crs = CRS(layer.crs().toProj4())
        target_crs = CRS('+proj=latlong +datum=WGS84')
        extent = layer.extent()  # type: QgsRectangle
        bbox = rect_to_bbox(extent)
        bbox_geo = layer_crs.transform_bbox(bbox, target_crs.srs)
        padding = 5  # degrees
        lat_south = max(bbox_geo.miny - 5, -90)
        lat_north = min(bbox_geo.maxy + 5, 90)
        lon_west = max(bbox_geo.minx - 5, -180)
        lon_east = min(bbox_geo.maxx + 5, 180)
        self.bottom.set_value(lat_south)
        self.top.set_value(lat_north)
        self.left.set_value(lon_west)
        self.right.set_value(lon_east)
Пример #26
0
class ConnMail(ElementMaster):

    pixmap_path = 'images/ConnMail.png'
    child_pos = (True, False)

    def __init__(self, row, column):
        self.row = row
        self.column = column

        recipient = None
        sender = None
        password = None
        server_url = None
        server_port = '465'
        subject = None
        input_opt_index = 0
        input_opt_data = None
        filename = None
        pass_input = False
        message_state = False
        message_txt = None
        log_state = False

        # recipient, sender, password, server_url, server_port, subject
        # input_opt_index, input_opt_data, filename, pass_input, message_state, message_txt, log_state
        self.config = (recipient, sender, password, server_url, server_port,
                       subject, input_opt_index, input_opt_data, filename,
                       pass_input, message_state, message_txt, log_state)

        super().__init__(self.row, self.column, QPixmap(self.pixmap_path),
                         True, self.config)
        super().edit_sig.connect(self.edit)
        logging.debug(
            'ConnMail::__init__() called at row {}, column {}'.format(
                row, column))
        self.addFunction(ConnMailFunction)

    def __setstate__(self, state):
        logging.debug('ConnMail::__setstate__() called')
        self.row, self.column, self.config = state
        super().__init__(self.row, self.column, QPixmap(self.pixmap_path),
                         True, self.config)
        super().edit_sig.connect(self.edit)
        self.addFunction(ConnMailFunction)

    def __getstate__(self):
        logging.debug('ConnMail__getstate__() called')
        return (self.row, self.column, self.config)

    def openEditor(self):
        logging.debug('ConnMail::openEditor() called')

    def edit(self):

        logging.debug('ConnMail::edit()')

        self.conn_mail_layout = QVBoxLayout()
        self.confirm_button = QPushButton(QC.translate('', 'Ok'))

        self.recipient_address_txt = QLabel()
        self.recipient_address_txt.setText(
            QC.translate('', 'Recipient address:'))
        self.recipient_address_input = QLineEdit()
        self.recipient_address_input.setPlaceholderText(
            QC.translate('', 'Separate addresses with spaces'))

        self.sender_address_txt = QLabel()
        self.sender_address_txt.setText(
            QC.translate('', 'Enter sender address:'))
        self.sender_address_input = QLineEdit()
        self.sender_address_input.setPlaceholderText(
            QC.translate('', '*****@*****.**'))

        self.password_txt = QLabel()
        self.password_txt.setText(QC.translate('', 'Enter password:'******'', 'Enter subject:'))
        self.subject_input = QLineEdit()

        self.server_txt = QLabel()
        self.server_txt.setText(
            QC.translate('', 'Enter server URL and port number:'))

        self.server_input_line = QWidget()
        self.server_input_line_layout = QHBoxLayout(self.server_input_line)
        self.server_url_input = QLineEdit()
        self.server_url_input.setPlaceholderText(
            QC.translate('', 'e.g. smtp.gmail.com'))
        self.server_port_input = QLineEdit()
        self.server_port_input.setMaximumWidth(50)
        self.server_port_input.setValidator(QIntValidator(0, 9999))
        self.server_port_input.setText('465')
        self.server_input_line_layout.addWidget(self.server_url_input)
        self.server_input_line_layout.addWidget(self.server_port_input)

        self.message_box_line = QWidget()
        self.message_box_txt = QLabel()
        self.message_box_txt.setText(
            QC.translate('', 'Activate user defined message text?'))
        self.message_box_checkbox = QCheckBox()
        self.message_box_line_layout = QHBoxLayout(self.message_box_line)
        self.message_box_line_layout.addWidget(self.message_box_txt)
        self.message_box_line_layout.addWidget(self.message_box_checkbox)
        self.message_box_line_layout = QHBoxLayout(self.message_box_line)

        self.message_txt_input = QTextEdit()

        self.input_option_line = QWidget()
        self.input_option_txt = QLabel()
        self.input_option_txt.setText(QC.translate('', 'Use input as:'))
        self.input_options = QComboBox()
        self.input_options.addItem(QC.translate('', 'None'))
        self.input_options.addItem(QC.translate('', 'Message text'))
        self.input_options.addItem(QC.translate('', 'Attachment (String)'))
        self.input_options.addItem(QC.translate('', 'Attachment (Pickle)'))
        self.input_option_line_layout = QHBoxLayout(self.input_option_line)
        self.input_option_line_layout.addWidget(self.input_option_txt)
        self.input_option_line_layout.addWidget(self.input_options)

        self.filename_input_line = QWidget()
        self.filename_input_line_layout = QHBoxLayout(self.filename_input_line)
        self.filename_input_txt = QLabel()
        self.filename_input_txt.setText(QC.translate('', 'Filename:'))
        self.filename_input = QLineEdit()
        self.filename_input.setPlaceholderText(QC.translate(
            '', 'filename.txt'))
        self.filename_input_line_layout.addWidget(self.filename_input_txt)
        self.filename_input_line_layout.addWidget(self.filename_input)

        self.input_params_1 = QLabel()
        self.input_params_1.setText(
            QC.translate('', 'Note: Input configuration dict has priority'))
        self.input_params_2 = QLabel()
        self.input_params_2.setText(
            '{\'subject\' : \'Hello\', \'message\' : \'World!\'}')

        self.pass_input_line = QWidget()
        self.pass_input_txt = QLabel()
        self.pass_input_txt.setText(QC.translate('', 'Pass input forward?'))
        self.pass_input_check = QCheckBox()
        self.pass_input_line_layout = QHBoxLayout(self.pass_input_line)
        self.pass_input_line_layout.addWidget(self.pass_input_txt)
        self.pass_input_line_layout.addWidget(self.pass_input_check)

        self.help_txt = QLabel()
        self.help_txt.setText(
            QC.translate('', 'Only encrypted connections are allowed'))

        # hier logging option einfügen
        self.log_line = QWidget()
        self.ask_for_logging = QLabel()
        self.ask_for_logging.setText(QC.translate('', 'Log output?'))
        self.log_checkbox = QCheckBox()
        self.log_line_layout = QHBoxLayout(self.log_line)
        self.log_line_layout.addWidget(self.ask_for_logging)
        self.log_line_layout.addWidget(self.log_checkbox)
        self.log_line_layout.addStretch(1)

        self.conn_mail_edit = ElementEditor(self)
        self.conn_mail_edit.setWindowTitle(QC.translate('', 'Send E-Mail'))
        #self.conn_mail_edit.setMinimumSize(240, 330)

        # signals and slots
        self.confirm_button.clicked.connect(self.conn_mail_edit.closeEvent)
        self.conn_mail_edit.window_closed.connect(self.edit_done)
        self.message_box_checkbox.stateChanged.connect(self.toggle_message_box)
        self.input_options.currentIndexChanged.connect(self.indexChanged)
        # load existing config
        self.loadLastConfig()

        self.conn_mail_layout.addWidget(self.recipient_address_txt)
        self.conn_mail_layout.addWidget(self.recipient_address_input)
        self.conn_mail_layout.addWidget(self.sender_address_txt)
        self.conn_mail_layout.addWidget(self.sender_address_input)
        self.conn_mail_layout.addWidget(self.password_txt)
        self.conn_mail_layout.addWidget(self.password_input)
        self.conn_mail_layout.addWidget(self.server_txt)
        self.conn_mail_layout.addWidget(self.server_input_line)
        self.conn_mail_layout.addWidget(self.subject_txt)
        self.conn_mail_layout.addWidget(self.subject_input)
        self.conn_mail_layout.addWidget(self.message_box_line)
        self.conn_mail_layout.addWidget(self.message_txt_input)
        self.conn_mail_layout.addWidget(self.input_option_line)
        self.conn_mail_layout.addWidget(self.filename_input_line)
        self.conn_mail_layout.addWidget(self.input_params_1)
        self.conn_mail_layout.addWidget(self.input_params_2)
        self.conn_mail_layout.addWidget(self.pass_input_line)

        self.conn_mail_layout.addWidget(self.help_txt)
        self.conn_mail_layout.addWidget(self.log_line)
        self.conn_mail_layout.addWidget(self.confirm_button)
        self.conn_mail_edit.setLayout(self.conn_mail_layout)
        self.conn_mail_edit.show()

    def toggle_message_box(self, event):

        logging.debug('ConnMail::toggle_message_box() called')
        if event == 0:
            self.message_txt_input.setDisabled(True)
        else:
            self.message_txt_input.setDisabled(False)

    def indexChanged(self, event):

        current_index = event
        logging.debug('ConnMail::indexChanged() called: {}'.format(event))
        if event == 2 or event == 3:
            self.filename_input_line.setVisible(True)
        else:
            self.filename_input_line.setVisible(False)

    def loadLastConfig(self):

        # recipient, sender, password, server_url, server_port, subject
        # input_opt_index, input_opt_data, filename, pass_input, message_state, log_state

        recipient, sender, password, server_url, server_port, subject, \
                input_opt_index, input_opt_data, filename, pass_input, message_state, \
                message_txt, log_state = self.config

        if message_state:
            self.toggle_message_box(2)
            self.message_box_checkbox.setChecked(True)
        else:
            self.toggle_message_box(0)
            self.message_box_checkbox.setChecked(False)

        if pass_input:
            self.pass_input_check.setChecked(True)
        else:
            self.pass_input_check.setChecked(False)

        if recipient:
            self.recipient_address_input.setText(recipient)

        if sender:
            self.sender_address_input.setText(sender)

        if password:
            self.password_input.setText(password)

        if server_url:
            self.server_url_input.setText(server_url)

        if server_port:
            self.server_port_input.setText(server_port)

        if subject:
            self.subject_input.setText(subject)

        if message_txt:
            self.message_txt_input.setPlainText(message_txt)

        if filename:
            self.filename_input.setText(filename)

        if log_state:
            self.log_checkbox.setChecked(True)
        else:
            self.log_checkbox.setChecked(False)

        self.input_options.setCurrentIndex(input_opt_index)
        self.indexChanged(input_opt_index)

    def edit_done(self):

        logging.debug('ConnMail::edit_done() called')

        recipient = self.recipient_address_input.text()
        sender = self.sender_address_input.text()
        password = self.password_input.text()
        server_url = self.server_url_input.text()
        server_port = self.server_port_input.text()
        subject = self.subject_input.text()
        input_opt_index = self.input_options.currentIndex()
        input_opt_data = self.input_options.currentData()
        filename = self.filename_input.text()

        pass_input = self.pass_input_check.isChecked()
        message_state = self.message_box_checkbox.isChecked()
        log_state = self.log_checkbox.isChecked()

        if self.message_txt_input.toPlainText() == '':
            message_txt = None
        else:
            message_txt = self.message_txt_input.toPlainText()
        # recipient, sender, password, server_url, server_port, subject
        # input_opt_index, input_opt_data, filename, pass_input, message_state, message_txt, log_state
        self.config = (recipient, sender, password, server_url, server_port,
                       subject, input_opt_index, input_opt_data, filename,
                       pass_input, message_state, message_txt, log_state)

        self.addFunction(ConnMailFunction)
class Bound_Split_Dialog_Preview(Muliti_Input_Dialog):
    def __init__(self, *args, **kwargs):
        Muliti_Input_Dialog.__init__(self, *args, **kwargs)
        self.Split = QLineEdit(self)
        self.Split.setText("1")
        self.Ref = QComboBox(self)
        self.Preview = QLabel(self)
        self.Preview.setStyleSheet("background:white")
        self.Preview.setAlignment(Qt.AlignCenter)
        self.Add_row('Input Grid Mode:', self.Split)
        self.Add_row('Choose Bound Refer:', self.Ref)
        self.Derivate_cutoff_x = QLineEdit(self)
        self.Derivate_cutoff_x.setText("1")
        self.Add_row('Derivate cutoff x:', self.Derivate_cutoff_x)
        self.Derivate_cutoff_y = QLineEdit(self)
        self.Derivate_cutoff_y.setText("1")
        self.Add_row('Derivate cutoff y:', self.Derivate_cutoff_y)
        self.Add_row(self.Preview)

        self.Split.textEdited.connect(self.update_preview)
        self.Ref.currentIndexChanged.connect(self.update_preview)
        self.Derivate_cutoff_x.textEdited.connect(self.update_preview)
        self.Derivate_cutoff_y.textEdited.connect(self.update_preview)
        self.Split_Width = []

    def update_preview(self):
        data = self.Ref.currentData()
        img = data['img']
        x, y, w, h = data['rec']
        wb_img = img.copy(x + 2, y, w - 4, h)
        cv_img = QImage_to_CV_Img(wb_img)
        if self.Derivate_cutoff_x.text():
            cutoff_x = float(self.Derivate_cutoff_x.text())
        else:
            cutoff_x = 1

        if self.Derivate_cutoff_x.text():
            cutoff_y = float(self.Derivate_cutoff_x.text())
        else:
            cutoff_y = 1

        bounds_x, bounds_y = bound_from_cv_img(cv_img, cutoff_x, cutoff_y)
        bounds_x_fixed = bounds_x + x + 2
        self.bounds_x = bounds_x_fixed
        bounds_y_fixed = bounds_y + y + 2
        cv_img = QImage_to_CV_Img(img)
        ih = cv_img.shape[0]
        iw = cv_img.shape[1]
        for split in bounds_x_fixed:
            cv_img = cv2.line(cv_img, (split, 0), (split, ih), (255, 0, 0), 1)
        for split in bounds_y_fixed:
            cv_img = cv2.line(cv_img, (0, split), (iw, split), (255, 0, 0), 1)

        # 补充根据split_str 来分割框框
        split_str = self.Split.text().split(',')
        split_str = [int(i) for i in split_str if len(i) > 0]
        ll = min(len(split_str), len(bounds_x_fixed))
        current = 0
        self.Split_str = split_str[0:ll]
        self.Split_Width = []
        for i in range(ll):
            sp = split_str[i]
            start = current
            end = current + sp
            self.Split_Width.append(bounds_x_fixed[end] -
                                    bounds_x_fixed[start])
            cv_img = cv2.line(cv_img,
                              (bounds_x_fixed[start] + 10, bounds_y_fixed[0]),
                              (bounds_x_fixed[end] - 10, bounds_y_fixed[1]),
                              (0, 0, 255), 2)
            current = end
        qimg, _ = CV_Img_to_QImage(cv_img)
        self.Preview.setPixmap(qimg)
Пример #28
0
class GuiConfigEditGeneralTab(QWidget):

    def __init__(self, theParent):
        QWidget.__init__(self, theParent)

        self.mainConf  = nw.CONFIG
        self.theParent = theParent
        self.theTheme  = theParent.theTheme

        # The Form
        self.mainForm = QConfigLayout()
        self.mainForm.setHelpTextStyle(self.theTheme.helpText)
        self.setLayout(self.mainForm)

        # Look and Feel
        # =============
        self.mainForm.addGroupLabel("Look and Feel")

        ## Select Theme
        self.selectTheme = QComboBox()
        self.selectTheme.setMinimumWidth(self.mainConf.pxInt(200))
        self.theThemes = self.theTheme.listThemes()
        for themeDir, themeName in self.theThemes:
            self.selectTheme.addItem(themeName, themeDir)
        themeIdx = self.selectTheme.findData(self.mainConf.guiTheme)
        if themeIdx != -1:
            self.selectTheme.setCurrentIndex(themeIdx)

        self.mainForm.addRow(
            "Main GUI theme",
            self.selectTheme,
            "Changing this requires restarting novelWriter."
        )

        ## Select Icon Theme
        self.selectIcons = QComboBox()
        self.selectIcons.setMinimumWidth(self.mainConf.pxInt(200))
        self.theIcons = self.theTheme.theIcons.listThemes()
        for iconDir, iconName in self.theIcons:
            self.selectIcons.addItem(iconName, iconDir)
        iconIdx = self.selectIcons.findData(self.mainConf.guiIcons)
        if iconIdx != -1:
            self.selectIcons.setCurrentIndex(iconIdx)

        self.mainForm.addRow(
            "Main icon theme",
            self.selectIcons,
            "Changing this requires restarting novelWriter."
        )

        ## Dark Icons
        self.preferDarkIcons = QSwitch()
        self.preferDarkIcons.setChecked(self.mainConf.guiDark)
        self.mainForm.addRow(
            "Prefer icons for dark backgrounds",
            self.preferDarkIcons,
            "This may improve the look of icons on dark themes."
        )

        ## Font Family
        self.guiFont = QLineEdit()
        self.guiFont.setReadOnly(True)
        self.guiFont.setFixedWidth(self.mainConf.pxInt(162))
        self.guiFont.setText(self.mainConf.guiFont)
        self.fontButton = QPushButton("...")
        self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("...")))
        self.fontButton.clicked.connect(self._selectFont)
        self.mainForm.addRow(
            "Font family",
            self.guiFont,
            "Changing this requires restarting novelWriter.",
            theButton = self.fontButton
        )

        ## Font Size
        self.guiFontSize = QSpinBox(self)
        self.guiFontSize.setMinimum(8)
        self.guiFontSize.setMaximum(60)
        self.guiFontSize.setSingleStep(1)
        self.guiFontSize.setValue(self.mainConf.guiFontSize)
        self.mainForm.addRow(
            "Font size",
            self.guiFontSize,
            "Changing this requires restarting novelWriter.",
            theUnit = "pt"
        )

        # GUI Settings
        # ============
        self.mainForm.addGroupLabel("GUI Settings")

        self.showFullPath = QSwitch()
        self.showFullPath.setChecked(self.mainConf.showFullPath)
        self.mainForm.addRow(
            "Show full path in document header",
            self.showFullPath,
            "Shows the document title and parent folder names."
        )

        self.hideVScroll = QSwitch()
        self.hideVScroll.setChecked(self.mainConf.hideVScroll)
        self.mainForm.addRow(
            "Hide vertical scroll bars in main windows",
            self.hideVScroll,
            "Scrolling with mouse wheel and keys only."
        )

        self.hideHScroll = QSwitch()
        self.hideHScroll.setChecked(self.mainConf.hideHScroll)
        self.mainForm.addRow(
            "Hide horizontal scroll bars in main windows",
            self.hideHScroll,
            "Scrolling with mouse wheel and keys only."
        )

        return

    def saveValues(self):
        """Save the values set for this tab.
        """
        validEntries = True
        needsRestart = False

        guiTheme     = self.selectTheme.currentData()
        guiIcons     = self.selectIcons.currentData()
        guiDark      = self.preferDarkIcons.isChecked()
        guiFont      = self.guiFont.text()
        guiFontSize  = self.guiFontSize.value()
        showFullPath = self.showFullPath.isChecked()
        hideVScroll  = self.hideVScroll.isChecked()
        hideHScroll  = self.hideHScroll.isChecked()

        # Check if restart is needed
        needsRestart |= self.mainConf.guiTheme != guiTheme
        needsRestart |= self.mainConf.guiIcons != guiIcons
        needsRestart |= self.mainConf.guiFont != guiFont
        needsRestart |= self.mainConf.guiFontSize != guiFontSize

        self.mainConf.guiTheme     = guiTheme
        self.mainConf.guiIcons     = guiIcons
        self.mainConf.guiDark      = guiDark
        self.mainConf.guiFont      = guiFont
        self.mainConf.guiFontSize  = guiFontSize
        self.mainConf.showFullPath = showFullPath
        self.mainConf.hideVScroll  = hideVScroll
        self.mainConf.hideHScroll  = hideHScroll

        self.mainConf.confChanged = True

        return validEntries, needsRestart

    ##
    #  Slots
    ##

    def _selectFont(self):
        """Open the QFontDialog and set a font for the font style.
        """
        currFont = QFont()
        currFont.setFamily(self.mainConf.guiFont)
        currFont.setPointSize(self.mainConf.guiFontSize)
        theFont, theStatus = QFontDialog.getFont(currFont, self)
        if theStatus:
            self.guiFont.setText(theFont.family())
            self.guiFontSize.setValue(theFont.pointSize())
        return
Пример #29
0
class GuiBuildNovel(QDialog):

    FMT_PDF    = 1  # Print to PDF
    FMT_ODT    = 2  # Open Document file
    FMT_FODT   = 3  # Flat Open Document file
    FMT_HTM    = 4  # HTML5
    FMT_NWD    = 5  # nW Markdown
    FMT_MD     = 6  # Standard Markdown
    FMT_GH     = 7  # GitHub Markdown
    FMT_JSON_H = 8  # HTML5 wrapped in JSON
    FMT_JSON_M = 9  # nW Markdown wrapped in JSON

    def __init__(self, mainGui):
        QDialog.__init__(self, mainGui)

        logger.debug("Initialising GuiBuildNovel ...")
        self.setObjectName("GuiBuildNovel")

        self.mainConf   = novelwriter.CONFIG
        self.mainGui    = mainGui
        self.mainTheme  = mainGui.mainTheme
        self.theProject = mainGui.theProject

        self.htmlText  = []  # List of html documents
        self.htmlStyle = []  # List of html styles
        self.htmlSize  = 0   # Size of the html document
        self.buildTime = 0   # The timestamp of the last build

        self.setWindowTitle(self.tr("Build Novel Project"))
        self.setMinimumWidth(self.mainConf.pxInt(700))
        self.setMinimumHeight(self.mainConf.pxInt(600))

        pOptions = self.theProject.options
        self.resize(
            self.mainConf.pxInt(pOptions.getInt("GuiBuildNovel", "winWidth",  900)),
            self.mainConf.pxInt(pOptions.getInt("GuiBuildNovel", "winHeight", 800))
        )

        self.docView = GuiBuildNovelDocView(self, self.theProject)

        hS = self.mainTheme.fontPixelSize
        wS = 2*hS

        # Title Formats
        # =============

        self.titleGroup = QGroupBox(self.tr("Title Formats for Novel Files"), self)
        self.titleForm  = QGridLayout(self)
        self.titleGroup.setLayout(self.titleForm)

        fmtHelp = "<br>".join([
            "<b>%s</b>" % self.tr("Formatting Codes:"),
            self.tr("{0} for the title as set in the document").format(r"%title%"),
            self.tr("{0} for chapter number (1, 2, 3)").format(r"%ch%"),
            self.tr("{0} for chapter number as a word (one, two)").format(r"%chw%"),
            self.tr("{0} for chapter number in upper case Roman").format(r"%chI%"),
            self.tr("{0} for chapter number in lower case Roman").format(r"%chi%"),
            self.tr("{0} for scene number within chapter").format(r"%sc%"),
            self.tr("{0} for scene number within novel").format(r"%sca%"),
        ])
        fmtScHelp = "<br><br>%s" % self.tr(
            "Leave blank to skip this heading, or set to a static text, like "
            "for instance '{0}', to make a separator. The separator will "
            "be centred automatically and only appear between sections of "
            "the same type."
        ).format("* * *")
        xFmt = self.mainConf.pxInt(100)

        self.fmtTitle = QLineEdit()
        self.fmtTitle.setMaxLength(200)
        self.fmtTitle.setMinimumWidth(xFmt)
        self.fmtTitle.setToolTip(fmtHelp)
        self.fmtTitle.setText(
            self._reFmtCodes(self.theProject.titleFormat["title"])
        )

        self.fmtChapter = QLineEdit()
        self.fmtChapter.setMaxLength(200)
        self.fmtChapter.setMinimumWidth(xFmt)
        self.fmtChapter.setToolTip(fmtHelp)
        self.fmtChapter.setText(
            self._reFmtCodes(self.theProject.titleFormat["chapter"])
        )

        self.fmtUnnumbered = QLineEdit()
        self.fmtUnnumbered.setMaxLength(200)
        self.fmtUnnumbered.setMinimumWidth(xFmt)
        self.fmtUnnumbered.setToolTip(fmtHelp)
        self.fmtUnnumbered.setText(
            self._reFmtCodes(self.theProject.titleFormat["unnumbered"])
        )

        self.fmtScene = QLineEdit()
        self.fmtScene.setMaxLength(200)
        self.fmtScene.setMinimumWidth(xFmt)
        self.fmtScene.setToolTip(fmtHelp + fmtScHelp)
        self.fmtScene.setText(
            self._reFmtCodes(self.theProject.titleFormat["scene"])
        )

        self.fmtSection = QLineEdit()
        self.fmtSection.setMaxLength(200)
        self.fmtSection.setMinimumWidth(xFmt)
        self.fmtSection.setToolTip(fmtHelp + fmtScHelp)
        self.fmtSection.setText(
            self._reFmtCodes(self.theProject.titleFormat["section"])
        )

        self.buildLang = QComboBox()
        self.buildLang.setMinimumWidth(xFmt)
        theLangs = self.mainConf.listLanguages(self.mainConf.LANG_PROJ)
        self.buildLang.addItem("[%s]" % self.tr("Not Set"), "None")
        for langID, langName in theLangs:
            self.buildLang.addItem(langName, langID)

        langIdx = self.buildLang.findData(self.theProject.projLang)
        if langIdx != -1:
            self.buildLang.setCurrentIndex(langIdx)

        self.hideScene = QSwitch(width=wS, height=hS)
        self.hideScene.setChecked(
            pOptions.getBool("GuiBuildNovel", "hideScene", False)
        )

        self.hideSection = QSwitch(width=wS, height=hS)
        self.hideSection.setChecked(
            pOptions.getBool("GuiBuildNovel", "hideSection", True)
        )

        # Wrapper boxes due to QGridView and QLineEdit expand bug
        self.boxTitle = QHBoxLayout()
        self.boxTitle.addWidget(self.fmtTitle)
        self.boxChapter = QHBoxLayout()
        self.boxChapter.addWidget(self.fmtChapter)
        self.boxUnnumb = QHBoxLayout()
        self.boxUnnumb.addWidget(self.fmtUnnumbered)
        self.boxScene = QHBoxLayout()
        self.boxScene.addWidget(self.fmtScene)
        self.boxSection = QHBoxLayout()
        self.boxSection.addWidget(self.fmtSection)

        titleLabel    = QLabel(self.tr("Title"))
        chapterLabel  = QLabel(self.tr("Chapter"))
        unnumbLabel   = QLabel(self.tr("Unnumbered"))
        sceneLabel    = QLabel(self.tr("Scene"))
        sectionLabel  = QLabel(self.tr("Section"))
        langLabel     = QLabel(self.tr("Language"))
        hSceneLabel   = QLabel(self.tr("Hide scene"))
        hSectionLabel = QLabel(self.tr("Hide section"))

        self.titleForm.addWidget(titleLabel,       0, 0, 1, 1, Qt.AlignLeft)
        self.titleForm.addLayout(self.boxTitle,    0, 1, 1, 1, Qt.AlignRight)
        self.titleForm.addWidget(chapterLabel,     1, 0, 1, 1, Qt.AlignLeft)
        self.titleForm.addLayout(self.boxChapter,  1, 1, 1, 1, Qt.AlignRight)
        self.titleForm.addWidget(unnumbLabel,      2, 0, 1, 1, Qt.AlignLeft)
        self.titleForm.addLayout(self.boxUnnumb,   2, 1, 1, 1, Qt.AlignRight)
        self.titleForm.addWidget(sceneLabel,       3, 0, 1, 1, Qt.AlignLeft)
        self.titleForm.addLayout(self.boxScene,    3, 1, 1, 1, Qt.AlignRight)
        self.titleForm.addWidget(sectionLabel,     4, 0, 1, 1, Qt.AlignLeft)
        self.titleForm.addLayout(self.boxSection,  4, 1, 1, 1, Qt.AlignRight)
        self.titleForm.addWidget(langLabel,        5, 0, 1, 1, Qt.AlignLeft)
        self.titleForm.addWidget(self.buildLang,   5, 1, 1, 1, Qt.AlignRight)
        self.titleForm.addWidget(hSceneLabel,      6, 0, 1, 1, Qt.AlignLeft)
        self.titleForm.addWidget(self.hideScene,   6, 1, 1, 1, Qt.AlignRight)
        self.titleForm.addWidget(hSectionLabel,    7, 0, 1, 1, Qt.AlignLeft)
        self.titleForm.addWidget(self.hideSection, 7, 1, 1, 1, Qt.AlignRight)

        self.titleForm.setColumnStretch(0, 0)
        self.titleForm.setColumnStretch(1, 1)

        # Font Options
        # ============

        self.fontGroup = QGroupBox(self.tr("Font Options"), self)
        self.fontForm  = QGridLayout(self)
        self.fontGroup.setLayout(self.fontForm)

        # Font Family
        self.textFont = QLineEdit()
        self.textFont.setReadOnly(True)
        self.textFont.setMinimumWidth(xFmt)
        self.textFont.setText(
            pOptions.getString("GuiBuildNovel", "textFont", self.mainConf.textFont)
        )
        self.fontButton = QPushButton("...")
        self.fontButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("...")))
        self.fontButton.clicked.connect(self._selectFont)

        self.textSize = QSpinBox(self)
        self.textSize.setFixedWidth(6*self.mainTheme.textNWidth)
        self.textSize.setMinimum(6)
        self.textSize.setMaximum(72)
        self.textSize.setSingleStep(1)
        self.textSize.setValue(
            pOptions.getInt("GuiBuildNovel", "textSize", self.mainConf.textSize)
        )

        self.lineHeight = QDoubleSpinBox(self)
        self.lineHeight.setFixedWidth(6*self.mainTheme.textNWidth)
        self.lineHeight.setMinimum(0.8)
        self.lineHeight.setMaximum(3.0)
        self.lineHeight.setSingleStep(0.05)
        self.lineHeight.setDecimals(2)
        self.lineHeight.setValue(
            pOptions.getFloat("GuiBuildNovel", "lineHeight", 1.15)
        )

        # Wrapper box due to QGridView and QLineEdit expand bug
        self.boxFont = QHBoxLayout()
        self.boxFont.addWidget(self.textFont)

        fontFamilyLabel = QLabel(self.tr("Font family"))
        fontSizeLabel   = QLabel(self.tr("Font size"))
        lineHeightLabel = QLabel(self.tr("Line height"))
        justifyLabel    = QLabel(self.tr("Justify text"))
        stylingLabel    = QLabel(self.tr("Disable styling"))

        self.fontForm.addWidget(fontFamilyLabel,  0, 0, 1, 1, Qt.AlignLeft)
        self.fontForm.addLayout(self.boxFont,     0, 1, 1, 1, Qt.AlignRight)
        self.fontForm.addWidget(self.fontButton,  0, 2, 1, 1, Qt.AlignRight)
        self.fontForm.addWidget(fontSizeLabel,    1, 0, 1, 1, Qt.AlignLeft)
        self.fontForm.addWidget(self.textSize,    1, 1, 1, 2, Qt.AlignRight)
        self.fontForm.addWidget(lineHeightLabel,  2, 0, 1, 1, Qt.AlignLeft)
        self.fontForm.addWidget(self.lineHeight,  2, 1, 1, 2, Qt.AlignRight)

        self.fontForm.setColumnStretch(0, 0)
        self.fontForm.setColumnStretch(1, 1)
        self.fontForm.setColumnStretch(2, 0)

        # Styling Options
        # ===============

        self.styleGroup = QGroupBox(self.tr("Styling Options"), self)
        self.styleForm  = QGridLayout(self)
        self.styleGroup.setLayout(self.styleForm)

        self.justifyText = QSwitch(width=wS, height=hS)
        self.justifyText.setChecked(
            pOptions.getBool("GuiBuildNovel", "justifyText", False)
        )

        self.noStyling = QSwitch(width=wS, height=hS)
        self.noStyling.setChecked(
            pOptions.getBool("GuiBuildNovel", "noStyling", False)
        )

        self.styleForm.addWidget(justifyLabel,     1, 0, 1, 1, Qt.AlignLeft)
        self.styleForm.addWidget(self.justifyText, 1, 1, 1, 2, Qt.AlignRight)
        self.styleForm.addWidget(stylingLabel,     2, 0, 1, 1, Qt.AlignLeft)
        self.styleForm.addWidget(self.noStyling,   2, 1, 1, 2, Qt.AlignRight)

        self.styleForm.setColumnStretch(0, 0)
        self.styleForm.setColumnStretch(1, 1)

        # Include Options
        # ===============

        self.textGroup = QGroupBox(self.tr("Include Options"), self)
        self.textForm  = QGridLayout(self)
        self.textGroup.setLayout(self.textForm)

        self.includeSynopsis = QSwitch(width=wS, height=hS)
        self.includeSynopsis.setChecked(
            pOptions.getBool("GuiBuildNovel", "incSynopsis", False)
        )

        self.includeComments = QSwitch(width=wS, height=hS)
        self.includeComments.setChecked(
            pOptions.getBool("GuiBuildNovel", "incComments", False)
        )

        self.includeKeywords = QSwitch(width=wS, height=hS)
        self.includeKeywords.setChecked(
            pOptions.getBool("GuiBuildNovel", "incKeywords", False)
        )

        self.includeBody = QSwitch(width=wS, height=hS)
        self.includeBody.setChecked(
            pOptions.getBool("GuiBuildNovel", "incBodyText", True)
        )

        synopsisLabel = QLabel(self.tr("Include synopsis"))
        commentsLabel = QLabel(self.tr("Include comments"))
        keywordsLabel = QLabel(self.tr("Include keywords"))
        bodyLabel     = QLabel(self.tr("Include body text"))

        self.textForm.addWidget(synopsisLabel,        0, 0, 1, 1, Qt.AlignLeft)
        self.textForm.addWidget(self.includeSynopsis, 0, 1, 1, 1, Qt.AlignRight)
        self.textForm.addWidget(commentsLabel,        1, 0, 1, 1, Qt.AlignLeft)
        self.textForm.addWidget(self.includeComments, 1, 1, 1, 1, Qt.AlignRight)
        self.textForm.addWidget(keywordsLabel,        2, 0, 1, 1, Qt.AlignLeft)
        self.textForm.addWidget(self.includeKeywords, 2, 1, 1, 1, Qt.AlignRight)
        self.textForm.addWidget(bodyLabel,            3, 0, 1, 1, Qt.AlignLeft)
        self.textForm.addWidget(self.includeBody,     3, 1, 1, 1, Qt.AlignRight)

        self.textForm.setColumnStretch(0, 1)
        self.textForm.setColumnStretch(1, 0)

        # File Filter Options
        # ===================

        self.fileGroup = QGroupBox(self.tr("File Filter Options"), self)
        self.fileForm  = QGridLayout(self)
        self.fileGroup.setLayout(self.fileForm)

        self.novelFiles = QSwitch(width=wS, height=hS)
        self.novelFiles.setChecked(
            pOptions.getBool("GuiBuildNovel", "addNovel", True)
        )

        self.noteFiles = QSwitch(width=wS, height=hS)
        self.noteFiles.setChecked(
            pOptions.getBool("GuiBuildNovel", "addNotes", False)
        )

        self.ignoreFlag = QSwitch(width=wS, height=hS)
        self.ignoreFlag.setChecked(
            pOptions.getBool("GuiBuildNovel", "ignoreFlag", False)
        )

        novelLabel  = QLabel(self.tr("Include novel files"))
        notesLabel  = QLabel(self.tr("Include note files"))
        exportLabel = QLabel(self.tr("Ignore export flag"))

        self.fileForm.addWidget(novelLabel,      0, 0, 1, 1, Qt.AlignLeft)
        self.fileForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight)
        self.fileForm.addWidget(notesLabel,      1, 0, 1, 1, Qt.AlignLeft)
        self.fileForm.addWidget(self.noteFiles,  1, 1, 1, 1, Qt.AlignRight)
        self.fileForm.addWidget(exportLabel,     2, 0, 1, 1, Qt.AlignLeft)
        self.fileForm.addWidget(self.ignoreFlag, 2, 1, 1, 1, Qt.AlignRight)

        self.fileForm.setColumnStretch(0, 1)
        self.fileForm.setColumnStretch(1, 0)

        # Export Options
        # ==============

        self.exportGroup = QGroupBox(self.tr("Export Options"), self)
        self.exportForm  = QGridLayout(self)
        self.exportGroup.setLayout(self.exportForm)

        self.replaceTabs = QSwitch(width=wS, height=hS)
        self.replaceTabs.setChecked(
            pOptions.getBool("GuiBuildNovel", "replaceTabs", False)
        )

        self.replaceUCode = QSwitch(width=wS, height=hS)
        self.replaceUCode.setChecked(
            pOptions.getBool("GuiBuildNovel", "replaceUCode", False)
        )

        tabsLabel  = QLabel(self.tr("Replace tabs with spaces"))
        uCodeLabel = QLabel(self.tr("Replace Unicode in HTML"))

        self.exportForm.addWidget(tabsLabel,         0, 0, 1, 1, Qt.AlignLeft)
        self.exportForm.addWidget(self.replaceTabs,  0, 1, 1, 1, Qt.AlignRight)
        self.exportForm.addWidget(uCodeLabel,        1, 0, 1, 1, Qt.AlignLeft)
        self.exportForm.addWidget(self.replaceUCode, 1, 1, 1, 1, Qt.AlignRight)

        self.exportForm.setColumnStretch(0, 1)
        self.exportForm.setColumnStretch(1, 0)

        # Build Button
        # ============

        self.buildProgress = QProgressBar()

        self.buildNovel = QPushButton(self.tr("Build Preview"))
        self.buildNovel.clicked.connect(self._buildPreview)

        # Action Buttons
        # ==============

        self.buttonBox = QHBoxLayout()

        # Printing

        self.printMenu = QMenu(self)
        self.btnPrint = QPushButton(self.tr("Print"))
        self.btnPrint.setMenu(self.printMenu)

        self.printSend = QAction(self.tr("Print Preview"), self)
        self.printSend.triggered.connect(self._printDocument)
        self.printMenu.addAction(self.printSend)

        self.printFile = QAction(self.tr("Print to PDF"), self)
        self.printFile.triggered.connect(lambda: self._saveDocument(self.FMT_PDF))
        self.printMenu.addAction(self.printFile)

        # Saving to File

        self.saveMenu = QMenu(self)
        self.btnSave = QPushButton(self.tr("Save As"))
        self.btnSave.setMenu(self.saveMenu)

        self.saveODT = QAction(self.tr("Open Document (.odt)"), self)
        self.saveODT.triggered.connect(lambda: self._saveDocument(self.FMT_ODT))
        self.saveMenu.addAction(self.saveODT)

        self.saveFODT = QAction(self.tr("Flat Open Document (.fodt)"), self)
        self.saveFODT.triggered.connect(lambda: self._saveDocument(self.FMT_FODT))
        self.saveMenu.addAction(self.saveFODT)

        self.saveHTM = QAction(self.tr("novelWriter HTML (.htm)"), self)
        self.saveHTM.triggered.connect(lambda: self._saveDocument(self.FMT_HTM))
        self.saveMenu.addAction(self.saveHTM)

        self.saveNWD = QAction(self.tr("novelWriter Markdown (.nwd)"), self)
        self.saveNWD.triggered.connect(lambda: self._saveDocument(self.FMT_NWD))
        self.saveMenu.addAction(self.saveNWD)

        self.saveMD = QAction(self.tr("Standard Markdown (.md)"), self)
        self.saveMD.triggered.connect(lambda: self._saveDocument(self.FMT_MD))
        self.saveMenu.addAction(self.saveMD)

        self.saveGH = QAction(self.tr("GitHub Markdown (.md)"), self)
        self.saveGH.triggered.connect(lambda: self._saveDocument(self.FMT_GH))
        self.saveMenu.addAction(self.saveGH)

        self.saveJsonH = QAction(self.tr("JSON + novelWriter HTML (.json)"), self)
        self.saveJsonH.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_H))
        self.saveMenu.addAction(self.saveJsonH)

        self.saveJsonM = QAction(self.tr("JSON + novelWriter Markdown (.json)"), self)
        self.saveJsonM.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_M))
        self.saveMenu.addAction(self.saveJsonM)

        self.btnClose = QPushButton(self.tr("Close"))
        self.btnClose.clicked.connect(self._doClose)

        self.buttonBox.addWidget(self.btnSave)
        self.buttonBox.addWidget(self.btnPrint)
        self.buttonBox.addWidget(self.btnClose)
        self.buttonBox.setSpacing(self.mainConf.pxInt(4))

        # Assemble GUI
        # ============

        # Splitter Position
        boxWidth = self.mainConf.pxInt(350)
        boxWidth = pOptions.getInt("GuiBuildNovel", "boxWidth", boxWidth)
        docWidth = max(self.width() - boxWidth, 100)
        docWidth = pOptions.getInt("GuiBuildNovel", "docWidth", docWidth)

        # The Tool Box
        self.toolsBox = QVBoxLayout()
        self.toolsBox.addWidget(self.titleGroup)
        self.toolsBox.addWidget(self.fontGroup)
        self.toolsBox.addWidget(self.styleGroup)
        self.toolsBox.addWidget(self.textGroup)
        self.toolsBox.addWidget(self.fileGroup)
        self.toolsBox.addWidget(self.exportGroup)
        self.toolsBox.addStretch(1)

        # Tool Box Wrapper Widget
        self.toolsWidget = QWidget()
        self.toolsWidget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
        self.toolsWidget.setLayout(self.toolsBox)

        # Tool Box Scroll Area
        self.toolsArea = QScrollArea()
        self.toolsArea.setMinimumWidth(self.mainConf.pxInt(250))
        self.toolsArea.setWidgetResizable(True)
        self.toolsArea.setWidget(self.toolsWidget)

        if self.mainConf.hideVScroll:
            self.toolsArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        else:
            self.toolsArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)

        if self.mainConf.hideHScroll:
            self.toolsArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        else:
            self.toolsArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)

        # Tools and Buttons Layout
        tSp = self.mainConf.pxInt(8)
        self.innerBox = QVBoxLayout()
        self.innerBox.addWidget(self.toolsArea)
        self.innerBox.addSpacing(tSp)
        self.innerBox.addWidget(self.buildProgress)
        self.innerBox.addWidget(self.buildNovel)
        self.innerBox.addSpacing(tSp)
        self.innerBox.addLayout(self.buttonBox)

        # Tools and Buttons Wrapper Widget
        self.innerWidget = QWidget()
        self.innerWidget.setLayout(self.innerBox)

        # Main Dialog Splitter
        self.mainSplit = QSplitter(Qt.Horizontal)
        self.mainSplit.addWidget(self.innerWidget)
        self.mainSplit.addWidget(self.docView)
        self.mainSplit.setSizes([boxWidth, docWidth])

        self.idxSettings = self.mainSplit.indexOf(self.innerWidget)
        self.idxDocument = self.mainSplit.indexOf(self.docView)

        self.mainSplit.setCollapsible(self.idxSettings, False)
        self.mainSplit.setCollapsible(self.idxDocument, False)

        # Outer Layout
        self.outerBox = QHBoxLayout()
        self.outerBox.addWidget(self.mainSplit)

        self.setLayout(self.outerBox)
        self.buildNovel.setFocus()

        logger.debug("GuiBuildNovel initialisation complete")

        return

    def viewCachedDoc(self):
        """Load the previously generated document from cache.
        """
        if self._loadCache():
            textFont = self.textFont.text()
            textSize = self.textSize.value()
            justifyText = self.justifyText.isChecked()
            self.docView.setTextFont(textFont, textSize)
            self.docView.setJustify(justifyText)
            if self.noStyling.isChecked():
                self.docView.clearStyleSheet()
            else:
                self.docView.setStyleSheet(self.htmlStyle)

            htmlSize = sum([len(x) for x in self.htmlText])
            if htmlSize < nwConst.MAX_BUILDSIZE:
                qApp.processEvents()
                self.docView.setContent(self.htmlText, self.buildTime)
            else:
                self.docView.setText(
                    self.tr("Failed to generate preview. The result is too big.")
                )

        else:
            self.htmlText = []
            self.htmlStyle = []
            self.buildTime = 0
            return False

        return True

    ##
    #  Slots and Related
    ##

    def _buildPreview(self):
        """Build a preview of the project in the document viewer.
        """
        # Get Settings
        justifyText = self.justifyText.isChecked()
        noStyling = self.noStyling.isChecked()
        textFont = self.textFont.text()
        textSize = self.textSize.value()
        replaceTabs = self.replaceTabs.isChecked()

        self.htmlText = []
        self.htmlStyle = []
        self.htmlSize = 0

        # Build Preview
        # =============

        makeHtml = ToHtml(self.theProject)
        self._doBuild(makeHtml, isPreview=True)
        if replaceTabs:
            makeHtml.replaceTabs()

        self.htmlText  = makeHtml.fullHTML
        self.htmlStyle = makeHtml.getStyleSheet()
        self.htmlSize  = makeHtml.getFullResultSize()
        self.buildTime = int(time())

        # Load Preview
        # ============

        self.docView.setTextFont(textFont, textSize)
        self.docView.setJustify(justifyText)
        if noStyling:
            self.docView.clearStyleSheet()
        else:
            self.docView.setStyleSheet(self.htmlStyle)

        if self.htmlSize < nwConst.MAX_BUILDSIZE:
            self.docView.setContent(self.htmlText, self.buildTime)
        else:
            self.docView.setText(
                "Failed to generate preview. The result is too big."
            )

        self._saveCache()

        return

    def _doBuild(self, bldObj, isPreview=False, doConvert=True):
        """Rund the build with a specific build object.
        """
        tStart = int(time())

        # Get Settings
        fmtTitle      = self.fmtTitle.text()
        fmtChapter    = self.fmtChapter.text()
        fmtUnnumbered = self.fmtUnnumbered.text()
        fmtScene      = self.fmtScene.text()
        fmtSection    = self.fmtSection.text()
        buildLang     = self.buildLang.currentData()
        hideScene     = self.hideScene.isChecked()
        hideSection   = self.hideSection.isChecked()
        textFont      = self.textFont.text()
        textSize      = self.textSize.value()
        lineHeight    = self.lineHeight.value()
        justifyText   = self.justifyText.isChecked()
        noStyling     = self.noStyling.isChecked()
        incSynopsis   = self.includeSynopsis.isChecked()
        incComments   = self.includeComments.isChecked()
        incKeywords   = self.includeKeywords.isChecked()
        novelFiles    = self.novelFiles.isChecked()
        noteFiles     = self.noteFiles.isChecked()
        ignoreFlag    = self.ignoreFlag.isChecked()
        includeBody   = self.includeBody.isChecked()
        replaceUCode  = self.replaceUCode.isChecked()

        # The language lookup dict is reloaded if needed
        self.theProject.setProjectLang(buildLang)

        # Get font information
        fontInfo = QFontInfo(QFont(textFont, textSize))
        textFixed = fontInfo.fixedPitch()

        isHtml = isinstance(bldObj, ToHtml)
        isOdt = isinstance(bldObj, ToOdt)

        bldObj.setTitleFormat(fmtTitle)
        bldObj.setChapterFormat(fmtChapter)
        bldObj.setUnNumberedFormat(fmtUnnumbered)
        bldObj.setSceneFormat(fmtScene, hideScene)
        bldObj.setSectionFormat(fmtSection, hideSection)

        bldObj.setFont(textFont, textSize, textFixed)
        bldObj.setJustify(justifyText)
        bldObj.setLineHeight(lineHeight)

        bldObj.setSynopsis(incSynopsis)
        bldObj.setComments(incComments)
        bldObj.setKeywords(incKeywords)
        bldObj.setBodyText(includeBody)

        if isHtml:
            bldObj.setStyles(not noStyling)
            bldObj.setReplaceUnicode(replaceUCode)

        if isOdt:
            bldObj.setColourHeaders(not noStyling)
            bldObj.setLanguage(buildLang)
            bldObj.initDocument()

        # Make sure the project and document is up to date
        self.mainGui.saveDocument()

        self.buildProgress.setMaximum(len(self.theProject.tree))
        self.buildProgress.setValue(0)

        for nItt, tItem in enumerate(self.theProject.tree):

            noteRoot = noteFiles
            noteRoot &= tItem.itemType == nwItemType.ROOT
            noteRoot &= tItem.itemClass != nwItemClass.NOVEL
            noteRoot &= tItem.itemClass != nwItemClass.ARCHIVE

            try:
                if noteRoot:
                    # Add headers for root folders of notes
                    bldObj.addRootHeading(tItem.itemHandle)
                    if doConvert:
                        bldObj.doConvert()

                elif self._checkInclude(tItem, noteFiles, novelFiles, ignoreFlag):
                    bldObj.setText(tItem.itemHandle)
                    bldObj.doPreProcessing()
                    bldObj.tokenizeText()
                    bldObj.doHeaders()
                    if doConvert:
                        bldObj.doConvert()
                    bldObj.doPostProcessing()

            except Exception:
                logger.error("Failed to build document '%s'", tItem.itemHandle)
                logException()
                if isPreview:
                    self.docView.setText((
                        "Failed to generate preview. "
                        "Document with title '%s' could not be parsed."
                    ) % tItem.itemName)

                return False

            # Update progress bar, also for skipped items
            self.buildProgress.setValue(nItt+1)

        if isOdt:
            bldObj.closeDocument()

        tEnd = int(time())
        logger.debug("Built project in %.3f ms", 1000*(tEnd - tStart))

        if bldObj.errData:
            self.mainGui.makeAlert([
                self.tr("There were problems when building the project:")
            ] + bldObj.errData, nwAlert.ERROR)

        return

    def _checkInclude(self, theItem, noteFiles, novelFiles, ignoreFlag):
        """This function checks whether a file should be included in the
        export or not. For standard note and novel files, this is
        controlled by the options selected by the user. For other files
        classified as non-exportable, a few checks must be made, and the
        following are not:
        * Items that are not actual files.
        * Items that have been orphaned which are tagged as NO_LAYOUT
          and NO_CLASS.
        * Items that appear in the TRASH folder or have parent set to
          None (orphaned files).
        """
        if theItem is None:
            return False

        if not (theItem.isExported or ignoreFlag):
            return False

        isNone  = theItem.itemType != nwItemType.FILE
        isNone |= theItem.itemLayout == nwItemLayout.NO_LAYOUT
        isNone |= theItem.isInactive()
        isNone |= theItem.itemParent is None
        isNote  = theItem.itemLayout == nwItemLayout.NOTE
        isNovel = not isNone and not isNote

        if isNone:
            return False
        if isNote and not noteFiles:
            return False
        if isNovel and not novelFiles:
            return False

        return True

    def _saveDocument(self, theFmt):
        """Save the document to various formats.
        """
        replaceTabs = self.replaceTabs.isChecked()

        fileExt = ""
        textFmt = ""

        # Settings
        # ========

        if theFmt == self.FMT_ODT:
            fileExt = "odt"
            textFmt = self.tr("Open Document")

        elif theFmt == self.FMT_FODT:
            fileExt = "fodt"
            textFmt = self.tr("Flat Open Document")

        elif theFmt == self.FMT_HTM:
            fileExt = "htm"
            textFmt = self.tr("Plain HTML")

        elif theFmt == self.FMT_NWD:
            fileExt = "nwd"
            textFmt = self.tr("novelWriter Markdown")

        elif theFmt == self.FMT_MD:
            fileExt = "md"
            textFmt = self.tr("Standard Markdown")

        elif theFmt == self.FMT_GH:
            fileExt = "md"
            textFmt = self.tr("GitHub Markdown")

        elif theFmt == self.FMT_JSON_H:
            fileExt = "json"
            textFmt = self.tr("JSON + novelWriter HTML")

        elif theFmt == self.FMT_JSON_M:
            fileExt = "json"
            textFmt = self.tr("JSON + novelWriter Markdown")

        elif theFmt == self.FMT_PDF:
            fileExt = "pdf"
            textFmt = self.tr("PDF")

        else:
            return False

        # Generate File Name
        # ==================

        cleanName = makeFileNameSafe(self.theProject.projName)
        fileName = "%s.%s" % (cleanName, fileExt)
        saveDir = self.mainConf.lastPath
        if not os.path.isdir(saveDir):
            saveDir = os.path.expanduser("~")

        savePath = os.path.join(saveDir, fileName)
        savePath, _ = QFileDialog.getSaveFileName(
            self, self.tr("Save Document As"), savePath
        )
        if not savePath:
            return False

        self.mainConf.setLastPath(savePath)

        # Build and Write
        # ===============

        errMsg = ""
        wSuccess = False

        if theFmt == self.FMT_ODT:
            makeOdt = ToOdt(self.theProject, isFlat=False)
            self._doBuild(makeOdt)
            try:
                makeOdt.saveOpenDocText(savePath)
                wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt == self.FMT_FODT:
            makeOdt = ToOdt(self.theProject, isFlat=True)
            self._doBuild(makeOdt)
            try:
                makeOdt.saveFlatXML(savePath)
                wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt == self.FMT_HTM:
            makeHtml = ToHtml(self.theProject)
            self._doBuild(makeHtml)
            if replaceTabs:
                makeHtml.replaceTabs()

            try:
                makeHtml.saveHTML5(savePath)
                wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt == self.FMT_NWD:
            makeNwd = ToMarkdown(self.theProject)
            makeNwd.setKeepMarkdown(True)
            self._doBuild(makeNwd, doConvert=False)
            if replaceTabs:
                makeNwd.replaceTabs(spaceChar=" ")

            try:
                makeNwd.saveRawMarkdown(savePath)
                wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt in (self.FMT_MD, self.FMT_GH):
            makeMd = ToMarkdown(self.theProject)
            if theFmt == self.FMT_GH:
                makeMd.setGitHubMarkdown()
            else:
                makeMd.setStandardMarkdown()

            self._doBuild(makeMd)
            if replaceTabs:
                makeMd.replaceTabs(nSpaces=4, spaceChar=" ")

            try:
                makeMd.saveMarkdown(savePath)
                wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt == self.FMT_JSON_H or theFmt == self.FMT_JSON_M:
            jsonData = {
                "meta": {
                    "workingTitle": self.theProject.projName,
                    "novelTitle": self.theProject.bookTitle,
                    "authors": self.theProject.bookAuthors,
                    "buildTime": self.buildTime,
                }
            }

            if theFmt == self.FMT_JSON_H:
                makeHtml = ToHtml(self.theProject)
                self._doBuild(makeHtml)
                if replaceTabs:
                    makeHtml.replaceTabs()

                theBody = []
                for htmlPage in makeHtml.fullHTML:
                    theBody.append(htmlPage.rstrip("\n").split("\n"))
                jsonData["text"] = {
                    "css": self.htmlStyle,
                    "html": theBody,
                }

            elif theFmt == self.FMT_JSON_M:
                makeMd = ToHtml(self.theProject)
                makeMd.setKeepMarkdown(True)
                self._doBuild(makeMd, doConvert=False)
                if replaceTabs:
                    makeMd.replaceTabs(spaceChar=" ")

                theBody = []
                for nwdPage in makeMd.theMarkdown:
                    theBody.append(nwdPage.split("\n"))
                jsonData["text"] = {
                    "nwd": theBody,
                }

            try:
                with open(savePath, mode="w", encoding="utf-8") as outFile:
                    outFile.write(json.dumps(jsonData, indent=2))
                    wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt == self.FMT_PDF:
            try:
                thePrinter = QPrinter()
                thePrinter.setOutputFormat(QPrinter.PdfFormat)
                thePrinter.setOrientation(QPrinter.Portrait)
                thePrinter.setDuplex(QPrinter.DuplexLongSide)
                thePrinter.setFontEmbeddingEnabled(True)
                thePrinter.setColorMode(QPrinter.Color)
                thePrinter.setOutputFileName(savePath)
                self.docView.document().print(thePrinter)
                wSuccess = True

            except Exception as exc:
                errMsg = formatException(exc)

        else:
            # If the if statements above and here match, it should not
            # be possible to reach this else statement.
            return False  # pragma: no cover

        # Report to User
        # ==============

        if wSuccess:
            self.mainGui.makeAlert([
                self.tr("{0} file successfully written to:").format(textFmt), savePath
            ], nwAlert.INFO)
        else:
            self.mainGui.makeAlert(self.tr(
                "Failed to write {0} file. {1}"
            ).format(textFmt, errMsg), nwAlert.ERROR)

        return wSuccess

    def _printDocument(self):
        """Open the print preview dialog.
        """
        thePreview = QPrintPreviewDialog(self)
        thePreview.paintRequested.connect(self._doPrintPreview)
        thePreview.exec_()
        return

    def _doPrintPreview(self, thePrinter):
        """Connect the print preview painter to the document viewer.
        """
        qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
        thePrinter.setOrientation(QPrinter.Portrait)
        self.docView.document().print(thePrinter)
        qApp.restoreOverrideCursor()
        return

    def _selectFont(self):
        """Open the QFontDialog and set a font for the font style.
        """
        currFont = QFont()
        currFont.setFamily(self.textFont.text())
        currFont.setPointSize(self.textSize.value())
        theFont, theStatus = QFontDialog.getFont(currFont, self)
        if theStatus:
            self.textFont.setText(theFont.family())
            self.textSize.setValue(theFont.pointSize())

        self.raise_()  # Move the dialog to front (fixes a bug on macOS)

        return

    def _loadCache(self):
        """Save the current data to cache.
        """
        buildCache = os.path.join(self.theProject.projCache, nwFiles.BUILD_CACHE)
        dataCount = 0
        if os.path.isfile(buildCache):
            logger.debug("Loading build cache")
            try:
                with open(buildCache, mode="r", encoding="utf-8") as inFile:
                    theJson = inFile.read()
                theData = json.loads(theJson)
            except Exception:
                logger.error("Failed to load build cache")
                logException()
                return False

            if "buildTime" in theData.keys():
                self.buildTime = theData["buildTime"]
            if "htmlStyle" in theData.keys():
                self.htmlStyle = theData["htmlStyle"]
                dataCount += 1
            if "htmlText" in theData.keys():
                self.htmlText = theData["htmlText"]
                dataCount += 1

        return dataCount == 2

    def _saveCache(self):
        """Save the current data to cache.
        """
        buildCache = os.path.join(self.theProject.projCache, nwFiles.BUILD_CACHE)
        logger.debug("Saving build cache")
        try:
            with open(buildCache, mode="w+", encoding="utf-8") as outFile:
                outFile.write(json.dumps({
                    "buildTime": self.buildTime,
                    "htmlStyle": self.htmlStyle,
                    "htmlText": self.htmlText,
                }, indent=2))
        except Exception:
            logger.error("Failed to save build cache")
            logException()
            return False

        return True

    def _doClose(self):
        """Close button was clicked.
        """
        self.close()
        return

    ##
    #  Events
    ##

    def closeEvent(self, theEvent):
        """Capture the user closing the window so we can save settings.
        """
        self._saveSettings()
        self.docView.clear()
        theEvent.accept()
        return

    ##
    #  Internal Functions
    ##

    def _saveSettings(self):
        """Save the various user settings.
        """
        logger.debug("Saving GuiBuildNovel settings")

        # Formatting
        self.theProject.setTitleFormat({
            "title":      self.fmtTitle.text().strip(),
            "chapter":    self.fmtChapter.text().strip(),
            "unnumbered": self.fmtUnnumbered.text().strip(),
            "scene":      self.fmtScene.text().strip(),
            "section":    self.fmtSection.text().strip(),
        })

        buildLang    = self.buildLang.currentData()
        hideScene    = self.hideScene.isChecked()
        hideSection  = self.hideSection.isChecked()
        winWidth     = self.mainConf.rpxInt(self.width())
        winHeight    = self.mainConf.rpxInt(self.height())
        justifyText  = self.justifyText.isChecked()
        noStyling    = self.noStyling.isChecked()
        textFont     = self.textFont.text()
        textSize     = self.textSize.value()
        lineHeight   = self.lineHeight.value()
        novelFiles   = self.novelFiles.isChecked()
        noteFiles    = self.noteFiles.isChecked()
        ignoreFlag   = self.ignoreFlag.isChecked()
        incSynopsis  = self.includeSynopsis.isChecked()
        incComments  = self.includeComments.isChecked()
        incKeywords  = self.includeKeywords.isChecked()
        incBodyText  = self.includeBody.isChecked()
        replaceTabs  = self.replaceTabs.isChecked()
        replaceUCode = self.replaceUCode.isChecked()

        mainSplit = self.mainSplit.sizes()
        boxWidth  = self.mainConf.rpxInt(mainSplit[0])
        docWidth  = self.mainConf.rpxInt(mainSplit[1])

        self.theProject.setProjectLang(buildLang)

        # GUI Settings
        pOptions = self.theProject.options
        pOptions.setValue("GuiBuildNovel", "hideScene",    hideScene)
        pOptions.setValue("GuiBuildNovel", "hideSection",  hideSection)
        pOptions.setValue("GuiBuildNovel", "winWidth",     winWidth)
        pOptions.setValue("GuiBuildNovel", "winHeight",    winHeight)
        pOptions.setValue("GuiBuildNovel", "boxWidth",     boxWidth)
        pOptions.setValue("GuiBuildNovel", "docWidth",     docWidth)
        pOptions.setValue("GuiBuildNovel", "justifyText",  justifyText)
        pOptions.setValue("GuiBuildNovel", "noStyling",    noStyling)
        pOptions.setValue("GuiBuildNovel", "textFont",     textFont)
        pOptions.setValue("GuiBuildNovel", "textSize",     textSize)
        pOptions.setValue("GuiBuildNovel", "lineHeight",   lineHeight)
        pOptions.setValue("GuiBuildNovel", "addNovel",     novelFiles)
        pOptions.setValue("GuiBuildNovel", "addNotes",     noteFiles)
        pOptions.setValue("GuiBuildNovel", "ignoreFlag",   ignoreFlag)
        pOptions.setValue("GuiBuildNovel", "incSynopsis",  incSynopsis)
        pOptions.setValue("GuiBuildNovel", "incComments",  incComments)
        pOptions.setValue("GuiBuildNovel", "incKeywords",  incKeywords)
        pOptions.setValue("GuiBuildNovel", "incBodyText",  incBodyText)
        pOptions.setValue("GuiBuildNovel", "replaceTabs",  replaceTabs)
        pOptions.setValue("GuiBuildNovel", "replaceUCode", replaceUCode)
        pOptions.saveSettings()

        return

    def _reFmtCodes(self, theFormat):
        """Translates old formatting codes to new ones.
        """
        theFormat = theFormat.replace(r"%chnum%",     r"%ch%")
        theFormat = theFormat.replace(r"%scnum%",     r"%sc%")
        theFormat = theFormat.replace(r"%scabsnum%",  r"%sca%")
        theFormat = theFormat.replace(r"%chnumword%", r"%chw%")
        return theFormat
Пример #30
0
class GuiConfigEditEditingTab(QWidget):

    def __init__(self, theParent):
        QWidget.__init__(self, theParent)

        self.mainConf  = nw.CONFIG
        self.theParent = theParent
        self.theTheme  = theParent.theTheme

        # The Form
        self.mainForm = QConfigLayout()
        self.mainForm.setHelpTextStyle(self.theTheme.helpText)
        self.setLayout(self.mainForm)

        # Spell Checking
        # ==============
        self.mainForm.addGroupLabel("Syntax Highlighting")

        ## Syntax Highlighting
        self.selectSyntax = QComboBox()
        self.selectSyntax.setMinimumWidth(self.mainConf.pxInt(200))
        self.theSyntaxes = self.theTheme.listSyntax()
        for syntaxFile, syntaxName in self.theSyntaxes:
            self.selectSyntax.addItem(syntaxName, syntaxFile)
        syntaxIdx = self.selectSyntax.findData(self.mainConf.guiSyntax)
        if syntaxIdx != -1:
            self.selectSyntax.setCurrentIndex(syntaxIdx)

        self.mainForm.addRow(
            "Highlight theme",
            self.selectSyntax,
            ""
        )

        self.highlightQuotes = QSwitch()
        self.highlightQuotes.setChecked(self.mainConf.highlightQuotes)
        self.mainForm.addRow(
            "Highlight text wrapped in quotes",
            self.highlightQuotes,
            helpText="Applies to single, double and straight quotes."
        )

        self.highlightEmph = QSwitch()
        self.highlightEmph.setChecked(self.mainConf.highlightEmph)
        self.mainForm.addRow(
            "Add highlight colour to emphasised text",
            self.highlightEmph,
            helpText="Applies to emphasis, strong and strikethrough."
        )

        # Spell Checking
        # ==============
        self.mainForm.addGroupLabel("Spell Checking")

        ## Spell Check Provider and Language
        self.spellLangList = QComboBox(self)
        self.spellToolList = QComboBox(self)
        self.spellToolList.addItem("Internal (difflib)",        NWSpellCheck.SP_INTERNAL)
        self.spellToolList.addItem("Spell Enchant (pyenchant)", NWSpellCheck.SP_ENCHANT)

        theModel  = self.spellToolList.model()
        idEnchant = self.spellToolList.findData(NWSpellCheck.SP_ENCHANT)
        theModel.item(idEnchant).setEnabled(self.mainConf.hasEnchant)

        self.spellToolList.currentIndexChanged.connect(self._doUpdateSpellTool)
        toolIdx = self.spellToolList.findData(self.mainConf.spellTool)
        if toolIdx != -1:
            self.spellToolList.setCurrentIndex(toolIdx)
        self._doUpdateSpellTool(0)

        self.mainForm.addRow(
            "Spell check provider",
            self.spellToolList,
            "Note that the internal spell check tool is quite slow."
        )
        self.mainForm.addRow(
            "Spell check language",
            self.spellLangList
        )

        ## Big Document Size Limit
        self.bigDocLimit = QSpinBox(self)
        self.bigDocLimit.setMinimum(10)
        self.bigDocLimit.setMaximum(10000)
        self.bigDocLimit.setSingleStep(10)
        self.bigDocLimit.setValue(self.mainConf.bigDocLimit)
        self.mainForm.addRow(
            "Big document limit",
            self.bigDocLimit,
            "Disables full spell checking over the size limit.",
            theUnit="kB"
        )

        # Writing Guides
        # ==============
        self.mainForm.addGroupLabel("Writing Guides")

        ## Show Tabs and Spaces
        self.showTabsNSpaces = QSwitch()
        self.showTabsNSpaces.setChecked(self.mainConf.showTabsNSpaces)
        self.mainForm.addRow(
            "Show tabs and spaces",
            self.showTabsNSpaces
        )

        ## Show Line Endings
        self.showLineEndings = QSwitch()
        self.showLineEndings.setChecked(self.mainConf.showLineEndings)
        self.mainForm.addRow(
            "Show line endings",
            self.showLineEndings
        )

        return

    def saveValues(self):
        """Save the values set for this tab.
        """
        validEntries = True
        needsRestart = False

        guiSyntax       = self.selectSyntax.currentData()
        highlightQuotes = self.highlightQuotes.isChecked()
        highlightEmph   = self.highlightEmph.isChecked()
        spellTool       = self.spellToolList.currentData()
        spellLanguage   = self.spellLangList.currentData()
        bigDocLimit     = self.bigDocLimit.value()
        showTabsNSpaces = self.showTabsNSpaces.isChecked()
        showLineEndings = self.showLineEndings.isChecked()

        self.mainConf.guiSyntax       = guiSyntax
        self.mainConf.highlightQuotes = highlightQuotes
        self.mainConf.highlightEmph   = highlightEmph
        self.mainConf.spellTool       = spellTool
        self.mainConf.spellLanguage   = spellLanguage
        self.mainConf.bigDocLimit     = bigDocLimit
        self.mainConf.showTabsNSpaces = showTabsNSpaces
        self.mainConf.showLineEndings = showLineEndings

        self.mainConf.confChanged = True

        return validEntries, needsRestart

    ##
    #  Internal Functions
    ##

    def _disableComboItem(self, theList, theValue):
        """Disable a list item in the combo box.
        """
        theModel = theList.model()
        anItem = theModel.item(1)
        anItem.setFlags(anItem.flags() ^ Qt.ItemIsEnabled)
        return theModel

    def _doUpdateSpellTool(self, currIdx):
        """Update the list of dictionaries based on spell tool selected.
        """
        spellTool = self.spellToolList.currentData()
        self._updateLanguageList(spellTool)
        return

    def _updateLanguageList(self, spellTool):
        """Updates the list of available spell checking dictionaries
        available for the selected spell check tool. It will try to
        preserve the language choice, if the language exists in the
        updated list.
        """
        if spellTool == NWSpellCheck.SP_ENCHANT:
            theDict = NWSpellEnchant()
        else:
            theDict = NWSpellSimple()

        self.spellLangList.clear()
        for spTag, spName in theDict.listDictionaries():
            self.spellLangList.addItem(spName, spTag)

        spellIdx = self.spellLangList.findData(self.mainConf.spellLanguage)
        if spellIdx != -1:
            self.spellLangList.setCurrentIndex(spellIdx)

        return
Пример #31
0
class OWCSVFileImport(widget.OWWidget):
    name = "CSV导入(CSV File Import)"
    description = "从csv格式文件导入数据表。"
    icon = "icons/CSVFile.svg"
    priority = 11
    category = "Data"
    keywords = ["file", "load", "read", "open", "csv"]

    outputs = [
        widget.OutputSignal(
            name="数据(Data)",
            type=Orange.data.Table,
            doc="Loaded data set.",
            replaces=['Data']),
        widget.OutputSignal(
            name="数据帧格式(Data Frame)",
            type=pd.DataFrame,
            doc="",
            replaces=['Data Frame']
        )
    ]

    class Error(widget.OWWidget.Error):
        error = widget.Msg(
            "Unexpected error"
        )
        encoding_error = widget.Msg(
            "Encoding error\n"
            "The file might be encoded in an unsupported encoding or it "
            "might be binary"
        )

    #: Paths and options of files accessed in a 'session'
    _session_items = settings.Setting(
        [], schema_only=True)  # type: List[Tuple[str, dict]]

    #: Saved dialog state (last directory and selected filter)
    dialog_state = settings.Setting({
        "directory": "",
        "filter": ""
    })  # type: Dict[str, str]
    MaxHistorySize = 50

    want_main_area = False
    buttons_area_orientation = None

    def __init__(self, *args, **kwargs):
        super().__init__(self, *args, **kwargs)

        self.__committimer = QTimer(self, singleShot=True)
        self.__committimer.timeout.connect(self.commit)

        self.__executor = qconcurrent.ThreadExecutor()
        self.__watcher = None  # type: Optional[qconcurrent.FutureWatcher]

        self.controlArea.layout().setSpacing(-1)  # reset spacing
        grid = QGridLayout()
        grid.addWidget(QLabel("文件(File):", self), 0, 0, 1, 1)

        self.import_items_model = QStandardItemModel(self)
        self.recent_combo = QComboBox(
            self, objectName="recent-combo", toolTip="Recent files.",
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=16,
        )
        self.recent_combo.setModel(self.import_items_model)
        self.recent_combo.activated.connect(self.activate_recent)
        self.recent_combo.setSizePolicy(
            QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
        self.browse_button = QPushButton(
            "…", icon=self.style().standardIcon(QStyle.SP_DirOpenIcon),
            toolTip="Browse filesystem", autoDefault=False,
        )
        self.browse_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.browse_button.clicked.connect(self.browse)
        grid.addWidget(self.recent_combo, 0, 1, 1, 1)
        grid.addWidget(self.browse_button, 0, 2, 1, 1)
        self.controlArea.layout().addLayout(grid)

        ###########
        # Info text
        ###########
        box = gui.widgetBox(self.controlArea, "信息", addSpace=False)
        self.summary_text = QTextBrowser(
            verticalScrollBarPolicy=Qt.ScrollBarAsNeeded,
            readOnly=True,
        )
        self.summary_text.viewport().setBackgroundRole(QPalette.NoRole)
        self.summary_text.setFrameStyle(QTextBrowser.NoFrame)
        self.summary_text.setMinimumHeight(self.fontMetrics().ascent() * 2 + 4)
        self.summary_text.viewport().setAutoFillBackground(False)
        box.layout().addWidget(self.summary_text)

        button_box = QDialogButtonBox(
            orientation=Qt.Horizontal,
            standardButtons=QDialogButtonBox.Cancel | QDialogButtonBox.Retry
        )
        self.load_button = b = button_box.button(QDialogButtonBox.Retry)
        b.setText("载入")
        b.clicked.connect(self.__committimer.start)
        b.setEnabled(False)
        b.setDefault(True)

        self.cancel_button = b = button_box.button(QDialogButtonBox.Cancel)
        b.clicked.connect(self.cancel)
        b.setEnabled(False)
        b.setAutoDefault(False)

        self.import_options_button = QPushButton(
            "导入选项", enabled=False, autoDefault=False,
            clicked=self._activate_import_dialog
        )

        def update_buttons(cbindex):
            self.import_options_button.setEnabled(cbindex != -1)
            self.load_button.setEnabled(cbindex != -1)
        self.recent_combo.currentIndexChanged.connect(update_buttons)

        button_box.addButton(
            self.import_options_button, QDialogButtonBox.ActionRole
        )
        button_box.setStyleSheet(
            "button-layout: {:d};".format(QDialogButtonBox.MacLayout)
        )
        self.controlArea.layout().addWidget(button_box)
        self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum)

        self._restoreState()
        item = self.current_item()
        if item is not None:
            self.set_selected_file(item.path(), item.options())

    @Slot(int)
    def activate_recent(self, index):
        """
        Activate an item from the recent list.
        """
        if 0 <= index < self.import_items_model.rowCount():
            item = self.import_items_model.item(index)
            assert item is not None
            path = item.data(ImportItem.PathRole)
            opts = item.data(ImportItem.OptionsRole)
            if not isinstance(opts, Options):
                opts = None
            self.set_selected_file(path, opts)
        else:
            self.recent_combo.setCurrentIndex(-1)

    @Slot()
    def browse(self):
        """
        Open a file dialog and select a user specified file.
        """
        formats = [
            "Text - comma separated (*.csv, *)",
            "Text - tab separated (*.tsv, *)",
            "Text - all files (*)"
        ]

        dlg = QFileDialog(
            self, windowTitle="打开数据文件",
            acceptMode=QFileDialog.AcceptOpen,
            fileMode=QFileDialog.ExistingFile
        )
        dlg.setNameFilters(formats)
        state = self.dialog_state
        lastdir = state.get("directory", "")
        lastfilter = state.get("filter", "")

        if lastdir and os.path.isdir(lastdir):
            dlg.setDirectory(lastdir)
        if lastfilter:
            dlg.selectNameFilter(lastfilter)

        status = dlg.exec_()
        dlg.deleteLater()
        if status == QFileDialog.Accepted:
            self.dialog_state["directory"] = dlg.directory().absolutePath()
            self.dialog_state["filter"] = dlg.selectedNameFilter()

            selected_filter = dlg.selectedNameFilter()
            path = dlg.selectedFiles()[0]
            # pre-flight check; try to determine the nature of the file
            mtype = _mime_type_for_path(path)
            if not mtype.inherits("text/plain"):
                mb = QMessageBox(
                    parent=self,
                    windowTitle="",
                    icon=QMessageBox.Question,
                    text="The '{basename}' may be a binary file.\n"
                         "Are you sure you want to continue?".format(
                             basename=os.path.basename(path)),
                    standardButtons=QMessageBox.Cancel | QMessageBox.Yes
                )
                mb.setWindowModality(Qt.WindowModal)
                if mb.exec() == QMessageBox.Cancel:
                    return

            # initialize dialect based on selected extension
            if selected_filter in formats[:-1]:
                filter_idx = formats.index(selected_filter)
                if filter_idx == 0:
                    dialect = csv.excel()
                elif filter_idx == 1:
                    dialect = csv.excel_tab()
                else:
                    dialect = csv.excel_tab()
                header = True
            else:
                try:
                    dialect, header = sniff_csv_with_path(path)
                except Exception:  # pylint: disable=broad-except
                    dialect, header = csv.excel(), True

            options = None
            # Search for path in history.
            # If found use the stored params to initialize the import dialog
            items = self.itemsFromSettings()
            idx = index_where(items, lambda t: samepath(t[0], path))
            if idx is not None:
                _, options_ = items[idx]
                if options_ is not None:
                    options = options_

            if options is None:
                if not header:
                    rowspec = []
                else:
                    rowspec = [(range(0, 1), RowSpec.Header)]
                options = Options(
                    encoding="utf-8", dialect=dialect, rowspec=rowspec)

            dlg = CSVImportDialog(
                self, windowTitle="Import Options", sizeGripEnabled=True)
            dlg.setWindowModality(Qt.WindowModal)
            dlg.setPath(path)
            dlg.setOptions(options)
            status = dlg.exec_()
            dlg.deleteLater()
            if status == QDialog.Accepted:
                self.set_selected_file(path, dlg.options())

    def current_item(self):
        # type: () -> Optional[ImportItem]
        """
        Return the current selected item (file) or None if there is no
        current item.
        """
        idx = self.recent_combo.currentIndex()
        if idx == -1:
            return None

        item = self.recent_combo.model().item(idx)  # type: QStandardItem
        if isinstance(item, ImportItem):
            return item
        else:
            return None

    def _activate_import_dialog(self):
        """Activate the Import Options dialog for the  current item."""
        item = self.current_item()
        assert item is not None
        dlg = CSVImportDialog(
            self, windowTitle="导入选项", sizeGripEnabled=True,
        )
        dlg.setWindowModality(Qt.WindowModal)
        dlg.setAttribute(Qt.WA_DeleteOnClose)
        settings = QSettings()
        qualname = qname(type(self))
        settings.beginGroup(qualname)
        size = settings.value("size", QSize(), type=QSize)  # type: QSize
        if size.isValid():
            dlg.resize(size)

        path = item.data(ImportItem.PathRole)
        options = item.data(ImportItem.OptionsRole)
        dlg.setPath(path)  # Set path before options so column types can
        if isinstance(options, Options):
            dlg.setOptions(options)

        def update():
            newoptions = dlg.options()
            item.setData(newoptions, ImportItem.OptionsRole)
            # update the stored item
            self._add_recent(path, newoptions)
            if newoptions != options:
                self._invalidate()
        dlg.accepted.connect(update)

        def store_size():
            settings.setValue("size", dlg.size())
        dlg.finished.connect(store_size)
        dlg.show()

    def set_selected_file(self, filename, options=None):
        """
        Set the current selected filename path.
        """
        self._add_recent(filename, options)
        self._invalidate()

    #: Saved options for a filename
    SCHEMA = {
        "path": str,  # Local filesystem path
        "options": str,  # json encoded 'Options'
    }

    @classmethod
    def _local_settings(cls):
        # type: () -> QSettings
        """Return a QSettings instance with local persistent settings."""
        filename = "{}.ini".format(qname(cls))
        fname = os.path.join(settings.widget_settings_dir(), filename)
        return QSettings(fname, QSettings.IniFormat)

    def _add_recent(self, filename, options=None):
        # type: (str, Optional[Options]) -> None
        """
        Add filename to the list of recent files.
        """
        model = self.import_items_model
        index = index_where(
            (model.index(i, 0).data(ImportItem.PathRole)
             for i in range(model.rowCount())),
            lambda path: isinstance(path, str) and samepath(path, filename)
        )
        if index is not None:
            item, *_ = model.takeRow(index)
        else:
            item = ImportItem.fromPath(filename)

        model.insertRow(0, item)

        if options is not None:
            item.setOptions(options)

        self.recent_combo.setCurrentIndex(0)
        # store items to local persistent settings
        s = self._local_settings()
        arr = QSettings_readArray(s, "recent", OWCSVFileImport.SCHEMA)
        item = {"path": filename}
        if options is not None:
            item["options"] = json.dumps(options.as_dict())

        arr = [item for item in arr if item.get("path") != filename]
        arr.append(item)
        QSettings_writeArray(s, "recent", arr)

        # update workflow session items
        items = self._session_items[:]
        idx = index_where(items, lambda t: samepath(t[0], filename))
        if idx is not None:
            del items[idx]
        items.insert(0, (filename, options.as_dict()))
        self._session_items = items[:OWCSVFileImport.MaxHistorySize]

    def _invalidate(self):
        # Invalidate the current output and schedule a new commit call.
        # (NOTE: The widget enters a blocking state)
        self.__committimer.start()
        if self.__watcher is not None:
            self.__cancel_task()
        self.setBlocking(True)

    def commit(self):
        """
        Commit the current state and submit the load task for execution.

        Note
        ----
        Any existing pending task is canceled.
        """
        self.__committimer.stop()
        if self.__watcher is not None:
            self.__cancel_task()
        self.error()

        item = self.current_item()
        if item is None:
            return
        path = item.data(ImportItem.PathRole)
        opts = item.data(ImportItem.OptionsRole)
        if not isinstance(opts, Options):
            return

        task = state = TaskState()
        state.future = ...
        state.watcher = qconcurrent.FutureWatcher()
        state.progressChanged.connect(
            self.__set_read_progress, Qt.DirectConnection)

        def progress_(i, j):
            task.emitProgressChangedOrCancel(i, j)

        task.future = self.__executor.submit(
            clear_stack_on_cancel(load_csv),
            path, opts, progress_,
        )
        task.watcher.setFuture(task.future)
        w = task.watcher
        w.done.connect(self.__handle_result)
        w.progress = state
        self.__watcher = w
        self.__set_running_state()

    @Slot('qint64', 'qint64')
    def __set_read_progress(self, read, count):
        if count > 0:
            self.progressBarSet(100 * read / count)

    def __cancel_task(self):
        # Cancel and dispose of the current task
        assert self.__watcher is not None
        w = self.__watcher
        w.future().cancel()
        w.progress.cancel = True
        w.done.disconnect(self.__handle_result)
        w.progress.progressChanged.disconnect(self.__set_read_progress)
        w.progress.deleteLater()
        # wait until completion
        futures.wait([w.future()])
        self.__watcher = None

    def cancel(self):
        """
        Cancel current pending or executing task.
        """
        if self.__watcher is not None:
            self.__cancel_task()
            self.__clear_running_state()
            self.setStatusMessage("Cancelled")
            self.summary_text.setText(
                "<div>Cancelled<br/><small>Press 'Reload' to try again</small></div>"
            )

    def __set_running_state(self):
        self.progressBarInit()
        self.setBlocking(True)
        self.setStatusMessage("Running")
        self.cancel_button.setEnabled(True)
        self.load_button.setText("Restart")
        path = self.current_item().path()
        self.Error.clear()
        self.summary_text.setText(
            "<div>Loading: <i>{}</i><br/>".format(prettyfypath(path))
        )

    def __clear_running_state(self, ):
        self.progressBarFinished()
        self.setStatusMessage("")
        self.setBlocking(False)
        self.cancel_button.setEnabled(False)
        self.load_button.setText("重新加载")

    def __set_error_state(self, err):
        self.Error.clear()
        if isinstance(err, UnicodeDecodeError):
            self.Error.encoding_error(exc_info=err)
        else:
            self.Error.error(exc_info=err)

        path = self.current_item().path()
        basename = os.path.basename(path)
        if isinstance(err, UnicodeDecodeError):
            text = (
                "<div><i>{basename}</i> was not loaded due to a text encoding "
                "error. The file might be saved in an unknown or invalid "
                "encoding, or it might be a binary file.</div>"
            ).format(
                basename=escape(basename)
            )
        else:
            text = (
                "<div><i>{basename}</i> was not loaded due to an error:"
                "<p style='white-space: pre;'>{err}</p>"
            ).format(
                basename=escape(basename),
                err="".join(traceback.format_exception_only(type(err), err))
            )
        self.summary_text.setText(text)

    def __clear_error_state(self):
        self.Error.error.clear()
        self.summary_text.setText("")

    def onDeleteWidget(self):
        """Reimplemented."""
        if self.__watcher is not None:
            self.__cancel_task()
            self.__executor.shutdown()
        super().onDeleteWidget()

    @Slot(object)
    def __handle_result(self, f):
        # type: (qconcurrent.Future[pd.DataFrame]) -> None
        assert f.done()
        assert f is self.__watcher.future()
        self.__watcher = None
        self.__clear_running_state()

        try:
            df = f.result()
            assert isinstance(df, pd.DataFrame)
        except pandas.errors.EmptyDataError:
            df = pd.DataFrame({})
        except Exception as e:  # pylint: disable=broad-except
            self.__set_error_state(e)
            df = None
        else:
            self.__clear_error_state()

        if df is not None:
            table = pandas_to_table(df)
        else:
            table = None
        self.send("数据帧格式(Data Frame)", df)
        self.send('数据(Data)', table)
        self._update_status_messages(table)

    def _update_status_messages(self, data):
        if data is None:
            return

        def pluralize(seq):
            return "" if len(seq) != 1 else ""

        summary = ("{n_instances} 行{plural_1}, "
                   "{n_features} 个特征{plural_2}, "
                   "{n_meta} 个meta{plural_3}").format(
                       n_instances=len(data), plural_1=pluralize(data),
                       n_features=len(data.domain.attributes),
                       plural_2=pluralize(data.domain.attributes),
                       n_meta=len(data.domain.metas),
                       plural_3=pluralize(data.domain.metas))
        self.summary_text.setText(summary)

    def itemsFromSettings(self):
        # type: () -> List[Tuple[str, Options]]
        """
        Return items from local history.
        """
        s = self._local_settings()
        items_ = QSettings_readArray(s, "recent", OWCSVFileImport.SCHEMA)
        items = []  # type: List[Tuple[str, Options]]
        for item in items_:
            path = item.get("path", "")
            if not path:
                continue
            opts_json = item.get("options", "")
            try:
                opts = Options.from_dict(json.loads(opts_json))
            except (csv.Error, LookupError, TypeError, json.JSONDecodeError):
                _log.error("Could not reconstruct options for '%s'", path,
                           exc_info=True)
            else:
                items.append((path, opts))
        return items[::-1]

    def _restoreState(self):
        # Restore the state. Merge session (workflow) items with the
        # local history.
        model = self.import_items_model
        # local history
        items = self.itemsFromSettings()
        # stored session items
        sitems = []
        for p, m in self._session_items:
            try:
                item_ = (p, Options.from_dict(m))
            except (csv.Error, LookupError):
                # Is it better to fail then to lose a item slot?
                _log.error("Failed to restore '%s'", p, exc_info=True)
            else:
                sitems.append(item_)

        items = sitems + items
        items = unique(items, key=lambda t: pathnormalize(t[0]))

        curr = self.recent_combo.currentIndex()
        if curr != -1:
            currentpath = self.recent_combo.currentData(ImportItem.PathRole)
        else:
            currentpath = None
        for path, options in items:
            item = ImportItem.fromPath(path)
            item.setOptions(options)
            model.appendRow(item)

        if currentpath is not None:
            idx = self.recent_combo.findData(currentpath, ImportItem.PathRole)
            if idx != -1:
                self.recent_combo.setCurrentIndex(idx)
class ExportWindow(QDialog):
    """
    Class describing a dialog for exports of schedules.
    """
    def __init__(self, schedule: Schedule, parent: QWidget = None):
        super().__init__(parent)
        self._schedule_ref = schedule
        self._date_start_cache = None

        # window settings
        self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
        self.setWindowTitle(self.tr("Export window"))
        self.setMinimumWidth(800)

        # title, add_date, work mode
        self.layout_title_date_mode = QHBoxLayout()

        self.label_title = QLabel(self.tr("Title"))
        self.layout_title_date_mode.addWidget(self.label_title)

        self.line_edit_title = QLineEdit()
        self.layout_title_date_mode.addWidget(self.line_edit_title)

        self.check_box_add_date = QCheckBox(self.tr("Add date"))
        self.layout_title_date_mode.addWidget(self.check_box_add_date)

        self.combo_box_work_mode = QComboBox()
        self.layout_title_date_mode.addWidget(self.combo_box_work_mode)

        self.line_edit_title.setPlaceholderText(
            self.tr("My Group. A subgroup - green color, "
                    "B subgroup - yellow color. "))
        self.check_box_add_date.setChecked(True)

        self.combo_box_work_mode.addItem(self.tr("Weekly"))
        self.combo_box_work_mode.addItem(self.tr("Full"))

        # list widget with files
        self.layout_list_widget = QVBoxLayout()

        self.check_box_use_current = QCheckBox(self.tr("Use current schedule"))
        self.layout_list_widget.addWidget(self.check_box_use_current)
        self.check_box_use_current.setChecked(True)

        self.list_widget = QListWidget()
        self.layout_list_widget.addWidget(self.list_widget)
        self.list_widget.setSortingEnabled(True)
        self.list_widget.setEnabled(False)

        self.layout_open_folder = QHBoxLayout()
        self.layout_list_widget.addLayout(self.layout_open_folder)

        self.label_find = QLabel(self.tr("Schedules: ") + "0")
        self.layout_open_folder.addWidget(self.label_find)

        self.layout_open_folder.addStretch(1)

        self.push_button_open_folder = QToolButton()
        self.layout_open_folder.addWidget(self.push_button_open_folder)
        self.push_button_open_folder.setEnabled(False)

        self.push_button_open_folder.setText(self.tr("Open folder"))
        self.push_button_open_folder.setPopupMode(QToolButton.MenuButtonPopup)

        self.action_open_files = QAction(self.tr("Open files"))
        self.push_button_open_folder.addAction(self.action_open_files)

        # font edit
        self.group_box_font = QGroupBox(self.tr("Font settings"))
        self.form_layout_font = QFormLayout(self.group_box_font)

        self.label_font = QLabel(self.tr("Font"))
        self.form_layout_font.setWidget(0, QFormLayout.LabelRole,
                                        self.label_font)
        self.combo_box_font = QComboBox()
        self.form_layout_font.setWidget(0, QFormLayout.FieldRole,
                                        self.combo_box_font)

        self.label_encoding = QLabel(self.tr("Encoding"))
        self.form_layout_font.setWidget(1, QFormLayout.LabelRole,
                                        self.label_encoding)
        self.combo_box_encoding = QComboBox()
        self.form_layout_font.setWidget(1, QFormLayout.FieldRole,
                                        self.combo_box_encoding)

        for font_name, font_path in util.get_fonts():
            self.combo_box_font.addItem(font_name, font_path)

        self.combo_box_font.setCurrentText(qApp.font().family())
        self.combo_box_font.setEditable(True)

        self.combo_box_encoding.addItem("UTF-8")
        self.combo_box_encoding.addItem("Latin-1")
        self.combo_box_encoding.addItem("Windows-1252")

        # date edit
        self.group_box_date = QGroupBox(self.tr("Date settings"))
        self.form_layout_date = QFormLayout(self.group_box_date)

        self.label_date_start = QLabel(self.tr("Start"))
        self.form_layout_date.setWidget(0, QFormLayout.LabelRole,
                                        self.label_date_start)
        self.date_edit_start = QDateEdit()
        self.form_layout_date.setWidget(0, QFormLayout.FieldRole,
                                        self.date_edit_start)

        self.label_date_end = QLabel(self.tr("End"))
        self.form_layout_date.setWidget(1, QFormLayout.LabelRole,
                                        self.label_date_end)
        self.date_edit_end = QDateEdit()
        self.form_layout_date.setWidget(1, QFormLayout.FieldRole,
                                        self.date_edit_end)

        self.date_edit_start.setCalendarPopup(True)
        self.date_edit_end.setCalendarPopup(True)

        if QDate.currentDate().day() < (QDate.currentDate().dayOfYear() / 2):
            date = QDate(QDate.currentDate().year(), 2, 1)
        else:
            date = QDate(QDate.currentDate().year(), 9, 1)

        self._date_start_cache = date.addDays(8 - date.dayOfWeek())
        self.date_edit_start.setDate(self._date_start_cache)
        self.date_edit_end.setMinimumDate(self._date_start_cache.addDays(7))
        self.date_edit_end.setDate(self._date_start_cache.addDays(16 * 7))

        # subgroup edit
        self.group_box_subgroup = QGroupBox(self.tr("Subgroup settings"))
        self.form_layout_subgroup = QFormLayout(self.group_box_subgroup)

        self.label_color_a = QLabel(self.tr("Color A"))
        self.form_layout_subgroup.setWidget(0, QFormLayout.LabelRole,
                                            self.label_color_a)
        self.combo_box_color_a = QComboBox()
        self.form_layout_subgroup.setWidget(0, QFormLayout.FieldRole,
                                            self.combo_box_color_a)

        self.label_color_b = QLabel(self.tr("Color B"))
        self.form_layout_subgroup.setWidget(1, QFormLayout.LabelRole,
                                            self.label_color_b)
        self.combo_box_color_b = QComboBox()
        self.form_layout_subgroup.setWidget(1, QFormLayout.FieldRole,
                                            self.combo_box_color_b)

        self.label_pattern_a_b = QLabel(self.tr("Pattern A and B"))
        self.form_layout_subgroup.setWidget(2, QFormLayout.LabelRole,
                                            self.label_pattern_a_b)
        self.combo_box_pattern_a_b = QComboBox()
        self.form_layout_subgroup.setWidget(2, QFormLayout.FieldRole,
                                            self.combo_box_pattern_a_b)

        self.add_standard_colors(self.combo_box_color_a)
        self.add_standard_colors(self.combo_box_color_b)
        self.combo_box_color_a.setCurrentIndex(9)  # lime
        self.combo_box_color_b.setCurrentIndex(15)  # yellow

        self.combo_box_pattern_a_b.addItem(self.tr("Chess order"))
        self.combo_box_pattern_a_b.setEnabled(False)

        # navigate buttons
        self.layout_navigate = QHBoxLayout()

        self.layout_navigate.addStretch(1)

        self.push_button_export = QPushButton(self.tr("Export"))
        self.layout_navigate.addWidget(self.push_button_export)

        self.push_button_cancel = QPushButton(self.tr("Cancel"))
        self.layout_navigate.addWidget(self.push_button_cancel)

        # layout setup
        self.layout_right_setting = QVBoxLayout()
        self.layout_right_setting.addWidget(self.group_box_font)
        self.layout_right_setting.addWidget(self.group_box_date)
        self.layout_right_setting.addWidget(self.group_box_subgroup)
        self.layout_right_setting.addStretch(1)

        self.layout_center = QHBoxLayout()
        self.layout_center.addLayout(self.layout_list_widget)
        self.layout_center.addLayout(self.layout_right_setting)

        self.layout_main = QVBoxLayout()
        self.layout_main.addLayout(self.layout_title_date_mode)
        self.layout_main.addLayout(self.layout_center)
        self.layout_main.addLayout(self.layout_navigate)

        self.setLayout(self.layout_main)

        # connection
        self.check_box_use_current.clicked.connect(
            self.check_box_use_current_clicked)
        self.push_button_open_folder.clicked.connect(self.open_folder_clicked)
        self.action_open_files.triggered.connect(self.open_files_clicked)

        self.date_edit_start.dateChanged.connect(self.date_edit_start_changed)
        self.combo_box_color_a.activated.connect(
            self.combo_box_color_a_clicked)
        self.combo_box_color_b.activated.connect(
            self.combo_box_color_b_clicked)
        self.push_button_export.clicked.connect(self.export_to_pdf)
        self.push_button_cancel.clicked.connect(self.close)

    def add_standard_colors(self, combo_box: QComboBox) -> None:
        """
        Adds colors to the color selection menu.

        :param combo_box: Color selection menu
        """
        color_items = [(self.tr("Custom color"), QColor()),
                       (self.tr("Aqua"), QColor(0, 255, 255)),
                       (self.tr("Grey"), QColor(128, 128, 128)),
                       (self.tr("Navy"), QColor(0, 0, 192)),
                       (self.tr("Silver"), QColor(192, 192, 192)),
                       (self.tr("Black"), QColor(0, 0, 0)),
                       (self.tr("Green"), QColor(0, 128, 0)),
                       (self.tr("Olive"), QColor(192, 192, 0)),
                       (self.tr("Blue"), QColor(0, 0, 255)),
                       (self.tr("Lime"), QColor(0, 255, 0)),
                       (self.tr("Purple"), QColor(128, 0, 128)),
                       (self.tr("White"), QColor(255, 255, 255)),
                       (self.tr("Fuchsia"), QColor(255, 0, 255)),
                       (self.tr("Maroon"), QColor(128, 0, 0)),
                       (self.tr("Red"), QColor(255, 0, 0)),
                       (self.tr("Yellow"), QColor(255, 255, 0))]

        for name, data in color_items:
            combo_box.addItem(util.create_color_icon(data), name, data)

    def export_to_pdf(self) -> None:
        # select path

        paths = []

        if self.check_box_use_current.isChecked():
            path = QFileDialog.getSaveFileName(self, self.tr("Export to pdf"),
                                               ".", "PDF file (*.pdf)")[0]
            if path == "":
                return

            if not path.endswith(".pdf"):
                path += ".pdf"

            paths.append(path)
        else:
            for index in range(self.list_widget.count()):
                path = self.list_widget.item(index).data(Qt.UserRole)
                paths.append(path)

        # progress dialog
        progress = QProgressDialog(self.tr("Export to pdf"),
                                   self.tr("Abort exports"), 0, 100, self)
        progress.setWindowModality(Qt.WindowModal)
        progress.setMinimumDuration(2000)

        try:
            for index, path in enumerate(paths):

                if self.check_box_use_current.isChecked():
                    title_text = self.line_edit_title.text()
                    schedule = self._schedule_ref
                else:
                    title_text = QFileInfo(path).baseName()
                    schedule = Schedule()
                    schedule.load(path)
                    path = path[0:-5] + ".pdf"

                print(path)
                mode = self.combo_box_work_mode.currentIndex()
                if mode == 0:
                    export_weeks_to_pdf(
                        schedule, title_text,
                        self.check_box_add_date.isChecked(), path,
                        self.combo_box_font.currentText(),
                        self.combo_box_font.currentData(Qt.UserRole),
                        self.combo_box_encoding.currentText(),
                        self.date_edit_start.date().toPyDate(),
                        self.date_edit_end.date().toPyDate(),
                        self.combo_box_color_a.currentData(Qt.UserRole),
                        self.combo_box_color_b.currentData(Qt.UserRole),
                        progress
                        if self.check_box_use_current.isChecked() else None)
                else:
                    export_full_to_pdf(
                        schedule, title_text, path,
                        self.combo_box_font.currentText(),
                        self.combo_box_font.currentData(Qt.UserRole),
                        self.combo_box_encoding.currentText(), progress
                        if self.check_box_use_current.isChecked() else None)

                progress.setValue(int(index * 100 / len(paths)))

            # finish dialog
            progress.setValue(100)
            finish_msg_box = QMessageBox(QMessageBox.Information,
                                         self.tr("Export to pdf"),
                                         self.tr("Gone!"))
            open_folder_button = finish_msg_box.addButton(
                self.tr("Open folder"), QMessageBox.ActionRole)
            finish_msg_box.addButton(QMessageBox.Ok)
            finish_msg_box.exec_()

            if finish_msg_box.clickedButton() == open_folder_button:
                QDesktopServices.openUrl(
                    QUrl(
                        QFileInfo(paths[0] if len(paths) != 0 else ".").
                        absolutePath()))

        except UnicodeEncodeError as ex:
            QMessageBox.critical(self, self.tr("Encoding error"), str(ex))
        except Exception as ex:
            QMessageBox.critical(self, self.tr("Unknown error"), str(ex))
        progress.setValue(100)

    def check_box_use_current_clicked(self, checked: bool):
        if checked:
            self.list_widget.setEnabled(False)
            self.push_button_open_folder.setEnabled(False)
            self.line_edit_title.setEnabled(False)
        else:
            self.list_widget.setEnabled(True)
            self.push_button_open_folder.setEnabled(True)
            self.line_edit_title.setEnabled(True)

    def open_folder_clicked(self):
        path = QFileDialog.getExistingDirectory(self, self.tr("Select folder"))

        provider = QFileIconProvider()
        self.list_widget.clear()

        for dir_path, dir_names, file_names in os.walk(path):
            for file_name in file_names:
                if file_name.endswith(".json"):
                    item = QListWidgetItem()
                    item.setText(file_name[0:-5])
                    item.setData(Qt.UserRole, dir_path + os.sep + file_name)
                    item.setIcon(
                        provider.icon(QFileInfo(dir_path + os.sep +
                                                file_name)))

                    self.list_widget.addItem(item)

        self.label_find.setText(
            self.tr("Schedules: ") + str(self.list_widget.count()))

    def open_files_clicked(self):
        files = QFileDialog.getOpenFileNames(
            self, self.tr("Select files"), "",
            "JSON file (*.json) ;; All files (*.*)")[0]
        provider = QFileIconProvider()
        self.list_widget.clear()

        for file_path in files:
            file = QFileInfo(file_path)
            item = QListWidgetItem()
            item.setText(file.baseName())
            item.setData(Qt.UserRole, file_path)
            item.setIcon(provider.icon(file))
            self.list_widget.addItem(item)

        self.label_find.setText(
            self.tr("Schedules: ") + str(self.list_widget.count()))

    def combo_box_color_a_clicked(self) -> None:
        """
        Slot for color selection of A subgroup.
        """
        if self.combo_box_color_a.currentIndex() == 0:
            self.custom_color_selected(self.combo_box_color_a)

    def combo_box_color_b_clicked(self) -> None:
        """
        Slot for color selection of B subgroup.
        """
        if self.combo_box_color_b.currentIndex() == 0:
            self.custom_color_selected(self.combo_box_color_b)

    def custom_color_selected(self, combo_box: QComboBox) -> None:
        """
        Slot to select the color for the desired menu.

        :param combo_box: Menu
        """
        color = QColorDialog.getColor(combo_box.currentData(), self)
        if color.isValid():
            combo_box.setItemIcon(0, util.create_color_icon(color))
            combo_box.setItemData(0, color)

    def date_edit_start_changed(self, date: QDate):
        """
        Slot for changing the end of a range of dates.

        :param date: Start of the date range
        """
        end_date = self.date_edit_end.date().addDays(
            self._date_start_cache.daysTo(date))
        self.date_edit_end.setMinimumDate(date.addDays(7))
        self.date_edit_end.setDate(end_date)
        self._date_start_cache = QDate(date)
Пример #33
0
class GuiItemEditor(QDialog):

    def __init__(self, theParent, theProject, tHandle):
        QDialog.__init__(self, theParent)

        logger.debug("Initialising GuiItemEditor ...")
        self.setObjectName("GuiItemEditor")

        self.mainConf   = nw.CONFIG
        self.theProject = theProject
        self.theParent  = theParent

        ##
        #  Build GUI
        ##

        self.theItem = self.theProject.projTree[tHandle]
        if self.theItem is None:
            self._doClose()

        self.setWindowTitle(self.tr("Item Settings"))

        mVd = self.mainConf.pxInt(220)
        mSp = self.mainConf.pxInt(16)
        vSp = self.mainConf.pxInt(4)

        # Item Label
        self.editName = QLineEdit()
        self.editName.setMinimumWidth(mVd)
        self.editName.setMaxLength(200)

        # Item Status
        self.editStatus = QComboBox()
        self.editStatus.setMinimumWidth(mVd)
        if self.theItem.itemClass in nwLists.CLS_NOVEL:
            for sLabel, _, _ in self.theProject.statusItems:
                self.editStatus.addItem(
                    self.theParent.statusIcons[sLabel], sLabel, sLabel
                )
        else:
            for sLabel, _, _ in self.theProject.importItems:
                self.editStatus.addItem(
                    self.theParent.importIcons[sLabel], sLabel, sLabel
                )

        # Item Layout
        self.editLayout = QComboBox()
        self.editLayout.setMinimumWidth(mVd)
        validLayouts = []
        if self.theItem.itemType == nwItemType.FILE:
            if self.theItem.itemClass in nwLists.CLS_NOVEL:
                validLayouts.append(nwItemLayout.TITLE)
                validLayouts.append(nwItemLayout.BOOK)
                validLayouts.append(nwItemLayout.PAGE)
                validLayouts.append(nwItemLayout.PARTITION)
                validLayouts.append(nwItemLayout.UNNUMBERED)
                validLayouts.append(nwItemLayout.CHAPTER)
                validLayouts.append(nwItemLayout.SCENE)
            validLayouts.append(nwItemLayout.NOTE)
        else:
            validLayouts.append(nwItemLayout.NO_LAYOUT)
            self.editLayout.setEnabled(False)

        for itemLayout in nwItemLayout:
            if itemLayout in validLayouts:
                self.editLayout.addItem(trConst(nwLabels.LAYOUT_NAME[itemLayout]), itemLayout)

        # Export Switch
        self.textExport = QLabel(self.tr("Include when building project"))
        self.editExport = QSwitch()
        if self.theItem.itemType == nwItemType.FILE:
            self.editExport.setEnabled(True)
            self.editExport.setChecked(self.theItem.isExported)
        else:
            self.editExport.setEnabled(False)
            self.editExport.setChecked(False)

        # Buttons
        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.buttonBox.accepted.connect(self._doSave)
        self.buttonBox.rejected.connect(self._doClose)

        # Set Current Values
        self.editName.setText(self.theItem.itemName)
        self.editName.selectAll()

        statusIdx = self.editStatus.findData(self.theItem.itemStatus)
        if statusIdx != -1:
            self.editStatus.setCurrentIndex(statusIdx)

        layoutIdx = self.editLayout.findData(self.theItem.itemLayout)
        if layoutIdx != -1:
            self.editLayout.setCurrentIndex(layoutIdx)

        ##
        #  Assemble
        ##

        nameLabel   = QLabel(self.tr("Label"))
        statusLabel = QLabel(self.tr("Status"))
        layoutLabel = QLabel(self.tr("Layout"))

        self.mainForm = QGridLayout()
        self.mainForm.setVerticalSpacing(vSp)
        self.mainForm.setHorizontalSpacing(mSp)
        self.mainForm.addWidget(nameLabel,       0, 0, 1, 1)
        self.mainForm.addWidget(self.editName,   0, 1, 1, 2)
        self.mainForm.addWidget(statusLabel,     1, 0, 1, 1)
        self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2)
        self.mainForm.addWidget(layoutLabel,     2, 0, 1, 1)
        self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2)
        self.mainForm.addWidget(self.textExport, 3, 0, 1, 2)
        self.mainForm.addWidget(self.editExport, 3, 2, 1, 1)
        self.mainForm.setColumnStretch(0, 0)
        self.mainForm.setColumnStretch(1, 1)
        self.mainForm.setColumnStretch(2, 0)

        self.outerBox = QVBoxLayout()
        self.outerBox.setSpacing(mSp)
        self.outerBox.addLayout(self.mainForm)
        self.outerBox.addStretch(1)
        self.outerBox.addWidget(self.buttonBox)
        self.setLayout(self.outerBox)

        self.rejected.connect(self._doClose)

        logger.debug("GuiItemEditor initialisation complete")

        return

    ##
    #  Slots
    ##

    @pyqtSlot()
    def _doSave(self):
        """Save the setting to the item.
        """
        logger.verbose("ItemEditor save button clicked")

        itemName   = self.editName.text()
        itemStatus = self.editStatus.currentData()
        itemLayout = self.editLayout.currentData()
        isExported = self.editExport.isChecked()

        self.theItem.setName(itemName)
        self.theItem.setStatus(itemStatus)
        self.theItem.setLayout(itemLayout)
        self.theItem.setExported(isExported)

        self.theProject.setProjectChanged(True)

        self.accept()
        self.close()

        return

    @pyqtSlot()
    def _doClose(self):
        """Close the dialog without saving the settings.
        """
        logger.verbose("ItemEditor cancel button clicked")
        self.close()
        return
Пример #34
0
class ViewWidget(QWidget):
    tab_active = pyqtSignal()

    def __init__(self, iface, dock_widget: QDockWidget) -> None:
        super().__init__()
        self.iface = iface
        self.dock_widget = dock_widget

        self.vbox = QVBoxLayout()
        self.create_variable_selector()
        self.create_time_selector()
        self.create_extra_dim_selector()
        self.create_interp_input()
        self.create_dataset_selector()
        self.setLayout(self.vbox)

        self.datasets = {}  # type: Dict[str, Dataset]
        self.selected_dataset = None  # type: Optional[str]
        self.selected_variable = {}  # type: Dict[str,str]
        self.selected_time = {}  # type: Dict[str,int]
        self.selected_extra_dim = {}  # type: Dict[Tuple[str,str],int]

        self.pause_replace_layer = False

    def create_variable_selector(self) -> None:
        self.variable_selector = QListWidget()
        self.variable_selector.currentItemChanged.connect(
            self.on_variable_selected)
        hbox = QHBoxLayout()
        hbox.addWidget(self.variable_selector)
        self.vbox.addLayout(hbox)

    def create_time_selector(self) -> None:
        self.time_label = QLabel('Time: N/A')
        self.time_selector = QSlider(Qt.Horizontal)
        self.time_selector.setSingleStep(1)
        self.time_selector.setPageStep(1)
        self.time_selector.setMinimum(0)
        self.time_selector.valueChanged.connect(self.on_time_selected)
        self.vbox.addWidget(self.time_label)
        self.vbox.addWidget(self.time_selector)

    def create_extra_dim_selector(self) -> None:
        self.extra_dim_label = QLabel('N/A:')
        self.extra_dim_selector = QComboBox()
        self.extra_dim_selector.currentIndexChanged.connect(
            self.on_extra_dim_selected)
        hbox = QHBoxLayout()
        hbox.addWidget(self.extra_dim_label)
        hbox.addWidget(self.extra_dim_selector)
        hbox.setContentsMargins(0, 0, 0, 0)
        self.extra_dim_container = QWidget()
        self.extra_dim_container.setLayout(hbox)
        self.extra_dim_container.setHidden(True)
        self.vbox.addWidget(self.extra_dim_container)

    def create_interp_input(self) -> None:
        grid = QGridLayout()

        self.interp_vert_selector = add_grid_combobox(grid, 0,
                                                      'Vertical Variable')
        self.interp_input = add_grid_lineedit(grid,
                                              1,
                                              'Desired Level',
                                              QDoubleValidator(
                                                  0.0, 10000.0, 50),
                                              required=True)
        self.interp_input.returnPressed.connect(self.on_interp_btn_clicked)

        btn = QPushButton('Interpolate')
        btn.clicked.connect(self.on_interp_btn_clicked)
        grid.addWidget(btn, 2, 1)

        self.interp_container = QGroupBox('Interpolate Vertical Level')
        self.interp_container.setCheckable(True)
        self.interp_container.setChecked(False)
        self.interp_container.toggled.connect(self.on_interp_toggled)
        self.interp_container.setLayout(grid)
        self.interp_container.setHidden(True)
        self.vbox.addWidget(self.interp_container)

    def create_dataset_selector(self) -> None:
        dataset_label = QLabel('Dataset:')
        self.dataset_selector = QComboBox()
        self.dataset_selector.currentIndexChanged.connect(
            self.on_dataset_selected)
        hbox = QHBoxLayout()
        hbox.addWidget(dataset_label)
        hbox.addWidget(self.dataset_selector)
        self.vbox.addLayout(hbox)

    def add_dataset(self, path: str) -> None:
        variables = gis4wrf.core.get_supported_wrf_nc_variables(path)
        times = gis4wrf.core.get_wrf_nc_time_steps(path)
        extra_dims = gis4wrf.core.get_wrf_nc_extra_dims(path)
        dataset_name = os.path.basename(path)
        is_new_dataset = dataset_name not in self.datasets
        self.datasets[dataset_name] = Dataset(dataset_name, path, variables,
                                              times, extra_dims)
        if is_new_dataset:
            self.dataset_selector.addItem(dataset_name, dataset_name)
        self.select_dataset(dataset_name)

    def select_dataset(self, dataset_name: str) -> None:
        index = self.dataset_selector.findData(dataset_name)
        self.dataset_selector.setCurrentIndex(index)

    def init_variable_selector(self) -> None:
        dataset = self.dataset
        selected = self.selected_variable.get(dataset.name)
        self.variable_selector.clear()
        for var_name, variable in sorted(dataset.variables.items(),
                                         key=lambda v: v[1].label):
            item = QListWidgetItem(variable.label)
            item.setData(Qt.UserRole, var_name)
            self.variable_selector.addItem(item)
            if var_name == selected:
                item.setSelected(True)
        if selected is None:
            self.extra_dim_container.hide()

    def init_time_selector(self) -> None:
        dataset = self.dataset
        self.time_selector.setMaximum(len(dataset.times) - 1)
        selected_time = self.selected_time.get(dataset.name, 0)
        self.select_time(selected_time)
        # force label update in case the index didn't change during dataset change
        self.on_time_selected(selected_time)

    def select_time(self, index: int) -> None:
        self.time_selector.setValue(index)

    def init_extra_dim_selector(self) -> None:
        dataset = self.dataset
        variable = self.variable
        extra_dim_name = variable.extra_dim_name
        if extra_dim_name is None:
            self.extra_dim_container.hide()
            return
        # prevent double layer replace, already happens in on_variable_selected()
        self.pause_replace_layer = True
        extra_dim = dataset.extra_dims[extra_dim_name]
        selected_extra_dim = self.selected_extra_dim.get(
            (dataset.name, extra_dim_name), 0)
        self.extra_dim_label.setText(extra_dim.label + ':')
        self.extra_dim_selector.clear()
        for step in extra_dim.steps:
            self.extra_dim_selector.addItem(step)
        self.extra_dim_selector.setCurrentIndex(selected_extra_dim)
        self.extra_dim_container.show()
        self.pause_replace_layer = False

    def init_interp_input(self, dataset_init: bool) -> None:
        if dataset_init:
            self.interp_vert_selector.clear()
            has_vert = False
            for variable in sorted(self.dataset.variables.values(),
                                   key=lambda v: v.label):
                if variable.extra_dim_name != 'bottom_top':
                    continue
                has_vert = True
                self.interp_vert_selector.addItem(variable.label,
                                                  variable.name)
            if not has_vert:
                self.extra_dim_container.setEnabled(True)
                self.interp_container.hide()
        else:
            variable = self.variable
            extra_dim_name = variable.extra_dim_name
            if extra_dim_name != 'bottom_top':
                self.interp_container.hide()
                return
            self.interp_container.show()

    def on_dataset_selected(self, index: int) -> None:
        self.init_variable_selector()
        self.init_time_selector()
        self.init_interp_input(True)

        previous_dataset = self.selected_dataset
        if previous_dataset is not None:
            gis4wrf.plugin.geo.remove_group(previous_dataset)
        self.selected_dataset = self.dataset_name

    def on_variable_selected(self, current: Optional[QListWidgetItem],
                             previous: Optional[QListWidgetItem]) -> None:
        if current is None:
            return
        dataset = self.dataset
        var_name = current.data(Qt.UserRole)
        assert var_name == self.var_name
        self.selected_variable[dataset.name] = var_name
        self.init_extra_dim_selector()
        self.init_interp_input(False)
        self.replace_variable_layer()
        self.select_time_band_in_variable_layers()

    def on_time_selected(self, index: int) -> None:
        dataset = self.dataset
        self.selected_time[dataset.name] = index
        self.time_label.setText('Time: ' + dataset.times[index])
        self.select_time_band_in_variable_layers()

    def on_extra_dim_selected(self, index: int) -> None:
        if index == -1:
            # happens when clearing the dropdown entries
            return
        variable = self.variable
        extra_dim_name = variable.extra_dim_name
        self.selected_extra_dim[(self.dataset_name, extra_dim_name)] = index
        self.replace_variable_layer()
        self.select_time_band_in_variable_layers()

    def on_interp_toggled(self, enabled: True) -> None:
        self.extra_dim_container.setEnabled(not enabled)
        self.replace_variable_layer()

    def on_interp_btn_clicked(self) -> None:
        self.replace_variable_layer()
        self.select_time_band_in_variable_layers()

    def replace_variable_layer(self) -> None:
        if self.pause_replace_layer:
            return
        if self.interp_enabled and self.interp_level is None:
            return
        dataset = self.dataset
        variable = self.variable
        extra_dim_index = self.extra_dim_index
        interp_level = self.interp_level
        interp_vert_name = self.interp_vert_name
        if interp_level is not None:
            extra_dim_index = None
        uri, dispose = gis4wrf.core.convert_wrf_nc_var_to_gdal_dataset(
            dataset.path, variable.name, extra_dim_index, interp_level,
            interp_vert_name)
        layer = gis4wrf.plugin.geo.load_layers(
            [(uri, variable.label, variable.name)],
            group_name=dataset.name,
            visible=True)[0]
        dispose_after_delete(layer, dispose)

    def select_time_band_in_variable_layers(self) -> None:
        dataset = self.dataset
        time_idx = self.time_index
        layers = gis4wrf.plugin.geo.get_raster_layers_in_group(dataset.name)
        for layer in layers:
            var_name = layer.shortName()
            if var_name in dataset.variables:
                gis4wrf.plugin.geo.switch_band(layer, time_idx)

    @property
    def dataset_name(self) -> str:
        return self.dataset_selector.currentData()

    @property
    def var_name(self) -> str:
        return self.variable_selector.currentItem().data(Qt.UserRole)

    @property
    def time_index(self) -> int:
        return self.time_selector.value()

    @property
    def extra_dim_index(self) -> Optional[int]:
        if self.variable.extra_dim_name is None:
            return None
        index = self.extra_dim_selector.currentIndex()
        assert index != -1
        return index

    @property
    def interp_enabled(self):
        return self.interp_container.isVisible(
        ) and self.interp_container.isChecked()

    @property
    def interp_vert_name(self):
        if not self.interp_enabled:
            return None
        return self.interp_vert_selector.currentData()

    @property
    def interp_level(self) -> Optional[float]:
        if not self.interp_enabled:
            return None
        if not self.interp_input.is_valid():
            return None
        return self.interp_input.value()

    @property
    def dataset(self) -> Dataset:
        return self.datasets[self.dataset_name]

    @property
    def variable(self) -> WRFNetCDFVariable:
        return self.dataset.variables[self.var_name]
Пример #35
0
class RegisterViewWidget(WidgetBase):
    def __init__(self, parent: QWidget):
        super(RegisterViewWidget, self).__init__(parent)

        self._registers = []
        self._running_task: asyncio.Task = None

        self._visibility_selector = QComboBox(self)
        self._visibility_selector.addItem("Show all registers", lambda _: True)
        self._visibility_selector.addItem("Only configuration parameters",
                                          lambda r: r.mutable and r.persistent)

        # noinspection PyUnresolvedReferences
        self._visibility_selector.currentIndexChanged.connect(
            lambda _: self._on_visibility_changed())

        self._reset_selected_button = make_button(
            self,
            "Reset selected",
            icon_name="clear-symbol",
            tool_tip=f"Reset the currently selected registers to their default "
            f"values. The restored values will be committed "
            f"immediately. This function is available only if a "
            f"default value is defined. [{RESET_SELECTED_SHORTCUT}]",
            on_clicked=self._do_reset_selected,
        )

        self._reset_all_button = make_button(
            self,
            "Reset all",
            icon_name="skull-crossbones",
            tool_tip=f"Reset the all registers to their default "
            f"values. The restored values will be committed "
            f"immediately.",
            on_clicked=self._do_reset_all,
        )

        self._read_selected_button = make_button(
            self,
            "Read selected",
            icon_name="process",
            tool_tip=f"Read the currently selected registers only "
            f"[{READ_SELECTED_SHORTCUT}]",
            on_clicked=self._do_read_selected,
        )

        self._read_all_button = make_button(
            self,
            "Read all",
            icon_name="process-plus",
            tool_tip="Read all registers from the device",
            on_clicked=self._do_read_all,
        )

        self._export_button = make_button(
            self,
            "Export",
            icon_name="export",
            tool_tip="Export configuration parameters",
            on_clicked=self._do_export,
        )

        self._import_button = make_button(
            self,
            "Import",
            icon_name="import",
            tool_tip="Import configuration parameters",
            on_clicked=self._do_import,
        )

        self._expand_all_button = make_button(
            self,
            "",
            icon_name="expand-arrow",
            tool_tip="Expand all namespaces",
            on_clicked=lambda: self._tree.expandAll(),
        )

        self._collapse_all_button = make_button(
            self,
            "",
            icon_name="collapse-arrow",
            tool_tip="Collapse all namespaces",
            on_clicked=lambda: self._tree.collapseAll(),
        )

        self._status_display = QLabel(self)
        self._status_display.setWordWrap(True)

        self._reset_selected_button.setEnabled(False)
        self._reset_all_button.setEnabled(False)
        self._read_selected_button.setEnabled(False)
        self._read_all_button.setEnabled(False)
        self._export_button.setEnabled(False)
        self._import_button.setEnabled(False)

        self._tree = QTreeView(self)
        self._tree.setVerticalScrollMode(QTreeView.ScrollPerPixel)
        self._tree.setHorizontalScrollMode(QTreeView.ScrollPerPixel)
        self._tree.setAnimated(True)
        self._tree.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self._tree.setAlternatingRowColors(True)
        self._tree.setContextMenuPolicy(Qt.ActionsContextMenu)

        # Not sure about this one. This hardcoded value may look bad on some platforms.
        self._tree.setIndentation(20)

        def add_action(
            callback: typing.Callable[[], None],
            icon_name: str,
            name: str,
            shortcut: typing.Optional[str] = None,
        ):
            action = QAction(get_icon(icon_name), name, self)
            # noinspection PyUnresolvedReferences
            action.triggered.connect(callback)
            if shortcut:
                action.setShortcut(shortcut)
                action.setAutoRepeat(False)
                try:
                    action.setShortcutVisibleInContextMenu(True)
                except AttributeError:
                    pass  # This feature is not available in PyQt before 5.10

            self._tree.addAction(action)

        add_action(self._do_read_all, "process-plus", "Read all registers")
        add_action(
            self._do_read_selected,
            "process",
            "Read selected registers",
            READ_SELECTED_SHORTCUT,
        )
        add_action(
            self._do_reset_selected,
            "clear-symbol",
            "Reset selected to default",
            RESET_SELECTED_SHORTCUT,
        )

        self._tree.setItemDelegateForColumn(
            int(Model.ColumnIndices.VALUE),
            EditorDelegate(self._tree, self._display_status),
        )

        # It doesn't seem to be explicitly documented, but it seems to be necessary to select either top or bottom
        # decoration position in order to be able to use center alignment. Left or right positions do not work here.
        self._tree.setItemDelegateForColumn(
            int(Model.ColumnIndices.FLAGS),
            StyleOptionModifyingDelegate(
                self._tree,
                decoration_position=QStyleOptionViewItem.Top,  # Important
                decoration_alignment=Qt.AlignCenter,
            ),
        )

        header: QHeaderView = self._tree.header()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)
        header.setStretchLastSection(
            False)  # Horizontal scroll bar doesn't work if this is enabled

        buttons_layout = QGridLayout()
        buttons_layout.addWidget(self._read_selected_button, 0, 0)
        buttons_layout.addWidget(self._reset_selected_button, 0, 2)
        buttons_layout.addWidget(self._read_all_button, 1, 0)
        buttons_layout.addWidget(self._reset_all_button, 1, 2)
        buttons_layout.addWidget(self._import_button, 2, 0)
        buttons_layout.addWidget(self._export_button, 2, 2)

        for col in range(3):
            buttons_layout.setColumnStretch(col, 1)

        layout = lay_out_vertically(
            (self._tree, 1),
            buttons_layout,
            lay_out_horizontally(
                self._visibility_selector,
                (None, 1),
                self._expand_all_button,
                self._collapse_all_button,
            ),
            self._status_display,
        )

        self.setLayout(layout)

    def reset(self):
        self.setup([])

    def setup(self, registers: typing.Iterable[Register]):
        self._registers = list(registers)
        self._on_visibility_changed()

    def _replace_model(
            self, register_visibility_predicate: typing.Callable[[Register],
                                                                 bool]):
        # Cancel all operations that might be pending on the old model
        self._cancel_task()

        old_model = self._tree.model()

        # Configure the new model
        filtered_registers = list(
            filter(register_visibility_predicate, self._registers))
        # It is important to set the Tree widget as the parent in order to let the widget take ownership
        new_model = Model(self._tree, filtered_registers)
        _logger.info("New model %r", new_model)
        self._tree.setModel(new_model)

        # The selection model is implicitly replaced when we replace the model, so it has to be reconfigured
        self._tree.selectionModel().selectionChanged.connect(
            lambda *_: self._on_selection_changed())

        # TODO: Something fishy is going on. Something keeps the old model alive when we're replacing it.
        #       We could call deleteLater() on it, but it seems dangerous, because if that something ever decided
        #       to refer to that dead model later for any reason, we'll get a rougue dangling pointer access on
        #       our hands. The horror!
        if old_model is not None:
            import gc

            model_referrers = gc.get_referrers(old_model)
            if len(model_referrers) > 1:
                _logger.warning(
                    "Extra references to the old model %r: %r",
                    old_model,
                    model_referrers,
                )

        # Update the widget - all root items are expanded by default
        for row in itertools.count():
            index = self._tree.model().index(row, 0)
            if not index.isValid():
                break

            self._tree.expand(index)

        self._reset_selected_button.setEnabled(False)
        self._read_selected_button.setEnabled(False)
        self._read_all_button.setEnabled(len(filtered_registers) > 0)
        self._reset_all_button.setEnabled(len(filtered_registers) > 0)
        self._export_button.setEnabled(len(filtered_registers) > 0)
        self._import_button.setEnabled(len(filtered_registers) > 0)

        self._display_status(f"{len(filtered_registers)} registers loaded")

    def _on_visibility_changed(self):
        self._replace_model(self._visibility_selector.currentData())

    def _on_selection_changed(self):
        selected = self._get_selected_registers()

        self._reset_selected_button.setEnabled(
            any(map(lambda r: r.has_default_value, selected)))
        self._read_selected_button.setEnabled(len(selected) > 0)

    def _do_read_selected(self):
        selected = self._get_selected_registers()
        if selected:
            self._read_specific(selected)
        else:
            self._display_status("No registers are selected, nothing to read")

    def _do_reset_selected(self):
        rv = {}
        for r in self._get_selected_registers():
            if r.has_default_value:
                rv[r] = r.default_value

        self._write_specific(rv)

    def _do_reset_all(self):
        rv = {}
        for r in self._registers:
            if r.has_default_value:
                rv[r] = r.default_value

        self._write_specific(rv)

    def _do_read_all(self):
        self._read_specific(self._tree.model().registers)

    def _do_import(self):
        import_registers(parent=self, registers=self._registers)

    def _do_export(self):
        export_registers(parent=self, registers=self._registers)

    def _read_specific(self, registers: typing.List[Register]):
        total_registers_read = None

        def progress_callback(register: Register, current_register_index: int,
                              total_registers: int):
            nonlocal total_registers_read
            total_registers_read = total_registers
            self._display_status(
                f"Reading {register.name!r} "
                f"({current_register_index + 1} of {total_registers})")

        async def executor():
            try:
                _logger.info("Reading registers: %r",
                             [r.name for r in registers])
                mod: Model = self._tree.model()
                await mod.read(registers=registers,
                               progress_callback=progress_callback)
            except asyncio.CancelledError:
                self._display_status(f"Read has been cancelled")
                raise
            except Exception as ex:
                _logger.exception("Register read failed")
                show_error("Read failed", "Could not read registers", repr(ex),
                           self)
                self._display_status(f"Could not read registers: {ex!r}")
            else:
                self._display_status(
                    f"{total_registers_read} registers have been read")

        self._cancel_task()
        self._running_task = asyncio.get_event_loop().create_task(executor())

    def _write_specific(self, register_value_mapping: typing.Dict[Register,
                                                                  typing.Any]):
        total_registers_assigned = None

        def progress_callback(register: Register, current_register_index: int,
                              total_registers: int):
            nonlocal total_registers_assigned
            total_registers_assigned = total_registers
            self._display_status(
                f"Writing {register.name!r} "
                f"({current_register_index + 1} of {total_registers})")

        async def executor():
            try:
                _logger.info(
                    "Writing registers: %r",
                    [r.name for r in register_value_mapping.keys()],
                )
                mod: Model = self._tree.model()
                await mod.write(
                    register_value_mapping=register_value_mapping,
                    progress_callback=progress_callback,
                )
            except asyncio.CancelledError:
                self._display_status(f"Write has been cancelled")
                raise
            except Exception as ex:
                _logger.exception("Register write failed")
                show_error("Write failed", "Could not read registers",
                           repr(ex), self)
                self._display_status(f"Could not write registers: {ex!r}")
            else:
                self._display_status(
                    f"{total_registers_assigned} registers have been written")

        self._cancel_task()
        self._running_task = asyncio.get_event_loop().create_task(executor())

    def _get_selected_registers(self) -> typing.List[Register]:
        selected_indexes: typing.List[
            QModelIndex] = self._tree.selectedIndexes()
        selected_registers = set()
        for si in selected_indexes:
            r = Model.get_register_from_index(si)
            if r is not None:
                selected_registers.add(r)
        # Beware that sets are not sorted, this may lead to weird user experience when watching the registers
        # read in a funny order.
        return list(sorted(selected_registers, key=lambda x: x.name))

    def _cancel_task(self):
        # noinspection PyBroadException
        try:
            self._running_task.cancel()
        except Exception:
            pass
        else:
            _logger.info("A running task had to be cancelled: %r",
                         self._running_task)
        finally:
            self._running_task = None

    def _display_status(self, text=None):
        self._status_display.setText(text)
Пример #36
0
def createTranformControls(grp_title):
    grp_tranform = QGroupBox(grp_title)
    lyt_grid = QGridLayout()

    # Camera Flip
    ckbox_flip_h = QCheckBox("Flip Horizontal")
    ckbox_flip_v = QCheckBox("Flip Vertical")
    ckbox_flip_h.stateChanged.connect(
        lambda: setDisplayFlipHorizontal(ckbox_flip_h.checkState()))
    ckbox_flip_v.stateChanged.connect(
        lambda: setDisplayFlipVertical(ckbox_flip_v.checkState()))

    # Camera Rotate
    lbl_rotate = QLabel("Rotate: ")
    cbbox_rotate = QComboBox()
    cbbox_rotate.addItem(("0: none - No rotation"), 0)
    cbbox_rotate.addItem(("1: 90CW - Rotate 90 degrees clockwise"), 1)
    cbbox_rotate.addItem(("2: 90CCW - Rotate 90 degrees counter-clockwise"), 2)
    cbbox_rotate.addItem(("3: 180 - Rotate 180 degrees"), 3)

    cbbox_rotate.currentIndexChanged.connect(
        lambda: setRotate(cbbox_rotate.currentData()))

    # Camera Crop
    ckbox_crop = QCheckBox("Crop")
    lbl_crop_x = QLabel("Crop X: ")
    lbl_crop_y = QLabel("Crop Y: ")
    lbl_crop_w = QLabel("Crop Width: ")
    lbl_crop_h = QLabel("Crop Height: ")
    edi_crop_x = QLineEdit("0")
    edi_crop_y = QLineEdit("0")
    edi_crop_w = QLineEdit("640")
    edi_crop_h = QLineEdit("360")

    edi_crop_x.textChanged.connect(
        lambda: setCropValue("x", int(edi_crop_x.text())))
    edi_crop_y.textChanged.connect(
        lambda: setCropValue("y", int(edi_crop_y.text())))
    edi_crop_w.textChanged.connect(
        lambda: setCropValue("w", int(edi_crop_w.text())))
    edi_crop_h.textChanged.connect(
        lambda: setCropValue("h", int(edi_crop_h.text())))

    edi_crop_x.setEnabled(False)
    edi_crop_y.setEnabled(False)
    edi_crop_w.setEnabled(False)
    edi_crop_h.setEnabled(False)

    ckbox_crop.stateChanged.connect(lambda: setCrop(
        ckbox_crop, [edi_crop_x, edi_crop_y, edi_crop_w, edi_crop_h]))

    # Adding Component to layout
    lyt_grid.addWidget(ckbox_flip_h, 0, 1)
    lyt_grid.addWidget(ckbox_flip_v, 0, 2)
    lyt_grid.addWidget(lbl_rotate, 2, 0, Qt.AlignRight)
    lyt_grid.addWidget(cbbox_rotate, 2, 1, 1, 3)
    lyt_grid.addWidget(ckbox_crop, 3, 1)
    lyt_grid.addWidget(lbl_crop_x, 4, 0, Qt.AlignRight)
    lyt_grid.addWidget(edi_crop_x, 4, 1)
    lyt_grid.addWidget(lbl_crop_y, 4, 2, Qt.AlignRight)
    lyt_grid.addWidget(edi_crop_y, 4, 3)
    lyt_grid.addWidget(lbl_crop_w, 5, 0, Qt.AlignRight)
    lyt_grid.addWidget(edi_crop_w, 5, 1)
    lyt_grid.addWidget(lbl_crop_h, 5, 2, Qt.AlignRight)
    lyt_grid.addWidget(edi_crop_h, 5, 3)

    grp_tranform.setLayout(lyt_grid)

    return grp_tranform
Пример #37
0
class MidiSettings(CueSettingsPage):

    Name = 'MIDI controls'

    def __init__(self, cue_class, **kwargs):
        super().__init__(cue_class, **kwargs)
        self.setLayout(QVBoxLayout())
        self.layout().setAlignment(Qt.AlignTop)

        self.midiGroup = QGroupBox(self)
        self.midiGroup.setTitle('MIDI')
        self.midiGroup.setEnabled(check_module('midi'))
        self.midiGroup.setLayout(QGridLayout())
        self.layout().addWidget(self.midiGroup)

        self.midiModel = SimpleTableModel(['Type', 'Channel', 'Note', 'Action'])

        self.midiView = MidiView(cue_class, parent=self.midiGroup)
        self.midiView.setModel(self.midiModel)
        self.midiGroup.layout().addWidget(self.midiView, 0, 0, 1, 2)

        self.addButton = QPushButton('Add', self.midiGroup)
        self.addButton.clicked.connect(self.__new_message)
        self.midiGroup.layout().addWidget(self.addButton, 1, 0)

        self.removeButton = QPushButton('Remove', self.midiGroup)
        self.removeButton.clicked.connect(self.__remove_message)
        self.midiGroup.layout().addWidget(self.removeButton, 1, 1)

        self.midiCapture = QPushButton('Capture', self.midiGroup)
        self.midiCapture.clicked.connect(self.capture_message)
        self.midiGroup.layout().addWidget(self.midiCapture, 2, 0)

        self.msgTypeCombo = QComboBox(self.midiGroup)
        self.msgTypeCombo.addItem('Filter "note on"')
        self.msgTypeCombo.setItemData(0, 'note_on', Qt.UserRole)
        self.msgTypeCombo.addItem('Filter "note off"')
        self.msgTypeCombo.setItemData(1, 'note_off', Qt.UserRole)
        self.midiGroup.layout().addWidget(self.msgTypeCombo, 2, 1)

        self._default_action = self._cue_class.CueActions[0].name

    def enable_check(self, enabled):
        self.midiGroup.setCheckable(enabled)
        self.midiGroup.setChecked(False)

    def get_settings(self):
        settings = {}
        checkable = self.midiGroup.isCheckable()

        if not (checkable and not self.midiGroup.isChecked()):
            messages = []

            for row in self.midiModel.rows:
                message = Midi.str_from_values(row[0], row[1], row[2])
                messages.append((message, row[-1]))

            if messages:
                settings['midi'] = messages

        return settings

    def load_settings(self, settings):
        if 'midi' in settings:
            for options in settings['midi']:
                m_type, channel, note = Midi.from_string(options[0])
                self.midiModel.append_row(m_type, channel, note, options[1])

    def capture_message(self):
        handler = MIDIInputHandler()
        handler.alternate_mode = True
        handler.new_message_alt.connect(self.__add_message)

        QMessageBox.information(self, 'Input', 'Listening MIDI events ...')

        handler.new_message_alt.disconnect(self.__add_message)
        handler.alternate_mode = False

    def __add_message(self, msg):
        if self.msgTypeCombo.currentData(Qt.UserRole) == msg.type:
            self.midiModel.append_row(msg.type, msg.channel, msg.note,
                                      self._default_action)

    def __new_message(self):
        message_type = self.msgTypeCombo.currentData(Qt.UserRole)
        self.midiModel.append_row(message_type, 0, 0, self._default_action)

    def __remove_message(self):
        self.midiModel.removeRow(self.midiView.currentIndex().row())
Пример #38
0
class SubwindowBrowserSources(QWidget):
    """Show connections settings sub window."""
    current_tab = -1

    def createWindow(self, mainWindow, tab=''):
        """Create window."""
        try:
            parent = None
            super().__init__(parent)
            # self.setWindowFlags(Qt.WindowStaysOnTopHint)

            self.setWindowIcon(
                QIcon(scctool.settings.getResFile('browser.png')))
            self.setWindowModality(Qt.ApplicationModal)
            self.mainWindow = mainWindow
            self.passEvent = False
            self.controller = mainWindow.controller
            self.__dataChanged = False

            self.createButtonGroup()
            self.createTabs(tab)

            mainLayout = QVBoxLayout()

            mainLayout.addWidget(self.tabs)
            mainLayout.addLayout(self.buttonGroup)

            self.setLayout(mainLayout)

            self.resize(
                QSize(mainWindow.size().width() * 0.8,
                      self.sizeHint().height()))
            relativeChange = QPoint(mainWindow.size().width() / 2,
                                    mainWindow.size().height() / 3) -\
                QPoint(self.size().width() / 2,
                       self.size().height() / 3)
            self.move(mainWindow.pos() + relativeChange)

            self.setWindowTitle(_("Browser Sources"))

        except Exception:
            module_logger.exception("message")

    def createTabs(self, tab):
        """Create tabs."""
        self.tabs = QTabWidget()

        self.createFormGroupIntro()
        self.createFormGroupMapstats()
        self.createFormGroupMapBox()
        self.createFormGroupMapLandscape()
        self.createFormGroupVetoes()

        # Add tabs
        self.tabs.addTab(self.formGroupIntro, _("Intros"))
        self.tabs.addTab(self.formGroupMapstats, _("Mapstats"))

        self.tabs.addTab(self.formGroupMapBox, _("Box Map Icons"))
        self.tabs.addTab(self.formGroupMapLandscape, _("Landscape Map Icons"))
        self.tabs.addTab(self.formGroupVetoes, _("Veto Icons"))

        table = dict()
        table['intro'] = 0
        table['mapstats'] = 1
        table['mapicons_box'] = 2
        table['mapicons_landscape'] = 3
        table['vetoes'] = 4
        self.tabs.setCurrentIndex(
            table.get(tab, SubwindowBrowserSources.current_tab))
        self.tabs.currentChanged.connect(self.tabChanged)

    @classmethod
    def tabChanged(cls, idx):
        """Save the current tab."""
        SubwindowBrowserSources.current_tab = idx

    def addHotkey(self, ident, label):
        """Add a hotkey to the layout."""
        element = HotkeyLayout(
            self, ident, label,
            scctool.settings.config.parser.get("Intros", ident))
        self.hotkeys[ident] = element
        return element

    def connectHotkeys(self):
        """Connect the hotkeys."""
        for ident, key in self.hotkeys.items():
            if ident == 'hotkey_debug':
                for ident2, key2 in self.hotkeys.items():
                    if ident == ident2:
                        continue
                    key.modified.connect(key2.check_dublicate)
            key.modified.connect(self.hotkeyChanged)

    def hotkeyChanged(self, key, ident):
        """Handle change of hotkeys."""
        self.changed()

        if (ident == 'hotkey_player1' and self.cb_single_hotkey.isChecked()):
            self.hotkeys['hotkey_player2'].setData(
                self.hotkeys['hotkey_player1'].getKey())

        if not key:
            return

        if ((ident == 'hotkey_player1'
             and key == self.hotkeys['hotkey_player2'].getKey()['name']) or
            (ident == 'hotkey_player2'
             and key == self.hotkeys['hotkey_player1'].getKey()['name'])):
            self.cb_single_hotkey.setChecked(True)

        if (ident in ['hotkey_player1', 'hotkey_player2']
                and key == self.hotkeys['hotkey_debug'].getKey()['name']):
            self.hotkeys['hotkey_debug'].clear()

    def singleHotkeyChanged(self):
        """Handle single hotkey changed event."""
        checked = self.cb_single_hotkey.isChecked()
        self.hotkeys['hotkey_player2'].setDisabled(checked)
        if checked:
            self.hotkeys['hotkey_player2'].setData(
                self.hotkeys['hotkey_player1'].getKey())
        elif (self.hotkeys['hotkey_player1'].getKey() ==
              self.hotkeys['hotkey_player2'].getKey()):
            self.hotkeys['hotkey_player2'].clear()

    def createFormGroupMapstats(self):
        """Create the form group for mapstats."""
        self.formGroupMapstats = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("General"))
        layout = QFormLayout()

        container = QHBoxLayout()
        self.qb_boxStyle = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/mapstats",
            "mapstats")
        self.qb_boxStyle.connect2WS(self.controller, 'mapstats')
        label = QLabel(_("Style:"))
        label.setMinimumWidth(120)
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/mapstats.html"))
        container.addWidget(self.qb_boxStyle, 2)
        container.addWidget(button, 1)
        layout.addRow(label, container)

        self.cb_mappool = QComboBox()
        self.cb_mappool.addItem(_("Current Ladder Map Pool"))
        self.cb_mappool.addItem(_("Custom Map Pool (defined below)"))
        self.cb_mappool.addItem(_("Currently entered Maps only"))
        self.cb_mappool.setCurrentIndex(
            self.controller.mapstatsManager.getMapPoolType())
        self.cb_mappool.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(_("Map Pool:")), self.cb_mappool)

        self.cb_autoset_map = QCheckBox(_("Select the next map automatically"))
        self.cb_autoset_map.setChecked(
            scctool.settings.config.parser.getboolean("Mapstats",
                                                      "autoset_next_map"))
        self.cb_autoset_map.stateChanged.connect(self.changed)
        label = QLabel(_("Next Map:"))
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_autoset_map)

        self.cb_mark_played = QCheckBox(_("Mark already played maps"))
        self.cb_mark_played.setChecked(
            scctool.settings.config.parser.getboolean("Mapstats",
                                                      "mark_played"))
        self.cb_mark_played.stateChanged.connect(self.changed)
        label = QLabel(_("Mark:"))
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_mark_played)

        self.cb_mark_vetoed = QCheckBox(_("Mark vetoed maps"))
        self.cb_mark_vetoed.setChecked(
            scctool.settings.config.parser.getboolean("Mapstats",
                                                      "mark_vetoed"))
        self.cb_mark_vetoed.stateChanged.connect(self.changed)
        label = QLabel(" ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_mark_vetoed)

        box.setLayout(layout)
        mainLayout.addWidget(box)

        box = QGroupBox(_("Custom Map Pool"))
        layout = QGridLayout()
        self.maplist = QListWidget()
        self.maplist.setSortingEnabled(True)

        ls = list(self.controller.mapstatsManager.getCustomMapPool())
        self.maplist.addItems(ls)
        self.maplist.setCurrentItem(self.maplist.item(0))

        layout.addWidget(self.maplist, 0, 0, 3, 1)

        qb_add = QPushButton()
        pixmap = QIcon(scctool.settings.getResFile('add.png'))
        qb_add.setIcon(pixmap)
        qb_add.clicked.connect(self.addMap)
        layout.addWidget(qb_add, 0, 1)

        qb_remove = QPushButton()
        pixmap = QIcon(scctool.settings.getResFile('delete.png'))
        qb_remove.clicked.connect(self.removeMap)
        qb_remove.setIcon(pixmap)
        layout.addWidget(qb_remove, 1, 1)

        self.sc_removeMap = QShortcut(QKeySequence("Del"), self)
        self.sc_removeMap.setAutoRepeat(False)
        self.sc_removeMap.activated.connect(self.removeMap)

        box.setLayout(layout)
        mainLayout.addWidget(box)

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupMapstats.setLayout(mainLayout)

    def addMap(self):
        """Add a map to the list."""
        maplist = list(scctool.settings.maps)
        for i in range(self.maplist.count()):
            sc2map = str(self.maplist.item(i).text())
            if sc2map in maplist:
                maplist.remove(sc2map)

        if len(maplist) > 0:
            sc2map, ok = QInputDialog.getItem(self,
                                              _('Add Map'),
                                              _('Please select a map') + ':',
                                              maplist,
                                              editable=False)

            if ok:
                self.__dataChanged = True
                item = QListWidgetItem(sc2map)
                self.maplist.addItem(item)
                self.maplist.setCurrentItem(item)
        else:
            QMessageBox.information(
                self, _("No maps available"),
                _('All available maps have already been added.'))

    def removeMap(self):
        """Remove a map from the list."""
        item = self.maplist.currentItem()
        if item:
            self.maplist.takeItem(self.maplist.currentRow())
            self.__dataChanged = True

    def createFormGroupMapBox(self):
        """Create a QWidget for boxed map icons."""
        self.formGroupMapBox = QWidget()
        mainLayout = QVBoxLayout()
        box = QGroupBox(_("General"))
        layout = QFormLayout()

        container = QHBoxLayout()
        self.qb_boxStyle = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/mapicons_box",
            "mapicons_box")
        self.qb_boxStyle.connect2WS(self.controller, 'mapicons_box')
        label = QLabel(_("Style:"))
        label.setMinimumWidth(120)
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/mapicons_box_1.html"))
        container.addWidget(self.qb_boxStyle, 2)
        container.addWidget(button, 1)
        layout.addRow(label, container)

        self.sb_padding_box = QDoubleSpinBox()
        self.sb_padding_box.setRange(0, 50)
        self.sb_padding_box.setDecimals(1)
        self.sb_padding_box.setValue(
            scctool.settings.config.parser.getfloat("MapIcons", "padding_box"))
        self.sb_padding_box.setSuffix(" " + _("Pixel"))
        self.sb_padding_box.valueChanged.connect(
            lambda x: self.changePadding('box', x))
        layout.addRow(QLabel(_("Icon Padding:") + " "), self.sb_padding_box)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        options = self.controller.matchControl.scopes
        self.scope_box = dict()
        for idx in range(0, 3):
            self.scope_box[idx] = ScopeGroupBox(
                _("Icon Set {} Scope".format(idx + 1)), options,
                scctool.settings.config.parser.get(
                    "MapIcons", "scope_box_{}".format(idx + 1)), self)
            self.scope_box[idx].dataModified.connect(self.changed)
            mainLayout.addWidget(self.scope_box[idx])

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupMapBox.setLayout(mainLayout)

    def createFormGroupVetoes(self):
        """Create a QWidget for veto icons."""
        self.formGroupVetoes = QWidget()
        mainLayout = QVBoxLayout()
        box = QGroupBox(_("General"))
        layout = QFormLayout()

        container = QHBoxLayout()
        self.qb_boxStyle = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/vetoes", "vetoes")
        self.qb_boxStyle.connect2WS(self.controller, 'vetoes')
        label = QLabel(_("Style:"))
        label.setMinimumWidth(120)
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/vetoes.html"))
        container.addWidget(self.qb_boxStyle, 2)
        container.addWidget(button, 1)
        layout.addRow(label, container)

        self.sb_padding_box = QDoubleSpinBox()
        self.sb_padding_box.setRange(0, 50)
        self.sb_padding_box.setDecimals(1)
        self.sb_padding_box.setValue(
            scctool.settings.config.parser.getfloat("Vetoes", "padding"))
        self.sb_padding_box.setSuffix(" " + _("Pixel"))
        self.sb_padding_box.valueChanged.connect(
            lambda x: self.changePadding('vetoes', x))
        layout.addRow(QLabel(_("Icon Padding:") + " "), self.sb_padding_box)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupVetoes.setLayout(mainLayout)

    def createFormGroupMapLandscape(self):
        """Create a QWidget for the landscape map icons."""
        self.formGroupMapLandscape = QWidget()
        mainLayout = QVBoxLayout()
        box = QGroupBox(_("General"))
        layout = QFormLayout()

        container = QHBoxLayout()
        self.qb_boxStyle = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/mapicons_landscape",
            "mapicons_landscape")
        self.qb_boxStyle.connect2WS(self.controller, 'mapicons_landscape')
        label = QLabel(_("Style:"))
        label.setMinimumWidth(120)
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/mapicons_landscape_1.html"))
        container.addWidget(self.qb_boxStyle, 2)
        container.addWidget(button, 1)
        layout.addRow(label, container)

        self.sb_padding_landscape = QDoubleSpinBox()
        self.sb_padding_landscape.setRange(0, 50)
        self.sb_padding_landscape.setDecimals(1)
        self.sb_padding_landscape.setValue(
            scctool.settings.config.parser.getfloat("MapIcons",
                                                    "padding_landscape"))
        self.sb_padding_landscape.setSuffix(" " + _("Pixel"))
        self.sb_padding_landscape.valueChanged.connect(
            lambda x: self.changePadding('landscape', x))
        layout.addRow(QLabel(_("Icon Padding:") + " "),
                      self.sb_padding_landscape)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        options = self.controller.matchControl.scopes
        self.scope_landscape = dict()
        for idx in range(0, 3):
            self.scope_landscape[idx] = ScopeGroupBox(
                _("Icon Set {} Scope".format(idx + 1)), options,
                scctool.settings.config.parser.get(
                    "MapIcons", "scope_landscape_{}".format(idx + 1)), self)
            self.scope_landscape[idx].dataModified.connect(self.changed)
            mainLayout.addWidget(self.scope_landscape[idx])

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupMapLandscape.setLayout(mainLayout)

    def createFormGroupIntro(self):
        """Create forms for websocket connection to intro."""
        self.formGroupIntro = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Style"))
        layout = QHBoxLayout()
        styleqb = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/intro", "intro")
        styleqb.connect2WS(self.controller, 'intro')
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/intro.html"))
        layout.addWidget(styleqb, 2)
        layout.addWidget(button, 1)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        self.hotkeyBox = QGroupBox(_("Hotkeys"))
        layout = QVBoxLayout()
        self.controller.websocketThread.unregister_hotkeys(force=True)

        self.cb_single_hotkey = QCheckBox(
            _("Use a single hotkey for both players"))
        self.cb_single_hotkey.stateChanged.connect(self.singleHotkeyChanged)
        layout.addWidget(self.cb_single_hotkey)

        self.hotkeys = dict()
        layout.addLayout(self.addHotkey("hotkey_player1", _("Player 1")))
        layout.addLayout(self.addHotkey("hotkey_player2", _("Player 2")))
        layout.addLayout(self.addHotkey("hotkey_debug", _("Debug")))

        self.cb_single_hotkey.setChecked(self.hotkeys['hotkey_player1'].getKey(
        ) == self.hotkeys['hotkey_player2'].getKey())
        self.connectHotkeys()
        label = QLabel(
            _("Player 1 is always the player your observer"
              " camera is centered on at start of a game."))
        layout.addWidget(label)
        self.hotkeyBox.setLayout(layout)
        mainLayout.addWidget(self.hotkeyBox)

        self.introBox = QGroupBox(_("Animation"))
        layout = QFormLayout()
        self.cb_animation = QComboBox()
        animation = scctool.settings.config.parser.get("Intros", "animation")
        currentIdx = 0
        idx = 0
        options = dict()
        options['Fly-In'] = _("Fly-In")
        options['Slide'] = _("Slide")
        options['Fanfare'] = _("Fanfare")
        for key, item in options.items():
            self.cb_animation.addItem(item, key)
            if (key == animation):
                currentIdx = idx
            idx += 1
        self.cb_animation.setCurrentIndex(currentIdx)
        self.cb_animation.currentIndexChanged.connect(self.changed)
        label = QLabel(_("Animation:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_animation)
        self.sb_displaytime = QDoubleSpinBox()
        self.sb_displaytime.setRange(0, 10)
        self.sb_displaytime.setDecimals(1)
        self.sb_displaytime.setValue(
            scctool.settings.config.parser.getfloat("Intros", "display_time"))
        self.sb_displaytime.setSuffix(" " + _("Seconds"))
        self.sb_displaytime.valueChanged.connect(self.changed)
        layout.addRow(QLabel(_("Display Duration:") + " "),
                      self.sb_displaytime)
        self.sl_sound = QSlider(Qt.Horizontal)
        self.sl_sound.setMinimum(0)
        self.sl_sound.setMaximum(20)
        self.sl_sound.setValue(
            scctool.settings.config.parser.getint("Intros", "sound_volume"))
        self.sl_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_sound.setTickInterval(1)
        self.sl_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(_("Sound Volume:") + " "), self.sl_sound)
        self.introBox.setLayout(layout)
        mainLayout.addWidget(self.introBox)

        self.ttsBox = QGroupBox(_("Text-to-Speech"))
        layout = QFormLayout()

        self.cb_tts_active = QCheckBox()
        self.cb_tts_active.setChecked(
            scctool.settings.config.parser.getboolean("Intros", "tts_active"))
        self.cb_tts_active.stateChanged.connect(self.changed)
        label = QLabel(_("Activate Text-to-Speech:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_tts_active)

        self.icons = {}
        self.icons['MALE'] = QIcon(scctool.settings.getResFile('male.png'))
        self.icons['FEMALE'] = QIcon(scctool.settings.getResFile('female.png'))
        self.cb_tts_voice = QComboBox()

        currentIdx = 0
        idx = 0
        tts_voices = self.controller.tts.getVoices()
        tts_voice = scctool.settings.config.parser.get("Intros", "tts_voice")
        for voice in tts_voices:
            self.cb_tts_voice.addItem(self.icons[voice['ssmlGender']],
                                      '   ' + voice['name'], voice['name'])
            if (voice['name'] == tts_voice):
                currentIdx = idx
            idx += 1
        self.cb_tts_voice.setCurrentIndex(currentIdx)
        self.cb_tts_voice.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(_("Voice:") + " "), self.cb_tts_voice)
        self.ttsBox.setStyleSheet("QComboBox { combobox-popup: 0; }")
        self.ttsBox.setLayout(layout)
        mainLayout.addWidget(self.ttsBox)

        self.sb_tts_pitch = QDoubleSpinBox()
        self.sb_tts_pitch.setRange(-20, 20)
        self.sb_tts_pitch.setDecimals(2)
        self.sb_tts_pitch.setValue(
            scctool.settings.config.parser.getfloat("Intros", "tts_pitch"))
        self.sb_tts_pitch.valueChanged.connect(self.changed)
        layout.addRow(QLabel(_("Pitch:") + " "), self.sb_tts_pitch)

        self.sb_tts_rate = QDoubleSpinBox()
        self.sb_tts_rate.setRange(0.25, 4.00)
        self.sb_tts_rate.setSingleStep(0.1)
        self.sb_tts_rate.setDecimals(2)
        self.sb_tts_rate.setValue(
            scctool.settings.config.parser.getfloat("Intros", "tts_rate"))
        self.sb_tts_rate.valueChanged.connect(self.changed)
        layout.addRow(QLabel(_("Rate:") + " "), self.sb_tts_rate)

        self.cb_tts_scope = QComboBox()
        scope = scctool.settings.config.parser.get("Intros", "tts_scope")
        currentIdx = 0
        idx = 0
        options = self.controller.tts.getOptions()
        for key, item in options.items():
            self.cb_tts_scope.addItem(item['desc'], key)
            if (key == scope):
                currentIdx = idx
            idx += 1
        self.cb_tts_scope.setCurrentIndex(currentIdx)
        self.cb_tts_scope.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(_("Line:") + " "), self.cb_tts_scope)

        self.sl_tts_sound = QSlider(Qt.Horizontal)
        self.sl_tts_sound.setMinimum(0)
        self.sl_tts_sound.setMaximum(20)
        self.sl_tts_sound.setValue(
            scctool.settings.config.parser.getint("Intros", "tts_volume"))
        self.sl_tts_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_tts_sound.setTickInterval(1)
        self.sl_tts_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(_("Sound Volume:") + " "), self.sl_tts_sound)

        text = _(
            "Text-to-Speech provided by Google-Cloud is paid for "
            "by StarCraft Casting Tool Patrons. To keep this service up "
            "consider becoming a <a href='{patreon}'>Patron</a> yourself. "
            "You can test all voices at {tts}.")

        patreon = 'https://www.patreon.com/StarCraftCastingTool'

        url = 'https://cloud.google.com/text-to-speech/'
        tts = "<a href='{}'>cloud.google.com/text-to-speech</a>"
        tts = tts.format(url)

        label = QLabel(text.format(patreon=patreon, tts=tts))
        label.setAlignment(Qt.AlignJustify)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setMargin(5)
        layout.addRow(label)

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupIntro.setLayout(mainLayout)

    def createButtonGroup(self):
        """Create buttons."""
        try:
            layout = QHBoxLayout()

            layout.addWidget(QLabel(""))

            buttonCancel = QPushButton(_('Cancel'))
            buttonCancel.clicked.connect(self.closeWindow)
            layout.addWidget(buttonCancel)

            buttonSave = QPushButton(_('&Save && Close'))
            buttonSave.setToolTip(_("Shortcut: {}").format("Ctrl+S"))
            self.shortcut = QShortcut(QKeySequence("Ctrl+S"), self)
            self.shortcut.setAutoRepeat(False)
            self.shortcut.activated.connect(self.saveCloseWindow)
            buttonSave.clicked.connect(self.saveCloseWindow)
            layout.addWidget(buttonSave)

            self.buttonGroup = layout
        except Exception:
            module_logger.exception("message")

    def changed(self, *values):
        """Handle changed data."""
        self.__dataChanged = True

    def saveData(self):
        """Save the data to config."""
        if (self.__dataChanged):
            self.saveWebsocketdata()

            maps = list()
            for i in range(self.maplist.count()):
                maps.append(str(self.maplist.item(i).text()).strip())

            self.controller.mapstatsManager.setCustomMapPool(maps)
            self.controller.mapstatsManager.setMapPoolType(
                self.cb_mappool.currentIndex())

            scctool.settings.config.parser.set(
                "Mapstats", "autoset_next_map",
                str(self.cb_autoset_map.isChecked()))

            scctool.settings.config.parser.set(
                "Mapstats", "mark_played",
                str(self.cb_mark_played.isChecked()))

            scctool.settings.config.parser.set(
                "Mapstats", "mark_vetoed",
                str(self.cb_mark_vetoed.isChecked()))

            self.controller.mapstatsManager.sendMapPool()
            self.mainWindow.updateAllMapButtons()

            for idx in range(0, 3):
                scctool.settings.config.parser.set(
                    "MapIcons", "scope_box_{}".format(idx + 1),
                    self.scope_box[idx].getScope())
                scctool.settings.config.parser.set(
                    "MapIcons", "scope_landscape_{}".format(idx + 1),
                    self.scope_landscape[idx].getScope())
            self.controller.matchMetaDataChanged()
            self.__dataChanged = False
            # self.controller.refreshButtonStatus()

    def saveWebsocketdata(self):
        """Save Websocket data."""
        for ident, key in self.hotkeys.items():
            string = scctool.settings.config.dumpHotkey(key.getKey())
            scctool.settings.config.parser.set("Intros", ident, string)
        scctool.settings.config.parser.set("Intros", "display_time",
                                           str(self.sb_displaytime.value()))
        scctool.settings.config.parser.set("Intros", "sound_volume",
                                           str(self.sl_sound.value()))
        scctool.settings.config.parser.set(
            "Intros", "animation",
            self.cb_animation.currentData().strip())
        scctool.settings.config.parser.set(
            "Intros", "tts_voice",
            self.cb_tts_voice.currentData().strip())
        scctool.settings.config.parser.set(
            "Intros", "tts_scope",
            self.cb_tts_scope.currentData().strip())
        scctool.settings.config.parser.set("Intros", "tts_active",
                                           str(self.cb_tts_active.isChecked()))
        scctool.settings.config.parser.set("Intros", "tts_volume",
                                           str(self.sl_tts_sound.value()))
        scctool.settings.config.parser.set("Intros", "tts_pitch",
                                           str(self.sb_tts_pitch.value()))
        scctool.settings.config.parser.set("Intros", "tts_rate",
                                           str(self.sb_tts_rate.value()))

    def openHTML(self, file):
        """Open file in browser."""
        self.controller.openURL(scctool.settings.getAbsPath(file))

    def changePadding(self, scope, padding):
        """Change the padding."""
        if scope == 'vetoes':
            scctool.settings.config.parser.set("Vetoes", "padding",
                                               str(padding))
            self.controller.websocketThread.changePadding("vetoes", padding)
        else:
            scctool.settings.config.parser.set("MapIcons", f"padding_{scope}",
                                               str(padding))
            self.controller.websocketThread.changePadding(
                f"mapicons_{scope}", padding)

    def saveCloseWindow(self):
        """Save and close window."""
        self.saveData()
        self.closeWindow()

    def closeWindow(self):
        """Close window without save."""
        self.passEvent = True
        self.close()

    def closeEvent(self, event):
        """Handle close event."""
        try:
            if (not self.__dataChanged):
                self.controller.updateHotkeys()
                event.accept()
                return
            if (not self.passEvent):
                if (self.isMinimized()):
                    self.showNormal()
                buttonReply = QMessageBox.question(
                    self, _('Save data?'), _("Do you want to save the data?"),
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                if buttonReply == QMessageBox.Yes:
                    self.saveData()
            self.controller.updateHotkeys()
            event.accept()
        except Exception:
            module_logger.exception("message")
class OWCSVFileImport(widget.OWWidget):
    name = "CSV File Import"
    description = "Import a data table from a CSV formatted file."
    icon = "icons/CSVFile.svg"

    outputs = [
        widget.OutputSignal(
            name="Data",
            type=Orange.data.Table,
            doc="Loaded data set."),
        widget.OutputSignal(
            name="Data Frame",
            type=pd.DataFrame,
            doc=""
        )
    ]

    class Error(widget.OWWidget.Error):
        error = widget.Msg(
            "Unexpected error"
        )
        encoding_error = widget.Msg(
            "Encoding error\n"
            "The file might be encoded in an unsupported encoding or it "
            "might be binary"
        )

    #: Paths and options of files accessed in a 'session'
    _session_items = settings.Setting(
        [], schema_only=True)  # type: List[Tuple[str, dict]]

    #: Saved dialog state (last directory and selected filter)
    dialog_state = settings.Setting({
        "directory": "",
        "filter": ""
    })  # type: Dict[str, str]
    MaxHistorySize = 50

    want_main_area = False
    buttons_area_orientation = None

    def __init__(self, *args, **kwargs):
        super().__init__(self, *args, **kwargs)

        self.__committimer = QTimer(self, singleShot=True)
        self.__committimer.timeout.connect(self.commit)

        self.__executor = qconcurrent.ThreadExecutor()
        self.__watcher = None  # type: Optional[qconcurrent.FutureWatcher]

        self.controlArea.layout().setSpacing(-1)  # reset spacing
        grid = QGridLayout()
        grid.addWidget(QLabel("File:", self), 0, 0, 1, 1)

        self.import_items_model = QStandardItemModel(self)
        self.recent_combo = QComboBox(
            self, objectName="recent-combo", toolTip="Recent files.",
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=16,
        )
        self.recent_combo.setModel(self.import_items_model)
        self.recent_combo.activated.connect(self.activate_recent)
        self.recent_combo.setSizePolicy(
            QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
        self.browse_button = QPushButton(
            "…", icon=self.style().standardIcon(QStyle.SP_DirOpenIcon),
            toolTip="Browse filesystem", autoDefault=False,
        )
        self.browse_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.browse_button.clicked.connect(self.browse)
        grid.addWidget(self.recent_combo, 0, 1, 1, 1)
        grid.addWidget(self.browse_button, 0, 2, 1, 1)
        self.controlArea.layout().addLayout(grid)

        ###########
        # Info text
        ###########
        box = gui.widgetBox(self.controlArea, "Info", addSpace=False)
        self.summary_text = QTextBrowser(
            verticalScrollBarPolicy=Qt.ScrollBarAsNeeded,
            readOnly=True,
        )
        self.summary_text.viewport().setBackgroundRole(QPalette.NoRole)
        self.summary_text.setFrameStyle(QTextBrowser.NoFrame)
        self.summary_text.setMinimumHeight(self.fontMetrics().ascent() * 2 + 4)
        self.summary_text.viewport().setAutoFillBackground(False)
        box.layout().addWidget(self.summary_text)

        button_box = QDialogButtonBox(
            orientation=Qt.Horizontal,
            standardButtons=QDialogButtonBox.Cancel | QDialogButtonBox.Retry
        )
        self.load_button = b = button_box.button(QDialogButtonBox.Retry)
        b.setText("Load")
        b.clicked.connect(self.__committimer.start)
        b.setEnabled(False)
        b.setDefault(True)

        self.cancel_button = b = button_box.button(QDialogButtonBox.Cancel)
        b.clicked.connect(self.cancel)
        b.setEnabled(False)
        b.setAutoDefault(False)

        self.import_options_button = QPushButton(
            "Import Options…", enabled=False, autoDefault=False,
            clicked=self._activate_import_dialog
        )

        self.recent_combo.currentIndexChanged.connect(
            lambda idx:
                self.import_options_button.setEnabled(idx != -1) or
                self.load_button.setEnabled(idx != -1)
        )
        button_box.addButton(
            self.import_options_button, QDialogButtonBox.ActionRole
        )
        button_box.setStyleSheet(
            "button-layout: {:d};".format(QDialogButtonBox.MacLayout)
        )
        self.controlArea.layout().addWidget(button_box)

        self._restoreState()
        if self.current_item() is not None:
            self._invalidate()
        self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum)

    @Slot(int)
    def activate_recent(self, index):
        """
        Activate an item from the recent list.
        """
        if 0 <= index < self.import_items_model.rowCount():
            item = self.import_items_model.item(index)
            assert item is not None
            path = item.data(ImportItem.PathRole)
            opts = item.data(ImportItem.OptionsRole)
            if not isinstance(opts, Options):
                opts = None
            self.set_selected_file(path, opts)
        else:
            self.recent_combo.setCurrentIndex(-1)

    @Slot()
    def browse(self):
        """
        Open a file dialog and select a user specified file.
        """
        formats = [
            "Text - comma separated (*.csv, *)",
            "Text - tab separated (*.tsv, *)",
            "Text - all files (*)"
        ]

        dlg = QFileDialog(
            self, windowTitle="Open Data File",
            acceptMode=QFileDialog.AcceptOpen,
            fileMode=QFileDialog.ExistingFile
        )
        dlg.setNameFilters(formats)
        state = self.dialog_state
        lastdir = state.get("directory", "")
        lastfilter = state.get("filter", "")

        if lastdir and os.path.isdir(lastdir):
            dlg.setDirectory(lastdir)
        if lastfilter:
            dlg.selectNameFilter(lastfilter)

        status = dlg.exec_()
        dlg.deleteLater()
        if status == QFileDialog.Accepted:
            self.dialog_state["directory"] = dlg.directory().absolutePath()
            self.dialog_state["filter"] = dlg.selectedNameFilter()

            selected_filter = dlg.selectedNameFilter()
            path = dlg.selectedFiles()[0]
            # pre-flight check; try to determine the nature of the file
            mtype = _mime_type_for_path(path)
            if not mtype.inherits("text/plain"):
                mb = QMessageBox(
                    parent=self,
                    windowTitle="",
                    icon=QMessageBox.Question,
                    text="The '{basename}' may be a binary file.\n"
                         "Are you sure you want to continue?".format(
                            basename=os.path.basename(path)),
                    standardButtons=QMessageBox.Cancel | QMessageBox.Yes
                )
                mb.setWindowModality(Qt.WindowModal)
                if mb.exec() == QMessageBox.Cancel:
                    return

            # initialize dialect based on selected extension
            if selected_filter in formats[:-1]:
                filter_idx = formats.index(selected_filter)
                if filter_idx == 0:
                    dialect = csv.excel()
                elif filter_idx == 1:
                    dialect = csv.excel_tab()
                else:
                    dialect = csv.excel_tab()
                header = True
            else:
                try:
                    dialect, header = sniff_csv_with_path(path)
                except Exception:
                    dialect, header = csv.excel(), True

            options = None
            # Search for path in history.
            # If found use the stored params to initialize the import dialog
            items = self.itemsFromSettings()
            idx = index_where(items, lambda t: samepath(t[0], path))
            if idx is not None:
                _, options_ = items[idx]
                if options_ is not None:
                    options = options_

            if options is None:
                if not header:
                    rowspec = []
                else:
                    rowspec = [(range(0, 1), RowSpec.Header)]
                options = Options(
                    encoding="utf-8", dialect=dialect, rowspec=rowspec)

            dlg = CSVImportDialog(
                self, windowTitle="Import Options",  sizeGripEnabled=True)
            dlg.setWindowModality(Qt.WindowModal)
            dlg.setPath(path)
            dlg.setOptions(options)
            status = dlg.exec_()
            dlg.deleteLater()
            if status == QDialog.Accepted:
                self.set_selected_file(path, dlg.options())

    def current_item(self):
        # type: () -> Optional[ImportItem]
        """
        Return the current selected item (file) or None if there is no
        current item.
        """
        idx = self.recent_combo.currentIndex()
        if idx == -1:
            return None

        item = self.recent_combo.model().item(idx)  # type: QStandardItem
        if isinstance(item, ImportItem):
            return item
        else:
            return None

    def _activate_import_dialog(self):
        """Activate the Import Options dialog for the  current item."""
        item = self.current_item()
        assert item is not None
        dlg = CSVImportDialog(
            self, windowTitle="Import Options", sizeGripEnabled=True,
        )
        dlg.setWindowModality(Qt.WindowModal)
        dlg.setAttribute(Qt.WA_DeleteOnClose)
        settings = QSettings()
        qualname = qname(type(self))
        settings.beginGroup(qualname)
        size = settings.value("size", QSize(), type=QSize)  # type: QSize
        if size.isValid():
            dlg.resize(size)

        path = item.data(ImportItem.PathRole)
        options = item.data(ImportItem.OptionsRole)
        dlg.setPath(path)  # Set path before options so column types can
        if isinstance(options, Options):
            dlg.setOptions(options)

        def update():
            newoptions = dlg.options()
            item.setData(newoptions, ImportItem.OptionsRole)
            # update the stored item
            self._add_recent(path, newoptions)
            if newoptions != options:
                self._invalidate()
        dlg.accepted.connect(update)

        def store_size():
            settings.setValue("size", dlg.size())
        dlg.finished.connect(store_size)
        dlg.show()

    def set_selected_file(self, filename, options=None):
        """
        Set the current selected filename path.
        """
        self._add_recent(filename, options)
        self._invalidate()

    #: Saved options for a filename
    SCHEMA = {
        "path": str,  # Local filesystem path
        "options": str,  # json encoded 'Options'
    }

    @classmethod
    def _local_settings(cls):
        # type: () -> QSettings
        """Return a QSettings instance with local persistent settings."""
        filename = "{}.ini".format(qname(cls))
        fname = os.path.join(settings.widget_settings_dir(), filename)
        return QSettings(fname, QSettings.IniFormat)

    def _add_recent(self, filename, options=None):
        # type: (str, Optional[Options]) -> None
        """
        Add filename to the list of recent files.
        """
        model = self.import_items_model
        index = index_where(
            (model.index(i, 0).data(ImportItem.PathRole)
             for i in range(model.rowCount())),
            lambda path: isinstance(path, str) and samepath(path, filename)
        )
        if index is not None:
            item, *_ = model.takeRow(index)
        else:
            item = ImportItem.fromPath(filename)

        model.insertRow(0, item)

        if options is not None:
            item.setOptions(options)

        self.recent_combo.setCurrentIndex(0)
        # store items to local persistent settings
        s = self._local_settings()
        arr = QSettings_readArray(s, "recent", OWCSVFileImport.SCHEMA)
        item = {"path": filename}
        if options is not None:
            item["options"] = json.dumps(options.as_dict())

        arr = [item for item in arr if item.get("path") != filename]
        arr.append(item)
        QSettings_writeArray(s, "recent", arr)

        # update workflow session items
        items = self._session_items[:]
        idx = index_where(items, lambda t: samepath(t[0], filename))
        if idx is not None:
            del items[idx]
        items.insert(0, (filename, options.as_dict()))
        self._session_items = items[:OWCSVFileImport.MaxHistorySize]

    def _invalidate(self):
        # Invalidate the current output and schedule a new commit call.
        # (NOTE: The widget enters a blocking state)
        self.__committimer.start()
        if self.__watcher is not None:
            self.__cancel_task()
        self.setBlocking(True)

    def commit(self):
        """
        Commit the current state and submit the load task for execution.

        Note
        ----
        Any existing pending task is canceled.
        """
        self.__committimer.stop()
        if self.__watcher is not None:
            self.__cancel_task()
        self.error()

        item = self.current_item()
        if item is None:
            return
        path = item.data(ImportItem.PathRole)
        opts = item.data(ImportItem.OptionsRole)
        if not isinstance(opts, Options):
            return

        task = state = TaskState()
        state.future = ...
        state.watcher = qconcurrent.FutureWatcher()
        state.progressChanged.connect(self.__set_read_progress,
                                      Qt.QueuedConnection)

        def progress_(i, j):
            task.emitProgressChangedOrCancel(i, j)

        task.future = self.__executor.submit(
            clear_stack_on_cancel(load_csv),
            path, opts, progress_,
        )
        task.watcher.setFuture(task.future)
        w = task.watcher
        w.done.connect(self.__handle_result)
        w.progress = state
        self.__watcher = w
        self.__set_running_state()

    @Slot('qint64', 'qint64')
    def __set_read_progress(self, read, count):
        if count > 0:
            self.progressBarSet(100 * read / count)

    def __cancel_task(self):
        # Cancel and dispose of the current task
        assert self.__watcher is not None
        w = self.__watcher
        w.future().cancel()
        w.progress.cancel = True
        w.done.disconnect(self.__handle_result)
        w.progress.progressChanged.disconnect(self.__set_read_progress)
        w.progress.deleteLater()
        # wait until completion
        futures.wait([w.future()])
        self.__watcher = None

    def cancel(self):
        """
        Cancel current pending or executing task.
        """
        if self.__watcher is not None:
            self.__cancel_task()
            self.__clear_running_state()
            self.setStatusMessage("Cancelled")
            self.summary_text.setText(
                "<div>Cancelled<br/><small>Press 'Reload' to try again</small></div>"
            )

    def __set_running_state(self):
        self.progressBarInit()
        self.setBlocking(True)
        self.setStatusMessage("Running")
        self.cancel_button.setEnabled(True)
        self.load_button.setText("Restart")
        path = self.current_item().path()
        self.Error.clear()
        self.summary_text.setText(
            "<div>Loading: <i>{}</i><br/>".format(prettyfypath(path))
        )

    def __clear_running_state(self, ):
        self.progressBarFinished()
        self.setStatusMessage("")
        self.setBlocking(False)
        self.cancel_button.setEnabled(False)
        self.load_button.setText("Reload")

    def __set_error_state(self, err):
        self.Error.clear()
        if isinstance(err, UnicodeDecodeError):
            self.Error.encoding_error(exc_info=err)
        else:
            self.Error.error(exc_info=err)

        path = self.current_item().path()
        basename = os.path.basename(path)
        if isinstance(err, UnicodeDecodeError):
            text = (
                "<div><i>{basename}</i> was not loaded due to a text encoding "
                "error. The file might be saved in an unknown or invalid "
                "encoding, or it might be a binary file.</div>"
            ).format(
                basename=escape(basename)
            )
        else:
            text = (
                "<div><i>{basename}</i> was not loaded due to an error:"
                "<p style='white-space: pre;'>{err}</p>"
            ).format(
                basename=escape(basename),
                err="".join(traceback.format_exception_only(type(err), err))
            )
        self.summary_text.setText(text)

    def __clear_error_state(self):
        self.Error.error.clear()
        self.summary_text.setText("")

    def onDeleteWidget(self):
        """Reimplemented."""
        if self.__watcher is not None:
            self.__cancel_task()
            self.__executor.shutdown()
        super().onDeleteWidget()

    @Slot(object)
    def __handle_result(self, f):
        # type: (qconcurrent.Future[pd.DataFrame]) -> None
        assert f.done()
        assert f is self.__watcher.future()
        self.__watcher = None
        self.__clear_running_state()

        try:
            df = f.result()
            assert isinstance(df, pd.DataFrame)
        except pd.errors.EmptyDataError:
            df = pd.DataFrame({})
        except Exception as e:  # pylint: disable=broad-except
            self.__set_error_state(e)
            df = None
        else:
            self.__clear_error_state()

        if df is not None:
            table = pandas_to_table(df)
        else:
            table = None
        self.send("Data Frame", df)
        self.send('Data', table)
        self._update_status_messages(table)

    def _update_status_messages(self, data):
        if data is None:
            return

        def pluralize(seq):
            return "s" if len(seq) != 1 else ""

        summary = ("{n_instances} row{plural_1}, "
                   "{n_features} feature{plural_2}, "
                   "{n_meta} meta{plural_3}").format(
                        n_instances=len(data), plural_1=pluralize(data),
                        n_features=len(data.domain.attributes),
                        plural_2=pluralize(data.domain.attributes),
                        n_meta=len(data.domain.metas),
                        plural_3=pluralize(data.domain.metas))
        self.summary_text.setText(summary)

    def itemsFromSettings(self):
        # type: () -> List[Tuple[str, Options]]
        """
        Return items from local history.
        """
        s = self._local_settings()
        items_ = QSettings_readArray(s, "recent", OWCSVFileImport.SCHEMA)
        items = []  # type: List[Tuple[str, Options]]
        for item in items_:
            path = item.get("path", "")
            if not path:
                continue
            opts_json = item.get("options", "")
            try:
                opts = Options.from_dict(json.loads(opts_json))
            except (csv.Error, LookupError, TypeError, json.JSONDecodeError):
                _log.error("Could not reconstruct options for '%s'", path,
                           exc_info=True)
                pass
            else:
                items.append((path, opts))
        return items[::-1]

    def _restoreState(self):
        # Restore the state. Merge session (workflow) items with the
        # local history.
        model = self.import_items_model
        # local history
        items = self.itemsFromSettings()
        # stored session items
        sitems = []
        for p, m in self._session_items:
            try:
                item_ = (p, Options.from_dict(m))
            except (csv.Error, LookupError) as e:
                # Is it better to fail then to lose a item slot?
                _log.error("Failed to restore '%s'", p, exc_info=True)
            else:
                sitems.append(item_)

        items = sitems + items
        items = unique(items, key=lambda t: pathnormalize(t[0]))

        curr = self.recent_combo.currentIndex()
        if curr != -1:
            currentpath = self.recent_combo.currentData(ImportItem.PathRole)
        else:
            currentpath = None
        for path, options in items:
            item = ImportItem.fromPath(path)
            item.setOptions(options)
            model.appendRow(item)

        if currentpath is not None:
            idx = self.recent_combo.findData(currentpath, ImportItem.PathRole)
            if idx != -1:
                self.recent_combo.setCurrentIndex(idx)
Пример #40
0
class Adf4113NcountLatchWidget(QGroupBox):

    bitmapChanged = pyqtSignal()
    title = 'AB count latch'

    def __init__(self, parent=None):
        super().__init__(parent)

        self.setCheckable(True)
        self.setChecked(True)

        self._slideAcount = SpinSlide(0, 63, 0, '')
        self._slideBcount = SpinSlide(3, 8191, 1, '')
        self._comboCpGain = QComboBox()

        self._containerLayout = QVBoxLayout()
        self._formLayout = QFormLayout()
        self._bitLayout = QVBoxLayout()

        self._latch = Adf4113NcountLatch()

        self._tableBits = QTableView()
        self._bitModel = BitModel(rowSize=8,
                                  bits=self._latch.bin,
                                  labels=[
                                      'X', 'X', 'G1', 'B13', 'B12', 'B11',
                                      'B10', 'B9', 'B8', 'B7', 'B6', 'B5',
                                      'B4', 'B3', 'B2', 'B1', 'A6', 'A5', 'A4',
                                      'A3', 'A2', 'A1', 'C2', 'C1'
                                  ],
                                  disabled=[
                                      True, True, False, False, False, False,
                                      False, False, False, False, False, False,
                                      False, False, False, False, False, False,
                                      False, False, False, False, True, True
                                  ],
                                  parent=self)

        self._init()

    def _init(self):
        self._containerLayout.addLayout(self._formLayout)
        self._containerLayout.addLayout(self._bitLayout)

        self._formLayout.addRow('A counter', self._slideAcount)
        self._formLayout.addRow('B counter', self._slideBcount)
        self._formLayout.addRow('Charge pump gain', self._comboCpGain)

        self._bitLayout.addWidget(self._tableBits)

        self.setLayout(self._containerLayout)

        self._comboCpGain.setModel(
            MapModel(self, self._latch.cp_gain_mode_labels, sort=False))
        self.setTitle(
            f'{self.title} (h:{self._latch.hex} b:{self._latch.bin})')

        self._tableBits.setModel(self._bitModel)

        self._tableBits.horizontalHeader().setVisible(False)
        self._tableBits.verticalHeader().setVisible(False)
        self._tableBits.verticalHeader().setDefaultSectionSize(20)
        self._tableBits.resizeColumnsToContents()
        self._tableBits.setSelectionMode(0)

        self._setupSignals()

    def _setupSignals(self):
        self._slideAcount.valueChanged.connect(self.updateBitmap)
        self._slideBcount.valueChanged.connect(self.updateBitmap)
        self._comboCpGain.currentIndexChanged.connect(self.updateBitmap)
        self._bitModel.bitChanged.connect(self.onBitChanged)

    def updateDisplay(self):
        self.setTitle(
            f'{self.title} (h:{self._latch.hex} b:{self._latch.bin})')

        self._bitModel.update(self._latch.bin)

        self.bitmapChanged.emit()

    @pyqtSlot(int)
    def updateBitmap(self, _):
        self._latch.a_counter = self._slideAcount.value()
        self._latch.b_counter = self._slideBcount.value()
        self._latch.cp_gain = self._comboCpGain.currentData(
            MapModel.RoleNodeId)

        self.updateDisplay()

    @pyqtSlot(int, int)
    def onBitChanged(self, row, col):
        self.latch.toggle_nth_bit(row * 8 + 7 - col)

        self.updateDisplay()

    @property
    def latch(self):
        return self._latch
class Downloader(QDialog):
    combobox = None
    grid = None
    selected_file = None
    session = None
    loggedIn = False
    openbutton = None
    save_dir = None
    choose_folder_button = None

    def __init__(self):
        super().__init__()
        self.session = requests.Session()
        self.session.cookies = http.cookiejar.MozillaCookieJar()
        self.init_ui()

    def init_ui(self):
        self.grid = QGridLayout()
        self.setLayout(self.grid)

        self.combobox = QComboBox()
        download_qualities = [
            ("WAV", "?format=wav"),
            ("MP3 320", "?format=mp3&bitRate=320"),
            ("MP3 V0", "?format=mp3&quality=0"),
            ("MP3 V2", "?format=mp3&quality=2"),
            ("MP3 128", "?format=mp3&bitRate=128"),
            ("FLAC", "?format=flac")
        ]
        for i in range(len(download_qualities)):
            self.combobox.addItem(download_qualities[i][0], download_qualities[i][1])

        self.openbutton = QPushButton("Select file")
        self.openbutton.clicked.connect(self.show_open_file_dialog)

        download_button = QPushButton("Download")
        download_button.clicked.connect(self.download)

        self.choose_folder_button = QPushButton("Select folder")
        self.choose_folder_button.clicked.connect(self.show_select_folder_dialog)

        # ADD WIDGETS
        self.grid.addWidget(QLabel("Select your quality: "), *(1, 1))
        self.grid.addWidget(self.combobox, *(1, 2))
        self.grid.addWidget(QLabel("Please select your JSON file: "), *(2, 1))
        self.grid.addWidget(self.openbutton, *(2, 2))
        self.grid.addWidget(QLabel("Destination folder:"), *(3, 1))
        self.grid.addWidget(self.choose_folder_button, *(3, 2))
        self.grid.addWidget(QLabel(""), *(4, 1))
        self.grid.addWidget(download_button, *(5, 2))

        # MOVE TO CENTER OF SCREEN
        self.move(QDesktopWidget().availableGeometry().center() - self.frameGeometry().center())
        self.setWindowTitle('MonstercatConnectDownloader')
        self.show()

    def show_open_file_dialog(self):
        filepicker = QFileDialog.getOpenFileName(self, 'Open file', os.path.expanduser("~"), "JSON file (*.json)")
        if filepicker[0]:
            self.selected_file = filepicker[0]
            self.openbutton.setText("File selected")
            return True
        else:
            return False

    def show_select_folder_dialog(self):
        # DIALOG WHERE TO SAVE
        self.save_dir = QFileDialog.getExistingDirectory(self, "Select folder to download", os.path.expanduser("~"))
        if not self.save_dir:
            show_popup("Error", "No folder selected.")
            return False
        self.choose_folder_button.setText("Folder selected")
        return True

    def show_sign_in_dialog(self):
        dialog = SignInDialog(self)
        dialog.exec_()

    def download(self):
        # GET FILE
        if not self.selected_file:
            show_popup("Error", "Please select a file first.")
            return False
        if not self.save_dir:
            show_popup("Error", "Please select a destination folder first.")
            return False
        with open(self.selected_file) as f:
            album_ids = json.loads(f.read())

        # GET SELECTED QUALITY
        quality = self.combobox.currentData()

        # LOAD COOKIES IF EXIST
        cj, successful = load_cookies(COOKIE_FILE)
        if successful:
            self.session.cookies = cj
            self.loggedIn = True
            show_popup("Logged in", "Automatically logged in.")

        # GET SESSION
        if not self.loggedIn:
            self.show_sign_in_dialog()

        # CHECK IF LOGIN SUCESSFUL
        if not self.loggedIn:
            show_popup("Error", "Login failed.")
            return
        length = str(len(album_ids))
        bar = QProgressDialog("Downloading songs (1/" + length + ")", "Cancel", 0, int(length))
        bar.setWindowTitle("Downloading songs")
        bar.setValue(0)
        count = 1
        downloadsuccess = True
        # DOWNLOAD
        for album_id in album_ids:
            download_link = DOWNLOAD_BASE + album_id + "/download" + quality
            success = download_file(download_link, self.save_dir, self.session)
            if not success:
                show_popup("Cancelled", "Download was cancelled.")
                downloadsuccess = False
                break

            bar.setValue(count)
            bar.setLabelText("Downloading songs (" + str(count) + "/" + length + ")")
            count += 1
            if bar.wasCanceled():
                show_popup("Cancelled", "Download was cancelled.")
                downloadsuccess = False
                break
            QApplication.processEvents()
            # break     # activate for testing

        if downloadsuccess:
            show_popup("Success!", "Download finished!")
        else:
            show_popup("Finished.", "Finished with errors. Probably cancelled.")
Пример #42
0
class Settings_Net(QWidget):
    def __init__(self, parent: QWidget):
        super(Settings_Net, self).__init__(parent)
        self._layout = QVBoxLayout()
        self.setLayout(self._layout)
        # construct layout
        min_width = 150
        self.user_agents = dict()
        self.user_agents["chrome_win7_x64"] = (
            "Chrome 41, Windows 7 x64",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) "
            "AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/41.0.2227.0 Safari/537.36",
        )
        self.user_agents["chrome_linux_64"] = (
            "Chrome 41, Linux x86_64",
            "Mozilla/5.0 (X11; Linux x86_64) "
            "AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/41.0.2227.0 Safari/537.36",
        )
        self.user_agents["chrome_android"] = (
            "Chrome 47, Android 4.3 Galaxy-S3",
            "Mozilla/5.0 (Linux; Android 4.3; GT-I9300 Build/JSS15J) "
            "AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/47.0.2526.83 Mobile Safari/537.36",
        )
        self.user_agents["firefox_win32"] = (
            "Firefox 40, Windows 7 32-bit",
            "Mozilla/5.0 (Windows NT 6.1; rv:40.0) " "Gecko/20100101 Firefox/40.1",
        )
        self.user_agents["firefox_android"] = (
            "Firefox, Android 4.3 Galaxy-S3",
            "Mozilla/5.0 (Android 4.3; Mobile; rv:43.0) " "Gecko/43.0 Firefox/43.0",
        )
        self.user_agents["edge_win10"] = (
            "Microsoft Edge, Windows 10 x64",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/42.0.2311.135 Safari/537.36 Edge/12.246",
        )
        # server URL
        self._l_surl = QHBoxLayout()
        self._l_ua = QHBoxLayout()
        self._lbl_surl = QLabel(self.tr("Server URL:"), self)
        self._lbl_surl.setMinimumWidth(min_width)
        self._le_surl = QLineEdit(self)
        self._l_surl.addWidget(self._lbl_surl)
        self._l_surl.addWidget(self._le_surl)
        self._layout.addLayout(self._l_surl)
        # emulate browser combo box
        self._l_eb = QHBoxLayout()
        self._lbl_eb = QLabel(self.tr("Emulate browser:"), self)
        self._lbl_eb.setMinimumWidth(min_width)
        self._cb_eb = QComboBox(self)
        self._cb_eb.setEditable(False)
        self._cb_eb.setInsertPolicy(QComboBox.InsertAtBottom)
        ua_keylist = [i for i in self.user_agents.keys()]
        ua_keylist.sort()
        for key_id in ua_keylist:
            b_tuple = self.user_agents[key_id]
            display_string = b_tuple[0]
            self._cb_eb.addItem(display_string, QVariant(str(key_id)))
        self._cb_eb.addItem(self.tr("<Custom>"), QVariant("custom"))
        self._l_eb.addWidget(self._lbl_eb)
        self._l_eb.addWidget(self._cb_eb)
        self._layout.addLayout(self._l_eb)
        # custom user-agent string
        self._lbl_ua = QLabel(self.tr("User-agent string:"), self)
        self._lbl_ua.setMinimumWidth(min_width)
        self._le_ua = QLineEdit(self)
        self._l_ua.addWidget(self._lbl_ua)
        self._l_ua.addWidget(self._le_ua)
        self._layout.addLayout(self._l_ua)
        # proxy settings
        self._l_proxy = QHBoxLayout()
        self._lbl_proxy = QLabel(self.tr("Proxy type:"), self)
        self._lbl_proxy.setMinimumWidth(min_width)
        self._cb_proxy = QComboBox(self)
        self._cb_proxy.setEditable(False)
        self._cb_proxy.addItem(self.tr("No proxy"), QVariant("none"))
        self._cb_proxy.addItem(self.tr("HTTP proxy"), QVariant("http"))
        self._cb_proxy.addItem(self.tr("SOCKS5 proxy"), QVariant("socks5"))
        self._l_proxy.addWidget(self._lbl_proxy)
        self._l_proxy.addWidget(self._cb_proxy)
        self._layout.addLayout(self._l_proxy)
        self._l_proxy_s = QHBoxLayout()
        self._lbl_proxy_s = QLabel(self.tr("Proxy addr:port:"), self)
        self._lbl_proxy_s.setMinimumWidth(min_width)
        self._le_proxy_addr = QLineEdit(self)
        self._l_proxy_s.addWidget(self._lbl_proxy_s)
        self._l_proxy_s.addWidget(self._le_proxy_addr)
        self._layout.addLayout(self._l_proxy_s)
        # all connections
        self._cb_eb.currentIndexChanged.connect(self.on_cb_eb_current_index_changed)
        self._cb_proxy.currentIndexChanged.connect(self.on_cb_proxy_current_index_changed)
        # finalize
        self._layout.addStretch()

    @pyqtSlot(int)
    def on_cb_eb_current_index_changed(self, index: int):
        key_id = str(self._cb_eb.currentData(Qt.UserRole))
        if key_id == "custom":
            self._le_ua.setEnabled(True)
            return
        self._le_ua.setEnabled(False)
        if key_id in self.user_agents:
            b_tuple = self.user_agents[key_id]
            ua_str = b_tuple[1]
            self._le_ua.setText(ua_str)

    @pyqtSlot(int)
    def on_cb_proxy_current_index_changed(self, index: int):
        if index == 0:
            self._le_proxy_addr.setEnabled(False)
        else:
            self._le_proxy_addr.setEnabled(True)

    def ua_select(self, key_id: str):
        cnt = self._cb_eb.count()
        for i in range(cnt):
            item_key_id = str(self._cb_eb.itemData(i, Qt.UserRole))
            if item_key_id == key_id:
                self._cb_eb.setCurrentIndex(i)
                break

    def load_from_config(self, cfg: configparser.ConfigParser):
        # defaults
        xnova_url = "uni4.xnova.su"
        user_agent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0"
        user_agent_id = "custom"
        proxy = ""
        if "net" in cfg:
            xnova_url = cfg["net"]["xnova_url"]
            user_agent = cfg["net"]["user_agent"]
            user_agent_id = cfg["net"]["user_agent_id"]
            proxy = cfg["net"]["proxy"]
        self._le_surl.setText(xnova_url)
        self._le_surl.setEnabled(False)  # cannot be edited by user, for safety!
        # deal with user-agent
        self._le_ua.setText(user_agent)
        if user_agent_id == "custom":
            self._le_ua.setEnabled(True)
        else:
            self._le_ua.setEnabled(False)
        self.ua_select(user_agent_id)
        # deal with proxy
        if proxy == "":
            self._le_proxy_addr.setText("")
            self._cb_proxy.setCurrentIndex(0)
            self._le_proxy_addr.setEnabled(False)
        elif proxy.startswith("http://"):
            self._cb_proxy.setCurrentIndex(1)
            proxy_addr = proxy[7:]
            self._le_proxy_addr.setText(proxy_addr)
            self._le_proxy_addr.setEnabled(True)
        elif proxy.startswith("socks5://"):
            self._cb_proxy.setCurrentIndex(2)
            proxy_addr = proxy[9:]
            self._le_proxy_addr.setText(proxy_addr)
            self._le_proxy_addr.setEnabled(True)
        else:
            raise ValueError("Invalid proxy setting: " + proxy)

    def save_to_config(self, cfg: configparser.ConfigParser):
        # ensure there is a 'net' section
        if "net" not in cfg:
            cfg.add_section("net")
        # skip server url
        # deal with user-agent
        user_agent_id = ""
        user_agent = ""
        idx = self._cb_eb.currentIndex()
        if idx >= 0:
            user_agent_id = str(self._cb_eb.itemData(idx, Qt.UserRole))
            cfg["net"]["user_agent_id"] = user_agent_id
        user_agent = self._le_ua.text().strip()
        if user_agent != "":
            cfg["net"]["user_agent"] = user_agent
        # deal with proxy
        idx = self._cb_proxy.currentIndex()
        proxy_addr = self._le_proxy_addr.text().strip()
        if idx == 0:
            cfg["net"]["proxy"] = ""
        elif idx == 1:
            cfg["net"]["proxy"] = "http://" + proxy_addr
        elif idx == 2:
            cfg["net"]["proxy"] = "socks5://" + proxy_addr
        logger.debug("Saved network config")