bg
签名 云与原
文章 工具 随笔
default
qq github ms
设置
top
列表 时间线 标签 全部-第1/2页  11篇文章
  • 关于自动备份静态文件

    在做新网站knowledge时,也在熟悉Git中,电脑把更改后的代码push到了Github,我去Ubuntu删除了代码然后git clone试了试,没有问题。可是,可是后面突然意识到:网站上的所有后台静态图片和mp3都被同时删了!。当时我的表情是,只想抽自己一耳光。

    想了想

    虽说问题出在我的粗心上,但我还是想着,有没有一个方法可以自动备份文件到比较安全的地方呢?这时我发现,不是有Git这个版本管理的好东西吗,Gitee是国内的免费Git远程管理工具,于是就有了方案。

    写代码

    1. 创建一个远程仓库knowledge_img,clone到本地。

    2. 写一个sh文件,自动执行Git。

    3. # 复制三处静态文件到Git目录
      sudo cp -r /knowledge/static/img/book /usr/python/knowledge/knowledge_img/
      sudo cp -r /knowledge/static/img/film /usr/python/knowledge/knowledge_img/
      sudo cp -r /knowledge/static/mp3 /usr/python/knowledge/knowledge_img/
      # 删除.gitignore
      sudo rm -f /knowledge/knowledge_img/book/.gitignore
      sudo rm -f /knowledge/knowledge_img/mp3/.gitignore
      sudo rm -f /knowledge/knowledge_img/film/.gitignore
      # 执行Git操作
      cd /usr/python/knowledge/knowledge_img
      git rm -r --cached .
      git add .
      git commit -m update
      git push -f
      cd /usr/python/knowledge
      # 移除临时文件夹
      sudo rm -rf /knowledge/knowledge_img/book
      sudo rm -rf /knowledge/knowledge_img/film 
      sudo rm -rf /knowledge/knowledge_img/mp3
      
    4. 在服务端代码中修改静态文件的地方之后,加上一个push_git()
    5. def push_git():
          def do_it():
              system('bash /knowledge/knowledge_img/push.sh')
          Thread(target=do_it).start()
      

    重启服务器,这时候,如果修改静态文件,python执行shell代码,git复制静态文件夹到自己的目录,然后push到远程。此时若查看gitee上的仓库,就会发现多了一条commit记录

    解决完毕

    cover
    2020-07-21 42 0
  • popup.vue消息提示组件

    分享一个我使用vue单文件组件写的一个弹出提示消息的组件

    效果

    Edge下的测试

    代码

    popup.vue

    <template>
        <div>
            <div :class="['ani', state]"
                @mouseenter="toggle_pause(true)"
                @mouseleave="toggle_pause(false)"
            >
                <div class="bg"></div>
                <div class="show">
                    <span></span>
                    <a>{{msg}}</a>
                </div>
                <span @click="close"></span>
                <div class="process"></div>
            </div>
        </div>
    </template>
    
    <script>
    import $ from 'jQuery';
    window.$ = $;
    window.jQuery = $;
    import '../../../static/js_lib/velocity.min';
    
    export default {
        data (){
            return {
                state: 'suc',
                msg: '无消息',
                // 弹出/隐藏 延迟和动画
                pop_delay: 300,
                pop_easing: 'ease-out',
                // 展示时间
                duration: 3000,
            }
        },
        methods: {
            show (data){
                let vue_ = this;
                // 更改信息
                this.msg = data.msg;
                this.state = data.state;
                let poper = this.$el.querySelector('.ani'),
                    timer = this.$el.querySelector('.process');
                // 重置popup和timing状态
                $(poper).velocity('stop', true);
                $(timer).velocity('stop', true);
                $(poper).velocity({left: '100%'}, 0, ()=>{
                    $(timer).velocity({left: '0'}, 0, ()=>{
                       // 开始弹出动画
                        $(poper).velocity({left: '0'}, vue_.pop_delay, vue_.pop_easing,
                            ()=>{
                                // 开始计时动画
                                $(timer).velocity({left: '-100%'}, vue_.duration, 'linear',
                                    ()=>{
                                        // 开始收回动画
                                        $(poper).velocity({left: '100%'}, vue_.pop_delay, vue_.pop_easing,
                                            ()=>{
                                                // 计时器归零
                                                timer.style.left = '0';
                                            }
                                        )
                                    }
                                )
                            }
                        )
                    });
                });
            },
            close (){
                let vue_ = this,
                    poper = this.$el.querySelector('.ani'),
                    timer = this.$el.querySelector('.process');
                // 停止动画
                $(poper).velocity('stop', true);
                $(timer).velocity('stop', true);
                // 开始隐藏动画
                timer.style.left = '0';
                $(poper).velocity({left: '100%'}, vue_.pop_delay, vue_.pop_easing, ()=>{
                    timer.style.left = '0'
                })
            },
            toggle_pause (b){
                let timer = this.$el.querySelector('.process');
                if (b){
                    $(timer).velocity('pause');
                }else{
                    $(timer).velocity('resume');
                }
            }
        }
    }
    </script>
    
    <style lang="scss">
        #popup{
            max-width: 50%;
            position: fixed;
            right: 0;
            top: 0;
            >.ani{
                border-radius: 0.4rem 0.4rem 0 0;
                box-shadow: 0 0 0.6rem rgba(0, 0, 0, 0.4);
                padding: 1rem 2rem;
                margin: 1rem;
                overflow: hidden;
                position: relative;
                left: 100%;
                >.bg{
                    width: 100%;
                    height: 100%;
                    border-radius: inherit;
                    z-index: 1;
                    position: absolute;
                    left: 0;
                    top: 0;
                }
                >.show{
                    z-index: 2;
                    border-radius: inherit;
                    >span{
                        &:before{
                            font-family: var(--iconfont);
                            font-size: 1.4rem;
                            margin-right: 1rem;
                        }
                    }
                    >a{
                        font-size: 0.8rem;
                        line-height: 1.2rem;
                    }
                }
                @each $cls,$col,$f in (warn, #ffe99a, #ffb100), (suc, #b5ffca, #5478ff), (err, #ffc4ba, #ff0012){
                    &.#{$cls}{
                        >.bg{
                            background: $col;
                        }
                        >.show >span:before{
                            color: $f;
                            content: var(--i-#{$cls});
                        }
                    }
                }
                >span{
                    position: absolute;
                    top: 0.3rem;
                    right: 0.3rem;
                    line-height: 0.7rem;
                    cursor: pointer;
                    color: #666;
                    z-index: 2;
                    &:before{
                        font-family: var(--iconfont);
                        content: var(--i-close);
                        font-size: 0.6rem;
                    }
                    &:hover{
                        color: red;
                        font-weight: bold;
                    }
                }
                >.process{
                    width: 100%;
                    height: 0.2rem;
                    background: #c300ff;
                    position: absolute;
                    bottom: 0;
                    left: 0;
                    z-index: 2;
                }
            }
        }
    </style>
    

    util.js

        import popup from '../../components/popup/popup.vue';
        window.header_vue = new Vue({
            el: '#popup',
            data: {
            },
            components: {popup},
            methods: {
                pop_data (data) {
                    this.$refs.popup.show(data)
                }
            }
        });
    

    在使用的地方只需要这样写:

    ... ...
    header_vue.pop_data({state: 'suc', msg: '成功'})
    ... ...
    

    velocityjs

    此组件的动画基于velocityjs,它是一个兼容性好,性能强和可自定义的前端动画框架,有兴趣可以了解一下。

    cover
    2020-06-25 101 0
  • vue单文件组件无法获取$refs的问题

    记录一下学习webpack+vue碰到的一个大坑,踩这个坑是我才疏学浅的表现,特此引以为戒。因为该坑实在是太坑了!

    代码

    header.html

    <body>
        <div id="popup-wrap">
            <popup ref="popup"></popup>
        </div>
    </body>
    

    header.js

    import popup from '../../components/popup/popup.vue'
    import './header.scss'
    
    let header_vue;
    $(function () {
        header_vue = new Vue({
            el: '#popup-wrap',
            data: {
            },
            mounted: {
                // 输出为{popup: VueComponent}
                console.log(this.$refs);
            }
            components: {popup},
            methods: {
                pop_data: function () {
                    // 输出为{}
                    console.log(this.$refs);
                }
            }
        });
    });
    export {header_vue}
    
    

    other.js

    import {header_vue} from "../header/header";
    
    $(function () {
        header_vue.pop_data()
    });
    

    popup.vue是一个普通的弹窗组件。我在header.js中引入该组件,并实例化一个header_vue使用了popup组件,然后在other.js中引入header_vue试图使用pop_data函数,该函数仅输出header_vue$refs,经测试,该函数输出为一个空的对象,但是mounted钩子正常输出。

    我就很纳闷,为啥mounted输出正常,函数调用就成空的了呢,Vue也已经挂载完成了啊。

    resolve

    一番气急败坏的debug后,在header.js$(function())加上了一句console.log('ok'),结果浏览器输出了俩ok。短时间大脑的高速运转后,我发现了问题的所在:

    webpack打包了两遍header.js

    所以解决的办法就是把header_vue = new Vue()改成window.header_vue = new Vue()。别处直接用就行了。

    尾话

    目前没搞清楚具体的bug出现原因,正常使用webpack多次引入同一个export也没有出现过此问题。但是肯定是我没学明白,有大牛知道的话麻烦解答解答。

    cover
    2020-06-23 61 0
  • vue五星评分插件

    最近做毕设,一个电商网站,类似某宝某东。在学一些新的东西,SassVue(对我来说是新的)。


    五星评分插件

    做电商网站要用到评分功能,于是自己写了一个小插件vue_star.js

    测试html:

    <!DOCTYPE html>
    <html>
    <head>
        <script src="vue.min.js"></script>
        <script src="vue_star.js"></script>
        <style>
            @font-face {
                font-family: 'icon-font';
                src: url('iconfont.eot');
                src: url('iconfont.eot?#iefix') format('embedded-opentype'),
                url('iconfont.woff2') format('woff2'),
                url('iconfont.woff') format('woff'),
                url('iconfont.ttf') format('truetype'),
                url('iconfont.svg##iconfont') format('svg');
            }
        </style>
        <link href="vue_star.css" rel="stylesheet">
    </head>
    <body>
        <div id="container">
            固定:
            <star :star="3.8" :modify="'f'" :f_size="20"></star>
            手动:
            <star :star="0" :modify="'t'" :f_size="30"></star>
        </div>
    </body>
    <script>
        window.onload = function(){
            new Vue({el: '#container'})
        }
    </script>
    </html>
    

    效果图

    vue_star.js:

    Vue.component('star', {
        props: ['star', 'modify', 'f_size'],
        data: function(){
            return {
                percent: this.$props['star']/5,
                can_modify: this.$props['modify']!=='f',
                debounce: false,
            }
        },
        template: "" +
            "<div class='star' :style='{width: f_size*5+\"px\"}'>" +
            "   <span :style='{width: percent*100+\"%\", fontSize: f_size+\"px\"}' @mousemove='check_change'></span>" +
            "   <b>{{ mark }}</b>" +
            "</div>"
        ,
        computed: {
            mark: function () {
                return (this.percent*5).toString().replace(/(\.\d)\d*/, '$1')
            }
        },
        methods: {
            check_change: function(e){
                if(this.can_modify){
                    this.mouse_move(e);
                }
            },
            mouse_move: function (e) {
                this.percent = e.offsetX/this.$el.offsetWidth;
            }
        }
    });
    

    vue_star.scss:

    .star{
      display: flex;
      align-items: center;
      position: relative;
      >span{
        position: relative;
        display: flex;
        &:before,&:after{
          position: absolute;
          top: 0;
          left: 0;
          font-family: 'iconfont';
          content: '\e72d\e72d\e72d\e72d\e72d';
        }
        &:before{
          position: relative;
          color: #8b8b8b;
          overflow: visible;
        }
        &:after{
          width: 100%;
          color: #ffb652;
          overflow: hidden;
          transition: all .1s linear;
        }
      }
      >b{
          position: absolute;
          top: 0;
          left: 100%;
          font-size: 15px;
      }
    }
    

    题外话

    • Sass的官网说自己是世界上最成熟、稳定和强大的CSS扩展语言,名副其实,用Sass至少能省1/3的css编写时间,对笔者来说它最大的好处是可以很简单地精确定位到元素。不用绞尽脑汁想类名,同一个html用很多.head这种简单类名而不用担心css混淆。

    • Vue的出发点很好:只针对前端,数据驱动。改变了我对前端开发的理解。以前无论用jsp还是django和flask,都是数据和html耦合在一起,因为我都是一个人写前后端,也没觉得有什么不方便。学了vue之后,虽然后端还是servlet+jsp,但是jsp基本不处理数据,或处理一些不需要访问Dao层的简单数据(比如静态共用数据和session),其它数据交给ajax,数据渲染交给vue的数据双向绑定和v-if,实在好用。

    cover
    2020-04-05 170 0
  • 处理公共CDN突然失效的情况

    公共CDN的使用

    刚开始开发我的博客时,使用的bootcdn,发现他们被黑过,虽然想骂那些“黑客”,但是我们也没办法去防范,只能从自己的网站上入手解决。

    那时我还没技术解决这个问题,网上搜过,大都只提问题不提解决。。。

    如今尝试一番暂且解决了该问题。(一切建立在源站没被黑的前提下)

    思考方案

    浏览器加载js是顺序执行的,这里不提async和defer,只提onerror。

    考虑以下HTML(chrome测试):

    <head>
        // ------ 方法1 -----
        <script src="https://bootcdn.cn/jquery.min.js" onerror="this.src='mystatic/jquery.min.js'"></script>
        // ------- END -------
    
        // ------ 方法2 -----
        <script>
            function exchange(e, s){
                let new_s = document.createElement('script');
                new_s.src = s;
                document.head.insertBefore(new_s, e);
                e.remove();
            }
        </script>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js" onerror="exchange(this, 'mystatic/jquery.min.js')"></script>
        // ------- END -------
    </head>
    <body>
        <div id="main">something</div>
        <script>
            window.onload = ()=>{
                $('#main').text('other thing');
            }
        </script>
    </body>
    
    • 方法一:萌新错误。浏览器仅请求bootcdn,失败后修改src,但js文件和img不同,浏览器不会下载mystatic/jquery.min.js而是继续向下执行,到$处报错。
    • 方法二:浏览器请求bootcdn,失败后执行exchange函数创建一个新的<script>并替换bootcdn。观察到浏览器下载了mystatic/jquery.min.js,并且正确执行了onload函数,测试成功。

    那么为什么不用第二种方法呢? ---- 下面说处理无刷新加载

    改进优化

    1. onload问题
      如果从无刷新加载下进入某个页面page.html,而且有一个page.js文件需要监听window.onload以执行操作。显然无刷新加载时,onload之前就已经触发了,JQuery的$(document).ready()可以解决此问题,这里用原生js解决。
    2. function domready(callback) {
          if (document.readyState === 'complete') {
              callback();
          } else {
              let c = function () {
                  callback();
                  window.removeEventListener('load', c);
              };
              window.addEventListener('load', c);
          }
      }
      

      加载js时检查readyState,如果DOM已加载完毕。那么不必监听onload,直接执行。反之正常监听。

    3. js依赖问题
      如果page.js的初始化需要使用highlightjs,仅仅先添加highlightjs后添加page.js到<head>也无法保证page.js会在highlightjs执行后执行,解决方法详见无刷新加载
    cover
    2020-03-09 128 0
  • JS小插件

    虽然目前博客访问量小于3,作为一个认真的博主,我是不会放弃(水文章)的!

    1. 图片懒加载(度娘解释)
    2. 图片查看插件
    3. 文字头像生成

    懒加载

    网上解释居多,在此只提实现

    // 防抖
    let lazy_timer;
    window.addEventListener('scroll', function () {
        if (lazy_timer !== null){
            clearTimeout(lazy_timer)
        }
        lazy_timer = setTimeout(function () {
            load_lazy();
        }, 100)
    });
    load_lazy();
    // 懒加载
    function load_lazy() {
        $('.lazy-img:not([data-l])').each(function () {
            let y = this.getBoundingClientRect().top;
            if ((y+this.offsetHeight >= 0 && y <= document.body.clientHeight) || this.getAttribute('data-main') === 't') {
                this.src = this.getAttribute('data-s');
                this.setAttribute('data-l', '');
            }
        });
    }
    

    以上代码很简洁,主要就是三点

    • 对需要懒加载的图片,src设置loading.gif,data-s设置成原本的src,加载后设置data-l用于分辨是否已加载。
    • 监听scroll事件,如果图片进入了视野便执行替换src,此处注意防抖。
    • 对需要懒加载的图片,最好加上onerror事件。当加载失败时,图片的占有空间(浏览器一般会放一个破碎的图片)和实际大小不符,导致下面的图片已经到了视野中,却没有被加载,就是这样:

    查看器

    自己写的一个小插件,用于提升图片查看体验,具体提升了没有我是不知道的。先上代码

    • 一开始肯定把html界面写好啦
    <div id="zoom-div">
        <div id="zoom-wrap">
            <div id="zoom-head">
                <span title="保存" onclick="省略()"></span>
                <label>
                    <b>缩放:</b>
                    <input type="range" min="50" max="200" value="100" />
                    <a>100%</a>
                </label>
                <span title="关闭" onclick="$('#zoom-div').fadeOut(100)"></span>
            </div>
            <div id="zoom-body">
                <div id="zoom-show"></div>
            </div>
            <div id="zoom-list"><div></div></div>
        </div>
    </div>
    <script>
        // input缩放
        document.querySelector('#zoom-head input').addEventListener('input', zoom_scale_change);
        // 鼠标滚轮缩放
        document.querySelector('#zoom-show').addEventListener('mousewheel', function (e) {
            let input = document.querySelector('#zoom-head input');
            if (document.querySelector('#zoom-show img') !== null) input.value = parseInt(input.value)+((e.deltaY>0)?-10:10);
            zoom_scale_change();
        });
        // 缩放函数
        function zoom_scale_change(){
            let input = document.querySelector('#zoom-head input');
            input.nextElementSibling.innerText = input.value + '%';
            let img = document.querySelector('#zoom-show img');
            if (img !== null) img.style.transform = 'scale('+input.value/100+')';
        }
    </script>
    
    • 然后就是使用了
    // 给需要放大的图片.zoom-img绑定事件
    function init_zoom() {
        $('.zoom-img').each(function () {
            let img = this;
            // 上文提到无刷新加载,所以要防止重复绑定
            if (img.getAttribute('onclick') === null) img.setAttribute('onclick', 'zoom_this(this)')
        })
    }
    // 点击图片弹出界面
    function zoom_this(img) {
         $('#zoom-div').fadeIn(100, 'linear');
        set_zoom_img(img);
        // 添加底部缩略图
        let all_div = $('#zoom-list');
        all_div.empty();
        $('.zoom-img').each(function () {
            let im = new Image();
            // 当前图片是.active
            if (this === img) span.className = 'active';
            im.src = this.src;
            // lazy-img的图片地址是data-s
            if (this.className.search('lazy-img') !== -1) im.src = this.getAttribute('data-s');
            im.setAttribute('onclick', 'set_zoom_img(this);document.querySelectorAll("#zoom-list span").forEach(function(i){i.className=""});this.parentElement.className="active"');
            all_div[0].append(im);
        })
    }
    // 重点,初始化查看器
    function set_zoom_img(img) {
        let im = new Image();
        im.src = img.src;
        // 清除并添加
        let show_div = document.querySelector('#zoom-show');
        $(show_div).empty();
        show_div.append(im);
        // 拖动
        let pos = [0, 0];
        let old_pos = [0, 0];
        let last_pos = [0, 0];
        let grabed = false;
        im.ondragstart = function (e) {e.preventDefault()};
        show_div.onmousedown = show_div.ontouchstart = function (e) {
            e.stopPropagation();
            e.preventDefault();
            pos = [e.pageX, e.pageY];
            if (typeof e.pageX === "undefined"){
                pos = [e.touches[0].clientX, e.touches[0].clientY];
            }
            grabed =true;
            document.onmouseup = document.ontouchend = function () {
                grabed = false;
                old_pos = [last_pos[0], last_pos[1]];
            };
            document.onmousemove = document.ontouchmove = function (e) {
                if (!grabed) return;
                let n_pos = [e.pageX, e.pageY];
                if (typeof e.pageX === "undefined"){
                    n_pos = [e.touches[0].clientX, e.touches[0].clientY];
                }
                last_pos = [old_pos[0] + n_pos[0] - pos[0], old_pos[1] + n_pos[1] - pos[1]];
                im.style.left = last_pos[0] + 'px';
                im.style.top = last_pos[1] + 'px';
            };
        };
    }
    

    效果大概如下:

    文字头像生成

    顾名思义,生成只是文字的头像,此功能用于加载失败的用户头像

    function gen_text_img(size, s) {
        let colors = [
            "rgb(239,150,26)", 'rgb(255,58,201)', "rgb(111,75,255)", "rgb(36,174,34)", "rgb(80,80,80)"
        ];
        let cvs = document.createElement("canvas");
        cvs.setAttribute('width', size[0]);
        cvs.setAttribute('height', size[1]);
        let ctx = cvs.getContext("2d");
        ctx.fillStyle = colors[Math.floor(Math.random()*(colors.length))];
        ctx.fillRect(0, 0, size[0], size[1]);
        ctx.fillStyle = 'rgb(255,255,255)';
        ctx.font = size[0]*0.6+"px Arial";
        ctx.textBaseline = "middle";
        ctx.textAlign = "center";
        ctx.fillText(s,size[0]/2,size[1]/2);
    
        return  cvs.toDataURL('image/jpeg', 1);
    }
    

    效果如下,某些浏览器文字不能水平居中,暂时没有找到解决办法:

    cover
    2020-03-06 254 0
  • 无刷新加载

    了解


    以前我是压根不知无刷新加载这技术的,才疏学浅啊。

    偶然逛到某大佬的博客,发觉跳转界面很不错,细探才了解到pjax。即pushState+Ajax,顾名思义:ajax请求,pushState展示,原理不必多言。

    学习


    作为技术控,看到新东西当然是动手去折腾一下啦。下载jquery.pjax文件,代码写起来!

    接口貌似很简单。如下

    劈里啪啦几十行代码敲出了demo,确实可以使用,点击绑定的<a>标签后,pjax会先ajax请求html,再用html替换掉container的内容。

    但是,不自己写一个pjax怎么能叫搞懂了呢。

    开发


    随即写了一个博客定制的ajax请求并替换的函数,位置在share.js,代码如下:

    function ajax_url(u, callback) {
        show_loading();
        $.ajax({
            success: function (r) {
                let html = document.createElement('html');
                html.innerHTML = r;
                // title
                document.title = html.querySelector('head title').innerText;
                // keyword
                document.querySelector('head meta[name="description"]').content = html.querySelector('head meta[name="description"]').content;
                // bg
                let b = $('#body-bg-img');
                b.attr('src', b.attr('src').replace(/\d(\.jpg)$/, html.querySelector('bg').innerText+'$1'));
                // content
                $('#show-wrap').html(html.querySelector('#show-wrap').innerHTML);
                // css
                document.querySelectorAll('head link[rel="stylesheet"]').forEach(function (e) {
                    if (ignore_head.indexOf(e.href.replace(/.*\/(.*?\.css)/, '$1')) === -1){
                        e.remove();
                    }
                });
                html.querySelectorAll('head link[rel="stylesheet"]').forEach(function (e) {
                    let s = document.createElement('link');
                    s.rel = 'stylesheet';
                    s.href = e.href;
                    s.onload = function(){
                    };
                    document.head.append(s);
                });
                // script
                document.querySelectorAll('head script').forEach(function (e) {
                    if (ignore_head.indexOf(e.src.replace(/.*\/(.*?\.js)/, '$1')) === -1){
                        e.remove();
                    }
                });
                // js同步加载防止错位
                download_js();
                function download_js(){
                    let e = html.querySelector('head script');
                    if (typeof e !== 'undefined' && e !== null) {
                        let s = document.createElement('script');
                        s.src = e.src;
                        e.remove();
                        document.head.append(s);
                        if (e.getAttribute('onerror') !== null){
                            s.onerror = function () {
                                exchange(s, e.getAttribute('onerror').replace(/^.*?(\/.*\.js).*/, '$1'), download_js);
                            }
                        }
                        s.onload = download_js;
                    }else{
                        if (typeof callback !== 'undefined') callback();
                    }
                }
            },
            beforeSend: function (xhr) {
                xhr.setRequestHeader('x-single', 't');
            },
            url: u, type: 'GET', timeoutSeconds: 10
        });
    }
    

    服务器收到x-single头就不返回累赘的东西。
    其中有段递归回调代码是专门让js下载同步进行,防止引用错位。副作用是让网页加载稍微变慢了亿点,暂时没更好的解决办法。

    完毕

    cover
    2020-03-05 220 0
  • 写的俩小软件

    最近用pyqt5写的两个小软件,好久没用python了。

    二维码扫描器

    可以扫描笔记本摄像头,支持单图多个二维码。

    powered by pyzbar, cv2

    效果图:

    Github地址: qrreader

    md-to-html

    基础markdown实时编辑展示html代码和结果。可以自定义样式,我写这个博客的文章就是用的它。

    powered by python-markdown

    效果图:

    Github地址: md-to-html

    快毕业了,还感觉前途迷茫,不知所向

    cover
    2020-02-23 214 0
1 2