Django学习笔记-实现聊天系统

这篇具有很好参考价值的文章主要介绍了Django学习笔记-实现聊天系统。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

笔记内容转载自 AcWing 的 Django 框架课讲义,课程链接:AcWing Django 框架课。

1. 实现聊天系统前端界面

聊天系统整体可以分为两部分:输入框与历史记录。

我们需要先修改一下之前代码中的一个小 BUG,当在一个窗口中按 Q 时,另一个窗口中点击鼠标左键也能攻击,因为按下按键的事件被所有窗口都捕捉到了,这是不合理的。

我们之前监听的对象是 window,每个地图是一个 canvas 元素,因此我们可以绑定到 canvas 对象上。由于不是所有对象都能添加绑定事件的,因此我们还需要对 canvas 做一个修改,需要添加 tabindex 参数并将其聚焦后才能监听事件,首先在 GameMap 类中修改一下 canvas 对象:

class GameMap extends AcGameObject {
    constructor(playground) {  // 需要将AcGamePlayground传进来
        super();  // 调用基类构造函数,相当于将自己添加到了AC_GAME_OBJECTS中
        this.playground = playground;
        this.$canvas = $(`<canvas tabindex=0></canvas>`);  // 画布,用来渲染画面,tabindex=0表示能够监听事件
        ...
    }

    start() {
        this.$canvas.focus();  // 聚焦后才能监听事件
    }

    ...
}

Player 类中修改:

class Player extends AcGameObject {
    ...

    add_listening_events() {
        let outer = this;

        ...

        this.playground.game_map.$canvas.keydown(function(e) {
            if (outer.playground.state !== 'fighting')
                return true;

            if (e.which === 81 && outer.fireball_coldtime < outer.eps) {  // Q键
                outer.cur_skill = 'fireball';
                return false;
            } else if (e.which === 70 && outer.blink_coldtime < outer.eps) {  // F键
                outer.cur_skill = 'blink';
                return false;
            }
        });
    }

    ...
}

聊天的前端界面需要创建一个新的文件,我们在 ~/djangoapp/game/static/js/src/playground 目录下创建一个 chat_field 目录,并进入该目录创建 zbase.js 文件:

class ChatField {
    constructor(playground) {
        this.playground = playground;
        this.func_id = null;  // 在每次打开输入框时需要将之前历史记录框的计时函数删掉

        this.$history = $(`<div class='ac_game_chat_field_history'></div>`);
        this.$input = $(`<input type='text' class='ac_game_chat_field_input'>`);

        this.$history.hide();
        this.$input.hide();

        this.playground.$playground.append(this.$history);
        this.playground.$playground.append(this.$input);

        this.start();
    }

    start() {
        this.add_listening_events();
    }

    add_listening_events() {
        let outer = this;

        this.$input.keydown(function(e) {  // 输入框也需要监听ESC事件
            if (e.which === 27) {
                outer.hide_input();
                return false;
            }
        });
    }

    show_history() {
        let outer = this;
        this.$history.fadeIn();  // 慢慢显示出来

        if (this.func_id) clearTimeout(this.func_id);

        this.func_id = setTimeout(function() {
            outer.$history.fadeOut();
            outer.func_id = null;
        }, 3000);  // 显示3秒后消失
    }

    show_input() {
        this.$input.show();
        this.show_history();  // 打开输入框顺带打开历史记录
        this.$input.focus();  // 聚焦一下才能输入
    }

    hide_input() {
        this.$input.hide();
        this.playground.game_map.$canvas.focus();  // 关闭输入框后要重新聚焦回Canvas上
    }
}

然后在 AcGamePlayground 类中创建出来:

class AcGamePlayground {
    ...

    // 显示playground界面
    show(mode) {
        ...

        // 单人模式下创建AI敌人
        if (mode === 'single mode'){
            for (let i = 0; i < 8; i++) {
                this.players.push(new Player(this, this.width / 2 / this.scale, 0.5, 0.07, this.get_random_color(), 0.15, 'robot'));
            }
        } else if (mode === 'multi mode') {
            this.mps = new MultiPlayerSocket(this);
            this.mps.uuid = this.players[0].uuid;  // 用每名玩家的唯一编号区分不同的窗口

            this.chat_field = new ChatField(this);  // 聊天区

            this.mps.ws.onopen = function() {
                outer.mps.send_create_player(outer.root.settings.username, outer.root.settings.avatar);
            };
        }
    }

    ...
}

现在在 Player 类中即可监听事件:

class Player extends AcGameObject {
    ...

    add_listening_events() {
        let outer = this;

        ...

        this.playground.game_map.$canvas.keydown(function(e) {
            if (e.which === 13 && outer.playground.mode === 'multi mode') {  // 还没满人允许使用聊天功能
                outer.playground.chat_field.show_input();
                return false;
            } else if (e.which === 27 && outer.playground.mode === 'multi mode') {
                outer.playground.chat_field.hide_input();
                return false;
            }

            if (outer.playground.state !== 'fighting')
                return true;

            if (e.which === 81 && outer.fireball_coldtime < outer.eps) {  // Q键
                outer.cur_skill = 'fireball';
                return false;
            } else if (e.which === 70 && outer.blink_coldtime < outer.eps) {  // F键
                outer.cur_skill = 'blink';
                return false;
            }
        });
    }

    ...
}

然后我们还需要实现一下聊天区的 CSS 样式(在 ~/djangoapp/game/static/css 目录的 game.css 文件中):

...

.ac_game_chat_field_history {
    position: absolute;
    top: 40%;
    left: 15%;
    transform: translate(-50%, 50%);
    width: 20%;
    height:30%;
    color: white;
    background-color: rgba(77, 77, 77, 0.2);
    font-size: 1.5vh;
    padding: 5px;
    overflow: auto;
}

.ac_game_chat_field_history::-webkit-scrollbar {  /* 滚动条 */
    width: 1;
}

.ac_game_chat_field_input {
    position: absolute;
    top: 86%;
    left: 15%;
    transform: translate(-50%, 50%);
    width: 20%;
    height: 2vh;
    color: white;
    background-color: rgba(222, 225, 230, 0.2);
    font-size: 1.5vh;
}

现在我们实现在历史记录区域里添加新消息的功能:

class ChatField {
    constructor(playground) {
        ...
    }

    start() {
        this.add_listening_events();
    }

    add_listening_events() {
        let outer = this;

        this.$input.keydown(function(e) {  // 输入框也需要监听ESC事件
            if (e.which === 27) {
                outer.hide_input();
                return false;
            } else if (e.which === 13) {  // 按Enter键时发送消息
                let username = outer.playground.root.settings.username;
                let text = outer.$input.val();
                outer.hide_input();  // 发送完消息后关闭输入框
                if (text) {  // 信息不为空才渲染出来
                    outer.$input.val('');  // 将输入框清空
                    outer.add_message(username, text);
                }
                return false;
            }
        });
    }

    render_message(message) {  // 渲染消息
        return $(`<div>${message}</div>`);
    }

    add_message(username, text) {  // 向历史记录区里添加消息
        let message = `[${username}] ${text}`;
        this.$history.append(this.render_message(message));
        this.show_history();  // 每次发新消息时都显示一下历史记录
        this.$history.scrollTop(this.$history[0].scrollHeight);  // 将滚动条移动到最底部
    }

    ...
}

2. 实现后端同步函数

我们先在 WebSocket 前端实现发送和接收消息的函数:

class MultiPlayerSocket {
    ...

    receive() {
        let outer = this;

        this.ws.onmessage = function(e) {
            let data = JSON.parse(e.data);  // 将字符串变回JSON
            let uuid = data.uuid;
            if (uuid === outer.uuid) return false;  // 如果是给自己发送消息就直接过滤掉

            let event = data.event;
            if (event === 'create_player') {  // create_player路由
                outer.receive_create_player(uuid, data.username, data.avatar);
            } else if (event === 'move_to') {  // move_to路由
                outer.receive_move_to(uuid, data.tx, data.ty);
            } else if (event === 'shoot_fireball') {  // shoot_fireball路由
                outer.receive_shoot_fireball(uuid, data.tx, data.ty, data.fireball_uuid);
            } else if (event === 'attack') {  // attack路由
                outer.receive_attack(uuid, data.attackee_uuid, data.x, data.y, data.theta, data.damage, data.fireball_uuid);
            } else if (event === 'blink') {  // blink路由
                outer.receive_blink(uuid, data.tx, data.ty);
            } else if (event === 'message') {  // message路由
                outer.receive_message(data.username, data.text);
            }
        };
    }

    ...

    send_message(username, text) {
        let outer = this;
        this.ws.send(JSON.stringify({
            'event': 'message',
            'uuid': outer.uuid,
            'username': username,
            'text': text,
        }));
    }

    receive_message(username, text) {
        this.playground.chat_field.add_message(username, text);
    }
}

然后实现后端代码:

import json
from channels.generic.websocket import AsyncWebsocketConsumer
from django.conf import settings
from django.core.cache import cache

class MultiPlayer(AsyncWebsocketConsumer):
    ...

    async def message(self, data):
        await self.channel_layer.group_send(
            self.room_name,
            {
                'type': 'group_send_event',
                'event': 'message',
                'uuid': data['uuid'],
                'username': data['username'],
                'text': data['text'],
            }
        )


    async def receive(self, text_data):
        data = json.loads(text_data)
        print(data)

        event = data['event']
        if event == 'create_player':  # 做一个路由
            await self.create_player(data)
        elif event == 'move_to':  # move_to的路由
            await self.move_to(data)
        elif event == 'shoot_fireball':  # shoot_fireball的路由
            await self.shoot_fireball(data)
        elif event == 'attack':  # attack的路由
            await self.attack(data)
        elif event == 'blink':  # blink的路由
            await self.blink(data)
        elif event == 'message':  # message的路由
            await self.message(data)

最后在前端的 ChatField 类中调用一下发送消息的函数即可:文章来源地址https://www.toymoban.com/news/detail-728870.html

class ChatField {
    ...

    add_listening_events() {
        let outer = this;

        this.$input.keydown(function(e) {  // 输入框也需要监听ESC事件
            if (e.which === 27) {
                outer.hide_input();
                return false;
            } else if (e.which === 13) {  // 按Enter键时发送消息
                let username = outer.playground.root.settings.username;
                let text = outer.$input.val();
                outer.hide_input();  // 发送完消息后关闭输入框
                if (text) {  // 信息不为空才渲染出来
                    outer.$input.val('');  // 将输入框清空
                    outer.add_message(username, text);

                    outer.playground.mps.send_message(username, text);  // 给其他玩家的窗口发送消息
                }
                return false;
            }
        });
    }

    ...
}

到了这里,关于Django学习笔记-实现聊天系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python学习笔记_进阶篇(三)_django知识(二)

    本章内容 Django model django默认支持sqlite,mysql, oracle,postgresql数据库。 1 sqlite django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 引擎名称:django.db.backends.sqlite3 2mysql 引擎名称:django.db.backends.mysql 1、配置文件中sqlite 2、配置文件中mysql 注:由于Django内部连接MySQL时使用的

    2024年02月12日
    浏览(29)
  • Python学习笔记_进阶篇(四)_django知识(三)

    本章内容: Django 发送邮件 Django cookie Django session Django CSRF 我们常常会用到一些发送邮件的功能,比如有人提交了应聘的表单,可以向HR的邮箱发邮件,这样,HR不看网站就可以知道有人在网站上提交了应聘信息。今天我们尝试用django发送邮件做尝试 1、配置相关参数settings 往

    2024年02月11日
    浏览(30)
  • Python学习笔记_进阶篇(二)_django知识(一)

    本章简介: Django 简介 Django 基本配置 Django url Django view Django 模板语言 Django Form Django是一个开放源代码的Web应用框架,由Python写成。采用了MVC的软件设计模式,即模型M,视图V和控制器C。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的。并于

    2024年02月12日
    浏览(35)
  • Python学习笔记_实战篇(二)_django多条件筛选搜索

    多条件搜索在很多网站上都有用到,比如京东,淘宝,51cto,等等好多购物教育网站上都有,当然网上也有很多开源的比楼主写的好的多了去了,仅供参考,哈哈 先来一张效果图吧,不然幻想不出来是什么样的,前端样式很low,毕竟主要是说后台的嘛,前端为了简单测试就简

    2024年02月12日
    浏览(32)
  • Python学习笔记-Django框架基础,APP,数据模型,后台管理,路由

    Django框架是Python的常用web框架,遵循 MVC 设计模式的框架,采用了MTV的框架模式,即模型M,视图V和模版T。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是CMS(内容管理系统)软件。并于2005年7月在BSD许可证下发布。这套框架是以比利时的

    2024年02月16日
    浏览(27)
  • 基于Python+Django实现的学生成绩管理系统

    作者主页:编程指南针 作者简介:Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容:Java项目、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路  关注作者有好处 文末获取源码   语言环境:Python3.7 数据库:

    2024年02月11日
    浏览(29)
  • 鸟类识别系统python+TensorFlow+Django网页界面+卷积网络算法+深度学习模型

    鸟类识别系统,使用Python作为主要开发语言,基于深度学习TensorFlow框架,搭建卷积神经网络算法。并通过对数据集进行训练,最后得到一个识别精度较高的模型。并基于Django框架,开发网页端操作平台,实现用户上传一张图片识别其名称。 视频+代码:https://www.yuque.com/ziwu/

    2024年02月16日
    浏览(30)
  • 基于Python仓库管理系统的设计与实现django框架

    摘 要 随着信息化时代的到来,系统管理都趋向于智能化、系统化,仓库管理系统也不例外,但目前国内的有些公司仍然都使用人工管理,公司规模越来越大,同时信息量也越来越庞大,人工管理显然已无法应对时代的变化,而仓库管理系统能很好地解决这一问题,轻松应对

    2023年04月08日
    浏览(25)
  • python+django+sql实现简单的数据信息管理系统

    今天分享一个博客适合刚准备学习Pythonweb开发的小伙伴。 系统涉及功能: 1:运用django+mysql实现了数据的增删改查以及搜索。 2:实现了对前端语言和前端bootstrap库的基本运用。 3:实现了自定义分页的功能以及指定页码跳转。 项目涉及技术: Python的django框架,ORM数据*(库)

    2024年02月03日
    浏览(37)
  • 基于python+Django医用耗材网上申领系统设计与实现

    摘 要 医院信息化永远是我们社会发展的一个方向,医院信息化也是在信息化道路上应用最多的一个场景。就现阶段而言,医院信息化目前是我国社会发展的一个重心。简单而言信息化就是为了人们的生活便利所带来的新时代的东西,有了淘宝、京东,我们可以进行网购,通

    2024年02月10日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包