@

富文本编辑器是一种所见即所得(what you see is what you get 简称 WYSIWYG)文本样式编辑器,用户在编辑器中输入内容和所做的样式修改,都会直接反映在编辑器中。

在Web端常见的有QuillTinyMCE这些开源免费的富文本编辑器,而目前.NET MAUI方面没有类似的富文本编辑器可以免费使用。

使用.NET MAUI实现一个富文本编辑器并不难,今天就来写一个

使用.NET MAU实现跨平台支持,本项目可运行于Android、iOS平台。由于篇幅本文只展示Android平台的代码。

原理

.NET MAUI提供了编辑器控件,允许输入和编辑多行文本,虽然提供了字号,字体,颜色等控件属性,但我们无法为每个字符设置样式。我们将通过原生控件提供的范围选择器实现这一功能。

.NET MAUI提供了Handler的跨平台特性,我们将利用Handler实现所见即所得内容编辑器组件。这篇博文介绍了如何用Handler实现自定义跨平台控件,请阅读[MAUI程序设计] 用Handler实现自定义跨平台控件

在各平台中,我们将使用原生控件实现所见即所得的内容编辑器

  • Android使用SpannableString设置文本的复合样式,可以查看https://www.cnblogs.com/jisheng/archive/2013/01/10/2854088.html

  • *iOS使用NSAttributeString设置文本的复合样式,可以参考https://blog.csdn.net/weixin_44544690/article/details/124154949

创建编辑器

新建.NET MAUI项目,命名RichTextEditor

在Controls目录中创建WysiwygContentEditor,继承自Editor,用于实现所见即所得的内容编辑器

构造函数中注册HandlerChanged和HandlerChanging事件


public class WysiwygContentEditor : Editor
{
public WysiwygContentEditor()
{
HandlerChanged+=WysiwygContentEditor_HandlerChanged;
HandlerChanging+=WysiwygContentEditor_HandlerChanging;
} }

在HandlerChanged事件中,获取Handler对象,通过它访问虚拟视图和本机视图。

private void WysiwygContentEditor_HandlerChanged(object sender, EventArgs e)
{
var handler = Handler;
if (handler != null)
{
}
}

android端原生控件为AppCompatEditText,iOS端原生控件为UITextView

//Android
var platformView = handler.PlatformView as AppCompatEditText;
//iOS
var platformView = handler.PlatformView as UITextView;

不同平台的代码,通过.Net6的条件编译实现,有关条件编译的详细信息,请参考官方文档。这次实现的是Android和iOS平台,所以在代码中条件编译语句如下

#if ANDROID

//android codes
... #endif #if IOS //iOS codes
... #endif

定义

定义StyleType枚举,用于控件可以处理的文本样式更改请求类型。

  • underline:字体下划线
  • italic:字体斜体
  • bold:字体加粗
  • backgoundColor:字体背景色
  • foregroundColor:字体前景色
  • size:字体大小
public enum StyleType
{
underline, italic, bold, backgoundColor, foregroundColor, size
}

以及StyleArgs类,用于传递样式变更请求的参数

public class StyleArgs : EventArgs
{
public StyleType Style; public string Params;
public StyleArgs(StyleType style, string @params = null)
{
Style = style;
Params=@params;
}
}

定义SelectionArgs类,用于传递选择范围变更请求的参数

public class SelectionArgs : EventArgs
{
public int Start;
public int End;
public SelectionArgs(int start, int end)
{
Start = start;
End = end;
}
}

定义事件用于各平台本机代码的调用

public event EventHandler GetHtmlRequest;
public event EventHandler<string> SetHtmlRequest;
public event EventHandler<StyleArgs> StyleChangeRequested;
public event EventHandler<SelectionArgs> SelectionChangeHandler;

创建StyleChangeRequested的订阅事件以响应样式变更请求,对应不同的样式类型,调用不同的方法实现样式变更。


StyleChangeRequested =new EventHandler<StyleArgs>(
(sender, e) => {
var EditableText = platformView.EditableText; switch (e.Style)
{
case StyleType.underline:
UpdateUnderlineSpans(EditableText);
break;
case StyleType.italic:
UpdateStyleSpans(TypefaceStyle.Italic, EditableText);
break;
case StyleType.bold:
UpdateStyleSpans(TypefaceStyle.Bold, EditableText);
break;
case StyleType.backgoundColor:
UpdateBackgroundColorSpans(EditableText, Microsoft.Maui.Graphics.Color.FromArgb(e.Params));
break;
case StyleType.foregroundColor:
UpdateForegroundColorSpans(EditableText, Microsoft.Maui.Graphics.Color.FromArgb(e.Params));
break;
case StyleType.size:
UpdateAbsoluteSizeSpanSpans(EditableText, int.Parse(e.Params));
break;
default:
break;
} });

实现复合样式

选择范围

android端使用SelectionStart和SelectionEnd获取选择范围,iOS端使用SelectedRange获取选择范围

//Android

int getSelectionStart() => platformView.SelectionStart;
int getSelectionEnd() => platformView.SelectionEnd; //iOS
NSRange getSelectionRange() => platformView.SelectedRange;

字号

MAUI控件中字号使用FontSize属性单位为逻辑像素,与DPI设置相关联。

在android本机平台中,字号通过为EditableText对象设置AbsoluteSizeSpan实现,代码如下


void UpdateAbsoluteSizeSpanSpans(IEditable EditableText, int size)
{ var spanType = SpanTypes.InclusiveInclusive; EditableText.SetSpan(new AbsoluteSizeSpan(size, true), getSelectionStart(), getSelectionEnd(), spanType);
SetEditableText(EditableText, platformView);
}

字体颜色与背景色

Android平台中,字体颜色与背景色通过为EditableText对象设置ForegroundColorSpan和BackgroundColorSpan实现

void UpdateForegroundColorSpans(IEditable EditableText, Microsoft.Maui.Graphics.Color color)
{
var spanType = SpanTypes.InclusiveInclusive;
EditableText.SetSpan(new ForegroundColorSpan(color.ToAndroid()), getSelectionStart(), getSelectionEnd(), spanType);
SetEditableText(EditableText, platformView);
} void UpdateBackgroundColorSpans(IEditable EditableText, Microsoft.Maui.Graphics.Color color)
{
var spanType = SpanTypes.InclusiveInclusive;
EditableText.SetSpan(new BackgroundColorSpan(color.ToAndroid()), getSelectionStart(), getSelectionEnd(), spanType);
SetEditableText(EditableText, platformView);
}

字体下划线

将选择文本选择范围内若包含下划线,则移除下划线,否则添加下划线

Android平台中通过为EditableText对象设置UnderlineSpan实现为文本添加下划线,通过RemoveSpan方法可以移除下划线,

但选择范围可能已包含下划线片段的一部分,因此移除此下划线片段后,需要重新添加下划线片段,以实现部分移除的效果


void UpdateUnderlineSpans(IEditable EditableText)
{ var underlineSpans = EditableText.GetSpans(getSelectionStart(), getSelectionEnd(), Java.Lang.Class.FromType(typeof(UnderlineSpan))); bool hasFlag = false;
var spanType = SpanTypes.InclusiveInclusive; foreach (var span in underlineSpans)
{
hasFlag = true; var spanStart = EditableText.GetSpanStart(span);
var spanEnd = EditableText.GetSpanEnd(span);
var newStart = spanStart;
var newEnd = spanEnd;
var startsBefore = false;
var endsAfter = false; if (spanStart < getSelectionStart())
{
newStart = getSelectionStart();
startsBefore = true;
}
if (spanEnd > getSelectionEnd())
{
newEnd = getSelectionEnd();
endsAfter = true;
} EditableText.RemoveSpan(span); if (startsBefore)
{
EditableText.SetSpan(new UnderlineSpan(), spanStart, newStart, SpanTypes.ExclusiveExclusive);
}
if (endsAfter)
{
EditableText.SetSpan(new UnderlineSpan(), newEnd, spanEnd, SpanTypes.ExclusiveExclusive);
}
} if (!hasFlag)
{
EditableText.SetSpan(new UnderlineSpan(), getSelectionStart(), getSelectionEnd(), spanType);
}
SetEditableText(EditableText, platformView);
}

字体加粗与斜体

Android平台中,字体粗细与斜体通过为EditableText对象设置StyleSpan实现,与设置字体下划线一样,需要处理选择范围内已包含StyleSpan的情况

TypefaceStyle提供了Normal、Bold、Italic、BoldItalic四种字体样式,粗体+斜体样式是通过组合实现的,因此需要处理样式叠加问题


void UpdateStyleSpans(TypefaceStyle flagStyle, IEditable EditableText)
{
var styleSpans = EditableText.GetSpans(getSelectionStart(), getSelectionEnd(), Java.Lang.Class.FromType(typeof(StyleSpan)));
bool hasFlag = false;
var spanType = SpanTypes.InclusiveInclusive; foreach (StyleSpan span in styleSpans)
{
var spanStart = EditableText.GetSpanStart(span);
var spanEnd = EditableText.GetSpanEnd(span);
var newStart = spanStart;
var newEnd = spanEnd;
var startsBefore = false;
var endsAfter = false; if (spanStart < getSelectionStart())
{
newStart = getSelectionStart();
startsBefore = true;
}
if (spanEnd > getSelectionEnd())
{
newEnd = getSelectionEnd();
endsAfter = true;
} if (span.Style == flagStyle)
{
hasFlag = true;
EditableText.RemoveSpan(span);
EditableText.SetSpan(new StyleSpan(TypefaceStyle.Normal), newStart, newEnd, spanType);
}
else if (span.Style == TypefaceStyle.BoldItalic)
{
hasFlag = true;
EditableText.RemoveSpan(span);
var flagLeft = TypefaceStyle.Bold;
if (flagStyle == TypefaceStyle.Bold)
{
flagLeft = TypefaceStyle.Italic;
}
EditableText.SetSpan(new StyleSpan(flagLeft), newStart, newEnd, spanType);
} if (startsBefore)
{
EditableText.SetSpan(new StyleSpan(span.Style), spanStart, newStart, SpanTypes.ExclusiveExclusive);
}
if (endsAfter)
{
EditableText.SetSpan(new StyleSpan(span.Style), newEnd, spanEnd, SpanTypes.ExclusiveExclusive);
} }
if (!hasFlag)
{
EditableText.SetSpan(new StyleSpan(flagStyle), getSelectionStart(), getSelectionEnd(), spanType);
} SetEditableText(EditableText, platformView);
}

序列化和反序列化

所见即所得的内容需要被序列化和反序列化以便存储或传输,我们仍然使用HTML作为中间语言,好在Android和iOS平台都有HTML互转的对应实现。

  • Android平台中,Android.Text.Html提供了FromHtml()和Html.ToHtml(),
  • iOS中的NSAttributedStringDocumentAttributes提供了DocumentType属性,可以设置为NSHTMLTextDocumentType,使用它初始化AttributedString或调用AttributedString.GetDataFromRange()方法实现HTML和NSAttributedString的互转。

跨平台实现

在Platform/Android目录下创建HtmlParser.Android作为Android平台序列化和反序列化的实现。

public static class HtmlParser_Android
{
public static ISpanned HtmlToSpanned(string htmlString)
{
ISpanned spanned = Html.FromHtml(htmlString, FromHtmlOptions.ModeCompact);
return spanned;
} public static string SpannedToHtml(ISpanned spanned)
{
string htmlString = Html.ToHtml(spanned, ToHtmlOptions.ParagraphLinesIndividual);
return htmlString;
}
}

在Platform/iOS目录下创建HtmlParser.iOS作为iOS平台序列化和反序列化的实现。

public static class HtmlParser_iOS
{
static nfloat defaultSize = UIFont.SystemFontSize;
static UIFont defaultFont; public static NSAttributedString HtmlToAttributedString(string htmlString)
{
var nsString = new NSString(htmlString);
var data = nsString.Encode(NSStringEncoding.UTF8);
var dictionary = new NSAttributedStringDocumentAttributes();
dictionary.DocumentType = NSDocumentType.HTML;
NSError error = new NSError();
var attrString = new NSAttributedString(data, dictionary, ref error);
var mutString = ResetFontSize(new NSMutableAttributedString(attrString)); return mutString;
} static NSAttributedString ResetFontSize(NSMutableAttributedString attrString)
{
defaultFont = UIFont.SystemFontOfSize(defaultSize); attrString.EnumerateAttribute(UIStringAttributeKey.Font, new NSRange(0, attrString.Length), NSAttributedStringEnumeration.None, (NSObject value, NSRange range, ref bool stop) =>
{
if (value != null)
{
var oldFont = (UIFont)value;
var oldDescriptor = oldFont.FontDescriptor; var newDescriptor = defaultFont.FontDescriptor; bool hasBoldFlag = false;
bool hasItalicFlag = false; if (oldDescriptor.SymbolicTraits.HasFlag(UIFontDescriptorSymbolicTraits.Bold))
{
hasBoldFlag = true;
}
if (oldDescriptor.SymbolicTraits.HasFlag(UIFontDescriptorSymbolicTraits.Italic))
{
hasItalicFlag = true;
} if (hasBoldFlag && hasItalicFlag)
{
uint traitsInt = (uint)UIFontDescriptorSymbolicTraits.Bold + (uint)UIFontDescriptorSymbolicTraits.Italic;
newDescriptor = newDescriptor.CreateWithTraits((UIFontDescriptorSymbolicTraits)traitsInt);
}
else if (hasBoldFlag)
{
newDescriptor = newDescriptor.CreateWithTraits(UIFontDescriptorSymbolicTraits.Bold);
}
else if (hasItalicFlag)
{
newDescriptor = newDescriptor.CreateWithTraits(UIFontDescriptorSymbolicTraits.Italic);
} var newFont = UIFont.FromDescriptor(newDescriptor, defaultSize); attrString.RemoveAttribute(UIStringAttributeKey.Font, range);
attrString.AddAttribute(UIStringAttributeKey.Font, newFont, range);
} }); return attrString;
} public static string AttributedStringToHtml(NSAttributedString attributedString)
{
var range = new NSRange(0, attributedString.Length);
var dictionary = new NSAttributedStringDocumentAttributes();
dictionary.DocumentType = NSDocumentType.HTML;
NSError error = new NSError();
var data = attributedString.GetDataFromRange(range, dictionary, ref error);
var htmlString = new NSString(data, NSStringEncoding.UTF8);
return htmlString;
}
}

集成至编辑器

在所见即所得编辑器中设置两个方法,一个用于获取编辑器中的内容,一个用于设置编辑器中的内容。


public void SetHtmlText(string htmlString)
{
HtmlString = htmlString;
SetHtmlRequest(this, htmlString);
} public string GetHtmlText()
{ GetHtmlRequest(this, new EventArgs());
return HtmlString;
}

在HandlerChanged事件方法中的各平台代码段中添加如下代码:

GetHtmlRequest = new EventHandler(
(sender, e) =>
{
var editor = (WysiwygContentEditor)sender;
HtmlString=HtmlParser_Android.SpannedToHtml(platformView.EditableText);
}
);
SetHtmlRequest =new EventHandler<string>(
(sender, htmlString) =>
{
platformView.TextFormatted = HtmlParser_Android.HtmlToSpanned(htmlString);
}
);

在富文本编辑器中的内容,最终会生成一个带有内联样式的HTML字符串,如下所示:

创建控件

控件由所见即所得编辑器和工具栏组成,所见即所得编辑器用于显示和编辑内容,工具栏用于设置字号、颜色、加粗、斜体、下划线

创建RichTextEditor的带有Xaml的ContentView。将所见即所得编辑器放置中央,工具栏放置在底部。

<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:RichTextEditor.Controls;assembly=RichTextEditor"
x:Class="RichTextEditor.Controls.RichTextEditor">
<Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<controls:WysiwygContentEditor MinimumHeightRequest="150"
AutoSize="TextChanges"
BackgroundColor="{StaticResource PhoneContrastBackgroundBrush}"
IsSpellCheckEnabled="false"
x:Name="MainEditor"></controls:WysiwygContentEditor>
</Grid>
</Border>
</ContentView>

工具栏内的按钮横向排列

<HorizontalStackLayout Grid.Row="3"
Spacing="5"
Margin="0,10">
<Button Text="{Binding Source={x:Reference TextSizeCollectionView}, Path=SelectedItem.Name, FallbackValue=Auto}"
Style="{StaticResource RichTextButtonStyle}"
Clicked="TextSizeButton_Clicked"
x:Name="TextSizeButton"></Button>
<Button Text="Color"
TextColor="{Binding Source={x:Reference ColorCollectionView}, Path=SelectedItem}"
Style="{StaticResource RichTextButtonStyle}"
Clicked="TextColorButton_Clicked"
x:Name="TextColorButton"></Button>
<Button Text="B"
Style="{StaticResource RichTextButtonStyle}"
FontAttributes="Bold"
x:Name="BoldButton"
Clicked="BoldButton_Clicked"></Button>
<Button Text="I"
Style="{StaticResource RichTextButtonStyle}"
FontAttributes="Italic"
x:Name="ItalicButton"
Clicked="ItalicButton_Clicked"></Button>
<Button Text="U"
Style="{StaticResource RichTextButtonStyle}"
FontAttributes="None"
x:Name="UnderLineButton"
Clicked="UnderLineButton_Clicked"></Button>
</HorizontalStackLayout>

配置两个选择器:TextSizeCollectionView为字体大小选择器,ColorCollectionView为字体颜色选择器。

当点击字体大小选择器时,弹出字体大小选择器,当点击字体颜色选择器时,弹出字体颜色选择器。

<VerticalStackLayout x:Name="OptionsLayout"
Grid.Row="2"
Spacing="5">
<CollectionView x:Name="TextSizeCollectionView"
Background="Transparent"
SelectionChanged="TextSizeCollectionView_SelectionChanged"
SelectionMode="Single"
HeightRequest="45">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal"
ItemSpacing="5"></LinearItemsLayout>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate> <Border x:Name="TargetElement"
Style="{StaticResource SelectableLayoutStyle}"
Background="{StaticResource PhoneContrastBackgroundBrush}"
Padding="5,0">
<Label Text="{Binding Name}"
TextColor="{StaticResource PhoneForegroundBrush}"
VerticalOptions="Center"
FontSize="{Binding Value}"></Label>
</Border> </DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView> <CollectionView x:Name="ColorCollectionView"
SelectionChanged="ColorCollectionView_SelectionChanged"
SelectionMode="Single"
HeightRequest="45">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal"
ItemSpacing="5"></LinearItemsLayout>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate> <Border x:Name="TargetElement"
Style="{StaticResource SelectableLayoutStyle}"
BackgroundColor="{Binding}"
WidthRequest="40"
HeightRequest="40"
StrokeShape="RoundRectangle 40"> </Border> </DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>

后端代码,绑定一些默认值


public static List<Color> DefaultTextColorList = new List<Color>() {
Color.FromArgb("#000000"),
Color.FromArgb("#F9371C"),
Color.FromArgb("#F97C1C"),
Color.FromArgb("#F9C81C"),
Color.FromArgb("#41D0B6"),
Color.FromArgb("#2CADF6"),
Color.FromArgb("#6562FC")
}; public static List<TextSize> DefaultTextSizeList = new List<TextSize>() {
new TextSize(){Name="Large", Value=22},
new TextSize(){Name="Middle", Value=18},
new TextSize(){Name="Small", Value=12},
};

效果如下:

使用控件

在MainPage中使用RichTextEditor,代码如下


<controls:RichTextEditor
x:Name="MainRichTextEditor"
Text="{Binding Content}"
Placeholder="{Binding PlaceHolder}"></controls:RichTextEditor>

MainRichTextEditor.GetHtmlText()测试获取富文本编辑器Html序列化功能。

private async void Button_Clicked(object sender, EventArgs e)
{
var html = this.MainRichTextEditor.GetHtmlText();
await DisplayAlert("GetHtml()", html, "OK");
}

最终效果

已知问题

  • HTML样式会重复添加

项目地址

我在maui-sample项目中的一些控件,打算做成一个控件库,方便大家使用。控件库地址在下方。

maui-sample项目作为控件库孵化器,代码可能会有点乱,也没有经过严格的测试。当控件完善到一定程度,我会把控件封装起来放到控件库中。如果你有好的控件,欢迎pull request。

maui-sample:

Github:maui-sample

Mato.Maui控件库

Mato.Maui

[MAUI]写一个跨平台富文本编辑器的更多相关文章

  1. 整理15款实用javascript富文本编辑器

    百度UEditor 官方网址:http://ueditor.baidu.com/website/ UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验 ...

  2. [前端随笔][JavaScript] 制作一个富文本编辑器

    写在前面 现在网上有很多现成的富文本编辑器,比如百度家的UEditor,kindeditor,niceditor等等,功能特别的多,API也很多,要去熟悉他的规则也很麻烦,所以想自己了解一下原理,做一 ...

  3. 基于ABP做一个简单的系统——实战篇:4.基于富文本编辑器,Razor模板引擎生成内容并导出Word 填坑记录

    起因 需求是这样的,有一种协议需要生成,协议的模板是可配置的,在生成过程中,模板中的内容可以根据约定的标记进行替换(就像mvc的razor模板一样).生成后的内容还需要导出成word或pdf. 常见的 ...

  4. 从零开始, 开发一个 Web Office 套件 (1): 富文本编辑器

    这是一个系列博客, 最终目的是要做一个基于HTML Canvas 的, 类似于微软 Office 的 Web Office 套件, 包括: 文档, 表格, 幻灯片... 等等. 富文本编辑器 万里长征 ...

  5. ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork 制作一个添加新闻功能

    本文将交大伙怎么集成ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork来制作一个新闻系统 先上截图: 添加页面如下: 下面来看代码部分 列表页如下: @ ...

  6. [译] 通过 contentEditable 属性创建一个所见即所得的编辑器(富文本编辑器)

    译者注 这只是一篇入门教程,介绍了一些基础知识,仅供参考,切不可因此觉得富文本编辑器很简单. 创建富文本编辑器是一个非常复杂的工程,需要考虑到方方面面,也有很多坑(请参考原文第一条评论). 为免误导大 ...

  7. 从零开始, 开发一个 Web Office 套件 (2): 富文本编辑器

    书接前文: 从零开始, 开发一个 Web Office 套件 (1): 富文本编辑器 这是一个系列博客, 最终目的是要做一个基于HTML Canvas 的, 类似于微软 Office 的 Web Of ...

  8. iView + vue-quill-editor 实现一个富文本编辑器(包含图片,视频上传)

    1. 引入插件(注意IE10以下不支持) npm install vue-quill-editor --savenpm install quill --save (Vue-Quill-Editor需要 ...

  9. 富文本编辑器Simditor的简易使用

    最近打算自己做一个博客系统,并不打算使用帝国cms或者wordpress之类的做后台管理!自己处于学习阶段也就想把从前台到后台一起谢了.好了,废话不多说了,先来看看富文本编辑器SimDitor,这里是 ...

  10. 个人网站对xss跨站脚本攻击(重点是富文本编辑器情况)和sql注入攻击的防范

    昨天本博客受到了xss跨站脚本注入攻击,3分钟攻陷--其实攻击者进攻的手法很简单,没啥技术含量.只能感叹自己之前竟然完全没防范. 这是数据库里留下的一些记录.最后那人弄了一个无限循环弹出框的脚本,估计 ...

随机推荐

  1. 面试题锦集:1、数据库三大范式,2、mysql索引类型及作用,3、事务的特性和隔离级别

    目录 面试题集锦 一.数据库三大范式 二.mysql有哪些索引类型及作用 三.事务的特性和隔离级别 1.事务的四大特性 2.事务的隔离级别 3.什么是脏读.不可重复度.幻读 4.解决办法 面试题集锦 ...

  2. 【Deep Learning】DDPM

    DDPM 1. 大致流程 1.1 宏观流程 1.2 训练过程 1.3 推理过程 2. 对比GAN 2.1 GAN流程 2.2 相比GAN优点 训练过程更稳定,损失函数指向性更强(loss数值大小指示训 ...

  3. python入门教程之二十二网络编程

    Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法. 高级别的网络 ...

  4. [Windows]BAT脚本自定义函数

    1 helloworld @echo off call :helloworld helloworld goto :EOF :helloworld setlocal echo %1 endlocal&a ...

  5. Django笔记二十之手动编写migration文件

    本文首发于公众号:Hunter后端 原文链接:Django笔记二十之手动编写migration文件 前面介绍过,migration 文件主要记录的是 Django 系统 model 的变化,然后通过 ...

  6. Python常见面试题017: Python中是否可以获取类的所有实例

    017. Python中是否可以获取类的所有实例 转载请注明出处,https://www.cnblogs.com/wuxianfeng023 出处 https://docs.python.org/zh ...

  7. 会使用ChatGPT写作业找工作会不会加分啊!!!代码问题直接问ChatGPT不比CSDN好多了吗

    1.使用reactnative写一个数字游戏 2.MySQL问题

  8. python运维工程师-cmdb项目-day2

    1.捕获异常信息 import tracebackdef disk(): int('saaa')def run(): try: disk() except Exception: ret=traceba ...

  9. JVM有意思的图-持续更新

    放一些JVM有意思的图 通过一行代码联想JVM:

  10. Natasha V5.2.2.1 稳定版正式发布.

    DotNetCore.Natasha.CSharp v5.2.2.1 使用 NMS Template 接管 CI 的部分功能. 取消 SourceLink.GitHub 的继承性. 优化几处内存占用问 ...