【转】使用Python轻松批量压缩图片

转载自:使用Python轻松批量压缩图片


在互联网,图片的大小对一个网站的响应速度有着明显的影响,因此在提供用户预览的时候,图片往往是使用压缩后的。如果一个网站图片较多,一张张压缩显然很浪费时间。那么接下来,我就跟大家分享一个批量压缩图片的方法,只需几行python代码,即可轻松实现图片压缩。

压缩算法

用到PIL库,PIL是Python平台事实上的图像处理标准库,支持多种格式,并提供强大的图形与图像处理功能。使用如下命令安装:

pip install pillow

代码如下:

from PIL import Image
import os,shutil

#图片压缩批处理
def compressImage(srcPath,dstPath):
    for filename in os.listdir(srcPath):
        #如果不存在目的目录则创建一个,保持层级结构
        if not os.path.exists(dstPath):
                os.makedirs(dstPath)
 
        #拼接完整的文件或文件夹路径
        srcFile=os.path.join(srcPath,filename)
        dstFile=os.path.join(dstPath,filename)
 
        # 如果是文件就处理
        if os.path.isfile(srcFile):
            try:
                #打开原图片缩小后保存,可以用if srcFile.endswith(".jpg")或者split,splitext等函数等针对特定文件压缩
                sImg=Image.open(srcFile)
                w,h=sImg.size
                dImg=sImg.resize((int(w/2),int(h/2)),Image.ANTIALIAS)  #设置压缩尺寸和选项,注意尺寸要用括号
                dImg.save(dstFile) #也可以用srcFile原路径保存,或者更改后缀保存,save这个函数后面可以加压缩编码选项JPEG之类的
                print (dstFile+" 成功!")
            except Exception:
                print(dstFile+"失败!")
 
        # 如果是文件夹就递归
        if os.path.isdir(srcFile):
            compressImage(srcFile, dstFile)
# 遍历压缩图片
compressImage("./finish","./compress")

【转】Python批量重命名

转载自:python批量重命名


有这样一个需求,自己刚从b站下载的视频,但是文件名字看起来不好,想批量修改,不想一个一个的改,太麻烦

before:

after:

代码:

import os
path = 'G:/go'
num= 1
for file in os.listdir(path):
    before = os.path.join(path,file)
    after = os.path.join(path,str(num)+".flv")
    os.rename(before,after)
    num+=1

获取Android设备上的USB设备

import android.content.Context;
import android.os.storage.StorageManager;

import java.lang.reflect.Method;

public class USBUtil {

    public static String getUSBPath(Context context) {
        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        try {
            // 通过反射调用getVolumeList,获取所有挂载的设备(内部sd卡、外部sd卡、挂载的U盘)
            Method getVolumeListMethod = StorageManager.class.getMethod("getVolumeList");
            Object[] volumes = (Object[]) getVolumeListMethod.invoke(storageManager);

            Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
            // 通过反射调用getPath、isRemovable
            Method getPathMethod = storageVolumeClazz.getMethod("getPath");
            Method isRemovableMethod = storageVolumeClazz.getMethod("isRemovable");
            if (volumes != null && volumes.length > 0) {
                for (Object volume : volumes) {
                    String path = (String) getPathMethod.invoke(volume); // 获取路径
                    boolean isRemovable = (boolean) isRemovableMethod.invoke(volume);// 是否可移除
                    if (isRemovable) {
                        return path;
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return "/";
    }
}

参考链接:
获取Android设备上的所有存储设备

Android Fragment 重叠问题

缘起

首先,笔者的主Activity中包含了多个Fragment,通过show、hide来切换展示。当笔者切换到其他的APP,操作一段时间后,返回到当前APP,有很大几率会出现Fragment重叠现象。

缘由

首先,在Activity的生命周期中,当Activity不在前台展示时,如果此时其他的APP需要使用内存,系统会杀掉该APP的进程,当用户重新进入该APP时,系统会重新创建Activity。

Activity不在前台展示时会保存Fragment的状态,当系统重新创建Activity时会恢复之前保存的Fragment的状态。

缘解

方案一:在Activity的onCreate方法中判断savedInstanceState是否为空,不为空则使用系统保存的Fragment。

public class MainActivity extends AppCompatActivity {
    Fragment fragment;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            fragment = getSupportFragmentManager().findFragmentByTag("fragment");
        } else {
            fragment = new Fragment();
            getSupportFragmentManager().beginTransaction().add(R.id.frameLayout, fragment, "fragment").commit();
        }
    }
}

方案二:在Activity的onCreate方法中,调用super方法时直接传null,不使用保存的状态。

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(null);
}

参考资料:

Android fragment 重叠问题——通过hide,show方式导致的解决方法

解决Activity重新刷新后Fragment的show、hide失效问题

深入理解Activity的生命周期

ADV/AVG 游戏引擎介绍

  • 吉里吉里: C++,老牌 AVG 游戏引擎,著名的 Fate/stay night 便是用其制作。但是上一个稳定版本已经是十年前,很久没有更新了。
  • Ren’Py: Python,代表作「心跳文学部」(我老婆的 Twitter),开源万岁,并且直到现在更新也很活跃,如果喜欢 Python,是个不错的选择。不过需要预编译,剧本与程序未分离。(定位于浏览器端的话,JS 更有优势。)
  • NScripter: C++,非商业免费,Windows 平台,代表作「寒蝉鸣泣之时」,上一个稳定版本发布于 2015 年。
  • AVG32、RealLive、SiglusEngineVisual Art’s 公司开发,Key 社游戏「CLANNAD」等均用此开发,但很明显这种商业级咱接触不到。
  • BKEngine: C++,面包工坊,非商业免费、跨平台,但是制作工具不跨平台(只有 Windows)。
  • AVG.js:JavaScript,开源,基于 Pixi.js 与 React,Web 端运行。但是作者 Icemic 是个大 🐦,所以已经几年没有更新了。(不过作者也在 BKEngine 的面包工坊。)
  • Librian: Python,开源,跨平台,Galgame | Visual Novel 引擎,作者 还有在做 Vtuber,可惜是个变态。
  • 橙光制作工具:免费易操作,只有 Windows 平台,但是因为 如何看待橙光游戏签约合同中版权永久属于橙光,而作者仅保留署名权?,好感直线下降。
  • Nova:基于Unity、对程序员友好的视觉小说框架,可以做出丰富的演出效果,并且免费开源。

参考链接:

ADV 游戏引擎计划

我们自主研发的文字AVG框架Nova(能叶)现已免费开源发布

【转】解决Mosquitto创建MQTT服务器提示Starting in local only mode

转载自:解决Mosquitto创建MQTT服务器提示Starting in local only mode


一、问题现象

使用新下载的Mosquitto创建MQTT服务器提示Starting in local only mode. Connections will only be possible from clients running on this machine.测试发现服务器只能接入本机的客户端,而其他设备上的客户端在连接时直接被重置了。

二、原因分析

随后查阅了一下Mosquitto的更新记录,原来在其2.0.0大版本更新后如果不加载配件文件则使用回环接口(仅可用于本地Socket通信)。

Breaking changes
· When the Mosquitto broker is run without configuring any listeners it will now bind to the loopback interfaces 127.0.0.1 and/or ::1. This means that only connections from the local host will be possible.
· Running the broker as mosquitto or mosquitto -p 1883 will bind to the loopback interface.
· Running the broker with a configuration file with no listeners configured will bind to the loopback interface with port 1883.
· Running the broker with a listener defined will bind by default to 0.0.0.0 / :: and so will be accessible from any interface. It is still possible to bind to a specific address/interface.

附:Mosquitto更新记录

三、解决方法

先修改软件安装目录中的配置文件mosquitto.conf然后加载配置文件来启动服务器。

  • 配置端口号,编辑mosquitto.conf,搜索listener去掉行首的#并加上端口号,示例端口为1883

# On systems that support Unix Domain Sockets, it is also possible
# to create a # Unix socket rather than opening a TCP socket. In
# this case, the port number should be set to 0 and a unix socket
# path must be provided, e.g.
# listener 0 /tmp/mosquitto.sock
#
# listener port-number [ip address/host name/unix socket path]
listener 1883

  • 启用匿名访问,若不允许匿名访问则只有添加客户端鉴权信息才能接入。为简化测试我们启用匿名访问:将mosquitto.conf里的allow_anonymous选项改为true并保存

# Boolean value that determines whether clients that connect
# without providing a username are allowed to connect. If set to
# false then a password file should be created (see the
# password_file option) to control authenticated client access.
#
# Defaults to false, unless there are no listeners defined in the configuration
# file, in which case it is set to true, but connections are only allowed from
# the local machine.
allow_anonymous true

  • 加载配置文件启动服务器测试,执行命令mosquitto.exe -c mosquitto.conf -v,此时LOG上已经没了Starting in local only mode且非本地客户端可以正常接入了

TouchDelegate相关文章

Android鲜为人知的TouchDelegate

Android使用TouchDelegate增加View的触摸范围

ListView Tips & Tricks #5: Enlarged Touchable Areas

TouchDelegateGroup

import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.TouchDelegate;
import android.view.View;

import androidx.annotation.NonNull;

import java.util.ArrayList;

public class TouchDelegateGroup extends TouchDelegate {
    private ArrayList<TouchDelegate> mTouchDelegates = new ArrayList<>();
    private TouchDelegate mCurrentTouchDelegate;

    public TouchDelegateGroup(@NonNull Rect bounds, @NonNull View delegateView) {
        super(bounds, delegateView);
        mTouchDelegates.add(this);
    }

    public void addTouchDelegate(@NonNull TouchDelegate touchDelegate) {
        mTouchDelegates.add(touchDelegate);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        TouchDelegate delegate = null;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                for (TouchDelegate touchDelegate : mTouchDelegates) {
                    if (onDelegateTouchEvent(touchDelegate, event)) {
                        mCurrentTouchDelegate = touchDelegate;
                        return true;
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                delegate = mCurrentTouchDelegate;
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                delegate = mCurrentTouchDelegate;
                mCurrentTouchDelegate = null;
                break;
        }
        if (delegate != null) {
            return onDelegateTouchEvent(delegate, event);
        }
        return false;
    }

    public boolean onSelfTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }

    public boolean onDelegateTouchEvent(TouchDelegate touchDelegate, MotionEvent event) {
        if (touchDelegate instanceof TouchDelegateGroup) {
            return ((TouchDelegateGroup) touchDelegate).onSelfTouchEvent(event);
        } else {
            return touchDelegate.onTouchEvent(event);
        }
    }
}