Пример #1
0
class Window(QWidget):

    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        layout = QVBoxLayout(self)

        # 列表
        self.listWidget = QListWidget(self)
        layout.addWidget(self.listWidget)

        # 清空按钮
        self.clearBtn = QPushButton('清空', self, clicked=self.doClearItem)
        layout.addWidget(self.clearBtn)

        # 添加测试数据
        self.testData()

    def doDeleteItem(self, item):
        # 根据item得到它对应的行数
        row = self.listWidget.indexFromItem(item).row()
        # 删除item
        item = self.listWidget.takeItem(row)
        # 删除widget
        self.listWidget.removeItemWidget(item)
        del item

    def doClearItem(self):
        # 清空所有Item
        for _ in range(self.listWidget.count()):
            # 删除item
            # 一直是0的原因是一直从第一行删,删掉第一行后第二行变成了第一行
            # 这个和删除list [] 里的数据是一个道理
            item = self.listWidget.takeItem(0)
            # 删除widget
            self.listWidget.removeItemWidget(item)
            del item

    def testData(self):
        # 生成测试数据
        for i in range(100):
            item = QListWidgetItem(self.listWidget)
            widget = ItemWidget('item: {}'.format(i), item, self.listWidget)
            # 绑定删除信号
            widget.itemDeleted.connect(self.doDeleteItem)
            self.listWidget.setItemWidget(item, widget)
Пример #2
0
class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        self.resize(800, 600)
        layout = QVBoxLayout(self)

        self.myhwnd = int(self.winId())  # 自己的句柄

        layout.addWidget(
            QPushButton('获取所有可用、可视窗口',
                        self,
                        clicked=self._getWindowList,
                        maximumHeight=30))
        layout.addWidget(
            QLabel('双击列表中的项目则进行嵌入目标窗口到下方\n格式为:句柄|父句柄|标题|类名',
                   self,
                   maximumHeight=30))
        self.windowList = QListWidget(
            self,
            itemDoubleClicked=self.onItemDoubleClicked,
            maximumHeight=200)
        layout.addWidget(self.windowList)

    def closeEvent(self, event):
        """窗口关闭"""
        if self.layout().count() == 4:
            self.restore()
        super(Window, self).closeEvent(event)

    def _getWindowList(self):
        """清空原来的列表"""
        self.windowList.clear()
        win32gui.EnumWindows(self._enumWindows, None)

    def onItemDoubleClicked(self, item):
        """列表双击选择事件"""
        # 先移除掉item
        self.windowList.takeItem(self.windowList.indexFromItem(item).row())
        hwnd, phwnd, _, _ = item.text().split('|')
        # 开始嵌入

        if self.layout().count() == 4:
            # 如果数量等于4说明之前已经嵌入了一个窗口,现在需要把它释放出来
            self.restore()
        hwnd, phwnd = int(hwnd), int(phwnd)
        # 嵌入之前的属性
        style = win32gui.GetWindowLong(hwnd, win32con.GWL_STYLE)
        exstyle = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
        print('save', hwnd, style, exstyle)

        widget = QWidget.createWindowContainer(QWindow.fromWinId(hwnd))
        widget.hwnd = hwnd  # 窗口句柄
        widget.phwnd = phwnd  # 父窗口句柄
        widget.style = style  # 窗口样式
        widget.exstyle = exstyle  # 窗口额外样式
        self.layout().addWidget(widget)

    def restore(self):
        """归还窗口"""
        # 有bug,归还后窗口没有了WS_VISIBLE样式,不可见
        widget = self.layout().itemAt(3).widget()
        print('restore', widget.hwnd, widget.style, widget.exstyle)
        win32gui.SetParent(widget.hwnd, widget.phwnd)  # 让它返回它的父窗口
        win32gui.SetWindowLong(widget.hwnd, win32con.GWL_STYLE,
                               widget.style | win32con.WS_VISIBLE)  # 恢复样式
        win32gui.SetWindowLong(widget.hwnd, win32con.GWL_EXSTYLE,
                               widget.exstyle)  # 恢复样式
        win32gui.ShowWindow(widget.hwnd, win32con.SW_SHOW)  # 显示窗口
        widget.close()
        self.layout().removeWidget(widget)  # 从布局中移出
        widget.deleteLater()

    def _enumWindows(self, hwnd, _):
        """遍历回调函数"""
        if hwnd == self.myhwnd:
            return  # 防止自己嵌入自己
        if win32gui.IsWindow(hwnd) and win32gui.IsWindowVisible(
                hwnd) and win32gui.IsWindowEnabled(hwnd):
            phwnd = win32gui.GetParent(hwnd)
            title = win32gui.GetWindowText(hwnd)
            name = win32gui.GetClassName(hwnd)
            self.windowList.addItem('{0}|{1}|\t标题:{2}\t|\t类名:{3}'.format(
                hwnd, phwnd, title, name))
Пример #3
0
class TagStructuresWidget(QWidget):
    def __init__(self, app):
        super().__init__()
        self.images_paths = []
        self.selected_image_path = ""
        self.app = app

        self.files_list = QListWidget()
        self.structures_list = QListWidget()
        self.image_widget = TaggableImageWidget()
        self.highlighter_checkbox = QCheckBox("Wskaż wybraną strukturę")
        self.init_ui()

    def init_ui(self):
        self.image_widget.set_highlighter_pixmap(
            QPixmap("./resources/arrow.png"))
        self.image_widget.hide_highlighter()
        self.files_list.itemClicked.connect(self.on_files_list_item_clicked)
        self.files_list.itemActivated.connect(self.on_files_list_item_clicked)

        self.structures_list.itemChanged.connect(
            self.on_structure_name_changed)
        self.structures_list.itemClicked.connect(
            self.on_structure_name_clicked)
        self.structures_list.itemActivated.connect(
            self.on_structure_name_clicked)
        self.image_widget.on_tag_added.connect(self.on_tag_added)
        self.highlighter_checkbox.stateChanged.connect(
            self.set_hightlighter_visibility_state)

        self.grid = QGridLayout()
        self.grid.setColumnStretch(0, 1)
        self.grid.setColumnStretch(1, 3)

        self.lists_tab = QTabWidget()
        self.lists_tab.addTab(self.files_list, "Zdjęcia")
        self.lists_tab.addTab(self.structures_list, "Struktury")

        self.grid.addWidget(self.lists_tab, 0, 0)

        self.grid.addWidget(self.image_widget, 0, 1)

        back_btn = QPushButton("Zapisz i powróć")
        back_btn.clicked.connect(self.save_and_go_home)

        save_btn = QPushButton("Zapisz")
        save_btn.clicked.connect(self.app.save_workspace_structures)

        btn_layout = QHBoxLayout()
        btn_layout.addWidget(back_btn)
        btn_layout.addWidget(save_btn)

        self.grid.addLayout(btn_layout, 1, 0)
        self.grid.addWidget(self.highlighter_checkbox, 1, 1)

        self.setLayout(self.grid)

    def load_images_paths(self):
        self.images_paths = load_files_paths(self.app.workspace_path,
                                             ("jpeg", "jpg", "png"))
        self.files_list.clear()

        for path in self.images_paths:
            self.files_list.addItem(path)

    def on_files_list_item_clicked(self, item):
        self.selected_image_path = item.text()
        image_path = os.path.join(self.app.workspace_path,
                                  self.selected_image_path)
        self.image_widget.set_pixmap(QPixmap(image_path))
        self.image_widget.hide_highlighter()
        self.image_widget.clear_tags()
        label_id = 0

        self.structures_list.clear()
        for tagdata in self.app.workspace_structures.setdefault(
                self.selected_image_path, []):
            label_id += 1
            self.add_tag(label_id, tagdata)

    def on_tag_added(self, x, y):
        text, ok = QInputDialog.getText(self, 'Wprowadź strukturę',
                                        'Pol, Łac, Ang:')
        if ok:
            structures = self.app.workspace_structures[
                self.selected_image_path]
            tagdata = {'text': text, 'x': x, 'y': y}
            structures.append(tagdata)

            label_id = len(
                self.app.workspace_structures[self.selected_image_path])
            self.add_tag(label_id, tagdata)

    def on_structure_name_changed(self, item):
        item_id = self.structures_list.indexFromItem(item).row()
        if len(item.text()) > 0:
            self.update_structure_name(item_id, item)
        else:
            self.delete_structure(item_id, item)

    def on_structure_name_clicked(self, item):
        item_id = self.structures_list.indexFromItem(item).row()
        self.image_widget.highlight_tag_at(item_id)

        if self.highlighter_checkbox.checkState() == Qt.Checked:
            self.image_widget.show_highlighter()

    def update_structure_name(self, item_id, item):
        # update tag
        tag = self.image_widget.tag_at(item_id)
        tag.setToolTip(item.text())
        # update structure text
        structures = self.app.workspace_structures[self.selected_image_path]
        structures[item_id]['text'] = item.text()

    def delete_structure(self, item_id, item):
        # delete from structures
        structures = self.app.workspace_structures[self.selected_image_path]
        del structures[item_id]
        # update list and tags, so IDs can be restored
        mocked_item = QListWidgetItem()
        mocked_item.setText(self.selected_image_path)
        self.on_files_list_item_clicked(mocked_item)

    def save_and_go_home(self):
        self.app.save_workspace_structures()
        self.app.go_home()

    def add_tag(self, label_id, tagdata):
        item_widget = QListWidgetItem()
        item_widget.setText(tagdata['text'])
        item_widget.setFlags(item_widget.flags() | Qt.ItemIsEditable)
        self.structures_list.addItem(item_widget)

        tag_widget = TagWidget(str(label_id), tagdata['text'], tagdata['x'],
                               tagdata['y'])
        tag_widget.adjust_to_size(self.image_widget.image().size(), 0.015 / 2)

        self.image_widget.addWidget(tag_widget)

    def set_hightlighter_visibility_state(self, state):
        if len(self.structures_list.selectedItems()) > 0 and state:
            self.image_widget.show_highlighter()
        else:
            self.image_widget.hide_highlighter()
Пример #4
0
class ListParameterWidget(GenericParameterWidget):
    """Widget class for List parameter."""

    def __init__(self, parameter, parent=None):
        """Constructor

        .. versionadded:: 2.2

        :param parameter: A ListParameter object.
        :type parameter: ListParameter

        """
        super().__init__(parameter, parent)

        self._input = QListWidget()

        self._input.setSelectionMode(QAbstractItemView.MultiSelection)

        if self._parameter.maximum_item_count != \
                self._parameter.minimum_item_count:
            tool_tip = 'Select between %d and %d items' % (
                self._parameter.minimum_item_count,
                self._parameter.maximum_item_count)
        else:
            tool_tip = 'Select exactly %d items' % (
                       self._parameter.maximum_item_count)

        self._input.setToolTip(tool_tip)

        for opt in self._parameter.options_list:
            item = QListWidgetItem()
            item.setFlags(item.flags() | Qt.ItemIsEditable)
            item.setText(str(opt))
            self._input.addItem(item)
            if opt in self._parameter.value:
                item.setSelected(True)

        self.inner_input_layout.addWidget(self._input)

        # override self._input_layout arrangement to make the label at the top
        # reset the layout
        self.input_layout.setParent(None)
        self.help_layout.setParent(None)

        self.label.setParent(None)
        self.inner_input_layout.setParent(None)

        self.input_layout = QVBoxLayout()
        self.input_layout.setSpacing(0)

        # put element into layout
        self.input_layout.addWidget(self.label)
        self.input_layout.addLayout(self.inner_input_layout)

        self.main_layout.addLayout(self.input_layout)
        self.main_layout.addLayout(self.help_layout)

    def raise_invalid_type_exception(self):
        message = 'Expecting element type of %s' % (
            self._parameter.element_type.__name__)
        err = ValueError(message)
        return err

    def get_parameter(self):
        """Obtain list parameter object from the current widget state.

        :returns: A ListParameter from the current state of widget

        """
        selected_value = []
        for opt in self._input.selectedItems():
            index = self._input.indexFromItem(opt)
            selected_value.append(self._parameter.options_list[index.row()])

        try:
            self._parameter.value = selected_value
        except ValueError:
            err = self.raise_invalid_type_exception()
            raise err

        return self._parameter
Пример #5
0
class ExtendedLineEdit(QLineEdit):
    signal_send_movie = pyqtSignal(list)
    signal_search_movie = pyqtSignal(str)
    signal_request_movie_data = pyqtSignal(int)
    signal_set_loading = pyqtSignal(str, bool)

    DEBUG = False

    def __init__(self, parent=None):
        super(ExtendedLineEdit, self).__init__(parent)
        self.movies = []

        self.completer_lw = QListWidget()
        self.model = self.completer_lw.model()

        self.completer = QCompleter(self.model, self)
        self.completer.setPopup(self.completer_lw)
        self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        self.completer.setCompletionRole(
            Qt.UserRole
        )  # Change role because default EditRole is paited to list somehow
        self.setCompleter(self.completer)

        self.textEdited.connect(self.text_edited)
        #self.completer.activated[QModelIndex].connect(self.on_completer_activated)
        #self.completer.activated[str].connect(self.on_completer_activated_str)
        self.completer_lw.itemClicked.connect(self.item_clicked)

        self.installEventFilter(self)

    def eventFilter(self, obj, event):
        if event.type() == QEvent.MouseButtonPress:
            if event.button() == Qt.LeftButton:
                self.completer.complete()
        return super(ExtendedLineEdit, self).eventFilter(obj, event)

    def setText(self, text: str) -> None:
        if self.DEBUG: print('setText', text, self.sender())
        super(ExtendedLineEdit, self).setText(text)
        if not isinstance(self.sender(), QCompleter) and text:
            self.text_edited(text)

    @pyqtSlot(list)
    def update_movies_list(self, result):
        if self.DEBUG: print('update_movies_list', result[0], len(result) - 1)
        type = result.pop(0)

        if type == 'db':
            self.movies = [x + [
                type,
            ] for x in result]
            self.completer_lw.clear()
            self.completer.complete()
        else:
            for item in result:
                self.movies.append(item + [
                    type,
                ])

        for item in result:
            cwidget = MyWidget()
            cwidget.label_movie_name.setText(item[1] if item[1] else item[2])
            cwidget.label_original_name.setText(item[2])
            cwidget.label_source.setText(type)
            cwidget.label_year.setText(str(item[3]))

            completer_myQListWidgetItem = QListWidgetItem(self.completer_lw)
            completer_myQListWidgetItem.setSizeHint(cwidget.sizeHint())
            completer_myQListWidgetItem.setData(Qt.UserRole, item[1])
            self.completer_lw.addItem(completer_myQListWidgetItem)
            self.completer_lw.setItemWidget(completer_myQListWidgetItem,
                                            cwidget)

        if self.hasFocus() and not self.completer_lw.isVisible() and len(
                self.movies) > 0:
            self.completer.complete()

    @pyqtSlot(str)
    def text_edited(self, text):
        if self.DEBUG: print('text_edited', text, self.sender())
        if text and isinstance(self.sender(), ExtendedLineEdit):
            self.signal_search_movie.emit(text)

    @pyqtSlot(str)
    def on_completer_activated_str(self, name: str):
        if self.DEBUG: print('on_completer_activated_str', name)

    @pyqtSlot(QModelIndex)
    def on_completer_activated(self, index: QModelIndex):
        if self.DEBUG: print('on_completer_activated', index.row())
        item = self.movies[index.row()]

        if len(item) > 13:
            self.signal_send_movie.emit(item[:13])
            self.signal_request_movie_data.emit(item[13])
        else:
            self.signal_send_movie.emit(item)

    @pyqtSlot(QListWidgetItem)
    def item_clicked(self, item: QListWidgetItem):
        if self.DEBUG: print('item_clicked', item, [i[1] for i in self.movies])
        index = self.completer_lw.indexFromItem(item)
        item = self.movies[index.row()]
        type = item[-1]

        if type == 'db':
            self.signal_send_movie.emit(item)
        else:
            self.signal_set_loading.emit('movie', True)

            if len(item) > 13:
                self.signal_send_movie.emit(item[:13])
                self.signal_request_movie_data.emit(item[13])
            else:
                self.signal_send_movie.emit(item)

    def reset(self):
        if self.DEBUG: print('reset')
        self.completer_lw.clear()
        self.clearFocus()
Пример #6
0
class SettingsDialog(QDialog):
    def __init__(self, parent=None):
        super(SettingsDialog, self).__init__(parent)
        self.currentDirectories = []
        # self.addDirectories(directories)
        self.initButtons()
        self.initUi()

        self.restoreSettings()
        self.setLibraryDirectoriesWidget()

    def saveSettings(self):
        settings = QtCore.QSettings(
            QtCore.QSettings.IniFormat, QtCore.QSettings.UserScope,
            QtCore.QCoreApplication.organizationName(),
            QtCore.QCoreApplication.applicationName())

        settings.beginGroup("preferences")
        settings.beginWriteArray('library_directories',
                                 len(self.currentDirectories))

        for index, value in enumerate(self.currentDirectories):
            settings.setArrayIndex(index)
            settings.setValue("url", value)
        settings.endArray()
        settings.endGroup()

    def restoreSettings(self):
        settings = QtCore.QSettings(
            QtCore.QSettings.IniFormat, QtCore.QSettings.UserScope,
            QtCore.QCoreApplication.organizationName(),
            QtCore.QCoreApplication.applicationName())

#        settings.setValue("preferences_window/position", self.pos())

        settings.beginGroup("preferences")
        size = settings.beginReadArray('library_directories')
        if not size == 0:
            for i in range(0, size):
                settings.setArrayIndex(i)
                url = settings.value("url")
                self.currentDirectories.append(url)
        settings.endArray()
        settings.endGroup()

    def initButtons(self):
        self.libraryDirectories = QLabel()
        self.libraryDirectories.setText("Library Directories")
        self.libraryDirectories.setAlignment(QtCore.Qt.AlignLeft)

        self.addButton = QPushButton("Add Directory")
        self.addButton.clicked.connect(self._onAdd)

        self.removeButton = QPushButton("Remove Directory")
        self.removeButton.clicked.connect(self._onRemove)

        self.directoriesList = QListWidget(self)

    def addDirectories(self, directories):
        if not isinstance(directories, list):
            directories = [directories]

        for directory in directories:
            self.currentDirectories.append(directory)

    def setLibraryDirectoriesWidget(self):
        for directory in self.currentDirectories:
            newItem = QListWidgetItem(directory, self.directoriesList)

    def _onAdd(self):
        directories = self.choose_directory()
        if not directories:
            return None

        for directory in directories:
            if directory not in self.currentDirectories:
                self.currentDirectories.append(directory)
                newItem = QListWidgetItem(directory, self.directoriesList)

    def _onRemove(self):
        currentItem = self.directoriesList.currentItem()
        row = self.directoriesList.indexFromItem(currentItem).row()
        if currentItem:
            self.directoriesList.takeItem(row)
            self.currentDirectories.remove(currentItem.data(
                QtCore.Qt.DisplayRole))

    def choose_directory(self):
        fileDialog = QFileDialog(self)
        fileDialog.setAcceptMode(QFileDialog.AcceptOpen)
        fileDialog.setFileMode(QFileDialog.Directory)
        fileDialog.setViewMode(QFileDialog.Detail)
        fileDialog.setWindowTitle("Choose Media Directory")
        try:
            fileDialog.setDirectory(QtCore.QStandardPaths.standardLocations(
                                    QtCore.QStandardPaths.MusicLocation)[0])
        except IndexError:
            fileDialog.setDirectory(QtCore.QDir.homePath())

        if fileDialog.exec_() == QDialog.Accepted:
            return fileDialog.selectedFiles()

    def initUi(self):
        self.directoriesButtonsLayout = QVBoxLayout()
        self.directoriesButtonsLayout.addWidget(self.addButton)
        self.directoriesButtonsLayout.addWidget(self.removeButton)
        self.directoriesButtonsLayout.addSpacing(10)

        self.directoriesLayout = QHBoxLayout()
        self.directoriesLayout.addWidget(self.directoriesList)
        self.directoriesLayout.addLayout(self.directoriesButtonsLayout)

        self.closeButton = QPushButton("Close")
        self.saveButton = QPushButton("Save")

        self.closeButton.clicked.connect(self.reject)
        self.saveButton.clicked.connect(self.accept)

        self.bottomButtonsLayout = QHBoxLayout()
        self.bottomButtonsLayout.addStretch(1)
        self.bottomButtonsLayout.addSpacing(12)
        self.bottomButtonsLayout.addWidget(self.saveButton)
        self.bottomButtonsLayout.addWidget(self.closeButton)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.libraryDirectories)
        mainLayout.addLayout(self.directoriesLayout)
        mainLayout.addLayout(self.bottomButtonsLayout)
        self.setLayout(mainLayout)

        self.setWindowTitle("Settings")
        self.setMinimumSize(450, 340)
        self.resize(680, 420)
Пример #7
0
class TestNinePatchLabel(QWidget):

    skin_aio_friend_bubble_nor = "skin_aio_friend_bubble_nor.9.png"
    skin_aio_friend_bubble_pressed = "skin_aio_friend_bubble_pressed.9.png"
    skin_aio_user_bubble_nor = "skin_aio_user_bubble_nor.9.png"
    skin_aio_user_bubble_pressed = "skin_aio_user_bubble_pressed.9.png"

    def __init__(self):
        super(TestNinePatchLabel, self).__init__()
        self.resize(400, 600)

        self.listWidget = QListWidget(self)
        self.listWidget.setObjectName("listWidget")
        self.listWidget.setStyleSheet("""
            QListWidget::item:selected {
                background: rgba(0,0,0,0);
            }
            QListWidget::item:hover {
                background: rgba(0,0,0,0);
            }
        """)

        self.textEdit = QTextEdit(self)
        self.textEdit.setMaximumHeight(100)
        self.textEdit.setObjectName("textEdit")

        self.sendBtn = QPushButton("发送", self)
        self.sendBtn.setObjectName("sendBtn")

        hlayout = QHBoxLayout()
        hlayout.setContentsMargins(0, 0, 0, 0)
        hlayout.setSpacing(0)
        hlayout.addWidget(self.textEdit)
        hlayout.addWidget(self.sendBtn)

        vlayout = QVBoxLayout(self)
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.setSpacing(0)
        vlayout.addWidget(self.listWidget)
        vlayout.addItem(hlayout)

        QMetaObject.connectSlotsByName(self)  # 通过objectname注册信号
        self.init()
        self.setStyleSheet(SCROLLBARSTYLE)

    def init(self):
        '''加载气泡数组'''
        paths = os.listdir("..\\bubble")
        self.bubble = [(os.path.join(os.path.join("..\\bubble", path),
                                     self.skin_aio_friend_bubble_nor),
                        os.path.join(os.path.join("..\\bubble", path),
                                     self.skin_aio_friend_bubble_pressed),
                        os.path.join(os.path.join("..\\bubble", path),
                                     self.skin_aio_user_bubble_nor),
                        os.path.join(os.path.join("..\\bubble", path),
                                     self.skin_aio_user_bubble_pressed))
                       for path in paths]

    @pyqtSlot()
    def on_sendBtn_clicked(self):
        self.send()

    def send(self):
        '''发送消息'''
        text = self.textEdit.toPlainText().strip()
        if not text:
            return
        fn, fp, un, up = random.choice(self.bubble)  # 从中随机一个

        # 我说
        item = QListWidgetItem()
        self.listWidget.addItem(item)
        pItem = Item(None, "我说: " + text, un, up)
        item.setSizeHint(pItem.size())  # 把item的大小变得和label一样
        self.listWidget.setItemWidget(item, pItem)

        # 对方说
        item = QListWidgetItem()
        self.listWidget.addItem(item)
        pItem = Item(None, "她说: " + text, fn, fp)
        item.setSizeHint(pItem.size())  # 把item的大小变得和label一样
        self.listWidget.setItemWidget(item, pItem)

        self.listWidget.scrollTo(self.listWidget.indexFromItem(item))
Пример #8
0
class PageWidget(QWidget):

    ITEMCLICKED = pyqtSignal(CustomPageItem)

    RES_PATH = "uilib/img"    # 图片资源的路径

    STYLE = """
QPushButton#_previous_btn,QPushButton#_next_btn {{
    max-width: 49px;
    max-height: 39px;
    min-width: 49px;
    min-height: 39px;
    width: 49px;
    height: 39px;
    border-color: #E4E7EA;
    
}}
QPushButton#_previous_btn:hover,QPushButton#_next_btn:hover {{
    border-color: #1ABC9C;
    background-color: #1ABC9C;
    
}}
QPushButton#_previous_btn:pressed,QPushButton#_next_btn:pressed {{
    border-color: #1ABC9C;
    background-color: #1ABC9C;
    
}}
QPushButton#_previous_btn {{
    padding-right: 1px;
    border-top-left-radius: 6px;
    border-top-right-radius: 0px;
    border-bottom-left-radius: 6px;
    border-bottom-right-radius: 0px;
    background: #E4E7EA url({RES_PATH}/arrow_left_white.png) no-repeat center center;
}}
QPushButton#_next_btn {{
    padding-left: 1px;
    border-top-left-radius: 0px;
    border-top-right-radius: 6px;
    border-bottom-left-radius: 0px;
    border-bottom-right-radius: 6px;
    background: #E4E7EA url({RES_PATH}/arrow_right_white.png) no-repeat center center;
}}

QListView#_page_list_widget {{
    max-height: 40px;
    background-color: rgba(0,0,0,0);
}}
QListView#_page_list_widget::item {{
    margin: 1px;
    color: white;
    background-color: #E4E7EA;
    height: 40px;
}}

QListView#_page_list_widget::item:selected {{
    background-color: #1ABC9C;
}}
QListView#_page_list_widget::item:focus {{
    background-color: #1ABC9C;
}}

QListView#_page_list_widget::item:hover {{
    background-color: #1ABC9C;
}}"""

    def __init__(self, parent = None, pages = 0):
        super(PageWidget, self).__init__(parent)
        self.setObjectName("_page_widget")
        self.pages = pages    # 页数
        self.cpage = 0

        # 左边按钮
        self._previous_btn = BothSidesItem(self, which = "left")
        self._previous_btn.clicked.connect(self._previous)
        self._previous_btn.setToolTip("上一页")
        # 右边按钮
        self._next_btn = BothSidesItem(self, which = "right")
        self._next_btn.clicked.connect(self._next)
        self._next_btn.setToolTip("下一页")

        self._page_list_widget = QListWidget(self)
        self._page_list_widget.setObjectName("_page_list_widget")
        # 无边框
        self._page_list_widget.setFrameShape(QFrame.NoFrame)
        # 无滑动条
        self._page_list_widget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self._page_list_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        # 禁止自动滚动
        self._page_list_widget.setAutoScroll(False)
        # tab切换
        self._page_list_widget.setTabKeyNavigation(True)
        # 设置为横向
        self._page_list_widget.setFlow(QListView.LeftToRight)
        # 设置字体
        font = QFont()
        font.setPointSize(14)
        self._page_list_widget.setFont(font)
        # item点击事件
        self._page_list_widget.itemClicked.connect(self.ITEMCLICKED.emit)

        # 布局
        hLayout = QHBoxLayout(self)
        hLayout.setSpacing(0)
        hLayout.setContentsMargins(0, 0, 0, 0)
        spacerItem = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
        hLayout.addItem(spacerItem)
        hLayout.addWidget(self._previous_btn)
        hLayout.addWidget(self._page_list_widget)
        hLayout.addWidget(self._next_btn)
        hLayout.addItem(spacerItem)

        self._refresh(pages)

        self.updateStyle()    # 设置样式

    def _refresh(self, pages):
        # 动态调整宽度
        if pages == 0:
            self.resize(100, 40)
        elif pages >= 8:
            self._page_list_widget.setMinimumSize(320, 40)
            self._page_list_widget.resize(320, 40)
            self.resize(420, 40)
        self._page_list_widget.clear()
        # 中间
        for i in range(1, pages + 1):
            CustomPageItem(i, self._page_list_widget)

    def _previous(self, clicked):
        '''上一页'''
        num = int(self.pages / 8)
        if num == 0:
            return
        self.cpage -= 1
        if self.cpage >= 0:
            print("_previous: ", self.cpage)
            if self.cpage == 0:
                previousItem = self._page_list_widget.item(0)
            else:
                previousItem = self._page_list_widget.item(self.cpage * 8 - 4)
            self._page_list_widget.scrollTo(self._page_list_widget.indexFromItem(previousItem))
        else:
            self.cpage += 1

    def _next(self, clicked):
        '''下一页'''
        num = int(self.pages / 8)
        if num == 0:
            return
        self.cpage += 1
        if self.cpage <= num:
            print("_next: ", self.cpage)
            remainder = self.pages - self.cpage * 8
            if remainder > 8:
                nextItem = self._page_list_widget.item(self.cpage * 8 + 7)
            else:
                nextItem = self._page_list_widget.item(self.pages - 1)
            self._page_list_widget.scrollTo(self._page_list_widget.indexFromItem(nextItem))
        else:
            self.cpage -= 1

    def setResPath(self, resPath):
        '''设置资源路径'''
        self.RES_PATH = resPath

    def getResPath(self):
        return self.RES_PATH

    def updateStyle(self):
        '''刷新样式'''
        self.setStyleSheet(self.STYLE.format(RES_PATH = self.RES_PATH))

    def setPages(self, pages):
        '''设置页数'''
        self.pages = pages
        self._refresh(pages)

    def getPages(self):
        '''得到当前总的页数'''
        return self.pages
class InputListParameterWidget(GenericParameterWidget):
    """Widget class for List parameter."""

    def __init__(self, parameter, parent=None):
        """Constructor

        .. versionadded:: 2.2

        :param parameter: A ListParameter object.
        :type parameter: InputListParameter

        """
        super().__init__(parameter, parent)

        # value cache for self._parameter.value
        # copy the list so the original is unaffected
        self._value_cache = list(self._parameter.value)

        self.input = QListWidget()

        self.input.setSelectionMode(QAbstractItemView.SingleSelection)

        if self._parameter.maximum_item_count != \
                self._parameter.minimum_item_count:
            tool_tip = 'Select between %d and %d items' % (
                self._parameter.minimum_item_count,
                self._parameter.maximum_item_count)
        else:
            tool_tip = 'Select exactly %d items' % (
                self._parameter.maximum_item_count)

        self.input.setToolTip(tool_tip)

        # arrange widget

        self.insert_item_input = QLineEdit()

        vbox_layout = QVBoxLayout()
        hbox_layout = QHBoxLayout()
        self.input_add_button = QPushButton('Add')
        self.input_remove_button = QPushButton('Remove')
        # arrange line edit, add button, remove button in horizontal layout
        hbox_layout.addWidget(self.insert_item_input)
        hbox_layout.addWidget(self.input_add_button)
        hbox_layout.addWidget(self.input_remove_button)
        # arrange vertical layout
        vbox_layout.addLayout(hbox_layout)
        vbox_layout.addWidget(self.input)
        self.inner_input_layout.addLayout(vbox_layout)

        # override self._input_layout arrangement to make the label at the top
        # reset the layout
        self.input_layout.setParent(None)
        self.help_layout.setParent(None)

        self.label.setParent(None)
        self.inner_input_layout.setParent(None)

        self.input_layout = QVBoxLayout()
        self.input_layout.setSpacing(0)

        # put element into layout
        self.input_layout.addWidget(self.label)
        self.input_layout.addLayout(self.inner_input_layout)

        self.main_layout.addLayout(self.input_layout)
        self.main_layout.addLayout(self.help_layout)

        # connect handler
        # noinspection PyUnresolvedReferences
        self.input_add_button.clicked.connect(self.on_add_button_click)
        # noinspection PyUnresolvedReferences
        self.input_remove_button.clicked.connect(self.on_remove_button_click)
        # noinspection PyUnresolvedReferences
        self.insert_item_input.returnPressed.connect(
            self.input_add_button.click)
        # noinspection PyUnresolvedReferences
        self.input.itemChanged.connect(self.on_row_changed)

        self.refresh_list()

        # init row add error handler
        self._add_row_error_handler = None

    @property
    def add_row_error_handler(self):
        """return error handler if user mistakenly add row of unexpected type
        :return: a function handler
        :rtype: () -> None
        """
        return self._add_row_error_handler

    @add_row_error_handler.setter
    def add_row_error_handler(self, value):
        """Set error handler to handle user mistakenly add row of unexpected
        type
        """
        self._add_row_error_handler = value

    def refresh_list(self):
        self.input.clear()
        if not self._parameter.ordering == InputListParameter.NotOrdered:
            self._value_cache.sort()
        if self._parameter.ordering == InputListParameter.DescendingOrder:
            self._value_cache.reverse()
        for opt in self._value_cache:
            item = QListWidgetItem()
            item.setText(str(opt))
            item.setFlags(item.flags() | Qt.ItemIsEditable)
            self.input.addItem(item)

    def on_add_button_click(self):
        try:
            value = self._parameter.element_type(
                self.insert_item_input.text())
            self._value_cache.append(value)
            self.refresh_list()
        except ValueError:
            err = self.raise_invalid_type_exception()
            if self.add_row_error_handler is not None:
                self.add_row_error_handler(err)
            else:
                raise err

    def on_remove_button_click(self):
        for opt in self.input.selectedItems():
            index = self.input.indexFromItem(opt)
            del self._value_cache[index.row()]
        self.refresh_list()

    def on_row_changed(self, item):
        try:
            index = self.input.indexFromItem(item).row()
            prev_value = self._value_cache[index]
            self._value_cache[index] = self._parameter.element_type(
                item.text())
            self.refresh_list()
        except ValueError:
            item.setText(str(prev_value))
            self.raise_invalid_type_exception()

    def raise_invalid_type_exception(self):
        message = 'Expecting element type of %s' % (
            self._parameter.element_type.__name__)
        err = ValueError(message)
        return err

    def get_parameter(self):
        """Obtain list parameter object from the current widget state.

        :returns: A ListParameter from the current state of widget

        """

        try:
            self._parameter.value = self._value_cache
        except ValueError:
            err = self.raise_invalid_type_exception()
            raise err

        return self._parameter
Пример #10
0
class TestNinePatchLabel(QWidget):

    skin_aio_friend_bubble_nor = "skin_aio_friend_bubble_nor.9.png"
    skin_aio_friend_bubble_pressed = "skin_aio_friend_bubble_pressed.9.png"
    skin_aio_user_bubble_nor = "skin_aio_user_bubble_nor.9.png"
    skin_aio_user_bubble_pressed = "skin_aio_user_bubble_pressed.9.png"

    def __init__(self):
        super(TestNinePatchLabel, self).__init__()
        self.resize(400, 600)

        self.listWidget = QListWidget(self)
        self.listWidget.setObjectName("listWidget")
        self.listWidget.setStyleSheet("""
            QListWidget::item:selected {
                background: rgba(0,0,0,0);
            }
            QListWidget::item:hover {
                background: rgba(0,0,0,0);
            }
        """)

        self.textEdit = QTextEdit(self)
        self.textEdit.setMaximumHeight(100)
        self.textEdit.setObjectName("textEdit")

        self.sendBtn = QPushButton("发送", self)
        self.sendBtn.setObjectName("sendBtn")

        hlayout = QHBoxLayout()
        hlayout.setContentsMargins(0, 0, 0, 0)
        hlayout.setSpacing(0)
        hlayout.addWidget(self.textEdit)
        hlayout.addWidget(self.sendBtn)

        vlayout = QVBoxLayout(self)
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.setSpacing(0)
        vlayout.addWidget(self.listWidget)
        vlayout.addItem(hlayout)

        QMetaObject.connectSlotsByName(self)    # 通过objectname注册信号
        self.init()
        self.setStyleSheet(SCROLLBARSTYLE)

    def init(self):
        '''加载气泡数组'''
        paths = os.listdir("..\\bubble")
        self.bubble = [
            (
             os.path.join(os.path.join("..\\bubble", path), self.skin_aio_friend_bubble_nor),
             os.path.join(os.path.join("..\\bubble", path), self.skin_aio_friend_bubble_pressed),
             os.path.join(os.path.join("..\\bubble", path), self.skin_aio_user_bubble_nor),
             os.path.join(os.path.join("..\\bubble", path), self.skin_aio_user_bubble_pressed)
            )
            for path in paths
        ]

    @pyqtSlot()
    def on_sendBtn_clicked(self):
        self.send()

    def send(self):
        '''发送消息'''
        text = self.textEdit.toPlainText().strip()
        if not text:
            return
        fn , fp, un, up = random.choice(self.bubble)    # 从中随机一个

        # 我说
        item = QListWidgetItem()
        self.listWidget.addItem(item)
        pItem = Item(None, "我说: " + text, un, up)
        item.setSizeHint(pItem.size())    # 把item的大小变得和label一样
        self.listWidget.setItemWidget(item, pItem)

        # 对方说
        item = QListWidgetItem()
        self.listWidget.addItem(item)
        pItem = Item(None, "她说: " + text, fn, fp)
        item.setSizeHint(pItem.size())    # 把item的大小变得和label一样
        self.listWidget.setItemWidget(item, pItem)

        self.listWidget.scrollTo(self.listWidget.indexFromItem(item))
Пример #11
0
class Example(QWidget):
    def __init__(self):
        super().__init__()

        self.filenames = json_files()

        if type(self.filenames) is list:
            self.curr_file = self.filenames[0]
        else:
            self.curr_file = self.filenames

        self.initUI()

    def initUI(self):

        self.num = -1  # index for search bar query
        self.show_save = False  # bool for showing unsaved changes dialog
        self.temp_file = ".temp.csv"
        self.refresh_file = False  # failsafe 1 for itemChanged trigger

        self.list_1 = QListWidget()

        lister(file=self.curr_file, target=self.list_1, index=0, mode=0)
        self.list_1.clicked.connect(self.clear_selection)
        self.list_1.installEventFilter(self)

        self.list_items = self.list_1.count(
        )  # failsafe 2 for itemChanged trigger
        self.list_1.itemChanged.connect(self.edit_next_item)
        self.list_1.verticalScrollBar().valueChanged.connect(self.sync_scroll)

        self.list_2 = QListWidget()
        lister(file=self.curr_file, target=self.list_2, index=1, mode=0)
        self.list_2.clicked.connect(self.clear_selection)

        self.list_3 = QListWidget()
        lister(file=self.curr_file, target=self.list_3, index=2, mode=0)
        self.list_3.clicked.connect(self.clear_selection)

        self.all_lists = [self.list_1, self.list_2, self.list_3]

        self.menubar = QMenuBar()
        self.menubar.setNativeMenuBar(False)

        exit_event = QAction('Exit', self)
        exit_event.setShortcut('Ctrl+W')
        exit_event.triggered.connect(app.quit)

        showAct = QAction('Show extras', self, checkable=True)
        showAct.setChecked(False)
        showAct.setShortcut('Ctrl+E')
        showAct.triggered.connect(self.hide_notes)

        addAct = QAction('Fields', self)
        addAct.setShortcut('Ctrl+N')
        addAct.triggered.connect(self.add_item)

        fileOpen = QAction('Open file', self)
        fileOpen.triggered.connect(self.fileDialog)
        fileOpen.setShortcut('Ctrl+O')

        fileSave = QAction('Save file', self)
        fileSave.triggered.connect(self.save)
        fileSave.triggered.connect(self.refresh_recents)
        fileSave.setShortcut('Ctrl+S')

        self.fileRecents = QMenu('Recent file', self)
        self.refresh_recents()

        self.toggle_theme = QAction('Toggle theme', self, checkable=True)
        self.toggle_theme.setChecked(json_theme())
        self.toggle_theme.triggered.connect(self.theme)
        self.toggle_theme.setShortcut('Ctrl+T')

        self.col_sort_index = QMenu('Sorting column index', self)
        self.col_sort_index.addAction(QAction(str(0), self))
        self.col_sort_index.addAction(QAction(str(1), self))
        self.col_sort_index.addAction(QAction(str(2), self))
        self.col_sort_index.triggered.connect(self.sort_col_choice)

        self.col_search_index = QMenu('Searching column index', self)
        self.col_search_index.addAction(QAction(str(0), self))
        self.col_search_index.addAction(QAction(str(1), self))
        self.col_search_index.addAction(QAction(str(2), self))
        self.col_search_index.triggered.connect(self.search_col_choice)

        self.sort = QAction('Sort entries', self, checkable=True)
        self.curr_col = 0
        self.search_col = 0
        self.sort.triggered.connect(self.refresh_list)
        self.sort.setShortcut('Ctrl+R')

        self.addFields = self.menubar.addMenu('Add')
        self.addFields.addAction(addAct)

        self.optionMenu = self.menubar.addMenu('Options')
        self.optionMenu.addAction(exit_event)
        self.optionMenu.addAction(showAct)
        self.optionMenu.addAction(self.toggle_theme)
        self.optionMenu.addMenu(self.col_sort_index)
        self.optionMenu.addMenu(self.col_search_index)
        self.optionMenu.addAction(self.sort)

        self.fileMenu = self.menubar.addMenu('File')
        self.fileMenu.addAction(fileOpen)
        self.fileMenu.addAction(fileSave)
        self.fileMenu.addMenu(self.fileRecents)

        self.search_bar = QLineEdit()
        self.search_bar.setPlaceholderText('Search vocab')
        self.search_bar.setClearButtonEnabled(True)
        self.search_bar.setMaxLength(10)
        self.search_bar.returnPressed.connect(self.search_item)

        self.status_bar = QStatusBar()
        status(self.status_bar, self.list_1)

        grid = QGridLayout()
        grid.setSpacing(10)
        grid.addWidget(self.menubar, 0, 0)
        grid.addWidget(self.list_1, 1, 0)
        grid.addWidget(self.list_2, 1, 1)
        grid.addWidget(self.list_3, 1, 2)
        grid.addWidget(self.search_bar, 0, 1)
        grid.addWidget(self.status_bar)

        self.theme()
        self.setLayout(grid)
        self.setGeometry(*json_window_size())
        self.setWindowTitle(f'{split_name(self.curr_file)}')
        self.show()

        self.list_1.scrollToBottom()
        self.list_2.verticalScrollBar().setHidden(True)
        self.list_3.verticalScrollBar().setHidden(True)
        self.list_3.setHidden(True)

    def sync_scroll(self):

        scroll_location = self.list_1.verticalScrollBar().value()

        self.list_2.verticalScrollBar().setValue(scroll_location)
        self.list_3.verticalScrollBar().setValue(scroll_location)

    def edit_next_item(self, event):
        """When an item is added and edited on the first col, starts editing its counterpart on the next col"""

        if self.list_items == self.list_1.count(
        ) - 2 or self.list_items != self.list_1.count(
        ) and self.refresh_file == False:

            item = self.list_2.item(self.list_2.count() - 1)
            self.list_2.editItem(item)

            self.list_items = self.list_1.count()

    def closeEvent(self, event):
        """Triggered upon program exit, shows a dialog for unsaved changes using a bool"""

        if self.show_save == True:

            reply = QMessageBox.question(
                self, 'Message',
                "You may have unsaved changes, are you sure you want to quit?",
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

            if reply == QMessageBox.Yes:
                try:
                    remove(self.temp_file)
                except:
                    pass

                event.accept()
            else:
                event.ignore()

        else:
            pass

    def sort_col_choice(self, action):
        self.curr_col = int(action.text())

    def search_col_choice(self, action):
        self.search_col = int(action.text())

    def refresh_list(self):
        """Refreshes the contents of the lists, when sorting is used"""

        self.save(
            mode=1
        )  # saves a temp copy, with changes, but irreversable sorting introduced

        clear_lists(self.all_lists)

        if self.sort.isChecked() == True:
            mode = 2
        else:
            mode = 0

        try:
            lister(file=self.temp_file,
                   target=self.list_1,
                   index=0,
                   mode=mode,
                   column=self.curr_col)
            lister(file=self.temp_file,
                   target=self.list_2,
                   index=1,
                   mode=mode,
                   column=self.curr_col)
            lister(file=self.temp_file,
                   target=self.list_3,
                   index=2,
                   mode=mode,
                   column=self.curr_col)

        except:
            lister(file=self.curr_file,
                   target=self.list_1,
                   index=0,
                   mode=mode,
                   column=self.curr_col)
            lister(file=self.curr_file,
                   target=self.list_2,
                   index=1,
                   mode=mode,
                   column=self.curr_col)
            lister(file=self.curr_file,
                   target=self.list_3,
                   index=2,
                   mode=mode,
                   column=self.curr_col)

    def refresh_recents(self):

        try:

            file_1 = QAction(self.curr_file, self)
            self.fileRecents.addAction(file_1)
            file_1.triggered.connect(self.clickedFileAct)

            if type(self.filenames) is list:

                if self.filenames[1] != None:
                    file_2 = QAction(self.filenames[1], self)
                    self.fileRecents.addAction(file_2)
                    file_2.triggered.connect(self.clickedFileAct)

                if self.filenames[2] != None:
                    file_3 = QAction(self.filenames[2], self)
                    self.fileRecents.addAction(file_3)
                    file_3.triggered.connect(self.clickedFileAct)

        except:
            pass

    def clickedFileAct(self):

        self.refresh_file = True

        file = self.sender().text()
        self.curr_file = file
        self.setWindowTitle(f'{split_name(self.curr_file)}')

        clear_lists(self.all_lists)

        lister(file=self.curr_file, target=self.list_1, index=0)
        lister(file=self.curr_file, target=self.list_2, index=1)
        lister(file=self.curr_file, target=self.list_3, index=2)

        status(self.status_bar, self.list_1)
        self.theme()

        self.list_1.scrollToBottom()
        self.list_3.setHidden(True)

        self.refresh_file = False

    def eventFilter(self, source, event):
        """Item (row) deletion"""

        if (event.type() == QEvent.ContextMenu and source is self.list_1):
            menu = QMenu()
            menu.addAction("Delete row")
            if menu.exec_(event.globalPos()):
                item = source.itemAt(event.pos())
                try:
                    model = self.list_1.indexFromItem(item)
                    row = model.row()

                    self.show_save = True

                    self.list_1.takeItem(row)
                    self.list_2.takeItem(row)
                    self.list_3.takeItem(row)

                    status(self.status_bar, self.list_1,
                           f'Deleted row number: {row+1}.')
                    self.clearSelection()

                except:
                    pass

            return True
        return super(Example, self).eventFilter(source, event)

    def hide_notes(self):
        """Toggles showing the note column and stretches the window for clearer reading of it"""

        self.list_3.setHidden(not self.list_3.isHidden())

    def theme(self):
        """Sets the theme for the window and its widgets"""

        palette = QPalette()

        # dark theme
        if self.toggle_theme.isChecked() == True:

            palette.setColor(QPalette.Window, QColor(0, 0, 0))
            dark = "background-color: rgb(0, 0, 0); color: rgb(255, 255, 255);"

            self.menubar.setStyleSheet(dark)
            self.addFields.setStyleSheet(dark)
            self.optionMenu.setStyleSheet(dark)
            self.fileMenu.setStyleSheet(dark)
            self.search_bar.setStyleSheet(
                "background-color: rgb(0, 0, 0); color: rgb(255, 255, 255)"
            )  # border: 0px; for transparency
            self.status_bar.setStyleSheet(dark)

            style_items(self.all_lists, dark_theme=True)

        # light theme
        elif self.toggle_theme.isChecked() == False:

            palette.setColor(QPalette.Window, QColor(255, 255, 255))
            light = "background-color: rgb(255, 255, 255); color: rgb(0, 0, 0)"

            self.menubar.setStyleSheet(light)
            self.addFields.setStyleSheet(light)
            self.optionMenu.setStyleSheet(light)
            self.fileMenu.setStyleSheet(light)
            self.search_bar.setStyleSheet(light)
            self.status_bar.setStyleSheet(light)

            style_items(self.all_lists, dark_theme=False)

        self.setPalette(palette)

        self.theme_bool = self.toggle_theme.isChecked(
        )  # used in the save func

    def search_item(self):
        """Takes input from the search bar and matches with an item, 
		gets index and scrolls to it, more reusults being qued with the num class var
		"""

        query = self.search_bar.text()
        search = self.all_lists[self.search_col].findItems(
            query, Qt.MatchContains)
        status(self.status_bar, self.list_1, f'Found {len(search)} results.')
        self.clear_selection()

        # testing search in all column

        # search_list =[]
        # for x in range(3):
        # 	search_list.append(self.all_lists[x].findItems(query, Qt.MatchContains))

        # parent_list = []
        # for x in range(3):
        # 	for y in range(len(search_list[x])):
        # 		parent_list.append(self.all_lists[x]) # replace with x

        # import itertools
        # merged = list(itertools.chain.from_iterable(search_list))

        # search_dict = dict(zip(parent_list, merged))
        # print(search_dict)
        # print()
        # print(len(merged))
        # print(len(parent_list))

        self.num += 1
        for i in search:

            try:
                model_index = self.all_lists[self.search_col].indexFromItem(
                    search[self.num])

            except:
                self.num = 0
                model_index = self.all_lists[self.search_col].indexFromItem(
                    search[self.num])

            item_index = model_index.row()

            self.all_lists[self.search_col].item(item_index).setSelected(True)
            self.list_1.scrollToItem(self.list_1.item(item_index),
                                     QAbstractItemView.PositionAtCenter)

    def add_item(self):

        self.show_save = True

        for x in range(3):
            if x == 0:
                lister(file=self.curr_file,
                       target=self.list_1,
                       index=x,
                       mode=1)

            elif x == 1:
                lister(file=self.curr_file,
                       target=self.list_2,
                       index=x,
                       mode=1)

            elif x == 2:
                lister(file=self.curr_file,
                       target=self.list_3,
                       index=x,
                       mode=1)

        item = self.list_1.item(self.list_1.count() - 1)
        self.list_1.editItem(item)
        status(self.status_bar, self.list_1)

        self.list_1.scrollToBottom()
        self.list_2.scrollToBottom()
        self.list_3.scrollToBottom()

    def clear_selection(self):
        """Clears all item slections for aesthetical purposes, but only single clicks"""

        self.list_1.clearSelection()
        self.list_2.clearSelection()
        self.list_3.clearSelection()

    def fileDialog(self):

        fname = QFileDialog()
        path = fname.getOpenFileName(self,
                                     'Open file',
                                     getcwd(),
                                     filter='csv (*.csv);;')
        if path[0] == '':  # failsafe for canceling the dialog
            return self.curr_file

        self.curr_file = path[0]
        self.setWindowTitle(f'{split_name(self.curr_file)}')

        clear_lists(self.all_lists)

        lister(file=self.curr_file, target=self.list_1, index=0)
        lister(file=self.curr_file, target=self.list_2, index=1)
        lister(file=self.curr_file, target=self.list_3, index=2)

        status(self.status_bar, self.list_1)
        self.theme()

    def save(self, mode=0):

        self.show_save = False

        list1_items = items_text(self.list_1)
        list2_items = items_text(self.list_2)
        list3_items = items_text(self.list_3)

        total_dicts = []
        for (a, b, c) in zip(list1_items, list2_items,
                             list3_items):  # each letter is a column
            dictionary = {'word_1': a, 'word_2': b, 'notes': c}
            total_dicts.append(dictionary)

        if mode == 0:

            writer(file=self.curr_file, data=total_dicts)
            status(self.status_bar, self.list_1, ('Saved current changes.'))

            try:
                json_template(theme=self.theme_bool,
                              files=[self.curr_file, None, None],
                              window_size=self.geometry().getRect()
                              )  # current size values of the window

            except:
                json_template(
                )  # bug cannot be avoided, even though used setChecked at the beggining

        elif mode == 1:

            self.show_save = True
            writer(file=self.temp_file, data=total_dicts)

        # avoids stacking and refreshes recent file actions
        actions = self.fileRecents.actions()
        for action in actions:
            self.fileRecents.removeAction(action)
Пример #12
0
class Player(QMainWindow):
    """A simple Media Player using VLC and Qt
    """
    def __init__(self, master=None):
        if '--clear' in sys.argv: os.remove('.cache')
        # Read Data
        if not os.path.exists('AllChannel.json'):
            print('Reffering channel info from server.')
            print(
                'If you want to customize channel data, generate AllChannel.json file by generate_SKB_channels.py.',
                file=sys.stderr)
            from generate_SKB_channels import get_channels
            channels = dict([(_['ServiceId'], _) for _ in get_channels()])
        else:
            print('Using AllChannel.json for referring channel info.')
            with open('AllChannel.json', encoding='UTF8') as f:
                channels = dict([(_['ServiceId'], _)
                                 for _ in json.loads(f.read())])

        new_stream_db = read_m3u(sys.argv[1])
        old_stream_db = read_m3u(sys.argv[2])

        stream_urls = [_['multicast'] for _ in new_stream_db]

        if os.path.exists('.cache'):
            with open('.cache', 'rb') as f:
                cache = pickle.loads(f.read())
        else:
            cache = {}

        # Checking broken stream
        global vlc_error_count, vlc_error_check, vlc_handle_mode
        instance = vlc.Instance("--verbose=-1")
        instance.log_set(vlc_log_callback, None)
        mediaplayer = instance.media_player_new()
        mediaplayer.video_set_scale(0.1)
        print('Checking broken stream...')
        for url in tqdm.tqdm(stream_urls):
            if not url in cache:
                vlc_error_count = 0
                vlc_error_check = False
                mediaplayer.set_media(instance.media_new(url))
                mediaplayer.play()
                time.sleep(3)
                cache[url] = vlc_error_check
            with open('.cache', 'wb') as f:
                f.write(pickle.dumps(cache))
        mediaplayer.stop()
        self.stream_verify = cache
        vlc_handle_mode = 1
        ##############################

        playlist = remove_unmapped_channel(old_stream_db, stream_urls)
        playlist = fill_unmapped_channel(playlist, channels)

        self.channel_info = channels
        self.playlist = playlist
        self.stream_urls = stream_urls

        import pprint
        #pprint.pprint(update_db)
        print(len(old_stream_db), len(playlist), len(new_stream_db),
              len(channels))

        # QT Initialize
        QMainWindow.__init__(self, master)
        self.setWindowTitle("Media Player")

        # creating a basic vlc instance
        self.instance = vlc.Instance("--verbose=-1")
        self.instance.log_set(vlc_log_callback, None)
        # creating an empty vlc media player
        self.mediaplayer = self.instance.media_player_new()

        self.createUI()

    def createUI(self):
        """Set up the user interface, signals & slots
        """
        self.widget = QWidget(self)
        self.setCentralWidget(self.widget)

        # In this widget, the video will be drawn
        if sys.platform == "darwin":  # for MacOS
            from PyQt5.QtWidgets import QMacCocoaViewContainer
            self.videoframe = QMacCocoaViewContainer(0)
        else:
            self.videoframe = QFrame()
        self.palette = self.videoframe.palette()
        self.palette.setColor(QPalette.Window, QColor(0, 0, 0))
        self.videoframe.setPalette(self.palette)
        self.videoframe.setAutoFillBackground(True)
        self.videoframe.setMinimumWidth(720)
        self.videoframe.setMinimumHeight(480)

        #self.hbuttonbox = QHBoxLayout()
        #self.openchannel = QPushButton("Open Channel Data")
        #self.hbuttonbox.addWidget(self.openchannel)
        #self.openchannel.clicked.connect(self.PlayPause)

        self.hcontrolbox = QHBoxLayout()
        self.hinfobox = QHBoxLayout()
        self.icon = QLabel()
        self.icon.setFixedSize(200, 60)
        self.icon.setAlignment(Qt.AlignCenter)
        self.hinfobox.addWidget(self.icon)
        self.vinfobox = QVBoxLayout()
        self.ch_name = QLabel("Loading...")
        font = QFont()
        font.setBold(True)
        font.setFamily('Malgun Gothic')
        font.setPointSize(16)
        self.ch_name.setFont(font)
        self.vinfobox.addWidget(self.ch_name)
        self.hservicebox = QHBoxLayout()
        self.hservicebox.addWidget(QLabel('Service ID '))
        self.service_id = QLabel("[#]")
        self.hservicebox.addWidget(self.service_id)
        self.vinfobox.addLayout(self.hservicebox)
        self.hinfobox.addLayout(self.vinfobox)
        self.hcontrolbox.addLayout(self.hinfobox)

        self.hcontrolbox.addStretch(1)
        self.volumeslider = QSlider(Qt.Horizontal, self)
        self.volumeslider.setMaximum(100)
        self.volumeslider.setValue(self.mediaplayer.audio_get_volume())
        self.volumeslider.setToolTip("Volume")
        self.hcontrolbox.addWidget(self.volumeslider)
        self.volumeslider.valueChanged.connect(self.setVolume)

        #
        self.channelbox = QVBoxLayout()
        self.channellist = QListWidget()
        self.channellist.setFixedWidth(320)
        self.channellist.itemClicked.connect(self.selectChannel)
        self.channelbox.addWidget(self.channellist)
        self.channelfilter = QLineEdit()
        self.channelfilter.setFixedWidth(320)
        self.channelfilter.textChanged.connect(self.find_channel)
        self.channelbox.addWidget(self.channelfilter)

        self.streambox = QVBoxLayout()
        self.streamlist = QListWidget()
        self.streamlist.setFixedWidth(320)
        self.streamlist.itemClicked.connect(self.selectStream)
        self.streambox.addWidget(self.streamlist)
        self.mapbutton = QPushButton("Map")
        self.mapbutton.clicked.connect(self.map)
        self.streambox.addWidget(self.mapbutton)

        self.vboxlayout = QVBoxLayout()
        self.vboxlayout.addWidget(self.videoframe)
        self.vboxlayout.addLayout(self.hcontrolbox)

        self.hboxlayout = QHBoxLayout()
        self.hboxlayout.addLayout(self.vboxlayout)
        self.hboxlayout.addLayout(self.channelbox)
        self.hboxlayout.addLayout(self.streambox)

        self.widget.setLayout(self.hboxlayout)

        export = QAction("&Export", self)
        export.triggered.connect(self.ExportFile)
        exit = QAction("E&xit", self)
        exit.triggered.connect(sys.exit)
        menubar = self.menuBar()
        filemenu = menubar.addMenu("&File")
        filemenu.addAction(export)
        filemenu.addSeparator()
        filemenu.addAction(exit)

        self.updatePlaylist()

    def ExportFile(self):
        print(os.path.expanduser('~'))
        #filename = QFileDialog.getSaveFileName(self, "Save File", os.path.expanduser('~'),)[0]
        filename = QFileDialog.getSaveFileName(
            self,
            "Export Playlist File",
            'playlist.m3u',
            "M3U Playlist (*.m3u *.m3u8)",
        )[0]
        if not filename: return
        with open(filename, 'wt', encoding='UTF8') as f:
            f.write('#EXTM3U\n')
            for item in [
                    self.channellist.item(_)
                    for _ in range(self.channellist.count())
            ]:
                if item.checkState():
                    data = item.data(Qt.UserRole)
                    f.write(
                        f"#EXTINF:-1 tvg-id=\"{data['tvg-id']}\" tvg-logo=\"{data['tvg-logo']}\" tvg-chno=\"{data['tvh-chnum']}\" tvh-chnum=\"{data['tvh-chnum']}\", {data['ch-name']}\n"
                    )
                    f.write(f"{data['multicast']}\n")
        filename = QFileDialog.getSaveFileName(
            self,
            "Export Channel File",
            'Channel.json',
            "JSON File (*.json)",
        )[0]
        if not filename: return
        channels = []
        for item in [
                self.channellist.item(_)
                for _ in range(self.channellist.count())
        ]:
            if item.checkState():
                data = item.data(Qt.UserRole)
                channels.append(self.channel_info[data['tvg-id']])
        with open(filename, 'wt', encoding='UTF8') as f:
            f.write(json.dumps(channels, indent=2))

    def setVolume(self, Volume):
        """Set the volume
        """
        self.mediaplayer.audio_set_volume(Volume)

    def find_channel(self, text):
        if text:
            for item in [
                    self.channellist.item(_)
                    for _ in range(self.channellist.count())
            ]:
                if item.text().lower().find(text.lower()) >= 0:
                    item.setHidden(False)
                else:
                    item.setHidden(True)
        else:
            for item in [
                    self.channellist.item(_)
                    for _ in range(self.channellist.count())
            ]:
                item.setHidden(False)

    def map(self, *args, **kwargs):
        item = self.channellist.currentItem()
        item.setCheckState(Qt.Checked)
        channel = item.data(Qt.UserRole)
        sitem = self.streamlist.currentItem()
        radio = self.streamlist.itemWidget(sitem)
        channel['multicast'] = radio.text()
        item.setData(Qt.UserRole, channel)
        self.updateMappedInfo()

    def playStream(self, stream_url):
        global vlc_error_count, vlc_error_check
        vlc_error_count = 0
        vlc_error_check = False

        self.media = self.instance.media_new(stream_url)
        # put the media in the media player
        self.mediaplayer.set_media(self.media)

        # parse the metadata of the file
        self.media.parse()
        # set the title of the track as window title
        self.setWindowTitle(self.media.get_meta(0))

        # the media player has to be 'connected' to the QFrame
        # (otherwise a video would be displayed in it's own window)
        # this is platform specific!
        # you have to give the id of the QFrame (or similar object) to
        # vlc, different platforms have different functions for this
        if sys.platform.startswith('linux'):  # for Linux using the X Server
            self.mediaplayer.set_xwindow(self.videoframe.winId())
        elif sys.platform == "win32":  # for Windows
            self.mediaplayer.set_hwnd(self.videoframe.winId())
        elif sys.platform == "darwin":  # for MacOS
            self.mediaplayer.set_nsobject(int(self.videoframe.winId()))

        self.mediaplayer.play()

    def selectChannel(self, item):
        global vlc_error_count, vlc_error_check
        vlc_error_count = 0
        vlc_error_check = False

        channel = item.data(Qt.UserRole)
        print(channel)
        if 'multicast' in channel and channel['multicast']:
            streams = dict([(item.data(Qt.UserRole), item) for item in [
                self.streamlist.item(_) for _ in range(self.streamlist.count())
            ]])
            if channel['multicast'] in streams:
                self.selectStream(streams[channel['multicast']])
            else:
                item.setCheckState(Qt.Unchecked)
        else:
            item.setCheckState(Qt.Unchecked)

        self.ch_name.setText(channel['ch-name'])
        frame = self.getIcon(channel['tvg-logo'])
        pixmap = QPixmap()
        pixmap.loadFromData(frame)
        self.icon.setPixmap(
            pixmap.scaled(self.icon.width(), self.icon.height(),
                          Qt.KeepAspectRatio))
        self.service_id.setText(f"[{channel['tvg-id']}]")

    def updateStreamRadioState(self, state):
        if state:
            found = False
            for item in [
                    self.streamlist.item(_)
                    for _ in range(self.streamlist.count())
            ]:
                radio = self.streamlist.itemWidget(item)
                if radio.isChecked():
                    self.channellist.currentItem().setCheckState(Qt.Checked)
                    item.setText("")
                    self.streamlist.setCurrentItem(item)
                    self.streamlist.update(self.streamlist.currentIndex())
                    self.selectStreamImpl(item)
                    found = True
                    break
            if not found:
                item = self.streamlist.item(0)
                self.streamlist.setCurrentItem(item)
                self.selectStreamImpl(item)

    def selectStream(self, item):
        stream_info = item.data(Qt.UserRole)
        radio = self.streamlist.itemWidget(item)
        radio.setChecked(True)

    def selectStreamImpl(self, item):
        global vlc_error_count, vlc_error_check
        vlc_error_count = 0
        vlc_error_check = False

        radio = self.streamlist.itemWidget(item)
        url = radio.text()
        item.setData(Qt.UserRole, url)
        print('URL from Radio :', url)

        if url:
            self.playStream(url)
            #citem = self.channellist.currentItem()
            #data = citem.data(Qt.UserRole)
            #data['multicast'] = url
            #citem.setData(Qt.UserRole, data)
            #print('Saved URL :', url)
        else:
            pass
            #self.mediaplayer.pause()
            #citem = self.channellist.currentItem()
            #data = citem.data(Qt.UserRole)
            #data['multicast'] = None
            #citem.setData(Qt.UserRole, data)
            #print('Saved URL :', None)
        self.updateMappedInfo()

    def getMappedDict(self):
        mapped_dict = {}
        for item in [
                self.channellist.item(_)
                for _ in range(self.channellist.count())
        ]:
            data = item.data(Qt.UserRole)
            if data['multicast']:
                if data['multicast'] in mapped_dict:
                    mapped_dict[data['multicast']].append(data['ch-name'])
                else:
                    mapped_dict[data['multicast']] = [data['ch-name']]
        return mapped_dict

    def updateMappedInfo(self):
        print('UpdateMappedInfo')
        mapped_dict = self.getMappedDict()
        for item in [
                self.streamlist.item(_) for _ in range(self.streamlist.count())
        ]:
            url = item.data(Qt.UserRole)
            if url == '__BROKEN__':
                item.setText("*** BROKEN ***")
            elif url in mapped_dict:
                item.setText(f"[{','.join(mapped_dict[url])}]"
                             if mapped_dict[url] else "")
            else:
                item.setText("")
            self.streamlist.update(self.streamlist.indexFromItem(item))

    def disableChannel(self):
        self.channellist.currentItem().setCheckState(Qt.Unchecked)
        self.channellist.update(self.channellist.currentIndex())
        data = self.channellist.currentItem().data(Qt.UserRole)
        data['multicast'] = None
        self.channellist.currentItem().setData(Qt.UserRole, data)

        self.streamlist.update(self.streamlist.currentIndex())
        self.streamlist.currentItem().setData(Qt.UserRole, '__BROKEN__')

        self.updateMappedInfo()
        #app.processEvents()

    @functools.lru_cache(maxsize=None)
    def getIcon(self, url):
        req = request.Request(url)
        req.add_header(
            'User-Agent',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0'
        )
        frame = None
        for _ in range(3):
            try:
                resp = request.urlopen(req)
                frame = resp.read()
                break
            except:
                time.sleep(1)
        return frame

    def attachIcon(self, item):
        channel = item.data(Qt.UserRole)
        url = channel['tvg-logo']
        pixmap = QPixmap()
        pixmap.loadFromData(self.getIcon(url))
        item.setIcon(QIcon(pixmap))

    def asyncCacheIcon(self):
        def asyncThread(listwidget):
            for item in [
                    listwidget.item(_) for _ in range(listwidget.count())
            ]:
                channel = item.data(Qt.UserRole)
                self.attachIcon(item)
                listwidget.update(listwidget.indexFromItem(item))

        threading.Thread(target=asyncThread,
                         args=(self.channellist, ),
                         daemon=True).start()

    def updatePlaylist(self):
        self.channellist.clear()
        for channel in self.playlist:
            item = QListWidgetItem()
            item.setData(Qt.UserRole, channel)
            item.setText(f"[{channel['tvh-chnum']}] {channel['ch-name']}")
            item.setCheckState(Qt.Checked if channel['multicast']
                               and not self.stream_verify[channel['multicast']]
                               else Qt.Unchecked)
            self.channellist.addItem(item)

        item = QListWidgetItem()
        item.setData(Qt.UserRole, '')
        self.streamlist.addItem(item)
        radio = QRadioButton('Unbound Stream URL')
        radio.toggled.connect(self.updateStreamRadioState)
        self.streamlist.setItemWidget(item, radio)
        for url in self.stream_urls:
            item = QListWidgetItem()
            item.setTextAlignment(Qt.AlignRight)
            item.setData(Qt.UserRole,
                         '__BROKEN__' if self.stream_verify[url] else url)
            self.streamlist.addItem(item)
            radio = QRadioButton(url)
            radio.toggled.connect(self.updateStreamRadioState)
            self.streamlist.setItemWidget(item, radio)

        self.updateMappedInfo()
        self.asyncCacheIcon()

        def delayedSelectChannel(n):
            time.sleep(n)
            self.channellist.setCurrentRow(0)
            self.selectChannel(self.channellist.currentItem())

        threading.Thread(target=delayedSelectChannel, args=(3, ),
                         daemon=True).start()
Пример #13
0
class ListLayout(QWidget, CueLayout):

    NAME = 'List Layout'
    DESCRIPTION = '''
                    This layout organize the cues in a list:
                    <ul>
                        <li>Unlimited cues;
                        <li>Side panel with playing media-cues;
                        <li>Cues can be moved in the list;
                        <li>Keyboard control;
                    </ul>'''

    HEADER = ['', 'Cue', 'Duration', 'Progress']

    # I need to redefine those from CueLayout
    key_pressed = CueLayout.key_pressed
    cue_added = CueLayout.cue_added
    cue_removed = CueLayout.cue_removed
    focus_changed = CueLayout.focus_changed

    def __init__(self, app, **kwds):
        super().__init__(**kwds)

        self.mainWindow = app.mainWindow
        self.menuLayout = self.mainWindow.menuLayout

        self._cue_items = []
        self._playing_widgets = {}
        self._context_item = None

        self._show_dbmeter = config['ListLayout']['ShowDbMeters'] == 'True'
        self._show_seek = config['ListLayout']['ShowSeek'] == 'True'
        self._accurate_time = config['ListLayout']['ShowAccurate'] == 'True'
        self._auto_next = config['ListLayout']['AutoNext'] == 'True'

        # Add layout-specific menus
        self.showDbMeterAction = QAction(self)
        self.showDbMeterAction.setCheckable(True)
        self.showDbMeterAction.setChecked(self._show_dbmeter)
        self.showDbMeterAction.triggered.connect(self.set_dbmeter_visible)

        self.showSeekAction = QAction(self)
        self.showSeekAction.setCheckable(True)
        self.showSeekAction.setChecked(self._show_seek)
        self.showSeekAction.triggered.connect(self.set_seek_visible)

        self.accurateTimingAction = QAction(self)
        self.accurateTimingAction.setCheckable(True)
        self.accurateTimingAction.setChecked(self._accurate_time)
        self.accurateTimingAction.triggered.connect(self.set_accurate_time)

        self.autoNextAction = QAction(self)
        self.autoNextAction.setCheckable(True)
        self.autoNextAction.setChecked(self._auto_next)
        self.autoNextAction.triggered.connect(self.set_auto_next)

        self.menuLayout.addAction(self.showDbMeterAction)
        self.menuLayout.addAction(self.showSeekAction)
        self.menuLayout.addAction(self.accurateTimingAction)
        self.menuLayout.addAction(self.autoNextAction)

        # Add a toolbar to MainWindow
        self.toolBar = QToolBar(self.mainWindow)
        self.toolBar.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)

        self.playAction = QAction(self)
        self.playAction.setIcon(QIcon.fromTheme("media-playback-start"))
        self.playAction.triggered.connect(self.play_current)

        self.pauseAction = QAction(self)
        self.pauseAction.setIcon(QIcon.fromTheme("media-playback-pause"))
        self.pauseAction.triggered.connect(self.pause_current)

        self.stopAction = QAction(self)
        self.stopAction.setIcon(QIcon.fromTheme("media-playback-stop"))
        self.stopAction.triggered.connect(self.stop_current)

        self.stopAllAction = QAction(self)
        self.stopAllAction.font().setBold(True)
        self.stopAllAction.triggered.connect(self.stop_all)

        self.pauseAllAction = QAction(self)
        self.pauseAllAction.font().setBold(True)
        self.pauseAllAction.triggered.connect(self.pause_all)

        self.restartAllAction = QAction(self)
        self.restartAllAction.font().setBold(True)
        self.restartAllAction.triggered.connect(self.restart_all)

        self.toolBar.addAction(self.playAction)
        self.toolBar.addAction(self.pauseAction)
        self.toolBar.addAction(self.stopAction)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.stopAllAction)
        self.toolBar.addAction(self.pauseAllAction)
        self.toolBar.addAction(self.restartAllAction)

        self.mainWindow.addToolBar(self.toolBar)

        self.hLayout = QHBoxLayout(self)
        self.hLayout.setContentsMargins(5, 5, 5, 5)

        # On the left (cue list)
        self.listView = ListWidget(self)
        self.listView.context_event.connect(self.context_event)
        self.listView.key_event.connect(self.onKeyPressEvent)
        self.listView.drop_move_event.connect(self.move_cue_at)
        self.listView.drop_copy_event.connect(self._copy_cue_at)
        self.listView.setHeaderLabels(self.HEADER)
        self.listView.header().setSectionResizeMode(QHeaderView.Fixed)
        self.listView.header().setSectionResizeMode(self.HEADER.index('Cue'),
                                                    QHeaderView.Stretch)
        self.listView.setColumnWidth(0, 40)
        self.hLayout.addWidget(self.listView)

        self.vLayout = QVBoxLayout()
        self.vLayout.setContentsMargins(0, 0, 0, 0)
        self.vLayout.setSpacing(2)

        # On the right (playing media-cues)
        self.label = QLabel('Playing', self)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setStyleSheet('font-size: 17pt; font-weight: bold;')
        self.vLayout.addWidget(self.label)

        self.playView = QListWidget(self)
        self.playView.setMinimumWidth(300)
        self.playView.setMaximumWidth(300)
        self.playView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        self.playView.setFocusPolicy(QtCore.Qt.NoFocus)
        self.playView.setSelectionMode(QAbstractItemView.NoSelection)
        self.vLayout.addWidget(self.playView)

        self.hLayout.addLayout(self.vLayout)

        # Add cue preferences widgets
        self.add_settings_section(MediaCueGeneral, MediaCue)
        self.add_settings_section(Appearance)

        # Context menu actions
        self.edit_action = QAction(self)
        self.edit_action.triggered.connect(self.edit_context_cue)

        self.remove_action = QAction(self)
        self.remove_action.triggered.connect(self.remove_context_cue)

        self.select_action = QAction(self)
        self.select_action.triggered.connect(self.select_context_cue)

        self.add_context_item(self.edit_action)
        self.sep1 = self.add_context_separator()
        self.add_context_item(self.remove_action)
        self.add_context_item(self.select_action)

        self.retranslateUi()

    def retranslateUi(self):
        self.showDbMeterAction.setText("Show Db-meters")
        self.showSeekAction.setText("Show seek bars")
        self.accurateTimingAction.setText('Accurate timing')
        self.autoNextAction.setText('Auto select next cue')
        self.playAction.setText("Go")
        self.pauseAction.setText("Pause")
        self.stopAction.setText("Stop")
        self.stopAllAction.setText("Stop All")
        self.pauseAllAction.setText("Pause All")
        self.restartAllAction.setText("Restart All")

        self.edit_action.setText('Edit option')
        self.remove_action.setText('Remove')
        self.select_action.setText('Select')

    def current_cue(self):
        item = self.current_item()
        if item is not None:
            return item.cue

    def current_item(self):
        if len(self._cue_items) > 0:
            return self._cue_items[self.listView.currentIndex().row()]

    def select_context_cue(self):
        self._context_item.select()

    def __add_cue__(self, cue, index):
        if isinstance(cue, MediaCue):
            item = MediaItem(cue, self.listView)

            # Use weak-references for avoid cyclic-references with lambda(s)
            wself = weakref.ref(self)
            wcue = weakref.ref(cue)

            cue.media.on_play.connect(lambda: wself().show_playing(wcue()))
            cue.media.interrupted.connect(lambda: wself().hide_playing(wcue()))
            cue.media.stopped.connect(lambda: wself().hide_playing(wcue()))
            cue.media.error.connect(lambda: wself().hide_playing(wcue()))
            cue.media.eos.connect(lambda: wself().hide_playing(wcue()))
        elif isinstance(cue, ActionCue):
            item = ActionItem(cue)
            item.cue = cue
        else:
            raise Exception('Cue type not supported')

        item.setFlags(item.flags() & ~QtCore.Qt.ItemIsDropEnabled)

        if index is None or (index < 0 or index >= len(self._cue_items)):
            cue['index'] = len(self._cue_items)
            self.listView.addTopLevelItem(item)
            self._cue_items.append(item)
        else:
            self.listView.insertTopLevelItem(index, item)
            self._cue_items.insert(index, item)
            for n in range(index, len(self._cue_items)):
                self._cue_items[n].cue['index'] = n

        if isinstance(item, MediaItem):
            item.init()

        if len(self._cue_items) == 1:
            self.listView.setCurrentItem(item)
            self.listView.setFocus()
            self.listView.resizeColumnToContents(1)

        self.cue_added.emit(cue)

    def set_accurate_time(self, enable):
        self._accurate_time = enable

        for i in range(self.playView.count()):
            widget = self.playView.itemWidget(self.playView.item(i))
            widget.set_accurate_time(enable)

        for item in self._cue_items:
            if isinstance(item, MediaItem):
                item.set_accurate_time(enable)

    def set_auto_next(self, enable):
        self._auto_next = enable

    def set_seek_visible(self, visible):
        self._show_seek = visible
        for i in range(self.playView.count()):
            widget = self.playView.itemWidget(self.playView.item(i))
            widget.set_seek_visible(visible)

    def set_dbmeter_visible(self, visible):
        self._show_dbmeter = visible
        for i in range(self.playView.count()):
            widget = self.playView.itemWidget(self.playView.item(i))
            widget.set_dbmeter_visible(visible)

    def show_playing(self, cue):
        if cue not in self._playing_widgets:
            media_time = self._cue_items[cue['index']].media_time
            widget = PlayingMediaWidget(cue, media_time, self.playView)
            widget.set_dbmeter_visible(self._show_dbmeter)
            widget.set_seek_visible(self._show_seek)
            widget.set_accurate_time(self._accurate_time)

            list_item = QListWidgetItem()
            list_item.setSizeHint(widget.size())

            self.playView.addItem(list_item)
            self.playView.setItemWidget(list_item, widget)
            self._playing_widgets[cue] = list_item

    def hide_playing(self, cue):
        if cue in self._playing_widgets:
            list_item = self._playing_widgets.pop(cue)
            widget = self.playView.itemWidget(list_item)
            row = self.playView.indexFromItem(list_item).row()

            self.playView.removeItemWidget(list_item)
            self.playView.takeItem(row)

            widget.destroy_widget()

    def onKeyPressEvent(self, e):
        if e.key() == QtCore.Qt.Key_Space:
            if qApp.keyboardModifiers() == Qt.ShiftModifier:
                cue = self.current_cue()
                if cue is not None:
                    self.edit_cue(cue)
            elif qApp.keyboardModifiers() == Qt.ControlModifier:
                item = self.current_item()
                if item is not None:
                    item.select()
            else:
                cue = self.current_cue()
                if cue is not None:
                    cue.execute()
                if self._auto_next:
                    nextitem = self.listView.currentIndex().row() + 1
                    if nextitem < self.listView.topLevelItemCount():
                        nextitem = self.listView.topLevelItem(nextitem)
                        self.listView.setCurrentItem(nextitem)
        else:
            self.key_pressed.emit(e)

        e.accept()

    def play_current(self):
        cue = self.current_cue()
        if isinstance(cue, MediaCue):
            cue.media.play()
        else:
            cue.execute()

    def pause_current(self):
        cue = self.current_cue()
        if isinstance(cue, MediaCue):
            cue.media.pause()

    def stop_current(self):
        cue = self.current_cue()
        if isinstance(cue, MediaCue):
            cue.media.stop()

    def context_event(self, event):
        item = self.listView.itemAt(event.pos())
        if item is not None:
            index = self.listView.indexOfTopLevelItem(item)
            self._context_item = self._cue_items[index]
            self.show_context_menu(event.globalPos())
        event.accept()

    def remove_context_cue(self):
        self.remove_cue(self.get_context_cue())

    def edit_context_cue(self):
        self.edit_cue(self.get_context_cue())

    def stop_all(self):
        for item in self._cue_items:
            if isinstance(item.cue, MediaCue):
                item.cue.media.stop()

    def pause_all(self):
        for item in self._cue_items:
            if isinstance(item.cue, MediaCue):
                item.cue.media.pause()

    def restart_all(self):
        for item in self._cue_items:
            if isinstance(item.cue, MediaCue):
                if item.cue.media.state == Media.PAUSED:
                    item.cue.media.play()

    def __remove_cue__(self, cue):
        index = cue['index']
        self.listView.takeTopLevelItem(index)
        self._cue_items.pop(index)

        if isinstance(cue, MediaCue):
            cue.media.interrupt()
            if cue in self._playing_widgets:
                self.hide_playing(self._playing_widgets[cue])

        for item in self._cue_items[index:]:
            item.cue['index'] = item.cue['index'] - 1

        cue.finalize()

        self.cue_removed.emit(cue)

    def move_cue_at(self, old_index, index):
        self.move_cue(self._cue_items[old_index].cue, index)

    def _copy_cue_at(self, old_index, index):
        newcue = CueFactory.clone_cue(self._cue_items[old_index].cue)
        self.add_cue(newcue, index)

    def __move_cue__(self, cue, index):
        item = self._cue_items.pop(cue['index'])
        self._cue_items.insert(index, item)
        self.listView.setCurrentItem(item)

        if isinstance(item, MediaItem):
            item.init()

        for n, item in enumerate(self._cue_items):
            item.cue['index'] = n

    def get_cues(self, cue_class=Cue):
        # i -> item
        return [i.cue for i in self._cue_items if isinstance(i.cue, cue_class)]

    def get_cue_at(self, index):
        if index < len(self._cue_items):
            return self._cue_items[index].cue

    def get_cue_by_id(self, cue_id):
        for item in self._cue_items:
            if item.cue.cue_id() == cue_id:
                return item.cue

    def get_selected_cues(self, cue_class=Cue):
        cues = []
        for item in self._cue_items:
            if item.selected and isinstance(item.cue, cue_class):
                cues.append(item.cue)
        return cues

    def clear_layout(self):
        while len(self._cue_items) > 0:
            self.__remove_cue__(self._cue_items[-1].cue)

    def destroy_layout(self):
        self.clear_layout()
        self.menuLayout.clear()
        self.mainWindow.removeToolBar(self.toolBar)
        self.toolBar.deleteLater()

        # Remove context-items
        self.remove_context_item(self.edit_action)
        self.remove_context_item(self.sep1)
        self.remove_context_item(self.remove_action)
        self.remove_context_item(self.select_action)

        # Remove settings-sections
        self.remove_settings_section(Appearance)
        self.remove_settings_section(MediaCueGeneral)

        # !! Delete the layout references !!
        self.deleteLater()

    def get_context_cue(self):
        return self._context_item.cue

    def select_all(self):
        for item in self._cue_items:
            if not item.selected:
                item.select()

    def deselect_all(self):
        for item in self._cue_items:
            if item.selected:
                item.select()

    def invert_selection(self):
        for item in self._cue_items:
            item.select()
Пример #14
0
class SmartTileSelectorWidget(QWidget):
    def __init__(self, viewport):
        super().__init__()

        self.smart_tiles = []
        self.mappings = []
        self.load_tiles()
        self.layout = QVBoxLayout()

        self.list_widget = QListWidget(self)
        for tile in self.smart_tiles:
            self.list_widget.addItem(tile.name)

        self.width_label = QLabel("Width: ")
        self.width_input = QSpinBox(self)
        self.height_label = QLabel("Height: ")
        self.height_input = QSpinBox(self)
        self.width_box = QHBoxLayout()
        self.width_box.addWidget(self.width_label)
        self.width_box.addWidget(self.width_input)
        self.height_box = QHBoxLayout()
        self.height_box.addWidget(self.height_label)
        self.height_box.addWidget(self.height_input)
        self.layout.addWidget(self.list_widget)
        self.layout.addLayout(self.width_box)
        self.layout.addLayout(self.height_box)
        self.setLayout(self.layout)

        self.current = None
        self.list_widget.currentItemChanged.connect(self.item_changed)
        self.width_input.valueChanged.connect(self.set_brush)
        self.height_input.valueChanged.connect(self.set_brush)
        self.viewport = viewport

    def set_brush(self):
        current = self.get_current()
        # if current.has_mappings:
        self.brush = current.as_smart_brush(self.width_input.value(),
                                            self.height_input.value(),
                                            self.mappings)
        # else:
        #    self.brush = current.as_brush()

        if self.brush is not None:
            ToolContext.current.tile_brush = self.brush

    def set_tiles(self, tiles):
        pass

    def get_current(self):
        return self.smart_tiles[self.current]

    def set_current(self, index):
        self.current = index
        self.set_brush()

    def item_changed(self, curr, prev):
        item_index = self.list_widget.indexFromItem(curr).row()
        self.set_current(item_index)
        self.height_input.setValue(self.get_current().height)
        self.width_input.setValue(self.get_current().width)
        if self.get_current().has_mappings:
            self.height_input.setReadOnly(False)
            self.width_input.setReadOnly(False)

        else:
            self.height_input.setReadOnly(True)
            self.width_input.setReadOnly(True)

    def load_tiles(self):
        with open('data/supertux/smarttiles_mapping.csv', 'r') as mappings:
            reader = csv.reader(mappings)
            first_row = True
            for row in reader:
                if first_row:  # Ignore first heading row
                    first_row = False
                    continue
                mapping = SmartTileMapping()
                mapping.id = row[0]
                mapping.top = [int(val) for val in row[1].split("|")]
                mapping.right = [int(val) for val in row[2].split("|")]
                mapping.bottom = [int(val) for val in row[3].split("|")]
                mapping.left = [int(val) for val in row[4].split("|")]
                self.mappings.append(mapping)

        with open('data/supertux/smarttiles.csv', 'r') as csvfile:
            reader = csv.reader(csvfile)
            first_row = True
            for row in reader:
                if first_row:  # Ignore first heading row
                    first_row = False
                    continue

                id = row[0]
                name = row[1]
                width = int(row[2])
                height = int(row[3])
                start = int(row[4])
                reverse = row[5] if len(
                    row) >= 6 and row[5] == "true" else False
                max_width = int(row[6]) if len(row) >= 7 else width
                max_height = int(row[7]) if len(row) >= 8 else height
                tile = SmartTile(id, name, width, height, max_width,
                                 max_height, start, reverse)
                mappings = row[8] if len(row) >= 9 else None
                if mappings is not None:
                    tile.set_mappings(mappings)

                self.smart_tiles.append(tile)
Пример #15
0
class TAGenerateTab(QWidget):
    def __init__(self, ctx):
        super(TAGenerateTab, self).__init__()
        self.ctx = ctx

        self._table_being_updated = False
        self._order_lists_being_updated = False
        self._settings_being_updated = False

        self.create_ui()

    def create_ui(self):
        settings_layout = QHBoxLayout()

        self.order_cluster = TAListMove()
        self.order_cluster.trigger.connect(self.userchanged_order_lists)
        order_cluster_layout = QVBoxLayout()
        order_cluster_layout.addWidget(self.order_cluster)
        order_cluster_group = QGroupBox("Cluster Order")
        order_cluster_group.setLayout(order_cluster_layout)

        self.order_diverse = TAListMove()
        self.order_diverse.trigger.connect(self.userchanged_order_lists)
        order_diverse_layout = QVBoxLayout()
        order_diverse_layout.addWidget(self.order_diverse)
        order_diverse_group = QGroupBox("Diversify Order")
        order_diverse_group.setLayout(order_diverse_layout)

        self.order_manual = QListWidget()
        btn1 = QPushButton("Create")
        btn1.clicked.connect(self.buttonclicked_manual_add)
        btn2 = QPushButton("Delete")
        btn2.clicked.connect(self.buttonclicked_manual_del)
        order_manual_btnlst = QHBoxLayout()
        order_manual_btnlst.addWidget(btn1)
        order_manual_btnlst.addWidget(btn2)
        order_manual_btnlst_widget = QWidget()
        order_manual_btnlst_widget.setLayout(order_manual_btnlst)
        order_manual_layout = QVBoxLayout()
        order_manual_layout.addWidget(self.order_manual)
        order_manual_layout.addWidget(order_manual_btnlst_widget)
        order_manual_group = QGroupBox("Manual Allocation")
        order_manual_group.setLayout(order_manual_layout)

        settings_layout.addWidget(order_cluster_group)
        settings_layout.addWidget(order_diverse_group)
        settings_layout.addWidget(order_manual_group)

        settings_group = QGroupBox("Field Settings")
        settings_group.setLayout(settings_layout)

        run_layout = QHBoxLayout()

        run_settings1_layout = QFormLayout()

        self.seats_field = QLineEdit()
        self.seats_field.setValidator(QIntValidator(1, 16, self))
        self.seats_field.textChanged.connect(self.userchanged_settings_fields)

        self.tables_field = QLineEdit()
        self.tables_field.setValidator(QIntValidator(1, 100, self))
        self.tables_field.textChanged.connect(self.userchanged_settings_fields)

        run_settings1_layout.addRow(QLabel("Num. of Groups"),
                                    self.tables_field)
        run_settings1_layout.addRow(QLabel("Num. of People per Group"),
                                    self.seats_field)

        run_settings1_layout_widget = QWidget()
        run_settings1_layout_widget.setLayout(run_settings1_layout)

        run_settings2_layout = QFormLayout()

        self.number_field = QLineEdit()
        self.number_field.setValidator(QIntValidator(1, 16, self))
        self.number_field.textChanged.connect(self.userchanged_settings_fields)

        run_settings2_layout.addRow(QLabel("Number of Allocations"),
                                    self.number_field)
        run_advanced_settings_button = QPushButton("Change Settings")
        run_advanced_settings_button.clicked.connect(
            self.buttonclicked_advanced_settings)
        run_settings2_layout.addRow(QLabel("Advanced Settings"),
                                    run_advanced_settings_button)

        run_settings2_layout_widget = QWidget()
        run_settings2_layout_widget.setLayout(run_settings2_layout)

        run_settings3_layout = QGridLayout()

        run_button = QPushButton("Generate Groups!")
        run_button.clicked.connect(self.buttonclicked_run_allocation)

        run_settings3_layout_widget = QWidget()
        run_settings3_layout_widget.setLayout(run_settings3_layout)

        run_layout.addWidget(run_settings1_layout_widget, 1)
        run_layout.addWidget(run_settings2_layout_widget, 1)
        run_layout.addWidget(run_button, 1)

        run_group = QGroupBox("Allocation Settings")
        run_group.setLayout(run_layout)

        layout = QGridLayout()
        layout.addWidget(settings_group, 0, 0)
        layout.addWidget(run_group, 1, 0)
        layout.setRowStretch(0, 1)
        self.setLayout(layout)

    def init_order_list(self):
        for j in self.ctx.app_data.order_cluster:
            if j not in self.ctx.app_data_manager.get_fields_with_mode(
                    'cluster'):
                self.ctx.app_data.order_cluster.remove(j)

        for j in self.ctx.app_data_manager.get_fields_with_mode('cluster'):
            if j not in self.ctx.app_data.order_cluster:
                self.ctx.app_data.order_cluster.append(j)

        for j in self.ctx.app_data.order_diverse:
            if j not in self.ctx.app_data_manager.get_fields_with_mode(
                    'diversify'):
                self.ctx.app_data.order_diverse.remove(j)

        for j in self.ctx.app_data_manager.get_fields_with_mode('diversify'):
            if j not in self.ctx.app_data.order_diverse:
                self.ctx.app_data.order_diverse.append(j)

    def update_field_order_lists(self):
        self.init_order_list()

        self._order_lists_being_updated = True

        self.order_cluster.clear()
        for j in self.ctx.app_data.order_cluster:
            new_item = QListWidgetItem()
            new_item.setData(Qt.UserRole, j)
            new_item.setText(self.ctx.app_data.peopledata_keys[j])
            self.order_cluster.addItem(new_item)

        self.order_diverse.clear()
        for j in self.ctx.app_data.order_diverse:
            new_item = QListWidgetItem()
            new_item.setData(Qt.UserRole, j)
            new_item.setText(self.ctx.app_data.peopledata_keys[j])
            self.order_diverse.addItem(new_item)

        self._order_lists_being_updated = False

    def update_manuals_list(self):
        self.order_manual.clear()

        for m in self.ctx.app_data.manuals:
            self.order_manual.addItem("{0}: Table {1}".format(
                self.ctx.app_data_manager.get_print_labels(m[0]), m[1] + 1))

    def update_settings(self):
        self._settings_being_updated = True

        self.tables_field.setText(str(self.ctx.app_data.settings['tables']))
        self.seats_field.setText(str(self.ctx.app_data.settings['seats']))

        self.number_field.setText(
            str(self.ctx.app_data.settings['nallocations']))

        self._settings_being_updated = False

    def userchanged_order_lists(self):
        if self._order_lists_being_updated: return

        self.ctx.app_data.order_cluster = [
            self.order_cluster.item(l).data(Qt.UserRole)
            for l in range(self.order_cluster.count())
        ]
        self.ctx.app_data.order_diverse = [
            self.order_diverse.item(l).data(Qt.UserRole)
            for l in range(self.order_diverse.count())
        ]

        self.ctx.set_unsaved()

    def buttonclicked_manual_add(self):
        try:
            people_list = [
                [i, self.ctx.app_data_manager.get_print_labels(i)]
                for i in range(len(self.ctx.app_data.peopledata_vals))
                if i not in [m[0] for m in self.ctx.app_data.manuals]
            ]
        except Exception as e:
            QMessageBox.critical(self, "Error", "Error: {}".format(str(e)))
            return
        tables = self.ctx.app_data.settings['tables']
        status, person, table = TAManualDialog.get_input(
            self, people_list, tables)
        if not status: return
        self.ctx.app_data.manuals.append([person, table])
        self.update_manuals_list()
        self.ctx.set_unsaved()

    def buttonclicked_manual_del(self):
        if not self.order_manual.currentItem(): return
        index = self.order_manual.indexFromItem(
            self.order_manual.currentItem()).row()
        self.ctx.app_data.manuals.pop(index)
        self.update_manuals_list()
        self.ctx.set_unsaved()

    def userchanged_settings_fields(self):
        if self._settings_being_updated: return
        try:
            self.ctx.app_data.settings['tables'] = int(
                self.tables_field.text()) if self.tables_field.text() else 0
            self.ctx.app_data.settings['seats'] = int(
                self.seats_field.text()) if self.seats_field.text() else 0
            self.ctx.app_data.settings['nallocations'] = int(
                self.number_field.text()) if self.number_field.text() else 0
        except Exception as e:
            QMessageBox.critical(
                self, "Error",
                "Error occurred while processing your entry: {}".format(
                    str(e)))
        self.ctx.set_unsaved()

    def buttonclicked_advanced_settings(self):
        try:
            attempts_default = self.ctx.app_data.settings['nattempts']
            seed_default = self.ctx.app_data.settings['seed']
            status, attempts, seed = TAAdvancedSettingsDialog.get_input(
                self, attempts_default, seed_default)
            if not status: return
            self.ctx.app_data.settings['nattempts'] = attempts
            self.ctx.app_data.settings['seed'] = seed
        except Exception as e:
            QMessageBox.critical(
                self, "Error",
                "Error occurred while processing your entry: {}".format(
                    str(e)))
        self.ctx.set_unsaved()

    def buttonclicked_run_allocation(self):
        attempts = self.ctx.app_data.settings['nattempts']
        progress_bar = QProgressDialog("Generating table allocations...", "",
                                       0, attempts, self.ctx.window)
        progress_bar.setWindowTitle("Generating...")
        progress_bar.setWindowModality(Qt.WindowModal)
        progress_bar.setAutoClose(False)
        progress_bar.setMinimumDuration(0)
        progress_bar.setCancelButton(None)

        progress_bar.show()
        progress_bar.setValue(0)

        try:
            self.ctx.ta_manager.run(progress_bar)
        except Exception as e:
            progress_bar.close()
            QMessageBox.critical(
                self, "Error",
                "An error occured during allocation: {}".format(str(e)))
            return

        progress_bar.close()
        QMessageBox.information(
            self, "Success!",
            "The allocations were successfully computed. Average number of links is {:.2f} ({:.2f} % of max)."
            .format(self.ctx.ta_manager.links,
                    100 * self.ctx.ta_manager.links_rel))

        self.ctx.set_unsaved()
        self.ctx.window.tabs.results_updated()
        self.ctx.window.results_menu.setEnabled(True)