#ansible版本说明:ansible1.9.1

1.简单使用例子

  1. # -*- coding=utf-8 -*-
  2. import ansible.runner
  3. ##########################
  4. runner = ansible.runner.Runner(
  5. host_list = 'ip.txt', #指定主机文件
  6. remote_user = 'admin', #指定远程执行用户
  7. module_name = 'shell', #使用ansible的shell
  8. module_args = 'echo 111;sleep 5', #模块参数
  9. pattern = 'test', #主机文件中生效的组
  10. forks = 5, #多进程并发数量
  11. remote_pass = '', #远程执行的密码
  12. #is_playbook= True,
  13. )
  14. datastructure = runner.run()
  15. print datastructure

ip.txt

  1. [test]
  2. 127.0.0.1

2.ansible.runner模块中的Runner类

(1)初始化函数学习

  1. # -*- coding=utf-8 -*-
  2.  
  3. class Runner(object):
  4. ''' core API interface to ansible '''
  5.  
  6. # see bin/ansible for how this is used...
  7.  
  8. def __init__(self,
  9. host_list=C.DEFAULT_HOST_LIST, # ex: /etc/ansible/hosts, legacy usage  #初始化默认参数,如果没指定走/etc/ansible/hosts文件
  10. module_path=None, # ex: /usr/share/ansible         #Ansible的路径一般不用写
  11. module_name=C.DEFAULT_MODULE_NAME, # ex: copy                  #模块名字必须指定
  12. module_args=C.DEFAULT_MODULE_ARGS, # ex: "src=/tmp/a dest=/tmp/b" #模块的参数,对应了ansible命令行中的-a后面带的内容
  13. forks=C.DEFAULT_FORKS, # parallelism level
  14.      #进程的数目,如果你填入了20,它会判断你的list_hosts里面是否有20个IP,没有的话根据主机的数量来派生进程,如果多的话,就用multiprocess的pool进程池来调用
  15.  
  16. timeout=C.DEFAULT_TIMEOUT, # SSH timeout #SSH的超时时间
  17. pattern=C.DEFAULT_PATTERN, # which hosts? ex: 'all', 'acme.example.org'#指定hostfile文件中执行的分组
  18. remote_user=C.DEFAULT_REMOTE_USER, # ex: 'username' #远程执行的用户
  19. remote_pass=C.DEFAULT_REMOTE_PASS, # ex: 'password123' or None if using key #远程执行的密码
  20. remote_port=None, # if SSH on different ports #远程执行的端口,如果ssh端口被变更过的话
  21. private_key_file=C.DEFAULT_PRIVATE_KEY_FILE, # if not using keys/passwords #自钥地址,用来秘钥验证的
  22. background=0, # async poll every X seconds, else 0 for non-async #0代表不异步,其余数代表多少秒后根据任务id去取数据
  23. basedir=None, # directory of playbook, if applicable #指定playbook的存放目录
  24. setup_cache=None, # used to share fact data w/ other tasks #用于与其他任务共享实时数据
  25. vars_cache=None, # used to store variables about hosts #存储有关主机的变量
  26. transport=C.DEFAULT_TRANSPORT, # 'ssh', 'paramiko', 'local' #3种传输模式,ssh,paramiko,local
  27. conditional='True', # run only if this fact expression evals to true #状态的判断,
  28. callbacks=None, # used for output #指定回调输出
  29. module_vars=None, # a playbooks internals thing #playbooks内部的东西
  30. play_vars=None, # #和playbook相关的变量
  31. play_file_vars=None, #
  32. role_vars=None, #
  33. role_params=None, #
  34. default_vars=None, # #默认变量
  35. extra_vars=None, # extra vars specified with he playbook(s)#playbook指定额外的变量
  36. is_playbook=False, # running from playbook or not? #是否以playbook运行
  37. inventory=None, # reference to Inventory object #引用inventory对象
  38. subset=None, # subset pattern #子集模式
  39. check=False, # don't make any changes, just try to probe for potential changes #不做任何改变,仅仅尝试
  40. diff=False, # whether to show diffs for template files that change #是否显示更改的模板文件差异
  41. environment=None, # environment variables (as dict) to use inside the command #环境变量以字典方式传递进来
  42. complex_args=None, # structured data in addition to module_args, must be a dict #结构化数据,参数
  43. error_on_undefined_vars=C.DEFAULT_UNDEFINED_VAR_BEHAVIOR, # ex. False
  44. accelerate=False, # use accelerated connection #使用加速ssh连接
  45. accelerate_ipv6=False, # accelerated connection w/ IPv6
  46. accelerate_port=None, # port to use with accelerated connection #使用加速连接使用的端口
  47. vault_pass=None,
  48. run_hosts=None, # an optional list of pre-calculated hosts to run on #要运行的预计算的主机列表
  49. no_log=False, # option to enable/disable logging for a given task #是否开启任务日志
  50. run_once=False, # option to enable/disable host bypass loop for a given task
  51. become=False, # whether to run privelege escalation or not #是否运行特权升级sudo
  52. become_method=C.DEFAULT_BECOME_METHOD,
  53. become_user=C.DEFAULT_BECOME_USER, # ex: 'root' #sudo用户
  54. become_pass=C.DEFAULT_BECOME_PASS, # ex: 'password123' or None #sudo密码
  55. become_exe=C.DEFAULT_BECOME_EXE, # ex: /usr/local/bin/sudo #sudo命令行
  56. ):
  57.  
  58. # used to lock multiprocess inputs and outputs at various levels
  59. self.output_lockfile = OUTPUT_LOCKFILE #输出文件锁
  60. self.process_lockfile = PROCESS_LOCKFILE #进程锁
  61.  
  62. if not complex_args: #为空的话初始化为空字典
  63. complex_args = {}
  64.  
  65. # storage & defaults
  66. self.check = check
  67. self.diff = diff
  68. self.setup_cache = utils.default(setup_cache, lambda: ansible.cache.FactCache())
  69. self.vars_cache = utils.default(vars_cache, lambda: collections.defaultdict(dict))
  70. self.basedir = utils.default(basedir, lambda: os.getcwd())
  71. self.callbacks = utils.default(callbacks, lambda: DefaultRunnerCallbacks())
  72. self.generated_jid = str(random.randint(0, 999999999999))
  73. self.transport = transport
  74. self.inventory = utils.default(inventory, lambda: ansible.inventory.Inventory(host_list))
  75. # utils.default详细见下方的备注执行lambda函数,self.inventory就是Inventory对象,初始化通过host_list来执行,这个对象下包含了主机信息以及各种变量
  76. self.module_vars = utils.default(module_vars, lambda: {})
  77. self.play_vars = utils.default(play_vars, lambda: {})
  78. self.play_file_vars = utils.default(play_file_vars, lambda: {})
  79. self.role_vars = utils.default(role_vars, lambda: {})
  80. self.role_params = utils.default(role_params, lambda: {})
  81. self.default_vars = utils.default(default_vars, lambda: {})
  82. self.extra_vars = utils.default(extra_vars, lambda: {})
  83.  
  84. self.always_run = None
  85. self.connector = connection.Connector(self)
  86. self.conditional = conditional
  87. self.delegate_to = None
  88. self.module_name = module_name
  89. self.forks = int(forks)
  90. self.pattern = pattern
  91. self.module_args = module_args
  92. self.timeout = timeout
  93. self.remote_user = remote_user
  94. self.remote_pass = remote_pass
  95. self.remote_port = remote_port
  96. self.private_key_file = private_key_file
  97. self.background = background
  98. self.become = become
  99. self.become_method = become_method
  100. self.become_user_var = become_user
  101. self.become_user = None
  102. self.become_pass = become_pass
  103. self.become_exe = become_exe
  104. self.is_playbook = is_playbook
  105. self.environment = environment
  106. self.complex_args = complex_args
  107. self.error_on_undefined_vars = error_on_undefined_vars
  108. self.accelerate = accelerate
  109. self.accelerate_port = accelerate_port
  110. self.accelerate_ipv6 = accelerate_ipv6
  111. self.callbacks.runner = self
  112. self.omit_token = '__omit_place_holder__%s' % sha1(os.urandom(64)).hexdigest()
  113. self.vault_pass = vault_pass
  114. self.no_log = no_log
  115. self.run_once = run_once
  116.  
  117. if self.transport == 'smart':
  118.        # 判断传输模式,确定最后是否使用paramiko,ansible1.2.1/1.3版本里面有smart的东西,前向兼容
  119. # If the transport is 'smart', check to see if certain conditions
  120. # would prevent us from using ssh, and fallback to paramiko.
  121. # 'smart' is the default since 1.2.1/1.3
  122. self.transport = "ssh"
  123.        #判断系统平台和远程执行密码来确定传输模式
  124.  
  125. if sys.platform.startswith('darwin') and self.remote_pass:
  126. # due to a current bug in sshpass on OSX, which can trigger
  127. # a kernel panic even for non-privileged users, we revert to
  128. # paramiko on that OS when a SSH password is specified
  129. self.transport = "paramiko"
  130. else:
  131. # see if SSH can support ControlPersist if not use paramiko #执行命令ssh -o ControlPersist加速模式,确定传输模式
  132. cmd = subprocess.Popen(['ssh','-o','ControlPersist'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  133. (out, err) = cmd.communicate()
  134. if "Bad configuration option" in err:
  135. self.transport = "paramiko"
  136.  
  137. # save the original transport, in case it gets
  138. # changed later via options like accelerate
  139. self.original_transport = self.transport
  140.  
  141. # misc housekeeping
  142. if subset and self.inventory._subset is None:
  143. # don't override subset when passed from playbook
  144. self.inventory.subset(subset)
  145.  
  146. # If we get a pre-built list of hosts to run on, from say a playbook, use them.
  147. # Also where we will store the hosts to run on once discovered
  148. self.run_hosts = run_hosts
  149.  
  150. if self.transport == 'local':
  151.        #传输模式本地的话,pwd和os模块联合方法取出当前系统执行用户,linux下的方法
  152. self.remote_user = pwd.getpwuid(os.geteuid())[0]
  153. #模块路径指定的话,切割模块路径
  154. if module_path is not None:
  155. for i in module_path.split(os.pathsep):
  156. utils.plugins.module_finder.add_directory(i)
  157.  
  158. utils.plugins.push_basedir(self.basedir)
  159.  
  160. # ensure we are using unique tmp paths
  161. # 初始化基本随机数生成器
  162. random.seed()
  163. # *****************************************************

备注:utils.default

  1. def default(value, function):
  2. ''' syntactic sugar around lazy evaluation of defaults '''
  3. if value is None:
  4. return function()
  5. return value

(2) ansible.inventory.Inventory(host_list)原理剖析(假设都按照最上面的例子来传递,则host_list=ip.txt)

ansible/inventory/__init__.py

  1. class Inventory(object):
  2. """
  3. Host inventory for ansible.
  4. """
  5.  
  6. __slots__ = [ 'host_list', 'groups', '_restriction', '_also_restriction', '_subset',
  7. 'parser', '_vars_per_host', '_vars_per_group', '_hosts_cache', '_groups_list',
  8. '_pattern_cache', '_vault_password', '_vars_plugins', '_playbook_basedir']
  9.  
  10. def __init__(self, host_list=C.DEFAULT_HOST_LIST, vault_password=None):
  11.  
  12. # the host file file, or script path, or list of hosts
  13. # if a list, inventory data will NOT be loaded
  14. self.host_list = host_list
  15. # 文件名传递进去
  16. self._vault_password=vault_password
  17.  
  18. # caching to avoid repeated calculations, particularly with
  19. # external inventory scripts.
  20.  
  21. self._vars_per_host = {}
  22. self._vars_per_group = {}
  23. self._hosts_cache = {}
  24. self._groups_list = {}
  25. self._pattern_cache = {}
  26.  
  27. # to be set by calling set_playbook_basedir by playbook code
  28. self._playbook_basedir = None
  29.  
  30. # the inventory object holds a list of groups
  31. self.groups = []
  32.  
  33. # a list of host(names) to contain current inquiries to
  34. self._restriction = None
  35. self._also_restriction = None
  36. self._subset = None
  37. # 判断host_list是不是字符串类型
  38. if isinstance(host_list, basestring):
  39. if "," in host_list:
  40. host_list = host_list.split(",")
  41. host_list = [ h for h in host_list if h and h.strip() ]
  42. if host_list is None:
  43. self.parser = None
  44. elif isinstance(host_list, list):
  45. self.parser = None
  46. all = Group('all')
  47. self.groups = [ all ]
  48. ipv6_re = re.compile('\[([a-f:A-F0-9]*[%[0-z]+]?)\](?::(\d+))?')
  49. for x in host_list:
  50. m = ipv6_re.match(x)
  51. if m:
  52. all.add_host(Host(m.groups()[0], m.groups()[1]))
  53. else:
  54. if ":" in x:
  55. tokens = x.rsplit(":", 1)
  56. # if there is ':' in the address, then this is an ipv6
  57. if ':' in tokens[0]:
  58. all.add_host(Host(x))
  59. else:
  60. all.add_host(Host(tokens[0], tokens[1]))
  61. else:
  62. all.add_host(Host(x))
  63. # 判断host_list文件是否存在,将IP筛选出来
  64. elif os.path.exists(host_list):
  65. if os.path.isdir(host_list):
  66. # Ensure basedir is inside the directory判断是否是目录
  67. self.host_list = os.path.join(self.host_list, "")
  68. self.parser = InventoryDirectory(filename=host_list)
  69. self.groups = self.parser.groups.values()
  70. else:
  71. # check to see if the specified file starts with a 如果是文件的话执行如下流程
  72. # shebang (#!/), so if an error is raised by the parser
  73. # class we can show a more apropos error
  74. shebang_present = False
  75. try:
  76. inv_file = open(host_list)
  77. first_line = inv_file.readlines()[0]
  78. inv_file.close()
  79. if first_line.startswith('#!'):
  80. shebang_present = True
    #判断文件第一行是不是shebang
  81. except:
  82. pass
  83. #实际的逻辑执行
  84. if utils.is_executable(host_list):
  85. try:
  86. self.parser = InventoryScript(filename=host_list)
  87. self.groups = self.parser.groups.values()
  88. except:
  89. if not shebang_present:
  90. raise errors.AnsibleError("The file %s is marked as executable, but failed to execute correctly. " % host_list + \
  91. "If this is not supposed to be an executable script, correct this with `chmod -x %s`." % host_list)
  92. else:
  93. raise
  94. else:
  95. try:
  96. self.parser = InventoryParser(filename=host_list)
  97. self.groups = self.parser.groups.values()
  98. except:
  99. if shebang_present:
  100. raise errors.AnsibleError("The file %s looks like it should be an executable inventory script, but is not marked executable. " % host_list + \
  101. "Perhaps you want to correct this with `chmod +x %s`?" % host_list)
  102. else:
  103. raise
  104.  
  105. utils.plugins.vars_loader.add_directory(self.basedir(), with_subdir=True)
  106. else:
  107. raise errors.AnsibleError("Unable to find an inventory file, specify one with -i ?")
  108. # 获取插件变量
  109. self._vars_plugins = [ x for x in utils.plugins.vars_loader.all(self) ]
  110.  
  111. # get group vars from group_vars/ files and vars plugins获取组变量
  112. for group in self.groups:
  113. group.vars = utils.combine_vars(group.vars, self.get_group_variables(group.name, vault_password=self._vault_password))
  114.  
  115. # get host vars from host_vars/ files and vars plugins获取主机变量
  116. for host in self.get_hosts():
  117. host.vars = utils.combine_vars(host.vars, self.get_host_variables(host.name, vault_password=self._vault_password))

(3)ansible/inventory/script.py

  1. class InventoryScript(object):
  2. ''' Host inventory parser for ansible using external inventory scripts. '''
  3.  
  4. def __init__(self, filename=C.DEFAULT_HOST_LIST):
  5.  
  6. # Support inventory scripts that are not prefixed with some
  7. # path information but happen to be in the current working
  8. # directory when '.' is not in PATH.
  9. self.filename = os.path.abspath(filename)
  10. cmd = [ self.filename, "--list" ]
  11. try:
  12. # 检测主机文件是否存在
  13. sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  14. except OSError, e:
  15. raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e))
  16. (stdout, stderr) = sp.communicate()
  17.  
  18. if sp.returncode != 0:
  19. raise errors.AnsibleError("Inventory script (%s) had an execution error: %s " % (filename,stderr))
  20.  
  21. self.data = stdout
  22. # see comment about _meta below
  23. self.host_vars_from_top = None
  24. # 错误信息处理
  25. self.groups = self._parse(stderr)
  26.  
  27. def _parse(self, err):
  28.  
  29. all_hosts = {}
  30.  
  31. # not passing from_remote because data from CMDB is trusted
  32. self.raw = utils.parse_json(self.data)
  33. self.raw = json_dict_bytes_to_unicode(self.raw)
  34.  
  35. all = Group('all')
  36. groups = dict(all=all)
  37. group = None
  38.  
  39. if 'failed' in self.raw:
  40. sys.stderr.write(err + "\n")
  41. raise errors.AnsibleError("failed to parse executable inventory script results: %s" % self.raw)
  42.  
  43. for (group_name, data) in self.raw.items():
  44.  
  45. # in Ansible 1.3 and later, a "_meta" subelement may contain
  46. # a variable "hostvars" which contains a hash for each host
  47. # if this "hostvars" exists at all then do not call --host for each
  48. # host. This is for efficiency and scripts should still return data
  49. # if called with --host for backwards compat with 1.2 and earlier.
  50.  
  51. if group_name == '_meta':
  52. if 'hostvars' in data:
  53. self.host_vars_from_top = data['hostvars']
  54. continue
  55.  
  56. if group_name != all.name:
  57. group = groups[group_name] = Group(group_name)
  58. else:
  59. group = all
  60. host = None
  61.  
  62. if not isinstance(data, dict):
  63. data = {'hosts': data}
  64. # is not those subkeys, then simplified syntax, host with vars
  65. elif not any(k in data for k in ('hosts','vars','children')):
  66. data = {'hosts': [group_name], 'vars': data}
  67.  
  68. if 'hosts' in data:
  69. if not isinstance(data['hosts'], list):
  70. raise errors.AnsibleError("You defined a group \"%s\" with bad "
  71. "data for the host list:\n %s" % (group_name, data))
  72.  
  73. for hostname in data['hosts']:
  74. if not hostname in all_hosts:
  75. all_hosts[hostname] = Host(hostname)
  76. host = all_hosts[hostname]
  77. group.add_host(host)
  78.  
  79. if 'vars' in data:
  80. if not isinstance(data['vars'], dict):
  81. raise errors.AnsibleError("You defined a group \"%s\" with bad "
  82. "data for variables:\n %s" % (group_name, data))
  83.  
  84. for k, v in data['vars'].iteritems():
  85. if group.name == all.name:
  86. all.set_variable(k, v)
  87. else:
  88. group.set_variable(k, v)
  89.  
  90. # Separate loop to ensure all groups are defined
  91. for (group_name, data) in self.raw.items():
  92. if group_name == '_meta':
  93. continue
  94. if isinstance(data, dict) and 'children' in data:
  95. for child_name in data['children']:
  96. if child_name in groups:
  97. groups[group_name].add_child_group(groups[child_name])
  98.  
  99. for group in groups.values():
  100. if group.depth == 0 and group.name != 'all':
  101. all.add_child_group(group)
  102.  
  103. return groups

总归一句话就是,通过以上代码片段,json化输出IP列表

(4)run函数,实际执行

  1. def run(self):
  2. ''' xfer & run module on all matched hosts '''
  3.  
  4. # find hosts that match the pattern
  5. # 通过pattern找到host_list中的主机信息
  6. if not self.run_hosts:
  7. self.run_hosts = self.inventory.list_hosts(self.pattern)
  8. # 通过self.pattern找到list_hosts的json字段,获取内容
  9. hosts = self.run_hosts
  10. # 如果主机数量是0的话,callback回调,内容是空字典
  11. if len(hosts) == 0:
  12. self.callbacks.on_no_hosts()
  13. return dict(contacted={}, dark={})
  14. # 把实例赋值给全局变量multiprocessing_runner
  15. global multiprocessing_runner
  16. multiprocessing_runner = self
  17. results = None
  18.  
  19. # Check if this is an action plugin. Some of them are designed
  20. # to be ran once per group of hosts. Example module: pause,
  21. # run once per hostgroup, rather than pausing once per each
  22. # host.
         # p呢就是根据module_name找出要执行的相应模块插件
  23. p = utils.plugins.action_loader.get(self.module_name, self)

  24.      # 进程数量优化
  25. if self.forks == 0 or self.forks > len(hosts):
  26. self.forks = len(hosts)
  27.  
  28. if (p and (getattr(p, 'BYPASS_HOST_LOOP', None)) or self.run_once):
  29.  
  30. # Expose the current hostgroup to the bypassing plugins
  31. self.host_set = hosts
  32. # We aren't iterating over all the hosts in this
  33. # group. So, just choose the "delegate_to" host if that is defined and is
  34. # one of the targeted hosts, otherwise pick the first host in our group to
  35. # construct the conn object with.
  36. if self.delegate_to is not None and self.delegate_to in hosts:
  37. host = self.delegate_to
  38. else:
  39. host = hosts[0]
  40.  
  41. result_data = self._executor(host, None).result
  42. # Create a ResultData item for each host in this group
  43. # using the returned result. If we didn't do this we would
  44. # get false reports of dark hosts.
  45. results = [ ReturnData(host=h, result=result_data, comm_ok=True) \
  46. for h in hosts ]
  47. del self.host_set
  48.  
  49. elif self.forks > 1:
  50. try:
              # 调用_parallel_exec函数去跑结果
  51. results = self._parallel_exec(hosts)
  52. except IOError, ie:
  53. print ie.errno
  54. if ie.errno == 32:
  55. # broken pipe from Ctrl+C
  56. raise errors.AnsibleError("interrupted")
  57. raise
  58. else:
  59. results = [ self._executor(h, None) for h in hosts ]
  60.  
  61. return self._partition_results(results)

(5)_parallel_exec函数

  1. def _parallel_exec(self, hosts):
  2. ''' handles mulitprocessing when more than 1 fork is required '''
  3.  
  4. manager = multiprocessing.Manager()
  5. job_queue = manager.Queue()
  6. #任务队列
  7. for host in hosts:
  8. job_queue.put(host)
  9. #结果队列
  10. result_queue = manager.Queue()
  11.  
  12. try:
  13. fileno = sys.stdin.fileno()
  14. except ValueError:
  15. fileno = None
  16.  
  17. workers = []
  18. '''起forks进程数的进程去执行_executor_hook函数,函数的参数是任务队列,结果队列,以及new_stdin'''
  19. for i in range(self.forks):
  20. new_stdin = None
  21. if fileno is not None:
  22. try:
  23. new_stdin = os.fdopen(os.dup(fileno))
  24. except OSError, e:
  25. # couldn't dupe stdin, most likely because it's
  26. # not a valid file descriptor, so we just rely on
  27. # using the one that was passed in
  28. pass
  29. prc = multiprocessing.Process(target=_executor_hook,
  30. args=(job_queue, result_queue, new_stdin))
  31. prc.start()
  32. workers.append(prc)
           #把每个进程放到workers列表里
  33.  
  34. try:
            #遍历workers列表中的每个多进程实例,join方法呢是等待每个进程执行结束。保证多进程每个进程都执行结束,如果出现异常,进程中断再结束
  35. for worker in workers:
  36. worker.join()
  37. except KeyboardInterrupt:
  38. for worker in workers:
  39. worker.terminate()
  40. worker.join()
  41.     #结果集列表
  42. results = []
  43. try:
           #结果队列不为空的话,不断从中取数据追加到结果result中
  44. while not result_queue.empty():
  45. results.append(result_queue.get(block=False))
  46. except socket.error:
  47. raise errors.AnsibleError("<interrupted>")
  48. return results

(6)_executor_hook函数

  1. def _executor_hook(job_queue, result_queue, new_stdin):
  2.  
  3. # attempt workaround of https://github.com/newsapps/beeswithmachineguns/issues/17
  4. # this function also not present in CentOS 6
  5. if HAS_ATFORK:
  6. atfork()
  7.  
  8. signal.signal(signal.SIGINT, signal.SIG_IGN)
  9. while not job_queue.empty():
  10. try:
  11. host = job_queue.get(block=False)
  12. return_data = multiprocessing_runner._executor(host, new_stdin)
  13. result_queue.put(return_data)
  14. except Queue.Empty:
  15. pass
  16. except:
  17. traceback.print_exc()

AnsibleAPI源码剖析(1)-Runner类的 初始化的更多相关文章

  1. ChartCtrl源码剖析之——CChartObject类

    首先,做一些简单的铺垫,目前针对ChartCtrl源码的剖析只针对V.15版本.名义上说是剖析,倒不如说是记录下自己针对该控件的理解,非常感谢Cedric Moonen大神,一切的功劳与掌声都该赠予给 ...

  2. ChartCtrl源码剖析之——CChartScrollBar类

    CChartScrollBar类用来针对每个轴的数据进行滚动,将那些不在当前区域内的数据通过滚动展示出来. CChartScrollBar类的头文件. #pragma once class CChar ...

  3. ChartCtrl源码剖析之——CChartAxis类

    CChartAxis类用来绘制波形控件的坐标轴,这个源码相对较复杂,当初阅读的时候耗费了不少精力来理解源码中的一些实现细节. CChartAxis类的头文件. #if !defined(AFX_CHA ...

  4. ChartCtrl源码剖析之——CChartTitle类

    CChartTitle类顾名思义,该类用来绘制波形控件的标题,它处于该控件的区域,如下图所示: CChartTitle类的头文件. #if !defined(AFX_CHARTTITLE_H__499 ...

  5. ChartCtrl源码剖析之——CChartGrid类

    CChartGrid类用来绘制波形区域中的表格,当绘制波形时波形就显示在这些表格上面.它处于该控件的区域,如下图所示: CChartGrid类的头文件. #if !defined(AFX_CHARTG ...

  6. ChartCtrl源码剖析之——CChartAxisLabel类

    CChartAxisLabel类用来绘制轴标签,上.下.左.右都可以根据实际需要设置对应的轴标签.它处于该控件的区域,如下图所示: CChartAxisLabel类的头文件. #if !defined ...

  7. ChartCtrl源码剖析之——CChartLegend类

    CChartLegend类用来绘制每一个波形的描述信息,它处于该控件的区域,如下图所示: CChartLegend类的头文件. #if !defined(AFX_CHARTLEGEND_H__CD72 ...

  8. PART(Persistent Adaptive Radix Tree)的Java实现源码剖析

    论文地址 Adaptive Radix Tree: https://db.in.tum.de/~leis/papers/ART.pdf Persistent Adaptive Radix Tree: ...

  9. 老李推荐:第6章3节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令翻译类

    老李推荐:第6章3节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令翻译类   每个来自网络的字串命令都需要进行解析执行,只是有些是在解析的过程中直接执行 ...

随机推荐

  1. linkin大话面向对象--内部类

    内部类说白了就是类中有类 内部类:嵌套类 外部类:宿主类 内部类主要有以下作用:记住了3个字:多继承... 1,内部类提供了更好的封装,可以把内部类隐藏在外部类中,不允许同一个包中的其他类访问该类. ...

  2. C# TCP 了解

    参考:http://www.jb51.net/article/118682.htm 一: TCP 粘包原理:发送方发送若干数据给接收方时粘成一包.从接收缓冲区看,后一包的头紧接前一包的数据的尾. 发送 ...

  3. 【转】sed命令n,N,d,D,p,P,h,H,g,G,x解析

    1. sed执行模板=sed '模式{命令1;命令2}' 即逐行读入模式空间,执行命令,最后输出打印出来 2. 为方便下面,先说下p和P,p打印当前模式空间内容,追加到默认输出之后,P打印当前模式空间 ...

  4. LANMP系列教程之php编译安装CentOS7环境

    前提:必须先安装好MySQL以及Apache   1.准备好源码包并配置好yum源,需要的源码包包括: libmcrypt-2.5.8-9.el6.x86_64.rpm libmcrypt-devel ...

  5. RMI基础篇

    远程方法调用(Remote Method Invocation,RMI)从JDK1.1就已经实现,它大大增强了Java开发分布式应用的能力. RMI可以实现通过网络完成不同JVM间的通信,不仅可以传递 ...

  6. 洛谷 [p1196] 银河英雄传说

    所谓带权并查集 本题所求的不止是两个编号之间是否有关系,还要求两个编号之间有什么关系,这就要求我们维护多个数组,fa[]数组维护两个编号之间的连通性,dis[]维护编号为i的战舰到fa[i]之间的距离 ...

  7. UOJ 241. 【UR #16】破坏发射台 [矩阵乘法]

    UOJ 241. [UR #16]破坏发射台 题意:长度为 n 的环,每个点染色,有 m 种颜色,要求相邻相对不能同色,求方案数.(定义两个点相对为去掉这两个点后环能被分成相同大小的两段) 只想到一个 ...

  8. BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 466[Submit][Statu ...

  9. [Python Study Notes]CS架构远程访问获取信息--SERVER端

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...

  10. docker cs50 ide 安装

    ECS上搭建Docker(CentOS7): https://help.aliyun.com/document_detail/51853.html docker官方文档: https://docs.d ...