分类目录归档:Uncategorized

Linux Shell清除过期文件

find $log_path -cmin +1 -type d | xargs rm -rf {} # 1分钟前创建的
find $log_path -mmin +1 -type d | xargs rm -rf {} # 1分钟前修改的
find $log_path -ctime +1 -type d | xargs rm -rf {} # 1天前创建的
find $log_path -mtime +1 -type d | xargs rm -rf {} # 1天前修改的
find $log_path/* -mtime +1 -type d | xargs rm -rf {} # 30天钟前修改的,不包括当前目录

捕获Qt Crash全图教程

关键点

  • 在main.cpp引入SetUnhandledExceptionFilter程序奔溃时生成dmp文件(代码)
  • Build release时包含debug信息(图)
  • Link dbghelp.lib库(图)
  • Build post自动复制qt依赖库(图)

Qt捕获Crash示例代码下载

Qt捕获Crash关键代码

main.cpp

#include <QtWidgets/QApplication>
#include <QMessageBox>
#include <QDateTime>
#include <QDir>
#include <QDebug>
#include "QtCrashApplication.h"
#include "main.h"

#ifdef Q_OS_WIN
#include <Windows.h>
#include <DbgHelp.h>
#endif

#ifdef Q_OS_WIN
static LONG WINAPI exceptionCallback(struct _EXCEPTION_POINTERS* exceptionInfo)
{
    QCoreApplication* app = QApplication::instance();

    QString savePath = app->applicationDirPath() + "dump/";
    qDebug() << "save path :" << savePath;
    QDir dir(savePath);
    if (!dir.exists() && !dir.mkpath(savePath)) {
        app->exit(E_UNEXPECTED);
        return EXCEPTION_EXECUTE_HANDLER;
    }

    savePath.append("assit_");
    savePath.append(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
    savePath.append(".dmp");

    HANDLE dump = CreateFileW(savePath.toStdWString().c_str(), GENERIC_WRITE,
        0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == dump) {
        app->exit(E_UNEXPECTED);
        return EXCEPTION_EXECUTE_HANDLER;
    }

    MINIDUMP_EXCEPTION_INFORMATION miniDumpExceptionInfo;
    miniDumpExceptionInfo.ExceptionPointers = exceptionInfo;
    miniDumpExceptionInfo.ThreadId = GetCurrentThreadId();
    miniDumpExceptionInfo.ClientPointers = TRUE;
    DWORD idProcess = GetCurrentProcessId();
    MiniDumpWriteDump(GetCurrentProcess(), idProcess, dump,
        MiniDumpNormal, &miniDumpExceptionInfo, NULL, NULL);

    CloseHandle(dump);

    app->exit(E_UNEXPECTED);
    return EXCEPTION_EXECUTE_HANDLER;
}
#endif

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);

#ifdef Q_OS_WIN
    SetUnhandledExceptionFilter(exceptionCallback);
#endif

    QtCrashApplication w;
    w.show();
    return a.exec();
}

QtCrashApplication.cpp

#include "QtCrashApplication.h"

QtCrashApplication::QtCrashApplication(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    QObject::connect(ui.CrashButton, SIGNAL(clicked()), this, SLOT(crashMe()));
}

void QtCrashApplication::crashMe() {
    int i = 0;
    int n = 1 / i;
    ui.CrashButton->setText(QString(n));
}

新建项目

新建项目

配置项目

配置项目

项目配置向导

项目配置向导

项目配置向导2

项目配置向导2

编写会奔溃程序

编写会奔溃程序

程序运行截图

程序运行截图

点击运行后出现异常

点击运行后出现异常

自动生成依赖qt库

"$(QTDIR)\bin\windeployqt.exe" "$(OutDir.TrimEnd('\'))" --$(Configuration.toLower())

自动生成依赖qt库

自动生成依赖库

自动生成依赖库

生成release时生成debug信息

生成release时生成debug信息

包含dbghelp.lib库

包含dbghelp.lib库

查看生成记录

查看生成记录

查看结果

查看结果

参考

WPF使用MVVM入门

MVVM 的三个部分

让我们看一下MVVM的三个部分:模型,视图和视图模型。

视图:这些都是UI元素,是应用程序的漂亮面孔。对于WPF,这些都是您的所有XAML文件。它们可能是Windows,用户控件或资源字典。尽管完全可以从代码构造视图当然是可能的,但绝大多数UI将(并且应该)使用XAML构建。该视图可能非常动态,甚至可以处理一些用户交互(请参见下面的“命令”部分)。

视图模型:这些是为每个视图提供数据和功能的对象。通常,视图和视图模型类之间通常存在一对一的映射。视图模型类,将数据公开给视图,并提供处理用户交互的命令。与其他设计模式不同,视图模型不应该知道其视图。关注点的分离是MVVM的主要宗旨之一。视图模型是视图和模型之间的连接。

模型:从广义上讲,模型提供对应用程序所需数据和服务的访问。根据您的应用程序,这是完成实际工作的地方。虽然视图模型与将模型的数据汇总在一起有关,但是模型类执行应用程序的实际工作。如果使用依赖项注入,则通常在视图模型中将模型类作为接口构造函数参数传递。

由于视图模型与模型之间的交互将在很大程度上取决于您的特定应用程序,因此在本文的其余部分中,我们将仅着眼于视图与视图模型之间的交互。

Bidding

绑定引擎使MVVM模式成为可能。绑定在视图中声明,并将视图中的属性链接回视图模型中的属性。

public class ViewModel
{
    public string FirstName { get; set; }
}
<TextBlock Text="{Binding Path=FirstName}" VerticalAlignment="Center" HorizontalAlignment="Center"/>

上面的代码是实现MVVM模式的开始。绑定将Text属性的值设置为FirstName属性中的值。如果要运行此代码,则TextBlock的Text仍为空。这是因为没有将ViewModel类链接到Window的东西。在WPF中,此链接来自DataContext属性。

在Window的构造函数中,我们将设置其DataContext。如果在UI元素上未指定DataContext,它将继承其父级的DataContext。因此,在Window上设置DataContext将有效地为Window中的每个元素设置它。

public MainWindow()
{
    var viewModel = new ViewModel();
    viewModel.FirstName = "Kevin";

    DataContext = viewModel;
    InitializeComponent();

    viewModel.FirstName = "Mark";
}

如果我们运行应用程序,TextBox 仍将显示“ Kevin”,而不是更新后的值“ Mark”。虽然属性的值已经更改,但是没有通知 Binding 更新其值。我们可以通过实现 INotifyPropertyChanged (INPC)接口来解决这个问题。此接口有一个事件,通知绑定,特定属性已更改,使用该事件的任何绑定都应重新计算其值。

我们可以这样实现它:

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string FirstName { get; set; }

    public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

现在,在 Window 的构造函数中,我们通知视图该属性已更改。

public MainWindow()
{
    var viewModel = new ViewModel();
    viewModel.FirstName = "Kevin";

    DataContext = viewModel;
    InitializeComponent();

    viewModel.FirstName = "Mark";
    viewModel.OnPropertyChanged(nameof(ViewModel.FirstName));
}

现在绑定正确地更新以显示“ Mark”。

但是,每次更改属性值时都要记住引发该事件可能会变得非常乏味。因为这种模式非常普遍,许多 MVVM 框架为你的视图模型类提供了一个基类,类似于下面这样:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName]string propertyName = null)
    {
        if(!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }
        return false;
    }
}

这使我们可以像这样重写FirstName属性:

public class ViewModel : ViewModelBase
{
    private string _firstName;
    public string FirstName
    {
        get => _firstName;
        set => SetProperty(ref _firstName, value);
    }
}

Command

绑定是将数据从视图模型移入视图的一种好方法,但是我们还需要允许我们的视图模型响应用户交互。大多数具有默认用户交互功能(例如单击按钮)的用户控件均由命令处理。所有实现ICommandSource接口的用户控件都支持Command属性,当控件的默认操作发生时,该属性将被调用。有许多实现此接口的控件,例如按钮,菜单项,复选框,单选按钮,超链接等。

命令只是实现ICommand接口的对象。或者换一种说法,命令是从视图到视图模型的消息。当控件的默认事件发生时,例如单击按钮时,将调用命令上的Execute方法。更重要的是,命令还可以指示它们何时能够执行。这允许控件根据是否可以执行其命令来启用或禁用自身。

命令示例

从我们非常简单的示例中可以看出,单击按钮时,我们会更改名字的值。

首先,我们需要在视图模型中添加一个command属性:

public class ViewModel : ViewModelBase
{
    public ICommand ChangeNameCommand { get; }
    ...
}

接下来,我们将向MainWindow添加一个按钮,并使用Binding将其Command属性设置为视图模型中的命令。

<Button Content="Change Name" Command="{Binding Path=ChangeNameCommand}" VerticalAlignment="Bottom" HorizontalAlignment="Center" />

现在,我们只需要向视图模型中的ChangeNameCommand属性分配一个新的命令对象即可。不幸的是,WPF没有附带适合在视图模型中使用的默认ICommand实现,但是该接口非常简单,可以实现:


public class DelegateCommand : ICommand
{
    private readonly Action<object> _executeAction;

    public DelegateCommand(Action<object> executeAction)
    {
        _executeAction = executeAction;
    }

    public void Execute(object parameter) => _executeAction(parameter);

    public bool CanExecute(object parameter) => true;

    public event EventHandler CanExecuteChanged;
}

例如,在这个非常简单的实现中,执行命令时将调用Action委托。现在,我们将忽略接口的CanExecute部分,并始终允许执行命令。

现在,我们可以完成视图模型中的代码。


public class ViewModel : ViewModelBase
{
    ...

    private readonly DelegateCommand _changeNameCommand;
    public ICommand ChangeNameCommand => _changeNameCommand;

    public ViewModel()
    {
        _changeNameCommand = new DelegateCommand(OnChangeName);
    }

    private void OnChangeName(object commandParameter)
    {
        FirstName = "Walter";
    }
}

运行我们的简单应用程序,我们可以看到单击按钮确实可以更改名称。

file

接下来,让我们返回并实现ICommand接口的CanExecute部分。


public class DelegateCommand : ICommand
{
    private readonly Action<object> _executeAction;
    private readonly Func<object, bool> _canExecuteAction;

    public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecuteAction)
    {
        _executeAction = executeAction;
        _canExecuteAction = canExecuteAction;
    }

    public void Execute(object parameter) => _executeAction(parameter);

    public bool CanExecute(object parameter) => _canExecuteAction?.Invoke(parameter) ?? true;

    public event EventHandler CanExecuteChanged;

    public void InvokeCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

与execute方法类似,此命令还将接受CanExecute委托。同样,CanExecuteChanged事件也使用公共方法公开,因此我们可以在CanExecute委托的返回值更改时随时提高它。

回到我们的视图模型中,我们将进行以下补充。


public ViewModel()
{
    _changeNameCommand = new DelegateCommand(OnChangeName, CanChangeName);
}

private void OnChangeName(object commandParameter)
{
    FirstName = "Walter";
    _changeNameCommand.InvokeCanExecuteChanged();
}

private bool CanChangeName(object commandParameter)
{
    return FirstName != "Walter";
}

调用CanChangeName以确定命令是否可以执行。在这种情况下,一旦名称更改为“ Walter”,我们将仅阻止命令执行。最后,在OnChangeName方法中更改名称后,该命令通过引发其事件来通知按钮其CanExecute状态已更改。

运行该应用程序,我们可以看到在更改名称后该按钮可以正确禁用。

file

参考

解决docker容器中apt-get、pip慢问题

以debian/ubuntu为例,修改Dockerfile使用deiban和pip镜像。

RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
RUN sed -i 's|security.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple \
    && pip config set install.trusted-host mirrors.aliyun.com

使用npm镜像

RUN npm config set registry https://registry.npm.taobao.org

参考

Django扩展推荐

Saleor

A modular, high performance, headless e-commerce storefront built with Python, GraphQL, Django, and ReactJS.

pretix 活动票务系统

cookiecutter-django 快速启动可投入生产的Django项目

PostHog开源分析工具

weblate 基于WEB且带版本控制的本地化工具

netbox DigitalOcean开源的数据中心基础设施管理工具

Django-environ 使用十二因子方法环境变量配置Django

然后创建一个.env文件:

DATABASE_URL=psql://user:un-githubbedpassword@127.0.0.1:8458/database

使用:

DATABASES = {
    'default': env.db('DATABASE_URL', default='sqlite:////tmp/my-tmp-sqlite.db')
}

django-constance 动态Django设置

特征:

  • 轻松将静态设置迁移到动态设置。
  • 在Django管理界面中编辑动态设置。

file

django-moderation 审核任何模型对象

django-moderation是Django框架的可重用应用程序,它允许审核任何模型对象。

可能的用例:

  • 用户创建了自己的个人资料,该个人资料在网站上不可见。主持人批准后,它将在现场显示。
  • 用户更改了个人资料,可以在网站上看到旧的个人资料数据。主持人批准后,新数据将在现场显示。

特征:

  • 可配置的管理员集成(主持人批准后,可以在现场看到在管理员中更改的数据)
  • 管理员中的审核队列
  • html对象版本之间的变化差异
  • 可配置的电子邮件通知
  • 自定义模型形式,允许编辑对象的更改数据
  • 自动批准/拒绝选定的用户组或用户类型
  • 支持中等对象页面上的ImageField模型字段
  • 100%PEP8正确代码
  • 测试覆盖率> 80%

django-guardian – django 实现对象级别的权限控制

django-fsm – django状态机支持

cookiecutter-django-vue

Cookiecutter Django Vue is a template for Django-Vue projects.

django-bootstrap4

Bootstrap 4 integration with Django.

IntelliJ/PhpStorm/PyCharm等启动报错Cannot Lock System Folders

解决办法

Disable hyper-v (which will required a couple of restarts)

dism.exe /Online /Disable-Feature:Microsoft-Hyper-V

When you finish all the required restarts, reserve the port you want so hyper-v doesn’t reserve it back

netsh int ipv4 add excludedportrange protocol=tcp startport=50051  numberofports=1

Re-Enable hyper-V (which will require a couple of restart)

dism.exe /Online /Enable-Feature:Microsoft-Hyper-V /All

when your system is back, you will be able to bind to that port successfully.

参考

Windows Terminal支持ssh,git-bash,wsl, msys

打开 profiles.json 配置文件

打开Windows Terminal,点击设置,可以看到,复制增加一组ssh配置,然后修改三个地方。

{
// Make changes here to the powershell.exe profile.
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"name": "Windows PowerShell",
"commandline": "powershell.exe",
"hidden": false
},

修改guid

利用powershell可以生成guid

powershell -Command "[guid]::NewGuid().ToString()"

修改名字

任意,可以是系统名字,IP,用户名等,如 username@aliyun-bj

修改commandline

ssh -i C:\user\username\id_rsa username@aliyun-bj

其中i参数后面为证书存放位置,如果试用密码登录可以不填

完整SSH支持(图标,配色等)

 {
     "acrylicOpacity" : 1,
     "closeOnExit" : true,
     "colorScheme" : "One Half Dark",
     "commandline" : "ssh username@aliyun-bj",
     "cursorColor" : "#FFFFFF",
     "cursorShape" : "bar",
     "fontFace" : "Consolas",
     "fontSize" : 14,
     "guid" : "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
     "historySize" : 9001,
     "icon" : "ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png",
     "name" : "username@aliyun-bj",
     "padding" : "0, 0, 0, 0",
     "snapOnInput" : true,
     "startingDirectory" : "",
     "useAcrylic" : false
 }

 支持Git-bash

{
            "guid": "{7f0152b2-c491-4bb6-8a87-610352d630c8}",
            "acrylicOpacity" :1,
            "closeOnExit" : true,
            "colorScheme" : "Campbell",
            "commandline" : "\"%PROGRAMFILES%\\Git\\Bin\\bash.exe\" -i -l",
            "cursorColor" : "#FFFFFF",
            "cursorShape" : "bar",
            "fontFace" : "Consolas",
            "fontSize" : 14,
            "historySize" : 9001,
            "icon" : "%PROGRAMFILES%\\Git\\mingw64\\share\\git\\git-for-windows.ico",
            "name" : "Git-bash",
            "padding" : "0, 0, 0, 0",
            "snapOnInput" : true,
            "startingDirectory" : "%USERPROFILE%",
            "useAcrylic" : true
        }

WSL

        {
            "guid": "{2c4de342-38b7-51cf-b940-2309a097f518}",
            "hidden": false,
            "name": "Ubuntu",
            "source": "Windows.Terminal.Wsl"
        },

msys2

 {
                "guid": "{17da3cac-b318-431e-8a3e-7fcdefe6d114}",
                "name": "MINGW64 / MSYS2",
                "commandline": "C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64",
                "startingDirectory": "C:/msys64/home/%USERNAME%",
                "icon": "C:/msys64/mingw64.ico",
                "fontFace": "Lucida Console",
                "fontSize": 9
            },
            {
                "guid": "{2d51fdc4-a03b-4efe-81bc-722b7f6f3820}",
                "name": "MINGW32 / MSYS2",
                "commandline": "C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw32",
                "startingDirectory": "C:/msys64/home/%USERNAME%",
                "icon": "C:/msys64/mingw32.ico",
                "fontFace": "Lucida Console",
                "fontSize": 9
            },
            {
                "guid": "{71160544-14d8-4194-af25-d05feeac7233}",
                "name": "MSYS / MSYS2",
                "commandline": "C:/msys64/msys2_shell.cmd -defterm -here -no-start -msys",
                "startingDirectory": "C:/msys64/home/%USERNAME%",
                "icon": "C:/msys64/msys2.ico",
                "fontFace": "Lucida Console",
                "fontSize": 9
            }

参考

nginx 非www跳www,www跳非www,http跳https

非www跳www

if ($host ~ ^(?!www\.)(?<domain>.+)$) {
    return  301 $scheme://www.$domain$request_uri;
}

www to non-www

if ($host ~ ^www\.(?<domain>.+)$) {
    return  301 $scheme://$domain$request_uri;
}

其他域名跳统一域名

if ($host != "example.com") {
    return  301 $scheme://example.com$request_uri;
}

http跳https

if ($scheme != "https") {
       return 301 https://$host$request_uri;
   } # managed by Certbot

参考