qt移植解惑

Qt是一个多平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的很容易扩展,并且 允许真正地组件编程。 (一)首先我们来讲下我们安装qt的原因: 我们平常在编写程序用的最多的是什么? 也许你会答些代码,这个当然是,但是有一个很重要的东西是 使用#include 我们为什么使用include,因为我们想使用printf,fork,malloc等这些函数,其实说白了就是使用别人写好的东西,一减少自己的代码量,这就是库的作用。其实我们安装qt其实就是安装库及头文件。当然如果我们想要更好的开发环境,就会希望有像vc那种可视化的开发工具,这些都是qt安装时生成,这也是为什么需要安装qt的原因。 (二)下面就进一步讲下一些概念:首先让我们来了解下一些概念: qt-x11 qt-embeded qtopia qt由于是跨平台的,故其能够在linux,window,mac等操作系统下运行。既然是跨平台的,它就要将GUI分层,分成和平台相关的,及同平台无关的,所以当我们要安装qt时,就要针对具体平台下载具体平台的qt. 在Linux,由于Linux广泛使用的是xwindow协议,所以qt-x11也要依赖xwindowx,这样当我们要在linuxpc上安装qt时,就要有xlib的想关库,可能也也许会说ubuntu的桌面是gnome,而gnome是基于gtk,而gtk又是在glib上扩展,glib又是xlib封装,那ubuntu应该就有xlib的相关库了吧,非也,glib是glib,xlib是xlib,他们的接口都不一样,你让qt在呢么使用xwindow的相关东西,也许你还会说那为啥qt不直接使用glib,而是使用xlib,其实这个很好理解,因为qt其实是和gtk等价的东西,你让他 gtk使用一样的继承关系glib,那未免不好,再说xlib是最基本的基础,用他可以更好的实现跨平台,否则,就算是在Linux下,如果一个用gtk,一个用xwindox,那么qt都要进行改动。 Qt-embeded大家光看单词就可看出是指嵌入式qt,其实说是嵌入式,也说大了,只能说是主要针对具有framebuffer的嵌入式操作系统,如果是其他操作系统,你要修改的东西就多了去了,那还不若使用小一点的gui,像minigui等,既然提到framebuffer,大家也查到了,如果再缩小范围,其实这个所谓的嵌入式就是指嵌入式中的linux,他为什么要分出个pc linux及嵌入式linux版本,主要是因为嵌入式系统中资源有限,使用framebuffer更能有效使用资源,且开发简单很多,既然使用的是framebuffer,它也就没使用xlib了,因为xwindow当初就是为了适用一台服务器,多个终端,然后通过客户窗口通过网络同服务器窗口通信并被管理,这个在嵌入式系统中根本用不上,所以就没使用xlib,当然可能还有很多其他原因,但是此版本自己实现的窗口管理系统还是使用c/s模式。 Qtopia又是什么呢?qtopia其实可以看成是qt-embedded的扩展,他主要是实现了一个桌面,使得应用程序员开发更简单,同时也增加了一些组件。如果你使用qt开发一个桌面系统,你就要写一个应用程序,这个应用程序要实现像任务栏,桌面图标,ime像输入法顶层窗口,及应用管理的这些功能。而这些在qtopia中,他已经帮你实现了,你要做得是开发具体的应用程序,这样就为我们减少了很大的任务量。(三)下面就进入qt安装。 (1)首先安装x11。 刚才说了x11版本使用的是xlib底层窗体机制,只要我们将x11编译完了,我们就可以直接些程序,运行。 PC所用 Linux系统版本:Ubuntu8.04 首先下载: Qt/Embedded版本:qt- embedded-linux-opensource-src-4.4.3 Qt/X11版本:qt-x11-opensource-src-4.4.3 然后解压: tar zxvf qt-x11-opensource-src-4.4.3 -C ~/opt/qt cd ~/opt/qt 刚才说到在linux pc上的qt是基于xlib所以要保证xlib库的存在,如果不存在使用sudo apt-get install libx11-dev 下载xlib库,同时我在编译的过程中也发现还需要xext库,也将他下载sudo apt-get install libxext-dev反正是在编译的过程中,发现什么错误,就看下,如果是缺少库,下载就是了。然后配置。由于是编译在pc上运行的库,配置非常简单 1 ./configure -prefix ~/opt/run/qt 2 make 3 make install 第1条命令是做啥用的呢,他其实就是生成makefile文件,及生成用来编译qt源代码用的一些工具,象qmake,qmake是用来解释.pro文件,然后生成makefile文件的,其实他的作用像automake,就是自动生成makefile文件。 第二条命令就是生成lib,及其他工具像designer,uic,等工具的 make install就是将lib,tool,doc复制到-prefix指定的文件夹。 2 make过程非常长,大概1-2小时,此时可以看看网页或出去玩下。然后我们就可以测试了。首先我们要添加环境变量,我们新建一个文件用来添加这些环境变量。 jimmy@jimmy-linux:~/opt/run/qt$ cat setenv.sh export QTDIR=$HOME/opt/run/qt //使的qmake生成makefile知道include,lib的 路径,同时还有mkspecs规则 export QTDEDIR=$QTDIR export PATH=$QTDIR/bin:$PATH //这使得qmake,uci等工具能够直接使用,而不用进入: ~/opt/run/qte export LD_LIBRARY_PAHT=$QTDIR/lib:$LD_LIBRARY_PAHT //这个使得加载程序是能够正确找到qt库 jimmy@jimmy-linux:~/opt/run/qt$ source setenv.sh //source的作用是使得上面的设置生效。上面的注释不在setenv.sh文件里,这里只是为了说明。然后自己写个简单的程序。 1.#include 2 #include 3 int main( int argc, char **argv ) 4 { 5 QApplication a( argc, argv ); 6 QPushButton hello( "Hello world!", 0 ); 7 hello.resize( 100, 30 ); 9 hello.show(); 10 return a.exec(); 11 } jimmy@jimmy-linux:~/test/cppd$ ls testb test.cpp jimmy@jimmy-linux:~/test/cppd$ qmake -project jimmy@jimmy-linux:~/test/cppd$ ls cppd.pro testb test.cpp jimmy@jimmy-linux:~/test/cppd$ qmake jimmy@jimmy-linux:~/test/cppd$ ls cppd.pro Makefile testb test.cpp jimmy@jimmy-linux:~/test/cppd$ make g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I../../opt/run/qt/mkspecs/linux-g++ -I. -I../../opt/run/qt/include/QtCore -I../../opt/run/qt/include/QtCore -I../../opt/run/qt/include/QtGui -I../../opt/run/qt/include/QtGui -I../../opt/run/qt/include -I. -I. -I. -o test.o test.cpp g++ -Wl,-rpath,/home/jimmy/opt/run/qt/lib -o cppd test.o -L/home/jimmy/opt/run/qt/lib -lQtGui -L/home/jimmy/opt/run/qt/lib -L/usr/X11R6/lib -lXext -lX11 -lQtCore -lm -lrt -ldl -lpthread jimmy@jimmy-linux:~/test/cppd$ ls cppd cppd.pro Makefile testb test.cpp test.o jimmy@jimmy-linux:~/test/cppd$ .cppd [img]http://blogimg.chinaunix.net/blog/upfile2/090513182315.jpg[/img] 然后编译: qt-embedded版本 过程一样:只是configure要变我们首先编译一个在仿framebuffer的版本,即生成的代码是针对pc的,用qvfb来模仿framebuffer,达到在宿机上开发目标机的程序并调试,这样当在宿机上运行成功时,再用交叉编译工具重新编译即可。 .configure -prefix ~/run/qte -embedded x86 -qvfb 这样这个版本的qt就是以qvfb作为自己的显示设备。 然后make,make install 同样我么调试,由于这次我们要是使用qt/embedded,所以就要使用qt/embedded的库,其实只需修改 QTDIR等环境变量即可。 jimmy@jimmy-linux:~/opt/run/qte$ cat setenv.sh export QTDIR=$HOME/opt/run/qte //使的qmake生成makefile知道include,lib的 路径,同时还有mkspecs规则 export QTDEDIR=$QTDIR export PATH=$QTDIR/bin:$PATH //这使得qmake,uci等工具能够直接使用,而不用进入 ~/opt/run/qte export LD_LIBRARY_PAHT=$QTDIR/lib:$LD_LIBRARY_PAHT //这个使得加载程序是能够正确找到qt库 jimmy@jimmy-linux:~/opt/run/qte$ source setenv.sh //source的作用是使得上面的设置生效。 x11时上面的目录是qt,现在是qte了。然后我们再进入test目录,编译test.cpp jimmy@jimmy-linux:~/test/cppd$ make clean rm -f test.o rm -f *~ core *.core jimmy@jimmy-linux:~/test/cppd$ make g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_SHARED -I../../opt/run/qte/mkspecs/qws/linux-x86-g++ -I. -I../../opt/run/qte/include/QtCore -I../../opt/run/qte/include/QtCore -I../../opt/run/qte/include/QtNetwork -I../../opt/run/qte/include/QtNetwork -I../../opt/run/qte/include/QtGui -I../../opt/run/qte/include/QtGui -I../../opt/run/qte/include -I. -I. -I. -o test.o test.cpp g++ -Wl,-rpath,/home/jimmy/opt/run/qte/lib -o cppd test.o -L/home/jimmy/opt/run/qte/lib -lQtGui -L/home/jimmy/opt/run/qte/lib -lQtNetwork -lQtCore -lm -lrt -ldl -lpthread jimmy@jimmy-linux:~/test/cppd$ ./cppd QWSSocket::connectToLocalFile could not connect:: 没有该文件或目录 QWSSocket::connectToLocalFile could not connect:: 没有该文件或目录 QWSSocket::connectToLocalFile could not connect:: 没有该文件或目录 QWSSocket::connectToLocalFile could not connect:: 没有该文件或目录 QWSSocket::connectToLocalFile could not connect:: 没有该文件或目录 jimmy@jimmy-linux:~/test/cppd$ 这时运行./cppd出错了,为什么,这就要提到我们前面说的,embedded版本下的qt也是采用c/s模式,所以必须要有server,由于没有server就出现了上面的错误,这时可能会问那为什么x11版本下没有server就可以运行呢。其实x11版本下也是有server,因为我们的pc linux gnome桌面系统也是基于xwindow的,所以在启动时就启动xwindow的服务器,这个就是X,正因为有了这个Xserver,x11版本下执行./cppd就可以了。 jimmy@jimmy-linux:~$ ps -aux |grep X Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html root 5902 5.2 4.9 109168 100060 tty7 Rs+ 08:50 3:56 /usr/bin/X :0 -br -audit 0 -auth /var/lib/gdm/:0.Xauth -nolisten tcp vt7 jimmy 9182 0.0 0.0 3224 796 pts/5 S+ 10:05 0:00 grep X jimmy@jimmy-linux:~$ 如果此时你sudo kill 5902,你将发现整个窗口程序没用了。那这个问题怎么解决呢,很简单,./cppd -qws,加个-qws就可以,这个是什么意思,这个就是说告诉qt,这个应用程序同时会作为server,所以会开启一个server,其源码实现是在 QApplication a( argc, argv ); 构建qapplication是创建的,当construct发现参数有qws时就会开启一个server线程运行qwserver类的实例,并初始化。让我们来看下代码实现,由于argv只传给了qapplication,应该可以看出在qapplication的构造函数里对argv进行了识别,事实上确实如此。 QApplication::QApplication( int &argc, char **argv ) { construct( argc, argv, GuiClient ); } void QApplication::construct( int &argc, char **argv, Type type ) { qt_appType = type; qt_is_gui_used = (type != Tty); init_precmdline(); static char *empty = (char*)""; if ( argc == 0 || argv == 0 ) { argc = 0; argv = ? } qt_init( &argc, argv, type ); // Must be called before initialize() process_cmdline( &argc, argv ); #if defined(QT_THREAD_SUPPORT) qt_mutex = new QMutex(TRUE); #endif initialize( argc, argv ); } void qt_init( int *argcptr, char **argv, QApplication::Type type ) { ................... else if ( arg == "-qws" ) { type = Qapplication::GuiServer; ................... if ( type == QApplication::GuiServer ) { qt_appType = type; qws_single_process = TRUE; QWSServer::startup(flags);//这个就会创建server setenv("QWS_DISPLAY", qws_display_spec, 0); } } extern QWSServer *qwsServer; //there can be only one 现在让我们来看下。当然由于我们要使用qvfb仿真framebuffer,当然就要开启qvfb 这个时候就看到。 [img]http://blogimg.chinaunix.net/blog/upfile2/090513182334.jpg[/img] 还有一点要注意的是:qt4不再有setmainwidget函数了。 下面是交叉版本的编译: 其实交叉编译qte是差不多的,只是 ./configure参数不一样: ./configure -prefix /home/jimmy/opt/run/qt-arm -release -shared -fast -pch -no-qt3support -qt-sql-sqlite -no-libtiff -no-libmng -qt-libjpeg -qt-zlib -qt-libpng -qt-freetype -no-openssl -nomake examples -nomake demos -nomake tools -optimized-qmake -no-phonon -no-nis -no-opengl -no-cups -no-xcursor -no-xfixes -no-xrandr -no-xrender -no-xkb -no-sm -no-xinerama -no-xshape -no-separate-debug-info -xplatform qws/linux-arm-g++ -embedded arm -depths 16 -no-qvfb -qt-gfx-linuxfb -no-gfx-qvfb -no-kbd-qvfb -no-mouse-qvfb -qt-kbd-usb -confirm-license 后面一大堆参数,其实就是为了尽量减少库的大小,而剪裁了一些不需要的功能。 然后make make install 然后就是应用程序的交叉编译。 这个时候要首先设置环境变量,这样 1 export QTDIR=$HOME/opt/run/qt-arm 2 export QTEDIR=$QTDIR 3 export PATH=$QTDIR/bin:$QPEDIR/bin:$PATH 4 export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH 然后到新建一个文件,main.cpp qmake -project qmake make 即了其实在第二步隐藏了一个细节,qmake 等价于qmake -spec default,这个spec参数就是到mkspecs文件夹找配置文件。 jimmy@jimmy-linux:~/opt/run/qt-arm$ ls bin include lib mkspecs plugins setenv.sh tmake translations jimmy@jimmy-linux:~/opt/run/qt-arm$ cd mkspecs jimmy@jimmy-linux:~/opt/run/qt-arm/mkspecs$ ls -l 总用量 336 drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 aix-g++ drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 aix-g++-64 drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 aix-xlc drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 aix-xlc-64 drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 common drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 cygwin-g++ drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 darwin-g++ lrwxrwxrwx 1 jimmy jimmy 17 2009-05-05 15:28 default -> qws/linux-arm-g++ drwxr-xr-x 5 jimmy jimmy 4096 2009-05-05 15:28 features drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 freebsd-g++ 也就是说default其实是使用linux-arm-g++ jimmy@jimmy-linux:~/opt/run/qt-arm/mkspecs$ cd qws/linux-arm-g++/ jimmy@jimmy-linux:~/opt/run/qt-arm/mkspecs/qws/linux-arm-g++$ ls qmake.conf qplatformdefs.h jimmy@jimmy-linux:~/opt/run/qt-arm/mkspecs/qws/linux-arm-g++$ cat qmake.conf # # qmake configuration for linux-g++ using the arm-linux-g++ crosscompiler # MAKEFILE_GENERATOR = UNIX TEMPLATE = app CONFIG += qt warn_on release link_prl QT += core gui network QMAKE_INCREMENTAL_STYLE = sublib QMAKE_CC = arm-linux-gcc QMAKE_LEX = flex QMAKE_LEXFLAGS = QMAKE_YACC = yacc QMAKE_YACCFLAGS = -d QMAKE_CFLAGS = -pipe QMAKE_CFLAGS_WARN_ON = -Wall -W QMAKE_CFLAGS_WARN_OFF = QMAKE_CFLAGS_RELEASE = -O2 QMAKE_CFLAGS_DEBUG = -g QMAKE_CFLAGS_SHLIB = -fPIC QMAKE_CFLAGS_YACC = -Wno-unused -Wno-parentheses QMAKE_CFLAGS_THREAD = -D_REENTRANT QMAKE_CFLAGS_HIDESYMS = -fvisibility=hidden QMAKE_CXX = arm-linux-g++ QMAKE_CXXFLAGS = $$QMAKE_CFLAGS QMAKE_CXXFLAGS_WARN_ON = $$QMAKE_CFLAGS_WARN_ON QMAKE_CXXFLAGS_WARN_OFF = $$QMAKE_CFLAGS_WARN_OFF QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE QMAKE_CXXFLAGS_DEBUG = $$QMAKE_CFLAGS_DEBUG QMAKE_CXXFLAGS_SHLIB = $$QMAKE_CFLAGS_SHLIB QMAKE_CXXFLAGS_YACC = $$QMAKE_CFLAGS_YACC QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD QMAKE_CXXFLAGS_HIDESYMS = $$QMAKE_CFLAGS_HIDESYMS -fvisibility-inlines-hidden QMAKE_INCDIR = QMAKE_LIBDIR = QMAKE_INCDIR_X11 = QMAKE_LIBDIR_X11 = QMAKE_INCDIR_QT = $$[QT_INSTALL_HEADERS] QMAKE_LIBDIR_QT = $$[QT_INSTALL_LIBS] QMAKE_INCDIR_OPENGL = QMAKE_LIBDIR_OPENGL = QMAKE_INCDIR_QTOPIA = $(QPEDIR)/include QMAKE_LIBDIR_QTOPIA = $(QPEDIR)/lib QMAKE_LINK = arm-linux-g++ QMAKE_LINK_SHLIB = arm-linux-g++ QMAKE_LFLAGS = QMAKE_LFLAGS_RELEASE = QMAKE_LFLAGS_DEBUG = QMAKE_LFLAGS_SHLIB = -shared QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB QMAKE_LFLAGS_SONAME = -Wl,-soname, QMAKE_LFLAGS_THREAD = QMAKE_RPATH = -Wl,-rpath, QMAKE_LIBS = QMAKE_LIBS_DYNLOAD = -ldl QMAKE_LIBS_X11 = QMAKE_LIBS_X11SM = QMAKE_LIBS_QT = -lqte QMAKE_LIBS_QT_THREAD = -lqte-mt QMAKE_LIBS_QT_OPENGL = -lqgl QMAKE_LIBS_QTOPIA = -lqpe -lqtopia QMAKE_LIBS_THREAD = -lpthread QMAKE_LIBS_OPENGL = QMAKE_MOC = $$[QT_INSTALL_BINS]/moc QMAKE_UIC = $$[QT_INSTALL_BINS]/uic QMAKE_AR = arm-linux-ar cqs QMAKE_OBJCOPY = arm-linux-objcopy QMAKE_RANLIB = arm-linux-ranlib QMAKE_TAR = tar -cf QMAKE_GZIP = gzip -9f QMAKE_COPY = cp -f QMAKE_MOVE = mv -f QMAKE_DEL_FILE = rm -f QMAKE_DEL_DIR = rmdir QMAKE_STRIP = arm-linux-strip QMAKE_CHK_DIR_EXISTS = test -d QMAKE_MKDIR = mkdir -p load(qt_config) jimmy@jimmy-linux:~/opt/run/qt-arm/mkspecs/qws/linux-arm-g++$ 由上面可以看出,其实spec就是一些makefile的参数,像编译器,库文件,这样达到交叉编译的作用。这样qmake就可利用这些信息生成交叉编译的makefile, 然后我们执行make即可生成针对arm的QT应用程序。下面来开心qtopia的原理:其实qtopia就是一个桌面qt应用程序,但是他扩展了一些类,所以,他也有库,头文件,然后生成的qte利用这些扩展的库实现。我们来看下qtopia的原理;既然他他也是应用程序,那他就有main函数,找到这个函数 int main( int argc, char ** argv ) { int retVal = initApplication( argc, argv ); if ( DesktopApplication::doRestart ) { qDebug("Trying to restart"); execl( (QPEApplication::qpeDir()+"bin//qpe").latin1(), "qpe", 0 ); } return retVal; } 很简单,其实就一个 initApplication函数 int initApplication( int argc, char ** argv ) { cleanup(); #ifdef QT_QWS_CASSIOPEIA initCassiopeia(); #endif #ifdef QPE_OWNAPM initAPM(); #endif #ifdef QT_DEMO_SINGLE_FLOPPY initFloppy(); #endif initEnvironment(); //Don't flicker at startup: #ifdef QWS QWSServer::setDesktopBackground( QImage() ); #endif ServerApplication a( argc, argv, QApplication::GuiServer );//这个就启动一个qwserver类,也 就有了服务器了 refreshTimeZoneConfig(); initBacklight(); initKeyboard(); // Don't use first use under Windows #ifdef Q_OS_UNIX if ( firstUse() ) { a.restart(); return 0; } #endif AlarmServer::initialize(); #if defined(QT_QWS_LOGIN) for( int i=0; i if( strcmp( a.argv()[i], "-login" ) == 0 ) { // No tr QDMDialogImpl::login( ); return 0; } #endif Server *s = new Server();//这个其实就是创建主窗口,桌面窗口,这个类是widget继承的 (void)new SysFileMonitor(s); #ifdef QWS Network::createServer(s); #endif s->show(); int rv = a.exec(); qDebug("exiting..."); delete s; return rv; } QWSServer::setDesktopBackground( QImage() ); 这个是静态函数,生成了一个image对象, 和qwsserver(指针,指向qserver对象)一样,其他文件以extern qwsserver来使用这些对象,保证了唯一性。网上有很多文章是:在./configure前就指定了 export QTDIR=$PWD/qt 26 export QPEDIR=$PWD/qtopia 29 export PATH=$QTDIR/bin:$QPEDIR/bin:$PATH 这让人产生错觉,好像./configure,make过程和上面的环境变量有关,其实没有关系,因为这个是后是生成Lib,所以不需要指定和qt有关的库,所以qtdir没有用, INCPATH = -I../../mkspecs/linux-g++ -I. -I../../include/QtCore -I../../include/QtCore -I../../include -I../../include/QtSql -I.rcc/release-shared -I.moc/release-shared -I.uic/release-shared LINK = g++ 且头文件文件夹都是用相对路径,所以就不需要这些变量。在应用程序的时候就不一样,因为这个时候是要使用这些变量,且应用程序和头文件夹的位置没有太大的关系,这样就只有用环境变量来指定,这个其实在qmake时,qmake就会使用这些变量来生成makefile 下面来说下使用designer编写qt程序。首先通过designer会产生一个.ui结尾的文件,假设是hello.ui 然后 uic -o test.h hello.ui 然后就会生产对应的类。 然后我们编写代码包含这个头文件,就相当于使用了这个类。 jimmy@jimmy-linux:~/qt/first$ cat hello.cpp #include #include #include "test.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); QDialog *window = new QDialog; Ui::Dialog *form = new Ui::Dialog; form->setupUi(window); window->show(); return app.exec(); } jimmy@jimmy-linux:~/qt/first$ Ui::Dialog;就是用designer生成由uic处理后生成的类,然后: qmake -project qmake make就可以了如果上面的qmake命令执行失败,那就是你的环境变量没有设置好。然后运行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值