Exemplo n.º 1
0
    def load_game_list(self, game_layout: QGridLayout):
        while game_layout.count():
            child = game_layout.takeAt(0)
            if child.widget():
                child.widget().deleteLater()
        games = self.all_displays
        all_entries = iter_entry_points('zero_play.game_display')
        filtered_entries = self.filter_games(all_entries)
        for game_entry in filtered_entries:
            display_class = game_entry.load()
            display: GameDisplay = display_class()
            self.destroyed.connect(display.close)  # type: ignore
            display.game_ended.connect(self.on_game_ended)  # type: ignore
            games.append(display)
        games.sort(key=attrgetter('start_state.game_name'))
        column_count = math.ceil(math.sqrt(len(games)))
        for i, display in enumerate(games):
            row = i // column_count
            column = i % column_count
            game_name = display.start_state.game_name
            game_button = QPushButton(game_name)
            game_button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

            game_button.clicked.connect(
                partial(
                    self.show_game,  # type: ignore
                    display))
            game_layout.addWidget(game_button, row, column)

            self.ui.history_game.addItem(game_name, userData=display)

            if display.rules_path is not None:
                game_rules_action = self.ui.menu_rules.addAction(game_name)
                game_rules_action.triggered.connect(
                    partial(self.on_rules, display))
Exemplo n.º 2
0
class InfoWidget(QWidget):
    """Display basic file information in a table (two columns).

    Parameters
    ----------
    values : dict
        Each key/value pair in this dict will be displayed in a row, separated by a colon.
    """
    def __init__(self, values=None):
        super().__init__()
        vbox = QVBoxLayout(self)
        self.grid = QGridLayout()
        self.grid.setColumnStretch(1, 1)
        vbox.addLayout(self.grid)
        vbox.addStretch(1)
        self.set_values(values)

    def set_values(self, values=None):
        """Set values (and overwrite existing values).

        Parameters
        ----------
        values : dict
            Each key/value pair in this dict is displayed in a row separated by a colon.
        """
        self.clear()
        if values:
            for row, (key, value) in enumerate(values.items()):
                left = QLabel(str(key) + ":")
                right = QLabel(str(value))
                right.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)
                self.grid.addWidget(left, row, 0)
                self.grid.addWidget(right, row, 1)

    def clear(self):
        """Clear all values."""
        item = self.grid.takeAt(0)
        while item:
            item.widget().deleteLater()
            del item
            item = self.grid.takeAt(0)
Exemplo n.º 3
0
    class ResultsGrid(QScrollArea):
        def __init__(self, parent=None):
            QScrollArea.__init__(self)
            self.setParent(parent)

            # Make QScrollArea transparent
            self.setStyleSheet(
                "QScrollArea { background-color: transparent } .QFrame { background-color: transparent }"
            )
            self.setWidgetResizable(True)
            self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

            # Add Touch Gestures to menu.
            QScroller.grabGesture(self, QScroller.LeftMouseButtonGesture)

            # Layout settings
            self.layout = QGridLayout()
            self.layout.setContentsMargins(0, 0, 0, 0)
            self.layout.setSpacing(0)

            # QScrollArea requires a separate QWidget to work properly
            frame = QFrame()
            frame.setLayout(self.layout)

            self.setWidget(frame)

        def clearResults(self):
            # Clear the results in the list
            while self.layout.count():
                item = self.layout.takeAt(0)
                if item.widget() is not None:
                    item.widget().deleteLater()

        def addResults(self, results):
            # Add the results to the list
            # results (list): list of python dict representing results details (playlist or song search)
            self.clearResults()
            for i in range(len(results)):
                item = self.ResultsGridItem(results[i], self)
                self.layout.addWidget(item, i // 6, i % 6)
            if results:
                self.layout.setRowStretch(self.layout.rowCount(), 1)
                self.layout.setColumnStretch(self.layout.columnCount(), 1)
            else:
                # If the results are empty, display no result
                label = QLabel("没有结果/No Result")
                label.setStyleSheet("color: white")
                label.setAlignment(Qt.AlignCenter)
                font = QFont()
                font.setPointSize(35)
                label.setFont(font)

                self.layout.addWidget(label)

        class ResultsGridItem(QToolButton):
            def __init__(self, result, parent=None):
                QToolButton.__init__(self)
                self.setParent(parent)
                self.result = result

                self.setContentsMargins(0, 0, 0, 0)
                # Button formatting
                self.setFixedSize(200, 240)
                self.setAutoRaise(True)
                # TODO: change with global themes
                self.setStyleSheet(
                    "QToolButton:pressed { background-color: rgba(255, 255, 255, 0.1)} QToolButton { background-color: rgba(255, 255, 255, 0.05); border: 1px solid white; color: white}"
                )

                # Set layout settings
                self.layout = QGridLayout()
                self.layout.setContentsMargins(0, 0, 0, 0)
                self.layout.setSpacing(0)

                if result["type"] == "artists":
                    # Artist image
                    self.formattedImage(self.window().artistPath +
                                        result["artist_path"])
                    self.formattedLabel(result["artist_name"])

                    # Favourite button
                    self.favouriteButton = QToolButton()
                    self.favouriteButton.setStyleSheet(
                        "QToolButton:pressed { background-color: rgb(31, 41, 75)} QToolButton { background-color: rgb(25, 33, 60);}"
                    )

                    # Toggle Favourite Icon depending on DB
                    if self.result["favourited"] == 0:
                        self.favouriteButton.isFavourited = False
                        self.favouriteButton.setIcon(QIcon("icons/star.svg"))
                    else:
                        self.favouriteButton.isFavourited = True
                        self.favouriteButton.setIcon(
                            QIcon("icons/star-yellow.svg"))
                    self.favouriteButton.setIconSize(QSize(30, 30))
                    self.favouriteButton.setFixedSize(70, 70)
                    self.favouriteButton.clicked.connect(self.clickedFavourite)
                    self.layout.addWidget(self.favouriteButton, 0, 0,
                                          Qt.AlignRight | Qt.AlignTop)

                    self.clicked.connect(self.clickedArtist)
                elif result["type"] == "languages":
                    # Language image
                    self.formattedImage(self.window().languagePath +
                                        result["language_path"])
                    self.formattedLabel(result["language_name"])
                    self.clicked.connect(self.clickedLanguage)

                self.setLayout(self.layout)

            def clickedFavourite(self):
                # Toggle Icon and DB state of song being favourited
                # For artists only
                if self.favouriteButton.isFavourited:
                    self.favouriteButton.isFavourited = False
                    self.favouriteButton.setIcon(QIcon("icons/star.svg"))
                else:
                    self.favouriteButton.isFavourited = True
                    self.favouriteButton.setIcon(
                        QIcon("icons/star-yellow.svg"))
                DB.setFavouriteArtist(self.result["artist_id"])

            def formattedImage(self, path):
                # Format an image given the path
                self.setIcon(QIcon(path))
                self.setIconSize(QSize(180, 180))

            def formattedLabel(self, text):
                # Format a label given text
                font = QFont()
                font.setPixelSize(18)
                self.setText(text)
                self.setFont(font)
                self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)

            def clickedArtist(self):
                # Called when an Artist is clicked on
                self.window().content.addWidget(
                    WindowSearch(
                        "搜索全部/Search", self,
                        self.window().content.currentWidget().counter + 1,
                        self.result))
                self.window().content.setCurrentIndex(
                    self.window().content.currentWidget().counter + 1)

            def clickedLanguage(self):
                # Called when a Language is clicked on
                self.window().content.addWidget(
                    WindowSearch(
                        "搜索歌手/Artist Search",
                        self,
                        self.window().content.currentWidget().counter + 1,
                        self.result,
                        grid=True))
                self.window().content.setCurrentIndex(
                    self.window().content.currentWidget().counter + 1)
Exemplo n.º 4
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.art_scene = QGraphicsScene(self)
        self.art_scene.addText('Open an image file.')
        self.ui.art_view.setScene(self.art_scene)
        self.symbols_scene = QGraphicsScene(self)
        self.ui.symbols_view.setScene(self.symbols_scene)
        self.ui.action_exit.triggered.connect(self.close)
        self.ui.action_open_art.triggered.connect(self.open_image)
        self.ui.action_open_words.triggered.connect(self.open_words)
        self.ui.action_save.triggered.connect(self.save_pdf)
        self.ui.action_save_png.triggered.connect(self.save_png)
        self.ui.action_shuffle.triggered.connect(self.shuffle)
        self.ui.action_sort.triggered.connect(self.sort)
        self.ui.rows.valueChanged.connect(self.on_options_changed)
        self.ui.columns.valueChanged.connect(self.on_options_changed)
        self.ui.word_clues_radio.toggled.connect(self.on_options_changed)
        self.ui.symbol_clues_radio.toggled.connect(self.on_options_changed)

        self.word_layout = QGridLayout(self.ui.word_content)
        self.ui.word_scroll.setWidgetResizable(True)
        self.word_labels: typing.Dict[str, QLabel] = {}
        self.word_shuffler = WordShuffler([])

        self.clues = None

        self.pixmap = self.scaled_pixmap = self.mini_pixmap = None
        self.sliced_pixmap_item: typing.Optional[QGraphicsPixmapItem] = None
        self.sliced_image: typing.Optional[QImage] = None
        self.selection_grid: typing.Optional[SelectionGrid] = None
        self.cells = []
        self.art_shuffler: typing.Optional[ArtShuffler] = None
        self.symbols_source_pixmap_item: typing.Optional[
            QGraphicsPixmapItem] = None
        self.symbols_pixmap_item: typing.Optional[QGraphicsPixmapItem] = None
        self.symbols_image: typing.Optional[QImage] = None
        self.symbols_shuffler: typing.Optional[ArtShuffler] = None
        self.selected_row: typing.Optional[int] = None
        self.selected_column: typing.Optional[int] = None
        self.settings = QSettings()
        self.image_path: typing.Optional[str] = self.settings.value(
            'image_path')
        self.words_path: typing.Optional[str] = self.settings.value(
            'words_path')

        self.dirty_letters = set()
        self.timer = QTimer()
        self.timer.setInterval(500)
        self.timer.setSingleShot(True)
        # noinspection PyUnresolvedReferences
        self.timer.timeout.connect(self.on_dirty)

        self.row_count = self.column_count = 0
        self.clue_type = ClueType.words
        self.ui.rows.setValue(self.settings.value('row_count', 6, int))
        self.ui.columns.setValue(self.settings.value('column_count', 4, int))
        clue_type_name = self.settings.value('clue_type', ClueType.words.name)
        try:
            clue_type = ClueType[clue_type_name]
        except KeyError:
            clue_type = ClueType.words
        if clue_type == ClueType.words:
            self.ui.word_clues_radio.setChecked(True)
        else:
            self.ui.symbol_clues_radio.setChecked(True)
        self.row_clues: typing.List[QPixmap] = []
        self.column_clues: typing.List[QPixmap] = []
        self.on_options_changed()

    def on_dirty(self):
        for letter in self.dirty_letters:
            self.word_labels[letter].setText(
                self.word_shuffler.make_display(letter))
            self.settings.setValue(f'word_{letter}',
                                   self.word_shuffler[letter])
        if self.dirty_letters:
            self.clues = self.word_shuffler.make_clues()
            self.art_shuffler.clues = dict(self.clues)
            self.dirty_letters.clear()
            self.on_selection_moved()

        if self.pixmap is not None:
            x, y, width, height = self.get_selected_fraction()
            self.settings.setValue('x', x)
            self.settings.setValue('y', y)
            self.settings.setValue('width', width)
            self.settings.setValue('height', height)

        new_rows = self.ui.rows.value()
        new_columns = self.ui.columns.value()
        if self.ui.word_clues_radio.isChecked():
            new_clue_type = ClueType.words
        else:
            new_clue_type = ClueType.symbols
        if (new_rows, new_columns,
                new_clue_type) == (self.row_count, self.column_count,
                                   self.clue_type):
            return
        self.settings.setValue('row_count', new_rows)
        self.settings.setValue('column_count', new_columns)
        self.settings.setValue('clue_type', new_clue_type.name)
        self.row_count, self.column_count = new_rows, new_columns
        self.clue_type = new_clue_type

        word_count = (self.row_count * self.column_count)
        while self.word_layout.count():
            layout_item = self.word_layout.takeAt(0)
            layout_item.widget().deleteLater()
        self.word_labels.clear()

        self.row_clues.clear()
        self.column_clues.clear()
        if self.image_path is not None:
            self.load_image(self.image_path)

        if self.words_path is not None:
            self.load_words(self.words_path)

        letters = [chr(65 + i) for i in range(word_count)]
        if self.word_shuffler.needs_blank:
            letters.insert(0, '')
        word_fields = {}
        for i, letter in enumerate(letters):
            word_field = QLineEdit()
            self.word_layout.addWidget(word_field, i, 0)
            # noinspection PyUnresolvedReferences
            word_field.textEdited.connect(partial(self.on_word_edited, letter))

            word_label = QLabel()
            self.word_layout.addWidget(word_label, i, 1)
            self.word_labels[letter] = word_label
            word_fields[letter] = word_field
        for i, letter in enumerate(letters):
            word = self.settings.value(f'word_{letter}', '')
            self.word_shuffler[letter] = word
            self.dirty_letters.add(letter)
            word_fields[letter].setText(word)

    def on_options_changed(self, *_):
        self.timer.start()

    def shuffle(self):
        self.clues = self.word_shuffler.make_clues()
        if self.art_shuffler is not None:
            self.art_shuffler.shuffle()
            self.on_selection_moved()

    def sort(self):
        if self.art_shuffler is not None:
            self.art_shuffler.sort()
            self.on_selection_moved()

    def open_words(self):
        word_filter = 'Text files (*.txt)'
        if self.words_path is None:
            words_folder = None
        else:
            words_folder = str(Path(self.words_path).parent)
        file_name, _ = QFileDialog.getOpenFileName(self,
                                                   "Open a words file.",
                                                   dir=words_folder,
                                                   filter=word_filter)
        if not file_name:
            return
        self.settings.setValue('words_path', file_name)
        self.load_words(file_name)

    def load_words(self, words_path):
        with open(words_path) as f:
            choice = 0
            if choice == 0:
                self.word_shuffler = WordShuffler(f)
            else:
                self.word_shuffler = WordStripper(f)

    def open_image(self):
        formats = QImageReader.supportedImageFormats()
        patterns = (f'*.{fmt.data().decode()}' for fmt in formats)
        image_filter = f'Images ({" ".join(patterns)})'
        if self.image_path is None:
            image_folder = None
        else:
            image_folder = str(Path(self.image_path).parent)
        file_name, _ = QFileDialog.getOpenFileName(self,
                                                   "Open an image file.",
                                                   dir=image_folder,
                                                   filter=image_filter)
        if not file_name:
            return
        self.settings.setValue('image_path', file_name)
        self.load_image(file_name)

    def load_image(self, image_path):
        self.pixmap = QPixmap(image_path)
        if self.pixmap.isNull():
            self.pixmap = None
        self.image_path = image_path
        self.scale_image()

    def scale_image(self):
        if self.pixmap is None:
            return

        if self.selection_grid is None:
            x = self.settings.value('x', 0.0, float)
            y = self.settings.value('y', 0.0, float)
            width = self.settings.value('width', 1.0, float)
            height = self.settings.value('height', 1.0, float)
        else:
            x, y, width, height = self.get_selected_fraction()
        self.art_scene.clear()
        self.cells.clear()
        view_size = self.ui.art_view.maximumViewportSize()
        if view_size.width() == 0:
            return
        self.art_scene.setSceneRect(0, 0, view_size.width(),
                                    view_size.height())
        display_size = QSize(view_size.width() * 0.99 / 2,
                             view_size.height() * 0.99)
        self.scaled_pixmap = self.pixmap.scaled(
            display_size, aspectMode=Qt.AspectRatioMode.KeepAspectRatio)
        self.art_scene.addPixmap(self.scaled_pixmap)
        scaled_size = self.scaled_pixmap.size()
        self.selection_grid = SelectionGrid(scaled_size.width() * x,
                                            scaled_size.height() * y,
                                            scaled_size.width() * width,
                                            scaled_size.height() * height,
                                            row_count=self.row_count,
                                            column_count=self.column_count)
        self.selection_grid.on_moved = self.on_selection_moved
        self.art_scene.addItem(self.selection_grid)
        self.sliced_image = QImage(display_size,
                                   QImage.Format.Format_ARGB32_Premultiplied)
        self.check_clues()
        self.art_shuffler = ArtShuffler(self.selection_grid.row_count,
                                        self.selection_grid.column_count,
                                        self.sliced_image,
                                        QRect(0, 0, display_size.width(),
                                              display_size.height()),
                                        clues=self.clues,
                                        row_clues=self.row_clues,
                                        column_clues=self.column_clues)
        self.sliced_pixmap_item = self.art_scene.addPixmap(
            QPixmap.fromImage(self.sliced_image))
        self.sliced_pixmap_item.setPos(display_size.width(), 0)

        self.symbols_scene.clear()
        self.symbols_source_pixmap_item = self.symbols_scene.addPixmap(
            self.scaled_pixmap)
        self.symbols_image = QImage(display_size,
                                    QImage.Format.Format_ARGB32_Premultiplied)
        if self.symbols_shuffler is not None:
            selected_row = self.symbols_shuffler.selected_row
            selected_column = self.symbols_shuffler.selected_column
        else:
            selected_row = 0
            selected_column = None
        self.symbols_shuffler = ArtShuffler(self.selection_grid.row_count,
                                            self.selection_grid.column_count,
                                            self.symbols_image,
                                            QRect(0, 0, display_size.width(),
                                                  display_size.height()),
                                            row_clues=self.row_clues,
                                            column_clues=self.column_clues)
        self.symbols_shuffler.selected_row = selected_row
        self.symbols_shuffler.selected_column = selected_column
        self.symbols_pixmap_item = ClickablePixmapItem(
            QPixmap.fromImage(self.symbols_image))
        self.symbols_pixmap_item.on_click = self.on_symbols_clicked
        self.symbols_scene.addItem(self.symbols_pixmap_item)

        self.symbols_pixmap_item.setPos(display_size.width(), 0)

        self.on_selection_moved()

    def on_symbols_clicked(self, event: QGraphicsSceneMouseEvent):
        self.symbols_scene.clearSelection()
        self.symbols_shuffler.select_clue(event.pos().toPoint())
        self.on_selection_moved()

    def on_word_edited(self, letter, word):
        self.word_shuffler[letter] = word
        self.dirty_letters.add(letter)
        self.timer.start()

    def get_selected_fraction(self):
        selection_rect = self.selection_grid.rect()
        selection_pos = self.selection_grid.pos()
        size = self.scaled_pixmap.size()
        x = (selection_pos.x() + selection_rect.x()) / size.width()
        width = selection_rect.width() / size.width()
        y = (selection_pos.y() + selection_rect.y()) / size.height()
        height = selection_rect.height() / size.height()
        return x, y, width, height

    def on_selection_moved(self):
        selected_pixmap = self.get_selected_pixmap()
        self.art_shuffler.draw(selected_pixmap)
        self.sliced_pixmap_item.setPixmap(QPixmap.fromImage(self.sliced_image))

        selected_pixmap = self.get_selected_pixmap()
        cell_width = (selected_pixmap.width() /
                      self.selection_grid.column_count)
        cell_height = (selected_pixmap.height() /
                       self.selection_grid.row_count)
        self.row_clues.clear()
        self.column_clues.clear()
        for i in range(self.selection_grid.row_count):
            clue_image = selected_pixmap.copy(0, i * cell_height, cell_width,
                                              cell_height)
            self.row_clues.append(clue_image)
        for j in range(self.selection_grid.column_count):
            clue_image = selected_pixmap.copy(j * cell_width, 0, cell_width,
                                              cell_height)
            self.column_clues.append(clue_image)
        self.symbols_shuffler.row_clues = self.row_clues
        self.symbols_shuffler.column_clues = self.column_clues

        self.symbols_shuffler.draw_grid(selected_pixmap)
        self.symbols_pixmap_item.setPixmap(
            QPixmap.fromImage(self.symbols_image))
        self.timer.start()

    def get_selected_pixmap(self) -> QPixmap:
        x, y, width, height = self.get_selected_fraction()
        original_size = self.pixmap.size()
        selected_pixmap = self.pixmap.copy(x * original_size.width(),
                                           y * original_size.height(),
                                           width * original_size.width(),
                                           height * original_size.height())
        return selected_pixmap

    def resizeEvent(self, event: QResizeEvent):
        super().resizeEvent(event)
        self.scale_image()

    def save_pdf(self):
        pdf_folder = self.settings.value('pdf_folder')
        file_name, _ = QFileDialog.getSaveFileName(self,
                                                   "Save a PDF file.",
                                                   dir=pdf_folder,
                                                   filter='Documents (*.pdf)')
        if not file_name:
            return
        self.settings.setValue('pdf_folder', os.path.dirname(file_name))
        writer = QPdfWriter(file_name)
        writer.setPageSize(QPageSize(QPageSize.Letter))
        writer.setTitle('Sliced Art Puzzle')
        writer.setCreator('Don Kirkby')
        self.paint_puzzle(writer)

    def save_png(self):
        pdf_folder = self.settings.value('pdf_folder')
        file_name, _ = QFileDialog.getSaveFileName(self,
                                                   "Save an image file.",
                                                   dir=pdf_folder,
                                                   filter='Images (*.png)')
        if not file_name:
            return
        writer = QPixmap(1000, 2000)
        self.paint_puzzle(writer)
        writer.save(file_name)
        self.settings.setValue('pdf_folder', os.path.dirname(file_name))

    def paint_puzzle(self, writer: QPaintDevice):
        self.check_clues()
        painter = QPainter(writer)
        try:
            print_shuffler = ArtShuffler(self.art_shuffler.rows,
                                         self.art_shuffler.cols,
                                         writer,
                                         QRect(0, 0, writer.width(),
                                               round(writer.height() / 2)),
                                         clues=self.clues,
                                         row_clues=self.row_clues,
                                         column_clues=self.column_clues)
            print_shuffler.cells = self.art_shuffler.cells[:]
            print_shuffler.is_shuffled = self.art_shuffler.is_shuffled
            selected_pixmap = self.get_selected_pixmap()
            print_shuffler.draw(selected_pixmap, painter)

            print_shuffler.rect.moveTop(writer.height() / 2)
            print_shuffler.draw_grid(selected_pixmap, painter)
        finally:
            painter.end()

    def check_clues(self):
        if self.clue_type == ClueType.words:
            if self.clues is None:
                self.clues = self.word_shuffler.make_clues()
            self.row_clues.clear()
            self.column_clues.clear()
        else:
            self.clues = None
Exemplo n.º 5
0
class SettingWidget(QWidget):
    settings_changed_signal = QtCore.Signal()

    def __init__(self, parent, settings=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.setObjectName("settings_widget")
        self.group_box = QGroupBox("Настройки")
        self.layout = QGridLayout()
        self.layout.setObjectName('settings_layout')
        self.layout.setColumnStretch(0, 6)
        self.layout.setColumnStretch(1, 1)
        self.group_box.setLayout(self.layout)
        self.widgets = []

        self.change_settings(settings)

    def change_settings(self, settings):

        # First clean up all previous items
        for i in range(self.layout.count()):
            self.layout.removeItem(self.layout.takeAt(0))
        for w in self.widgets:
            w.deleteLater()
        self.widgets = []

        if not settings:
            label = QLabel(self.parentWidget())
            label.setObjectName("settings_empty_label")
            label.setText("Для данной операции настройки отсутствуют")
            label.setWordWrap(True)
            self.widgets.append(label)
            self.layout.addWidget(label, 0, 0)

        else:
            x = 0
            for s in settings:
                if isinstance(s.type, range):
                    label = QLabel(self.parentWidget())
                    label.setText(s.text)
                    label.setWordWrap(True)
                    self.widgets.append(label)
                    self.layout.addWidget(label, x, 0)

                    slider = QSlider(self.parentWidget())
                    slider.setOrientation(Qt.Horizontal)
                    slider.setMinimum(s.type.start // s.type.step)
                    slider.setMaximum(s.type.stop // s.type.step - 1)
                    slider.setValue(s.value // s.type.step)
                    self.layout.addWidget(slider, x + 1, 0)

                    line_edit = QLineEdit(self.parentWidget())
                    line_edit.setText(str(s.value))
                    self.layout.addWidget(line_edit, x + 1, 1)

                    slider.valueChanged.connect(
                        partial(self.slider_change, line_edit, s))
                    line_edit.returnPressed.connect(
                        partial(self.line_edit_change, slider, line_edit, s))

                    self.widgets.append(slider)
                    self.widgets.append(line_edit)
                    x += 2

                elif s.type == bool:
                    checkbox = QCheckBox(self.parentWidget())
                    checkbox.setText('\n'.join(wrap(s.text, 40)))
                    checkbox.setChecked(s.value)
                    checkbox.clicked.connect(
                        partial(self.checkbox_changed, checkbox, s))

                    self.layout.addWidget(checkbox, x, 0)
                    self.widgets.append(checkbox)
                    x += 1
                elif s.type in (int, float, str):
                    pass

    def slider_change(self, line_edit: QLineEdit, settings_item, value):
        value *= settings_item.type.step
        if settings_item.value != value:
            line_edit.setText(str(value))
            settings_item.value = value
            self.settings_changed_signal.emit()

    def line_edit_change(self, slider: QSlider, line_edit: QLineEdit,
                         settings_item):
        value = line_edit.text()
        try:
            if settings_item.type == int or isinstance(settings_item.type,
                                                       range):
                value = int(value)
            elif settings_item.type == float:
                value = float(value)
            if isinstance(settings_item.type,
                          range) and value not in settings_item.type:
                raise ValueError
        except ValueError:
            line_edit.setText(str(settings_item.value))
            return
        if settings_item.value != value:
            settings_item.value = value
            if slider is not None:
                slider.setValue(value // settings_item.type.step)
            self.settings_changed_signal.emit()

    def checkbox_changed(self, checkbox: QCheckBox, settings_item):
        if settings_item.value != checkbox.isChecked():
            settings_item.value = checkbox.isChecked()
            self.settings_changed_signal.emit()