作为一名linux系统下的C语言开发,日常工作中经常遇到两个问题:

  一是分析代码过程中,各种数据结构互相关联,只通过代码很难理清系统中所有结构体的整体架构,影响代码消化的效率;

  二是多层头文件嵌套包含,在新增需要被多处引用的结构体或者函数接口时,难以找到合适的地方放置结构体和函数接口的定义。

  为解决这两个问题,用python分别写了两个脚本:

  第一个脚本用于绘制关键数据结构的关联关系图,协助快速理解组织架构,加速理解代码逻辑;

  第二个脚本用于分析指定目录下的头文件包含关系,协助新增结构体或者函数接口时快速找到合适的放置位置;

  两个脚本绘图效果分别见下图1,图2。

图1.数据结构关联关系图

图2.头文件包含关系图(截取部分)

  以下代码是用于分析结构体关联关系的python脚本(analysis_data_struct.py),使用方法如下:

  1.在电脑上安装python和graphviz绘图工具(自行搜索,安装方法略);

  2.把需要绘制关系图的关键数据结构复制粘贴到一个文本文件中;

  3.把脚本中的保存数据结构文件路径(G:\git_repository\libreofficedraw\linux_4.18\plfc_struct )替换为自己的保存数据结构的文件路径(可自行修改脚本,通过参数传入文件路径);

  4.执行命令 python analysis_data_struct.py >tmpfile; dot -Tsvg tmpfile -o xxxx.svg; 其中第一条命令使用python分析数据结构并生成用于绘图的dot语言,第二条命令利用graphviz根据tmpfile中的dot语言描述绘图。图形保存到xxxx.svg文件中;可以使用浏览器打开。

  1. #!/usr/bin/python3
  2. import os,re
  3. prefix = '''digraph spdk {
  4. graph [
  5. rankdir = "LR"
  6. //splines=polyline
  7. //overlap=false
  8. ];
  9.  
  10. node [
  11. fontsize = "16"
  12. shape = "ellipse"\r
  13. ];
  14.  
  15. edge [
  16. ];
  17. '''
  18.  
  19. middle_str = ''
  20. edge_list = []
  21. edge_string = ''
  22. cur_indentation_level = 0
  23. space4 = ' '
  24. space8 = space4 + space4
  25. space12 = space4 + space8
  26. space16 = space4 + space12
  27. node_database = {}
  28. node_database['created'] = []
  29. color_arrary = ['red', 'green', 'blue', 'black','blueviolet','brown', 'cadetblue','chocolate','crimson','cyan','darkgrey','deeppink','darkred']
  30. with open(r'G:\git_repository\libreofficedraw\linux_4.18\plfc_struct', 'r') as file_input:
  31. tmpline = file_input.readline()
  32. while(tmpline):
  33. tmpline = re.sub(r'([^a-zA-Z0-9]const )', ' ', tmpline)
  34. #for match :struct device {
  35. if re.search(r'struct\s*([0-9a-zA-Z_\-]+)\s*\{', tmpline):
  36. m = re.search(r'struct\s*([0-9a-zA-Z_\-]+)\s*\{', tmpline)
  37. cur_indentation_level += 1
  38. if (cur_indentation_level == 1):
  39. node_name = m.group(1)
  40. node_str = space4 + '\"' + node_name + '\" [\n' + space8 + 'label = \"<head> '+ node_name +'\l|\n' + space12 + '{|{\n'
  41. node_database['created'].append(node_name)
  42. try:
  43. node_database[node_name]['node_str'] = node_str
  44. except:
  45. node_database[node_name] = {}
  46. node_database[node_name]['node_str'] = node_str
  47. #for match :struct device *parent;
  48. elif re.search(r'struct\s*([0-9a-zA-Z_\-]+)\s*(\**)(\s*)([0-9a-zA-Z_\-]+)\s*;', tmpline) and cur_indentation_level > 0:
  49. m = re.search(r'struct\s*([0-9a-zA-Z_\-]+)\s*(\**)(\s*)([0-9a-zA-Z_\-]+)\s*;', tmpline)
  50. member_type = m.group(1)
  51. node_database[node_name]['node_str'] += space16 + '<'+ member_type + '> ' + m.group(2) + m.group(3) + m.group(4) + '\l|\n'
  52. try:
  53. node_database[member_type]['included_by'].append(node_name)
  54. except:
  55. try:
  56. node_database[member_type]['included_by'] = []
  57. node_database[member_type]['included_by'].append(node_name)
  58. except:
  59. node_database[member_type] = {}
  60. node_database[member_type]['included_by'] = []
  61. node_database[member_type]['included_by'].append(node_name)
  62. #print('%s included by %s'%(member_type, node_database[member_type]['included_by']))
  63. if(member_type in node_database['created']):
  64. tmp_edge_str = space4 + node_name + ':' + member_type + ' -> ' + member_type + ':' + 'head'
  65. if not tmp_edge_str in edge_list:
  66. edge_list.append(tmp_edge_str)
  67. #for match : void *driver_data;
  68. elif re.search(r'\s*[0-9a-zA-Z_\-]+\s*(\**[0-9a-zA-Z_\-]+)\s*;', tmpline) and cur_indentation_level > 0:
  69. m = re.search(r'\s*[0-9a-zA-Z_\-]+\s*(\**[0-9a-zA-Z_\-]+)\s*;', tmpline)
  70. node_database[node_name]['node_str'] += space16 + '<'+ m.group(1) + '> ' + m.group(1) + '\l|\n'
  71. #for match:const char *init_name;
  72. elif re.search(r'(.*)\s+(\**)(\s*)([0-9a-zA-Z_\-]+\s*);', tmpline) and cur_indentation_level > 0:
  73. m = re.search(r'(.*)\s+(\**)(\s*)([0-9a-zA-Z_\-]+\s*);', tmpline)
  74. node_database[node_name]['node_str'] += space16 + '<'+ m.group(2) + '> ' + m.group(2) + m.group(3) + m.group(4) + '\l|\n'
  75. #for match:int *(*runtime_idle)(struct device *dev);
  76. elif re.search(r'\s*[0-9a-zA-Z_\-]+\s*\**\s*\(\s*(\**\s*[0-9a-zA-Z_\-]+)\s*\)\s*\([^\)]*\)\s*;', tmpline) and cur_indentation_level > 0:
  77. m = re.search(r'\s*[0-9a-zA-Z_\-]+\s*\**\s*\(\s*(\**\s*[0-9a-zA-Z_\-]+)\s*\)\s*\([^\)]*\)\s*;', tmpline)
  78. node_database[node_name]['node_str'] += space16 + '<'+ m.group(1) + '> (' + m.group(1) + ')\l|\n'
  79. #for match: };
  80. elif re.search(r'\s*\}\s*;', tmpline):
  81. if(cur_indentation_level >= 1):
  82. cur_indentation_level -= 1
  83. if (cur_indentation_level == 0):
  84. node_database[node_name]['node_str'] += space12 + '}}\"\n'
  85. node_database[node_name]['node_str'] += space8 + 'shape = \"record\"\n' + space4 + '];\n'
  86. if 'included_by' in node_database[node_name]:
  87. for parent_node in node_database[node_name]['included_by']:
  88. if parent_node in node_database['created']:
  89. tmp_edge_str = space4 + parent_node + ':' + node_name + ' -> ' + node_name + ':' + 'head'
  90. if not tmp_edge_str in edge_list:
  91. edge_list.append(tmp_edge_str)
  92. tmpline = file_input.readline()
  93.  
  94. for tmpnode in node_database['created']:
  95. middle_str = middle_str + node_database[tmpnode]['node_str']
  96. for i, tmpstr in enumerate(edge_list):
  97. edge_string += tmpstr + '[color="' + color_arrary[i%len(color_arrary)] + '"]\n'
  98.  
  99. print(prefix + middle_str + '\n' + edge_string + '}')

以下为记录数据结构的文本文件(plfc_struct),用于测试。

  1. struct bus_type {
  2. const char *name;
  3. const char *dev_name;
  4. struct device *dev_root;
  5. const struct attribute_group **bus_groups;
  6. const struct attribute_group **dev_groups;
  7. const struct attribute_group **drv_groups;
  8.  
  9. int (*match)(struct device *dev, struct device_driver *drv);
  10. int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
  11. int (*probe)(struct device *dev);
  12. int (*remove)(struct device *dev);
  13. void (*shutdown)(struct device *dev);
  14.  
  15. int (*online)(struct device *dev);
  16. int (*offline)(struct device *dev);
  17.  
  18. int (*suspend)(struct device *dev, pm_message_t state);
  19. int (*resume)(struct device *dev);
  20.  
  21. int (*num_vf)(struct device *dev);
  22.  
  23. int (*dma_configure)(struct device *dev);
  24.  
  25. const struct dev_pm_ops *pm;
  26.  
  27. const struct iommu_ops *iommu_ops;
  28.  
  29. struct subsys_private *p;
  30. struct lock_class_key lock_key;
  31.  
  32. bool need_parent_lock;
  33. };
  34.  
  35. struct pci_driver {
  36. struct list_head node;
  37. const char *name;
  38. const struct pci_device_id *id_table; /* Must be non-NULL for probe to be called */
  39. int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
  40. void (*remove)(struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
  41. int (*suspend)(struct pci_dev *dev, pm_message_t state); /* Device suspended */
  42. int (*suspend_late)(struct pci_dev *dev, pm_message_t state);
  43. int (*resume_early)(struct pci_dev *dev);
  44. int (*resume) (struct pci_dev *dev); /* Device woken up */
  45. void (*shutdown) (struct pci_dev *dev);
  46. int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* On PF */
  47. const struct pci_error_handlers *err_handler;
  48. const struct attribute_group **groups;
  49. struct device_driver driver;
  50. struct pci_dynids dynids;
  51. };
  52.  
  53. struct device_driver {
  54. const char *name;
  55. struct bus_type *bus;
  56.  
  57. struct module *owner;
  58. const char *mod_name; /* used for built-in modules */
  59.  
  60. bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
  61. enum probe_type probe_type;
  62.  
  63. const struct of_device_id *of_match_table;
  64. const struct acpi_device_id *acpi_match_table;
  65.  
  66. int (*probe) (struct device *dev);
  67. int (*remove) (struct device *dev);
  68. void (*shutdown) (struct device *dev);
  69. int (*suspend) (struct device *dev, pm_message_t state);
  70. int (*resume) (struct device *dev);
  71. const struct attribute_group **groups;
  72.  
  73. const struct dev_pm_ops *pm;
  74. void (*coredump) (struct device *dev);
  75.  
  76. struct driver_private *p;
  77. };
  78.  
  79. struct driver_private {
  80. struct kobject kobj;
  81. struct klist klist_devices;
  82. struct klist_node knode_bus;
  83. struct module_kobject *mkobj;
  84. struct device_driver *driver;
  85. };
  86.  
  87. struct kobject {
  88. const char *name;
  89. struct list_head entry;
  90. struct kobject *parent;
  91. struct kset *kset;
  92. struct kobj_type *ktype;
  93. struct kernfs_node *sd; /* sysfs directory entry */
  94. struct kref kref;
  95. #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
  96. struct delayed_work release;
  97. #endif
  98. unsigned int state_initialized:;
  99. unsigned int state_in_sysfs:;
  100. unsigned int state_add_uevent_sent:;
  101. unsigned int state_remove_uevent_sent:;
  102. unsigned int uevent_suppress:;
  103. };
  104.  
  105. struct subsys_private {
  106. struct kset subsys;
  107. struct kset *devices_kset;
  108. struct list_head interfaces;
  109. struct mutex mutex;
  110.  
  111. struct kset *drivers_kset;
  112. struct klist klist_devices;
  113. struct klist klist_drivers;
  114. struct blocking_notifier_head bus_notifier;
  115. unsigned int drivers_autoprobe:;
  116. struct bus_type *bus;
  117.  
  118. struct kset glue_dirs;
  119. struct class *class;
  120. };
  121. struct kset {
  122. struct list_head list;
  123. spinlock_t list_lock;
  124. struct kobject kobj;
  125. const struct kset_uevent_ops *uevent_ops;
  126. };
  127.  
  128. struct kobj_type {
  129. void (*release)(struct kobject *kobj);
  130. const struct sysfs_ops *sysfs_ops;
  131. struct attribute **default_attrs;
  132. const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
  133. const void *(*namespace)(struct kobject *kobj);
  134. };
  135.  
  136. struct sysfs_ops {
  137. ssize_t (*show)(struct kobject *, struct attribute *, char *);
  138. ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
  139. };
  140. struct dev_pm_ops {
  141. int (*prepare)(struct device *dev);
  142. void (*complete)(struct device *dev);
  143. int (*suspend)(struct device *dev);
  144. int (*resume)(struct device *dev);
  145. int (*freeze)(struct device *dev);
  146. int (*thaw)(struct device *dev);
  147. int (*poweroff)(struct device *dev);
  148. int (*restore)(struct device *dev);
  149. int (*suspend_late)(struct device *dev);
  150. };
  151.  
  152. struct kset_uevent_ops {
  153. int (* const filter)(xxxxxxx);
  154. const char *(* const name)(struct kset *kset, struct kobject *kobj);
  155. int (* const uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env);
  156. };
  157.  
  158. struct qla_init_msix_entry {
  159. const char *name;
  160. irq_handler_t handler;
  161. };
  162.  
  163. struct attribute_group {
  164. const char *name;
  165. umode_t (*is_visible)(struct kobject *, struct attribute *, int);
  166. umode_t (*is_bin_visible)(struct kobject *,struct bin_attribute *, int);
  167. struct attribute **attrs;
  168. struct bin_attribute **bin_attrs;
  169. };
  170.  
  171. struct bus_attribute {
  172. struct attribute attr;
  173. ssize_t (*show)(struct bus_type *bus, char *buf);
  174. ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
  175. };
  176.  
  177. struct klist {
  178. spinlock_t k_lock;
  179. struct list_head k_list;
  180. void (*get)(struct klist_node *);
  181. void (*put)(struct klist_node *);
  182. } ;
  183.  
  184. struct pci_device_id {
  185. __u32 vendor_device; /* Vendor and device ID or PCI_ANY_ID*/
  186. __u32 subvendor_subdevice; /* Subsystem ID's or PCI_ANY_ID */
  187. __u32 class_class_mask; /* (class,subclass,prog-if) triplet */
  188. kernel_ulong_t driver_data; /* Data private to the driver */
  189. };
  190.  
  191. struct device {
  192. struct device *parent;
  193.  
  194. struct device_private *p;
  195.  
  196. struct kobject kobj;
  197. const char *init_name; /* initial name of the device */
  198. const struct device_type *type;
  199.  
  200. struct mutex mutex; /* mutex to synchronize calls to
  201. * its driver.
  202. */
  203.  
  204. struct bus_type *bus; /* type of bus device is on */
  205. struct device_driver *driver; /* which driver has allocated this
  206. device */
  207. void *platform_data; /* Platform specific data, device
  208. core doesn't touch it */
  209. void *driver_data; /* Driver data, set and get with
  210. dev_set/get_drvdata */
  211. struct dev_links_info links;
  212. const struct attribute_group **groups; /* optional groups */
  213. void (*release)(struct device *dev);
  214. struct iommu_group *iommu_group;
  215. struct iommu_fwspec *iommu_fwspec;
  216. };
  217.  
  218. struct dev_links_info {
  219. struct list_head suppliers;
  220. struct list_head consumers;
  221. enum dl_dev_state status;
  222. };
  223.  
  224. struct pci_dynids {
  225. spinlock_t lock; /* Protects list, index */
  226. struct list_head list; /* For IDs added at runtime */
  227. };
  228.  
  229. ////////////////////以下结构体名称为全局变量,结构体成员为全局变量的类型,用于绘图时关联全局变量及其类型///////////////////////
  230. struct qla2xxx_pci_driver {
  231. struct pci_driver pci_driver;
  232. };
  233.  
  234. struct pci_bus_type {
  235. struct bus_type bus_type;
  236. };
  237.  
  238. struct driver_ktype {
  239. struct kobj_type kobj_type;
  240. };
  241.  
  242. struct driver_sysfs_ops {
  243. struct sysfs_ops sysfs_ops;
  244. };
  245. struct qla2xxx_pci_tbl{
  246. struct pci_device_id pci_device_id;
  247. };
  248. struct bus_kset {
  249. struct kset kset;
  250. };
  251. struct bus_uevent_ops {
  252. struct kset_uevent_ops kset_uevent_ops;
  253. };

  以下脚本(analysis_head_file.py)用于分析指定目录下头文件包含关系,使用方法如下:

  1.在电脑上安装python和graphviz绘图工具(自行搜索,安装方法略);

  2.把脚本中的代码路径(G:\git_repository\linux-stable\linux-4.18\drivers\net\wireless\broadcom)替换为需要分析的文件路径(可自行修改脚本,通过参数传入文件路径);

  3.执行命令 python analysis_head_file.py >tmpfile; dot -Tsvg tmpfile -o xxxx.svg; 其中第一条命令使用python分析数据结构并生成用于绘图的dot语言,第二条命令利用graphviz根据tmpfile中的dot语言描述绘图。图形保存到xxxx.svg文件中;可以使用浏览器打开。

  1. #!/usr/bin/python3
  2. import os,re
  3. prefix = '''digraph spdk {
  4. graph [
  5. rankdir = "LR"
  6. //splines=polyline
  7. overlap=false
  8. ];
  9.  
  10. node [
  11. fontsize = "16"
  12. shape = "ellipse"\r
  13. ];
  14.  
  15. edge [
  16. ];
  17. '''
  18.  
  19. def get_head_file_list(path_file):
  20. head_file_list = []
  21. with open(path_file, 'r') as file_input:
  22. tmpline = file_input.readline()
  23. while (tmpline):
  24. #to match #include < XXX/YYY.h >
  25. m = re.search(r'#include\s*[<\"]\s*(.*/)([0-9a-zA-Z_\-]*)\.[Hh]\s*[>\"]', tmpline)
  26. if m:
  27. head_file_list.append(re.sub(r'\-', '_', m.group(2)))
  28.  
  29. #to match #include < XXX.h >
  30. elif re.search(r'#include\s*[<\"]\s*([0-9a-zA-Z_\-]*)\.[Hh]\s*[>\"]', tmpline):
  31. m = re.search(r'#include\s*[<\"]\s*([0-9a-zA-Z_\-]*)\.[Hh]\s*[>\"]', tmpline)
  32. head_file_list.append(re.sub(r'\-', '_', m.group(1)))
  33. tmpline = file_input.readline()
  34. return head_file_list
  35.  
  36. def build_node_from_file(file_path, file_name, edges, included_by):
  37. i = 0
  38. space4 = ' '
  39. space8 = space4 + space4
  40. space12 = space4 + space8
  41. space16 = space4 + space12
  42. file_name_wo_h = re.search(r'([0-9a-zA-Z_\-]*)\.h', file_name).group(1)
  43. file_name_wo_h = re.sub(r'\-', '_',file_name_wo_h)
  44. #print(file_name_wo_h)
  45. node_str = space4 + '\"' + file_name_wo_h + '\" [\n' + space8 + 'label = \"<head> '+ file_name_wo_h +'.h\l|\n' + space12 + '{|{\n'
  46. headfilelist = ["aaa", "bbb"] #fake file list
  47. headfilelist2 = get_head_file_list(os.path.join(file_path, file_name))
  48. #print('headfilelist2:')
  49. #print(headfilelist2)
  50. for headfile in headfilelist2:
  51. i += 1
  52. try:
  53. included_by[headfile].append(file_name)
  54. except:
  55. included_by[headfile] = []
  56. included_by[headfile].append(file_name)
  57. node_str = node_str + space16 + '<'+ headfile + '> ' + headfile + '.h\l|\n'
  58. tmp_edge_str = space4 + file_name_wo_h + ':' + headfile + ' -> ' + headfile + ':' + 'head' #+ '[color="' + color_arrary[i%len(color_arrary)] + '"]\n'
  59. try:
  60. if not tmp_edge_str in edges[headfile]:
  61. edges[headfile].append(tmp_edge_str)
  62. except:
  63. edges[headfile] = []
  64. edges[headfile].append(tmp_edge_str)
  65. node_str = node_str + space12 + '}}\"\n'
  66. node_str = node_str + space8 + 'shape = \"record\"\n' + space4 + '];\n'
  67. #print(included_by)
  68. return {'node_str':node_str,'edges':edges}
  69.  
  70. edges = {}
  71. included_by = {}
  72. node_created = []
  73. middle_str = ''
  74. edge_string = ''
  75. color_arrary = ['red', 'green', 'blue', 'black','blueviolet','brown', 'cadetblue','chocolate','crimson','cyan','darkgrey','deeppink','darkred']
  76.  
  77. for maindir, subdir, file_name_list in os.walk(r'G:\git_repository\linux-stable\linux-4.18\drivers\net\wireless\broadcom'):#('G:\git_repository\linux-stable\linux-4.18\drivers\usb'):
  78. for tmpfile in file_name_list:
  79. if re.match(r'.*\.h', tmpfile):
  80. result = build_node_from_file(maindir, tmpfile, edges, included_by)
  81. node_created.append(re.search(r'([0-9a-zA-Z_\-]*)\.h', tmpfile).group(1))
  82. middle_str = middle_str + '\n' + result['node_str']
  83. edges = result['edges']
  84. ##print(filelist2)
  85. for tmpfile in edges:
  86. if tmpfile in node_created:
  87. for i,tmpstr in enumerate(edges[tmpfile]):
  88. edge_string += tmpstr + '[color="' + color_arrary[i%len(color_arrary)] + '"]\n'
  89. print(prefix + middle_str + '\n' + edge_string + '}')

利用python+graphviz绘制数据结构关系图和指定目录下头文件包含关系图的更多相关文章

  1. 利用Graphviz绘制逻辑关系依赖图

    说明:在很多情况下,需要将复杂且有些规律的代码整理成逻辑片段,这个时候就需要画图,很多时候图比代码更加直观 Graphviz是一个比较好的绘图工具,可以通过简单的代码绘制出复杂的逻辑图,且其代码就像平 ...

  2. c# 简易绘制C语言头文件包含关系图

    最近在做一个项目的移植工作,项目很大,光c文件大约有1800多.由于某些需要,想要对某些代码文件引用的.h文件进行分析. 网上找了好久,暂无发现类似的工具. 正好,今天放假,就做了这么个工具. 好了, ...

  3. 利用Python快速绘制海报级别地图

    1 简介 基于Python中诸如matplotlib等功能丰富.自由度极高的绘图库,我们可以完成各种极富艺术感的可视化作品,关于这一点我在系列文章在模仿中精进数据可视化中已经带大家学习过很多案例了. ...

  4. 利用python实现dll依赖关系导出

    #说明:遍历rootdir目录下所有dll,导出每个dll依赖的dll信息到dstdir目录下 # 配合NotePad++打开所有txt,搜索,可快速定位到某dll被依赖的所有dll文件 import ...

  5. c# 简易绘制C语言头文件包含关系图 v2.0

    老规矩,先上图 节点样式说明: 1.粉色圆角,说明该节点下有循环引用 2.黄色菱形,说明该节点代表的文件在项目目录下未找到. 3.红色圆角,说明循环引用(从开始到最终,这种感情没变过,没有谁..... ...

  6. 利用File类过滤器列出目录下的指定目录或文件

    需求:列出d盘下的全部txt文件 实现方法:利用File类的过滤器功能 package com.test.common.util; import java.io.File; import java.i ...

  7. python 实现统计ftp服务器指定目录下文件夹数目、文件数目及所有文件大小

    本次主要为满足应用方核对上传到ftp服务器的文件是否缺漏. 主要要求:指定目录下,文件夹数目/文件数目/所有文件大小,类似Windows如下功能: 模块介绍: from ftplib import F ...

  8. Python批量重命名指定目录下文件的两种方法

    #法一 import os path = "C://Python34//" for file in os.listdir(path): if os.path.isfile(os.p ...

  9. 《利用python进行数据分析》读书笔记 --第一、二章 准备与例子

    http://www.cnblogs.com/batteryhp/p/4868348.html 第一章 准备工作 今天开始码这本书--<利用python进行数据分析>.R和python都得 ...

随机推荐

  1. jquery多级树形下拉菜单

    效果图: 使用方法 (1)引入 jQuery 包,下载地址 (2)引入 zTree 包,下载地址 (3)引入 tree-select.js (4)$("#id").treeSele ...

  2. 16 (OC)* UIAnimation和CoreAnimation

    目录 一 Core Animation 二 核心动画 2.1 基础动画 2.2 关键帧动画 2.3 动画组 2.4 转场动画 2.5 逐帧动画 三 UIView动画封装 3.1 基础动画 3.2 弹簧 ...

  3. iOS渠道追踪统计方法大全

    说起 iOS 的渠道统计,不少人会想到苹果官方的 App 分析功能(iTunes Connect),但实际操作中我们会发现,这个服务的统计维度还不够全面,许多广告主和运营人员更关心的是各个推广渠道实际 ...

  4. oracle 11g 下载安装 使用记录

    Oracle 11g 使用记录 1.下载oracle快捷安装版:   (1)下载连接:https://pan.baidu.com/s/1ClC0hQepmTw2lSJ2ODtL7g 无提取码 (2)去 ...

  5. 实操:Could not autowire No beans of 'FastDFS Client' type found 的解决方法

    前言: 今天接手了同事之前做的一个小项目,里面涉及到了 FastDFS 的使用.但是当我在本地运行项目的时候,却报了 Could not autowire No beans of 'FastDFS C ...

  6. [C++] C++中的常用库

    转载自:C++常用库 C++ 资源大全 关于 C++ 框架.库和资源的一些汇总列表,内容包括:标准库.Web应用框架.人工智能.数据库.图片处理.机器学习.日志.代码分析等. 标准库 C++标准库,包 ...

  7. springboot项目启动报错 url' attribute is not specified and no embedded datasource could be configured

    报错相关信息: 2019-07-22 17:12:48.971 ERROR 8312 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : **** ...

  8. JQuery对于动态生成的标签绑定事件失效

    JQuery对整个html文档进行dom操作后,我们要想动态绑定事件,有两种方法 1.在进行dom操作时,在标签中写上onclick="afun()" 2.利用document的操 ...

  9. 不fq安装 golang tools

    go get -u -v github.com/golang/tools/go/buildutil ln -s $GOPATH/src/github.com/golang/tools $GOPATH/ ...

  10. OpenGL在ubuntu下的成功配置

    sudo apt-get update sudo apt-get install build-essential sudo apt-get install libgl1-mesa-dev sudo a ...