android编译系统分析一:source build/envsetup.sh与lunch

article/2025/10/13 4:40:40

Android编译系统分析系列文章:

android编译系统分析一<source build/envsetup.sh与lunch>
Android编译系统<二>-mm编译单个模块
android编译系统分析(三)-make
android编译系统(四)-实战:新增一个产品
Android编译系统分析(五)-system.img的生成过程


虽然已经有很多人分析过android的编译系统的代码了,我也看过他们的博客,也学到了不少知识,但单纯的看别人分析,终究还是理解的不深入,所以,我还是要自己再认真的分析一遍。

想想我们编译android系统的过程:

首先:source build/envsetup.sh

其次:lunch    ---选择一个特定的类型

最后:make

按着这个顺序,追踪这看似简单的几步,到底有哪些背后的秘密?   

1. source build/envsetup.sh

这个文件虽然很大,但暂且不需要统统看一遍。它里面定义了很多函数,这些函数在使用的时候再具体详细学习,现在主要看看这个脚本做的事情。即便如此,打开这个脚本后第一个函数还是非常吸引人的,因为它里面介绍了这个脚本主要要做的事情:

function hmm() {
cat <<EOF
Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
- lunch:   lunch <product_name>-<build_variant>
- tapas:   tapas [<App1> <App2> ...] [arm|x86|mips|armv5|arm64|x86_64|mips64] [eng|userdebug|user]
- croot:   Changes directory to the top of the tree.
- m:       Makes from the top of the tree.
- mm:      Builds all of the modules in the current directory, but not their dependencies.
- mmm:     Builds all of the modules in the supplied directories, but not their dependencies.To limit the modules being built use the syntax: mmm dir/:target1,target2.
- mma:     Builds all of the modules in the current directory, and their dependencies.
- mmma:    Builds all of the modules in the supplied directories, and their dependencies.
- cgrep:   Greps on all local C/C++ files.
- ggrep:   Greps on all local Gradle files.
- jgrep:   Greps on all local Java files.
- resgrep: Greps on all local res/*.xml files.
- mangrep: Greps on all local AndroidManifest.xml files.
- sepgrep: Greps on all local sepolicy files.
- sgrep:   Greps on all local source files.
- godir:   Go to the directory containing a file.Environemnt options:
- SANITIZE_HOST: Set to 'true' to use ASAN for all host modules. Note thatASAN_OPTIONS=detect_leaks=0 will be set by default until thebuild is leak-check clean.Look at the source to view more functions. The complete list is:
EOFT=$(gettop)local AA=""for i in `cat $T/build/envsetup.sh | sed -n "/^[ \t]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; doA="$A $i"doneecho $A
}
这个函数列出来主要是它介绍了这个脚本的一些功能,第一行cat <<EOP是一个HERE文档,意思就是把EOF后面到下一个EOF前面的内容当做一个文件,然后cat 会接收这个文件的内容,而cat默认的输出是标准输出,也就是这个文件的内容会被打印到屏幕上来。这些内容介绍了这个脚本的用法和功能,用法就是“. build/envsetup.sh”注意.后面有个空格,这个.命令就是source命令,也就是说你也可以执行“source build/envsetup.sh”。此外,这个函数介绍了很多函数的功能,比如lunch,m,mm,mmm,cgrep等。

函数只有在调用到它的时候才会执行,所以暂时统统不看,现在只看函数外面的内容:

# Clear this variable.  It will be built up again when the vendorsetup.sh
# files are included at the end of this file.
unset LUNCH_MENU_CHOICES
这里调用了unset命令,unset是一个bash命令,它会删除给定的变量。也就是说它会删除LUNCH_MENU_CHOICES变量。既然这里刻意把它删除了,那么它肯定要立刻重新构造这个变量了,果然:

# add the default one here
add_lunch_combo aosp_arm-eng
add_lunch_combo aosp_arm64-eng
add_lunch_combo aosp_mips-eng
add_lunch_combo aosp_mips64-eng
add_lunch_combo aosp_x86-eng
add_lunch_combo aosp_x86_64-eng
这几行调用了add_lunch_combo函数,并且传入了一些参数,这使得我们执行lunch函数的时候,多了这几条可以选择的选项。

而这个函数,就是就是重新构造LUNCH_MENU_CHOICES变量:

function add_lunch_combo()
{local new_combo=$1local cfor c in ${LUNCH_MENU_CHOICES[@]} ; doif [ "$new_combo" = "$c" ] ; thenreturnfidoneLUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
}

了解过shell编程就知道,$1表示传入函数的第一个参数,然后又定义了一个变量c。

从for循环中可以看出,LUNCH_MENU_CHOICES是一个数组,在shell中,可以通过 "变量名[@]"或者“变量名[*]”的方式过得数组的所有项。然后注意比较数组中的每一项,如果数组中已经有传入的参数项,就继续返回,否则,就把新的传入的参数加入到LUNCH_MENU_CHOIES数组中。

这个脚本虽然很长,但是正真执行的代码没有多少,就是说当执行source build/envsetup.sh的时候执行的代码没有多少,它里面大多数内容都是函数的定义。在该文件的最后,又执行了一点代码:

if [ "x$SHELL" != "x/bin/bash" ]; thencase `ps -o command -p $$` in*bash*);;*)echo "WARNING: Only bash is supported, use of other shell would lead to erroneous results";;esac
fi# Execute the contents of any vendorsetup.sh files we can find.
for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \`test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort`
doecho "including $f". $f
done
unset f


前面的if语句块就是检查支持的shell解析器。后面的for语句块比较关键。

这里使用了shell的反引号的用法,它的作用就是把反引号的内容当做shell命令来执行。然后把执行结果使用echo输出到屏幕。反引号中,首先使用test命令测试 device目录是否存在,存在的话就查找vendorsetup.sh脚本,并且设定了查找深度为4层目录。2> /dev/null 是shell中的重定向语法,这里把标准错误重定向到/dev/null中,也就是,把错误统统删掉,左后把找到的vendorsetup.sh脚本使用sort命令进行排序。

. $f相当于source $f,也就是source /device和/vendor下找到的所有vendorsetup.sh脚本。然后,这个脚本就分析结束了,接下来,自然是要去分析/device和/vendor下的vendorsetup.sh脚本了。


2./device/vendor下的vendorsetup.sh

执行一下source build/envsetup.sh,会打印一下内容:

including device/generic/mini-emulator-arm64/vendorsetup.sh
including device/generic/mini-emulator-armv7-a-neon/vendorsetup.sh
including device/generic/mini-emulator-mips/vendorsetup.sh
including device/generic/mini-emulator-x86_64/vendorsetup.sh
including device/generic/mini-emulator-x86/vendorsetup.sh
这里只列出了一部分,那以第一条为例,看看它的内容:

add_lunch_combo mini_emulator_arm64-userdebug
这个函数我们已经分析过了,而且这个文件也只有这么一行,这里列出的这几个vendorsetup.sh脚本都是只有这么一行,他就是在lunch的菜单中添加一项。当然也不都是如此,在有些厂家的vendorsetup.sh也会做一些其他的工作,但是,不管做多少其他的事情,第一件事情似乎都是一定的,就是调用add_lunch_combo 添加一项。

因此,总结来说,envsetup.sh脚本做了这样的事情:


3.lunch

编译android的时候,执行完souce build/envsetup.sh,我们还需要执行lunch,选择一个特定的单板。

function lunch()
{local answerif [ "$1" ] ; thenanswer=$1elseprint_lunch_menuecho -n "Which would you like? [aosp_arm-eng] "read answerfilocal selection=if [ -z "$answer" ]thenselection=aosp_arm-engelif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")thenif [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]thenselection=${LUNCH_MENU_CHOICES[$(($answer-1))]}fielif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")thenselection=$answerfiif [ -z "$selection" ]thenechoecho "Invalid lunch combo: $answer"return 1fiexport TARGET_BUILD_APPS=local product=$(echo -n $selection | sed -e "s/-.*$//")check_product $productif [ $? -ne 0 ]thenechoecho "** Don't have a product spec for: '$product'"echo "** Do you have the right repo manifest?"product=filocal variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")check_variant $variantif [ $? -ne 0 ]thenechoecho "** Invalid variant: '$variant'"echo "** Must be one of ${VARIANT_CHOICES[@]}"variant=fiif [ -z "$product" -o -z "$variant" ]thenechoreturn 1fiexport TARGET_PRODUCT=$productexport TARGET_BUILD_VARIANT=$variantexport TARGET_BUILD_TYPE=releaseechoset_stuff_for_environmentprintconfig
}
这个函数首先判断有没有传入参数,如果传入了就把值赋给answer这个变量,如果没有传参数,也就是说知识执行了lunch,那么就会调用fprint_lunch_menu函数显示一份菜单出来,显示出来后,接受用户的输入。

print_lunch_menu函数如下:

function print_lunch_menu()
{local uname=$(uname)echoecho "You're building on" $unameechoecho "Lunch menu... pick a combo:"local i=1local choicefor choice in ${LUNCH_MENU_CHOICES[@]}doecho "     $i. $choice"i=$(($i+1))doneecho
}
可以看到,这个函数输出了一些头信息以后,就输出LUNCH_MENU_CHOICES数组,这里通过for遍历整个这个数组,打印每一项。这个数组在之前就已经分析过,它的每一项是通过调用add_lunch_combo函数添加进来的。

1.不管执行lunch的时候有没有传入参数,最终都会将选择的结果存入到answer变量中,那么接下来,当然是检查输入的参数的合法性了。这里首先判断answer变量是不是为0,如果为零的话,selection变量就会赋值为aosp_arm-eng,但是如果不为零的话,首先会输出answer的值,使用echo -n $answer,-n选项是的输出的时候不输出换行符,answer变量的值并没有输出到屏幕上,而是通过管道传给了后面一条命令:grep -q -e "^[0-9][0-9]*$",这条命令从answer变量中搜寻以两位数字开头的字符串,如果找到,就认为是输入的是数字。然后进一步对这个数字做有没有越界的检查。如果这个数字小于LUNCH_MENU_CHOICES的大小,就会把LUNCH_MENU_CHOICES的地$answer-1项复制给selection变量。

2.如果传入的不是数字,就会尝试匹配这样的字符串grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$",-q标示quiet模式,也就是不打印信息,-e表示后面跟的是一个用于匹配的模式,这里,^标示匹配开始,[]中的^则标示排除,\是转译字符,因为-可能是表示范围的符号。所以,这里匹配的是不以连续两个--开始,后面跟任意字符,然后必须有个-,最后又不以--结束的字符串。其实,这里就是期望得到product-varient的形式。也就是我们之前使用add_lunch_combo添加的那些字符串的格式。比如:aosp_arm-eng,product就是aosp_arm,varient就是eng.中间的-是必须的。如果发现符合要求的格式的话selection变量就会被$answer赋值,也就是说,selection其实就是一个product-varient模式的字符串。

3.如果既不是数字,又不是合法的字符串,或者是数字,这个时候,selection应该就没有被赋值过,这个时候-z就成立了,那么就会提示你输入的东西不对。并且直接返回。

如果输入合法,通过检测后,  export TARGET_BUILD_APPS=   这行导出了一个变量,但是它的值是空的。而之后的 local product=$(echo -n $selection | sed -e "s/-.*$//")这句则是得到了product部分,也就是把varient部分砍掉了。这里使用了sed编辑器,-e 表示执行多条命令,但这里只有一条,双引号的s表示替换,这里就是把-后面接任意字符,然后以任意字符结尾的部分替换为空,也就是砍掉-后面的了。得到produce后就开始检查product的合法性。 check_product $produc  ,这里使用了check_product函数:

# check to see if the supplied product is one we can build
function check_product()
{T=$(gettop)if [ ! "$T" ]; thenecho "Couldn't locate the top of the tree.  Try setting TOP." >&2returnfiTARGET_PRODUCT=$1 \TARGET_BUILD_VARIANT= \TARGET_BUILD_TYPE= \TARGET_BUILD_APPS= \get_build_var TARGET_DEVICE > /dev/null# hide successful answers, but allow the errors to show
}

函数的一开始就调用了gettop函数,所以,我们得先弄明白这个函数的功能:

function gettop
{local TOPFILE=build/core/envsetup.mkif [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then# The following circumlocution ensures we remove symlinks from TOP.(cd $TOP; PWD= /bin/pwd)elseif [ -f $TOPFILE ] ; then# The following circumlocution (repeated below as well) ensures# that we record the true directory name and not one that is# faked up with symlink names.PWD= /bin/pwdelselocal HERE=$PWDT=while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do\cd ..T=`PWD= /bin/pwd -P`done\cd $HEREif [ -f "$T/$TOPFILE" ]; thenecho $Tfififi
}

gettop函数一开始定义了一个局部变量TOPFILE,并且给他赋了值,然后是一个测试语句:if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then,这里-n 是判断 $TOP是否不为空, -a 就是and的意思,和C语言中的&&相同, -f是判断给定的变量是不是文件,那么,这个测试语句就是如果 $TOP不为空 切同时 $TOP/$TOPFILE文件存在,就执行下面的代码:

(cd $TOP; PWD= /bin/pwd)

也就是进入到$TOP目录下,并且给PWD变量赋一个pwd命令的返回值,也就是当前目录的路劲。我试着在这个脚本中搜索TOP变量,发现它并没有出现并且赋值,所以,这里应该执行else部分。else中,首先判断build/core/envsetup.mk这个文件是否存在,当在源码顶层目录下的时候,这个文件是存在的,那么这里为真,然后PWD变量就是android代码的根目录。所以如果souce build/envsetup.sh的时候,如果处于android源码的顶级目录,那么这个函数就返回了。关于shell函数的返回值问题,还需要留意一下,当一个函数没有返回任何内容的时候,默认返回的是最后一条命令的执行结果,也就是这里的/bin/pwd的结果。那当然就是android源码的顶级目录了。这个时候如果不在顶级目录,build/core/envsetup.mk应该不存在,这个时候就会while循环不断的进入道上层目录,然后判断$TOPFILE是否存在,并且判断是否到达根目录了,如果这个文件不存在且没有到达根目录,那么就会一个往上一级目录查找。最终如果找到了这个文件,就意味着找到了android源码的顶层目录,并把这个路劲返回。前面的两次判断如果都成立的话也没有返回任何东西,是因为,当前目录肯定就是源码的顶级目录了。也就是说,这个函数就是找到源码的顶级目录,如果当前目录就是顶级目录,就什么也不返回,如果当前目录不是顶级目录,就返回顶级目录的路劲。
再回过头来看check_product函数,可以看到在获取到android源码的顶级目录以后,就会判断这个T是不是空值,空的的话就说明没有获取到顶级目录,这个时候这个函数就直接返回了。如果一切正常,那么就会定义几个变量。

        TARGET_PRODUCT=$1 \
        TARGET_BUILD_VARIANT= \
        TARGET_BUILD_TYPE= \
        TARGET_BUILD_APPS= \

这几个变量只有一个变量都是全局变量,因为没有加local关键字修饰,它们中,只有第一个变量赋值为$1,也就是这个函数的第一个参数。目前,这几个变量的作用还不得而知,所以,我们继续向下分析。

这个函数还有最后一件事情要做:

get_build_var TARGET_DEVICE > /dev/null

这里调用了get_build_var这个函数,这个函数如下:

# Get the exact value of a build variable.
function get_build_var()
{T=$(gettop)if [ ! "$T" ]; thenecho "Couldn't locate the top of the tree.  Try setting TOP." >&2returnfi(\cd $T; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \command make --no-print-directory -f build/core/config.mk dumpvar-$1)
}
乍看之下,这个函数非常简单,一开始,就是获得android源码的顶层目录,然后检查是否获得成功,这在之前已经分析过了。做完这些以后,迎来了一个看着很奇怪的语句。这个语句先是进入到android源码的顶层目录,然后然后定义了两个变量,之后使用command执行了一条命令。command是一个shell中的命令,它的功能是执行指定的命令,
这里就是执行make命令,-f给它指定了makefile,同时还传入了一个目标。然后,config.mk就会执行。

这个函数,我们不妨感性认识一下先:在命令行中执行get_build_var TARGET_DEVICE

可以看到打印了generic这个值。这个函数虽然分析起来比较复杂,但是它做的非常简单。config.mk会include dumpvar.mk,这个文件中会提取我们传入的dumpvar-TARGET_DEVICE变量中的TARGET_DEVICE,然后打印$(TARGET_DEVICE)。所以它做的事情很简单。

这个函数执行完以后,有返回到lunch函数继续执行:

    if [ $? -ne 0 ]thenechoecho "** Don't have a product spec for: '$product'"echo "** Do you have the right repo manifest?"product=fi
这里就是判断这个函数的返回值,-ne是不等于的意思,如果返回值不等于0,那么就出问题了。

假定一切正常,继续执行代码:

    local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")check_variant $variantif [ $? -ne 0 ]thenechoecho "** Invalid variant: '$variant'"echo "** Must be one of ${VARIANT_CHOICES[@]}"variant=fi
这段代码时提取variant并且检查是否合法,不要忘了前面说过,add_lunch_combo添加的字符串就是product-variant格式的字符串,这两部分代码非常相似,就不具体分析这段代码了。

下面的代码是:

    if [ -z "$product" -o -z "$variant" ]thenechoreturn 1fi
检查得到的product和variant是不是为空,空就不可以 了。

    export TARGET_PRODUCT=$productexport TARGET_BUILD_VARIANT=$variantexport TARGET_BUILD_TYPE=release
然后把辛辛苦苦得到的product和variant变量复制给全局变量并且导出为环境变量。以后看到这几个变量,知道它们的值就可以了。

然后调用了set_stuff_for_environment函数,这个函数内容如下:

function set_stuff_for_environment()
{settitleset_java_homesetpathsset_sequence_numberexport ANDROID_BUILD_TOP=$(gettop)# With this environment variable new GCC can apply colors to warnings/errorsexport GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'export ASAN_OPTIONS=detect_leaks=0
}

这个函数会继续补充一些变量。其中set_java_home会检查导出JAVA_HOME这个环境变量,这个环境变量就是JDK坐在的路劲,setpaths函数会给PATH环境变量补充编译Android需要的一些路径。

最后lunch调用了 printconfig函数,这个函数打印出了配置信息。

至此,source build/envsetup.sh 和 lunch就分析完了。接下来将分析make 命令所做的事情。

小结:source build/envsetup.sh会调用add_lunch_combo函数添加很多单板信息进来,同时还会查找/device和/vendor下的vendorsetup.sh文件,查找深度为4级目录,找到后就执行它,它里面至少会有这么一行:add_lunch_combo xxxx,继续添加单板信息。lunch函数则会打印出所有的单板信息供你选择,你输入选择后,luch命令会对你的选择做一系列检测,并从中提取出product和varient,并最终导出这些信息,供正式编译的时候使用。



http://chatgpt.dhexx.cn/article/lc6Ra2Xv.shtml

相关文章

Lesson 2 Breakfast or lunch? 早餐还是午餐?

1.原文 2. 参考译文 3. New words and expressions ★until prep.直到 until用于表示动作、状态等的持续&#xff0c;可译为“一直到……为止”或“在……以前”。在肯定句中&#xff0c;它与表示持续性状态的动词连用&#xff0c;表示持续到某一时刻&#xff1a; I’ll wait…

Android 10 添加 lunch

需求&#xff08;当然这只是其中一个&#xff09;&#xff1a;多个产品用同一个核心板&#xff0c;外设驱动不一样&#xff0c;设备树不一样&#xff0c;开机画面等不一样&#xff0c;如果不添加&#xff0c;就会每次要生成哪个板子就覆盖对应的文件&#xff0c;麻烦不说还容易…

没有免费午餐定理No Free Lunch Theorem

不得不说&#xff0c;网上博客千千万&#xff0c;在技术方面&#xff0c;我承认这些博客的重要性。然而&#xff0c;只要和机器学习理论挂钩&#xff0c;似乎都讲得不清不楚&#xff0c;大家都是各自地抄&#xff0c;抄书籍&#xff0c;抄论文&#xff0c;抄别人的博客或者直接…

没有免费午餐定理(No Free Lunch Theorem)

当我们拿到数据之后&#xff0c;构建机器学习算法的第一步应当是&#xff1a;观察数据&#xff0c;总结规律。 目前由于大数据和深度学习的发展&#xff0c;很多人会认为&#xff0c;只要收集足够多的数据&#xff0c;从网上的开源算法模型中随便找一个&#xff0c;直接将数据丢…

[TIST 2022] No Free Lunch Theorem for Security and Utility in Federated Learning

联邦学习中的安全性和实用性没有免费午餐定理 No Free Lunch Theorem for Security and Utility in Federated Learning 目录 摘要简介2 相关文献2.1 隐私测量2.2 联邦学习2.2.1 FL 中的威胁模型。2.2.2 FL 中的保护机制。 2.3 隐私-实用权衡 3 一般设置和框架3.1 符号3.2 一般…

Andriod中如何新建lunch项

Andriod编译过程一般为&#xff1a; 1.source build/envsetup.sh //加载命令&#xff0c;在项目根目录下&#xff08;~/purple/code/a/A_code20211126/sdm660&#xff09;目录 备注&#xff1a;在envsetup.sh里将执行vendor和device目录及各自子目录下所有的vendorsetup.sh&a…

VS中创建自定义控件

第一步&#xff1a;创建一个ASP.NET WEB应用程序 第二步&#xff1a;在同一个解决方案中创建一个服务控件项目 2.1 再次创建一个asp.net web应用程序。如图&#xff1a; 2.2 然后在这个项目下创建一个Web窗体服务器控件 第三步&#xff1a;编辑为我想要的控件 在这个我这个…

C#自定义控件的设计与调用

在C#下建立自己的控件库&#xff0c;需用到自定义控件的设计与调用。 一、自定义控件的设计 自定义控件&#xff0c;步骤如下&#xff1a; 1.点击文件&#xff0d;&#xff1e;新建项目&#xff0d;&#xff1e;选择Windows控件库2.编辑控件3.点击生成&#xff0d;&#xff1…

树形控件

一&#xff0e;分析过程 1.今天就来说说树形控件&#xff0c;什么是树形控件呢&#xff1f;树形控件在Windows系统中是很常见的&#xff0c;例如资源管理器左侧的窗口中就有用来显示目录的树形视图。 树形视图中以分层结构显示数据&#xff0c;每层的缩进不同&#xff0c;层次越…

WPF基本控件简介

默认可见的基本控件有 1、Border 设置控件画边框&#xff0c;2、Button 按钮 3、Calendar 日历 4、Canvas 画布控件 5、Checkbox 复选框 6、Combobox 下拉列表框 7、ContentControl 内容控件 8、DataGrid 显示表格数据 9、DataPicker 日期选择控件&#xff0c;带日历 10、Dock…

labview自定义控件

创建自定义输入控件、显示控件和自定义类型 目录 LabVIEW 2011帮助 版本日期&#xff1a;June 2011 产品编号&#xff1a;371361H-0118 查看产品信息 下载帮助&#xff08;仅限Windows&#xff09; 自定义输入控件和显示控件是对现有前面板对象集的扩展。用户可创建外观与内置L…

Excel 2010 VBA 入门 124 日期选择控件

目录 码 DTPicker控件 DTPicker控件的时间和日期的切换 DTPicker控件的日期输入方式 DTPicker控件的Value属性与Change事件 使用DTPicker控件实现日期选择并赋值给单元格 注册DTPicker控件 在Excel中&#xff0c;经常需要输入日期。为保证输入正确&#xff0c;可以通过一…

vue日期控件

<el-form-item label"有效期限" ><el-col :span"6"><el-form-item><el-date-pickertype"date"placeholder"选择日期"value-format"yyyy-MM-dd 00:00:00"v-model"effectiveStartTime":picker…

QT 布局,控件自适应大小 自动缩放 自动布局

目录 前言 1. 先来说简单的布局控件自适应 说明我们实现了自动布局&#xff1b; 3.通过代码设置控件自动缩放重写resizeEvent 4. 源码&#xff1a;https://upload.csdn.net/creation/uploadResources/86620882 前言 QT版本&#xff1a;Qt5.12.3(msvc2017_64) 有时&#xf…

WindowsFormsHost控件

WPF和WinForms是两个不同的UI框架&#xff0c;都是由Microsoft创建的。 WPF是WinForms的一个更现代的替代品&#xff0c;WinForms是第一个.NET UI框架。 为了在两者之间轻松过渡&#xff0c;Microsoft确保WinForms控件仍然可以在WPF应用程序中使用。 这是通过WindowsFormsHost完…

C#添加第三方控件

C#添加第三方控件 第三方控件操作步骤 在项目开发时&#xff0c;C#自带的控件可能无法满足项目需求&#xff0c;需要引入第三方控件&#xff0c;本文主要介绍在VS2019上如何导入第三方控件。 第三方控件 第三方控件指自定义的控件或者用户控件&#xff0c;它们继承自.NET类库…

qt自定义控件

文章目录 前言一、自定义控件需要的准备二、自定义控件步骤1.创建自定义插件2.添加带ui的类&#xff0c;删当前生成的.h和.cpp&#xff0c;重新添加qt带ui的类。3.编辑自定义控件数据4.使用和运行 总结 前言 如何自定义控件 一、自定义控件需要的准备 QT大多数采用MSVC编译&a…

C#自定义控件VS用户控件

C#自定义控件VS用户控件 1、C#中自定义控件VS用户控件大比拼2、为自定义控件&#xff08;或类&#xff09;的方法属性添加注解2.1、Description&#xff1a;在属性窗口中添加属性及属性说明2.2、Browsable2.3、EditorBrowsable2.4、Category2.5、ToolboxBitmap2.6、DefaultEven…

C# 自定义控件

一 自定义控件 1 自定义控件的三种方式&#xff1a; 1&#xff09;复合控件&#xff1a;将标准控件组合起来 class YourControl:UserControl{}2) 扩展控件&#xff1a;继承于标准控件 class YourControl:Button{}3) 自定义控件&#xff1a;完全地自定义一个控件 class You…

C#窗体控件简介

C#窗体控件简介-选项卡控件 在Windows 应用程序中&#xff0c;选项卡用于将相关的控件集中在一起&#xff0c;放在一个页面中用以显示多种综合信息。选项卡控件通常用于显示多个选项卡&#xff0c;其中每个选项卡均可包含图片和其他控件。选项卡相当于多窗体控件&#xff0c;可…