Spaces:
Sleeping
Sleeping
CI CD markeing comppgain app
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .github/workflows/.gitkeep +0 -0
- .github/workflows/deploy.yml +39 -0
- Dockerfile +22 -0
- __pycache__/app.cpython-310.pyc +0 -0
- __pycache__/app.cpython-37.pyc +0 -0
- app.py +33 -0
- config/config.yaml +0 -0
- main.py +29 -0
- marketing/.nonadmin +0 -0
- marketing/LICENSE_PYTHON.txt +279 -0
- marketing/Scripts/2to3-script.py +4 -0
- marketing/Scripts/2to3.exe +0 -0
- marketing/Scripts/idle-script.py +4 -0
- marketing/Scripts/idle.exe +0 -0
- marketing/Scripts/pip-script.py +10 -0
- marketing/Scripts/pip.exe +0 -0
- marketing/Scripts/pip3-script.py +10 -0
- marketing/Scripts/pip3.exe +0 -0
- marketing/Scripts/pydoc-script.py +4 -0
- marketing/Scripts/pydoc.exe +0 -0
- marketing/Scripts/wheel-script.py +10 -0
- marketing/Scripts/wheel.exe +0 -0
- marketing/Tools/demo/__pycache__/beer.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/eiffel.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/hanoi.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/life.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/markov.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/mcast.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/queens.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/redemo.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/rpython.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/rpythond.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/sortvisu.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/spreadsheet.cpython-39.pyc +0 -0
- marketing/Tools/demo/__pycache__/vector.cpython-39.pyc +0 -0
- marketing/Tools/demo/beer.py +25 -0
- marketing/Tools/demo/eiffel.py +146 -0
- marketing/Tools/demo/hanoi.py +154 -0
- marketing/Tools/demo/life.py +262 -0
- marketing/Tools/demo/markov.py +125 -0
- marketing/Tools/demo/mcast.py +82 -0
- marketing/Tools/demo/queens.py +85 -0
- marketing/Tools/demo/redemo.py +171 -0
- marketing/Tools/demo/rpython.py +37 -0
- marketing/Tools/demo/rpythond.py +58 -0
- marketing/Tools/demo/sortvisu.py +635 -0
- marketing/Tools/demo/spreadsheet.py +829 -0
- marketing/Tools/demo/vector.py +74 -0
- marketing/Tools/i18n/__pycache__/makelocalealias.cpython-39.pyc +0 -0
- marketing/Tools/i18n/__pycache__/msgfmt.cpython-39.pyc +0 -0
.github/workflows/.gitkeep
ADDED
File without changes
|
.github/workflows/deploy.yml
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# .github/workflows/deploy.yml
|
2 |
+
name: CI/CD Pipeline
|
3 |
+
|
4 |
+
on:
|
5 |
+
push:
|
6 |
+
branches:
|
7 |
+
- main
|
8 |
+
|
9 |
+
jobs:
|
10 |
+
build-and-deploy:
|
11 |
+
runs-on: ubuntu-latest
|
12 |
+
|
13 |
+
steps:
|
14 |
+
- name: Checkout code
|
15 |
+
uses: actions/checkout@v3
|
16 |
+
|
17 |
+
- name: Set up Python
|
18 |
+
uses: actions/setup-python@v4
|
19 |
+
with:
|
20 |
+
python-version: "3.10"
|
21 |
+
|
22 |
+
- name: Install dependencies
|
23 |
+
run: |
|
24 |
+
python -m pip install --upgrade pip
|
25 |
+
pip install -r requirements.txt
|
26 |
+
|
27 |
+
- name: Run tests
|
28 |
+
run: |
|
29 |
+
echo "✅ Add Pytest or other checks here"
|
30 |
+
|
31 |
+
- name: Build Docker image
|
32 |
+
run: docker build -t llm-campaign-app .
|
33 |
+
|
34 |
+
# Optional: Push to DockerHub/GHCR (needs secrets configured)
|
35 |
+
# - name: Push Docker Image
|
36 |
+
# run: |
|
37 |
+
# echo "${{ secrets.DOCKERHUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin
|
38 |
+
# docker tag llm-campaign-app your-dockerhub-user/llm-campaign-app
|
39 |
+
# docker push your-dockerhub-user/llm-campaign-app
|
Dockerfile
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Dockerfile
|
2 |
+
FROM python:3.10-slim
|
3 |
+
|
4 |
+
# Set working directory
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Copy project files
|
8 |
+
COPY . .
|
9 |
+
|
10 |
+
# Install system dependencies (if needed)
|
11 |
+
RUN apt-get update && apt-get install -y \
|
12 |
+
build-essential \
|
13 |
+
&& rm -rf /var/lib/apt/lists/*
|
14 |
+
|
15 |
+
# Install Python dependencies
|
16 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
17 |
+
|
18 |
+
# Expose FastAPI port
|
19 |
+
EXPOSE 8000
|
20 |
+
|
21 |
+
# Run FastAPI app (backend)
|
22 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
|
__pycache__/app.cpython-310.pyc
ADDED
Binary file (1.37 kB). View file
|
|
__pycache__/app.cpython-37.pyc
ADDED
Binary file (1.17 kB). View file
|
|
app.py
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# backend/main.py
|
2 |
+
from fastapi import FastAPI, HTTPException
|
3 |
+
from pydantic import BaseModel
|
4 |
+
from src.marketingCampaignGen.components.model.predictor import LLMPredictionPipeline
|
5 |
+
|
6 |
+
|
7 |
+
app = FastAPI(title="Marketing Campaign Generator")
|
8 |
+
|
9 |
+
# Define request schema
|
10 |
+
class ProductRequest(BaseModel):
|
11 |
+
product_name: str
|
12 |
+
features: list[str]
|
13 |
+
brand: str
|
14 |
+
audience: str
|
15 |
+
tone: str
|
16 |
+
goal: str
|
17 |
+
@app.get("/")
|
18 |
+
def read_root():
|
19 |
+
return {"message": "Welcome to the Marketing Campaign Generator API!"}
|
20 |
+
|
21 |
+
@app.post("/generate")
|
22 |
+
def generate_campaign(data: ProductRequest):
|
23 |
+
try:
|
24 |
+
predictor = LLMPredictionPipeline()
|
25 |
+
result = predictor.predict(data.dict())
|
26 |
+
|
27 |
+
|
28 |
+
return {"campaign": result}
|
29 |
+
except Exception as e:
|
30 |
+
raise HTTPException(status_code=500, detail=str(e))
|
31 |
+
if __name__ == "__main__":
|
32 |
+
import uvicorn
|
33 |
+
uvicorn.run("app:app", host="127.0.0.1", port=8000, reload=True)
|
config/config.yaml
ADDED
File without changes
|
main.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import subprocess
|
2 |
+
|
3 |
+
def generate_with_ollama(prompt: str, model_name="mistral") -> str:
|
4 |
+
try:
|
5 |
+
# Run the ollama model using subprocess and pass the prompt via echo
|
6 |
+
result = subprocess.run(
|
7 |
+
["ollama", "run", model_name],
|
8 |
+
input=prompt.encode(),
|
9 |
+
capture_output=True,
|
10 |
+
timeout=300
|
11 |
+
|
12 |
+
)
|
13 |
+
|
14 |
+
if result.returncode != 0:
|
15 |
+
raise RuntimeError(f"Ollama returned error: {result.stderr.decode()}")
|
16 |
+
|
17 |
+
return result.stdout.decode().strip()
|
18 |
+
|
19 |
+
except Exception as e:
|
20 |
+
return f"[Error] Failed to generate content: {e}"
|
21 |
+
|
22 |
+
|
23 |
+
# Example usage
|
24 |
+
if __name__ == "__main__":
|
25 |
+
sample_prompt = "Generate a friendly marketing ad for an eco-friendly water bottle that keeps water cold for 24 hours."
|
26 |
+
print("[Campaign Generator] Sending prompt to Ollama...")
|
27 |
+
output = generate_with_ollama(sample_prompt)
|
28 |
+
print("\n[Generated Campaign]")
|
29 |
+
print(output)
|
marketing/.nonadmin
ADDED
File without changes
|
marketing/LICENSE_PYTHON.txt
ADDED
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
A. HISTORY OF THE SOFTWARE
|
2 |
+
==========================
|
3 |
+
|
4 |
+
Python was created in the early 1990s by Guido van Rossum at Stichting
|
5 |
+
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
|
6 |
+
as a successor of a language called ABC. Guido remains Python's
|
7 |
+
principal author, although it includes many contributions from others.
|
8 |
+
|
9 |
+
In 1995, Guido continued his work on Python at the Corporation for
|
10 |
+
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
|
11 |
+
in Reston, Virginia where he released several versions of the
|
12 |
+
software.
|
13 |
+
|
14 |
+
In May 2000, Guido and the Python core development team moved to
|
15 |
+
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
16 |
+
year, the PythonLabs team moved to Digital Creations, which became
|
17 |
+
Zope Corporation. In 2001, the Python Software Foundation (PSF, see
|
18 |
+
https://www.python.org/psf/) was formed, a non-profit organization
|
19 |
+
created specifically to own Python-related Intellectual Property.
|
20 |
+
Zope Corporation was a sponsoring member of the PSF.
|
21 |
+
|
22 |
+
All Python releases are Open Source (see http://www.opensource.org for
|
23 |
+
the Open Source Definition). Historically, most, but not all, Python
|
24 |
+
releases have also been GPL-compatible; the table below summarizes
|
25 |
+
the various releases.
|
26 |
+
|
27 |
+
Release Derived Year Owner GPL-
|
28 |
+
from compatible? (1)
|
29 |
+
|
30 |
+
0.9.0 thru 1.2 1991-1995 CWI yes
|
31 |
+
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
32 |
+
1.6 1.5.2 2000 CNRI no
|
33 |
+
2.0 1.6 2000 BeOpen.com no
|
34 |
+
1.6.1 1.6 2001 CNRI yes (2)
|
35 |
+
2.1 2.0+1.6.1 2001 PSF no
|
36 |
+
2.0.1 2.0+1.6.1 2001 PSF yes
|
37 |
+
2.1.1 2.1+2.0.1 2001 PSF yes
|
38 |
+
2.1.2 2.1.1 2002 PSF yes
|
39 |
+
2.1.3 2.1.2 2002 PSF yes
|
40 |
+
2.2 and above 2.1.1 2001-now PSF yes
|
41 |
+
|
42 |
+
Footnotes:
|
43 |
+
|
44 |
+
(1) GPL-compatible doesn't mean that we're distributing Python under
|
45 |
+
the GPL. All Python licenses, unlike the GPL, let you distribute
|
46 |
+
a modified version without making your changes open source. The
|
47 |
+
GPL-compatible licenses make it possible to combine Python with
|
48 |
+
other software that is released under the GPL; the others don't.
|
49 |
+
|
50 |
+
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
51 |
+
because its license has a choice of law clause. According to
|
52 |
+
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
53 |
+
is "not incompatible" with the GPL.
|
54 |
+
|
55 |
+
Thanks to the many outside volunteers who have worked under Guido's
|
56 |
+
direction to make these releases possible.
|
57 |
+
|
58 |
+
|
59 |
+
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
60 |
+
===============================================================
|
61 |
+
|
62 |
+
Python software and documentation are licensed under the
|
63 |
+
Python Software Foundation License Version 2.
|
64 |
+
|
65 |
+
Starting with Python 3.8.6, examples, recipes, and other code in
|
66 |
+
the documentation are dual licensed under the PSF License Version 2
|
67 |
+
and the Zero-Clause BSD license.
|
68 |
+
|
69 |
+
Some software incorporated into Python is under different licenses.
|
70 |
+
The licenses are listed with code falling under that license.
|
71 |
+
|
72 |
+
|
73 |
+
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
74 |
+
--------------------------------------------
|
75 |
+
|
76 |
+
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
77 |
+
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
78 |
+
otherwise using this software ("Python") in source or binary form and
|
79 |
+
its associated documentation.
|
80 |
+
|
81 |
+
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
82 |
+
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
83 |
+
analyze, test, perform and/or display publicly, prepare derivative works,
|
84 |
+
distribute, and otherwise use Python alone or in any derivative version,
|
85 |
+
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
86 |
+
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
87 |
+
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation;
|
88 |
+
All Rights Reserved" are retained in Python alone or in any derivative version
|
89 |
+
prepared by Licensee.
|
90 |
+
|
91 |
+
3. In the event Licensee prepares a derivative work that is based on
|
92 |
+
or incorporates Python or any part thereof, and wants to make
|
93 |
+
the derivative work available to others as provided herein, then
|
94 |
+
Licensee hereby agrees to include in any such work a brief summary of
|
95 |
+
the changes made to Python.
|
96 |
+
|
97 |
+
4. PSF is making Python available to Licensee on an "AS IS"
|
98 |
+
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
99 |
+
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
100 |
+
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
101 |
+
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
102 |
+
INFRINGE ANY THIRD PARTY RIGHTS.
|
103 |
+
|
104 |
+
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
105 |
+
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
106 |
+
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
107 |
+
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
108 |
+
|
109 |
+
6. This License Agreement will automatically terminate upon a material
|
110 |
+
breach of its terms and conditions.
|
111 |
+
|
112 |
+
7. Nothing in this License Agreement shall be deemed to create any
|
113 |
+
relationship of agency, partnership, or joint venture between PSF and
|
114 |
+
Licensee. This License Agreement does not grant permission to use PSF
|
115 |
+
trademarks or trade name in a trademark sense to endorse or promote
|
116 |
+
products or services of Licensee, or any third party.
|
117 |
+
|
118 |
+
8. By copying, installing or otherwise using Python, Licensee
|
119 |
+
agrees to be bound by the terms and conditions of this License
|
120 |
+
Agreement.
|
121 |
+
|
122 |
+
|
123 |
+
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
124 |
+
-------------------------------------------
|
125 |
+
|
126 |
+
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
127 |
+
|
128 |
+
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
129 |
+
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
130 |
+
Individual or Organization ("Licensee") accessing and otherwise using
|
131 |
+
this software in source or binary form and its associated
|
132 |
+
documentation ("the Software").
|
133 |
+
|
134 |
+
2. Subject to the terms and conditions of this BeOpen Python License
|
135 |
+
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
136 |
+
royalty-free, world-wide license to reproduce, analyze, test, perform
|
137 |
+
and/or display publicly, prepare derivative works, distribute, and
|
138 |
+
otherwise use the Software alone or in any derivative version,
|
139 |
+
provided, however, that the BeOpen Python License is retained in the
|
140 |
+
Software, alone or in any derivative version prepared by Licensee.
|
141 |
+
|
142 |
+
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
143 |
+
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
144 |
+
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
145 |
+
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
146 |
+
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
147 |
+
INFRINGE ANY THIRD PARTY RIGHTS.
|
148 |
+
|
149 |
+
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
150 |
+
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
151 |
+
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
152 |
+
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
153 |
+
|
154 |
+
5. This License Agreement will automatically terminate upon a material
|
155 |
+
breach of its terms and conditions.
|
156 |
+
|
157 |
+
6. This License Agreement shall be governed by and interpreted in all
|
158 |
+
respects by the law of the State of California, excluding conflict of
|
159 |
+
law provisions. Nothing in this License Agreement shall be deemed to
|
160 |
+
create any relationship of agency, partnership, or joint venture
|
161 |
+
between BeOpen and Licensee. This License Agreement does not grant
|
162 |
+
permission to use BeOpen trademarks or trade names in a trademark
|
163 |
+
sense to endorse or promote products or services of Licensee, or any
|
164 |
+
third party. As an exception, the "BeOpen Python" logos available at
|
165 |
+
http://www.pythonlabs.com/logos.html may be used according to the
|
166 |
+
permissions granted on that web page.
|
167 |
+
|
168 |
+
7. By copying, installing or otherwise using the software, Licensee
|
169 |
+
agrees to be bound by the terms and conditions of this License
|
170 |
+
Agreement.
|
171 |
+
|
172 |
+
|
173 |
+
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
174 |
+
---------------------------------------
|
175 |
+
|
176 |
+
1. This LICENSE AGREEMENT is between the Corporation for National
|
177 |
+
Research Initiatives, having an office at 1895 Preston White Drive,
|
178 |
+
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
179 |
+
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
180 |
+
source or binary form and its associated documentation.
|
181 |
+
|
182 |
+
2. Subject to the terms and conditions of this License Agreement, CNRI
|
183 |
+
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
184 |
+
license to reproduce, analyze, test, perform and/or display publicly,
|
185 |
+
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
186 |
+
alone or in any derivative version, provided, however, that CNRI's
|
187 |
+
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
188 |
+
1995-2001 Corporation for National Research Initiatives; All Rights
|
189 |
+
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
190 |
+
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
191 |
+
Agreement, Licensee may substitute the following text (omitting the
|
192 |
+
quotes): "Python 1.6.1 is made available subject to the terms and
|
193 |
+
conditions in CNRI's License Agreement. This Agreement together with
|
194 |
+
Python 1.6.1 may be located on the Internet using the following
|
195 |
+
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
196 |
+
Agreement may also be obtained from a proxy server on the Internet
|
197 |
+
using the following URL: http://hdl.handle.net/1895.22/1013".
|
198 |
+
|
199 |
+
3. In the event Licensee prepares a derivative work that is based on
|
200 |
+
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
201 |
+
the derivative work available to others as provided herein, then
|
202 |
+
Licensee hereby agrees to include in any such work a brief summary of
|
203 |
+
the changes made to Python 1.6.1.
|
204 |
+
|
205 |
+
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
206 |
+
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
207 |
+
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
208 |
+
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
209 |
+
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
210 |
+
INFRINGE ANY THIRD PARTY RIGHTS.
|
211 |
+
|
212 |
+
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
213 |
+
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
214 |
+
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
215 |
+
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
216 |
+
|
217 |
+
6. This License Agreement will automatically terminate upon a material
|
218 |
+
breach of its terms and conditions.
|
219 |
+
|
220 |
+
7. This License Agreement shall be governed by the federal
|
221 |
+
intellectual property law of the United States, including without
|
222 |
+
limitation the federal copyright law, and, to the extent such
|
223 |
+
U.S. federal law does not apply, by the law of the Commonwealth of
|
224 |
+
Virginia, excluding Virginia's conflict of law provisions.
|
225 |
+
Notwithstanding the foregoing, with regard to derivative works based
|
226 |
+
on Python 1.6.1 that incorporate non-separable material that was
|
227 |
+
previously distributed under the GNU General Public License (GPL), the
|
228 |
+
law of the Commonwealth of Virginia shall govern this License
|
229 |
+
Agreement only as to issues arising under or with respect to
|
230 |
+
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
231 |
+
License Agreement shall be deemed to create any relationship of
|
232 |
+
agency, partnership, or joint venture between CNRI and Licensee. This
|
233 |
+
License Agreement does not grant permission to use CNRI trademarks or
|
234 |
+
trade name in a trademark sense to endorse or promote products or
|
235 |
+
services of Licensee, or any third party.
|
236 |
+
|
237 |
+
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
238 |
+
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
239 |
+
bound by the terms and conditions of this License Agreement.
|
240 |
+
|
241 |
+
ACCEPT
|
242 |
+
|
243 |
+
|
244 |
+
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
245 |
+
--------------------------------------------------
|
246 |
+
|
247 |
+
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
248 |
+
The Netherlands. All rights reserved.
|
249 |
+
|
250 |
+
Permission to use, copy, modify, and distribute this software and its
|
251 |
+
documentation for any purpose and without fee is hereby granted,
|
252 |
+
provided that the above copyright notice appear in all copies and that
|
253 |
+
both that copyright notice and this permission notice appear in
|
254 |
+
supporting documentation, and that the name of Stichting Mathematisch
|
255 |
+
Centrum or CWI not be used in advertising or publicity pertaining to
|
256 |
+
distribution of the software without specific, written prior
|
257 |
+
permission.
|
258 |
+
|
259 |
+
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
260 |
+
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
261 |
+
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
262 |
+
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
263 |
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
264 |
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
265 |
+
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
266 |
+
|
267 |
+
ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
|
268 |
+
----------------------------------------------------------------------
|
269 |
+
|
270 |
+
Permission to use, copy, modify, and/or distribute this software for any
|
271 |
+
purpose with or without fee is hereby granted.
|
272 |
+
|
273 |
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
274 |
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
275 |
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
276 |
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
277 |
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
278 |
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
279 |
+
PERFORMANCE OF THIS SOFTWARE.
|
marketing/Scripts/2to3-script.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys
|
2 |
+
from lib2to3.main import main
|
3 |
+
|
4 |
+
sys.exit(main("lib2to3.fixes"))
|
marketing/Scripts/2to3.exe
ADDED
Binary file (54 kB). View file
|
|
marketing/Scripts/idle-script.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
from idlelib.pyshell import main
|
3 |
+
if __name__ == '__main__':
|
4 |
+
main()
|
marketing/Scripts/idle.exe
ADDED
Binary file (54 kB). View file
|
|
marketing/Scripts/pip-script.py
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
# -*- coding: utf-8 -*-
|
3 |
+
import re
|
4 |
+
import sys
|
5 |
+
|
6 |
+
from pip._internal.cli.main import main
|
7 |
+
|
8 |
+
if __name__ == '__main__':
|
9 |
+
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
10 |
+
sys.exit(main())
|
marketing/Scripts/pip.exe
ADDED
Binary file (54 kB). View file
|
|
marketing/Scripts/pip3-script.py
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
# -*- coding: utf-8 -*-
|
3 |
+
import re
|
4 |
+
import sys
|
5 |
+
|
6 |
+
from pip._internal.cli.main import main
|
7 |
+
|
8 |
+
if __name__ == '__main__':
|
9 |
+
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
10 |
+
sys.exit(main())
|
marketing/Scripts/pip3.exe
ADDED
Binary file (54 kB). View file
|
|
marketing/Scripts/pydoc-script.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import pydoc
|
3 |
+
if __name__ == '__main__':
|
4 |
+
pydoc.cli()
|
marketing/Scripts/pydoc.exe
ADDED
Binary file (54 kB). View file
|
|
marketing/Scripts/wheel-script.py
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
# -*- coding: utf-8 -*-
|
3 |
+
import re
|
4 |
+
import sys
|
5 |
+
|
6 |
+
from wheel.cli import main
|
7 |
+
|
8 |
+
if __name__ == '__main__':
|
9 |
+
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
10 |
+
sys.exit(main())
|
marketing/Scripts/wheel.exe
ADDED
Binary file (54 kB). View file
|
|
marketing/Tools/demo/__pycache__/beer.cpython-39.pyc
ADDED
Binary file (738 Bytes). View file
|
|
marketing/Tools/demo/__pycache__/eiffel.cpython-39.pyc
ADDED
Binary file (5.64 kB). View file
|
|
marketing/Tools/demo/__pycache__/hanoi.cpython-39.pyc
ADDED
Binary file (3.34 kB). View file
|
|
marketing/Tools/demo/__pycache__/life.cpython-39.pyc
ADDED
Binary file (6.72 kB). View file
|
|
marketing/Tools/demo/__pycache__/markov.cpython-39.pyc
ADDED
Binary file (3.34 kB). View file
|
|
marketing/Tools/demo/__pycache__/mcast.cpython-39.pyc
ADDED
Binary file (1.9 kB). View file
|
|
marketing/Tools/demo/__pycache__/queens.cpython-39.pyc
ADDED
Binary file (2.41 kB). View file
|
|
marketing/Tools/demo/__pycache__/redemo.cpython-39.pyc
ADDED
Binary file (4.11 kB). View file
|
|
marketing/Tools/demo/__pycache__/rpython.cpython-39.pyc
ADDED
Binary file (957 Bytes). View file
|
|
marketing/Tools/demo/__pycache__/rpythond.cpython-39.pyc
ADDED
Binary file (1.38 kB). View file
|
|
marketing/Tools/demo/__pycache__/sortvisu.cpython-39.pyc
ADDED
Binary file (18.7 kB). View file
|
|
marketing/Tools/demo/__pycache__/spreadsheet.cpython-39.pyc
ADDED
Binary file (24.9 kB). View file
|
|
marketing/Tools/demo/__pycache__/vector.cpython-39.pyc
ADDED
Binary file (2.71 kB). View file
|
|
marketing/Tools/demo/beer.py
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
A Python version of the classic "bottles of beer on the wall" programming
|
5 |
+
example.
|
6 |
+
|
7 |
+
By Guido van Rossum, demystified after a version by Fredrik Lundh.
|
8 |
+
"""
|
9 |
+
|
10 |
+
import sys
|
11 |
+
|
12 |
+
n = 100
|
13 |
+
if sys.argv[1:]:
|
14 |
+
n = int(sys.argv[1])
|
15 |
+
|
16 |
+
def bottle(n):
|
17 |
+
if n == 0: return "no more bottles of beer"
|
18 |
+
if n == 1: return "one bottle of beer"
|
19 |
+
return str(n) + " bottles of beer"
|
20 |
+
|
21 |
+
for i in range(n, 0, -1):
|
22 |
+
print(bottle(i), "on the wall,")
|
23 |
+
print(bottle(i) + ".")
|
24 |
+
print("Take one down, pass it around,")
|
25 |
+
print(bottle(i-1), "on the wall.")
|
marketing/Tools/demo/eiffel.py
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
Support Eiffel-style preconditions and postconditions for functions.
|
5 |
+
|
6 |
+
An example for Python metaclasses.
|
7 |
+
"""
|
8 |
+
|
9 |
+
import unittest
|
10 |
+
from types import FunctionType as function
|
11 |
+
|
12 |
+
class EiffelBaseMetaClass(type):
|
13 |
+
|
14 |
+
def __new__(meta, name, bases, dict):
|
15 |
+
meta.convert_methods(dict)
|
16 |
+
return super(EiffelBaseMetaClass, meta).__new__(
|
17 |
+
meta, name, bases, dict)
|
18 |
+
|
19 |
+
@classmethod
|
20 |
+
def convert_methods(cls, dict):
|
21 |
+
"""Replace functions in dict with EiffelMethod wrappers.
|
22 |
+
|
23 |
+
The dict is modified in place.
|
24 |
+
|
25 |
+
If a method ends in _pre or _post, it is removed from the dict
|
26 |
+
regardless of whether there is a corresponding method.
|
27 |
+
"""
|
28 |
+
# find methods with pre or post conditions
|
29 |
+
methods = []
|
30 |
+
for k, v in dict.items():
|
31 |
+
if k.endswith('_pre') or k.endswith('_post'):
|
32 |
+
assert isinstance(v, function)
|
33 |
+
elif isinstance(v, function):
|
34 |
+
methods.append(k)
|
35 |
+
for m in methods:
|
36 |
+
pre = dict.get("%s_pre" % m)
|
37 |
+
post = dict.get("%s_post" % m)
|
38 |
+
if pre or post:
|
39 |
+
dict[m] = cls.make_eiffel_method(dict[m], pre, post)
|
40 |
+
|
41 |
+
|
42 |
+
class EiffelMetaClass1(EiffelBaseMetaClass):
|
43 |
+
# an implementation of the "eiffel" meta class that uses nested functions
|
44 |
+
|
45 |
+
@staticmethod
|
46 |
+
def make_eiffel_method(func, pre, post):
|
47 |
+
def method(self, *args, **kwargs):
|
48 |
+
if pre:
|
49 |
+
pre(self, *args, **kwargs)
|
50 |
+
rv = func(self, *args, **kwargs)
|
51 |
+
if post:
|
52 |
+
post(self, rv, *args, **kwargs)
|
53 |
+
return rv
|
54 |
+
|
55 |
+
if func.__doc__:
|
56 |
+
method.__doc__ = func.__doc__
|
57 |
+
|
58 |
+
return method
|
59 |
+
|
60 |
+
|
61 |
+
class EiffelMethodWrapper:
|
62 |
+
|
63 |
+
def __init__(self, inst, descr):
|
64 |
+
self._inst = inst
|
65 |
+
self._descr = descr
|
66 |
+
|
67 |
+
def __call__(self, *args, **kwargs):
|
68 |
+
return self._descr.callmethod(self._inst, args, kwargs)
|
69 |
+
|
70 |
+
|
71 |
+
class EiffelDescriptor:
|
72 |
+
|
73 |
+
def __init__(self, func, pre, post):
|
74 |
+
self._func = func
|
75 |
+
self._pre = pre
|
76 |
+
self._post = post
|
77 |
+
|
78 |
+
self.__name__ = func.__name__
|
79 |
+
self.__doc__ = func.__doc__
|
80 |
+
|
81 |
+
def __get__(self, obj, cls=None):
|
82 |
+
return EiffelMethodWrapper(obj, self)
|
83 |
+
|
84 |
+
def callmethod(self, inst, args, kwargs):
|
85 |
+
if self._pre:
|
86 |
+
self._pre(inst, *args, **kwargs)
|
87 |
+
x = self._func(inst, *args, **kwargs)
|
88 |
+
if self._post:
|
89 |
+
self._post(inst, x, *args, **kwargs)
|
90 |
+
return x
|
91 |
+
|
92 |
+
|
93 |
+
class EiffelMetaClass2(EiffelBaseMetaClass):
|
94 |
+
# an implementation of the "eiffel" meta class that uses descriptors
|
95 |
+
|
96 |
+
make_eiffel_method = EiffelDescriptor
|
97 |
+
|
98 |
+
|
99 |
+
class Tests(unittest.TestCase):
|
100 |
+
|
101 |
+
def testEiffelMetaClass1(self):
|
102 |
+
self._test(EiffelMetaClass1)
|
103 |
+
|
104 |
+
def testEiffelMetaClass2(self):
|
105 |
+
self._test(EiffelMetaClass2)
|
106 |
+
|
107 |
+
def _test(self, metaclass):
|
108 |
+
class Eiffel(metaclass=metaclass):
|
109 |
+
pass
|
110 |
+
|
111 |
+
class Test(Eiffel):
|
112 |
+
def m(self, arg):
|
113 |
+
"""Make it a little larger"""
|
114 |
+
return arg + 1
|
115 |
+
|
116 |
+
def m2(self, arg):
|
117 |
+
"""Make it a little larger"""
|
118 |
+
return arg + 1
|
119 |
+
|
120 |
+
def m2_pre(self, arg):
|
121 |
+
assert arg > 0
|
122 |
+
|
123 |
+
def m2_post(self, result, arg):
|
124 |
+
assert result > arg
|
125 |
+
|
126 |
+
class Sub(Test):
|
127 |
+
def m2(self, arg):
|
128 |
+
return arg**2
|
129 |
+
|
130 |
+
def m2_post(self, Result, arg):
|
131 |
+
super(Sub, self).m2_post(Result, arg)
|
132 |
+
assert Result < 100
|
133 |
+
|
134 |
+
t = Test()
|
135 |
+
self.assertEqual(t.m(1), 2)
|
136 |
+
self.assertEqual(t.m2(1), 2)
|
137 |
+
self.assertRaises(AssertionError, t.m2, 0)
|
138 |
+
|
139 |
+
s = Sub()
|
140 |
+
self.assertRaises(AssertionError, s.m2, 1)
|
141 |
+
self.assertRaises(AssertionError, s.m2, 10)
|
142 |
+
self.assertEqual(s.m2(5), 25)
|
143 |
+
|
144 |
+
|
145 |
+
if __name__ == "__main__":
|
146 |
+
unittest.main()
|
marketing/Tools/demo/hanoi.py
ADDED
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
Animated Towers of Hanoi using Tk with optional bitmap file in background.
|
5 |
+
|
6 |
+
Usage: hanoi.py [n [bitmapfile]]
|
7 |
+
|
8 |
+
n is the number of pieces to animate; default is 4, maximum 15.
|
9 |
+
|
10 |
+
The bitmap file can be any X11 bitmap file (look in /usr/include/X11/bitmaps for
|
11 |
+
samples); it is displayed as the background of the animation. Default is no
|
12 |
+
bitmap.
|
13 |
+
"""
|
14 |
+
|
15 |
+
from tkinter import Tk, Canvas
|
16 |
+
|
17 |
+
# Basic Towers-of-Hanoi algorithm: move n pieces from a to b, using c
|
18 |
+
# as temporary. For each move, call report()
|
19 |
+
def hanoi(n, a, b, c, report):
|
20 |
+
if n <= 0: return
|
21 |
+
hanoi(n-1, a, c, b, report)
|
22 |
+
report(n, a, b)
|
23 |
+
hanoi(n-1, c, b, a, report)
|
24 |
+
|
25 |
+
|
26 |
+
# The graphical interface
|
27 |
+
class Tkhanoi:
|
28 |
+
|
29 |
+
# Create our objects
|
30 |
+
def __init__(self, n, bitmap=None):
|
31 |
+
self.n = n
|
32 |
+
self.tk = tk = Tk()
|
33 |
+
self.canvas = c = Canvas(tk)
|
34 |
+
c.pack()
|
35 |
+
width, height = tk.getint(c['width']), tk.getint(c['height'])
|
36 |
+
|
37 |
+
# Add background bitmap
|
38 |
+
if bitmap:
|
39 |
+
self.bitmap = c.create_bitmap(width//2, height//2,
|
40 |
+
bitmap=bitmap,
|
41 |
+
foreground='blue')
|
42 |
+
|
43 |
+
# Generate pegs
|
44 |
+
pegwidth = 10
|
45 |
+
pegheight = height//2
|
46 |
+
pegdist = width//3
|
47 |
+
x1, y1 = (pegdist-pegwidth)//2, height*1//3
|
48 |
+
x2, y2 = x1+pegwidth, y1+pegheight
|
49 |
+
self.pegs = []
|
50 |
+
p = c.create_rectangle(x1, y1, x2, y2, fill='black')
|
51 |
+
self.pegs.append(p)
|
52 |
+
x1, x2 = x1+pegdist, x2+pegdist
|
53 |
+
p = c.create_rectangle(x1, y1, x2, y2, fill='black')
|
54 |
+
self.pegs.append(p)
|
55 |
+
x1, x2 = x1+pegdist, x2+pegdist
|
56 |
+
p = c.create_rectangle(x1, y1, x2, y2, fill='black')
|
57 |
+
self.pegs.append(p)
|
58 |
+
self.tk.update()
|
59 |
+
|
60 |
+
# Generate pieces
|
61 |
+
pieceheight = pegheight//16
|
62 |
+
maxpiecewidth = pegdist*2//3
|
63 |
+
minpiecewidth = 2*pegwidth
|
64 |
+
self.pegstate = [[], [], []]
|
65 |
+
self.pieces = {}
|
66 |
+
x1, y1 = (pegdist-maxpiecewidth)//2, y2-pieceheight-2
|
67 |
+
x2, y2 = x1+maxpiecewidth, y1+pieceheight
|
68 |
+
dx = (maxpiecewidth-minpiecewidth) // (2*max(1, n-1))
|
69 |
+
for i in range(n, 0, -1):
|
70 |
+
p = c.create_rectangle(x1, y1, x2, y2, fill='red')
|
71 |
+
self.pieces[i] = p
|
72 |
+
self.pegstate[0].append(i)
|
73 |
+
x1, x2 = x1 + dx, x2-dx
|
74 |
+
y1, y2 = y1 - pieceheight-2, y2-pieceheight-2
|
75 |
+
self.tk.update()
|
76 |
+
self.tk.after(25)
|
77 |
+
|
78 |
+
# Run -- never returns
|
79 |
+
def run(self):
|
80 |
+
while True:
|
81 |
+
hanoi(self.n, 0, 1, 2, self.report)
|
82 |
+
hanoi(self.n, 1, 2, 0, self.report)
|
83 |
+
hanoi(self.n, 2, 0, 1, self.report)
|
84 |
+
hanoi(self.n, 0, 2, 1, self.report)
|
85 |
+
hanoi(self.n, 2, 1, 0, self.report)
|
86 |
+
hanoi(self.n, 1, 0, 2, self.report)
|
87 |
+
|
88 |
+
# Reporting callback for the actual hanoi function
|
89 |
+
def report(self, i, a, b):
|
90 |
+
if self.pegstate[a][-1] != i: raise RuntimeError # Assertion
|
91 |
+
del self.pegstate[a][-1]
|
92 |
+
p = self.pieces[i]
|
93 |
+
c = self.canvas
|
94 |
+
|
95 |
+
# Lift the piece above peg a
|
96 |
+
ax1, ay1, ax2, ay2 = c.bbox(self.pegs[a])
|
97 |
+
while True:
|
98 |
+
x1, y1, x2, y2 = c.bbox(p)
|
99 |
+
if y2 < ay1: break
|
100 |
+
c.move(p, 0, -1)
|
101 |
+
self.tk.update()
|
102 |
+
|
103 |
+
# Move it towards peg b
|
104 |
+
bx1, by1, bx2, by2 = c.bbox(self.pegs[b])
|
105 |
+
newcenter = (bx1+bx2)//2
|
106 |
+
while True:
|
107 |
+
x1, y1, x2, y2 = c.bbox(p)
|
108 |
+
center = (x1+x2)//2
|
109 |
+
if center == newcenter: break
|
110 |
+
if center > newcenter: c.move(p, -1, 0)
|
111 |
+
else: c.move(p, 1, 0)
|
112 |
+
self.tk.update()
|
113 |
+
|
114 |
+
# Move it down on top of the previous piece
|
115 |
+
pieceheight = y2-y1
|
116 |
+
newbottom = by2 - pieceheight*len(self.pegstate[b]) - 2
|
117 |
+
while True:
|
118 |
+
x1, y1, x2, y2 = c.bbox(p)
|
119 |
+
if y2 >= newbottom: break
|
120 |
+
c.move(p, 0, 1)
|
121 |
+
self.tk.update()
|
122 |
+
|
123 |
+
# Update peg state
|
124 |
+
self.pegstate[b].append(i)
|
125 |
+
|
126 |
+
|
127 |
+
def main():
|
128 |
+
import sys
|
129 |
+
|
130 |
+
# First argument is number of pegs, default 4
|
131 |
+
if sys.argv[1:]:
|
132 |
+
n = int(sys.argv[1])
|
133 |
+
else:
|
134 |
+
n = 4
|
135 |
+
|
136 |
+
# Second argument is bitmap file, default none
|
137 |
+
if sys.argv[2:]:
|
138 |
+
bitmap = sys.argv[2]
|
139 |
+
# Reverse meaning of leading '@' compared to Tk
|
140 |
+
if bitmap[0] == '@': bitmap = bitmap[1:]
|
141 |
+
else: bitmap = '@' + bitmap
|
142 |
+
else:
|
143 |
+
bitmap = None
|
144 |
+
|
145 |
+
# Create the graphical objects...
|
146 |
+
h = Tkhanoi(n, bitmap)
|
147 |
+
|
148 |
+
# ...and run!
|
149 |
+
h.run()
|
150 |
+
|
151 |
+
|
152 |
+
# Call main when run as script
|
153 |
+
if __name__ == '__main__':
|
154 |
+
main()
|
marketing/Tools/demo/life.py
ADDED
@@ -0,0 +1,262 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
A curses-based version of Conway's Game of Life.
|
5 |
+
|
6 |
+
An empty board will be displayed, and the following commands are available:
|
7 |
+
E : Erase the board
|
8 |
+
R : Fill the board randomly
|
9 |
+
S : Step for a single generation
|
10 |
+
C : Update continuously until a key is struck
|
11 |
+
Q : Quit
|
12 |
+
Cursor keys : Move the cursor around the board
|
13 |
+
Space or Enter : Toggle the contents of the cursor's position
|
14 |
+
|
15 |
+
Contributed by Andrew Kuchling, Mouse support and color by Dafydd Crosby.
|
16 |
+
"""
|
17 |
+
|
18 |
+
import curses
|
19 |
+
import random
|
20 |
+
|
21 |
+
|
22 |
+
class LifeBoard:
|
23 |
+
"""Encapsulates a Life board
|
24 |
+
|
25 |
+
Attributes:
|
26 |
+
X,Y : horizontal and vertical size of the board
|
27 |
+
state : dictionary mapping (x,y) to 0 or 1
|
28 |
+
|
29 |
+
Methods:
|
30 |
+
display(update_board) -- If update_board is true, compute the
|
31 |
+
next generation. Then display the state
|
32 |
+
of the board and refresh the screen.
|
33 |
+
erase() -- clear the entire board
|
34 |
+
make_random() -- fill the board randomly
|
35 |
+
set(y,x) -- set the given cell to Live; doesn't refresh the screen
|
36 |
+
toggle(y,x) -- change the given cell from live to dead, or vice
|
37 |
+
versa, and refresh the screen display
|
38 |
+
|
39 |
+
"""
|
40 |
+
def __init__(self, scr, char=ord('*')):
|
41 |
+
"""Create a new LifeBoard instance.
|
42 |
+
|
43 |
+
scr -- curses screen object to use for display
|
44 |
+
char -- character used to render live cells (default: '*')
|
45 |
+
"""
|
46 |
+
self.state = {}
|
47 |
+
self.scr = scr
|
48 |
+
Y, X = self.scr.getmaxyx()
|
49 |
+
self.X, self.Y = X - 2, Y - 2 - 1
|
50 |
+
self.char = char
|
51 |
+
self.scr.clear()
|
52 |
+
|
53 |
+
# Draw a border around the board
|
54 |
+
border_line = '+' + (self.X * '-') + '+'
|
55 |
+
self.scr.addstr(0, 0, border_line)
|
56 |
+
self.scr.addstr(self.Y + 1, 0, border_line)
|
57 |
+
for y in range(0, self.Y):
|
58 |
+
self.scr.addstr(1 + y, 0, '|')
|
59 |
+
self.scr.addstr(1 + y, self.X + 1, '|')
|
60 |
+
self.scr.refresh()
|
61 |
+
|
62 |
+
def set(self, y, x):
|
63 |
+
"""Set a cell to the live state"""
|
64 |
+
if x < 0 or self.X <= x or y < 0 or self.Y <= y:
|
65 |
+
raise ValueError("Coordinates out of range %i,%i" % (y, x))
|
66 |
+
self.state[x, y] = 1
|
67 |
+
|
68 |
+
def toggle(self, y, x):
|
69 |
+
"""Toggle a cell's state between live and dead"""
|
70 |
+
if x < 0 or self.X <= x or y < 0 or self.Y <= y:
|
71 |
+
raise ValueError("Coordinates out of range %i,%i" % (y, x))
|
72 |
+
if (x, y) in self.state:
|
73 |
+
del self.state[x, y]
|
74 |
+
self.scr.addch(y + 1, x + 1, ' ')
|
75 |
+
else:
|
76 |
+
self.state[x, y] = 1
|
77 |
+
if curses.has_colors():
|
78 |
+
# Let's pick a random color!
|
79 |
+
self.scr.attrset(curses.color_pair(random.randrange(1, 7)))
|
80 |
+
self.scr.addch(y + 1, x + 1, self.char)
|
81 |
+
self.scr.attrset(0)
|
82 |
+
self.scr.refresh()
|
83 |
+
|
84 |
+
def erase(self):
|
85 |
+
"""Clear the entire board and update the board display"""
|
86 |
+
self.state = {}
|
87 |
+
self.display(update_board=False)
|
88 |
+
|
89 |
+
def display(self, update_board=True):
|
90 |
+
"""Display the whole board, optionally computing one generation"""
|
91 |
+
M, N = self.X, self.Y
|
92 |
+
if not update_board:
|
93 |
+
for i in range(0, M):
|
94 |
+
for j in range(0, N):
|
95 |
+
if (i, j) in self.state:
|
96 |
+
self.scr.addch(j + 1, i + 1, self.char)
|
97 |
+
else:
|
98 |
+
self.scr.addch(j + 1, i + 1, ' ')
|
99 |
+
self.scr.refresh()
|
100 |
+
return
|
101 |
+
|
102 |
+
d = {}
|
103 |
+
self.boring = 1
|
104 |
+
for i in range(0, M):
|
105 |
+
L = range(max(0, i - 1), min(M, i + 2))
|
106 |
+
for j in range(0, N):
|
107 |
+
s = 0
|
108 |
+
live = (i, j) in self.state
|
109 |
+
for k in range(max(0, j - 1), min(N, j + 2)):
|
110 |
+
for l in L:
|
111 |
+
if (l, k) in self.state:
|
112 |
+
s += 1
|
113 |
+
s -= live
|
114 |
+
if s == 3:
|
115 |
+
# Birth
|
116 |
+
d[i, j] = 1
|
117 |
+
if curses.has_colors():
|
118 |
+
# Let's pick a random color!
|
119 |
+
self.scr.attrset(curses.color_pair(
|
120 |
+
random.randrange(1, 7)))
|
121 |
+
self.scr.addch(j + 1, i + 1, self.char)
|
122 |
+
self.scr.attrset(0)
|
123 |
+
if not live:
|
124 |
+
self.boring = 0
|
125 |
+
elif s == 2 and live:
|
126 |
+
# Survival
|
127 |
+
d[i, j] = 1
|
128 |
+
elif live:
|
129 |
+
# Death
|
130 |
+
self.scr.addch(j + 1, i + 1, ' ')
|
131 |
+
self.boring = 0
|
132 |
+
self.state = d
|
133 |
+
self.scr.refresh()
|
134 |
+
|
135 |
+
def make_random(self):
|
136 |
+
"Fill the board with a random pattern"
|
137 |
+
self.state = {}
|
138 |
+
for i in range(0, self.X):
|
139 |
+
for j in range(0, self.Y):
|
140 |
+
if random.random() > 0.5:
|
141 |
+
self.set(j, i)
|
142 |
+
|
143 |
+
|
144 |
+
def erase_menu(stdscr, menu_y):
|
145 |
+
"Clear the space where the menu resides"
|
146 |
+
stdscr.move(menu_y, 0)
|
147 |
+
stdscr.clrtoeol()
|
148 |
+
stdscr.move(menu_y + 1, 0)
|
149 |
+
stdscr.clrtoeol()
|
150 |
+
|
151 |
+
|
152 |
+
def display_menu(stdscr, menu_y):
|
153 |
+
"Display the menu of possible keystroke commands"
|
154 |
+
erase_menu(stdscr, menu_y)
|
155 |
+
|
156 |
+
# If color, then light the menu up :-)
|
157 |
+
if curses.has_colors():
|
158 |
+
stdscr.attrset(curses.color_pair(1))
|
159 |
+
stdscr.addstr(menu_y, 4,
|
160 |
+
'Use the cursor keys to move, and space or Enter to toggle a cell.')
|
161 |
+
stdscr.addstr(menu_y + 1, 4,
|
162 |
+
'E)rase the board, R)andom fill, S)tep once or C)ontinuously, Q)uit')
|
163 |
+
stdscr.attrset(0)
|
164 |
+
|
165 |
+
|
166 |
+
def keyloop(stdscr):
|
167 |
+
# Clear the screen and display the menu of keys
|
168 |
+
stdscr.clear()
|
169 |
+
stdscr_y, stdscr_x = stdscr.getmaxyx()
|
170 |
+
menu_y = (stdscr_y - 3) - 1
|
171 |
+
display_menu(stdscr, menu_y)
|
172 |
+
|
173 |
+
# If color, then initialize the color pairs
|
174 |
+
if curses.has_colors():
|
175 |
+
curses.init_pair(1, curses.COLOR_BLUE, 0)
|
176 |
+
curses.init_pair(2, curses.COLOR_CYAN, 0)
|
177 |
+
curses.init_pair(3, curses.COLOR_GREEN, 0)
|
178 |
+
curses.init_pair(4, curses.COLOR_MAGENTA, 0)
|
179 |
+
curses.init_pair(5, curses.COLOR_RED, 0)
|
180 |
+
curses.init_pair(6, curses.COLOR_YELLOW, 0)
|
181 |
+
curses.init_pair(7, curses.COLOR_WHITE, 0)
|
182 |
+
|
183 |
+
# Set up the mask to listen for mouse events
|
184 |
+
curses.mousemask(curses.BUTTON1_CLICKED)
|
185 |
+
|
186 |
+
# Allocate a subwindow for the Life board and create the board object
|
187 |
+
subwin = stdscr.subwin(stdscr_y - 3, stdscr_x, 0, 0)
|
188 |
+
board = LifeBoard(subwin, char=ord('*'))
|
189 |
+
board.display(update_board=False)
|
190 |
+
|
191 |
+
# xpos, ypos are the cursor's position
|
192 |
+
xpos, ypos = board.X // 2, board.Y // 2
|
193 |
+
|
194 |
+
# Main loop:
|
195 |
+
while True:
|
196 |
+
stdscr.move(1 + ypos, 1 + xpos) # Move the cursor
|
197 |
+
c = stdscr.getch() # Get a keystroke
|
198 |
+
if 0 < c < 256:
|
199 |
+
c = chr(c)
|
200 |
+
if c in ' \n':
|
201 |
+
board.toggle(ypos, xpos)
|
202 |
+
elif c in 'Cc':
|
203 |
+
erase_menu(stdscr, menu_y)
|
204 |
+
stdscr.addstr(menu_y, 6, ' Hit any key to stop continuously '
|
205 |
+
'updating the screen.')
|
206 |
+
stdscr.refresh()
|
207 |
+
# Activate nodelay mode; getch() will return -1
|
208 |
+
# if no keystroke is available, instead of waiting.
|
209 |
+
stdscr.nodelay(1)
|
210 |
+
while True:
|
211 |
+
c = stdscr.getch()
|
212 |
+
if c != -1:
|
213 |
+
break
|
214 |
+
stdscr.addstr(0, 0, '/')
|
215 |
+
stdscr.refresh()
|
216 |
+
board.display()
|
217 |
+
stdscr.addstr(0, 0, '+')
|
218 |
+
stdscr.refresh()
|
219 |
+
|
220 |
+
stdscr.nodelay(0) # Disable nodelay mode
|
221 |
+
display_menu(stdscr, menu_y)
|
222 |
+
|
223 |
+
elif c in 'Ee':
|
224 |
+
board.erase()
|
225 |
+
elif c in 'Qq':
|
226 |
+
break
|
227 |
+
elif c in 'Rr':
|
228 |
+
board.make_random()
|
229 |
+
board.display(update_board=False)
|
230 |
+
elif c in 'Ss':
|
231 |
+
board.display()
|
232 |
+
else:
|
233 |
+
# Ignore incorrect keys
|
234 |
+
pass
|
235 |
+
elif c == curses.KEY_UP and ypos > 0:
|
236 |
+
ypos -= 1
|
237 |
+
elif c == curses.KEY_DOWN and ypos + 1 < board.Y:
|
238 |
+
ypos += 1
|
239 |
+
elif c == curses.KEY_LEFT and xpos > 0:
|
240 |
+
xpos -= 1
|
241 |
+
elif c == curses.KEY_RIGHT and xpos + 1 < board.X:
|
242 |
+
xpos += 1
|
243 |
+
elif c == curses.KEY_MOUSE:
|
244 |
+
mouse_id, mouse_x, mouse_y, mouse_z, button_state = curses.getmouse()
|
245 |
+
if (mouse_x > 0 and mouse_x < board.X + 1 and
|
246 |
+
mouse_y > 0 and mouse_y < board.Y + 1):
|
247 |
+
xpos = mouse_x - 1
|
248 |
+
ypos = mouse_y - 1
|
249 |
+
board.toggle(ypos, xpos)
|
250 |
+
else:
|
251 |
+
# They've clicked outside the board
|
252 |
+
curses.flash()
|
253 |
+
else:
|
254 |
+
# Ignore incorrect keys
|
255 |
+
pass
|
256 |
+
|
257 |
+
|
258 |
+
def main(stdscr):
|
259 |
+
keyloop(stdscr) # Enter the main loop
|
260 |
+
|
261 |
+
if __name__ == '__main__':
|
262 |
+
curses.wrapper(main)
|
marketing/Tools/demo/markov.py
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
Markov chain simulation of words or characters.
|
5 |
+
"""
|
6 |
+
|
7 |
+
class Markov:
|
8 |
+
def __init__(self, histsize, choice):
|
9 |
+
self.histsize = histsize
|
10 |
+
self.choice = choice
|
11 |
+
self.trans = {}
|
12 |
+
|
13 |
+
def add(self, state, next):
|
14 |
+
self.trans.setdefault(state, []).append(next)
|
15 |
+
|
16 |
+
def put(self, seq):
|
17 |
+
n = self.histsize
|
18 |
+
add = self.add
|
19 |
+
add(None, seq[:0])
|
20 |
+
for i in range(len(seq)):
|
21 |
+
add(seq[max(0, i-n):i], seq[i:i+1])
|
22 |
+
add(seq[len(seq)-n:], None)
|
23 |
+
|
24 |
+
def get(self):
|
25 |
+
choice = self.choice
|
26 |
+
trans = self.trans
|
27 |
+
n = self.histsize
|
28 |
+
seq = choice(trans[None])
|
29 |
+
while True:
|
30 |
+
subseq = seq[max(0, len(seq)-n):]
|
31 |
+
options = trans[subseq]
|
32 |
+
next = choice(options)
|
33 |
+
if not next:
|
34 |
+
break
|
35 |
+
seq += next
|
36 |
+
return seq
|
37 |
+
|
38 |
+
|
39 |
+
def test():
|
40 |
+
import sys, random, getopt
|
41 |
+
args = sys.argv[1:]
|
42 |
+
try:
|
43 |
+
opts, args = getopt.getopt(args, '0123456789cdwq')
|
44 |
+
except getopt.error:
|
45 |
+
print('Usage: %s [-#] [-cddqw] [file] ...' % sys.argv[0])
|
46 |
+
print('Options:')
|
47 |
+
print('-#: 1-digit history size (default 2)')
|
48 |
+
print('-c: characters (default)')
|
49 |
+
print('-w: words')
|
50 |
+
print('-d: more debugging output')
|
51 |
+
print('-q: no debugging output')
|
52 |
+
print('Input files (default stdin) are split in paragraphs')
|
53 |
+
print('separated blank lines and each paragraph is split')
|
54 |
+
print('in words by whitespace, then reconcatenated with')
|
55 |
+
print('exactly one space separating words.')
|
56 |
+
print('Output consists of paragraphs separated by blank')
|
57 |
+
print('lines, where lines are no longer than 72 characters.')
|
58 |
+
sys.exit(2)
|
59 |
+
histsize = 2
|
60 |
+
do_words = False
|
61 |
+
debug = 1
|
62 |
+
for o, a in opts:
|
63 |
+
if '-0' <= o <= '-9': histsize = int(o[1:])
|
64 |
+
if o == '-c': do_words = False
|
65 |
+
if o == '-d': debug += 1
|
66 |
+
if o == '-q': debug = 0
|
67 |
+
if o == '-w': do_words = True
|
68 |
+
if not args:
|
69 |
+
args = ['-']
|
70 |
+
|
71 |
+
m = Markov(histsize, random.choice)
|
72 |
+
try:
|
73 |
+
for filename in args:
|
74 |
+
if filename == '-':
|
75 |
+
f = sys.stdin
|
76 |
+
if f.isatty():
|
77 |
+
print('Sorry, need stdin from file')
|
78 |
+
continue
|
79 |
+
else:
|
80 |
+
f = open(filename, 'r')
|
81 |
+
with f:
|
82 |
+
if debug: print('processing', filename, '...')
|
83 |
+
text = f.read()
|
84 |
+
paralist = text.split('\n\n')
|
85 |
+
for para in paralist:
|
86 |
+
if debug > 1: print('feeding ...')
|
87 |
+
words = para.split()
|
88 |
+
if words:
|
89 |
+
if do_words:
|
90 |
+
data = tuple(words)
|
91 |
+
else:
|
92 |
+
data = ' '.join(words)
|
93 |
+
m.put(data)
|
94 |
+
except KeyboardInterrupt:
|
95 |
+
print('Interrupted -- continue with data read so far')
|
96 |
+
if not m.trans:
|
97 |
+
print('No valid input files')
|
98 |
+
return
|
99 |
+
if debug: print('done.')
|
100 |
+
|
101 |
+
if debug > 1:
|
102 |
+
for key in m.trans.keys():
|
103 |
+
if key is None or len(key) < histsize:
|
104 |
+
print(repr(key), m.trans[key])
|
105 |
+
if histsize == 0: print(repr(''), m.trans[''])
|
106 |
+
print()
|
107 |
+
while True:
|
108 |
+
data = m.get()
|
109 |
+
if do_words:
|
110 |
+
words = data
|
111 |
+
else:
|
112 |
+
words = data.split()
|
113 |
+
n = 0
|
114 |
+
limit = 72
|
115 |
+
for w in words:
|
116 |
+
if n + len(w) > limit:
|
117 |
+
print()
|
118 |
+
n = 0
|
119 |
+
print(w, end=' ')
|
120 |
+
n += len(w) + 1
|
121 |
+
print()
|
122 |
+
print()
|
123 |
+
|
124 |
+
if __name__ == "__main__":
|
125 |
+
test()
|
marketing/Tools/demo/mcast.py
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
Send/receive UDP multicast packets.
|
5 |
+
Requires that your OS kernel supports IP multicast.
|
6 |
+
|
7 |
+
Usage:
|
8 |
+
mcast -s (sender, IPv4)
|
9 |
+
mcast -s -6 (sender, IPv6)
|
10 |
+
mcast (receivers, IPv4)
|
11 |
+
mcast -6 (receivers, IPv6)
|
12 |
+
"""
|
13 |
+
|
14 |
+
MYPORT = 8123
|
15 |
+
MYGROUP_4 = '225.0.0.250'
|
16 |
+
MYGROUP_6 = 'ff15:7079:7468:6f6e:6465:6d6f:6d63:6173'
|
17 |
+
MYTTL = 1 # Increase to reach other networks
|
18 |
+
|
19 |
+
import time
|
20 |
+
import struct
|
21 |
+
import socket
|
22 |
+
import sys
|
23 |
+
|
24 |
+
def main():
|
25 |
+
group = MYGROUP_6 if "-6" in sys.argv[1:] else MYGROUP_4
|
26 |
+
|
27 |
+
if "-s" in sys.argv[1:]:
|
28 |
+
sender(group)
|
29 |
+
else:
|
30 |
+
receiver(group)
|
31 |
+
|
32 |
+
|
33 |
+
def sender(group):
|
34 |
+
addrinfo = socket.getaddrinfo(group, None)[0]
|
35 |
+
|
36 |
+
s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
|
37 |
+
|
38 |
+
# Set Time-to-live (optional)
|
39 |
+
ttl_bin = struct.pack('@i', MYTTL)
|
40 |
+
if addrinfo[0] == socket.AF_INET: # IPv4
|
41 |
+
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl_bin)
|
42 |
+
else:
|
43 |
+
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl_bin)
|
44 |
+
|
45 |
+
while True:
|
46 |
+
data = repr(time.time()).encode('utf-8') + b'\0'
|
47 |
+
s.sendto(data, (addrinfo[4][0], MYPORT))
|
48 |
+
time.sleep(1)
|
49 |
+
|
50 |
+
|
51 |
+
def receiver(group):
|
52 |
+
# Look up multicast group address in name server and find out IP version
|
53 |
+
addrinfo = socket.getaddrinfo(group, None)[0]
|
54 |
+
|
55 |
+
# Create a socket
|
56 |
+
s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
|
57 |
+
|
58 |
+
# Allow multiple copies of this program on one machine
|
59 |
+
# (not strictly needed)
|
60 |
+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
61 |
+
|
62 |
+
# Bind it to the port
|
63 |
+
s.bind(('', MYPORT))
|
64 |
+
|
65 |
+
group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0])
|
66 |
+
# Join group
|
67 |
+
if addrinfo[0] == socket.AF_INET: # IPv4
|
68 |
+
mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
|
69 |
+
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
|
70 |
+
else:
|
71 |
+
mreq = group_bin + struct.pack('@I', 0)
|
72 |
+
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
|
73 |
+
|
74 |
+
# Loop, printing any data we receive
|
75 |
+
while True:
|
76 |
+
data, sender = s.recvfrom(1500)
|
77 |
+
while data[-1:] == '\0': data = data[:-1] # Strip trailing \0's
|
78 |
+
print(str(sender) + ' ' + repr(data))
|
79 |
+
|
80 |
+
|
81 |
+
if __name__ == '__main__':
|
82 |
+
main()
|
marketing/Tools/demo/queens.py
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
N queens problem.
|
5 |
+
|
6 |
+
The (well-known) problem is due to Niklaus Wirth.
|
7 |
+
|
8 |
+
This solution is inspired by Dijkstra (Structured Programming). It is
|
9 |
+
a classic recursive backtracking approach.
|
10 |
+
"""
|
11 |
+
|
12 |
+
N = 8 # Default; command line overrides
|
13 |
+
|
14 |
+
class Queens:
|
15 |
+
|
16 |
+
def __init__(self, n=N):
|
17 |
+
self.n = n
|
18 |
+
self.reset()
|
19 |
+
|
20 |
+
def reset(self):
|
21 |
+
n = self.n
|
22 |
+
self.y = [None] * n # Where is the queen in column x
|
23 |
+
self.row = [0] * n # Is row[y] safe?
|
24 |
+
self.up = [0] * (2*n-1) # Is upward diagonal[x-y] safe?
|
25 |
+
self.down = [0] * (2*n-1) # Is downward diagonal[x+y] safe?
|
26 |
+
self.nfound = 0 # Instrumentation
|
27 |
+
|
28 |
+
def solve(self, x=0): # Recursive solver
|
29 |
+
for y in range(self.n):
|
30 |
+
if self.safe(x, y):
|
31 |
+
self.place(x, y)
|
32 |
+
if x+1 == self.n:
|
33 |
+
self.display()
|
34 |
+
else:
|
35 |
+
self.solve(x+1)
|
36 |
+
self.remove(x, y)
|
37 |
+
|
38 |
+
def safe(self, x, y):
|
39 |
+
return not self.row[y] and not self.up[x-y] and not self.down[x+y]
|
40 |
+
|
41 |
+
def place(self, x, y):
|
42 |
+
self.y[x] = y
|
43 |
+
self.row[y] = 1
|
44 |
+
self.up[x-y] = 1
|
45 |
+
self.down[x+y] = 1
|
46 |
+
|
47 |
+
def remove(self, x, y):
|
48 |
+
self.y[x] = None
|
49 |
+
self.row[y] = 0
|
50 |
+
self.up[x-y] = 0
|
51 |
+
self.down[x+y] = 0
|
52 |
+
|
53 |
+
silent = 0 # If true, count solutions only
|
54 |
+
|
55 |
+
def display(self):
|
56 |
+
self.nfound = self.nfound + 1
|
57 |
+
if self.silent:
|
58 |
+
return
|
59 |
+
print('+-' + '--'*self.n + '+')
|
60 |
+
for y in range(self.n-1, -1, -1):
|
61 |
+
print('|', end=' ')
|
62 |
+
for x in range(self.n):
|
63 |
+
if self.y[x] == y:
|
64 |
+
print("Q", end=' ')
|
65 |
+
else:
|
66 |
+
print(".", end=' ')
|
67 |
+
print('|')
|
68 |
+
print('+-' + '--'*self.n + '+')
|
69 |
+
|
70 |
+
def main():
|
71 |
+
import sys
|
72 |
+
silent = 0
|
73 |
+
n = N
|
74 |
+
if sys.argv[1:2] == ['-n']:
|
75 |
+
silent = 1
|
76 |
+
del sys.argv[1]
|
77 |
+
if sys.argv[1:]:
|
78 |
+
n = int(sys.argv[1])
|
79 |
+
q = Queens(n)
|
80 |
+
q.silent = silent
|
81 |
+
q.solve()
|
82 |
+
print("Found", q.nfound, "solutions.")
|
83 |
+
|
84 |
+
if __name__ == "__main__":
|
85 |
+
main()
|
marketing/Tools/demo/redemo.py
ADDED
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""Basic regular expression demonstration facility (Perl style syntax)."""
|
4 |
+
|
5 |
+
from tkinter import *
|
6 |
+
import re
|
7 |
+
|
8 |
+
class ReDemo:
|
9 |
+
|
10 |
+
def __init__(self, master):
|
11 |
+
self.master = master
|
12 |
+
|
13 |
+
self.promptdisplay = Label(self.master, anchor=W,
|
14 |
+
text="Enter a Perl-style regular expression:")
|
15 |
+
self.promptdisplay.pack(side=TOP, fill=X)
|
16 |
+
|
17 |
+
self.regexdisplay = Entry(self.master)
|
18 |
+
self.regexdisplay.pack(fill=X)
|
19 |
+
self.regexdisplay.focus_set()
|
20 |
+
|
21 |
+
self.addoptions()
|
22 |
+
|
23 |
+
self.statusdisplay = Label(self.master, text="", anchor=W)
|
24 |
+
self.statusdisplay.pack(side=TOP, fill=X)
|
25 |
+
|
26 |
+
self.labeldisplay = Label(self.master, anchor=W,
|
27 |
+
text="Enter a string to search:")
|
28 |
+
self.labeldisplay.pack(fill=X)
|
29 |
+
self.labeldisplay.pack(fill=X)
|
30 |
+
|
31 |
+
self.showframe = Frame(master)
|
32 |
+
self.showframe.pack(fill=X, anchor=W)
|
33 |
+
|
34 |
+
self.showvar = StringVar(master)
|
35 |
+
self.showvar.set("first")
|
36 |
+
|
37 |
+
self.showfirstradio = Radiobutton(self.showframe,
|
38 |
+
text="Highlight first match",
|
39 |
+
variable=self.showvar,
|
40 |
+
value="first",
|
41 |
+
command=self.recompile)
|
42 |
+
self.showfirstradio.pack(side=LEFT)
|
43 |
+
|
44 |
+
self.showallradio = Radiobutton(self.showframe,
|
45 |
+
text="Highlight all matches",
|
46 |
+
variable=self.showvar,
|
47 |
+
value="all",
|
48 |
+
command=self.recompile)
|
49 |
+
self.showallradio.pack(side=LEFT)
|
50 |
+
|
51 |
+
self.stringdisplay = Text(self.master, width=60, height=4)
|
52 |
+
self.stringdisplay.pack(fill=BOTH, expand=1)
|
53 |
+
self.stringdisplay.tag_configure("hit", background="yellow")
|
54 |
+
|
55 |
+
self.grouplabel = Label(self.master, text="Groups:", anchor=W)
|
56 |
+
self.grouplabel.pack(fill=X)
|
57 |
+
|
58 |
+
self.grouplist = Listbox(self.master)
|
59 |
+
self.grouplist.pack(expand=1, fill=BOTH)
|
60 |
+
|
61 |
+
self.regexdisplay.bind('<Key>', self.recompile)
|
62 |
+
self.stringdisplay.bind('<Key>', self.reevaluate)
|
63 |
+
|
64 |
+
self.compiled = None
|
65 |
+
self.recompile()
|
66 |
+
|
67 |
+
btags = self.regexdisplay.bindtags()
|
68 |
+
self.regexdisplay.bindtags(btags[1:] + btags[:1])
|
69 |
+
|
70 |
+
btags = self.stringdisplay.bindtags()
|
71 |
+
self.stringdisplay.bindtags(btags[1:] + btags[:1])
|
72 |
+
|
73 |
+
def addoptions(self):
|
74 |
+
self.frames = []
|
75 |
+
self.boxes = []
|
76 |
+
self.vars = []
|
77 |
+
for name in ('IGNORECASE',
|
78 |
+
'MULTILINE',
|
79 |
+
'DOTALL',
|
80 |
+
'VERBOSE'):
|
81 |
+
if len(self.boxes) % 3 == 0:
|
82 |
+
frame = Frame(self.master)
|
83 |
+
frame.pack(fill=X)
|
84 |
+
self.frames.append(frame)
|
85 |
+
val = getattr(re, name).value
|
86 |
+
var = IntVar()
|
87 |
+
box = Checkbutton(frame,
|
88 |
+
variable=var, text=name,
|
89 |
+
offvalue=0, onvalue=val,
|
90 |
+
command=self.recompile)
|
91 |
+
box.pack(side=LEFT)
|
92 |
+
self.boxes.append(box)
|
93 |
+
self.vars.append(var)
|
94 |
+
|
95 |
+
def getflags(self):
|
96 |
+
flags = 0
|
97 |
+
for var in self.vars:
|
98 |
+
flags = flags | var.get()
|
99 |
+
return flags
|
100 |
+
|
101 |
+
def recompile(self, event=None):
|
102 |
+
try:
|
103 |
+
self.compiled = re.compile(self.regexdisplay.get(),
|
104 |
+
self.getflags())
|
105 |
+
bg = self.promptdisplay['background']
|
106 |
+
self.statusdisplay.config(text="", background=bg)
|
107 |
+
except re.error as msg:
|
108 |
+
self.compiled = None
|
109 |
+
self.statusdisplay.config(
|
110 |
+
text="re.error: %s" % str(msg),
|
111 |
+
background="red")
|
112 |
+
self.reevaluate()
|
113 |
+
|
114 |
+
def reevaluate(self, event=None):
|
115 |
+
try:
|
116 |
+
self.stringdisplay.tag_remove("hit", "1.0", END)
|
117 |
+
except TclError:
|
118 |
+
pass
|
119 |
+
try:
|
120 |
+
self.stringdisplay.tag_remove("hit0", "1.0", END)
|
121 |
+
except TclError:
|
122 |
+
pass
|
123 |
+
self.grouplist.delete(0, END)
|
124 |
+
if not self.compiled:
|
125 |
+
return
|
126 |
+
self.stringdisplay.tag_configure("hit", background="yellow")
|
127 |
+
self.stringdisplay.tag_configure("hit0", background="orange")
|
128 |
+
text = self.stringdisplay.get("1.0", END)
|
129 |
+
last = 0
|
130 |
+
nmatches = 0
|
131 |
+
while last <= len(text):
|
132 |
+
m = self.compiled.search(text, last)
|
133 |
+
if m is None:
|
134 |
+
break
|
135 |
+
first, last = m.span()
|
136 |
+
if last == first:
|
137 |
+
last = first+1
|
138 |
+
tag = "hit0"
|
139 |
+
else:
|
140 |
+
tag = "hit"
|
141 |
+
pfirst = "1.0 + %d chars" % first
|
142 |
+
plast = "1.0 + %d chars" % last
|
143 |
+
self.stringdisplay.tag_add(tag, pfirst, plast)
|
144 |
+
if nmatches == 0:
|
145 |
+
self.stringdisplay.yview_pickplace(pfirst)
|
146 |
+
groups = list(m.groups())
|
147 |
+
groups.insert(0, m.group())
|
148 |
+
for i in range(len(groups)):
|
149 |
+
g = "%2d: %r" % (i, groups[i])
|
150 |
+
self.grouplist.insert(END, g)
|
151 |
+
nmatches = nmatches + 1
|
152 |
+
if self.showvar.get() == "first":
|
153 |
+
break
|
154 |
+
|
155 |
+
if nmatches == 0:
|
156 |
+
self.statusdisplay.config(text="(no match)",
|
157 |
+
background="yellow")
|
158 |
+
else:
|
159 |
+
self.statusdisplay.config(text="")
|
160 |
+
|
161 |
+
|
162 |
+
# Main function, run when invoked as a stand-alone Python program.
|
163 |
+
|
164 |
+
def main():
|
165 |
+
root = Tk()
|
166 |
+
demo = ReDemo(root)
|
167 |
+
root.protocol('WM_DELETE_WINDOW', root.quit)
|
168 |
+
root.mainloop()
|
169 |
+
|
170 |
+
if __name__ == '__main__':
|
171 |
+
main()
|
marketing/Tools/demo/rpython.py
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
Remote python client.
|
5 |
+
Execute Python commands remotely and send output back.
|
6 |
+
"""
|
7 |
+
|
8 |
+
import sys
|
9 |
+
from socket import socket, AF_INET, SOCK_STREAM, SHUT_WR
|
10 |
+
|
11 |
+
PORT = 4127
|
12 |
+
BUFSIZE = 1024
|
13 |
+
|
14 |
+
def main():
|
15 |
+
if len(sys.argv) < 3:
|
16 |
+
print("usage: rpython host command")
|
17 |
+
sys.exit(2)
|
18 |
+
host = sys.argv[1]
|
19 |
+
port = PORT
|
20 |
+
i = host.find(':')
|
21 |
+
if i >= 0:
|
22 |
+
port = int(host[i+1:])
|
23 |
+
host = host[:i]
|
24 |
+
command = ' '.join(sys.argv[2:])
|
25 |
+
with socket(AF_INET, SOCK_STREAM) as s:
|
26 |
+
s.connect((host, port))
|
27 |
+
s.send(command.encode())
|
28 |
+
s.shutdown(SHUT_WR)
|
29 |
+
reply = b''
|
30 |
+
while True:
|
31 |
+
data = s.recv(BUFSIZE)
|
32 |
+
if not data:
|
33 |
+
break
|
34 |
+
reply += data
|
35 |
+
print(reply.decode(), end=' ')
|
36 |
+
|
37 |
+
main()
|
marketing/Tools/demo/rpythond.py
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
Remote python server.
|
5 |
+
Execute Python commands remotely and send output back.
|
6 |
+
|
7 |
+
WARNING: This version has a gaping security hole -- it accepts requests
|
8 |
+
from any host on the Internet!
|
9 |
+
"""
|
10 |
+
|
11 |
+
import sys
|
12 |
+
from socket import socket, AF_INET, SOCK_STREAM
|
13 |
+
import io
|
14 |
+
import traceback
|
15 |
+
|
16 |
+
PORT = 4127
|
17 |
+
BUFSIZE = 1024
|
18 |
+
|
19 |
+
def main():
|
20 |
+
if len(sys.argv) > 1:
|
21 |
+
port = int(sys.argv[1])
|
22 |
+
else:
|
23 |
+
port = PORT
|
24 |
+
s = socket(AF_INET, SOCK_STREAM)
|
25 |
+
s.bind(('', port))
|
26 |
+
s.listen(1)
|
27 |
+
while True:
|
28 |
+
conn, (remotehost, remoteport) = s.accept()
|
29 |
+
with conn:
|
30 |
+
print('connection from', remotehost, remoteport)
|
31 |
+
request = b''
|
32 |
+
while True:
|
33 |
+
data = conn.recv(BUFSIZE)
|
34 |
+
if not data:
|
35 |
+
break
|
36 |
+
request += data
|
37 |
+
reply = execute(request.decode())
|
38 |
+
conn.send(reply.encode())
|
39 |
+
|
40 |
+
def execute(request):
|
41 |
+
stdout = sys.stdout
|
42 |
+
stderr = sys.stderr
|
43 |
+
sys.stdout = sys.stderr = fakefile = io.StringIO()
|
44 |
+
try:
|
45 |
+
try:
|
46 |
+
exec(request, {}, {})
|
47 |
+
except:
|
48 |
+
print()
|
49 |
+
traceback.print_exc(100)
|
50 |
+
finally:
|
51 |
+
sys.stderr = stderr
|
52 |
+
sys.stdout = stdout
|
53 |
+
return fakefile.getvalue()
|
54 |
+
|
55 |
+
try:
|
56 |
+
main()
|
57 |
+
except KeyboardInterrupt:
|
58 |
+
pass
|
marketing/Tools/demo/sortvisu.py
ADDED
@@ -0,0 +1,635 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
Sorting algorithms visualizer using Tkinter.
|
5 |
+
|
6 |
+
This module is comprised of three ``components'':
|
7 |
+
|
8 |
+
- an array visualizer with methods that implement basic sorting
|
9 |
+
operations (compare, swap) as well as methods for ``annotating'' the
|
10 |
+
sorting algorithm (e.g. to show the pivot element);
|
11 |
+
|
12 |
+
- a number of sorting algorithms (currently quicksort, insertion sort,
|
13 |
+
selection sort and bubble sort, as well as a randomization function),
|
14 |
+
all using the array visualizer for its basic operations and with calls
|
15 |
+
to its annotation methods;
|
16 |
+
|
17 |
+
- and a ``driver'' class which can be used as a Grail applet or as a
|
18 |
+
stand-alone application.
|
19 |
+
"""
|
20 |
+
|
21 |
+
from tkinter import *
|
22 |
+
import random
|
23 |
+
|
24 |
+
XGRID = 10
|
25 |
+
YGRID = 10
|
26 |
+
WIDTH = 6
|
27 |
+
|
28 |
+
|
29 |
+
class Array:
|
30 |
+
|
31 |
+
class Cancelled(BaseException):
|
32 |
+
pass
|
33 |
+
|
34 |
+
def __init__(self, master, data=None):
|
35 |
+
self.master = master
|
36 |
+
self.frame = Frame(self.master)
|
37 |
+
self.frame.pack(fill=X)
|
38 |
+
self.label = Label(self.frame)
|
39 |
+
self.label.pack()
|
40 |
+
self.canvas = Canvas(self.frame)
|
41 |
+
self.canvas.pack()
|
42 |
+
self.report = Label(self.frame)
|
43 |
+
self.report.pack()
|
44 |
+
self.left = self.canvas.create_line(0, 0, 0, 0)
|
45 |
+
self.right = self.canvas.create_line(0, 0, 0, 0)
|
46 |
+
self.pivot = self.canvas.create_line(0, 0, 0, 0)
|
47 |
+
self.items = []
|
48 |
+
self.size = self.maxvalue = 0
|
49 |
+
if data:
|
50 |
+
self.setdata(data)
|
51 |
+
|
52 |
+
def setdata(self, data):
|
53 |
+
olditems = self.items
|
54 |
+
self.items = []
|
55 |
+
for item in olditems:
|
56 |
+
item.delete()
|
57 |
+
self.size = len(data)
|
58 |
+
self.maxvalue = max(data)
|
59 |
+
self.canvas.config(width=(self.size+1)*XGRID,
|
60 |
+
height=(self.maxvalue+1)*YGRID)
|
61 |
+
for i in range(self.size):
|
62 |
+
self.items.append(ArrayItem(self, i, data[i]))
|
63 |
+
self.reset("Sort demo, size %d" % self.size)
|
64 |
+
|
65 |
+
speed = "normal"
|
66 |
+
|
67 |
+
def setspeed(self, speed):
|
68 |
+
self.speed = speed
|
69 |
+
|
70 |
+
def destroy(self):
|
71 |
+
self.frame.destroy()
|
72 |
+
|
73 |
+
in_mainloop = 0
|
74 |
+
stop_mainloop = 0
|
75 |
+
|
76 |
+
def cancel(self):
|
77 |
+
self.stop_mainloop = 1
|
78 |
+
if self.in_mainloop:
|
79 |
+
self.master.quit()
|
80 |
+
|
81 |
+
def step(self):
|
82 |
+
if self.in_mainloop:
|
83 |
+
self.master.quit()
|
84 |
+
|
85 |
+
def wait(self, msecs):
|
86 |
+
if self.speed == "fastest":
|
87 |
+
msecs = 0
|
88 |
+
elif self.speed == "fast":
|
89 |
+
msecs = msecs//10
|
90 |
+
elif self.speed == "single-step":
|
91 |
+
msecs = 1000000000
|
92 |
+
if not self.stop_mainloop:
|
93 |
+
self.master.update()
|
94 |
+
id = self.master.after(msecs, self.master.quit)
|
95 |
+
self.in_mainloop = 1
|
96 |
+
self.master.mainloop()
|
97 |
+
self.master.after_cancel(id)
|
98 |
+
self.in_mainloop = 0
|
99 |
+
if self.stop_mainloop:
|
100 |
+
self.stop_mainloop = 0
|
101 |
+
self.message("Cancelled")
|
102 |
+
raise Array.Cancelled
|
103 |
+
|
104 |
+
def getsize(self):
|
105 |
+
return self.size
|
106 |
+
|
107 |
+
def show_partition(self, first, last):
|
108 |
+
for i in range(self.size):
|
109 |
+
item = self.items[i]
|
110 |
+
if first <= i < last:
|
111 |
+
self.canvas.itemconfig(item, fill='red')
|
112 |
+
else:
|
113 |
+
self.canvas.itemconfig(item, fill='orange')
|
114 |
+
self.hide_left_right_pivot()
|
115 |
+
|
116 |
+
def hide_partition(self):
|
117 |
+
for i in range(self.size):
|
118 |
+
item = self.items[i]
|
119 |
+
self.canvas.itemconfig(item, fill='red')
|
120 |
+
self.hide_left_right_pivot()
|
121 |
+
|
122 |
+
def show_left(self, left):
|
123 |
+
if not 0 <= left < self.size:
|
124 |
+
self.hide_left()
|
125 |
+
return
|
126 |
+
x1, y1, x2, y2 = self.items[left].position()
|
127 |
+
## top, bot = HIRO
|
128 |
+
self.canvas.coords(self.left, (x1 - 2, 0, x1 - 2, 9999))
|
129 |
+
self.master.update()
|
130 |
+
|
131 |
+
def show_right(self, right):
|
132 |
+
if not 0 <= right < self.size:
|
133 |
+
self.hide_right()
|
134 |
+
return
|
135 |
+
x1, y1, x2, y2 = self.items[right].position()
|
136 |
+
self.canvas.coords(self.right, (x2 + 2, 0, x2 + 2, 9999))
|
137 |
+
self.master.update()
|
138 |
+
|
139 |
+
def hide_left_right_pivot(self):
|
140 |
+
self.hide_left()
|
141 |
+
self.hide_right()
|
142 |
+
self.hide_pivot()
|
143 |
+
|
144 |
+
def hide_left(self):
|
145 |
+
self.canvas.coords(self.left, (0, 0, 0, 0))
|
146 |
+
|
147 |
+
def hide_right(self):
|
148 |
+
self.canvas.coords(self.right, (0, 0, 0, 0))
|
149 |
+
|
150 |
+
def show_pivot(self, pivot):
|
151 |
+
x1, y1, x2, y2 = self.items[pivot].position()
|
152 |
+
self.canvas.coords(self.pivot, (0, y1 - 2, 9999, y1 - 2))
|
153 |
+
|
154 |
+
def hide_pivot(self):
|
155 |
+
self.canvas.coords(self.pivot, (0, 0, 0, 0))
|
156 |
+
|
157 |
+
def swap(self, i, j):
|
158 |
+
if i == j: return
|
159 |
+
self.countswap()
|
160 |
+
item = self.items[i]
|
161 |
+
other = self.items[j]
|
162 |
+
self.items[i], self.items[j] = other, item
|
163 |
+
item.swapwith(other)
|
164 |
+
|
165 |
+
def compare(self, i, j):
|
166 |
+
self.countcompare()
|
167 |
+
item = self.items[i]
|
168 |
+
other = self.items[j]
|
169 |
+
return item.compareto(other)
|
170 |
+
|
171 |
+
def reset(self, msg):
|
172 |
+
self.ncompares = 0
|
173 |
+
self.nswaps = 0
|
174 |
+
self.message(msg)
|
175 |
+
self.updatereport()
|
176 |
+
self.hide_partition()
|
177 |
+
|
178 |
+
def message(self, msg):
|
179 |
+
self.label.config(text=msg)
|
180 |
+
|
181 |
+
def countswap(self):
|
182 |
+
self.nswaps = self.nswaps + 1
|
183 |
+
self.updatereport()
|
184 |
+
|
185 |
+
def countcompare(self):
|
186 |
+
self.ncompares = self.ncompares + 1
|
187 |
+
self.updatereport()
|
188 |
+
|
189 |
+
def updatereport(self):
|
190 |
+
text = "%d cmps, %d swaps" % (self.ncompares, self.nswaps)
|
191 |
+
self.report.config(text=text)
|
192 |
+
|
193 |
+
|
194 |
+
class ArrayItem:
|
195 |
+
|
196 |
+
def __init__(self, array, index, value):
|
197 |
+
self.array = array
|
198 |
+
self.index = index
|
199 |
+
self.value = value
|
200 |
+
self.canvas = array.canvas
|
201 |
+
x1, y1, x2, y2 = self.position()
|
202 |
+
self.item_id = array.canvas.create_rectangle(x1, y1, x2, y2,
|
203 |
+
fill='red', outline='black', width=1)
|
204 |
+
self.canvas.tag_bind(self.item_id, '<Button-1>', self.mouse_down)
|
205 |
+
self.canvas.tag_bind(self.item_id, '<Button1-Motion>', self.mouse_move)
|
206 |
+
self.canvas.tag_bind(self.item_id, '<ButtonRelease-1>', self.mouse_up)
|
207 |
+
|
208 |
+
def delete(self):
|
209 |
+
item_id = self.item_id
|
210 |
+
self.array = None
|
211 |
+
self.item_id = None
|
212 |
+
self.canvas.delete(item_id)
|
213 |
+
|
214 |
+
def mouse_down(self, event):
|
215 |
+
self.lastx = event.x
|
216 |
+
self.lasty = event.y
|
217 |
+
self.origx = event.x
|
218 |
+
self.origy = event.y
|
219 |
+
self.canvas.tag_raise(self.item_id)
|
220 |
+
|
221 |
+
def mouse_move(self, event):
|
222 |
+
self.canvas.move(self.item_id,
|
223 |
+
event.x - self.lastx, event.y - self.lasty)
|
224 |
+
self.lastx = event.x
|
225 |
+
self.lasty = event.y
|
226 |
+
|
227 |
+
def mouse_up(self, event):
|
228 |
+
i = self.nearestindex(event.x)
|
229 |
+
if i >= self.array.getsize():
|
230 |
+
i = self.array.getsize() - 1
|
231 |
+
if i < 0:
|
232 |
+
i = 0
|
233 |
+
other = self.array.items[i]
|
234 |
+
here = self.index
|
235 |
+
self.array.items[here], self.array.items[i] = other, self
|
236 |
+
self.index = i
|
237 |
+
x1, y1, x2, y2 = self.position()
|
238 |
+
self.canvas.coords(self.item_id, (x1, y1, x2, y2))
|
239 |
+
other.setindex(here)
|
240 |
+
|
241 |
+
def setindex(self, index):
|
242 |
+
nsteps = steps(self.index, index)
|
243 |
+
if not nsteps: return
|
244 |
+
if self.array.speed == "fastest":
|
245 |
+
nsteps = 0
|
246 |
+
oldpts = self.position()
|
247 |
+
self.index = index
|
248 |
+
newpts = self.position()
|
249 |
+
trajectory = interpolate(oldpts, newpts, nsteps)
|
250 |
+
self.canvas.tag_raise(self.item_id)
|
251 |
+
for pts in trajectory:
|
252 |
+
self.canvas.coords(self.item_id, pts)
|
253 |
+
self.array.wait(50)
|
254 |
+
|
255 |
+
def swapwith(self, other):
|
256 |
+
nsteps = steps(self.index, other.index)
|
257 |
+
if not nsteps: return
|
258 |
+
if self.array.speed == "fastest":
|
259 |
+
nsteps = 0
|
260 |
+
myoldpts = self.position()
|
261 |
+
otheroldpts = other.position()
|
262 |
+
self.index, other.index = other.index, self.index
|
263 |
+
mynewpts = self.position()
|
264 |
+
othernewpts = other.position()
|
265 |
+
myfill = self.canvas.itemcget(self.item_id, 'fill')
|
266 |
+
otherfill = self.canvas.itemcget(other.item_id, 'fill')
|
267 |
+
self.canvas.itemconfig(self.item_id, fill='green')
|
268 |
+
self.canvas.itemconfig(other.item_id, fill='yellow')
|
269 |
+
self.array.master.update()
|
270 |
+
if self.array.speed == "single-step":
|
271 |
+
self.canvas.coords(self.item_id, mynewpts)
|
272 |
+
self.canvas.coords(other.item_id, othernewpts)
|
273 |
+
self.array.master.update()
|
274 |
+
self.canvas.itemconfig(self.item_id, fill=myfill)
|
275 |
+
self.canvas.itemconfig(other.item_id, fill=otherfill)
|
276 |
+
self.array.wait(0)
|
277 |
+
return
|
278 |
+
mytrajectory = interpolate(myoldpts, mynewpts, nsteps)
|
279 |
+
othertrajectory = interpolate(otheroldpts, othernewpts, nsteps)
|
280 |
+
if self.value > other.value:
|
281 |
+
self.canvas.tag_raise(self.item_id)
|
282 |
+
self.canvas.tag_raise(other.item_id)
|
283 |
+
else:
|
284 |
+
self.canvas.tag_raise(other.item_id)
|
285 |
+
self.canvas.tag_raise(self.item_id)
|
286 |
+
try:
|
287 |
+
for i in range(len(mytrajectory)):
|
288 |
+
mypts = mytrajectory[i]
|
289 |
+
otherpts = othertrajectory[i]
|
290 |
+
self.canvas.coords(self.item_id, mypts)
|
291 |
+
self.canvas.coords(other.item_id, otherpts)
|
292 |
+
self.array.wait(50)
|
293 |
+
finally:
|
294 |
+
mypts = mytrajectory[-1]
|
295 |
+
otherpts = othertrajectory[-1]
|
296 |
+
self.canvas.coords(self.item_id, mypts)
|
297 |
+
self.canvas.coords(other.item_id, otherpts)
|
298 |
+
self.canvas.itemconfig(self.item_id, fill=myfill)
|
299 |
+
self.canvas.itemconfig(other.item_id, fill=otherfill)
|
300 |
+
|
301 |
+
def compareto(self, other):
|
302 |
+
myfill = self.canvas.itemcget(self.item_id, 'fill')
|
303 |
+
otherfill = self.canvas.itemcget(other.item_id, 'fill')
|
304 |
+
if self.value < other.value:
|
305 |
+
myflash = 'white'
|
306 |
+
otherflash = 'black'
|
307 |
+
outcome = -1
|
308 |
+
elif self.value > other.value:
|
309 |
+
myflash = 'black'
|
310 |
+
otherflash = 'white'
|
311 |
+
outcome = 1
|
312 |
+
else:
|
313 |
+
myflash = otherflash = 'grey'
|
314 |
+
outcome = 0
|
315 |
+
try:
|
316 |
+
self.canvas.itemconfig(self.item_id, fill=myflash)
|
317 |
+
self.canvas.itemconfig(other.item_id, fill=otherflash)
|
318 |
+
self.array.wait(500)
|
319 |
+
finally:
|
320 |
+
self.canvas.itemconfig(self.item_id, fill=myfill)
|
321 |
+
self.canvas.itemconfig(other.item_id, fill=otherfill)
|
322 |
+
return outcome
|
323 |
+
|
324 |
+
def position(self):
|
325 |
+
x1 = (self.index+1)*XGRID - WIDTH//2
|
326 |
+
x2 = x1+WIDTH
|
327 |
+
y2 = (self.array.maxvalue+1)*YGRID
|
328 |
+
y1 = y2 - (self.value)*YGRID
|
329 |
+
return x1, y1, x2, y2
|
330 |
+
|
331 |
+
def nearestindex(self, x):
|
332 |
+
return int(round(float(x)/XGRID)) - 1
|
333 |
+
|
334 |
+
|
335 |
+
# Subroutines that don't need an object
|
336 |
+
|
337 |
+
def steps(here, there):
|
338 |
+
nsteps = abs(here - there)
|
339 |
+
if nsteps <= 3:
|
340 |
+
nsteps = nsteps * 3
|
341 |
+
elif nsteps <= 5:
|
342 |
+
nsteps = nsteps * 2
|
343 |
+
elif nsteps > 10:
|
344 |
+
nsteps = 10
|
345 |
+
return nsteps
|
346 |
+
|
347 |
+
def interpolate(oldpts, newpts, n):
|
348 |
+
if len(oldpts) != len(newpts):
|
349 |
+
raise ValueError("can't interpolate arrays of different length")
|
350 |
+
pts = [0]*len(oldpts)
|
351 |
+
res = [tuple(oldpts)]
|
352 |
+
for i in range(1, n):
|
353 |
+
for k in range(len(pts)):
|
354 |
+
pts[k] = oldpts[k] + (newpts[k] - oldpts[k])*i//n
|
355 |
+
res.append(tuple(pts))
|
356 |
+
res.append(tuple(newpts))
|
357 |
+
return res
|
358 |
+
|
359 |
+
|
360 |
+
# Various (un)sorting algorithms
|
361 |
+
|
362 |
+
def uniform(array):
|
363 |
+
size = array.getsize()
|
364 |
+
array.setdata([(size+1)//2] * size)
|
365 |
+
array.reset("Uniform data, size %d" % size)
|
366 |
+
|
367 |
+
def distinct(array):
|
368 |
+
size = array.getsize()
|
369 |
+
array.setdata(range(1, size+1))
|
370 |
+
array.reset("Distinct data, size %d" % size)
|
371 |
+
|
372 |
+
def randomize(array):
|
373 |
+
array.reset("Randomizing")
|
374 |
+
n = array.getsize()
|
375 |
+
for i in range(n):
|
376 |
+
j = random.randint(0, n-1)
|
377 |
+
array.swap(i, j)
|
378 |
+
array.message("Randomized")
|
379 |
+
|
380 |
+
def insertionsort(array):
|
381 |
+
size = array.getsize()
|
382 |
+
array.reset("Insertion sort")
|
383 |
+
for i in range(1, size):
|
384 |
+
j = i-1
|
385 |
+
while j >= 0:
|
386 |
+
if array.compare(j, j+1) <= 0:
|
387 |
+
break
|
388 |
+
array.swap(j, j+1)
|
389 |
+
j = j-1
|
390 |
+
array.message("Sorted")
|
391 |
+
|
392 |
+
def selectionsort(array):
|
393 |
+
size = array.getsize()
|
394 |
+
array.reset("Selection sort")
|
395 |
+
try:
|
396 |
+
for i in range(size):
|
397 |
+
array.show_partition(i, size)
|
398 |
+
for j in range(i+1, size):
|
399 |
+
if array.compare(i, j) > 0:
|
400 |
+
array.swap(i, j)
|
401 |
+
array.message("Sorted")
|
402 |
+
finally:
|
403 |
+
array.hide_partition()
|
404 |
+
|
405 |
+
def bubblesort(array):
|
406 |
+
size = array.getsize()
|
407 |
+
array.reset("Bubble sort")
|
408 |
+
for i in range(size):
|
409 |
+
for j in range(1, size):
|
410 |
+
if array.compare(j-1, j) > 0:
|
411 |
+
array.swap(j-1, j)
|
412 |
+
array.message("Sorted")
|
413 |
+
|
414 |
+
def quicksort(array):
|
415 |
+
size = array.getsize()
|
416 |
+
array.reset("Quicksort")
|
417 |
+
try:
|
418 |
+
stack = [(0, size)]
|
419 |
+
while stack:
|
420 |
+
first, last = stack[-1]
|
421 |
+
del stack[-1]
|
422 |
+
array.show_partition(first, last)
|
423 |
+
if last-first < 5:
|
424 |
+
array.message("Insertion sort")
|
425 |
+
for i in range(first+1, last):
|
426 |
+
j = i-1
|
427 |
+
while j >= first:
|
428 |
+
if array.compare(j, j+1) <= 0:
|
429 |
+
break
|
430 |
+
array.swap(j, j+1)
|
431 |
+
j = j-1
|
432 |
+
continue
|
433 |
+
array.message("Choosing pivot")
|
434 |
+
j, i, k = first, (first+last) // 2, last-1
|
435 |
+
if array.compare(k, i) < 0:
|
436 |
+
array.swap(k, i)
|
437 |
+
if array.compare(k, j) < 0:
|
438 |
+
array.swap(k, j)
|
439 |
+
if array.compare(j, i) < 0:
|
440 |
+
array.swap(j, i)
|
441 |
+
pivot = j
|
442 |
+
array.show_pivot(pivot)
|
443 |
+
array.message("Pivot at left of partition")
|
444 |
+
array.wait(1000)
|
445 |
+
left = first
|
446 |
+
right = last
|
447 |
+
while True:
|
448 |
+
array.message("Sweep right pointer")
|
449 |
+
right = right-1
|
450 |
+
array.show_right(right)
|
451 |
+
while right > first and array.compare(right, pivot) >= 0:
|
452 |
+
right = right-1
|
453 |
+
array.show_right(right)
|
454 |
+
array.message("Sweep left pointer")
|
455 |
+
left = left+1
|
456 |
+
array.show_left(left)
|
457 |
+
while left < last and array.compare(left, pivot) <= 0:
|
458 |
+
left = left+1
|
459 |
+
array.show_left(left)
|
460 |
+
if left > right:
|
461 |
+
array.message("End of partition")
|
462 |
+
break
|
463 |
+
array.message("Swap items")
|
464 |
+
array.swap(left, right)
|
465 |
+
array.message("Swap pivot back")
|
466 |
+
array.swap(pivot, right)
|
467 |
+
n1 = right-first
|
468 |
+
n2 = last-left
|
469 |
+
if n1 > 1: stack.append((first, right))
|
470 |
+
if n2 > 1: stack.append((left, last))
|
471 |
+
array.message("Sorted")
|
472 |
+
finally:
|
473 |
+
array.hide_partition()
|
474 |
+
|
475 |
+
def demosort(array):
|
476 |
+
while True:
|
477 |
+
for alg in [quicksort, insertionsort, selectionsort, bubblesort]:
|
478 |
+
randomize(array)
|
479 |
+
alg(array)
|
480 |
+
|
481 |
+
|
482 |
+
# Sort demo class -- usable as a Grail applet
|
483 |
+
|
484 |
+
class SortDemo:
|
485 |
+
|
486 |
+
def __init__(self, master, size=15):
|
487 |
+
self.master = master
|
488 |
+
self.size = size
|
489 |
+
self.busy = 0
|
490 |
+
self.array = Array(self.master)
|
491 |
+
|
492 |
+
self.botframe = Frame(master)
|
493 |
+
self.botframe.pack(side=BOTTOM)
|
494 |
+
self.botleftframe = Frame(self.botframe)
|
495 |
+
self.botleftframe.pack(side=LEFT, fill=Y)
|
496 |
+
self.botrightframe = Frame(self.botframe)
|
497 |
+
self.botrightframe.pack(side=RIGHT, fill=Y)
|
498 |
+
|
499 |
+
self.b_qsort = Button(self.botleftframe,
|
500 |
+
text="Quicksort", command=self.c_qsort)
|
501 |
+
self.b_qsort.pack(fill=X)
|
502 |
+
self.b_isort = Button(self.botleftframe,
|
503 |
+
text="Insertion sort", command=self.c_isort)
|
504 |
+
self.b_isort.pack(fill=X)
|
505 |
+
self.b_ssort = Button(self.botleftframe,
|
506 |
+
text="Selection sort", command=self.c_ssort)
|
507 |
+
self.b_ssort.pack(fill=X)
|
508 |
+
self.b_bsort = Button(self.botleftframe,
|
509 |
+
text="Bubble sort", command=self.c_bsort)
|
510 |
+
self.b_bsort.pack(fill=X)
|
511 |
+
|
512 |
+
# Terrible hack to overcome limitation of OptionMenu...
|
513 |
+
class MyIntVar(IntVar):
|
514 |
+
def __init__(self, master, demo):
|
515 |
+
self.demo = demo
|
516 |
+
IntVar.__init__(self, master)
|
517 |
+
def set(self, value):
|
518 |
+
IntVar.set(self, value)
|
519 |
+
if str(value) != '0':
|
520 |
+
self.demo.resize(value)
|
521 |
+
|
522 |
+
self.v_size = MyIntVar(self.master, self)
|
523 |
+
self.v_size.set(size)
|
524 |
+
sizes = [1, 2, 3, 4] + list(range(5, 55, 5))
|
525 |
+
if self.size not in sizes:
|
526 |
+
sizes.append(self.size)
|
527 |
+
sizes.sort()
|
528 |
+
self.m_size = OptionMenu(self.botleftframe, self.v_size, *sizes)
|
529 |
+
self.m_size.pack(fill=X)
|
530 |
+
|
531 |
+
self.v_speed = StringVar(self.master)
|
532 |
+
self.v_speed.set("normal")
|
533 |
+
self.m_speed = OptionMenu(self.botleftframe, self.v_speed,
|
534 |
+
"single-step", "normal", "fast", "fastest")
|
535 |
+
self.m_speed.pack(fill=X)
|
536 |
+
|
537 |
+
self.b_step = Button(self.botleftframe,
|
538 |
+
text="Step", command=self.c_step)
|
539 |
+
self.b_step.pack(fill=X)
|
540 |
+
|
541 |
+
self.b_randomize = Button(self.botrightframe,
|
542 |
+
text="Randomize", command=self.c_randomize)
|
543 |
+
self.b_randomize.pack(fill=X)
|
544 |
+
self.b_uniform = Button(self.botrightframe,
|
545 |
+
text="Uniform", command=self.c_uniform)
|
546 |
+
self.b_uniform.pack(fill=X)
|
547 |
+
self.b_distinct = Button(self.botrightframe,
|
548 |
+
text="Distinct", command=self.c_distinct)
|
549 |
+
self.b_distinct.pack(fill=X)
|
550 |
+
self.b_demo = Button(self.botrightframe,
|
551 |
+
text="Demo", command=self.c_demo)
|
552 |
+
self.b_demo.pack(fill=X)
|
553 |
+
self.b_cancel = Button(self.botrightframe,
|
554 |
+
text="Cancel", command=self.c_cancel)
|
555 |
+
self.b_cancel.pack(fill=X)
|
556 |
+
self.b_cancel.config(state=DISABLED)
|
557 |
+
self.b_quit = Button(self.botrightframe,
|
558 |
+
text="Quit", command=self.c_quit)
|
559 |
+
self.b_quit.pack(fill=X)
|
560 |
+
|
561 |
+
def resize(self, newsize):
|
562 |
+
if self.busy:
|
563 |
+
self.master.bell()
|
564 |
+
return
|
565 |
+
self.size = newsize
|
566 |
+
self.array.setdata(range(1, self.size+1))
|
567 |
+
|
568 |
+
def c_qsort(self):
|
569 |
+
self.run(quicksort)
|
570 |
+
|
571 |
+
def c_isort(self):
|
572 |
+
self.run(insertionsort)
|
573 |
+
|
574 |
+
def c_ssort(self):
|
575 |
+
self.run(selectionsort)
|
576 |
+
|
577 |
+
def c_bsort(self):
|
578 |
+
self.run(bubblesort)
|
579 |
+
|
580 |
+
def c_demo(self):
|
581 |
+
self.run(demosort)
|
582 |
+
|
583 |
+
def c_randomize(self):
|
584 |
+
self.run(randomize)
|
585 |
+
|
586 |
+
def c_uniform(self):
|
587 |
+
self.run(uniform)
|
588 |
+
|
589 |
+
def c_distinct(self):
|
590 |
+
self.run(distinct)
|
591 |
+
|
592 |
+
def run(self, func):
|
593 |
+
if self.busy:
|
594 |
+
self.master.bell()
|
595 |
+
return
|
596 |
+
self.busy = 1
|
597 |
+
self.array.setspeed(self.v_speed.get())
|
598 |
+
self.b_cancel.config(state=NORMAL)
|
599 |
+
try:
|
600 |
+
func(self.array)
|
601 |
+
except Array.Cancelled:
|
602 |
+
pass
|
603 |
+
self.b_cancel.config(state=DISABLED)
|
604 |
+
self.busy = 0
|
605 |
+
|
606 |
+
def c_cancel(self):
|
607 |
+
if not self.busy:
|
608 |
+
self.master.bell()
|
609 |
+
return
|
610 |
+
self.array.cancel()
|
611 |
+
|
612 |
+
def c_step(self):
|
613 |
+
if not self.busy:
|
614 |
+
self.master.bell()
|
615 |
+
return
|
616 |
+
self.v_speed.set("single-step")
|
617 |
+
self.array.setspeed("single-step")
|
618 |
+
self.array.step()
|
619 |
+
|
620 |
+
def c_quit(self):
|
621 |
+
if self.busy:
|
622 |
+
self.array.cancel()
|
623 |
+
self.master.after_idle(self.master.quit)
|
624 |
+
|
625 |
+
|
626 |
+
# Main program -- for stand-alone operation outside Grail
|
627 |
+
|
628 |
+
def main():
|
629 |
+
root = Tk()
|
630 |
+
demo = SortDemo(root)
|
631 |
+
root.protocol('WM_DELETE_WINDOW', demo.c_quit)
|
632 |
+
root.mainloop()
|
633 |
+
|
634 |
+
if __name__ == '__main__':
|
635 |
+
main()
|
marketing/Tools/demo/spreadsheet.py
ADDED
@@ -0,0 +1,829 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
SS1 -- a spreadsheet-like application.
|
5 |
+
"""
|
6 |
+
|
7 |
+
import os
|
8 |
+
import re
|
9 |
+
import sys
|
10 |
+
from xml.parsers import expat
|
11 |
+
from xml.sax.saxutils import escape
|
12 |
+
|
13 |
+
LEFT, CENTER, RIGHT = "LEFT", "CENTER", "RIGHT"
|
14 |
+
|
15 |
+
def ljust(x, n):
|
16 |
+
return x.ljust(n)
|
17 |
+
def center(x, n):
|
18 |
+
return x.center(n)
|
19 |
+
def rjust(x, n):
|
20 |
+
return x.rjust(n)
|
21 |
+
align2action = {LEFT: ljust, CENTER: center, RIGHT: rjust}
|
22 |
+
|
23 |
+
align2xml = {LEFT: "left", CENTER: "center", RIGHT: "right"}
|
24 |
+
xml2align = {"left": LEFT, "center": CENTER, "right": RIGHT}
|
25 |
+
|
26 |
+
align2anchor = {LEFT: "w", CENTER: "center", RIGHT: "e"}
|
27 |
+
|
28 |
+
def sum(seq):
|
29 |
+
total = 0
|
30 |
+
for x in seq:
|
31 |
+
if x is not None:
|
32 |
+
total += x
|
33 |
+
return total
|
34 |
+
|
35 |
+
class Sheet:
|
36 |
+
|
37 |
+
def __init__(self):
|
38 |
+
self.cells = {} # {(x, y): cell, ...}
|
39 |
+
self.ns = dict(
|
40 |
+
cell = self.cellvalue,
|
41 |
+
cells = self.multicellvalue,
|
42 |
+
sum = sum,
|
43 |
+
)
|
44 |
+
|
45 |
+
def cellvalue(self, x, y):
|
46 |
+
cell = self.getcell(x, y)
|
47 |
+
if hasattr(cell, 'recalc'):
|
48 |
+
return cell.recalc(self.ns)
|
49 |
+
else:
|
50 |
+
return cell
|
51 |
+
|
52 |
+
def multicellvalue(self, x1, y1, x2, y2):
|
53 |
+
if x1 > x2:
|
54 |
+
x1, x2 = x2, x1
|
55 |
+
if y1 > y2:
|
56 |
+
y1, y2 = y2, y1
|
57 |
+
seq = []
|
58 |
+
for y in range(y1, y2+1):
|
59 |
+
for x in range(x1, x2+1):
|
60 |
+
seq.append(self.cellvalue(x, y))
|
61 |
+
return seq
|
62 |
+
|
63 |
+
def getcell(self, x, y):
|
64 |
+
return self.cells.get((x, y))
|
65 |
+
|
66 |
+
def setcell(self, x, y, cell):
|
67 |
+
assert x > 0 and y > 0
|
68 |
+
assert isinstance(cell, BaseCell)
|
69 |
+
self.cells[x, y] = cell
|
70 |
+
|
71 |
+
def clearcell(self, x, y):
|
72 |
+
try:
|
73 |
+
del self.cells[x, y]
|
74 |
+
except KeyError:
|
75 |
+
pass
|
76 |
+
|
77 |
+
def clearcells(self, x1, y1, x2, y2):
|
78 |
+
for xy in self.selectcells(x1, y1, x2, y2):
|
79 |
+
del self.cells[xy]
|
80 |
+
|
81 |
+
def clearrows(self, y1, y2):
|
82 |
+
self.clearcells(0, y1, sys.maxsize, y2)
|
83 |
+
|
84 |
+
def clearcolumns(self, x1, x2):
|
85 |
+
self.clearcells(x1, 0, x2, sys.maxsize)
|
86 |
+
|
87 |
+
def selectcells(self, x1, y1, x2, y2):
|
88 |
+
if x1 > x2:
|
89 |
+
x1, x2 = x2, x1
|
90 |
+
if y1 > y2:
|
91 |
+
y1, y2 = y2, y1
|
92 |
+
return [(x, y) for x, y in self.cells
|
93 |
+
if x1 <= x <= x2 and y1 <= y <= y2]
|
94 |
+
|
95 |
+
def movecells(self, x1, y1, x2, y2, dx, dy):
|
96 |
+
if dx == 0 and dy == 0:
|
97 |
+
return
|
98 |
+
if x1 > x2:
|
99 |
+
x1, x2 = x2, x1
|
100 |
+
if y1 > y2:
|
101 |
+
y1, y2 = y2, y1
|
102 |
+
assert x1+dx > 0 and y1+dy > 0
|
103 |
+
new = {}
|
104 |
+
for x, y in self.cells:
|
105 |
+
cell = self.cells[x, y]
|
106 |
+
if hasattr(cell, 'renumber'):
|
107 |
+
cell = cell.renumber(x1, y1, x2, y2, dx, dy)
|
108 |
+
if x1 <= x <= x2 and y1 <= y <= y2:
|
109 |
+
x += dx
|
110 |
+
y += dy
|
111 |
+
new[x, y] = cell
|
112 |
+
self.cells = new
|
113 |
+
|
114 |
+
def insertrows(self, y, n):
|
115 |
+
assert n > 0
|
116 |
+
self.movecells(0, y, sys.maxsize, sys.maxsize, 0, n)
|
117 |
+
|
118 |
+
def deleterows(self, y1, y2):
|
119 |
+
if y1 > y2:
|
120 |
+
y1, y2 = y2, y1
|
121 |
+
self.clearrows(y1, y2)
|
122 |
+
self.movecells(0, y2+1, sys.maxsize, sys.maxsize, 0, y1-y2-1)
|
123 |
+
|
124 |
+
def insertcolumns(self, x, n):
|
125 |
+
assert n > 0
|
126 |
+
self.movecells(x, 0, sys.maxsize, sys.maxsize, n, 0)
|
127 |
+
|
128 |
+
def deletecolumns(self, x1, x2):
|
129 |
+
if x1 > x2:
|
130 |
+
x1, x2 = x2, x1
|
131 |
+
self.clearcells(x1, x2)
|
132 |
+
self.movecells(x2+1, 0, sys.maxsize, sys.maxsize, x1-x2-1, 0)
|
133 |
+
|
134 |
+
def getsize(self):
|
135 |
+
maxx = maxy = 0
|
136 |
+
for x, y in self.cells:
|
137 |
+
maxx = max(maxx, x)
|
138 |
+
maxy = max(maxy, y)
|
139 |
+
return maxx, maxy
|
140 |
+
|
141 |
+
def reset(self):
|
142 |
+
for cell in self.cells.values():
|
143 |
+
if hasattr(cell, 'reset'):
|
144 |
+
cell.reset()
|
145 |
+
|
146 |
+
def recalc(self):
|
147 |
+
self.reset()
|
148 |
+
for cell in self.cells.values():
|
149 |
+
if hasattr(cell, 'recalc'):
|
150 |
+
cell.recalc(self.ns)
|
151 |
+
|
152 |
+
def display(self):
|
153 |
+
maxx, maxy = self.getsize()
|
154 |
+
width, height = maxx+1, maxy+1
|
155 |
+
colwidth = [1] * width
|
156 |
+
full = {}
|
157 |
+
# Add column heading labels in row 0
|
158 |
+
for x in range(1, width):
|
159 |
+
full[x, 0] = text, alignment = colnum2name(x), RIGHT
|
160 |
+
colwidth[x] = max(colwidth[x], len(text))
|
161 |
+
# Add row labels in column 0
|
162 |
+
for y in range(1, height):
|
163 |
+
full[0, y] = text, alignment = str(y), RIGHT
|
164 |
+
colwidth[0] = max(colwidth[0], len(text))
|
165 |
+
# Add sheet cells in columns with x>0 and y>0
|
166 |
+
for (x, y), cell in self.cells.items():
|
167 |
+
if x <= 0 or y <= 0:
|
168 |
+
continue
|
169 |
+
if hasattr(cell, 'recalc'):
|
170 |
+
cell.recalc(self.ns)
|
171 |
+
if hasattr(cell, 'format'):
|
172 |
+
text, alignment = cell.format()
|
173 |
+
assert isinstance(text, str)
|
174 |
+
assert alignment in (LEFT, CENTER, RIGHT)
|
175 |
+
else:
|
176 |
+
text = str(cell)
|
177 |
+
if isinstance(cell, str):
|
178 |
+
alignment = LEFT
|
179 |
+
else:
|
180 |
+
alignment = RIGHT
|
181 |
+
full[x, y] = (text, alignment)
|
182 |
+
colwidth[x] = max(colwidth[x], len(text))
|
183 |
+
# Calculate the horizontal separator line (dashes and dots)
|
184 |
+
sep = ""
|
185 |
+
for x in range(width):
|
186 |
+
if sep:
|
187 |
+
sep += "+"
|
188 |
+
sep += "-"*colwidth[x]
|
189 |
+
# Now print The full grid
|
190 |
+
for y in range(height):
|
191 |
+
line = ""
|
192 |
+
for x in range(width):
|
193 |
+
text, alignment = full.get((x, y)) or ("", LEFT)
|
194 |
+
text = align2action[alignment](text, colwidth[x])
|
195 |
+
if line:
|
196 |
+
line += '|'
|
197 |
+
line += text
|
198 |
+
print(line)
|
199 |
+
if y == 0:
|
200 |
+
print(sep)
|
201 |
+
|
202 |
+
def xml(self):
|
203 |
+
out = ['<spreadsheet>']
|
204 |
+
for (x, y), cell in self.cells.items():
|
205 |
+
if hasattr(cell, 'xml'):
|
206 |
+
cellxml = cell.xml()
|
207 |
+
else:
|
208 |
+
cellxml = '<value>%s</value>' % escape(cell)
|
209 |
+
out.append('<cell row="%s" col="%s">\n %s\n</cell>' %
|
210 |
+
(y, x, cellxml))
|
211 |
+
out.append('</spreadsheet>')
|
212 |
+
return '\n'.join(out)
|
213 |
+
|
214 |
+
def save(self, filename):
|
215 |
+
text = self.xml()
|
216 |
+
with open(filename, "w", encoding='utf-8') as f:
|
217 |
+
f.write(text)
|
218 |
+
if text and not text.endswith('\n'):
|
219 |
+
f.write('\n')
|
220 |
+
|
221 |
+
def load(self, filename):
|
222 |
+
with open(filename, 'rb') as f:
|
223 |
+
SheetParser(self).parsefile(f)
|
224 |
+
|
225 |
+
class SheetParser:
|
226 |
+
|
227 |
+
def __init__(self, sheet):
|
228 |
+
self.sheet = sheet
|
229 |
+
|
230 |
+
def parsefile(self, f):
|
231 |
+
parser = expat.ParserCreate()
|
232 |
+
parser.StartElementHandler = self.startelement
|
233 |
+
parser.EndElementHandler = self.endelement
|
234 |
+
parser.CharacterDataHandler = self.data
|
235 |
+
parser.ParseFile(f)
|
236 |
+
|
237 |
+
def startelement(self, tag, attrs):
|
238 |
+
method = getattr(self, 'start_'+tag, None)
|
239 |
+
if method:
|
240 |
+
method(attrs)
|
241 |
+
self.texts = []
|
242 |
+
|
243 |
+
def data(self, text):
|
244 |
+
self.texts.append(text)
|
245 |
+
|
246 |
+
def endelement(self, tag):
|
247 |
+
method = getattr(self, 'end_'+tag, None)
|
248 |
+
if method:
|
249 |
+
method("".join(self.texts))
|
250 |
+
|
251 |
+
def start_cell(self, attrs):
|
252 |
+
self.y = int(attrs.get("row"))
|
253 |
+
self.x = int(attrs.get("col"))
|
254 |
+
|
255 |
+
def start_value(self, attrs):
|
256 |
+
self.fmt = attrs.get('format')
|
257 |
+
self.alignment = xml2align.get(attrs.get('align'))
|
258 |
+
|
259 |
+
start_formula = start_value
|
260 |
+
|
261 |
+
def end_int(self, text):
|
262 |
+
try:
|
263 |
+
self.value = int(text)
|
264 |
+
except (TypeError, ValueError):
|
265 |
+
self.value = None
|
266 |
+
|
267 |
+
end_long = end_int
|
268 |
+
|
269 |
+
def end_double(self, text):
|
270 |
+
try:
|
271 |
+
self.value = float(text)
|
272 |
+
except (TypeError, ValueError):
|
273 |
+
self.value = None
|
274 |
+
|
275 |
+
def end_complex(self, text):
|
276 |
+
try:
|
277 |
+
self.value = complex(text)
|
278 |
+
except (TypeError, ValueError):
|
279 |
+
self.value = None
|
280 |
+
|
281 |
+
def end_string(self, text):
|
282 |
+
self.value = text
|
283 |
+
|
284 |
+
def end_value(self, text):
|
285 |
+
if isinstance(self.value, BaseCell):
|
286 |
+
self.cell = self.value
|
287 |
+
elif isinstance(self.value, str):
|
288 |
+
self.cell = StringCell(self.value,
|
289 |
+
self.fmt or "%s",
|
290 |
+
self.alignment or LEFT)
|
291 |
+
else:
|
292 |
+
self.cell = NumericCell(self.value,
|
293 |
+
self.fmt or "%s",
|
294 |
+
self.alignment or RIGHT)
|
295 |
+
|
296 |
+
def end_formula(self, text):
|
297 |
+
self.cell = FormulaCell(text,
|
298 |
+
self.fmt or "%s",
|
299 |
+
self.alignment or RIGHT)
|
300 |
+
|
301 |
+
def end_cell(self, text):
|
302 |
+
self.sheet.setcell(self.x, self.y, self.cell)
|
303 |
+
|
304 |
+
class BaseCell:
|
305 |
+
__init__ = None # Must provide
|
306 |
+
"""Abstract base class for sheet cells.
|
307 |
+
|
308 |
+
Subclasses may but needn't provide the following APIs:
|
309 |
+
|
310 |
+
cell.reset() -- prepare for recalculation
|
311 |
+
cell.recalc(ns) -> value -- recalculate formula
|
312 |
+
cell.format() -> (value, alignment) -- return formatted value
|
313 |
+
cell.xml() -> string -- return XML
|
314 |
+
"""
|
315 |
+
|
316 |
+
class NumericCell(BaseCell):
|
317 |
+
|
318 |
+
def __init__(self, value, fmt="%s", alignment=RIGHT):
|
319 |
+
assert isinstance(value, (int, float, complex))
|
320 |
+
assert alignment in (LEFT, CENTER, RIGHT)
|
321 |
+
self.value = value
|
322 |
+
self.fmt = fmt
|
323 |
+
self.alignment = alignment
|
324 |
+
|
325 |
+
def recalc(self, ns):
|
326 |
+
return self.value
|
327 |
+
|
328 |
+
def format(self):
|
329 |
+
try:
|
330 |
+
text = self.fmt % self.value
|
331 |
+
except:
|
332 |
+
text = str(self.value)
|
333 |
+
return text, self.alignment
|
334 |
+
|
335 |
+
def xml(self):
|
336 |
+
method = getattr(self, '_xml_' + type(self.value).__name__)
|
337 |
+
return '<value align="%s" format="%s">%s</value>' % (
|
338 |
+
align2xml[self.alignment],
|
339 |
+
self.fmt,
|
340 |
+
method())
|
341 |
+
|
342 |
+
def _xml_int(self):
|
343 |
+
if -2**31 <= self.value < 2**31:
|
344 |
+
return '<int>%s</int>' % self.value
|
345 |
+
else:
|
346 |
+
return '<long>%s</long>' % self.value
|
347 |
+
|
348 |
+
def _xml_float(self):
|
349 |
+
return '<double>%r</double>' % self.value
|
350 |
+
|
351 |
+
def _xml_complex(self):
|
352 |
+
return '<complex>%r</complex>' % self.value
|
353 |
+
|
354 |
+
class StringCell(BaseCell):
|
355 |
+
|
356 |
+
def __init__(self, text, fmt="%s", alignment=LEFT):
|
357 |
+
assert isinstance(text, str)
|
358 |
+
assert alignment in (LEFT, CENTER, RIGHT)
|
359 |
+
self.text = text
|
360 |
+
self.fmt = fmt
|
361 |
+
self.alignment = alignment
|
362 |
+
|
363 |
+
def recalc(self, ns):
|
364 |
+
return self.text
|
365 |
+
|
366 |
+
def format(self):
|
367 |
+
return self.text, self.alignment
|
368 |
+
|
369 |
+
def xml(self):
|
370 |
+
s = '<value align="%s" format="%s"><string>%s</string></value>'
|
371 |
+
return s % (
|
372 |
+
align2xml[self.alignment],
|
373 |
+
self.fmt,
|
374 |
+
escape(self.text))
|
375 |
+
|
376 |
+
class FormulaCell(BaseCell):
|
377 |
+
|
378 |
+
def __init__(self, formula, fmt="%s", alignment=RIGHT):
|
379 |
+
assert alignment in (LEFT, CENTER, RIGHT)
|
380 |
+
self.formula = formula
|
381 |
+
self.translated = translate(self.formula)
|
382 |
+
self.fmt = fmt
|
383 |
+
self.alignment = alignment
|
384 |
+
self.reset()
|
385 |
+
|
386 |
+
def reset(self):
|
387 |
+
self.value = None
|
388 |
+
|
389 |
+
def recalc(self, ns):
|
390 |
+
if self.value is None:
|
391 |
+
try:
|
392 |
+
self.value = eval(self.translated, ns)
|
393 |
+
except:
|
394 |
+
exc = sys.exc_info()[0]
|
395 |
+
if hasattr(exc, "__name__"):
|
396 |
+
self.value = exc.__name__
|
397 |
+
else:
|
398 |
+
self.value = str(exc)
|
399 |
+
return self.value
|
400 |
+
|
401 |
+
def format(self):
|
402 |
+
try:
|
403 |
+
text = self.fmt % self.value
|
404 |
+
except:
|
405 |
+
text = str(self.value)
|
406 |
+
return text, self.alignment
|
407 |
+
|
408 |
+
def xml(self):
|
409 |
+
return '<formula align="%s" format="%s">%s</formula>' % (
|
410 |
+
align2xml[self.alignment],
|
411 |
+
self.fmt,
|
412 |
+
escape(self.formula))
|
413 |
+
|
414 |
+
def renumber(self, x1, y1, x2, y2, dx, dy):
|
415 |
+
out = []
|
416 |
+
for part in re.split(r'(\w+)', self.formula):
|
417 |
+
m = re.match('^([A-Z]+)([1-9][0-9]*)$', part)
|
418 |
+
if m is not None:
|
419 |
+
sx, sy = m.groups()
|
420 |
+
x = colname2num(sx)
|
421 |
+
y = int(sy)
|
422 |
+
if x1 <= x <= x2 and y1 <= y <= y2:
|
423 |
+
part = cellname(x+dx, y+dy)
|
424 |
+
out.append(part)
|
425 |
+
return FormulaCell("".join(out), self.fmt, self.alignment)
|
426 |
+
|
427 |
+
def translate(formula):
|
428 |
+
"""Translate a formula containing fancy cell names to valid Python code.
|
429 |
+
|
430 |
+
Examples:
|
431 |
+
B4 -> cell(2, 4)
|
432 |
+
B4:Z100 -> cells(2, 4, 26, 100)
|
433 |
+
"""
|
434 |
+
out = []
|
435 |
+
for part in re.split(r"(\w+(?::\w+)?)", formula):
|
436 |
+
m = re.match(r"^([A-Z]+)([1-9][0-9]*)(?::([A-Z]+)([1-9][0-9]*))?$", part)
|
437 |
+
if m is None:
|
438 |
+
out.append(part)
|
439 |
+
else:
|
440 |
+
x1, y1, x2, y2 = m.groups()
|
441 |
+
x1 = colname2num(x1)
|
442 |
+
if x2 is None:
|
443 |
+
s = "cell(%s, %s)" % (x1, y1)
|
444 |
+
else:
|
445 |
+
x2 = colname2num(x2)
|
446 |
+
s = "cells(%s, %s, %s, %s)" % (x1, y1, x2, y2)
|
447 |
+
out.append(s)
|
448 |
+
return "".join(out)
|
449 |
+
|
450 |
+
def cellname(x, y):
|
451 |
+
"Translate a cell coordinate to a fancy cell name (e.g. (1, 1)->'A1')."
|
452 |
+
assert x > 0 # Column 0 has an empty name, so can't use that
|
453 |
+
return colnum2name(x) + str(y)
|
454 |
+
|
455 |
+
def colname2num(s):
|
456 |
+
"Translate a column name to number (e.g. 'A'->1, 'Z'->26, 'AA'->27)."
|
457 |
+
s = s.upper()
|
458 |
+
n = 0
|
459 |
+
for c in s:
|
460 |
+
assert 'A' <= c <= 'Z'
|
461 |
+
n = n*26 + ord(c) - ord('A') + 1
|
462 |
+
return n
|
463 |
+
|
464 |
+
def colnum2name(n):
|
465 |
+
"Translate a column number to name (e.g. 1->'A', etc.)."
|
466 |
+
assert n > 0
|
467 |
+
s = ""
|
468 |
+
while n:
|
469 |
+
n, m = divmod(n-1, 26)
|
470 |
+
s = chr(m+ord('A')) + s
|
471 |
+
return s
|
472 |
+
|
473 |
+
import tkinter as Tk
|
474 |
+
|
475 |
+
class SheetGUI:
|
476 |
+
|
477 |
+
"""Beginnings of a GUI for a spreadsheet.
|
478 |
+
|
479 |
+
TO DO:
|
480 |
+
- clear multiple cells
|
481 |
+
- Insert, clear, remove rows or columns
|
482 |
+
- Show new contents while typing
|
483 |
+
- Scroll bars
|
484 |
+
- Grow grid when window is grown
|
485 |
+
- Proper menus
|
486 |
+
- Undo, redo
|
487 |
+
- Cut, copy and paste
|
488 |
+
- Formatting and alignment
|
489 |
+
"""
|
490 |
+
|
491 |
+
def __init__(self, filename="sheet1.xml", rows=10, columns=5):
|
492 |
+
"""Constructor.
|
493 |
+
|
494 |
+
Load the sheet from the filename argument.
|
495 |
+
Set up the Tk widget tree.
|
496 |
+
"""
|
497 |
+
# Create and load the sheet
|
498 |
+
self.filename = filename
|
499 |
+
self.sheet = Sheet()
|
500 |
+
if os.path.isfile(filename):
|
501 |
+
self.sheet.load(filename)
|
502 |
+
# Calculate the needed grid size
|
503 |
+
maxx, maxy = self.sheet.getsize()
|
504 |
+
rows = max(rows, maxy)
|
505 |
+
columns = max(columns, maxx)
|
506 |
+
# Create the widgets
|
507 |
+
self.root = Tk.Tk()
|
508 |
+
self.root.wm_title("Spreadsheet: %s" % self.filename)
|
509 |
+
self.beacon = Tk.Label(self.root, text="A1",
|
510 |
+
font=('helvetica', 16, 'bold'))
|
511 |
+
self.entry = Tk.Entry(self.root)
|
512 |
+
self.savebutton = Tk.Button(self.root, text="Save",
|
513 |
+
command=self.save)
|
514 |
+
self.cellgrid = Tk.Frame(self.root)
|
515 |
+
# Configure the widget lay-out
|
516 |
+
self.cellgrid.pack(side="bottom", expand=1, fill="both")
|
517 |
+
self.beacon.pack(side="left")
|
518 |
+
self.savebutton.pack(side="right")
|
519 |
+
self.entry.pack(side="left", expand=1, fill="x")
|
520 |
+
# Bind some events
|
521 |
+
self.entry.bind("<Return>", self.return_event)
|
522 |
+
self.entry.bind("<Shift-Return>", self.shift_return_event)
|
523 |
+
self.entry.bind("<Tab>", self.tab_event)
|
524 |
+
self.entry.bind("<Shift-Tab>", self.shift_tab_event)
|
525 |
+
self.entry.bind("<Delete>", self.delete_event)
|
526 |
+
self.entry.bind("<Escape>", self.escape_event)
|
527 |
+
# Now create the cell grid
|
528 |
+
self.makegrid(rows, columns)
|
529 |
+
# Select the top-left cell
|
530 |
+
self.currentxy = None
|
531 |
+
self.cornerxy = None
|
532 |
+
self.setcurrent(1, 1)
|
533 |
+
# Copy the sheet cells to the GUI cells
|
534 |
+
self.sync()
|
535 |
+
|
536 |
+
def delete_event(self, event):
|
537 |
+
if self.cornerxy != self.currentxy and self.cornerxy is not None:
|
538 |
+
self.sheet.clearcells(*(self.currentxy + self.cornerxy))
|
539 |
+
else:
|
540 |
+
self.sheet.clearcell(*self.currentxy)
|
541 |
+
self.sync()
|
542 |
+
self.entry.delete(0, 'end')
|
543 |
+
return "break"
|
544 |
+
|
545 |
+
def escape_event(self, event):
|
546 |
+
x, y = self.currentxy
|
547 |
+
self.load_entry(x, y)
|
548 |
+
|
549 |
+
def load_entry(self, x, y):
|
550 |
+
cell = self.sheet.getcell(x, y)
|
551 |
+
if cell is None:
|
552 |
+
text = ""
|
553 |
+
elif isinstance(cell, FormulaCell):
|
554 |
+
text = '=' + cell.formula
|
555 |
+
else:
|
556 |
+
text, alignment = cell.format()
|
557 |
+
self.entry.delete(0, 'end')
|
558 |
+
self.entry.insert(0, text)
|
559 |
+
self.entry.selection_range(0, 'end')
|
560 |
+
|
561 |
+
def makegrid(self, rows, columns):
|
562 |
+
"""Helper to create the grid of GUI cells.
|
563 |
+
|
564 |
+
The edge (x==0 or y==0) is filled with labels; the rest is real cells.
|
565 |
+
"""
|
566 |
+
self.rows = rows
|
567 |
+
self.columns = columns
|
568 |
+
self.gridcells = {}
|
569 |
+
# Create the top left corner cell (which selects all)
|
570 |
+
cell = Tk.Label(self.cellgrid, relief='raised')
|
571 |
+
cell.grid_configure(column=0, row=0, sticky='NSWE')
|
572 |
+
cell.bind("<ButtonPress-1>", self.selectall)
|
573 |
+
# Create the top row of labels, and configure the grid columns
|
574 |
+
for x in range(1, columns+1):
|
575 |
+
self.cellgrid.grid_columnconfigure(x, minsize=64)
|
576 |
+
cell = Tk.Label(self.cellgrid, text=colnum2name(x), relief='raised')
|
577 |
+
cell.grid_configure(column=x, row=0, sticky='WE')
|
578 |
+
self.gridcells[x, 0] = cell
|
579 |
+
cell.__x = x
|
580 |
+
cell.__y = 0
|
581 |
+
cell.bind("<ButtonPress-1>", self.selectcolumn)
|
582 |
+
cell.bind("<B1-Motion>", self.extendcolumn)
|
583 |
+
cell.bind("<ButtonRelease-1>", self.extendcolumn)
|
584 |
+
cell.bind("<Shift-Button-1>", self.extendcolumn)
|
585 |
+
# Create the leftmost column of labels
|
586 |
+
for y in range(1, rows+1):
|
587 |
+
cell = Tk.Label(self.cellgrid, text=str(y), relief='raised')
|
588 |
+
cell.grid_configure(column=0, row=y, sticky='WE')
|
589 |
+
self.gridcells[0, y] = cell
|
590 |
+
cell.__x = 0
|
591 |
+
cell.__y = y
|
592 |
+
cell.bind("<ButtonPress-1>", self.selectrow)
|
593 |
+
cell.bind("<B1-Motion>", self.extendrow)
|
594 |
+
cell.bind("<ButtonRelease-1>", self.extendrow)
|
595 |
+
cell.bind("<Shift-Button-1>", self.extendrow)
|
596 |
+
# Create the real cells
|
597 |
+
for x in range(1, columns+1):
|
598 |
+
for y in range(1, rows+1):
|
599 |
+
cell = Tk.Label(self.cellgrid, relief='sunken',
|
600 |
+
bg='white', fg='black')
|
601 |
+
cell.grid_configure(column=x, row=y, sticky='NSWE')
|
602 |
+
self.gridcells[x, y] = cell
|
603 |
+
cell.__x = x
|
604 |
+
cell.__y = y
|
605 |
+
# Bind mouse events
|
606 |
+
cell.bind("<ButtonPress-1>", self.press)
|
607 |
+
cell.bind("<B1-Motion>", self.motion)
|
608 |
+
cell.bind("<ButtonRelease-1>", self.release)
|
609 |
+
cell.bind("<Shift-Button-1>", self.release)
|
610 |
+
|
611 |
+
def selectall(self, event):
|
612 |
+
self.setcurrent(1, 1)
|
613 |
+
self.setcorner(sys.maxsize, sys.maxsize)
|
614 |
+
|
615 |
+
def selectcolumn(self, event):
|
616 |
+
x, y = self.whichxy(event)
|
617 |
+
self.setcurrent(x, 1)
|
618 |
+
self.setcorner(x, sys.maxsize)
|
619 |
+
|
620 |
+
def extendcolumn(self, event):
|
621 |
+
x, y = self.whichxy(event)
|
622 |
+
if x > 0:
|
623 |
+
self.setcurrent(self.currentxy[0], 1)
|
624 |
+
self.setcorner(x, sys.maxsize)
|
625 |
+
|
626 |
+
def selectrow(self, event):
|
627 |
+
x, y = self.whichxy(event)
|
628 |
+
self.setcurrent(1, y)
|
629 |
+
self.setcorner(sys.maxsize, y)
|
630 |
+
|
631 |
+
def extendrow(self, event):
|
632 |
+
x, y = self.whichxy(event)
|
633 |
+
if y > 0:
|
634 |
+
self.setcurrent(1, self.currentxy[1])
|
635 |
+
self.setcorner(sys.maxsize, y)
|
636 |
+
|
637 |
+
def press(self, event):
|
638 |
+
x, y = self.whichxy(event)
|
639 |
+
if x > 0 and y > 0:
|
640 |
+
self.setcurrent(x, y)
|
641 |
+
|
642 |
+
def motion(self, event):
|
643 |
+
x, y = self.whichxy(event)
|
644 |
+
if x > 0 and y > 0:
|
645 |
+
self.setcorner(x, y)
|
646 |
+
|
647 |
+
release = motion
|
648 |
+
|
649 |
+
def whichxy(self, event):
|
650 |
+
w = self.cellgrid.winfo_containing(event.x_root, event.y_root)
|
651 |
+
if w is not None and isinstance(w, Tk.Label):
|
652 |
+
try:
|
653 |
+
return w.__x, w.__y
|
654 |
+
except AttributeError:
|
655 |
+
pass
|
656 |
+
return 0, 0
|
657 |
+
|
658 |
+
def save(self):
|
659 |
+
self.sheet.save(self.filename)
|
660 |
+
|
661 |
+
def setcurrent(self, x, y):
|
662 |
+
"Make (x, y) the current cell."
|
663 |
+
if self.currentxy is not None:
|
664 |
+
self.change_cell()
|
665 |
+
self.clearfocus()
|
666 |
+
self.beacon['text'] = cellname(x, y)
|
667 |
+
self.load_entry(x, y)
|
668 |
+
self.entry.focus_set()
|
669 |
+
self.currentxy = x, y
|
670 |
+
self.cornerxy = None
|
671 |
+
gridcell = self.gridcells.get(self.currentxy)
|
672 |
+
if gridcell is not None:
|
673 |
+
gridcell['bg'] = 'yellow'
|
674 |
+
|
675 |
+
def setcorner(self, x, y):
|
676 |
+
if self.currentxy is None or self.currentxy == (x, y):
|
677 |
+
self.setcurrent(x, y)
|
678 |
+
return
|
679 |
+
self.clearfocus()
|
680 |
+
self.cornerxy = x, y
|
681 |
+
x1, y1 = self.currentxy
|
682 |
+
x2, y2 = self.cornerxy or self.currentxy
|
683 |
+
if x1 > x2:
|
684 |
+
x1, x2 = x2, x1
|
685 |
+
if y1 > y2:
|
686 |
+
y1, y2 = y2, y1
|
687 |
+
for (x, y), cell in self.gridcells.items():
|
688 |
+
if x1 <= x <= x2 and y1 <= y <= y2:
|
689 |
+
cell['bg'] = 'lightBlue'
|
690 |
+
gridcell = self.gridcells.get(self.currentxy)
|
691 |
+
if gridcell is not None:
|
692 |
+
gridcell['bg'] = 'yellow'
|
693 |
+
self.setbeacon(x1, y1, x2, y2)
|
694 |
+
|
695 |
+
def setbeacon(self, x1, y1, x2, y2):
|
696 |
+
if x1 == y1 == 1 and x2 == y2 == sys.maxsize:
|
697 |
+
name = ":"
|
698 |
+
elif (x1, x2) == (1, sys.maxsize):
|
699 |
+
if y1 == y2:
|
700 |
+
name = "%d" % y1
|
701 |
+
else:
|
702 |
+
name = "%d:%d" % (y1, y2)
|
703 |
+
elif (y1, y2) == (1, sys.maxsize):
|
704 |
+
if x1 == x2:
|
705 |
+
name = "%s" % colnum2name(x1)
|
706 |
+
else:
|
707 |
+
name = "%s:%s" % (colnum2name(x1), colnum2name(x2))
|
708 |
+
else:
|
709 |
+
name1 = cellname(*self.currentxy)
|
710 |
+
name2 = cellname(*self.cornerxy)
|
711 |
+
name = "%s:%s" % (name1, name2)
|
712 |
+
self.beacon['text'] = name
|
713 |
+
|
714 |
+
|
715 |
+
def clearfocus(self):
|
716 |
+
if self.currentxy is not None:
|
717 |
+
x1, y1 = self.currentxy
|
718 |
+
x2, y2 = self.cornerxy or self.currentxy
|
719 |
+
if x1 > x2:
|
720 |
+
x1, x2 = x2, x1
|
721 |
+
if y1 > y2:
|
722 |
+
y1, y2 = y2, y1
|
723 |
+
for (x, y), cell in self.gridcells.items():
|
724 |
+
if x1 <= x <= x2 and y1 <= y <= y2:
|
725 |
+
cell['bg'] = 'white'
|
726 |
+
|
727 |
+
def return_event(self, event):
|
728 |
+
"Callback for the Return key."
|
729 |
+
self.change_cell()
|
730 |
+
x, y = self.currentxy
|
731 |
+
self.setcurrent(x, y+1)
|
732 |
+
return "break"
|
733 |
+
|
734 |
+
def shift_return_event(self, event):
|
735 |
+
"Callback for the Return key with Shift modifier."
|
736 |
+
self.change_cell()
|
737 |
+
x, y = self.currentxy
|
738 |
+
self.setcurrent(x, max(1, y-1))
|
739 |
+
return "break"
|
740 |
+
|
741 |
+
def tab_event(self, event):
|
742 |
+
"Callback for the Tab key."
|
743 |
+
self.change_cell()
|
744 |
+
x, y = self.currentxy
|
745 |
+
self.setcurrent(x+1, y)
|
746 |
+
return "break"
|
747 |
+
|
748 |
+
def shift_tab_event(self, event):
|
749 |
+
"Callback for the Tab key with Shift modifier."
|
750 |
+
self.change_cell()
|
751 |
+
x, y = self.currentxy
|
752 |
+
self.setcurrent(max(1, x-1), y)
|
753 |
+
return "break"
|
754 |
+
|
755 |
+
def change_cell(self):
|
756 |
+
"Set the current cell from the entry widget."
|
757 |
+
x, y = self.currentxy
|
758 |
+
text = self.entry.get()
|
759 |
+
cell = None
|
760 |
+
if text.startswith('='):
|
761 |
+
cell = FormulaCell(text[1:])
|
762 |
+
else:
|
763 |
+
for cls in int, float, complex:
|
764 |
+
try:
|
765 |
+
value = cls(text)
|
766 |
+
except (TypeError, ValueError):
|
767 |
+
continue
|
768 |
+
else:
|
769 |
+
cell = NumericCell(value)
|
770 |
+
break
|
771 |
+
if cell is None and text:
|
772 |
+
cell = StringCell(text)
|
773 |
+
if cell is None:
|
774 |
+
self.sheet.clearcell(x, y)
|
775 |
+
else:
|
776 |
+
self.sheet.setcell(x, y, cell)
|
777 |
+
self.sync()
|
778 |
+
|
779 |
+
def sync(self):
|
780 |
+
"Fill the GUI cells from the sheet cells."
|
781 |
+
self.sheet.recalc()
|
782 |
+
for (x, y), gridcell in self.gridcells.items():
|
783 |
+
if x == 0 or y == 0:
|
784 |
+
continue
|
785 |
+
cell = self.sheet.getcell(x, y)
|
786 |
+
if cell is None:
|
787 |
+
gridcell['text'] = ""
|
788 |
+
else:
|
789 |
+
if hasattr(cell, 'format'):
|
790 |
+
text, alignment = cell.format()
|
791 |
+
else:
|
792 |
+
text, alignment = str(cell), LEFT
|
793 |
+
gridcell['text'] = text
|
794 |
+
gridcell['anchor'] = align2anchor[alignment]
|
795 |
+
|
796 |
+
|
797 |
+
def test_basic():
|
798 |
+
"Basic non-gui self-test."
|
799 |
+
a = Sheet()
|
800 |
+
for x in range(1, 11):
|
801 |
+
for y in range(1, 11):
|
802 |
+
if x == 1:
|
803 |
+
cell = NumericCell(y)
|
804 |
+
elif y == 1:
|
805 |
+
cell = NumericCell(x)
|
806 |
+
else:
|
807 |
+
c1 = cellname(x, 1)
|
808 |
+
c2 = cellname(1, y)
|
809 |
+
formula = "%s*%s" % (c1, c2)
|
810 |
+
cell = FormulaCell(formula)
|
811 |
+
a.setcell(x, y, cell)
|
812 |
+
## if os.path.isfile("sheet1.xml"):
|
813 |
+
## print "Loading from sheet1.xml"
|
814 |
+
## a.load("sheet1.xml")
|
815 |
+
a.display()
|
816 |
+
a.save("sheet1.xml")
|
817 |
+
|
818 |
+
def test_gui():
|
819 |
+
"GUI test."
|
820 |
+
if sys.argv[1:]:
|
821 |
+
filename = sys.argv[1]
|
822 |
+
else:
|
823 |
+
filename = "sheet1.xml"
|
824 |
+
g = SheetGUI(filename)
|
825 |
+
g.root.mainloop()
|
826 |
+
|
827 |
+
if __name__ == '__main__':
|
828 |
+
#test_basic()
|
829 |
+
test_gui()
|
marketing/Tools/demo/vector.py
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
"""
|
4 |
+
A demonstration of classes and their special methods in Python.
|
5 |
+
"""
|
6 |
+
|
7 |
+
class Vec:
|
8 |
+
"""A simple vector class.
|
9 |
+
|
10 |
+
Instances of the Vec class can be constructed from numbers
|
11 |
+
|
12 |
+
>>> a = Vec(1, 2, 3)
|
13 |
+
>>> b = Vec(3, 2, 1)
|
14 |
+
|
15 |
+
added
|
16 |
+
>>> a + b
|
17 |
+
Vec(4, 4, 4)
|
18 |
+
|
19 |
+
subtracted
|
20 |
+
>>> a - b
|
21 |
+
Vec(-2, 0, 2)
|
22 |
+
|
23 |
+
and multiplied by a scalar on the left
|
24 |
+
>>> 3.0 * a
|
25 |
+
Vec(3.0, 6.0, 9.0)
|
26 |
+
|
27 |
+
or on the right
|
28 |
+
>>> a * 3.0
|
29 |
+
Vec(3.0, 6.0, 9.0)
|
30 |
+
"""
|
31 |
+
def __init__(self, *v):
|
32 |
+
self.v = list(v)
|
33 |
+
|
34 |
+
@classmethod
|
35 |
+
def fromlist(cls, v):
|
36 |
+
if not isinstance(v, list):
|
37 |
+
raise TypeError
|
38 |
+
inst = cls()
|
39 |
+
inst.v = v
|
40 |
+
return inst
|
41 |
+
|
42 |
+
def __repr__(self):
|
43 |
+
args = ', '.join(repr(x) for x in self.v)
|
44 |
+
return 'Vec({})'.format(args)
|
45 |
+
|
46 |
+
def __len__(self):
|
47 |
+
return len(self.v)
|
48 |
+
|
49 |
+
def __getitem__(self, i):
|
50 |
+
return self.v[i]
|
51 |
+
|
52 |
+
def __add__(self, other):
|
53 |
+
# Element-wise addition
|
54 |
+
v = [x + y for x, y in zip(self.v, other.v)]
|
55 |
+
return Vec.fromlist(v)
|
56 |
+
|
57 |
+
def __sub__(self, other):
|
58 |
+
# Element-wise subtraction
|
59 |
+
v = [x - y for x, y in zip(self.v, other.v)]
|
60 |
+
return Vec.fromlist(v)
|
61 |
+
|
62 |
+
def __mul__(self, scalar):
|
63 |
+
# Multiply by scalar
|
64 |
+
v = [x * scalar for x in self.v]
|
65 |
+
return Vec.fromlist(v)
|
66 |
+
|
67 |
+
__rmul__ = __mul__
|
68 |
+
|
69 |
+
|
70 |
+
def test():
|
71 |
+
import doctest
|
72 |
+
doctest.testmod()
|
73 |
+
|
74 |
+
test()
|
marketing/Tools/i18n/__pycache__/makelocalealias.cpython-39.pyc
ADDED
Binary file (3.61 kB). View file
|
|
marketing/Tools/i18n/__pycache__/msgfmt.cpython-39.pyc
ADDED
Binary file (4.74 kB). View file
|
|