├── .gitignore
├── HISTORY.txt
├── LICENSE.txt
├── MANIFEST.in
├── README.rst
├── cx_setup.py
├── docs
├── Makefile
├── _static
│ └── .empty
├── _templates
│ └── .empty
├── api.rst
├── conf.py
├── example.py
├── example_tables.py
├── gettingstarted.rst
├── index.rst
├── make.bat
└── usage.rst
├── examples
├── __init__.py
├── cable_list_from_schemes.py
├── cable_tables_to_csv.py
├── cables_xls_to_autocad.py
├── calc_cb_place_consumption.py
├── dev_get_table_info.py
├── get_names.py
└── lights.py
├── hello_world.py
├── pyautocad
├── __init__.py
├── api.py
├── cache.py
├── compat.py
├── contrib
│ ├── __init__.py
│ └── tables.py
├── types.py
└── utils.py
├── setup.py
└── tests
├── __init__.py
├── cmd_with_path.bat
├── test_api.py
├── test_cached.py
├── test_contrib_tables.py
├── test_some_ideas.py
├── test_types.py
└── test_utils.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[cod]
2 | *.idea
3 | build
4 | release
5 | _build
6 | dist
7 | MANIFEST
8 |
--------------------------------------------------------------------------------
/HISTORY.txt:
--------------------------------------------------------------------------------
1 | History
2 | -------
3 |
4 | 0.2.0 (2015-12-21)
5 | +++++++++++++++++++
6 |
7 | * Experimental Python 3 support
8 |
9 |
10 | 0.1.2 (2012-03-31)
11 | +++++++++++++++++++
12 |
13 | * Documentation improvements
14 | * ``cache.Cached`` proxy for caching expensive object attributes
15 | * ``utils.suppressed_regeneration_of(table)`` context manager
16 | * Fix: `cx_setup.py` script exclude list
17 |
18 |
19 | 0.1.1 (2012-03-25)
20 | +++++++++++++++++++
21 |
22 | * Documentation and usage examples
23 |
24 | 0.1.0 (2012-03-23)
25 | +++++++++++++++++++
26 |
27 | * initial PyPI release
28 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012, Roman Haritonov
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE.txt AUTHORS README.rst HISTORY.txt
2 | include *.py
3 | recursive-include tests *.py
4 | recursive-include examples *.py
5 | recursive-include docs *
6 | recursive-exclude docs *.pyc
7 | recursive-exclude docs *.pyo
8 | prune docs/_build
9 | prune docs/_themes/.git
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | pyautoacad - AutoCAD Automation for Python
2 | ------------------------------------------
3 |
4 | This library aimed to simplify writing ActiveX_ Automation_ scripts for AutoCAD_ with Python
5 |
6 | Requires:
7 | ----------
8 |
9 | - comtypes_
10 |
11 |
12 | Optional:
13 |
14 | - xlrd_, tablib_
15 |
16 |
17 | Features:
18 | -----------
19 |
20 | - Simplifies work with coordinates (3D points)
21 | - Efficient objects iteration and searching (with casting to correct type)
22 | - Excel/csv/json import and export (xlrd_ and tablib_ required)
23 |
24 | Simple usage example:
25 | ---------------------
26 |
27 | .. code-block:: python
28 |
29 | from pyautocad import Autocad, APoint
30 |
31 |
32 | acad = Autocad()
33 | acad.prompt("Hello, Autocad from Python\n")
34 | print acad.doc.Name
35 |
36 | p1 = APoint(0, 0)
37 | p2 = APoint(50, 25)
38 | for i in range(5):
39 | text = acad.model.AddText('Hi %s!' % i, p1, 2.5)
40 | acad.model.AddLine(p1, p2)
41 | acad.model.AddCircle(p1, 10)
42 | p1.y += 10
43 |
44 | dp = APoint(10, 0)
45 | for text in acad.iter_objects('Text'):
46 | print('text: %s at: %s' % (text.TextString, text.InsertionPoint))
47 | text.InsertionPoint = APoint(text.InsertionPoint) + dp
48 |
49 | for obj in acad.iter_objects(['Circle', 'Line']):
50 | print(obj.ObjectName)
51 |
52 | See more examples_ in source distribution.
53 |
54 | Links
55 | -----
56 |
57 | - **Documentation** at `readthedocs.org `_
58 |
59 | - **Source code and issue tracking** at `GitHub `_.
60 |
61 | .. _ActiveX: http://wikipedia.org/wiki/ActiveX
62 | .. _Automation: http://en.wikipedia.org/wiki/OLE_Automation
63 | .. _AutoCAD: http://wikipedia.org/wiki/AutoCAD
64 | .. _comtypes: http://pypi.python.org/pypi/comtypes
65 | .. _xlrd: http://pypi.python.org/pypi/xlrd
66 | .. _tablib: http://pypi.python.org/pypi/tablib
67 | .. _examples: https://github.com/reclosedev/pyautocad/tree/master/examples
68 | .. _documentation: https://pyautocad.readthedocs.io/
69 |
--------------------------------------------------------------------------------
/cx_setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | cx_setup
5 | ~~~~~~~~
6 |
7 | Converts example scripts to Windows executables.
8 | """
9 | import glob
10 | import sys
11 |
12 | from cx_Freeze import setup, Executable
13 |
14 | if not len(sys.argv[1:]):
15 | sys.argv.append('install_exe')
16 |
17 | install_exe_options = {'install_dir': './Autocad tools'}
18 | build_exe_options = {'excludes': ['bz2', '_hashlib', 'unittest', 'tests']}
19 |
20 |
21 | exclude_scripts = [r'examples\__init__.py', r'examples\dev_get_table_info.py']
22 | scripts_to_build = [name for name in glob.glob('examples/*.py') if
23 | name not in exclude_scripts]
24 |
25 |
26 | setup(name="Autocad tools",
27 | version="0.1",
28 | description="Generate cable list, get drawing names etc.",
29 | options=dict(install_exe=install_exe_options,
30 | build_exe=build_exe_options),
31 | executables=[Executable(script=script) for script in scripts_to_build])
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 | # the i18n builder cannot share the environment and doctrees with the others
15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
16 |
17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
18 |
19 | help:
20 | @echo "Please use \`make ' where is one of"
21 | @echo " html to make standalone HTML files"
22 | @echo " dirhtml to make HTML files named index.html in directories"
23 | @echo " singlehtml to make a single large HTML file"
24 | @echo " pickle to make pickle files"
25 | @echo " json to make JSON files"
26 | @echo " htmlhelp to make HTML files and a HTML help project"
27 | @echo " qthelp to make HTML files and a qthelp project"
28 | @echo " devhelp to make HTML files and a Devhelp project"
29 | @echo " epub to make an epub"
30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
31 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
32 | @echo " text to make text files"
33 | @echo " man to make manual pages"
34 | @echo " texinfo to make Texinfo files"
35 | @echo " info to make Texinfo files and run them through makeinfo"
36 | @echo " gettext to make PO message catalogs"
37 | @echo " changes to make an overview of all changed/added/deprecated items"
38 | @echo " linkcheck to check all external links for integrity"
39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
40 |
41 | clean:
42 | -rm -rf $(BUILDDIR)/*
43 |
44 | html:
45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
48 |
49 | dirhtml:
50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
51 | @echo
52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
53 |
54 | singlehtml:
55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
56 | @echo
57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
58 |
59 | pickle:
60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
61 | @echo
62 | @echo "Build finished; now you can process the pickle files."
63 |
64 | json:
65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
66 | @echo
67 | @echo "Build finished; now you can process the JSON files."
68 |
69 | htmlhelp:
70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
71 | @echo
72 | @echo "Build finished; now you can run HTML Help Workshop with the" \
73 | ".hhp project file in $(BUILDDIR)/htmlhelp."
74 |
75 | qthelp:
76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
77 | @echo
78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyautocad.qhcp"
81 | @echo "To view the help file:"
82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyautocad.qhc"
83 |
84 | devhelp:
85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
86 | @echo
87 | @echo "Build finished."
88 | @echo "To view the help file:"
89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/pyautocad"
90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyautocad"
91 | @echo "# devhelp"
92 |
93 | epub:
94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
95 | @echo
96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
97 |
98 | latex:
99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
100 | @echo
101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
103 | "(use \`make latexpdf' here to do that automatically)."
104 |
105 | latexpdf:
106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
107 | @echo "Running LaTeX files through pdflatex..."
108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
110 |
111 | text:
112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
113 | @echo
114 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
115 |
116 | man:
117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
118 | @echo
119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
120 |
121 | texinfo:
122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
123 | @echo
124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
125 | @echo "Run \`make' in that directory to run these through makeinfo" \
126 | "(use \`make info' here to do that automatically)."
127 |
128 | info:
129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
130 | @echo "Running Texinfo files through makeinfo..."
131 | make -C $(BUILDDIR)/texinfo info
132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
133 |
134 | gettext:
135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
136 | @echo
137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
138 |
139 | changes:
140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
141 | @echo
142 | @echo "The overview file is in $(BUILDDIR)/changes."
143 |
144 | linkcheck:
145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
146 | @echo
147 | @echo "Link check complete; look for any errors in the above output " \
148 | "or in $(BUILDDIR)/linkcheck/output.txt."
149 |
150 | doctest:
151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
152 | @echo "Testing of doctests in the sources finished, look at the " \
153 | "results in $(BUILDDIR)/doctest/output.txt."
154 |
--------------------------------------------------------------------------------
/docs/_static/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reclosedev/pyautocad/527a672099b1b3b95e055fcbbbb3b67e305f0fe5/docs/_static/.empty
--------------------------------------------------------------------------------
/docs/_templates/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reclosedev/pyautocad/527a672099b1b3b95e055fcbbbb3b67e305f0fe5/docs/_templates/.empty
--------------------------------------------------------------------------------
/docs/api.rst:
--------------------------------------------------------------------------------
1 | API
2 | ===
3 |
4 | This part of the documentation covers all the interfaces of `pyautocad`
5 |
6 | ``api`` - Main Autocad interface
7 | --------------------------------------------
8 |
9 | .. automodule:: pyautocad.api
10 | :members:
11 |
12 | .. data:: ACAD
13 |
14 | Constants from AutoCAD type library, for example::
15 |
16 | text.Alignment = ACAD.acAlignmentRight
17 |
18 | -----------------------------------------------------------------------------
19 |
20 | ``types`` - 3D Point and other AutoCAD data types
21 | -------------------------------------------------
22 |
23 | .. automodule:: pyautocad.types
24 | :members:
25 |
26 | -----------------------------------------------------------------------------
27 |
28 | ``utils`` - Utility functions
29 | -------------------------------------------------------------
30 |
31 | .. automodule:: pyautocad.utils
32 | :members:
33 | :exclude-members: timing, suppressed_regeneration_of
34 |
35 | .. autofunction:: timing(message)
36 | .. autofunction:: suppressed_regeneration_of(table)
37 |
38 |
39 |
40 |
41 |
42 | -----------------------------------------------------------------------------
43 |
44 | ``contrib.tables`` - Import and export tabular data from popular formats
45 | ------------------------------------------------------------------------
46 |
47 | .. automodule:: pyautocad.contrib.tables
48 | :synopsis: test
49 | :members:
50 |
51 | -----------------------------------------------------------------------------
52 |
53 | ``cache`` - Cache all object's attributes
54 | ------------------------------------------
55 | .. versionadded:: 0.1.2
56 |
57 | .. automodule:: pyautocad.cache
58 | :members:
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # pyautocad documentation build configuration file, created by
4 | # sphinx-quickstart on Sun Mar 25 09:29:19 2012.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 | import sys, os
15 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | sys.path.insert(0, os.path.abspath('..'))
20 |
21 | class Mock(object):
22 | def __init__(self, *args, **kwargs):
23 | pass
24 |
25 | def __call__(self, *args, **kwargs):
26 | return Mock()
27 |
28 | @classmethod
29 | def __getattr__(self, name):
30 | if name in ('__file__', '__path__'):
31 | return os.devnull
32 | elif name[0] == name[0].upper():
33 | return type(name, (), {})
34 | else:
35 | return Mock()
36 |
37 | MOCK_MODULES = ['comtypes', 'tablib', 'xlrd']
38 | for mod_name in MOCK_MODULES:
39 | sys.modules[mod_name] = Mock()
40 |
41 | from pyautocad import __version__
42 |
43 | # -- General configuration -----------------------------------------------------
44 |
45 | # If your documentation needs a minimal Sphinx version, state it here.
46 | #needs_sphinx = '1.0'
47 |
48 | # Add any Sphinx extension module names here, as strings. They can be extensions
49 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
50 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
51 |
52 | # Add any paths that contain templates here, relative to this directory.
53 | templates_path = ['_templates']
54 |
55 | # The suffix of source filenames.
56 | source_suffix = '.rst'
57 |
58 | # The encoding of source files.
59 | #source_encoding = 'utf-8-sig'
60 |
61 | # The master toctree document.
62 | master_doc = 'index'
63 |
64 | # General information about the project.
65 | project = u'pyautocad'
66 | copyright = u'2014, Roman Haritonov'
67 |
68 | # The version info for the project you're documenting, acts as replacement for
69 | # |version| and |release|, also used in various other places throughout the
70 | # built documents.
71 | #
72 | # The short X.Y version.
73 | version = __version__
74 | # The full version, including alpha/beta/rc tags.
75 | release = __version__
76 |
77 | # The language for content autogenerated by Sphinx. Refer to documentation
78 | # for a list of supported languages.
79 | #language = None
80 |
81 | # There are two options for replacing |today|: either, you set today to some
82 | # non-false value, then it is used:
83 | #today = ''
84 | # Else, today_fmt is used as the format for a strftime call.
85 | #today_fmt = '%B %d, %Y'
86 |
87 | # List of patterns, relative to source directory, that match files and
88 | # directories to ignore when looking for source files.
89 | exclude_patterns = ['_build']
90 |
91 | # The reST default role (used for this markup: `text`) to use for all documents.
92 | #default_role = None
93 |
94 | # If true, '()' will be appended to :func: etc. cross-reference text.
95 | #add_function_parentheses = True
96 |
97 | # If true, the current module name will be prepended to all description
98 | # unit titles (such as .. function::).
99 | #add_module_names = True
100 |
101 | # If true, sectionauthor and moduleauthor directives will be shown in the
102 | # output. They are ignored by default.
103 | #show_authors = False
104 |
105 | # The name of the Pygments (syntax highlighting) style to use.
106 | pygments_style = 'sphinx'
107 |
108 | # A list of ignored prefixes for module index sorting.
109 | #modindex_common_prefix = []
110 |
111 |
112 | # -- Options for HTML output ---------------------------------------------------
113 |
114 | # The theme to use for HTML and HTML Help pages. See the documentation for
115 | # a list of builtin themes.
116 | if on_rtd:
117 | html_theme = 'default'
118 | else:
119 | html_theme = 'default'
120 |
121 | # Theme options are theme-specific and customize the look and feel of a theme
122 | # further. For a list of options available for each theme, see the
123 | # documentation.
124 | #html_theme_options = {}
125 |
126 | # Add any paths that contain custom themes here, relative to this directory.
127 | #html_theme_path = []
128 |
129 | # The name for this set of Sphinx documents. If None, it defaults to
130 | # " v documentation".
131 | #html_title = None
132 |
133 | # A shorter title for the navigation bar. Default is the same as html_title.
134 | #html_short_title = None
135 |
136 | # The name of an image file (relative to this directory) to place at the top
137 | # of the sidebar.
138 | #html_logo = None
139 |
140 | # The name of an image file (within the static path) to use as favicon of the
141 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
142 | # pixels large.
143 | #html_favicon = None
144 |
145 | # Add any paths that contain custom static files (such as style sheets) here,
146 | # relative to this directory. They are copied after the builtin static files,
147 | # so a file named "default.css" will overwrite the builtin "default.css".
148 | html_static_path = ['_static']
149 |
150 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
151 | # using the given strftime format.
152 | #html_last_updated_fmt = '%b %d, %Y'
153 |
154 | # If true, SmartyPants will be used to convert quotes and dashes to
155 | # typographically correct entities.
156 | #html_use_smartypants = True
157 |
158 | # Custom sidebar templates, maps document names to template names.
159 | #html_sidebars = {}
160 |
161 | # Additional templates that should be rendered to pages, maps page names to
162 | # template names.
163 | #html_additional_pages = {}
164 |
165 | # If false, no module index is generated.
166 | #html_domain_indices = True
167 |
168 | # If false, no index is generated.
169 | #html_use_index = True
170 |
171 | # If true, the index is split into individual pages for each letter.
172 | #html_split_index = False
173 |
174 | # If true, links to the reST sources are added to the pages.
175 | #html_show_sourcelink = True
176 |
177 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
178 | #html_show_sphinx = True
179 |
180 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
181 | #html_show_copyright = True
182 |
183 | # If true, an OpenSearch description file will be output, and all pages will
184 | # contain a tag referring to it. The value of this option must be the
185 | # base URL from which the finished HTML is served.
186 | #html_use_opensearch = ''
187 |
188 | # This is the file name suffix for HTML files (e.g. ".xhtml").
189 | #html_file_suffix = None
190 |
191 | # Output file base name for HTML help builder.
192 | htmlhelp_basename = 'pyautocaddoc'
193 |
194 |
195 | # -- Options for LaTeX output --------------------------------------------------
196 |
197 | latex_elements = {
198 | # The paper size ('letterpaper' or 'a4paper').
199 | #'papersize': 'letterpaper',
200 |
201 | # The font size ('10pt', '11pt' or '12pt').
202 | #'pointsize': '10pt',
203 |
204 | # Additional stuff for the LaTeX preamble.
205 | #'preamble': '',
206 | }
207 |
208 | # Grouping the document tree into LaTeX files. List of tuples
209 | # (source start file, target name, title, author, documentclass [howto/manual]).
210 | latex_documents = [
211 | ('index', 'pyautocad.tex', u'pyautocad Documentation',
212 | u'Roman Haritonov', 'manual'),
213 | ]
214 |
215 | # The name of an image file (relative to this directory) to place at the top of
216 | # the title page.
217 | #latex_logo = None
218 |
219 | # For "manual" documents, if this is true, then toplevel headings are parts,
220 | # not chapters.
221 | #latex_use_parts = False
222 |
223 | # If true, show page references after internal links.
224 | #latex_show_pagerefs = False
225 |
226 | # If true, show URL addresses after external links.
227 | #latex_show_urls = False
228 |
229 | # Documents to append as an appendix to all manuals.
230 | #latex_appendices = []
231 |
232 | # If false, no module index is generated.
233 | #latex_domain_indices = True
234 |
235 |
236 | # -- Options for manual page output --------------------------------------------
237 |
238 | # One entry per manual page. List of tuples
239 | # (source start file, name, description, authors, manual section).
240 | man_pages = [
241 | ('index', 'pyautocad', u'pyautocad Documentation',
242 | [u'Roman Haritonov'], 1)
243 | ]
244 |
245 | # If true, show URL addresses after external links.
246 | #man_show_urls = False
247 |
248 |
249 | # -- Options for Texinfo output ------------------------------------------------
250 |
251 | # Grouping the document tree into Texinfo files. List of tuples
252 | # (source start file, target name, title, author,
253 | # dir menu entry, description, category)
254 | texinfo_documents = [
255 | ('index', 'pyautocad', u'pyautocad Documentation',
256 | u'Roman Haritonov', 'pyautocad', 'One line description of project.',
257 | 'Miscellaneous'),
258 | ]
259 |
260 | # Documents to append as an appendix to all manuals.
261 | #texinfo_appendices = []
262 |
263 | # If false, no module index is generated.
264 | #texinfo_domain_indices = True
265 |
266 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
267 | #texinfo_show_urls = 'footnote'
268 |
269 | autoclass_content = 'both'
270 | autodoc_member_order = 'bysource'
271 |
272 | def skip_modules_docstring(app, what, name, obj, options, lines):
273 | if what == 'module':
274 | del lines[:]
275 |
276 | def setup(app):
277 | app.connect('autodoc-process-docstring', skip_modules_docstring)
278 |
--------------------------------------------------------------------------------
/docs/example.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from pyautocad import Autocad, APoint
4 |
5 | acad = Autocad(create_if_not_exists=True)
6 | acad.prompt("Hello, Autocad from Python\n")
7 | print acad.doc.Name
8 |
9 | p1 = APoint(0, 0)
10 | p2 = APoint(50, 25)
11 | for i in range(5):
12 | text = acad.model.AddText(u'Hi %s!' % i, p1, 2.5)
13 | acad.model.AddLine(p1, p2)
14 | acad.model.AddCircle(p1, 10)
15 | p1.y += 10
16 |
17 | for obj in acad.iter_objects():
18 | print obj.ObjectName
19 |
20 | for text in acad.iter_objects('Text'):
21 | print text.TextString, text.InsertionPoint
22 |
23 | for obj in acad.iter_objects(['Text', 'Line']):
24 | print obj.ObjectName
25 |
26 | def text_contains_3(text_obj):
27 | return '3' in text_obj.TextString
28 |
29 | text = acad.find_one('Text', predicate=text_contains_3)
30 | print text.TextString
31 |
32 | from pyautocad import ACAD
33 |
34 | for text in acad.iter_objects('Text'):
35 | old_insertion_point = APoint(text.InsertionPoint)
36 | text.Alignment = ACAD.acAlignmentRight
37 | text.TextAlignmentPoint = old_insertion_point
38 |
39 | for line in acad.iter_objects('Line'):
40 | p1 = APoint(line.StartPoint)
41 | line.EndPoint = p1 - APoint(20, 0)
--------------------------------------------------------------------------------
/docs/example_tables.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from pyautocad import Autocad, APoint
4 | from pyautocad.contrib.tables import Table
5 |
6 | acad = Autocad()
7 | p1 = APoint(0, 0)
8 | for i in range(5):
9 | obj = acad.model.AddText(u'Hi %s!' % i, p1, 2.5)
10 | p1.y += 10
11 |
12 | table = Table()
13 | for obj in acad.iter_objects('Text'):
14 | x, y, z = obj.InsertionPoint
15 | table.writerow([obj.TextString, x, y, z])
16 | table.save('data.xls', 'xls')
17 |
18 | data = Table.data_from_file('data.xls')
19 |
20 |
--------------------------------------------------------------------------------
/docs/gettingstarted.rst:
--------------------------------------------------------------------------------
1 | Getting started
2 | ===============
3 |
4 | Installation
5 | ------------
6 |
7 | If you have pip_ or easy_install_, you can just::
8 |
9 | pip install --upgrade pyautocad
10 |
11 | or::
12 |
13 | easy_install -U pyautocad
14 |
15 | Also, you can download Windows installer from PyPI pyautocad_ page.
16 |
17 | .. _pyautocad: http://pypi.python.org/pypi/pyautocad/
18 | .. _pip: http://pypi.python.org/pypi/pip/
19 | .. _easy_install: http://pypi.python.org/pypi/setuptools
20 |
21 | Requirements
22 | ------------
23 |
24 | - comtypes_
25 | .. note::
26 |
27 | If you are using pip_ or easy_install_, then it will be installed automatically.
28 | Otherwise you should install comtypes_ package manually.
29 |
30 | - Optional: xlrd_ and tablib_ for working with tables
31 |
32 | .. _comtypes: http://pypi.python.org/pypi/comtypes/
33 | .. _xlrd: http://pypi.python.org/pypi/xlrd
34 | .. _tablib: http://pypi.python.org/pypi/tablib
35 |
36 | Retrieving AutoCAD ActiveX documentation
37 | ----------------------------------------
38 |
39 | A copy of the AutoCAD ActiveX guide and reference can be found in the ``help`` directory of your AutoCAD install.
40 |
41 |
42 | - ``acad_aag.chm`` - ActiveX and VBA Developer's Guide
43 | - ``acadauto.chm`` - ActiveX and VBA Reference
44 |
45 | Reference can also be found in ``C:\Program Files\Common Files\Autodesk Shared\acadauto.chm``
46 |
47 | What's next?
48 | ------------
49 |
50 | Read the :doc:`usage` section, or look for real applications in examples_ folder of source distribution.
51 |
52 | .. note::
53 |
54 | Applications in examples_ are Russian engineering specific, but anyway
55 | I hope you'll find something interesting in that code.
56 |
57 | For more info on features see :doc:`api` documentation and sources_.
58 |
59 | .. _examples: https://github.com/reclosedev/pyautocad/tree/master/examples
60 | .. _sources: https://github.com/reclosedev/pyautocad
61 |
62 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. pyautocad documentation master file, created by
2 | sphinx-quickstart on Sun Mar 25 09:29:19 2012.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to pyautocad's documentation!
7 | =====================================
8 |
9 | pyautocad_ - library aimed to simplify writing ActiveX_ Automation_ scripts for AutoCAD_ with Python
10 |
11 | Contents:
12 | =========
13 |
14 | .. toctree::
15 | :maxdepth: 2
16 |
17 | gettingstarted
18 | usage
19 | api
20 |
21 |
22 | Indices and tables
23 | ==================
24 |
25 | * :ref:`genindex`
26 | * :ref:`modindex`
27 | * :ref:`search`
28 |
29 | .. _pyautocad: http://pypi.python.org/pypi/pyautocad/
30 | .. _ActiveX: http://wikipedia.org/wiki/ActiveX
31 | .. _Automation: http://en.wikipedia.org/wiki/OLE_Automation
32 | .. _AutoCAD: http://wikipedia.org/wiki/AutoCAD
33 |
34 |
35 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. linkcheck to check all external links for integrity
37 | echo. doctest to run all doctests embedded in the documentation if enabled
38 | goto end
39 | )
40 |
41 | if "%1" == "clean" (
42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
43 | del /q /s %BUILDDIR%\*
44 | goto end
45 | )
46 |
47 | if "%1" == "html" (
48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
49 | if errorlevel 1 exit /b 1
50 | echo.
51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
52 | goto end
53 | )
54 |
55 | if "%1" == "dirhtml" (
56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
57 | if errorlevel 1 exit /b 1
58 | echo.
59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
60 | goto end
61 | )
62 |
63 | if "%1" == "singlehtml" (
64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
68 | goto end
69 | )
70 |
71 | if "%1" == "pickle" (
72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished; now you can process the pickle files.
76 | goto end
77 | )
78 |
79 | if "%1" == "json" (
80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished; now you can process the JSON files.
84 | goto end
85 | )
86 |
87 | if "%1" == "htmlhelp" (
88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can run HTML Help Workshop with the ^
92 | .hhp project file in %BUILDDIR%/htmlhelp.
93 | goto end
94 | )
95 |
96 | if "%1" == "qthelp" (
97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
98 | if errorlevel 1 exit /b 1
99 | echo.
100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
101 | .qhcp project file in %BUILDDIR%/qthelp, like this:
102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyautocad.qhcp
103 | echo.To view the help file:
104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyautocad.ghc
105 | goto end
106 | )
107 |
108 | if "%1" == "devhelp" (
109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
110 | if errorlevel 1 exit /b 1
111 | echo.
112 | echo.Build finished.
113 | goto end
114 | )
115 |
116 | if "%1" == "epub" (
117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
118 | if errorlevel 1 exit /b 1
119 | echo.
120 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
121 | goto end
122 | )
123 |
124 | if "%1" == "latex" (
125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
129 | goto end
130 | )
131 |
132 | if "%1" == "text" (
133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The text files are in %BUILDDIR%/text.
137 | goto end
138 | )
139 |
140 | if "%1" == "man" (
141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
145 | goto end
146 | )
147 |
148 | if "%1" == "texinfo" (
149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
150 | if errorlevel 1 exit /b 1
151 | echo.
152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
153 | goto end
154 | )
155 |
156 | if "%1" == "gettext" (
157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
158 | if errorlevel 1 exit /b 1
159 | echo.
160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
161 | goto end
162 | )
163 |
164 | if "%1" == "changes" (
165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
166 | if errorlevel 1 exit /b 1
167 | echo.
168 | echo.The overview file is in %BUILDDIR%/changes.
169 | goto end
170 | )
171 |
172 | if "%1" == "linkcheck" (
173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
174 | if errorlevel 1 exit /b 1
175 | echo.
176 | echo.Link check complete; look for any errors in the above output ^
177 | or in %BUILDDIR%/linkcheck/output.txt.
178 | goto end
179 | )
180 |
181 | if "%1" == "doctest" (
182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
183 | if errorlevel 1 exit /b 1
184 | echo.
185 | echo.Testing of doctests in the sources finished, look at the ^
186 | results in %BUILDDIR%/doctest/output.txt.
187 | goto end
188 | )
189 |
190 | :end
191 |
--------------------------------------------------------------------------------
/docs/usage.rst:
--------------------------------------------------------------------------------
1 | Usage
2 | =====
3 |
4 | Main interface and types
5 | -------------------------
6 |
7 | .. currentmodule:: pyautocad.api
8 |
9 |
10 | For our first example, we will use :class:`Autocad` (main Automation object) and
11 | :class:`pyautocad.types.APoint` for operations with coordinates
12 |
13 | .. literalinclude:: example.py
14 | :lines: 3
15 |
16 | Let's create AutoCAD application or connect to already running application:
17 |
18 | .. literalinclude:: example.py
19 | :lines: 5-7
20 |
21 | To work with AutoCAD documents and objects we can use ActiveX interface,
22 | :class:`Autocad` (from pyautocad) contains some methods to simplify common Automation tasks, such as
23 | object iteration and searching, getting objects from user's selection, printing messages.
24 |
25 | There are shortcuts for current ``ActiveDocument`` - :attr:`Autocad.doc`
26 | and ``ActiveDocument.ModelSpace`` - :attr:`Autocad.model`
27 |
28 | Let's add some objects to document:
29 |
30 | .. literalinclude:: example.py
31 | :lines: 9-15
32 |
33 | Now our document contains some ``Texts``, ``Lines`` and ``Circles``, let's iterate them all:
34 |
35 | .. literalinclude:: example.py
36 | :lines: 17-18
37 |
38 | Wea can also iterate objects of concrete type:
39 |
40 | .. literalinclude:: example.py
41 | :lines: 20-21
42 |
43 | .. note::
44 |
45 | Object name can be partial and case insensitive, e.g.
46 | ``acad.iter_objects('tex')`` will return ``AcDbText`` and ``AcDbMText`` objects
47 |
48 | Or multiple types:
49 |
50 | .. literalinclude:: example.py
51 | :lines: 23-24
52 |
53 | Also we can find first object with some conditions.
54 | For example, let's find first text item which contains ``3``:
55 |
56 | .. literalinclude:: example.py
57 | :lines: 26-30
58 |
59 | To modify objects in document, we need to find interesting objects and change its properties.
60 | Some properties are described with constants, e.g. text alignment. These constants can be accessed through
61 | :data:`ACAD`. Let's change all text objects text alignment:
62 |
63 | .. literalinclude:: example.py
64 | :lines: 32-37
65 |
66 | .. currentmodule:: pyautocad.types
67 |
68 | In previous code we have converted ``text.InsertionPoint`` to :class:`APoint` because
69 | we can't just use default ``tuple`` when setting another properties such as ``text.TextAlignmentPoint``.
70 |
71 | If wee need to change position of some object, we should use :class:`APoint`,
72 | for example let's change lines end position:
73 |
74 | .. literalinclude:: example.py
75 | :lines: 39-41
76 |
77 |
78 | Working with tables
79 | -------------------
80 |
81 | .. note::
82 |
83 | To work with tables, xlrd_ and tablib_ should be installed.
84 |
85 | .. _xlrd: http://pypi.python.org/pypi/xlrd
86 | .. _tablib: http://pypi.python.org/pypi/tablib
87 |
88 | .. currentmodule:: pyautocad.contrib.tables
89 |
90 | To simplify importing and exporting data there is :class:`Table` class exist.
91 | It allows you to read and write tabular data in popular formats:
92 |
93 | - csv
94 | - xls
95 | - xlsx (write only)
96 | - json
97 |
98 | Let's try to solve some basic task. We need to save text and position
99 | from all text objects to Excel file, and then load it back.
100 |
101 | First we need to add some objects to AutoCAD:
102 |
103 | .. literalinclude:: example_tables.py
104 | :lines: 3-10
105 |
106 | Now we can iterate this objects and save them to Excel table:
107 |
108 | .. literalinclude:: example_tables.py
109 | :lines: 12-16
110 |
111 | After saving this data to 'data.xls' and probably changing it with some table
112 | processor software (e.g. Microsoft Office Excel) we can retrieve our data from file:
113 |
114 | .. literalinclude:: example_tables.py
115 | :lines: 18
116 |
117 | ``data`` will contain::
118 |
119 | [[u'Hi 0!', 0.0, 0.0, 0.0],
120 | [u'Hi 1!', 0.0, 10.0, 0.0],
121 | [u'Hi 2!', 0.0, 20.0, 0.0],
122 | [u'Hi 3!', 0.0, 30.0, 0.0],
123 | [u'Hi 4!', 0.0, 40.0, 0.0]]
124 |
125 | .. seealso::
126 |
127 | Example of working with AutoCAD table objects
128 | at `examples/dev_get_table_info.py `_
129 |
130 | Improve speed
131 | -------------
132 |
133 | - ActiveX technology is quite slow. When you are accessing object attributes like
134 | position, text, etc, every time call is passed to AutoCAD. It can slowdown execution
135 | time. For example if you have program, which combines single line
136 | text based on its relative positions, you probably need to get each text position
137 | several times. To speed this up, you can cache objects attributes using the :class:`pyautocad.cache.Cached` proxy (see example in class documentation)
138 |
139 | - To improve speed of AutoCAD table manipulations, you can use ``Table.RegenerateTableSuppressed = True``
140 | or handy context manager :func:`suppressed_regeneration_of(table) `::
141 |
142 | table = acad.model.AddTable(pos, rows, columns, row_height, col_width)
143 | with suppressed_regeneration_of(table):
144 | table.SetAlignment(ACAD.acDataRow, ACAD.acMiddleCenter)
145 | for row in range(rows):
146 | for col in range(columns):
147 | table.SetText(row, col, '%s %s' % (row, col))
148 |
149 | Utility functions
150 | -----------------
151 |
152 | There is also some utility functions for work with AutoCAD text objects and more.
153 | See :mod:`pyautocad.utils` documentation.
--------------------------------------------------------------------------------
/examples/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reclosedev/pyautocad/527a672099b1b3b95e055fcbbbb3b67e305f0fe5/examples/__init__.py
--------------------------------------------------------------------------------
/examples/cable_list_from_schemes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from collections import OrderedDict, namedtuple
4 | import optparse
5 | import os
6 | import re
7 | import logging
8 |
9 |
10 | from pyautocad import Autocad
11 | from pyautocad.utils import unformat_mtext, timing
12 | from pyautocad.contrib.tables import Table, available_write_formats
13 |
14 |
15 | logging.basicConfig(level=logging.INFO, format='%(message)s')
16 | logger = logging.getLogger()
17 | logger.addHandler(logging.FileHandler('cables_from_schemes.log', 'w'))
18 |
19 |
20 | CableEntry = namedtuple('CableEntry', 'name, source, target, cable, section, length')
21 |
22 |
23 | def get_known_targets(filename):
24 | if not os.path.exists(filename):
25 | logger.warning("Can't find file with known targets: %s", filename)
26 | return {}
27 | targets = OrderedDict()
28 | data = Table.data_from_file(filename)
29 | for row in data:
30 | if len(row) < 3:
31 | continue
32 | targets[row[0]] = row[2]
33 | return targets
34 |
35 |
36 | def get_cables(acad, block, known_targets):
37 | patterns = [ur"""(?P.*?)-(?P[\dxх,\(\)]+)\s+
38 | (?P\d+)\s*[мm]\\P
39 | \s*(?P[^-]+)-(?P.+)\s*""",
40 |
41 | ur"""(?P.*?)-(?P.*?)\s*\\P
42 | \s*(?P.*?)-(?P[\dxх,\(\)]+)\s+
43 | (?P\d+)\s*[мm]"""]
44 | patterns = [re.compile(pat, re.VERBOSE) for pat in patterns]
45 |
46 | for text in acad.iter_objects("dbmtext", block):
47 | text = unformat_mtext(text.TextString)
48 | logger.info(text)
49 | m_cable = None
50 | for pattern in patterns:
51 | m_cable = pattern.match(text)
52 | if m_cable:
53 | break
54 | if not m_cable:
55 | continue
56 |
57 | logger.info("!!!%s\n", text)
58 | m = m_cable.groupdict()
59 | cable_name = "%s-%s" % (m['name'], m['source'])
60 | target = known_targets.get(cable_name, '')
61 | if not target:
62 | target = m['name']
63 | yield CableEntry(cable_name, m['source'], target,
64 | m['cable'], m['section'], m['length'])
65 |
66 |
67 | def sort_by_correct_order(messed_order, correct_order):
68 | return [x for x in correct_order if x in messed_order] +\
69 | [x for x in messed_order if x not in correct_order]
70 |
71 |
72 | def sort_cables_by_targets(cables, targets):
73 | presorted_cables = OrderedDict()
74 | for entry in sorted(cables, key=lambda x: (x.source, x.name)):
75 | presorted_cables[entry.name] = entry
76 | if not targets:
77 | return presorted_cables.itervalues()
78 | sorted_cable_names = sort_by_correct_order(presorted_cables, targets)
79 | return (presorted_cables[name] for name in sorted_cable_names)
80 |
81 |
82 | def main():
83 | acad = Autocad()
84 | parser = optparse.OptionParser(usage=u'%prog [опции] [файл для результатов]')
85 | parser.add_option('-f', '--format',
86 | choices=available_write_formats(), dest='format',
87 | metavar='FORMAT', default='xls',
88 | help=u"Формат файла (%s) по умолчанию - %%default" %
89 | ', '.join(available_write_formats()))
90 | parser.add_option('-k', '--known', dest='known_targets',
91 | metavar='FILE', default='', action='store',
92 | help=u'Файл с заполненым полем "Конец" и очередностью '
93 | u' записей. По умолчанию берется из существующего файла')
94 | parser.add_option('-q', '--quiet', action='callback',
95 | callback=lambda *x: logging.disable(logging.WARNING),
96 | help=u'"Тихий" режим (не печатать в консоль)')
97 | parser.add_option('-s', '--single', dest='single_doc', action='store_true',
98 | default=False,
99 | help=u'Собрать данные только из текущего документа '
100 | u'(Собирает из всех по умолчанию)')
101 | parser.add_option('-c', '--no-known', dest='dont_use_known',
102 | action='store_true', default=False,
103 | help=u'Не использовать данные об очередности и поле "Конец"')
104 | options, args = parser.parse_args()
105 |
106 | output_file = args[0] if args else u"cables_from_%s.%s" % (acad.doc.Name, options.format)
107 | if not options.known_targets and not options.dont_use_known:
108 | options.known_targets = output_file
109 | known_targets = get_known_targets(options.known_targets)
110 | output_table = Table()
111 | if options.single_doc:
112 | documents = [acad.doc]
113 | else:
114 | documents = acad.app.Documents
115 |
116 | for doc in documents:
117 | try:
118 | cables = get_cables(acad, doc.Modelspace, known_targets)
119 | sorted_cables = sort_cables_by_targets(cables, known_targets)
120 | for row in sorted_cables:
121 | output_table.writerow([s for s in row])
122 | except Exception:
123 | logger.exception('Error while processing %s', doc.Name)
124 | output_table.save(output_file, options.format)
125 |
126 |
127 | if __name__ == "__main__":
128 | with timing():
129 | main()
130 |
131 | # TODO append to existent file option
132 | # TODO atomic write
133 |
--------------------------------------------------------------------------------
/examples/cable_tables_to_csv.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #-*- coding: utf-8 -*-
3 | import optparse
4 |
5 | from pyautocad import Autocad, utils
6 | from pyautocad.contrib.tables import Table, available_write_formats
7 |
8 |
9 | def iter_cable_tables(acad, block):
10 | for table in acad.iter_objects("table", block):
11 | if table.Columns != 9:
12 | continue
13 | ncols = table.Columns # store in local, Automation is expensive
14 | for row in xrange(3, table.Rows):
15 | yield [utils.mtext_to_string(table.GetText(row, col))
16 | for col in xrange(ncols)]
17 |
18 |
19 | def extract_tables_from_dwg(acad, writer, skip_model=True):
20 | for layout in acad.iter_layouts(skip_model=skip_model):
21 | for row in iter_cable_tables(acad, layout.Block):
22 | writer.writerow(row)
23 |
24 |
25 | def main():
26 | parser = optparse.OptionParser()
27 | parser.add_option('-f', '--format',
28 | choices=available_write_formats(), dest='format',
29 | metavar='FMT', default='xls',
30 | help=u"Формат файла (%s) по умолчанию - %%default" %
31 | ', '.join(available_write_formats()))
32 | parser.add_option('-m', '--model',
33 | dest='include_model', default=False, action='store_true',
34 | help=u"Включить пространство Модели в область поиска")
35 |
36 | options, args = parser.parse_args()
37 | acad = Autocad()
38 | filename = args[0] if args else u"exported_%s.%s" % (acad.doc.Name,
39 | options.format)
40 | output_table = Table()
41 | extract_tables_from_dwg(acad, output_table, not options.include_model)
42 | output_table.save(filename, options.format)
43 |
44 | if __name__ == '__main__':
45 | with utils.timing():
46 | main()
47 |
--------------------------------------------------------------------------------
/examples/cables_xls_to_autocad.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #date: 13.02.12
4 | import sys
5 | from collections import defaultdict
6 |
7 | import xlrd
8 |
9 | from pyautocad import Autocad, ACAD, APoint
10 | from pyautocad.utils import timing
11 | from pyautocad.contrib.tables import Table
12 |
13 |
14 | HEADER_TEXT_HEIGHT = 3.5
15 | TEXT_HEIGHT = 3.0
16 | ROW_HEIGHT = 8.0
17 | TABLE_WIDTH = 287
18 | TABLE_GAP = 100
19 | FIRST_TABLE_ROWS = 23
20 | NEXT_TABLE_ROWS = 27
21 |
22 |
23 | def add_cables_list_to_autocad(block, data):
24 | insert_point = APoint(20, 0)
25 | distance = APoint(TABLE_WIDTH + TABLE_GAP, 0, 0)
26 |
27 | add_cables_table(block, data[:FIRST_TABLE_ROWS], APoint(20, 0))
28 | for chunk in chunks(data[FIRST_TABLE_ROWS:], NEXT_TABLE_ROWS):
29 | insert_point += distance
30 | add_cables_table(block, chunk, insert_point)
31 |
32 | # TODO names of pivot tables
33 | margin = APoint(0, TEXT_HEIGHT, 0)
34 | insert_point += distance
35 | block.AddText(u'Сводная таблица длин кабелей', insert_point + margin, TEXT_HEIGHT)
36 | add_pivot_table(block, insert_point, list(calc_pivot_table(data)))
37 |
38 | insert_point += distance
39 | block.AddText(u'Сводная таблица кабельных разделок', insert_point + margin, TEXT_HEIGHT)
40 | pivot_dcount = list(calc_pivot_table(data, count_double_pivot))
41 | add_pivot_table(block, insert_point, pivot_dcount)
42 |
43 | insert_point += distance
44 | block.AddText(u'ВНИМАНИЕ! У кабелей со сложным сечением (например 4х(5х70)'
45 | u' и т.п.) указано количество разделок',
46 | insert_point + margin * 4, TEXT_HEIGHT)
47 | block.AddText(u'Сводная таблица наконечников', insert_point + margin, TEXT_HEIGHT)
48 | add_pivot_table(block, insert_point, list(calc_pivot_tips(pivot_dcount)))
49 |
50 |
51 | def read_cables_from_table(filename):
52 | data = Table.data_from_file(filename)
53 | for row in data:
54 | columns = []
55 | for col in row:
56 | try:
57 | col = unicode(int(float(col))) # TODO HACK manipulate table format
58 | except ValueError:
59 | pass
60 | columns.append(col)
61 | yield columns
62 |
63 |
64 | def add_cables_table(block, entries, pos):
65 | table = prepare_cables_table(block, pos, len(entries))
66 | table.RegenerateTableSuppressed = True # speedup edit operations
67 | try:
68 | for row, row_data in enumerate(entries, 3):
69 | for col, text in enumerate(row_data):
70 | table.SetCellTextHeight(row, col, TEXT_HEIGHT)
71 | if text:
72 | table.SetText(row, col, text)
73 | finally:
74 | table.RegenerateTableSuppressed = False
75 |
76 |
77 | def prepare_cables_table(block, pos, rows):
78 | table = block.AddTable(pos, rows + 5, 9, ROW_HEIGHT, 15.0)
79 | table.RegenerateTableSuppressed = True
80 | table.DeleteRows(0, 2)
81 | table.SetAlignment(ACAD.acDataRow, ACAD.acMiddleCenter)
82 | table.VertCellMargin = 0.5
83 | table.HorzCellMargin = 0.5
84 |
85 | content = ((u"Обозначение кабеля, провода", u"Трасса", "", u"Кабель, провод", "", "", "", "",),
86 | ("", u"Начало", u"Конец", u"По проекту", "", "", u"Проложен", "", ),
87 | ("", "", "", u"Марка", u"Кол., число и сечение жил", u"Длина, м", u"Марка", u"Кол., число и сечение жил", u"Длина, м"))
88 |
89 | merged = ((1, 1, 6, 8), (0, 2, 0, 0), (1, 2, 1, 1), (0, 0, 3, 8),
90 | (0, 0, 0, 0), (1, 2, 2, 2), (1, 1, 3, 5), (0, 0, 1, 2))
91 |
92 | col_widths = [25, 60, 60, 20, 35, 16, 20, 35, 16]
93 |
94 | for col, width in enumerate(col_widths):
95 | table.SetColumnWidth(col, width)
96 | table.SetRowHeight(2, 20)
97 |
98 | # Merge cells before inserting text
99 | for merge in merged:
100 | table.MergeCells(*merge)
101 | for row, lst in enumerate(content):
102 | for col, text in enumerate(lst):
103 | table.SetCellTextHeight(row, col, HEADER_TEXT_HEIGHT)
104 | if text:
105 | table.SetText(row, col, text)
106 | table.RegenerateTableSuppressed = False
107 | return table
108 |
109 |
110 | def chunks(thing, chunk_length):
111 | for i in xrange(0, len(thing), chunk_length):
112 | yield thing[i:i + chunk_length]
113 |
114 |
115 | def add_pivot_table(block, pos, pivot):
116 | table = block.AddTable(pos, len(pivot) + 5, len(pivot[0]), ROW_HEIGHT, 20.0)
117 | table.RegenerateTableSuppressed = True
118 | table.SetColumnWidth(0, 35)
119 | table.DeleteRows(0, 2) # delete Header and Title. SuppressHeader and SuppressTitle is not working.
120 | table.SetAlignment(ACAD.acDataRow, ACAD.acMiddleCenter)
121 | table.VertCellMargin = 0.5
122 | table.HorzCellMargin = 0.5
123 | for row, columns in enumerate(pivot):
124 | for col, data in enumerate(columns):
125 | table.SetCellTextHeight(row, col, TEXT_HEIGHT)
126 | if data:
127 | table.SetText(row, col, unicode(data))
128 | table.RegenerateTableSuppressed = False
129 | return table
130 |
131 |
132 | def length_pivot(value):
133 | return value
134 |
135 |
136 | def count_pivot(value):
137 | return 1 if value else 0
138 |
139 |
140 | def count_double_pivot(value):
141 | return count_pivot(value) * 2
142 |
143 |
144 | def try_convert(val, val_type):
145 | try:
146 | return val_type(val)
147 | except ValueError:
148 | return val_type()
149 |
150 |
151 | def normalize_section(section):
152 | section = section.replace(u'х', u'x') # replace russian h letter
153 | section = section.replace(',', '.')
154 | return section
155 |
156 |
157 | def calc_pivot_table(data, value_extractor=length_pivot):
158 | first_key = 4
159 | second_key = 3
160 | value_key = 5
161 | pivot = defaultdict(lambda: defaultdict(int))
162 | cable_types = set()
163 |
164 | for columns in data:
165 | pivot[columns[first_key]][columns[second_key]] += value_extractor(try_convert(columns[value_key], int))
166 | cable_types.add(columns[second_key])
167 | cable_sections = sorted(pivot.keys())
168 |
169 | def sections_key(section):
170 | if '(' in section: # it's hard to handle multicable feeders
171 | return section # so return untouched (will be on top)
172 | section = normalize_section(section)
173 | return map(lambda x: try_convert(x, float), section.split('x'))
174 | cable_sections = sorted(cable_sections, key=sections_key)
175 |
176 | yield [u'Cечение'] + list(sorted(cable_types))
177 | for cable_section in cable_sections:
178 | columns = [cable_section]
179 | for cable_type in cable_types:
180 | columns.append(pivot[cable_section][cable_type])
181 | yield columns
182 |
183 |
184 | def calc_pivot_tips(pivot_dcount):
185 | tip_counts = []
186 | for row in pivot_dcount[1:]:
187 | tip_counts.append((row[0], sum(row[1:])))
188 | result = defaultdict(int)
189 | for section, count in tip_counts:
190 | if '(' in section:
191 | result[section] += count
192 | continue
193 | section = normalize_section(section)
194 | count_sect = map(lambda x: try_convert(x, float), section.split('x', 2)) # TODO buggy
195 | if len(count_sect) == 2:
196 | result[count_sect[1]] += int(count_sect[0] * count)
197 |
198 | yield u'Сечение', u'Кол-во наконечников'
199 | for sect in sorted(result.keys()):
200 | yield sect, result[sect]
201 |
202 |
203 | def main():
204 | filename = sys.argv[1] if sys.argv[1:] else 'cables_list.xls'
205 | acad = Autocad()
206 | data = list(read_cables_from_table(filename))
207 | add_cables_list_to_autocad(acad.doc.ActiveLayout.Block, data)
208 |
209 |
210 | if __name__ == "__main__":
211 | with timing():
212 | main()
213 |
--------------------------------------------------------------------------------
/examples/calc_cb_place_consumption.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import re
4 |
5 | from pyautocad import Autocad
6 | from pyautocad.utils import mtext_to_string, timing
7 |
8 |
9 | def main():
10 | acad = Autocad()
11 | print u'Примерный подсчет занимаемого места'
12 | print '%-20s| %s' % (u'Имя щита', u'Общее число модулей')
13 | for layout in acad.iter_layouts():
14 | table = acad.find_one('table', layout.Block, lambda x: x.Columns == 5)
15 | if not table:
16 | continue
17 |
18 | total_modules = 0
19 | row = -1
20 | while row < table.Rows:
21 | row += 1
22 | item_str = mtext_to_string(table.GetText(row, 2))
23 | item_str = item_str.replace(u'четырехполюсный', u'4-х')\
24 | .replace(u'трехполюсный', u'3-х')\
25 | .replace(u'двухполюсный', u'2-х')\
26 | .replace(u'однополюсный', u'1-но')
27 | m = re.match(ur'.*(\d)-([xх]|но).*', item_str)
28 | if m:
29 | n_modules = int(m.group(1))
30 | quantity = int(mtext_to_string(table.GetText(row, 3)))
31 | row += 1 # skip next row
32 | else:
33 | m = re.match(ur'(\d)[PР].*', item_str)
34 | if not m:
35 | continue
36 | n_modules = int(m.group(1))
37 | quantity = int(mtext_to_string(table.GetText(row - 1, 3)))
38 | total_modules += n_modules * quantity
39 | print '%-20s| %s' % (layout.Name, total_modules)
40 |
41 |
42 | if __name__ == "__main__":
43 | with timing():
44 | main()
45 | raw_input(u'\nPress enter to exit...')
46 |
--------------------------------------------------------------------------------
/examples/dev_get_table_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #date: 13.02.12
4 | import pprint
5 |
6 | from pyautocad import Autocad, utils
7 |
8 |
9 | def print_table_info(table, print_rows=0):
10 | merged = set()
11 | column_widths = [round(table.GetColumnWidth(col), 2) for col in xrange(table.Columns)]
12 | row_heights = [round(table.GetRowHeight(row), 2) for row in xrange(table.Rows)]
13 | row_texts = []
14 | for row in range(table.Rows):
15 | columns = []
16 | for col in range(table.Columns):
17 | if print_rows > 0:
18 | columns.append(table.GetText(row, col))
19 | minRow, maxRow, minCol, maxCol, is_merged = table.IsMergedCell(row, col)
20 | if is_merged:
21 | merged.add((minRow, maxRow, minCol, maxCol,))
22 | if print_rows > 0:
23 | print_rows -= 1
24 | row_texts.append(columns)
25 |
26 | print 'row_heights = %s' % str(row_heights)
27 | print 'column_widths = %s' % str(column_widths)
28 | print 'merged_cells = %s' % pprint.pformat(list(merged))
29 | if row_texts:
30 | print 'content = ['
31 | for row in row_texts:
32 | print u" [%s]," % u", ".join("u'%s'" % s for s in row)
33 | print ']'
34 |
35 |
36 | def main():
37 | acad = Autocad()
38 | layout = acad.doc.ActiveLayout
39 | table = acad.find_one('table', layout.Block)
40 | if not table:
41 | return
42 | print_table_info(table, 3)
43 |
44 |
45 | if __name__ == '__main__':
46 | with utils.timing():
47 | main()
48 |
--------------------------------------------------------------------------------
/examples/get_names.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import codecs
4 | import sys
5 |
6 | from pyautocad import Autocad, APoint, utils
7 |
8 |
9 | def iter_drawings_names(acad, doc):
10 | num_layouts = doc.Layouts.Count - 2
11 | for layout_number, layout in enumerate(acad.iter_layouts(doc)):
12 | utils.dynamic_print(' Layout %02d/%02d' % (layout_number, num_layouts))
13 | # first we need to find our main stamp with name 'f4'
14 | block = acad.find_one('blockreference', layout.Block, lambda x: 'f4' in x.EffectiveName)
15 | if not block:
16 | continue
17 | block_pos = APoint(block.InsertionPoint)
18 | # approximate position of drawing name
19 | name_pos = block_pos + APoint(-90, 12)
20 | for mt in acad.iter_objects("mtext", layout.Block):
21 | if name_pos.distance_to(mt.InsertionPoint) < 5.0:
22 | text = mt.TextString
23 | yield text.replace(" \\P", " ").replace("\\P", " ")
24 | break
25 | print
26 |
27 | def main():
28 | filename = sys.argv[1] if sys.argv[1:] else 'names.txt'
29 | output = codecs.open(filename, "w", encoding='utf-8')
30 | acad = Autocad()
31 | for doc in acad.app.Documents:
32 | print doc.Name
33 | output.write(u'%s\n' % ('-' * 50))
34 | output.write(u" %s\n" % doc.Name)
35 | output.write(u'%s\n' % ('-' * 50))
36 | for drawing_name in iter_drawings_names(acad, doc):
37 | output.write(u'%s\n' % drawing_name)
38 |
39 |
40 | if __name__ == "__main__":
41 | with utils.timing():
42 | main()
43 |
--------------------------------------------------------------------------------
/examples/lights.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/env python
2 | #-*- coding: utf-8 -*-
3 | import re
4 | import sys
5 | from collections import namedtuple, defaultdict
6 |
7 | from pyautocad import Autocad
8 | from pyautocad import utils
9 |
10 |
11 | LampEntry = namedtuple('LampEntry', 'number, mark, numxpower')
12 |
13 | # \A1;2ARCTIC SMC/SAN 254 \S2х54/2,5;\P300 лк
14 | def iter_lamps(acad, objects):
15 | for obj in acad.iter_objects(('MText', 'MLeader'), block=objects):
16 | try:
17 | text = obj.TextString
18 | except Exception:
19 | continue
20 | text = utils.unformat_mtext(text)
21 | m = re.search(ur'(?P\d+)(?P.*?)\\S(?P.*?)/.*?;', text)
22 | if not m:
23 | continue
24 | print m.group('num'), m.group('mark'), m.group('num_power')
25 | yield LampEntry(m.group('num'), m.group('mark'), m.group('num_power'))
26 |
27 | def main():
28 | acad = Autocad()
29 | objects = None
30 | if 'i' in sys.argv[1:]:
31 | objects = acad.get_selection('Select objects')
32 | lamps = defaultdict(int)
33 | for lamp in iter_lamps(acad, objects):
34 | lamps[lamp.mark] += int(lamp.number)
35 | print '-' * 79
36 | for mark, number in sorted(lamps.iteritems()):
37 | print '%-20s | %s' % (mark, number)
38 |
39 | if __name__ == "__main__":
40 | with utils.timing():
41 | main()
42 |
--------------------------------------------------------------------------------
/hello_world.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from pyautocad import Autocad, APoint
4 |
5 |
6 | acad = Autocad()
7 | acad.prompt("Hello, Autocad from Python\n")
8 | print acad.doc.Name
9 |
10 | p1 = APoint(0, 0)
11 | p2 = APoint(50, 25)
12 | for i in range(5):
13 | text = acad.model.AddText('Hi %s!' % i, p1, 2.5)
14 | acad.model.AddLine(p1, p2)
15 | acad.model.AddCircle(p1, 10)
16 | p1.y += 10
17 |
18 | dp = APoint(10, 0)
19 | for text in acad.iter_objects('Text'):
20 | print('text: %s at: %s' % (text.TextString, text.InsertionPoint))
21 | text.InsertionPoint = APoint(text.InsertionPoint) + dp
22 |
23 | for obj in acad.iter_objects(['Circle', 'Line']):
24 | print(obj.ObjectName)
--------------------------------------------------------------------------------
/pyautocad/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | pyautocad
5 | ~~~~~~~~~
6 |
7 | AutoCAD Automation for Python.
8 |
9 | :copyright: (c) 2012 by Roman Haritonov.
10 | :license: BSD, see LICENSE.txt for more details.
11 | """
12 | __docformat__ = 'restructuredtext en'
13 | __version__ = '0.2.0'
14 |
15 | from pyautocad.api import *
16 | from pyautocad.types import *
17 |
--------------------------------------------------------------------------------
/pyautocad/api.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | pyautocad.api
5 | ~~~~~~~~~~~~~~~
6 |
7 | Main AutoCAD automation object.
8 |
9 | :copyright: (c) 2012 by Roman Haritonov.
10 | :license: BSD, see LICENSE.txt for more details.
11 | """
12 |
13 | __all__ = ['Autocad', 'ACAD']
14 |
15 | import logging
16 | import comtypes
17 | import glob
18 | import os
19 | try:
20 | import comtypes.client
21 | # generate modules for work with ACAD constants
22 | for pattern in ("acax*enu.tlb", "axdb*enu.tlb"):
23 | pattern = os.path.join(
24 | r"C:\Program Files\Common Files\Autodesk Shared",
25 | pattern
26 | )
27 | tlib = glob.glob(pattern)[0]
28 | comtypes.client.GetModule(tlib)
29 | import comtypes.gen.AutoCAD as ACAD
30 | except Exception:
31 | # we are under readthedocs.org and need to mock this
32 | ACAD = None
33 |
34 | import pyautocad.types
35 | from pyautocad.compat import basestring, xrange
36 |
37 | logger = logging.getLogger(__name__)
38 |
39 |
40 | class Autocad(object):
41 | """Main AutoCAD Automation object
42 | """
43 |
44 | def __init__(self, create_if_not_exists=False, visible=True):
45 | """
46 | :param create_if_not_exists: if AutoCAD doesn't run, then
47 | new instanse will be crated
48 | :param visible: new AutoCAD instance will be visible if True (default)
49 | """
50 | self._create_if_not_exists = create_if_not_exists
51 | self._visible = visible
52 | self._app = None
53 |
54 | @property
55 | def app(self):
56 | """Returns active :class:`AutoCAD.Application`
57 |
58 | if :class:`Autocad` was created with :data:`create_if_not_exists=True`,
59 | it will create :class:`AutoCAD.Application` if there is no active one
60 | """
61 | if self._app is None:
62 | try:
63 | self._app = comtypes.client.GetActiveObject('AutoCAD.Application', dynamic=True)
64 | except WindowsError:
65 | if not self._create_if_not_exists:
66 | raise
67 | self._app = comtypes.client.CreateObject('AutoCAD.Application', dynamic=True)
68 | self._app.Visible = self._visible
69 | return self._app
70 |
71 | @property
72 | def doc(self):
73 | """ Returns `ActiveDocument` of current :attr:`Application`"""
74 | return self.app.ActiveDocument
75 |
76 | #: Same as :attr:`doc`
77 | ActiveDocument = doc
78 |
79 | #: Same as :attr:`app`
80 | Application = app
81 |
82 | @property
83 | def model(self):
84 | """ `ModelSpace` from active document """
85 | return self.doc.ModelSpace
86 |
87 | def iter_layouts(self, doc=None, skip_model=True):
88 | """Iterate layouts from *doc*
89 |
90 | :param doc: document to iterate layouts from if `doc=None` (default), :attr:`ActiveDocument` is used
91 | :param skip_model: don't include :class:`ModelSpace` if `True`
92 | """
93 | if doc is None:
94 | doc = self.doc
95 | for layout in sorted(doc.Layouts, key=lambda x: x.TabOrder):
96 | if skip_model and not layout.TabOrder:
97 | continue
98 | yield layout
99 |
100 | def iter_objects(self, object_name_or_list=None, block=None,
101 | limit=None, dont_cast=False):
102 | """Iterate objects from `block`
103 |
104 | :param object_name_or_list: part of object type name, or list of it
105 | :param block: Autocad block, default - :class:`ActiveDocument.ActiveLayout.Block`
106 | :param limit: max number of objects to return, default infinite
107 | :param dont_cast: don't retrieve best interface for object, may speedup
108 | iteration. Returned objects should be casted by caller
109 | """
110 | if block is None:
111 | block = self.doc.ActiveLayout.Block
112 | object_names = object_name_or_list
113 | if object_names:
114 | if isinstance(object_names, basestring):
115 | object_names = [object_names]
116 | object_names = [n.lower() for n in object_names]
117 |
118 | count = block.Count
119 | for i in xrange(count):
120 | item = block.Item(i) # it's faster than `for item in block`
121 | if limit and i >= limit:
122 | return
123 | if object_names:
124 | object_name = item.ObjectName.lower()
125 | if not any(possible_name in object_name for possible_name in object_names):
126 | continue
127 | if not dont_cast:
128 | item = self.best_interface(item)
129 | yield item
130 |
131 | def iter_objects_fast(self, object_name_or_list=None, container=None, limit=None):
132 | """Shortcut for `iter_objects(dont_cast=True)`
133 |
134 | Shouldn't be used in normal situations
135 | """
136 | return self.iter_objects(object_name_or_list, container, limit, dont_cast=True)
137 |
138 | def find_one(self, object_name_or_list, container=None, predicate=None):
139 | """Returns first occurance of object which match `predicate`
140 |
141 | :param object_name_or_list: like in :meth:`iter_objects`
142 | :param container: like in :meth:`iter_objects`
143 | :param predicate: callable, which accepts object as argument
144 | and returns `True` or `False`
145 | :returns: Object if found, else `None`
146 | """
147 | if predicate is None:
148 | predicate = bool
149 | for obj in self.iter_objects(object_name_or_list, container):
150 | if predicate(obj):
151 | return obj
152 | return None
153 |
154 | def best_interface(self, obj):
155 | """ Retrieve best interface for object """
156 | return comtypes.client.GetBestInterface(obj)
157 |
158 | def prompt(self, text):
159 | """ Prints text in console and in `AutoCAD` prompt
160 | """
161 | print(text)
162 | self.doc.Utility.Prompt(u"%s\n" % text)
163 |
164 | def get_selection(self, text="Select objects"):
165 | """ Asks user to select objects
166 |
167 | :param text: prompt for selection
168 | """
169 | self.prompt(text)
170 | try:
171 | self.doc.SelectionSets.Item("SS1").Delete()
172 | except Exception:
173 | logger.debug('Delete selection failed')
174 |
175 | selection = self.doc.SelectionSets.Add('SS1')
176 | selection.SelectOnScreen()
177 | return selection
178 |
179 | #: shortcut for :func:`pyautocad.types.aDouble`
180 | aDouble = staticmethod(pyautocad.types.aDouble)
181 | #: shortcut for :func:`pyautocad.types.aInt`
182 | aInt = staticmethod(pyautocad.types.aInt)
183 | #: shortcut for :func:`pyautocad.types.aShort`
184 | aShort = staticmethod(pyautocad.types.aShort)
185 |
186 |
--------------------------------------------------------------------------------
/pyautocad/cache.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | .. versionadded:: 0.1.2
5 |
6 | pyautocad.cache
7 | ~~~~~~~~~~~~~~~
8 |
9 | Proxy to cache all object attributes.
10 |
11 | :copyright: (c) 2012 by Roman Haritonov.
12 | :license: BSD, see LICENSE.txt for more details.
13 | """
14 | from __future__ import print_function
15 |
16 |
17 | class Cached(object):
18 | """
19 | Proxy for caching object attributes.
20 |
21 | Consider external class `Foo` with expensive property (we can't change its code)::
22 |
23 | class Foo(object):
24 | @property
25 | def x(self):
26 | print 'consuming time'
27 | time.sleep(1)
28 | return 42
29 |
30 | Cache all attributes and test access::
31 |
32 | foo = Foo()
33 | cached_foo = Cached(foo)
34 | for i in range(10):
35 | print cached_foo.x
36 |
37 | Output::
38 |
39 | consuming time
40 | 42
41 | 42
42 | 42
43 | 42
44 | 42
45 |
46 | It's possible to switch caching off with :meth:`switch_caching`
47 | and retrieve original instance with :meth:`get_original`
48 | """
49 | def __init__(self, instance):
50 | object.__setattr__(self, '_instance', instance)
51 | object.__setattr__(self, '_is_enabled', True)
52 | object.__setattr__(self, '_storage', {})
53 |
54 | def get_original(self):
55 | """ Returns original instance
56 | """
57 | return self._instance
58 |
59 | def switch_caching(self, is_enabled):
60 | """ Switch caching on or off
61 |
62 | :param is_enabled: caching status `True` or `False`
63 | :type is_enabled: bool
64 | """
65 | self._is_enabled = is_enabled
66 | if not is_enabled:
67 | self._storage = {}
68 |
69 | def __setattr__(self, key, value):
70 | if key in self.__dict__:
71 | return object.__setattr__(self, key, value)
72 | object.__setattr__(self._instance, key, value)
73 | if self._is_enabled:
74 | self._storage[key] = value
75 |
76 | def __getattr__(self, key):
77 | if key in self.__dict__:
78 | return object.__getattribute__(self, key)
79 | storage = self._storage
80 | if self._is_enabled and key in storage:
81 | return storage[key]
82 | value = getattr(self._instance, key)
83 | storage[key] = value
84 | return value
85 |
86 | def __delattr__(self, key):
87 | if key in self.__dict__:
88 | return object.__delattr__(self, key)
89 | if key in self._storage:
90 | del self._storage[key]
91 | object.__delattr__(self._instance, key)
92 |
93 |
94 | if __name__ == "__main__":
95 | import time
96 |
97 | class Foo(object):
98 | @property
99 | def x(self):
100 | print('consuming time')
101 | time.sleep(1)
102 | return 42
103 |
104 | foo = Foo()
105 | cached_foo = Cached(foo)
106 | for i in range(5):
107 | print(cached_foo.x)
108 |
--------------------------------------------------------------------------------
/pyautocad/compat.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | IS_PY3 = sys.version_info[0] == 3
4 |
5 | if IS_PY3:
6 | unicode = str
7 | bytes = bytes
8 | basestring = str
9 | xrange = range
10 | else:
11 | unicode = unicode
12 | _orig_bytes = bytes
13 | bytes = lambda s, *a: _orig_bytes(s)
14 | basestring = basestring
15 | xrange = xrange
--------------------------------------------------------------------------------
/pyautocad/contrib/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reclosedev/pyautocad/527a672099b1b3b95e055fcbbbb3b67e305f0fe5/pyautocad/contrib/__init__.py
--------------------------------------------------------------------------------
/pyautocad/contrib/tables.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | pyautocad.contrib.tables
5 | ~~~~~~~~~~~~~~~~~~~~~~~~
6 |
7 | Import and export tabular data from popular formats.
8 |
9 | :copyright: (c) 2012 by Roman Haritonov.
10 | :license: BSD, see LICENSE.txt for more details.
11 | """
12 |
13 | import csv
14 | import json
15 | import os
16 |
17 | import tablib
18 | import xlrd
19 |
20 | class FormatNotSupported(Exception):
21 | pass
22 |
23 | class Table(object):
24 | """ Represents table with ability to import and export data to following formats:
25 |
26 | - csv
27 | - xls
28 | - xlsx (write only)
29 | - json
30 |
31 | When you need to store some data, it can be done as follows::
32 |
33 | table = Table()
34 | for i in range(5):
35 | table.writerow([i, i, i])
36 |
37 | table.save('data.xls', 'xls')
38 |
39 | To import data from file, use :meth:`data_from_file`::
40 |
41 | data = Table.data_from_file('data.xls')
42 |
43 | """
44 |
45 | _write_formats = set(['csv', 'xls', 'xlsx', 'json'])
46 | _read_formats = _write_formats - set(['xlsx'])
47 |
48 | def __init__(self):
49 | self.dataset = tablib.Dataset()
50 |
51 | def writerow(self, row):
52 | """ Add `row` to table
53 |
54 | :param row: row to add
55 | :type row: list or tuple
56 | """
57 | self.dataset.append(row)
58 |
59 | def append(self, row):
60 | """ Synonym for :meth:`writerow`
61 | """
62 | self.writerow(row)
63 |
64 | def clear(self):
65 | """ Clear current table
66 | """
67 | self.dataset = tablib.Dataset()
68 |
69 | def save(self, filename, fmt, encoding='cp1251'):
70 | """ Save data to file
71 |
72 | :param filename: path to file
73 | :param fmt: data format (one of supported, e.g. 'xls', 'csv'
74 | :param encoding: encoding for 'csv' format
75 | """
76 | self._raise_if_bad_format(fmt)
77 | with open(filename, 'wb') as output:
78 | if encoding is not None and fmt == 'csv':
79 | self.to_csv(output, encoding)
80 | else:
81 | output.write(self.convert(fmt))
82 |
83 | def convert(self, fmt):
84 | """ Return data, converted to format
85 |
86 | :param fmt: desirable format of data
87 |
88 | **Note**: to convert to `csv` format, use :meth:`to_csv`
89 |
90 | See also :meth:`available_write_formats`
91 | """
92 | self._raise_if_bad_format(fmt)
93 | return getattr(self.dataset, fmt)
94 |
95 | def _raise_if_bad_format(self, fmt):
96 | if fmt not in self._write_formats:
97 | raise FormatNotSupported('Unknown format: %s' % fmt)
98 |
99 | def to_csv(self, stream, encoding='cp1251', delimiter=';', **kwargs):
100 | """ Writes data in `csv` format to stream
101 |
102 | :param stream: stream to write data to
103 | :param encoding: output encoding
104 | :param delimiter: `csv` delimiter
105 | :param kwargs: additional parameters for :class:`csv.writer`
106 | """
107 | writer = csv.writer(stream, delimiter=delimiter, **kwargs)
108 | for row in self.dataset.dict:
109 | row = [c.encode(encoding) for c in row]
110 | writer.writerow(row)
111 |
112 | @staticmethod
113 | def data_from_file(filename, fmt=None, csv_encoding='cp1251', csv_delimiter=';'):
114 | """ Returns data in desired format from file
115 |
116 | :param filename: path to file with data
117 | :param fmt: format of file, if it's `None`, then it tries to guess format
118 | from `filename` extension
119 | :param csv_encoding: encoding for `csv` data
120 | :param csv_delimiter: delimiter for `csv` data
121 |
122 | Format should be in :meth:`available_read_formats`
123 | """
124 | if fmt is None:
125 | fmt = os.path.splitext(filename)[1][1:]
126 | raw_data = _TableImporter(csv_encoding=csv_encoding,
127 | csv_delimiter=csv_delimiter).import_table(filename, fmt)
128 | return raw_data
129 |
130 | @staticmethod
131 | def available_write_formats():
132 |
133 | return list(Table._write_formats)
134 |
135 | @staticmethod
136 | def available_read_formats():
137 | return list(Table._read_formats)
138 |
139 |
140 | class _TableImporter(object):
141 | def __init__(self, csv_encoding='cp1251', csv_delimiter=';'):
142 | self.csv_encoding = csv_encoding
143 | self.csv_delimiter = csv_delimiter
144 |
145 | def import_table(self, filename, fmt):
146 | reader = getattr(self, 'read_%s' % fmt, None)
147 | if reader is None:
148 | raise FormatNotSupported('Unknown fmt: %s' % fmt)
149 | dataset = []
150 | with open(filename, 'rb') as stream:
151 | for row in reader(stream):
152 | dataset.append(row)
153 | return dataset
154 |
155 | def read_csv(self, stream):
156 | reader = csv.reader(stream, delimiter=self.csv_delimiter)
157 | for row in reader:
158 | yield [c.decode(self.csv_encoding) for c in row]
159 |
160 | def read_xls(self, stream):
161 | book = xlrd.open_workbook(file_contents=stream.read())
162 | sheet = book.sheet_by_index(0)
163 | for row in xrange(sheet.nrows):
164 | columns = []
165 | for col in xrange(sheet.ncols):
166 | val = sheet.cell(row, col).value
167 | columns.append(val)
168 | yield columns
169 |
170 | def read_json(self, stream):
171 | return json.load(stream)
172 |
173 | available_write_formats = Table.available_write_formats # backward compatibility
174 | available_read_formats = Table.available_read_formats
175 |
176 |
--------------------------------------------------------------------------------
/pyautocad/types.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | pyautocad.types
5 | ~~~~~~~~~~~~~~~
6 |
7 | 3D Points and and other AutoCAD data types.
8 |
9 | :copyright: (c) 2012 by Roman Haritonov.
10 | :license: BSD, see LICENSE.txt for more details.
11 | """
12 | import array
13 | import operator
14 | import math
15 |
16 | from pyautocad.compat import IS_PY3
17 |
18 |
19 | class APoint(array.array):
20 | """ 3D point with basic geometric operations and support for passing as a
21 | parameter for `AutoCAD` Automation functions
22 |
23 | Usage::
24 |
25 | >>> p1 = APoint(10, 10)
26 | >>> p2 = APoint(20, 20)
27 | >>> p1 + p2
28 | APoint(30.00, 30.00, 0.00)
29 |
30 | Also it supports iterable as parameter::
31 |
32 | >>> APoint([10, 20, 30])
33 | APoint(10.00, 20.00, 30.00)
34 | >>> APoint(range(3))
35 | APoint(0.00, 1.00, 2.00)
36 |
37 | Supported math operations: `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`::
38 |
39 | >>> p = APoint(10, 10)
40 | >>> p + p
41 | APoint(20.00, 20.00, 0.00)
42 | >>> p + 10
43 | APoint(20.00, 20.00, 10.00)
44 | >>> p * 2
45 | APoint(20.00, 20.00, 0.00)
46 | >>> p -= 1
47 | >>> p
48 | APoint(9.00, 9.00, -1.00)
49 |
50 | It can be converted to `tuple` or `list`::
51 |
52 | >>> tuple(APoint(1, 1, 1))
53 | (1.0, 1.0, 1.0)
54 |
55 | """
56 | def __new__(cls, x_or_seq, y=0.0, z=0.0):
57 | if isinstance(x_or_seq, (array.array, list, tuple)) and len(x_or_seq) == 3:
58 | return super(APoint, cls).__new__(cls, 'd', x_or_seq)
59 | return super(APoint, cls).__new__(cls, 'd', (x_or_seq, y, z))
60 |
61 |
62 | @property
63 | def x(self):
64 | """ x coordinate of 3D point"""
65 | return self[0]
66 |
67 | @x.setter
68 | def x(self, value):
69 | self[0] = value
70 |
71 | @property
72 | def y(self):
73 | """ y coordinate of 3D point"""
74 | return self[1]
75 |
76 | @y.setter
77 | def y(self, value):
78 | self[1] = value
79 |
80 | @property
81 | def z(self):
82 | """ z coordinate of 3D point"""
83 | return self[2]
84 |
85 | @z.setter
86 | def z(self, value):
87 | self[2] = value
88 |
89 | def distance_to(self, other):
90 | """ Returns distance to `other` point
91 |
92 | :param other: :class:`APoint` instance or any sequence of 3 coordinates
93 | """
94 | return distance(self, other)
95 |
96 | def __add__(self, other):
97 | return self.__left_op(self, other, operator.add)
98 |
99 | def __sub__(self, other):
100 | return self.__left_op(self, other, operator.sub)
101 |
102 | def __mul__(self, other):
103 | return self.__left_op(self, other, operator.mul)
104 |
105 | if IS_PY3:
106 | def __div__(self, other):
107 | return self.__left_op(self, other, operator.truediv)
108 | else:
109 | def __div__(self, other):
110 | return self.__left_op(self, other, operator.div)
111 |
112 | __radd__ = __add__
113 | __rsub__ = __sub__
114 | __rmul__ = __mul__
115 | __rdiv__ = __div__
116 | __floordiv__ = __div__
117 | __rfloordiv__ = __div__
118 | __truediv__ = __div__
119 | _r_truediv__ = __div__
120 |
121 | def __neg__(self):
122 | return self.__left_op(self, -1, operator.mul)
123 |
124 | def __left_op(self, p1, p2, op):
125 | if isinstance(p2, (float, int)):
126 | return APoint(op(p1[0], p2), op(p1[1], p2), op(p1[2], p2))
127 | return APoint(op(p1[0], p2[0]), op(p1[1], p2[1]), op(p1[2], p2[2]))
128 |
129 | def __iadd__(self, p2):
130 | return self.__iop(p2, operator.add)
131 |
132 | def __isub__(self, p2):
133 | return self.__iop(p2, operator.sub)
134 |
135 | def __imul__(self, p2):
136 | return self.__iop(p2, operator.mul)
137 |
138 | def __idiv__(self, p2):
139 | return self.__iop(p2, operator.div)
140 |
141 | def __iop(self, p2, op):
142 | if isinstance(p2, (float, int)):
143 | self[0] = op(self[0], p2)
144 | self[1] = op(self[1], p2)
145 | self[2] = op(self[2], p2)
146 | else:
147 | self[0] = op(self[0], p2[0])
148 | self[1] = op(self[1], p2[1])
149 | self[2] = op(self[2], p2[2])
150 | return self
151 |
152 | def __repr__(self):
153 | return self.__str__()
154 |
155 | def __str__(self):
156 | return 'APoint(%.2f, %.2f, %.2f)' % tuple(self)
157 |
158 | def __eq__(self, other):
159 | if not isinstance(other, (array.array, list, tuple)):
160 | return False
161 | return tuple(self) == tuple(other)
162 |
163 |
164 | def distance(p1, p2):
165 | """ Returns distance between two points `p1` and `p2`
166 | """
167 | return math.sqrt((p1[0] - p2[0]) ** 2 +
168 | (p1[1] - p2[1]) ** 2 +
169 | (p1[2] - p2[2]) ** 2)
170 |
171 |
172 | # next functions can accept parameters as aDouble(1, 2, 3)
173 | # or as list or tuple aDouble([1, 2, 3])
174 | def aDouble(*seq):
175 | """ Returns :class:`array.array` of doubles ('d' code) for passing to AutoCAD
176 |
177 | For 3D points use :class:`APoint` instead.
178 | """
179 | return _sequence_to_comtypes('d', *seq)
180 |
181 |
182 | def aInt(*seq):
183 | """ Returns :class:`array.array` of ints ('l' code) for passing to AutoCAD
184 | """
185 | return _sequence_to_comtypes('l', *seq)
186 |
187 |
188 | def aShort(*seq):
189 | """ Returns :class:`array.array` of shorts ('h' code) for passing to AutoCAD
190 | """
191 | return _sequence_to_comtypes('h', *seq)
192 |
193 |
194 | def _sequence_to_comtypes(typecode='d', *sequence):
195 | if len(sequence) == 1:
196 | return array.array(typecode, sequence[0])
197 | return array.array(typecode, sequence)
198 |
--------------------------------------------------------------------------------
/pyautocad/utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | pyautocad.utils
5 | ~~~~~~~~~~~~~~~
6 |
7 | Utility functions for work with texts, tables, etc.
8 |
9 | :copyright: (c) 2012 by Roman Haritonov.
10 | :license: BSD, see LICENSE.txt for more details.
11 | """
12 | from __future__ import print_function
13 |
14 | import sys
15 | import re
16 | import time
17 | from contextlib import contextmanager
18 |
19 |
20 | def unformat_mtext(s, exclude_list=('P', 'S')):
21 | """Returns string with removed format information
22 |
23 | :param s: string with multitext
24 | :param exclude_list: don't touch tags from this list. Default ('P', 'S') for
25 | newline and fractions
26 |
27 | ::
28 |
29 | >>> text = ur'{\\fGOST type A|b0|i0|c204|p34;TEST\\fGOST type A|b0|i0|c0|p34;123}'
30 | >>> unformat_mtext(text)
31 | u'TEST123'
32 |
33 | """
34 | s = re.sub(r'\{?\\[^%s][^;]+;' % ''.join(exclude_list), '', s)
35 | s = re.sub(r'\}', '', s)
36 | return s
37 |
38 |
39 | def mtext_to_string(s):
40 | """
41 | Returns string with removed format innformation as :func:`unformat_mtext` and
42 | `\\P` (paragraphs) replaced with newlines
43 |
44 | ::
45 |
46 | >>> text = ur'{\\fGOST type A|b0|i0|c204|p34;TEST\\fGOST type A|b0|i0|c0|p34;123}\\Ptest321'
47 | >>> mtext_to_string(text)
48 | u'TEST123\\ntest321'
49 |
50 | """
51 |
52 | return unformat_mtext(s).replace(u'\\P', u'\n')
53 |
54 |
55 | def string_to_mtext(s):
56 | """Returns string in Autocad multitext format
57 |
58 | Replaces newllines `\\\\n` with `\\\\P`, etc.
59 | """
60 | return s.replace('\\', '\\\\').replace(u'\n', u'\P')
61 |
62 |
63 | def text_width(text_item):
64 | """Returns width of Autocad `Text` or `MultiText` object
65 | """
66 | bbox_min, bbox_max = text_item.GetBoundingbox()
67 | return bbox_max[0] - bbox_min[0]
68 |
69 |
70 | @contextmanager
71 | def suppressed_regeneration_of(table):
72 | """ .. versionadded:: 0.1.2
73 |
74 | Context manager. Suppresses table regeneration to dramatically speedup table operations
75 |
76 | :param table: table object
77 |
78 | ::
79 |
80 | with suppressed_regeneration_of(table):
81 | populate(table) # or change its properties
82 |
83 | """
84 | # TODO: find the way to suppress regeneration of other objects
85 | table.RegenerateTableSuppressed = True
86 | try:
87 | yield
88 | finally:
89 | table.RegenerateTableSuppressed = False
90 |
91 | @contextmanager
92 | def timing(message=u'Elapsed'):
93 | """ Context manager for timing execution
94 |
95 | :param message: message to print
96 |
97 | Usage::
98 |
99 | with timing('some operation'):
100 | do_some_actions()
101 |
102 | Will print::
103 |
104 | some operation: 1.000 s # where 1.000 is actual execution time
105 |
106 | """
107 | begin = time.time()
108 | try:
109 | yield begin
110 | finally:
111 | elapsed = (time.time() - begin)
112 | print(u'%s: %.3f s' % (message, elapsed))
113 |
114 |
115 | def dynamic_print(text):
116 | """Prints text dynamically in one line
117 |
118 | Used for printing something like animations, or progress
119 | """
120 | sys.stdout.write('\r%s' % text)
121 | sys.stdout.flush()
122 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | try:
4 | from setuptools import setup
5 | except ImportError:
6 | from distutils.core import setup
7 |
8 |
9 | setup(
10 | name="pyautocad",
11 | packages=["pyautocad", "pyautocad.contrib"],
12 | version="0.2.0",
13 | description="AutoCAD Automation for Python",
14 | author="Roman Haritonov",
15 | author_email="reclosedev@gmail.com",
16 | url="https://github.com/reclosedev/pyautocad",
17 | install_requires=[
18 | 'comtypes>=1.1.1',
19 | ],
20 | keywords=["autocad", "automation", "activex", "comtypes"],
21 | license="BSD License",
22 | include_package_data=True,
23 | classifiers=[
24 | "Programming Language :: Python",
25 | "License :: OSI Approved :: BSD License",
26 | "Development Status :: 4 - Beta",
27 | "Intended Audience :: Developers",
28 | "Intended Audience :: System Administrators",
29 | "Programming Language :: Python :: 2",
30 | "Programming Language :: Python :: 3",
31 | "Programming Language :: Python :: 2.7",
32 | "Programming Language :: Python :: 3.3",
33 | "Programming Language :: Python :: 3.4",
34 | "Programming Language :: Python :: 3.5",
35 | "Programming Language :: Python :: Implementation :: CPython",
36 | "Operating System :: Microsoft :: Windows",
37 | "Topic :: Software Development :: Libraries :: Python Modules",
38 | "Topic :: Scientific/Engineering",
39 | ],
40 | long_description=open('README.rst').read()
41 | )
42 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reclosedev/pyautocad/527a672099b1b3b95e055fcbbbb3b67e305f0fe5/tests/__init__.py
--------------------------------------------------------------------------------
/tests/cmd_with_path.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | set PYTHONPATH=%PYTHONPATH%;..
3 | cmd
--------------------------------------------------------------------------------
/tests/test_api.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | # date: 16.01.12
4 | from __future__ import print_function
5 | import os, sys
6 | sys.path.insert(0, os.path.abspath('..'))
7 | from _ctypes import COMError
8 | import unittest
9 |
10 | from pyautocad import Autocad, aDouble, APoint, ACAD
11 | from pyautocad.compat import unicode
12 |
13 |
14 | NPASS = 3000
15 |
16 |
17 | class ApiTestCase(unittest.TestCase):
18 | def setUp(self):
19 | self.acad = Autocad(True)
20 | self.doc = self.acad.app.Documents.Add()
21 | print('Current', self.doc.Name)
22 |
23 | def tearDown(self):
24 | self.doc.Close(False)
25 | pass
26 |
27 | def test_points_arguments(self):
28 | model = self.acad.model
29 | p1 = APoint(0, 0, 0)
30 | for i in range(10):
31 | model.AddCircle(p1 * i, i + 1)
32 | p1 += i
33 |
34 | for circle in self.acad.iter_objects('circle'):
35 | cp = APoint(circle.Center)
36 | model.AddCircle(-cp, circle.Radius)
37 |
38 | def test_types(self):
39 | model = self.acad.model
40 | p1 = APoint(0, 0, 0)
41 | p2 = APoint(10, 10, 0)
42 | p3 = tuple(p + 10 for p in p2)
43 |
44 | model.AddLine(p1, p2)
45 | model.AddLine(p2, APoint(p3))
46 | lines = list(self.acad.iter_objects())
47 | self.assertEqual(len(lines), 2)
48 | self.assertEqual(lines[0].StartPoint, p1)
49 | self.assertEqual(lines[0].EndPoint, p2)
50 | self.assertEqual(lines[1].StartPoint, p2)
51 | self.assertEqual(lines[1].EndPoint, p3)
52 | with self.assertRaises(COMError):
53 | model.AddLine(aDouble(0, 0), APoint(0, 0, 0))
54 |
55 | def test_text(self):
56 | model = self.acad.model
57 | text1 = u'Русский текст'
58 | text2 = u'With paragraph \PYes'
59 |
60 | t1 = model.AddText(text1, APoint(0, 0, 0), 10)
61 | t2 = model.AddText(text2, APoint(10, 10, 0), 10)
62 |
63 | self.assertEqual(type(t1.TextString), unicode)
64 | self.assertEqual(t1.TextString, text1)
65 | self.assertEqual(t2.InsertionPoint, (10, 10, 0))
66 | self.assertNotEqual(t2.InsertionPoint, (10, 10, 1))
67 |
68 | def test_multitext(self):
69 | model = self.acad.model
70 | text1 = 'Line1\nLine2\nLine3\\'
71 | text2 = 'Line1\\PLine2\\PLine3\\P'
72 |
73 | t1 = model.AddMText(APoint(0, 0, 0), 10, text1)
74 | t2 = model.AddMText(APoint(10, 10, 0), 10, text2)
75 | self.assertEqual(t1.TextString, text1)
76 | self.assertEqual(t2.TextString, text2)
77 |
78 | def test_iter_objects(self):
79 | model = self.acad.model
80 | p1 = APoint(0, 0, 0)
81 | p2 = APoint(10, 10, 0)
82 | n_lines = 10
83 | n_texts = 15
84 | for i in range(n_lines):
85 | model.AddLine(p1, p2)
86 | for i in range(n_texts):
87 | model.AddMText(p2, 10, u'Dummy')
88 |
89 | lines_count = len(list(self.acad.iter_objects('Line')))
90 | texts_count = len(list(self.acad.iter_objects('MText')))
91 | all_count = len(list(self.acad.iter_objects(['MText', 'Line'])))
92 |
93 | self.assertEqual(lines_count, n_lines)
94 | self.assertEqual(texts_count, n_texts)
95 | self.assertEqual(all_count, n_lines + n_texts)
96 |
97 | def test_find_objects(self):
98 | p1 = APoint(0, 0)
99 | model = self.acad.model
100 | for i in range(5):
101 | model.AddText(u'test %s' % i, p1, 2.5)
102 |
103 | def text_contains_3(text_obj):
104 | return '3' in text_obj.TextString
105 |
106 | text = self.acad.find_one('Text', predicate=text_contains_3)
107 | self.assertEqual(text.TextString, 'test 3')
108 |
109 | def test_some_constants_available(self):
110 | self.assertTrue(ACAD.acAlignmentRight)
111 |
112 |
113 | if __name__ == '__main__':
114 | unittest.main()
115 |
--------------------------------------------------------------------------------
/tests/test_cached.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from __future__ import print_function
4 | import os, sys
5 | sys.path.insert(0, os.path.abspath('..'))
6 | import unittest
7 | import mock
8 |
9 | from pyautocad.cache import Cached
10 |
11 |
12 | class C(object):
13 | def __init__(self):
14 | self._x = None
15 |
16 | def getx(self):
17 | print("get x")
18 | return self._x
19 |
20 | def setx(self, value):
21 | print("set x", value)
22 | self._x = value
23 |
24 | x = property(lambda self: self.getx(), lambda self, x: self.setx(x))
25 |
26 | def modify(self, val):
27 | print("modify", val)
28 | self._x = val
29 |
30 |
31 | class CachedTestCase(unittest.TestCase):
32 | def test_caching_attributes(self):
33 | a = C()
34 |
35 | a.x = 1
36 | ac = Cached(a)
37 | self.assertEqual(ac.x, 1)
38 | a.modify(2)
39 | self.assertEqual(ac.x, 1)
40 | a.x = 2
41 |
42 | self.assertEqual(ac.x, 1)
43 | ac.modify(2)
44 | self.assertEqual(ac.x, 1)
45 |
46 | ac.x = 3
47 | self.assertEqual(ac.x, 3)
48 | self.assertEqual(a.x, 3)
49 | with self.assertRaises(AttributeError):
50 | y = ac.y
51 | ac.y = 10
52 | self.assertEqual(a.y, ac.y)
53 | self.assert_('y' in ac._storage)
54 | del ac.y
55 | with self.assertRaises(AttributeError):
56 | y = ac.y
57 | with self.assertRaises(AttributeError):
58 | del ac.z
59 |
60 | self.assertIs(ac.get_original(), a)
61 |
62 | a = C()
63 | ac = Cached(a)
64 | a.getx = mock.Mock(name='getx', return_value=1)
65 | for i in range(5):
66 | _ = ac.x
67 | self.assertEqual(a.getx.call_count, 1)
68 | ac.switch_caching(False)
69 | for i in range(5):
70 | _ = ac.x
71 | self.assertEqual(a.getx.call_count, 5 + 1)
72 |
73 | def test_caching_off(self):
74 | a = C()
75 | a.x = 1
76 | ac = Cached(a)
77 | a.x = 3
78 | self.assertEqual(ac.x, 3)
79 | a.x = 5
80 | self.assertEqual(ac.x, 3)
81 | ac.x = 5
82 | self.assertEqual(ac.x, 5)
83 | self.assertEqual(a.x, 5)
84 | ac.switch_caching(False)
85 | a.x = 7
86 | self.assertEqual(ac.x, 7)
87 | a.x = 8
88 | self.assertEqual(ac.x, 8)
89 | ac.switch_caching(True)
90 | a.x = 9
91 | self.assertEqual(ac.x, 8)
92 |
93 |
94 | if __name__ == '__main__':
95 | unittest.main()
96 |
--------------------------------------------------------------------------------
/tests/test_contrib_tables.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import os, sys
4 | sys.path.insert(0, os.path.abspath('..'))
5 | import unittest
6 |
7 | from pyautocad.contrib import tables
8 | import tablib
9 |
10 |
11 | class TableTestCase(unittest.TestCase):
12 |
13 | def test_tablib(self):
14 | d = tablib.Dataset([1,2,3], [4,5,6])
15 | with self.assertRaises(tablib.InvalidDimensions):
16 | d.append([1,2,3,4,5])
17 |
18 | def test_table_create(self):
19 | t = tables.Table()
20 | t.writerow([1]*3)
21 | t.writerow([2]*3)
22 | t.append([3]*3)
23 | with self.assertRaises(tablib.InvalidDimensions):
24 | t.writerow([4]*4)
25 | self.assertEqual(t.dataset.dict, [[1]*3, [2]*3, [3]*3])
26 | t.clear()
27 | t.writerow([1]*3)
28 | self.assertEqual(t.dataset.dict, [[1]*3])
29 |
30 | def test_table_unknown_format(self):
31 | t = tables.Table()
32 | t.writerow([1]*3)
33 | with self.assertRaises(tables.FormatNotSupported):
34 | t.save('tst', 'any_nonexistent')
35 | with self.assertRaises(tables.FormatNotSupported):
36 | t = tables.Table.data_from_file('tst', 'any_nonexistent')
37 |
38 | def test_write_read_encoding(self):
39 | t = tables.Table()
40 | row = [u'Привет, мир', u'мирtabbed', 'some']
41 | data = [row]
42 | t.writerow(row)
43 | #for fmt in tables.available_write_formats():
44 | for fmt in tables.available_read_formats():
45 | filename = 'test_hello.%s' % fmt
46 | t.save(filename, fmt)
47 | t2 = tables.Table.data_from_file(filename, fmt)
48 | self.assertEqual(t2, data)
49 | t2 = tables.Table.data_from_file(filename)
50 | self.assertEqual(t2, data)
51 | os.remove(filename)
52 |
53 | if __name__ == '__main__':
54 | unittest.main()
55 |
--------------------------------------------------------------------------------
/tests/test_some_ideas.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #date: 13.02.12
4 | import os, sys
5 | sys.path.insert(0, os.path.abspath('..'))
6 | import unittest
7 |
8 | from pyautocad import Autocad
9 | import tablib
10 |
11 |
12 | def sort_by_correct_order(messed_order, correct_order):
13 | return [x for x in correct_order if x in messed_order] + \
14 | [x for x in messed_order if x not in correct_order]
15 |
16 | class MyTestCase(unittest.TestCase):
17 | def test_sort_by_correct(self):
18 | correct = ['TP', 'VRU', 'SHR', 'SHO', 'LAMP']
19 | new_seq = ['SHR', 'VRU', 'LAMP', 'SHO', 'STANOK']
20 | print sort_by_correct_order(new_seq, correct)
21 |
22 | def test_tablib(self):
23 | d = tablib.Dataset([1,2,3], [4,5,6])
24 | with self.assertRaises(tablib.InvalidDimensions):
25 | d.append([1,2,3,4,5])
26 |
27 |
28 | if __name__ == '__main__':
29 | unittest.main()
30 |
--------------------------------------------------------------------------------
/tests/test_types.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #date: 17.01.12
4 | import os, sys
5 | sys.path.insert(0, os.path.abspath('..'))
6 | import unittest
7 |
8 | from pyautocad.types import APoint, distance
9 |
10 |
11 | class PointTestCase(unittest.TestCase):
12 | def test_point_ops(self):
13 | p1 = APoint(1, 1, 1)
14 | p2 = APoint(1, 1, 1)
15 | p3 = APoint(2, 2, 2)
16 | p4 = APoint(2, 2, 2)
17 |
18 | self.assertEqual(p1 + p2, (2, 2, 2))
19 | self.assertEqual(p1 - p3, (-1, -1, -1))
20 | self.assertEqual(p1 * p2, p1)
21 | self.assertEqual(p3 * p4, (4, 4, 4))
22 | self.assertEqual(p3 / p4, p1)
23 |
24 | self.assertEqual(p1 + 1, (2, 2, 2))
25 | self.assertEqual(p2 * 4, p3 * 2)
26 | self.assertEqual(p3 * 10, (20, 20, 20))
27 | self.assertEqual(p3 / 2, p1)
28 | self.assertEqual(p1 / 0.5, p3)
29 |
30 | self.assertEqual(-p1, (-1, -1, -1))
31 |
32 | def test_point_iops(self):
33 | p1 = APoint(1, 1, 1)
34 | p2 = APoint(2, 2, 2)
35 | p3 = APoint(3, 3, 3)
36 |
37 | p1 += 2
38 | p2 += p3
39 | self.assertEqual(p1, p3)
40 | self.assertEqual(p2, (5, 5, 5))
41 |
42 | p1 = APoint(1, 1, 1)
43 | p2 = APoint(2, 2, 2)
44 | p1 -= 2
45 | p2 -= p3
46 | self.assertEqual(p1, (-1, -1, -1))
47 | self.assertEqual(p2, (-1, -1, -1))
48 | p = APoint(5, 5, 5)
49 | p /= 2.0
50 | self.assertEqual(p, (2.5, 2.5, 2.5))
51 | p *= 2
52 | self.assertEqual(p, (5, 5, 5))
53 |
54 | def test_args(self):
55 | wrong_args = ['123', (1,2), [1,2,3,4]]
56 | for arg in wrong_args:
57 | with self.assertRaises(TypeError):
58 | p = APoint(arg)
59 |
60 | p = APoint(0, 0, 0)
61 | for arg in wrong_args:
62 | try:
63 | p += arg
64 | self.fail('Wrong arg passed')
65 | except Exception:
66 | pass
67 |
68 | def test_attributes(self):
69 | p1 = APoint(1, 1, 1)
70 | p1.x += 1
71 | p1.y += 1
72 | p1.z += 1
73 | self.assertEqual(p1, (2, 2, 2))
74 |
75 | def test_distance(self):
76 | p1 = APoint(10, 10, 10)
77 | p2 = APoint(15, 15, 15)
78 | self.assertAlmostEqual(p1.distance_to(p2), 8.660254037844387)
79 | self.assertEqual(distance(p1, p2), distance(p2, p1))
80 |
81 |
82 | if __name__ == '__main__':
83 | unittest.main()
84 |
--------------------------------------------------------------------------------
/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from __future__ import unicode_literals
4 | import os, sys
5 | sys.path.insert(0, os.path.abspath('..'))
6 | import unittest
7 |
8 | from pyautocad.utils import unformat_mtext, mtext_to_string, string_to_mtext
9 |
10 | class UtilsTestCase(unittest.TestCase):
11 | def test_unformat(self):
12 | texts = [r'{\fGOST type A|b0|i0|c204|p34;ЩО\fGOST type A|b0|i0|c0|p34;2-8}',
13 | r'text\Pwith {\fWide Latin|b0|i0|c0|p18;multi} {\fVerdana|b0|i0|c0|p34;format}',
14 | r'test\P\pxi-3,l3,t3;1. list1\P2. list2\P\pi0,l0,tz;\P\pi-3,l3,t3;{\fSymbol|b0|i0|c2|p18;· }bullet1\P{\fSymbol|b0|i0|c2|p18;· }bullet2\P\pi0,l0,tz;\P{\fVani|b0|i0|c0|p34;another font} {\fVerdana|b1|i0|c0|p34;and bold}',
15 | ]
16 | desired1 = [u'ЩО2-8', u'text\Pwith multi format', u'test\P1. list1\P2. list2\P\P· bullet1\P· bullet2\P\Panother font and bold']
17 | desired2 = [u'ЩО2-8', u'text\nwith multi format',
18 | u"""test
19 | 1. list1
20 | 2. list2
21 |
22 | · bullet1
23 | · bullet2
24 |
25 | another font and bold"""]
26 | result1 = list(map(unformat_mtext, texts))
27 | result2 = list(map(mtext_to_string, texts))
28 |
29 | self.assertEqual(result1, desired1)
30 | self.assertEqual(result2, desired2)
31 |
32 | def test_format(self):
33 | text = u'Line1\nLine2\nLine3\n\n\nBackslash\\ and \\P'
34 | desired = u'Line1\PLine2\PLine3\P\P\PBackslash\\\\ and \\\\P'
35 |
36 | self.assertEqual(string_to_mtext(text), desired)
37 |
38 |
39 | if __name__ == '__main__':
40 | unittest.main()
41 |
--------------------------------------------------------------------------------