Example #1
0
class GameListWidget(QWidget):
    def __init__(self, main):
        super(GameListWidget, self).__init__()
        self.main_win = main
        self.keystore_exchanged = False

        self.setObjectName("GameListWidget")
        v_layout = QVBoxLayout()

        h_layout1 = QHBoxLayout()
        self.game_list_view = QListView()
        self.game_list_view.setViewMode(QListView.ListMode)
        self.game_list_view.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.game_list_model = GameListModel(self.main_win.games)
        self.game_list_view.setModel(self.game_list_model)
        self.game_list_view.clicked.connect(self.list_item_onclick)
        h_layout1.addWidget(self.game_list_view, 1)
        self.game_list_view.setCurrentIndex(
            self.game_list_model.index(self.main_win.game_index))
        self.game = self.main_win.games[self.main_win.game_index]

        form_layout = QFormLayout()
        form_layout.setContentsMargins(20, 50, 20, 50)
        self.game_name_value = QLineEdit()
        form_layout.addRow("游戏名称:", self.game_name_value)
        self.game_desc_value = QTextEdit()
        form_layout.addRow("游戏简介:", self.game_desc_value)
        self.game_appid_value = QLabel()
        self.game_appid_value.setTextInteractionFlags(Qt.TextSelectableByMouse)
        form_layout.addRow("游戏ID:", self.game_appid_value)
        self.game_appkey_value = QLabel()
        self.game_appkey_value.setTextInteractionFlags(
            Qt.TextSelectableByMouse)
        form_layout.addRow("客户端Key:", self.game_appkey_value)
        h_layout = QHBoxLayout()
        self.keystore_path = QLineEdit()
        select_key_btn = QPushButton("浏览")
        select_key_btn.setStyleSheet('QPushButton{border-radius: 0px;}')
        select_key_btn.clicked.connect(self.select_ketstore)
        h_layout.addWidget(self.keystore_path)
        h_layout.addWidget(select_key_btn)
        form_layout.addRow("KeyStore:", h_layout)
        self.keystore_pwd_value = QLineEdit()
        form_layout.addRow("KeyPass:"******"Alias:", self.keystore_alias_value)
        self.keystore_aliaspwd_value = QLineEdit()
        form_layout.addRow("AliasPass:"******"更换Icon")
        icon_exchange_btn.setFixedWidth(100)
        icon_exchange_btn.clicked.connect(self.exchange_icon)
        v_layout1.addWidget(icon_exchange_btn, alignment=Qt.AlignHCenter)
        v_layout1.addStretch(2)
        h_layout1.addLayout(v_layout1, 1)
        v_layout.addLayout(h_layout1)

        h_layout2 = QHBoxLayout()
        create_game_btn = QPushButton("返 回")
        create_game_btn.setFixedWidth(100)
        create_game_btn.clicked.connect(self.back)
        h_layout2.addWidget(create_game_btn,
                            alignment=Qt.AlignLeft | Qt.AlignBottom)
        back_btn = QPushButton("创建游戏")
        back_btn.setFixedWidth(100)
        back_btn.clicked.connect(self.add_game)
        h_layout2.addWidget(back_btn, alignment=Qt.AlignHCenter)
        next_btn = QPushButton("下一步")
        next_btn.setFixedWidth(100)
        next_btn.clicked.connect(self.next)
        h_layout2.addWidget(next_btn, alignment=Qt.AlignRight | Qt.AlignBottom)
        v_layout.addLayout(h_layout2)
        self.setLayout(v_layout)
        self.set_game_info()

    def set_game_info(self):
        self.game_name_value.setText(self.game['name'])
        self.game_desc_value.setText(self.game['desc'])
        self.game_appid_value.setText(self.game['id'])
        self.game_appkey_value.setText(self.game['key'])
        self.keystore_path.setText(self.game['keystore'])
        self.keystore_pwd_value.setText(self.game['keypwd'])
        self.keystore_alias_value.setText(self.game['alias'])
        self.keystore_aliaspwd_value.setText(self.game['aliaspwd'])

        icon = Utils.get_full_path('games/' + self.game['id'] +
                                   '/icon/icon.png')
        if not os.path.exists(icon):
            icon = 'icon.png'
        self.icon_img.setPixmap(QPixmap(icon))

    def list_item_onclick(self):
        self.main_win.game_index = self.game_list_view.currentIndex().row()
        self.game = self.main_win.games[self.main_win.game_index]
        self.set_game_info()

    def back(self):
        self.main_win.set_main_widget()

    def add_game(self):
        if not self.save_data():
            return
        self.main_win.set_create_game_widget(self.main_win.games)

    def select_ketstore(self):
        fname = QFileDialog.getOpenFileName(
            self, '选择签名文件',
            Utils.get_full_path('games/' + self.game['id'] + '/keystore/'))
        if fname[0]:
            if os.path.samefile(
                    fname[0],
                    Utils.get_full_path('games/' + self.game['id'] +
                                        '/keystore/' + self.game['keystore'])):
                self.keystore_path.setText(self.game['keystore'])
                self.keystore_pwd_value.setText(self.game['keypwd'])
                self.keystore_alias_value.setText(self.game['alias'])
                self.keystore_aliaspwd_value.setText(self.game['aliaspwd'])
                self.keystore_exchanged = False
            else:
                self.keystore_path.setText(fname[0])
                self.keystore_pwd_value.clear()
                self.keystore_alias_value.clear()
                self.keystore_aliaspwd_value.clear()
                self.keystore_exchanged = True

    def exchange_icon(self):
        fname = QFileDialog.getOpenFileName(
            self, '选择icon',
            Utils.get_full_path('games/' + self.game['id'] + '/icon/'),
            ("Images (*.png)"))
        if fname[0]:
            pix = QPixmap(fname[0])
            if pix.width() != 512 or pix.height() != 512:
                QMessageBox.warning(self, "警告", "必须上传512*512.png图片")
                return
            self.icon_img.setPixmap(pix)
            current_icon = Utils.get_full_path('games/' + self.game['id'] +
                                               '/icon/icon.png')
            if os.path.exists(current_icon):
                if os.path.samefile(os.path.dirname(fname[0]),
                                    os.path.dirname(current_icon)):
                    if not os.path.samefile(
                            fname[0], current_icon
                    ):  # 如果选中的,在game的icon目录下,但不是当前icon,则进行重命名
                        count = 0
                        temp = 'icon0.png'
                        while os.path.exists(
                                Utils.get_full_path('games/' +
                                                    self.game['id'] +
                                                    '/icon/' + temp)):
                            count += 1
                            temp = 'icon' + str(count) + '.png'
                        os.renames(
                            current_icon,
                            Utils.get_full_path('games/' + self.game['id'] +
                                                '/icon/' + temp))
                        os.renames(fname[0], current_icon)
                    else:  # 如果所选的是当前icon,不做处理
                        return
                else:  # 如果选中的不在game的icon目录下,则重命名当前icon,并将选中的icon复制到目录下作为当前icon
                    count = 0
                    temp = 'icon0.png'
                    while os.path.exists(
                            Utils.get_full_path('games/' + self.game['id'] +
                                                '/icon/' + temp)):
                        count += 1
                        temp = 'icon' + str(count) + '.png'
                    os.renames(
                        current_icon,
                        Utils.get_full_path('games/' + self.game['id'] +
                                            '/icon/' + temp))
                    Utils.copy_file(fname[0], current_icon)
            else:
                Utils.copy_file(fname[0], current_icon)

    def save_data(self):
        if self.game_name_value.text().strip() == "":
            QMessageBox.warning(self, "警告", "游戏名称不能为空!")
            return False
        if self.keystore_path.text().strip() == "":
            QMessageBox.warning(self, "警告", "必须上传keystore签名文件!")
            return False
        if self.keystore_pwd_value.text().strip() == "":
            QMessageBox.warning(self, "警告", "keystore密码不能为空!")
            return False
        if self.keystore_alias_value.text().strip() == "":
            QMessageBox.warning(self, "警告", "alias不能为空!")
            return False
        if self.keystore_aliaspwd_value.text().strip() == "":
            QMessageBox.warning(self, "警告", "alias密码不能为空!")
            return False
        self.game['name'] = self.game_name_value.text().strip()
        self.game['desc'] = self.game_desc_value.toPlainText().strip()
        if self.keystore_exchanged:
            keystore = os.path.basename(self.keystore_path.text().strip())
            self.game['keystore'] = keystore
            self.game['keypwd'] = self.keystore_pwd_value.text().strip()
            self.game['alias'] = self.keystore_alias_value.text().strip()
            self.game['aliaspwd'] = self.keystore_aliaspwd_value.text().strip()
            keystore_path = Utils.get_full_path('games/' + self.game['id'] +
                                                '/keystore/' + keystore)
            if not os.path.exists(keystore_path):
                Utils.copy_file(self.keystore_path.text().strip(),
                                keystore_path)

        self.main_win.games[self.main_win.game_index] = self.game
        self.game_list_model.update_item(self.main_win.game_index, self.game)
        return Utils.update_games(Utils.get_full_path('games/games.xml'),
                                  self.game, self.main_win.game_index)

    def next(self):
        if not self.save_data():
            return
        channels = Utils.get_channels(
            Utils.get_full_path('games/' + self.game['id'] + '/config.xml'))
        if channels is None:
            if not os.path.exists(Utils.get_full_path('channelsdk')):
                QMessageBox.warning(
                    self, "警告",
                    os.path.dirname(os.getcwd()) + " 没有channelsdk文件夹")
                return
            elif len(os.listdir(Utils.get_full_path('channelsdk'))) <= 0:
                QMessageBox.warning(
                    self, "警告", "本地没有渠道sdk,请手动添加sdk文件夹到" +
                    Utils.get_full_path('channelsdk'))
                return
            channels = []
            self.main_win.set_add_channel_widget(channels)
        else:
            self.main_win.set_channel_list_widget(channels)
Example #2
0
    def __init__(self, main):
        super(PackageWidgetM, self).__init__()
        self.setObjectName("PackageWidgetM")
        self.main_win = main
        self.game_index = 0
        self.selected = [
        ]  # 已选中的channel及所属game列表 如:[{"game": 当前game字典, "channel": 当前channel字典}, {}, {}]
        self.selected_name = [
        ]  # 已选中的渠道显示名称列表 如:["10878922-765321", "", "", ""]
        self.lbps = {}  # 打包信息及进度条组合字典 {"765321": {}, "": {}}
        self.progress = None
        self.monitor = PackageMonitor()
        self.monitor.signal.connect(self.complete)

        v_layout = QVBoxLayout()
        h_layout1 = QHBoxLayout()
        # 全部游戏及其下的渠道列表
        self.tool_box = QToolBox(self)
        self.tool_box.setFixedWidth(100)
        for game in self.main_win.games:
            clv = QListView()
            clv.setEditTriggers(QAbstractItemView.NoEditTriggers)
            clv.setContextMenuPolicy(Qt.CustomContextMenu)
            self.tool_box.addItem(clv, game['id'])
        self.tool_box.currentChanged.connect(self.select_game)
        self.tool_box.setCurrentIndex(self.game_index)
        channel_list_area = QScrollArea()
        channel_list_area.setWidget(self.tool_box)
        h_layout1.addWidget(channel_list_area, 1)
        # 已选择的渠道列表
        self.cslv_model = QStringListModel()
        self.cslv_model.setStringList([])
        self.cslv = QListView()
        self.cslv.setModel(self.cslv_model)
        self.cslv.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.cslv.doubleClicked.connect(self.delete_channel)
        self.cslv.setContextMenuPolicy(Qt.CustomContextMenu)
        h_layout1.addWidget(self.cslv, 2)
        # 打包进度条显示列表
        self.qpb_list_widget = QListWidget()
        self.qpb_list_widget.setSelectionMode(
            QAbstractItemView.SingleSelection)
        self.qpb_list_widget.itemDoubleClicked.connect(self.select_qpb_list)
        h_layout1.addWidget(self.qpb_list_widget, 5)
        v_layout.addLayout(h_layout1)

        h_layout2 = QHBoxLayout()
        self.back_btn = QPushButton("返 回")
        self.back_btn.setFixedWidth(100)
        self.back_btn.clicked.connect(self.back)
        h_layout2.addWidget(self.back_btn,
                            alignment=Qt.AlignLeft | Qt.AlignBottom)

        h_layout2.addSpacing(100)
        select_apk_btn = QPushButton("选择母包:")
        select_apk_btn.setFixedWidth(100)
        select_apk_btn.clicked.connect(self.select_apk)
        h_layout2.addWidget(select_apk_btn)
        self.apk_path = QLabel()
        self.apk_path.setText("<h3><font color=%s>%s</font></h3>" %
                              ('red', "请浏览选择本地母包路径"))
        h_layout2.addWidget(self.apk_path)
        h_layout2.addSpacing(100)

        self.pack_btn = QPushButton("打 包")
        self.pack_btn.setFixedWidth(100)
        self.pack_btn.clicked.connect(self.click)
        h_layout2.addWidget(self.pack_btn,
                            alignment=Qt.AlignRight | Qt.AlignBottom)

        v_layout.addLayout(h_layout2)
        self.setLayout(v_layout)
Example #3
0
class SideView(QObject):
    onBeforeItemDeletion = pyqtSignal(str)
    onEntrySelected = pyqtSignal(str)
    onDirectoryChanged = pyqtSignal(str)
    onRemoveRequested = pyqtSignal(str)

    def __init__(self, dirLister, entryProvider: IEntryProvider,
                 newEntryText: str, itemNameNormalizer: IItemNameNormalizer):
        super().__init__()

        def initListView():
            self.listView = QListView()
            self.listView.setEditTriggers(QAbstractItemView.NoEditTriggers)
            self.listView.setModel(self.model)
            self.listView.setMinimumWidth(200)

            actionRemove = QAction("Remove", None)
            self.listView.addAction(actionRemove)

            self.listView.selectionModel().currentChanged.connect(
                lambda selectedItem, unselectedItem: self.onEntrySelected.emit(
                    self.itemNameNormalizer.normalizeName(
                        self.currentDir.filePath(selectedItem.data()))))

            def contextMenu(position):
                menu = QMenu()
                index = self.listView.indexAt(position)
                entry = self.model.data(index, Qt.DisplayRole)

                deleteAction = None
                renameAction = None

                addAction = menu.addAction("Add")

                if entry is not None:
                    deleteAction = menu.addAction("Delete")
                    renameAction = menu.addAction("Rename")

                refreshAction = menu.addAction("Refresh")

                chosenAction = menu.exec_(self.listView.mapToGlobal(position))

                if chosenAction is None:
                    return

                if chosenAction == deleteAction:
                    self.onRemoveRequested.emit(entry)
                elif chosenAction == renameAction:
                    self.renameEntry(index, entry)
                elif chosenAction == addAction:
                    self.onCreateNewEntry()
                elif chosenAction == refreshAction:
                    self.refreshListViewEntries()

            self.listView.customContextMenuRequested.connect(contextMenu)
            self.listView.setContextMenuPolicy(Qt.CustomContextMenu)

        self.currentDir: QDir = None
        self.model = QStringListModel()
        self.directoryLister = dirLister
        self.entryProvider = entryProvider
        self.newEntryText = newEntryText
        self.itemNameNormalizer = itemNameNormalizer
        self.sortingParser: IEntrySorting
        initListView()

    def renameEntry(self, index, oldName):
        def onNewEntryCommited(editedLine: QLineEdit):
            self.listView.itemDelegate().commitData.disconnect(
                onNewEntryCommited)
            try:
                newName = editedLine.text()
                normalizedNameOld = self.itemNameNormalizer.normalizeName(
                    oldName)
                normalizedNameNew = self.itemNameNormalizer.normalizeName(
                    newName)

                self.emitOnItemDeletion(normalizedNameOld)

                self.entryProvider.renameEnty(normalizedNameOld,
                                              normalizedNameNew)
                self.sortingParser.rename(oldName, newName)
                self.refreshListViewEntries()
            except Exception:
                pass

        self.listView.itemDelegate().commitData.connect(onNewEntryCommited)
        self.listView.edit(index)

    def emitOnItemDeletion(self, normalizedName):
        self.onBeforeItemDeletion.emit(
            self.currentDir.filePath(normalizedName))

    def removeEntry(self, entry):
        normalizedName = self.itemNameNormalizer.normalizeName(entry)
        self.emitOnItemDeletion(normalizedName)

        self.entryProvider.removeEntry(normalizedName)
        self.refreshListViewEntries()

    def setDirectory(self, dirPath: str):
        self.currentDir = QDir(dirPath)
        self.sortingParser = EntrySortingFile(
            self.currentDir.filePath('.sorting'))
        self.entryProvider.setContext(dirPath)
        self.refreshListViewEntries()

        self.onDirectoryChanged.emit(self.currentDir.absolutePath())

    def refreshListViewEntries(self):
        if self.currentDir is None:
            return
        entries = self.directoryLister.listEntries(self.currentDir)
        sortedEntries = self.sortingParser.getSortedList(entries)

        self.model.setStringList(sortedEntries)

    def onCreateNewEntry(self):
        def onNewEntryCommited(editedLine: QLineEdit):
            self.listView.itemDelegate().commitData.disconnect(
                onNewEntryCommited)
            try:
                if editedLine.text() == self.newEntryText:
                    raise Exception()

                normalizedName = self.itemNameNormalizer.normalizeName(
                    editedLine.text())
                self.entryProvider.addEntry(normalizedName)
                self.refreshListViewEntries()
                self.listView.setCurrentIndex(index)
            except Exception:
                print(f"Error adding entry {editedLine.text()}",
                      file=sys.stderr)
                self.model.removeRow(self.model.rowCount() - 1)

        if not self.model.insertRow(self.model.rowCount()):
            return

        self.listView.itemDelegate().commitData.connect(onNewEntryCommited)
        index = self.model.index(self.model.rowCount() - 1, 0)
        self.model.setData(index, self.newEntryText)
        self.listView.edit(index)
        pass
Example #4
0
class PackageWidgetM(QWidget):
    def __init__(self, main):
        super(PackageWidgetM, self).__init__()
        self.setObjectName("PackageWidgetM")
        self.main_win = main
        self.game_index = 0
        self.selected = [
        ]  # 已选中的channel及所属game列表 如:[{"game": 当前game字典, "channel": 当前channel字典}, {}, {}]
        self.selected_name = [
        ]  # 已选中的渠道显示名称列表 如:["10878922-765321", "", "", ""]
        self.lbps = {}  # 打包信息及进度条组合字典 {"765321": {}, "": {}}
        self.progress = None
        self.monitor = PackageMonitor()
        self.monitor.signal.connect(self.complete)

        v_layout = QVBoxLayout()
        h_layout1 = QHBoxLayout()
        # 全部游戏及其下的渠道列表
        self.tool_box = QToolBox(self)
        self.tool_box.setFixedWidth(100)
        for game in self.main_win.games:
            clv = QListView()
            clv.setEditTriggers(QAbstractItemView.NoEditTriggers)
            clv.setContextMenuPolicy(Qt.CustomContextMenu)
            self.tool_box.addItem(clv, game['id'])
        self.tool_box.currentChanged.connect(self.select_game)
        self.tool_box.setCurrentIndex(self.game_index)
        channel_list_area = QScrollArea()
        channel_list_area.setWidget(self.tool_box)
        h_layout1.addWidget(channel_list_area, 1)
        # 已选择的渠道列表
        self.cslv_model = QStringListModel()
        self.cslv_model.setStringList([])
        self.cslv = QListView()
        self.cslv.setModel(self.cslv_model)
        self.cslv.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.cslv.doubleClicked.connect(self.delete_channel)
        self.cslv.setContextMenuPolicy(Qt.CustomContextMenu)
        h_layout1.addWidget(self.cslv, 2)
        # 打包进度条显示列表
        self.qpb_list_widget = QListWidget()
        self.qpb_list_widget.setSelectionMode(
            QAbstractItemView.SingleSelection)
        self.qpb_list_widget.itemDoubleClicked.connect(self.select_qpb_list)
        h_layout1.addWidget(self.qpb_list_widget, 5)
        v_layout.addLayout(h_layout1)

        h_layout2 = QHBoxLayout()
        self.back_btn = QPushButton("返 回")
        self.back_btn.setFixedWidth(100)
        self.back_btn.clicked.connect(self.back)
        h_layout2.addWidget(self.back_btn,
                            alignment=Qt.AlignLeft | Qt.AlignBottom)

        h_layout2.addSpacing(100)
        select_apk_btn = QPushButton("选择母包:")
        select_apk_btn.setFixedWidth(100)
        select_apk_btn.clicked.connect(self.select_apk)
        h_layout2.addWidget(select_apk_btn)
        self.apk_path = QLabel()
        self.apk_path.setText("<h3><font color=%s>%s</font></h3>" %
                              ('red', "请浏览选择本地母包路径"))
        h_layout2.addWidget(self.apk_path)
        h_layout2.addSpacing(100)

        self.pack_btn = QPushButton("打 包")
        self.pack_btn.setFixedWidth(100)
        self.pack_btn.clicked.connect(self.click)
        h_layout2.addWidget(self.pack_btn,
                            alignment=Qt.AlignRight | Qt.AlignBottom)

        v_layout.addLayout(h_layout2)
        self.setLayout(v_layout)

    def select_game(self, p_int):
        self.game_index = p_int
        if 'apk' in self.main_win.games[self.game_index]:
            self.apk_path.setText(self.main_win.games[self.game_index]['apk'])
        else:
            self.apk_path.setText("<h3><font color=%s>%s</font></h3>" %
                                  ('red', "请浏览选择本地母包路径"))
            # self.apk_path.setText("请浏览选择本地母包路径")
        # 当前选中的game,其下的所有渠道列表
        self.channels = Utils.get_channels(
            Utils.get_full_path('games/' + self.main_win.games[p_int]['id'] +
                                '/config.xml'))
        channel_ids = []
        for channel in self.channels:
            channel_ids.append(channel['channelId'])
        list_model = QStringListModel()
        list_model.setStringList(channel_ids)
        self.clv = self.tool_box.currentWidget()
        self.clv.doubleClicked.connect(self.select_channel)
        self.clv.setModel(list_model)

    # 双击选中当前渠道,更新已选中列表的model
    def select_channel(self):
        if 'apk' not in self.main_win.games[self.game_index]:
            QMessageBox.warning(self, "警告", "请先添加母包!")
            return
        channel = self.channels[self.clv.currentIndex().row()]
        name = self.main_win.games[
            self.game_index]['id'] + '-' + channel['channelId']
        if name in self.selected_name:
            return
        self.selected_name.append(name)
        self.cslv_model.setStringList(self.selected_name)
        package = {
            'game': self.main_win.games[self.game_index],
            'channel': channel
        }
        self.selected.append(package)

    # 双击移除当前渠道,更新已选中列表的model
    def delete_channel(self):
        name = self.selected_name[self.cslv.currentIndex().row()]
        self.selected_name.remove(name)
        self.cslv_model.setStringList(self.selected_name)
        package = self.selected[self.cslv.currentIndex().row()]
        self.selected.remove(package)

    def select_qpb_list(self):
        index = self.qpb_list_widget.currentIndex().row()
        game_id = self.selected[index]['game']['id']
        channel_id = self.selected[index]['channel']['channelId']
        success = self.lbps[channel_id]['success']
        dest_apk_dir = Utils.get_full_path('output/' + game_id + '/' +
                                           channel_id)
        if success:
            os.startfile(dest_apk_dir)
        else:
            QMessageBox.warning(self, "警告", "打包成功了吗?")

    def back(self):
        self.monitor.deleteLater()
        self.main_win.set_main_widget()

    def select_apk(self):
        f_name = QFileDialog.getOpenFileName(
            self, '选择母包', os.path.join(os.path.expanduser('~'), "Desktop"),
            ("Apk (*.apk)"))
        if f_name[0]:
            self.apk_path.setStyleSheet("font-size:12px")
            self.apk_path.setText(f_name[0])
            self.main_win.games[self.game_index]['apk'] = f_name[0]

    def click(self):
        if self.pack_btn.text() == "打 包":
            self.package()
        elif self.pack_btn.text() == "取 消":
            self.cancel()

    def package(self):
        # 清空上次打包完成后的进度条显示列表
        count = self.qpb_list_widget.count()
        if count > 0:
            for i in range(count):
                item = self.qpb_list_widget.takeItem(0)
                del item

        if len(self.selected) <= 0:
            QMessageBox.warning(self, "警告", "请选择需要打包的渠道!")
            return
        for package in self.selected:
            # {"success": 是否成功, "label": 进度条文本view, "qpb": 进度条view, "runnable": 打包任务}
            lbp = {'success': False}
            self.set_qpb_list_item(package['channel']['channelId'], lbp)
            runnable = PackRunnable(package['game'], package['channel'],
                                    package['game']['apk'])
            runnable.signal.signal.connect(self.set_value)
            self.monitor.add_runnable(runnable)
            lbp['runnable'] = runnable
            self.lbps[package['channel']['channelId']] = lbp
        # 开启监听线程
        self.monitor.start()
        # 开始打包,不可返回,返回按钮禁用;设置打包按钮文本为"取 消"
        self.back_btn.setDisabled(True)
        self.pack_btn.setText("取 消")

    def set_qpb_list_item(self, channel_id, lbp):
        item = QListWidgetItem(self.qpb_list_widget)
        item.setSizeHint(QSize(400, 80))
        widget = QWidget(self.qpb_list_widget)
        v_layout = QVBoxLayout()
        label = QLabel(channel_id + "==>>>等待出包...")
        v_layout.addWidget(label)
        lbp['label'] = label
        qpb = QProgressBar(self.qpb_list_widget)
        v_layout.addWidget(qpb)
        lbp['qpb'] = qpb
        widget.setLayout(v_layout)
        self.qpb_list_widget.addItem(item)
        self.qpb_list_widget.setItemWidget(item, widget)

    def set_value(self, channel_id, result, msg, step):
        lbp = self.lbps[channel_id]
        if result:  # 打包步骤异常,提示异常,关闭进度条
            lbp['label'].setText(channel_id + "==>>>" + msg)
            lbp['qpb'].close()
            if step == 100:
                lbp['success'] = True
                self.lbps[channel_id] = lbp
        else:  # 打包正常,设置进度条进度
            lbp['qpb'].setValue(step)
            if step == 0:
                lbp['label'].setText(channel_id + "==>>>" + msg)

    # 取消打包(全部取消)
    def cancel(self):
        self.progress = QProgressDialog(self)
        self.progress.setFixedWidth(500)
        self.progress.setFixedHeight(80)
        self.progress.setWindowTitle("正在取消,请稍等...")
        self.progress.setCancelButtonText("取消")
        self.progress.setMinimumDuration(1)
        self.progress.setWindowModality(Qt.ApplicationModal)
        self.progress.setRange(0, 0)
        self.progress.show()
        # 清空进度条显示列表
        count = self.qpb_list_widget.count()
        for i in range(count):
            item = self.qpb_list_widget.takeItem(0)
            del item

        # 清空任务线程池;线程池清空后,会触发监听线程的完成信号,重置返回和打包按钮
        # 因为打包任务调用外部程序,并不能立即终止外部程序连接,所以清空过程有延迟
        for channel_id in self.lbps:
            self.lbps[channel_id]['runnable'].is_close = True
        self.monitor.clear()

    # 取消打包(清空任务完成),或打包完成,
    def complete(self):
        if self.progress is not None:
            self.progress.cancel()
        # 返回按钮解禁;设置打包按钮文本为"打 包"
        self.back_btn.setDisabled(False)
        self.pack_btn.setText("打 包")
class ChannelListWidget(QWidget):
    def __init__(self, main, channels):
        super(ChannelListWidget, self).__init__()
        self.main_win = main
        self.channels = channels
        self.channel_index = len(channels) - 1
        self.linedit_list = []
        self.channel_ids = []
        for channel in self.channels:
            self.channel_ids.append(channel['channelId'])
        self.channel_ids.append(" + 添加渠道")

        self.setObjectName("ChannelListWidget")

        v_layout1 = QVBoxLayout()
        h_layout1 = QHBoxLayout()
        self.list_model = QStringListModel()
        self.list_model.setStringList(self.channel_ids)
        self.channel_list_view = QListView()
        self.channel_list_view.setModel(self.list_model)
        self.channel_list_view.setEditTriggers(
            QAbstractItemView.NoEditTriggers)
        self.channel_list_view.clicked.connect(self.list_item_onclick)
        self.channel_list_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.channel_list_view.customContextMenuRequested.connect(
            self.show_del_menu)
        h_layout1.addWidget(self.channel_list_view, 1)

        self.channel = self.channels[self.channel_index]

        form_layout = QFormLayout()
        form_layout.setContentsMargins(10, 100, 10, 50)
        form_layout.addRow(
            "游戏ID:",
            QLabel(self.main_win.games[self.main_win.game_index]['id']))
        self.channel_id_value = QLabel(self.channel['channelId'])
        self.channel_id_value.setTextInteractionFlags(Qt.TextSelectableByMouse)
        form_layout.addRow("渠道ID:", self.channel_id_value)
        self.game_name_value = QLineEdit()
        form_layout.addRow("游戏名称:", self.game_name_value)
        self.game_package_value = QLineEdit()
        form_layout.addRow("游戏包名:", self.game_package_value)
        self.game_vcode_value = QLineEdit()
        form_layout.addRow("游戏版本号:", self.game_vcode_value)
        self.game_vname_value = QLineEdit()
        form_layout.addRow("游戏版本名:", self.game_vname_value)
        self.debug_value = QLineEdit()
        form_layout.addRow("打印日志:", self.debug_value)
        h_layout1.addLayout(form_layout, 4)

        self.form_layout2 = QFormLayout()
        self.form_layout2.setContentsMargins(10, 100, 10, 50)
        self.set_info()
        h_layout1.addLayout(self.form_layout2, 4)
        v_layout1.addLayout(h_layout1)

        h_layout2 = QHBoxLayout()
        back_btn = QPushButton("返 回")
        back_btn.setFixedWidth(100)
        back_btn.clicked.connect(self.back)
        h_layout2.addWidget(back_btn, alignment=Qt.AlignLeft | Qt.AlignBottom)

        save_btn = QPushButton("保 存")
        save_btn.setFixedWidth(100)
        save_btn.clicked.connect(self.save_data)
        h_layout2.addWidget(save_btn, alignment=Qt.AlignHCenter)

        pack_btn = QPushButton("打 包")
        pack_btn.setFixedWidth(100)
        pack_btn.clicked.connect(self.to_package)
        h_layout2.addWidget(pack_btn, alignment=Qt.AlignRight | Qt.AlignBottom)

        v_layout1.addLayout(h_layout2)
        self.setLayout(v_layout1)

    def set_info(self):
        self.channel_id_value.setText(self.channel['channelId'])
        self.game_name_value.setText(self.channel['gameName'])
        self.game_package_value.setText(self.channel['package'])
        self.game_vcode_value.setText(self.channel['gameVersionCode'])
        self.game_vname_value.setText(self.channel['gameVersionName'])
        self.debug_value.setText(self.channel['debug'])
        # 先清空之前渠道参数表单,再添加
        for i in range(self.form_layout2.rowCount()):
            # 因为formlayout清除一行,会自动上移,所以只需remove第一行
            self.form_layout2.removeRow(0)
        self.linedit_list.clear()
        # 再添加当前选择的渠道参数
        channel_name = QLabel(self.channel['name'] + '\t\t\tVersion:' +
                              self.channel['sdkVersionName'] + '\t\tUpdate:' +
                              self.channel['sdkUpdateTime'])
        channel_name.setAlignment(Qt.AlignRight)
        self.form_layout2.addRow(channel_name)
        for param in self.channel['sdkParams']:
            line_edit = QLineEdit(param['value'])
            self.form_layout2.addRow(param['showName'] + ':', line_edit)
            self.linedit_list.append(line_edit)

    def list_item_onclick(self):
        if self.channel_list_view.currentIndex().row() == len(
                self.channel_ids) - 1:
            self.main_win.set_add_channel_widget(self.channels, self.channel)
        else:
            self.channel_index = self.channel_list_view.currentIndex().row()
            self.channel = self.channels[self.channel_index]
            self.set_info()

    def back(self):
        self.main_win.set_game_list_widget(self.main_win.games)

    def to_package(self):
        if not self.save_data():
            return
        self.main_win.set_package_widget(self.channels)

    def save_data(self):
        self.channel['gameName'] = self.game_name_value.text().strip()
        self.channel['package'] = self.game_package_value.text().strip()
        self.channel['gameVersionCode'] = self.game_vcode_value.text().strip()
        self.channel['gameVersionName'] = self.game_vname_value.text().strip()
        self.channel['debug'] = self.debug_value.text().strip()
        i = 0
        while i < len(self.linedit_list):
            if self.linedit_list[i].text().strip() == "":
                QMessageBox.warning(self, "警告", "渠道参数不能为空!")
                return False
            self.channel['sdkParams'][i]['value'] = self.linedit_list[i].text(
            ).strip()
            i += 1
        self.channels[self.channel_index] = self.channel
        game_id = self.main_win.games[self.main_win.game_index]['id']
        return Utils.update_channels(
            Utils.get_full_path('games/' + game_id + '/config.xml'),
            self.channel, self.channel_index)

    def show_del_menu(self, point):
        self.list_item_onclick()
        if -1 < self.channel_index < len(self.channel_ids) - 1:
            menu = QMenu(self.channel_list_view)
            del_action = QAction("删 除", menu)
            del_action.triggered.connect(self.del_channel)
            menu.addAction(del_action)
            menu.popup(self.channel_list_view.mapToGlobal(point))

    def del_channel(self):
        reply = QMessageBox.warning(self, "警告", "确定删除当前渠道?",
                                    QMessageBox.Yes | QMessageBox.No,
                                    QMessageBox.No)
        if reply == QMessageBox.Yes:
            # 更新listview
            self.channel_ids.pop(self.channel_index)
            self.list_model.setStringList(self.channel_ids)
            # 更新表单view(index前移一位)
            self.channel = self.channels[self.channel_index - 1]
            self.set_info()
            # 更新本地数据
            self.channels.pop(self.channel_index)
            game_id = self.main_win.games[self.main_win.game_index]['id']
            Utils.del_channel(
                Utils.get_full_path('games/' + game_id + '/config.xml'),
                self.channel_index)
            # 重置index,防止 index out of range
            self.channel_index = self.channel_index - 1
            if self.channel_index < 0:
                reply = QMessageBox.warning(self, "警告", "当前游戏未添加渠道,将返回游戏列表界面!",
                                            QMessageBox.Yes)
                if reply == QMessageBox.Yes:
                    self.back()