diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 3d6e3950d1..7eaf62e379 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -205,7 +205,7 @@ if(BUILD_DOCS AND DOXYGEN_FOUND) list(APPEND js_tutorials_assets_deps "${f}" "${opencv_tutorial_html_dir}/${fname}") endforeach() - add_custom_target(doxygen + add_custom_target(doxygen_cpp COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} DEPENDS ${doxyfile} ${rootfile} ${bibfile} ${deps} ${js_tutorials_assets_deps} ) @@ -214,6 +214,28 @@ if(BUILD_DOCS AND DOXYGEN_FOUND) COMPONENT "docs" OPTIONAL ) + if(BUILD_opencv_python2) + add_custom_target(doxygen_python + COMMAND ${PYTHON2_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tools/python_signatures.py" "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html/" + DEPENDS doxygen_cpp opencv_python2 + ) + add_custom_target(doxygen + DEPENDS doxygen_cpp doxygen_python + ) + elseif(BUILD_opencv_python3) + add_custom_target(doxygen_python + COMMAND ${PYTHON3_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tools/python_signatures.py" "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html/" + DEPENDS doxygen_cpp opencv_python3 + ) + add_custom_target(doxygen + DEPENDS doxygen_cpp doxygen_python + ) + else() + add_custom_target(doxygen + DEPENDS doxygen_cpp + ) + endif() + # Alias to build/install docs only add_custom_target(install_docs DEPENDS doxygen diff --git a/doc/tools/html_functions.py b/doc/tools/html_functions.py new file mode 100644 index 0000000000..bf9577bad4 --- /dev/null +++ b/doc/tools/html_functions.py @@ -0,0 +1,209 @@ +import logging +import os +import codecs +import cv2 + + +try: + from bs4 import BeautifulSoup +except ImportError: + raise ImportError('Error: ' + 'Install BeautifulSoup (bs4) for adding' + ' Python & Java signatures documentation') + + +def is_not_module_link(tmp_link): + """ Checks if a link belongs to a c++ method """ + if tmp_link is None: + return True + if "group" not in tmp_link: + return True + if "#" in tmp_link: + return True + return False + + +def get_links_list(tmp_soup, filter_links): + """ Get a list of links from a soup """ + tmp_href_list = [] + for tmp_link in tmp_soup.findAll('a'): + tmp_href = tmp_link.get('href') + if filter_links: + if is_not_module_link(tmp_href): + continue + tmp_href_list.append(tmp_href) + return tmp_href_list + + +def load_html_file(file_dir): + """ Uses BeautifulSoup to load an html """ + with open(file_dir) as fp: + tmp_soup = BeautifulSoup(fp, 'html.parser') + return tmp_soup + + +def add_item(tmp_soup, new_row, is_parameter, text): + """ Adds a new html tag for the table with the signature """ + new_item = tmp_soup.new_tag('td') + if is_parameter: + new_item = tmp_soup.new_tag('td', **{'class': 'paramname'}) + new_item.append(text) + new_row.append(new_item) + return new_row + + +def get_text_between_substrings(sig, begin_char, end_char): + return sig.partition(begin_char)[-1].rpartition(end_char)[0] + + +def add_signature_to_table(tmp_soup, new_row, signature, function_name, language, ident): + """ Add a signature to an html table""" + if ident: + new_item = tmp_soup.new_tag('td', style="padding-left: 0.5cm;") + else: + new_item = tmp_soup.new_tag('td') + + if "-> None" in signature: + pass + elif "->" in signature: + new_item.append(signature.split("->", 1)[1] + ' =') + new_row.append(new_item) + + if "Python" in language: + function_name = "cv2." + function_name + elif "Java" in language: + # get word before function_name (= output) + str_before_bracket = signature.split('(', 1)[0] + list_of_words = str_before_bracket.split() + output = list_of_words[len(list_of_words) - 2] + new_item.append(output + " ") + new_row.append(new_item) + + new_row = add_item(tmp_soup, new_row, False, function_name + '(') + new_row = add_item(tmp_soup, new_row, True, get_text_between_substrings(signature, "(", ")")) + new_row = add_item(tmp_soup, new_row, False, ')') + return new_row + + +def new_line(tmp_soup, tmp_table, new_row): + """ Adds a new line to the html table """ + tmp_table.append(new_row) + new_row = tmp_soup.new_tag('tr') + return new_row + + +def add_bolded(tmp_soup, new_row, text): + """ Adds bolded text to the table """ + new_item = tmp_soup.new_tag('th', style="text-align:left") + new_item.append(text) + new_row.append(new_item) + return new_row + + +def append_table_to(cpp_table, tmp_soup, language, signature, function_name): + """ Insert the new Python / Java table after the current html c++ table """ + if signature != "": + tmp_table = tmp_soup.new_tag('table') + new_row = tmp_soup.new_tag('tr') + new_row = add_bolded(tmp_soup, new_row, language) + ident = False + + if len(signature) > 120: + new_row = new_line(tmp_soup, tmp_table, new_row) + ident = True + + if " or " in signature: + ident = True + for tmp_sig in signature.split(" or "): + new_row = new_line(tmp_soup, tmp_table, new_row) + new_row = add_signature_to_table(tmp_soup, new_row, tmp_sig, function_name, language, ident) + new_row = new_line(tmp_soup, tmp_table, new_row) + else: + new_row = add_signature_to_table(tmp_soup, new_row, signature, function_name, language, ident) + tmp_table.append(new_row) + + cpp_table.insert_after(tmp_table) + return cpp_table + + +def add_signatures(tmp_soup, tmp_dir, ADD_JAVA, ADD_PYTHON, module_name): + """ Add signatures to the current soup and rewrite the html file""" + logging.debug(tmp_dir) + sign_counter = 0 + python_sign_counter = 0 + java_sign_counter = 0 + + if ADD_JAVA: + functions_file = "java_doc_txts/" + module_name + "/functions.txt" + if os.path.exists(functions_file): + with open(functions_file, 'r') as f: + java_signatures = f.read().split("\n") + else: + ADD_JAVA = False # This C++ module (module_name) may not exist in Java + + # the HTML tag & class being used to find functions + for function in tmp_soup.findAll("h2", {"class": "memtitle"}): + function_name = function.getText() + if os.name == 'nt': # if Windows + function_name = function_name.encode("ascii","ignore").decode() + + # all functions have () in it's name + if "()" not in function_name: + continue + + if "[" in function_name: + if "[1/" in function_name: + function_name = function_name.replace(' ', '')[:-7] + else: + continue + else: + function_name = function_name.replace(' ', '')[:-2] + sign_counter += 1 + + # if not Windows computer + if os.name != 'nt': + function_name = function_name.replace(' ', '')[2:] + + cpp_table = function.findNext('table') + + if ADD_PYTHON: + try: + print(function_name) + method = getattr(cv2, str(function_name)) + description = str(method.__doc__).split("\n") + signature = "" + is_first_sig = True + for line in description: + if line.startswith(".") or line == "": + continue + else: + if is_first_sig: + signature += line + is_first_sig = False + else: + signature += " or " + line + + cpp_table = append_table_to(cpp_table, tmp_soup, "Python:", signature, function_name) + python_sign_counter += 1 + except AttributeError: + continue + + if ADD_JAVA: + for signature in java_signatures: + if function_name in signature: + append_table_to(cpp_table, tmp_soup, "Java:", signature, function_name) + java_sign_counter += 1 + break + + tmp_str = str(tmp_soup) + if os.name == 'nt': # if Windows + with open(tmp_dir, "wb") as tmp_file: + tmp_file.write(tmp_str.encode("ascii","ignore")) + else: + with open(tmp_dir, "w") as tmp_file: + tmp_file.write(tmp_str) + + logging.debug("Added [" + str(python_sign_counter) + \ + "/" + str(sign_counter) + "] Python signatures") + logging.debug("Added [" + str(java_sign_counter) + \ + "/" + str(sign_counter) + "] Java signatures") diff --git a/doc/tools/python_signatures.py b/doc/tools/python_signatures.py new file mode 100644 index 0000000000..34ea3229bd --- /dev/null +++ b/doc/tools/python_signatures.py @@ -0,0 +1,34 @@ +""" +This code adds Python signatures to the docs. + +TODO: +* clarify when there are several C++ signatures corresponding to a single Python function. + i.e: calcHist(): + http://docs.opencv.org/3.2.0/d6/dc7/group__imgproc__hist.html#ga4b2b5fd75503ff9e6844cc4dcdaed35d +* clarify special case: + http://docs.opencv.org/3.2.0/db/de0/group__core__utils.html#ga4910d7f86336cd4eff9dd05575667e41 +""" +import re +import sys +import html_functions + +ADD_JAVA = False +ADD_PYTHON = True +ROOT_DIR = sys.argv[1] + +soup = html_functions.load_html_file(ROOT_DIR + "index.html") +href_list = html_functions.get_links_list(soup, True) + +for link in href_list: + # add python signatures to the module + soup = html_functions.load_html_file(ROOT_DIR + link) + sub_href_list = html_functions.get_links_list(soup, True) + module_name = html_functions.get_text_between_substrings(link, "group__", ".html") + html_functions.add_signatures(soup, ROOT_DIR + link, ADD_JAVA, ADD_PYTHON, module_name) + + # add python signatures to the sub-modules + link = re.sub(r"group__.+html", "", link) + for sub_link in sub_href_list: + tmp_dir = ROOT_DIR + link + sub_link + soup = html_functions.load_html_file(tmp_dir) + html_functions.add_signatures(soup, tmp_dir, ADD_JAVA, ADD_PYTHON, module_name)