WPF学习:Slider — 冒泡显示值

news2024/11/17 15:41:24

想做一个下图所示的Slider,以冒泡的方式显示其Value值,该怎么做呢?

在这里插入图片描述

功能要求,当鼠标放在滑块上的时候,冒“泡”显示值;当滑块移动的时候,“泡”跟随移动。

看似简单的功能,但要完美实现,确实要花费不少心思的。

方法一:使用Canvas面板。

  1. 改造Slider
    Slider的样式改造,比较简单,重写其模板即可。
  <Slider.Template>
            <ControlTemplate TargetType="Slider">
                <Border Name="border" SnapsToDevicePixels="True"
                        Background="{TemplateBinding Panel.Background}" 
                        BorderBrush="{TemplateBinding Border.BorderBrush}" 
                        BorderThickness="{TemplateBinding Border.BorderThickness}" >
                    <Grid>
                        <Border Background="Gray" Height="4" CornerRadius="2"/>
                        <Track Name="PART_Track" Grid.Row="1">
                            <Track.DecreaseRepeatButton>
                                <RepeatButton>
                                    <RepeatButton.Template>
                                        <ControlTemplate TargetType="RepeatButton">
                                            <Border Height="4" CornerRadius="2" Background="#FF006CBE"/>
                                        </ControlTemplate>
                                    </RepeatButton.Template>
                                </RepeatButton>
                            </Track.DecreaseRepeatButton>

                            <Track.Thumb>
                                <Thumb Name="thumb" VerticalAlignment="Center" Focusable="False">
                                    <Thumb.Template>
                                        <ControlTemplate TargetType="Thumb">
                                            <Grid>
                                                <Ellipse Name="thumbEllipse" Width="10" Height="10" Fill="White" StrokeThickness="1" Stroke="#FF006CBE" />
                                            </Grid>
                                        </ControlTemplate>
                                    </Thumb.Template>
                                </Thumb>
                            </Track.Thumb>
                        </Track>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Slider.Template>
    </Slider>
  1. 添加“泡”
    很明显,这个“泡”的形状、文字都是一个元素,因此需要使用面板来将两个元素组合起来。这里最好使用Canvas,因为它的子元素是绝对定位的,可以通过Canvas.Left、Canvas.Top等属性安排子元素的位置,且Canvas也不会影响父元素的布局。因此将Slider的模板继续改造一下,如下所示:
 <Slider Name="slider" Width="255">
        <Slider.Template>
            <ControlTemplate TargetType="Slider">
                <Border Name="border" SnapsToDevicePixels="True"
                        Background="{TemplateBinding Panel.Background}" 
                        BorderBrush="{TemplateBinding Border.BorderBrush}" 
                        BorderThickness="{TemplateBinding Border.BorderThickness}" >
                    <Grid>
                        <Border Background="Gray" Height="4" CornerRadius="2"/>
                        <Track Name="PART_Track" Grid.Row="1">
                            <Track.DecreaseRepeatButton>
                                <RepeatButton>
                                    <RepeatButton.Template>
                                        <ControlTemplate TargetType="RepeatButton">
                                            <Border Height="4" CornerRadius="2" Background="#FF006CBE"/>
                                        </ControlTemplate>
                                    </RepeatButton.Template>
                                </RepeatButton>
                            </Track.DecreaseRepeatButton>

                            <Track.Thumb>
                                <Thumb Name="thumb" VerticalAlignment="Center" Focusable="False">
                                    <Thumb.Template>
                                        <ControlTemplate TargetType="Thumb">
                                            <Grid>
                                                <Ellipse Name="thumbEllipse" Width="10" Height="10" Fill="White" StrokeThickness="1" Stroke="#FF006CBE" />
                                                <Canvas Name="canvas" Opacity="0">
                                                    <Grid Canvas.Bottom="10" Canvas.Left="-8">
                                                        <Path Width="25" Stretch="Uniform" Fill="White" Stroke="#FF006CBE" StrokeThickness="1" Data="M172,633.14a339.4,339.4,0,0,1-169.9-294C2.13,151.76,154.2-.29,341.57-.29S681,151.76,681,339.11a339.38,339.38,0,0,1-169.9,294h0a183.88,183.88,0,0,0-84.27,106.38h0l-85.26,283.61L256.3,739.52A183.88,183.88,0,0,0,172,633.14Z"/>
                                                        <TextBlock HorizontalAlignment="Center" Margin="0,5,0,0" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Slider}, Path=Value, Converter={StaticResource double2Int}}"/>
                                                    </Grid>
                                                </Canvas>
                                            </Grid>

                                            <ControlTemplate.Triggers>
                                                <Trigger Property="IsMouseOver" Value="True">
                                                    <Setter TargetName="canvas" Property="Opacity" Value="1"/>
                                                </Trigger>
                                                <Trigger Property="IsDragging" Value="True">
                                                    <Setter TargetName="canvas" Property="Opacity" Value="1"/>
                                                </Trigger>
                                            </ControlTemplate.Triggers>
                                        </ControlTemplate>
                                    </Thumb.Template>
                                </Thumb>
                            </Track.Thumb>
                        </Track>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Slider.Template>
    </Slider>

红色部分为添加的代码。有两点需要注意:

  1. TextBlock.Text属性的绑定,使用了RelativeSource模式。

  2. 记得要添加触发器(Trigger),使的Canvas能在指定的状态下显示出来。

以这种方式改的Slider控件,有一个问题:“泡”不能超出窗体显示。也就是说当滑块处于屏幕的边缘时,这个“泡”是显示不出来的。

方法二:使用Window
WPF框架中有Popup控件和Tooltip控件。通过微软的源代码可知,他们的实现都是使用Window控件。即在一定的条件下打开一个Windows,并显示内容,在另一个条件下,则关闭这个Window。

根据这个思路,就可以自定义一个控件,并实现更多的功能

  1. 自定义控件
    控件类:FellowPopup,其他的几个类为辅助类。
/// <summary>
/// 坐标系转换类
/// </summary>
public static class CoordinateHelper
{
    /// <summary>
    /// 将用户空间坐标系的点,转换为屏幕坐标系的点。
    /// 已考虑系统Dpi问题
    /// </summary>
    /// <param name="relativeTo">可视化对象。它的坐标系原点在左上角</param>
    /// <param name="point">在可视化对象坐标系中的点</param>
    /// <returns></returns>
    public static Point ClientToScreen(Visual relativeTo, Point point)
    {
        if (relativeTo == null) return point;
        Point targetToScreen = relativeTo.PointToScreen(point);            // 将point的点转换为屏幕坐标系的点
        var source = PresentationSource.FromVisual(relativeTo);
        double dpiX = 96.0, dpiY = 96.0;
        if (source?.CompositionTarget != null)
        {
            dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
            dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
        }
        return new Point(targetToScreen.X * 96.0 / dpiX, targetToScreen.Y * 96 / dpiY);
    }
}

public static class DependencyPropertyHelper
{
    /// <summary>
    /// 为依赖属性注册值发生改变时的回调方法
    /// </summary>
    /// <param name="Register">含有依赖属性的对象</param>
    /// <param name="dependencyProperty">依赖属性</param>
    /// <param name="action">要注册的值发生改变时的回调方法</param>
    /// <param name="runOnceImmediately">是否立即执行一次</param>
    public static void RegisterDependencyPropertyChanged(this DependencyObject Register, DependencyProperty dependencyProperty, EventHandler action, bool runOnceImmediately = false)
    {
        DependencyPropertyDescriptor obj = DependencyPropertyDescriptor.FromProperty(dependencyProperty, Register.GetType());
        if(obj != null) obj.AddValueChanged(Register, action);
        if(runOnceImmediately) action?.Invoke(Register,new EventArgs());
    }
}

/// <summary>
/// 放置模式
/// </summary>
public enum PlacementMode
{
    /// <summary>
    /// 顶部靠左
    /// </summary>
    TopLeft,

    /// <summary>
    /// 顶部居中
    /// </summary>
    TopMiddle,

    /// <summary>
    /// 顶部靠右
    /// </summary>
    TopRight,

    /// <summary>
    /// 左侧居中
    /// </summary>
    LeftMiddle,

    /// <summary>
    /// 右侧居中
    /// </summary>
    RightMiddle,

    /// <summary>
    /// 底部靠左
    /// </summary>
    BottomLeft,

    /// <summary>
    /// 底部居中
    /// </summary>
    BottomMiddle,

    /// <summary>
    /// 底部靠右
    /// </summary>
    BottomRight
}

/// <summary>
/// 可跟随控件移动的弹出提示框。
/// </summary>
public class FellowPopup : ContentControl
{
    private Window _popupWnd;

    /// <summary>
    /// 水平位置偏移量
    /// </summary>
    public double HorizontalOffset
    {
        get => (double)GetValue(HorizontalOffsetProperty);
        set => SetValue(HorizontalOffsetProperty, value);
    }

    internal static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.Register(
        "HorizontalOffset", typeof(double), typeof(FellowPopup), new PropertyMetadata(0.0d, OnValueChanged));

    /// <summary>
    /// 垂直偏移量
    /// </summary>
    public double VerticalOffset
    {
        get => (double)GetValue(VerticalOffsetProperty);
        set => SetValue(VerticalOffsetProperty, value);
    }

    internal static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.Register(
        "VerticalOffset", typeof(double), typeof(FellowPopup), new PropertyMetadata(0.0d, OnValueChanged));

    /// <summary>
    /// 放置位置
    /// </summary>
    public PlacementMode Placement
    {
        get => (PlacementMode)GetValue(PlacementProperty);
        set => SetValue(PlacementProperty, value);
    }

    internal static readonly DependencyProperty PlacementProperty = DependencyProperty.Register(
        "Placement", typeof(PlacementMode), typeof(FellowPopup), new PropertyMetadata(PlacementMode.BottomRight,OnValueChanged));

    /// <summary>
    /// 放置的目标元素
    /// </summary>
    public FrameworkElement PlacementTarget
    {
        get => (FrameworkElement)GetValue(PlacementTargetProperty);
        set => SetValue(PlacementTargetProperty, value);
    }

    internal static readonly DependencyProperty PlacementTargetProperty = DependencyProperty.Register(
        "PlacementTarget", typeof(FrameworkElement), typeof(FellowPopup), new PropertyMetadata(null,OnValueChanged));

    private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        FellowPopup popup = (FellowPopup)d;
        popup.UpdateLocation();
    }

    /// <summary>
    /// 是否打开。当其为true时,则显示提示框,false则不显示提示框
    /// </summary>
    public bool IsOpen
    {
        get => (bool)GetValue(IsOpenProperty);
        set => SetValue(IsOpenProperty, value);
    }


    internal static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register(
        "IsOpen", typeof(bool), typeof(FellowPopup), new PropertyMetadata(false,OnIsOpenChanged));


    private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        FellowPopup fp = d as FellowPopup;
        if((bool)e.NewValue ) fp.Open();
        else  fp.Close();
    }
    #region 触发位置更新

    /// <summary>
    /// 触发位置更新的控件
    /// </summary>
    public FrameworkElement TriggerElement
    {
        get => (FrameworkElement)GetValue(TriggerElementProperty);
        set => SetValue(TriggerElementProperty, value);
    }

    internal static readonly DependencyProperty TriggerElementProperty = DependencyProperty.Register(
        "TriggerElement", typeof(UIElement), typeof(FellowPopup), new PropertyMetadata(null, OnTriggerChanged));

    /// <summary>
    /// 触发位置更新的控件的属性
    /// 此属性应该是TriggerElement的属性。
    /// 当此属性的值发生变化时,会触发FellowPopup的UpdateLocation方法。
    /// </summary>
    public DependencyProperty TriggerProperty
    {
        get => (DependencyProperty)GetValue(TriggerPropertyProperty);
        set => SetValue(TriggerPropertyProperty, value);
    }

    internal static readonly DependencyProperty TriggerPropertyProperty = DependencyProperty.Register(
        "TriggerProperty", typeof(DependencyProperty), typeof(FellowPopup), new PropertyMetadata(null, OnTriggerChanged));

    private static void OnTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        FellowPopup fp = (FellowPopup)d;
        if (fp.TriggerElement == null || fp.TriggerProperty == null) return;

        // 如果定义属性的类,与TriggerElement类相同,或者TriggerElement类是它的子类
        Type propertyType = fp.TriggerProperty.OwnerType;
        Type elementType = fp.TriggerElement.GetType();

        if ((elementType != propertyType) && (!elementType.IsSubclassOf(propertyType))) throw new ArgumentException("TriggerElement不包含TriggerProperty属性");
        DependencyPropertyHelper.RegisterDependencyPropertyChanged(fp.TriggerElement, fp.TriggerProperty, delegate (object sender, EventArgs args)
        {
            fp.UpdateLocation();
        }, false);
    }
    #endregion

    static FellowPopup()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(FellowPopup), new FrameworkPropertyMetadata(typeof(FellowPopup)));
    }

    /// <summary>
    /// 打开窗体
    /// </summary>
    private void Open()
    {
        _popupWnd = new Window()
        {
            Topmost = true,
            ShowActivated = false,
            AllowsTransparency = true,
            WindowStyle = WindowStyle.None,
            BorderThickness = new Thickness(0),
            SizeToContent = SizeToContent.WidthAndHeight,
            WindowStartupLocation = WindowStartupLocation.Manual,

            Background = this.Background,
            Content = this.Content,
            ContentStringFormat = this.ContentStringFormat,
            ContentTemplate = this.ContentTemplate,
            ContentTemplateSelector = this.ContentTemplateSelector,
        };
        UpdateLocation();
        _popupWnd.Show();
    }

    // 关闭_popup窗体,并回收
    private void Close()
    {
        if (_popupWnd != null)
        {
            _popupWnd.Close();
            GC.SuppressFinalize(_popupWnd);
        }
    }

    /// <summary>
    /// 更新弹出窗体的位置。
    /// </summary>
    private void UpdateLocation()
    {
        if (_popupWnd == null) return;
        if(PlacementTarget == null)
        {
            _popupWnd.Left = 0;
            _popupWnd.Top = 0;
            return;
        }

        double contentWidth = _popupWnd.Content == null ? 0 : ((FrameworkElement)_popupWnd.Content).ActualWidth;
        double contentHeght = _popupWnd.Content == null ? 0 : ((FrameworkElement)_popupWnd.Content).ActualHeight;

        Point targetToScreen;
        switch (Placement)
        {
            case PlacementMode.TopLeft:
                targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget,new Point(0, 0));
                _popupWnd.Left = targetToScreen.X - contentWidth - HorizontalOffset;
                _popupWnd.Top = targetToScreen.Y - contentHeght - VerticalOffset;
                break;

            case PlacementMode.TopMiddle:
                targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(PlacementTarget.ActualWidth/2, 0));
                _popupWnd.Left = targetToScreen.X - contentWidth / 2 + HorizontalOffset;
                _popupWnd.Top = targetToScreen.Y - contentHeght - VerticalOffset;
                break;

            case PlacementMode.TopRight:
                targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(PlacementTarget.ActualWidth, 0));
                _popupWnd.Left = targetToScreen.X + HorizontalOffset;
                _popupWnd.Top = targetToScreen.Y - contentHeght - VerticalOffset;
                break;

            case PlacementMode.LeftMiddle:
                targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(0, PlacementTarget.ActualHeight / 2));
                _popupWnd.Left = targetToScreen.X - contentWidth - HorizontalOffset;
                _popupWnd.Top = targetToScreen.Y - contentHeght / 2;
                break;

            case PlacementMode.RightMiddle:
                targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(PlacementTarget.ActualWidth, PlacementTarget.ActualHeight / 2));
                _popupWnd.Left = targetToScreen.X + HorizontalOffset;
                _popupWnd.Top = targetToScreen.Y - contentHeght / 2;
                break;

            case PlacementMode.BottomLeft:
                targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(0, PlacementTarget.ActualHeight));
                _popupWnd.Left = targetToScreen.X - contentWidth - HorizontalOffset;
                _popupWnd.Top = targetToScreen.Y + VerticalOffset;
                break;

            case PlacementMode.BottomMiddle:
                targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(PlacementTarget.ActualWidth / 2, PlacementTarget.ActualHeight));
                _popupWnd.Left = targetToScreen.X - contentWidth / 2 - HorizontalOffset;
                _popupWnd.Top = targetToScreen.Y + VerticalOffset;
                break;

            case PlacementMode.BottomRight:
                targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(PlacementTarget.ActualWidth, PlacementTarget.ActualHeight));
                _popupWnd.Left = targetToScreen.X + HorizontalOffset;
                _popupWnd.Top = targetToScreen.Y + VerticalOffset;
                break;
        }
    }
}

(1)在FellowPopup类中,定义了一个Window类型的变量_popup,用于展示“泡”的内容。PlacementTarget、Placement、HorizontalOffset、VerticalOffset四个属性,用于设置_popup的Left和Top属性,以确定_popup在屏幕中的位置。

这里需要注意,Visual.PointToScreen()方法,参数point是在Visual坐标空间的点,其返回值为将这个点,转换为屏幕坐标系的点。但转换后的点,未考虑到系统DPI及屏幕缩放的情况。因此,需要将这个点再运算,使之成为当前屏幕DPI及缩放模式下的点坐标。

(2)TriggerElement、TriggerProperty属性,用于指示,当哪个元素的哪个属性值发生变化时,更新位置。

要想让一个窗体跟着控件移动,就必须告诉窗体,控件当前的位置在哪儿,然后去计算窗体应该在哪儿。而在WPF中,几乎没有控件会实时报告自己的位置。因此,必须找到元素的一个属性,当元素的“位置”发生改变的时候,这个属性值会发生变化。

除了找到这样的一个属性外,还需要注册一个属性值发生改变时的回调方法。在本例中,滑块在滑动的过程中,本身没有属性(公开的)值发生改变,但是Slider的Value属性会发生改变。因此可以选择它,来触发FellowPopup的位置改变。

由此可见,PlacementTarge系列与TriggerElement可以是不同的。一个用于“布置”窗体,一个用于“触发”窗体。

  1. 使用自定义控件
    定义好了控件后,就可以在Xaml中使用它了。继续改造Slider的模板:

     <!-- 别忘了转换器 -->
     <local:Double2IntConverter  x:Key="double2Int" /> 
    
     <Slider Name="slider" Width="255">
         <Slider.Template>
             <ControlTemplate TargetType="Slider">
                 <Border Name="border" SnapsToDevicePixels="True"
                         Background="{TemplateBinding Panel.Background}" 
                         BorderBrush="{TemplateBinding Border.BorderBrush}" 
                         BorderThickness="{TemplateBinding Border.BorderThickness}" >
                     <Grid>
                         <Border Background="Gray" Height="4" CornerRadius="2"/>
                         <Track Name="PART_Track" Grid.Row="1">
                             <Track.DecreaseRepeatButton>
                                 <RepeatButton>
                                     <RepeatButton.Template>
                                         <ControlTemplate TargetType="RepeatButton">
                                             <Border Height="4" CornerRadius="2" Background="#FF006CBE"/>
                                         </ControlTemplate>
                                     </RepeatButton.Template>
                                 </RepeatButton>
                             </Track.DecreaseRepeatButton>
                             <Track.Thumb>
                                 <Thumb Name="thumb" VerticalAlignment="Center" Focusable="False">
                                     <Thumb.Template>
                                         <ControlTemplate TargetType="Thumb">
                                             <Grid>
                                                 <Ellipse Name="thumbEllipse" Width="10" Height="10" Fill="White" StrokeThickness="1" Stroke="#FF006CBE" />
                                                 <local:FellowPopup x:Name="popup" Background="Transparent"
                                                                    Placement="TopMiddle" PlacementTarget="{Binding ElementName=thumbEllipse}"
                                                                    TriggerElement="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Slider}}"
                                                                    TriggerProperty="Slider.Value">
                                                     <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
                                                         <Path Width="25" Stretch="Uniform" Fill="White" Stroke="#FF006CBE" StrokeThickness="1" Data="M172,633.14a339.4,339.4,0,0,1-169.9-294C2.13,151.76,154.2-.29,341.57-.29S681,151.76,681,339.11a339.38,339.38,0,0,1-169.9,294h0a183.88,183.88,0,0,0-84.27,106.38h0l-85.26,283.61L256.3,739.52A183.88,183.88,0,0,0,172,633.14Z"/>
                                                         <TextBlock HorizontalAlignment="Center" Margin="0,5,0,0" 
                                                                    Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Slider}, Path=Value, Converter={StaticResource double2Int}}"/>
                                                     </Grid>
                                                 </local:FellowPopup>
                                             </Grid>
                                             <ControlTemplate.Triggers>
                                                 <Trigger Property="IsMouseOver" Value="True">
                                                     <Setter TargetName="popup" Property="IsOpen" Value="True"/>
                                                 </Trigger>
                                                 <Trigger Property="IsDragging" Value="True">
                                                     <Setter TargetName="popup" Property="IsOpen" Value="True"/>
                                                 </Trigger>
                                             </ControlTemplate.Triggers>
                                         </ControlTemplate>
                                     </Thumb.Template>
                                 </Thumb>
                             </Track.Thumb>
                         </Track>
                     </Grid>
                 </Border>
    
             </ControlTemplate>
         </Slider.Template>
     </Slider>
    

复制代码
注意Placement、TriggerElement、TriggerProperty、Text等属性的绑定方式。

至此,改造搞定。以第二种方式改造的Slider,“泡”可以显示在主窗体之上。没有任何东西会挡住他,以为它是TopMost的。另外也可以再添加自己的逻辑去设置“泡”的位置。

以上为自己学习总结,如有错误,请指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/353491.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

扬帆优配“数字经济+实体经济”融合发展,行业增长空间大!

组织以为&#xff0c;数字经济已经逐步成为工业商场和资本商场的共同主题。 2月16日&#xff0c;国家发改委在《求是》杂志发表文章《努力推进经济完成质的有效提升和量的合理增加》。文章指出要加速开展数字经济&#xff0c;加速实施“东数西算”等重大工程&#xff0c;推进数…

计算机网络笔记(四)—— 数据链路层

点对点协议PPP 目前使用最广泛的点对点数据链路层协议 由以下三个部分构成&#xff1a; PPP工作状态 媒体接入控制 主要解决共享信道的协调问题 静态划分信道 复用&#xff1a;通过一条物理线路同时传输多路用户的信号 频分复用&#xff08;FDM&#xff09;&#xff1a;占用…

DataEase 制作数据可视化大屏经验分享

前言 DataEase 简介 DataEase 是开源的数据可视化分析工具&#xff0c;帮助用户快速分析数据并洞察业务趋势&#xff0c;从而实现业务的改进与优化。DataEase 支持丰富的数据源连接&#xff0c;能够通过拖拉拽方式快速制作图表&#xff0c;并可以方便地与他人分享。 更多详细介…

ChatGPT及相关产品体验与研究

ChatGPT及相关产品体验与研究 我的Github博客仓库链接&#xff1a;ChatGPT及相关产品体验与研究 - Github 一、ChatGPT介绍 1. ChatGPT概述 一句话描述ChatGPT&#xff1a;一个能够通过对话得到你想要的答案的聊天机器人。 ChatGPT 是由 OpenAI 开发的一种基于深度学习的自然…

SPI简介与实例分析

SPI简介 SPI 协议是由Motorola提出的通讯协议 (Serial Peripheral Interface) &#xff0c;是一种高速全双工的串行通信总线。 SPI 通讯使用 3 条总线 &#xff1a;SCK、 MOSI、 MISO &#xff0c;以及若干片选线(SS、CS、NSS)。 主机要和哪个从机通信&#xff0c;就把对应的…

看板项目管理使用指南

作为一种项目管理方法&#xff0c;看板涉及创建列出任务详细信息的可视化卡片&#xff0c;并将它们组织到代表生产过程不同阶段的白板列表中。 今天&#xff0c;看板已经从工厂的白板发展到了我们电脑屏幕上的数字看板应用程序形式。现在的看板项目管理方法可帮助团队管理编辑…

CentOS Stream 8配置DNS

1&#xff1a;用CentOS搭建DNS的目的是想解析一台下载服务器&#xff0c;IP地址172.18.0.58&#xff0c;现在是用IP地址方的式访问&#xff0c;想搭建DNS服务器用域名的方式访问。 使用下面的命令查看一下当前系统的Bind版本。 yum info bind 版本是9.11.36.我的CentOS是最小…

KDGK-703S便携式综合校验仪(毫伏发生器)

一、产品简介 KDGK-703S便携式综合校验仪&#xff08;毫伏发生器&#xff09;是一种集数显式电压、电流、频率标准信号源、Pt100 铂电阻温度模拟标准信号源、数字式电压、电流、频率测量功能于一体的高精度、高分辨率、多用途的自动化仪表校准仪器。 它具有信号输出和测试功能&…

有关eclipse的使用tips

一、alt/键 会产生单词提示&#xff0c;可以提高编程速度。例如不需要辛辛苦苦的打出&#xff1a;System.out.println();整句&#xff0c;只需要在eclipse中输入syso&#xff0c;然后按住ALT/就会出来System.out.println();在alt键/不管用的情况下&#xff0c;可使用以下方法来…

HDMI AVI InfoFrame

AVI/AUDIO/VSI Infoframe都是HDMI的辅助数据类别&#xff0c;HDMI遵循CEA-861规范&#xff0c;CEA 第八章--CONTROL AND CONFIGURATION 8.2定义了三种InfoFrame。 一个InfoFrame packet载送一个InfoFrame&#xff0c;根据HDMI协议规范&#xff0c;其大小为30字节一个CheckSum。…

关于医院医用医疗隔离电源系统应用案例的分析探讨

【摘要】&#xff1a;介绍该三级医院采用安科瑞医用隔离电源柜&#xff0c;使用落地式安装方式&#xff0c;从而实现将TN系统转化为IT系统&#xff0c;同时监测系统绝缘情况。 【关键词】医用隔离电源柜&#xff1b;IT系统&#xff1b;绝缘情况&#xff1b;中西医结合医院&…

盘点Linux内核网络知识100道题,这篇就够了

计算机网络模型 1、五层因特网协议栈和七层OSI&#xff08;Open System Interconnections&#xff09;参考模型分别是什么&#xff1f; 5层&#xff1a;应用层、传输层、网络层、数据链路层、物理层 7层&#xff1a;应用层、表示层、会话层、传输层、网络层、数据链路层、物理…

Gradle安装部署与基础入门详解

【1】Gradle简介 Gradle 是一款Google 推出的基于JVM、通用灵活的项目构建工具&#xff0c;支持Maven&#xff0c;JCenter 多种第三方仓库;支持传递性依赖管理、废弃了繁杂的xml 文件&#xff0c;转而使用简洁的、支持多种语言(例如&#xff1a;java、groovy 等)的build 脚本文…

Delphi 的TZipFile压缩文件自定义(去掉盘符)

关于Delphi中的TZipFile压缩文件&#xff0c;非常方便&#xff0c;该类位于System.Zip单元中&#xff0c;压缩一个目录只需要一条命令TZipFile.ZipDirectoryContents就可以&#xff0c;但是这也有个不方便&#xff0c;就是不能动态增加文件和目录。如果想自定义增加文件或者目录…

【MyBatis】第十篇:mybatis的分页插件---pageHelper

分页无论是那个开发都是常见的使用场景&#xff0c;当然对于分页需要如果自己写的&#xff0c;不过自己写的话可能会需要想到很多&#xff1a; 比如&#xff1a;通过查询sql判断有多少数据&#xff0c;在页面显示共有多少页面。然后每页返回的数据是多少&#xff0c;上一页以及…

Echarts 更改K线图颜色,解释K线图4个数字意义

第019个点击查看专栏目录本示例修改K线度的颜色&#xff0c;方法参考源代码。 这里面讲一下K线图的四个数字&#xff0c;如[20, 34, 10, 38], 第一位&#xff1a;20代表开盘价格&#xff0c; 第二位&#xff1a;34代表闭盘价格&#xff0c; 第三位&#xff1a;10代表最低价&…

buuctf Web 上

buuctf Web 1.[HCTF 2018]WarmUp[HCTF 2018]WarmUp 访问url http://e00c89a6-d7d6-4a78-a346-614edfb95738.node3.buuoj.cn/ 如下 打开靶场后&#xff0c;查看源码即可看到构造url访问获得index.php的源码 http://e00c89a6-d7d6-4a78-a346-614edfb95738.node3.buuoj.cn/in…

Typescript的原始据类型和Any类型

最新的ECMAScript标准定义了8中数据类型: 7种原始类型&#xff1a; BooleanNullUndefinedNumberBigintStringSymbol和 Object 除 Object 以外的所有类型都是不可变的 (值本身无法被改变》。例如&#xff0c;与C语言不同JavaScript 中字符串是不可变的 (译注: 如&#xff0c;Ja…

Unreal Engine05:UE4基本概念

写在前面 主要是介绍一下UE4中的一些常用概念。 参考的博客如下&#xff1a; UE4常用逻辑关系和说明&#xff1b;UE4入门学习4&#xff1a;C编程介绍&#xff1b;UE4中基础的类及其之间的关系&#xff1b;[官方] 虚幻引擎 4 术语&#xff1b;《图解UE4渲染体系》Part 0 引擎基…

记录robosense RS-LIDAR-16使用过程5

本篇记录RS-LIDAR-16录制bag包&#xff0c;并解析bag包为pcd。官网操作基本过了一遍&#xff0c;接下来记录标定。相机、雷达设备在出厂前通常都完成了内参标定工作&#xff0c;若要联合使用多雷达或雷达-相机时&#xff0c;就需要进行外参标定。接下来学习并记录标定。首先找到…