首页 > 编程语言 >【教程】0x04 绘制记牌器窗口及插件打包 —— 通过MYScrcpy学python系列

【教程】0x04 绘制记牌器窗口及插件打包 —— 通过MYScrcpy学python系列

时间:2024-10-11 09:50:25浏览次数:3  
标签:插件 0x04 self dpg python Card def cards CARD

前言

MYScrcpy Github / Gitee
从 1.6.4 版本开始,MYScrcpy为童鞋们提供了一个开放的插件开发环境(Extensions)。童鞋们可以根据需要自行开发插件。

本章主要讲解如何通过面向对象的思想,借助Dearpygui绘制一个记牌器面板,同时如何打包插件进行共享。

注意

插件用于功能测试及教学目的,切勿违法违规使用!

我们开始吧

创建功能类

  1. ddz类下创建ddz_cls.py,新建对象类
  2. 既然是记牌器,首先要把游戏各个对象抽象出来,形成类
@dataclass
class Card:
    """
        Poker Card
    """
    name: str
    index: int
    max_n: int = 4

    def __hash__(self):
        return hash(self.name)


class Cards:
    """
        Poker Card Type
    """
    CARD_D = Card("D", index=0, max_n=1)
    CARD_X = Card("X", index=1, max_n=1)

    CARD_2 = Card("2", index=2)

    CARD_A = Card("A", index=3)
    CARD_K = Card("K", index=4)
    CARD_Q = Card("Q", index=5)
    CARD_J = Card("J", index=6)
    CARD_T = Card("T", index=7)
    CARD_9 = Card("9", index=8)
    CARD_8 = Card("8", index=9)
    CARD_7 = Card("7", index=10)
    CARD_6 = Card("6", index=11)
    CARD_5 = Card("5", index=12)
    CARD_4 = Card("4", index=13)
    CARD_3 = Card("3", index=14)

    @classmethod
    def ordered_cards(cls) -> list[Card]:
        """
            卡牌顺序列表
        :return:
        """
        return [
            cls.CARD_D, cls.CARD_X,
            cls.CARD_2, cls.CARD_A,
            cls.CARD_K, cls.CARD_Q, cls.CARD_J,
            cls.CARD_T, cls.CARD_9, cls.CARD_8, cls.CARD_7, cls.CARD_6, cls.CARD_5, cls.CARD_4, cls.CARD_3
        ]

    @classmethod
    def str2cards(cls, cmd: str) -> list[Card] | None:
        """
            cmd to card list
            B5 means Bomb 4
            SQ means qqq
            E8 means 88

        :param cmd:
        :return:
        """
        cmd = cmd.upper()
        if len(cmd) in [3, 4] and cmd[1] == '-':
            try:
                card_0 = getattr(cls, f"CARD_{cmd[0]}")
                card_1 = getattr(cls, f"CARD_{cmd[2]}")
            except Exception as e:
                return None

            return cls.ordered_cards()[min(card_0.index, card_1.index):max(card_0.index, card_1.index)+1] * (
                1 if len(cmd) == 3 else int(cmd[3])
            )

        cmd = cmd.replace('W', 'DX')

        repeat = 0
        cards = []

        for _ in cmd:
            if repeat:
                cards.extend([getattr(cls, f"CARD_{_}")] * repeat)
                repeat = 0

            else:
                if _ == 'B':
                    repeat = 4
                    continue

                elif _ == 'S':
                    repeat = 3
                    continue

                elif _ == 'E':
                    repeat = 2
                    continue

                else:
                    cards.append(getattr(cls, f"CARD_{_}"))

        return cards

定义卡槽,存储卡序列

class CardSlot:
    """
        卡槽
    """

    def __init__(self, max_slots: int):
        self.max_slots = max_slots
        self.cards = []

    def __len__(self):
        return len(self.cards)

    def add_cards(self, cards: Iterable[Card] | None):
        """
            新增卡
        :param cards:
        :return:
        """
        cards and self.cards.extend(cards)

    @property
    def card_dict(self):
        """
            卡字典
        :return:
        """
        _ = {}

        for card in self.cards:
            _.setdefault(card, {'n': 0})['n'] += 1

        return _

定义玩家、出牌环节及游戏局

class Player:
    """
        玩家
    """
    def __init__(self, name: str, is_landlord: bool = False, public_cards: list[Card] = None):

        self.name = name

        self.is_landlord = is_landlord

        self.slot_on_table = CardSlot(20)
        self.slot_hold = CardSlot(20)

        if is_landlord and public_cards:
            self.slot_hold.add_cards(public_cards)

    @property
    def hold_cards_n(self) -> int:
        """
            剩卡数
        :return:
        """
        return (20 if self.is_landlord else 17) - len(self.slot_on_table)


@dataclass
class Round:
    """
        一轮游戏
    """
    index: int
    player: Player
    action: int
    cards: list[Card] = field(default_factory=list)


class Rounds:

    ACTION_PASS = 0
    ACTION_PLAY = 1

    PLAYER_N = 3

    def __init__(self, who_is_landlord: int, public_cards: list[Card], self_cards: list[Card]):

        self.players = [
            Player(['L', 'Y', 'R'][_], who_is_landlord == _, public_cards) for _ in range(3)
        ]

        self.now = who_is_landlord

        self.slot_on_table = CardSlot(54)

        self.card_rounds = []

        self.player_self.slot_hold.add_cards(self_cards)

    @property
    def player_self(self) -> Player:
        return self.players[1]

    @property
    def player_current(self) -> Player:
        return self.players[self.now]

    def play(self, action: int, cards: list[Card]) -> Round:
        """
            出牌
        :param action:
        :param cards:
        :return:
        """

        _r = Round(len(self.card_rounds), self.players[self.now], action, cards)

        self.card_rounds.append(_r)

        self.slot_on_table.add_cards(cards)

        self.now += (1 if self.now < (self.PLAYER_N - 1) else -(self.PLAYER_N - 1))

        return _r

定义一个记牌器组件及记牌器窗口

class CardsBoard:
    """
        记牌器
    """
    def __init__(self):
        self.tag_table = dpg.generate_uuid()

    def clear(self):
        dpg.delete_item(self.tag_table, children_only=True, slot=1)

    def draw(self, parent: str | int):
        with dpg.table(tag=self.tag_table, parent=parent):
            for card in Cards.ordered_cards():
                dpg.add_table_column(label=card.name)

            dpg.add_table_column(label='n')

    def update(self, rounds: Rounds):

        self.clear()

        with dpg.table_row(parent=self.tag_table):
            cards_out_all = 0
            for card in Cards.ordered_cards():
                cards_n = rounds.slot_on_table.cards.count(card)
                cards_out = card.max_n - cards_n - rounds.player_self.slot_hold.cards.count(card)
                cards_out_all += cards_out

                msg = ''
                if cards_out > 0:
                    if cards_out == 4:
                        msg += '@4'
                    else:
                        msg += f"{cards_out}"

                dpg.add_text(msg)

            dpg.add_text(str(cards_out_all))


class DDZBoard:
    """
        斗地主记牌器
    """

    players = ['Left', 'Your', 'Right']

    def __init__(self):
        self.tag_win = dpg.generate_uuid()

        self.tag_ipt_yc = dpg.generate_uuid()
        self.tag_ipt_cards = dpg.generate_uuid()
        self.tag_txt_cur_player = dpg.generate_uuid()

        self.board_cards = CardsBoard()

    def new_round(self, sender, app_data, user_data):
        """
            新游戏
        :param sender:
        :param app_data:
        :param user_data:
        :return:
        """
        self.rounds = Rounds(
            self.players.index(dpg.get_value(user_data[0])),
            Cards.str2cards(dpg.get_value(user_data[1])),
            Cards.str2cards(dpg.get_value(user_data[2]))
        )

        dpg.set_value(user_data[1], '')
        dpg.set_value(user_data[2], '')

        self.clear()
        self.update()

    def play(self, sender, app_data, user_data):
        if user_data is None:
            self.rounds.play(Rounds.ACTION_PASS, [])

        else:
            self.rounds.play(Rounds.ACTION_PLAY, Cards.str2cards(dpg.get_value(user_data)))
            dpg.set_value(user_data, '')

        self.update()

    def draw(self):
        with dpg.window(tag=self.tag_win, width=700, height=160, label='Cards Board'):

            with dpg.group(horizontal=True):
                tag_ll = dpg.add_combo(
                    items=self.players, label='LandLord', default_value=self.players[1], width=60)
                tag_pub = dpg.add_input_text(label='Public cards', width=50)
                self.tag_ipt_cards = dpg.add_input_text(tag=self.tag_ipt_yc, label='Your cards', width=150)
                dpg.add_button(label='Start', user_data=(tag_ll, tag_pub, self.tag_ipt_yc), callback=self.new_round)

            dpg.add_separator()

            with dpg.group(horizontal=True):
                dpg.add_text(tag=self.tag_txt_cur_player, default_value='Ready')
                dpg.add_input_text(label='Cards', width=150)
                dpg.add_button(label='Play', user_data=dpg.last_item(), callback=self.play)
                dpg.add_button(label='Pass', callback=self.play)

            dpg.add_separator()

            self.board_cards.draw(self.tag_win)

    def clear(self):
        self.board_cards.clear()
        dpg.set_value(self.tag_txt_cur_player, f"Ready")

    def update(self):
        self.board_cards.update(self.rounds)
        dpg.set_value(self.tag_txt_cur_player, f"Wait Player {self.rounds.player_current.name}")
  1. 在插件中引入记牌器类,并添加相应功能
...
def start(self):
	...
    # 创建记牌器面板
    self.board = DDZBoard()

def predict(self):
	...
	# 玩家手牌位置
    if y0 > 0.6:
       name = names[int(box.cls[0])][1]
       if name.upper() == 'O':
       	   # 判断大小王
           _img = Image.fromarray(
               result.orig_img
           ).crop([
               round(_) for _ in box.xyxy.tolist()[0]
           ])
           blue = self.cal_blue(_img)
           if blue > 5:
               cards += 'D'
           else:
               cards += 'X'

       else:
           cards += name.upper()

   new_cards = ''

   for card in Cards.ordered_cards():
       new_cards += card.name * cards.count(card.name)

   dpg.set_value(self.board.tag_ipt_cards,  new_cards)

 @staticmethod
 def cal_blue(img: Image.Image) -> int:
     pixels = img.size[0] * img.size[1]
     colors = img.getcolors(pixels)
     return round(sum([
         count / pixels for count, pixel_color in
         colors if pixel_color[0] < 50 and pixel_color[1] < 50 and pixel_color[2] > 150
     ]) * 100)

插件打包及共享

实际上,插件打包非常简单
新建一个ddz文件夹,将项目相关的文件拷贝其中
|- ddz
|- __init__.py
|- extension.toml
|- ddz.py
|- ddz_cls.py
|- pts/best.pt

将整个ddz文件压缩成ddz.zip文件
这样,插件就打包完成了。

注意,ZIP文件中需包含顶层文件夹,即打开ZIP文件时目录为 ddz文件夹
在这里插入图片描述

运行MYScrcpy,查看效果

将打包好的插件 ddz.zip 拷贝至 myscrcpy 插件目录下

~/.myscrcpy/extensions/

本次直接使用 mysc-cli 命令启动 MYScrcpy
可以看到,已经通过加载插件的方式加载ddz插件
在这里插入图片描述
加载日志:
在这里插入图片描述
现在,你可以将 ddz.zip 分享给你的小伙伴,通过MYScrcpy直接加载使用了!

注意,在加载插件时,需要提前安装插件运行所需的 PIL/Opencv/ultralytics 等包

总结

至此,我们借助MYScrcpy 插件架构完成了一个基础的记牌器,本系列教程也将告一段落。
因篇幅有限,插件功能简陋,不过正如笔者写这个系列的初衷一样,授之于鱼,不如授之以渔。
希望各位童鞋能通过这个系列了解MYScrcpy,了解Python,进而编写创造属于你自己的MYScrcpy插件!

文中ddz.zip已经放到群共享。
有任何问题欢迎留言或加Q群579618095交流。

标签:插件,0x04,self,dpg,python,Card,def,cards,CARD
From: https://blog.csdn.net/weixin_43463913/article/details/142832363

相关文章

  • Python快速编程小案例——打印蚂蚁森林植树证书
    提示:(个人学习),案例来自工业和信息化“十三五”人才培养规划教材,《Python快速编程入门》第2版,黑马程序员◎编著蚂蚁森林是支付宝客户端发起“碳账户”的一款公益活动:用户通过步行地铁出行、在线消费等行为,可在蚂蚁森林中获取能量,当能量到达一定数值后,用户可以在支付宝中申请......
  • [Python手撕]不同的二叉搜索树(给出具体的树)
    #Definitionforabinarytreenode.#classTreeNode:#def__init__(self,val=0,left=None,right=None):#self.val=val#self.left=left#self.right=rightclassSolution:defgenerateTrees(self,n:int)->List......
  • 计算机毕业设计 医院预约挂号系统的设计与实现 Python毕业设计 Python毕业设计选题【
    博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌......
  • 计算机毕业设计 Python医疗预约与诊断系统的设计与实现 Python毕业设计 Python毕业设
    博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌......
  • 【编程小白必看】Python编程练习题元组操作秘籍一文全掌握
    【编程小白必看】Python编程练习题元组操作秘籍......
  • Python小白进阶篇之概率论
        今天我们的学习笔记到了概率论这一篇,相信各位对于概率都不会太陌生,在高中作为选择题和大题,大家与之接触的不算少,那么走近属于大学的概率,战友们也一举拿下!!!一、事件概率1.1事件事件是指在某个试验或观察中可能发生的结果或结果的集合。是样本空间的一个子集,可以包......
  • python中迭代什么意思
    1、迭代:迭代是通过重复执行的代码处理相似的数据集的过程,并且本次迭代的处理数据要依赖上一次的结果继续往下做,上一次产生的结果为下一次产生结果的初始状态,如果中途有任何停顿,都不能算是迭代。例如:(1)非迭代例子loop = 0while loop < 3:    print("Hello worl......
  • python3开头如何设置utf-8
    编码格式1:在源文件第一行或者第二行定义:# coding=<encoding name>例如:# coding=utf-8编码格式2:(这种最流行)格式如下:#!/usr/bin/python # -*- coding: <encoding name> -*-例如:#!/usr/bin/python # -*- coding: utf-8 -*-编码格式3:(如果使用......
  • Python 从入门到实战35(进程-multiprocessing模块)
            我们的目标是:通过这一套资料学习下来,可以熟练掌握python基础,然后结合经典实例、实践相结合,使我们完全掌握python,并做到独立完成项目开发的能力。        上篇文章我们讨论了turtle库绘制图画操作的相关知识。今天学习一下进程的概念和创建方法。1、......
  • Python 从入门到实战36(进程-Process、Pool类)
            我们的目标是:通过这一套资料学习下来,可以熟练掌握python基础,然后结合经典实例、实践相结合,使我们完全掌握python,并做到独立完成项目开发的能力。        上篇文章我们讨论了通过multiprocessing模块创建进程操作的相关知识。今天学习一下Process、Po......