mirror of
https://github.com/wahyd4/ai-projects.git
synced 2026-08-08 20:56:10 +10:00
Update
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
3.12
|
||||
venv/lib
|
||||
|
||||
Binary file not shown.
+19
-2
@@ -1,6 +1,6 @@
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
from flask import Flask, render_template, request, jsonify, redirect, url_for
|
||||
from flask_cors import CORS
|
||||
from database import init_db, add_country, remove_country, get_all_countries, save_highlight_color, get_highlight_color, save_map_type, get_map_type
|
||||
from database import init_db, add_country, remove_country, get_all_countries, save_highlight_color, get_highlight_color, save_map_type, get_map_type, create_shared_map, get_shared_map
|
||||
import requests
|
||||
|
||||
app = Flask(__name__)
|
||||
@@ -67,5 +67,22 @@ def get_map_type_route():
|
||||
map_type = get_map_type()
|
||||
return jsonify({"mapType": map_type})
|
||||
|
||||
@app.route('/share_map', methods=['POST'])
|
||||
def share_map():
|
||||
data = request.json
|
||||
countries = data['countries']
|
||||
highlight_color = data['highlight_color']
|
||||
map_type = data['map_type']
|
||||
map_id = create_shared_map(countries, highlight_color, map_type)
|
||||
return jsonify({"success": True, "share_url": f"/maps/{map_id}"})
|
||||
|
||||
@app.route('/maps/<map_id>')
|
||||
def view_shared_map(map_id):
|
||||
shared_map = get_shared_map(map_id)
|
||||
if shared_map:
|
||||
return render_template('shared_map.html', map_data=shared_map)
|
||||
else:
|
||||
return "Map not found", 404
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, port=5001)
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
import sqlite3
|
||||
import uuid
|
||||
|
||||
def init_db():
|
||||
conn = sqlite3.connect('countries.db')
|
||||
@@ -7,6 +8,8 @@ def init_db():
|
||||
(name TEXT PRIMARY KEY)''')
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS settings
|
||||
(key TEXT PRIMARY KEY, value TEXT)''')
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS shared_maps
|
||||
(id TEXT PRIMARY KEY, countries TEXT, highlight_color TEXT, map_type TEXT)''')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
@@ -61,3 +64,28 @@ def get_map_type():
|
||||
result = c.fetchone()
|
||||
conn.close()
|
||||
return result[0] if result else 'standard'
|
||||
|
||||
def create_shared_map(countries, highlight_color, map_type):
|
||||
map_id = str(uuid.uuid4())
|
||||
conn = sqlite3.connect('countries.db')
|
||||
c = conn.cursor()
|
||||
c.execute("INSERT INTO shared_maps (id, countries, highlight_color, map_type) VALUES (?, ?, ?, ?)",
|
||||
(map_id, ','.join(countries), highlight_color, map_type))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return map_id
|
||||
|
||||
def get_shared_map(map_id):
|
||||
conn = sqlite3.connect('countries.db')
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT * FROM shared_maps WHERE id = ?", (map_id,))
|
||||
result = c.fetchone()
|
||||
conn.close()
|
||||
if result:
|
||||
return {
|
||||
'id': result[0],
|
||||
'countries': result[1].split(','),
|
||||
'highlight_color': result[2],
|
||||
'map_type': result[3]
|
||||
}
|
||||
return None
|
||||
|
||||
@@ -16,7 +16,7 @@ const mapTextures = {
|
||||
satellite: '/static/images/world.topo.bathy.200412.3x5400x2700.jpg'
|
||||
};
|
||||
|
||||
export { init, addCountry, removeCountry, exportMap };
|
||||
export { init, addCountry, removeCountry, exportMap, shareMap };
|
||||
|
||||
function init() {
|
||||
if (scene) {
|
||||
@@ -436,6 +436,46 @@ function exportMap() {
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
// After exporting, show share button
|
||||
showShareButton();
|
||||
}
|
||||
|
||||
function showShareButton() {
|
||||
const shareButton = document.createElement('button');
|
||||
shareButton.textContent = '分享地图';
|
||||
shareButton.className = 'w-full bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition duration-200 mt-4';
|
||||
shareButton.onclick = shareMap;
|
||||
document.querySelector('#export-btn').insertAdjacentElement('afterend', shareButton);
|
||||
}
|
||||
|
||||
function shareMap() {
|
||||
const mapData = {
|
||||
countries: countries,
|
||||
highlight_color: highlightColor.toString(16),
|
||||
map_type: currentMapType
|
||||
};
|
||||
|
||||
fetch('/share_map', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(mapData),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const shareUrl = window.location.origin + data.share_url;
|
||||
alert(`您的地图已分享。分享链接:${shareUrl}`);
|
||||
} else {
|
||||
alert('分享失败,请重试。');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error sharing map:', error);
|
||||
alert('分享失败,请重试。');
|
||||
});
|
||||
}
|
||||
|
||||
function updateRendererSize() {
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
import * as THREE from 'three';
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
|
||||
|
||||
let scene, camera, renderer, map, controls;
|
||||
let worldData;
|
||||
let worldDataLoaded = false;
|
||||
let highlightColor = 0xFFFF00; // 默认高亮颜色
|
||||
let currentMapType = 'standard';
|
||||
const mapTextures = {
|
||||
standard: '/static/images/eo_base_2020_clean_3600x1800.png',
|
||||
night: '/static/images/BlackMarble_2016_01deg.jpg',
|
||||
gray: '/static/images/world.oceanmask.5400x2700.png',
|
||||
satellite: '/static/images/world.topo.bathy.200412.3x5400x2700.jpg'
|
||||
};
|
||||
|
||||
let countries = [];
|
||||
|
||||
export function init() {
|
||||
scene = new THREE.Scene();
|
||||
const aspect = window.innerWidth / window.innerHeight;
|
||||
const frustumSize = 180;
|
||||
camera = new THREE.OrthographicCamera(
|
||||
frustumSize * aspect / -2, frustumSize * aspect / 2,
|
||||
frustumSize / 2, frustumSize / -2,
|
||||
0.1, 1000
|
||||
);
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.getElementById('map').appendChild(renderer.domElement);
|
||||
|
||||
const mapTexture = new THREE.TextureLoader().load(mapTextures[currentMapType]);
|
||||
const material = new THREE.MeshBasicMaterial({ map: mapTexture });
|
||||
map = new THREE.Mesh(new THREE.PlaneGeometry(360, 180), material);
|
||||
scene.add(map);
|
||||
|
||||
camera.position.z = 100;
|
||||
|
||||
controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableRotate = false;
|
||||
controls.enablePan = true;
|
||||
controls.enableZoom = true;
|
||||
controls.update();
|
||||
|
||||
setupControls();
|
||||
setupColorPicker();
|
||||
setupMapSelection();
|
||||
|
||||
loadWorldData();
|
||||
animate();
|
||||
loadAllCountries();
|
||||
setupAutocomplete();
|
||||
|
||||
window.addEventListener('resize', onWindowResize, false);
|
||||
}
|
||||
|
||||
export function loadSharedMap(mapData) {
|
||||
const { countries: sharedCountries, highlight_color, map_type } = mapData;
|
||||
|
||||
changeMapType(map_type);
|
||||
setHighlightColor(parseInt(highlight_color, 16));
|
||||
|
||||
countries = sharedCountries;
|
||||
updateCountryList();
|
||||
countries.forEach(country => highlightCountry(country));
|
||||
}
|
||||
|
||||
export function addCountry() {
|
||||
const input = document.getElementById('new-country');
|
||||
const country = input.value.trim();
|
||||
if (country && !countries.includes(country)) {
|
||||
countries.push(country);
|
||||
updateCountryList();
|
||||
highlightCountry(country);
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
export function removeCountry(country) {
|
||||
countries = countries.filter(c => c !== country);
|
||||
updateCountryList();
|
||||
updateHighlights();
|
||||
}
|
||||
|
||||
export function shareMap() {
|
||||
const mapData = {
|
||||
countries: countries,
|
||||
highlight_color: highlightColor.toString(16),
|
||||
map_type: currentMapType
|
||||
};
|
||||
|
||||
fetch('/share_map', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(mapData),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const shareUrl = window.location.origin + data.share_url;
|
||||
alert(`新的共享地图已创建。分享链接:${shareUrl}`);
|
||||
window.location.href = data.share_url; // 重定向到新的共享地图
|
||||
} else {
|
||||
alert('创建共享地图失败,请重试。');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error sharing map:', error);
|
||||
alert('创建共享地图失败,请重试。');
|
||||
});
|
||||
}
|
||||
|
||||
function updateCountryList() {
|
||||
const ul = document.getElementById('countries');
|
||||
ul.innerHTML = '';
|
||||
countries.forEach(country => {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'flex justify-between items-center bg-gray-800 p-2 rounded';
|
||||
const span = document.createElement('span');
|
||||
span.textContent = country;
|
||||
span.className = 'text-gray-200';
|
||||
li.appendChild(span);
|
||||
const removeButton = document.createElement('button');
|
||||
removeButton.textContent = '删除';
|
||||
removeButton.className = 'bg-red-600 text-white px-2 py-1 rounded text-sm hover:bg-red-700 transition duration-200';
|
||||
removeButton.onclick = () => removeCountry(country);
|
||||
li.appendChild(removeButton);
|
||||
ul.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
function changeMapType(mapType) {
|
||||
if (mapType !== currentMapType) {
|
||||
currentMapType = mapType;
|
||||
const newTexture = new THREE.TextureLoader().load(mapTextures[mapType]);
|
||||
map.material.map = newTexture;
|
||||
map.material.needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
function setHighlightColor(color) {
|
||||
highlightColor = color;
|
||||
}
|
||||
|
||||
function highlightCountry(countryName) {
|
||||
if (!worldDataLoaded) {
|
||||
console.log('World data not loaded yet. Skipping highlight for:', countryName);
|
||||
return;
|
||||
}
|
||||
const feature = worldData.features.find(f => f.properties.ADMIN.toLowerCase() === countryName.toLowerCase());
|
||||
if (feature) {
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
color: highlightColor,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
side: THREE.DoubleSide,
|
||||
depthWrite: false,
|
||||
depthTest: false
|
||||
});
|
||||
if (feature.geometry.type === "Polygon") {
|
||||
const mesh = createPolygonMesh(feature.geometry.coordinates[0], material);
|
||||
scene.add(mesh);
|
||||
} else if (feature.geometry.type === "MultiPolygon") {
|
||||
feature.geometry.coordinates.forEach(polygon => {
|
||||
const mesh = createPolygonMesh(polygon[0], material);
|
||||
scene.add(mesh);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createPolygonMesh(coords, material) {
|
||||
const shape = new THREE.Shape();
|
||||
coords.forEach((coord, index) => {
|
||||
if (index === 0) {
|
||||
shape.moveTo(coord[0], coord[1]);
|
||||
} else {
|
||||
shape.lineTo(coord[0], coord[1]);
|
||||
}
|
||||
});
|
||||
const geometry = new THREE.ShapeGeometry(shape);
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
mesh.position.z = 0.1;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
function loadWorldData() {
|
||||
fetch('https://raw.githubusercontent.com/datasets/geo-countries/master/data/countries.geojson')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
worldData = data;
|
||||
worldDataLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
const aspect = window.innerWidth / window.innerHeight;
|
||||
const frustumSize = 180;
|
||||
camera.left = -frustumSize * aspect / 2;
|
||||
camera.right = frustumSize * aspect / 2;
|
||||
camera.top = frustumSize / 2;
|
||||
camera.bottom = -frustumSize / 2;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
controls.update();
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
function setupControls() {
|
||||
// 实现控制设置
|
||||
}
|
||||
|
||||
function setupColorPicker() {
|
||||
// 实现颜色选择器设置
|
||||
}
|
||||
|
||||
function setupMapSelection() {
|
||||
// 实现地图类型选择设置
|
||||
}
|
||||
|
||||
function loadAllCountries() {
|
||||
// 实现加载所有国家的功能
|
||||
}
|
||||
|
||||
function setupAutocomplete() {
|
||||
// 实现自动完成功能
|
||||
}
|
||||
|
||||
function updateHighlights() {
|
||||
// 实现更新高亮的功能
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
<input type="text" id="new-country" placeholder="输入国家名称" list="country-suggestions" class="w-full px-3 py-2 bg-gray-800 text-gray-200 border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<datalist id="country-suggestions"></datalist>
|
||||
</div>
|
||||
<button id="add-country-btn" class="w-full bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition duration-200 mb-4">添加国家</button>
|
||||
<button id="add-country-btn" class="w-full bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition duration-200 mb-2">添加国家</button>
|
||||
<p id="error-message" class="text-red-400 mt-2 mb-4"></p>
|
||||
<div class="mb-4">
|
||||
<h3 class="text-lg font-semibold mb-2 text-gray-300">高亮颜色</h3>
|
||||
@@ -48,7 +48,8 @@
|
||||
<div class="map-type-option w-16 h-16 cursor-pointer border-2 border-gray-600 rounded-md transition duration-200 hover:border-blue-500" data-map-type="satellite"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="export-btn" class="w-full bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition duration-200">导出地图</button>
|
||||
<button id="export-btn" class="w-full bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition duration-200 mb-2">导出地图</button>
|
||||
<button id="share-btn" class="w-full bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition duration-200">分享地图</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -73,13 +74,15 @@
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import { addCountry, removeCountry, init, exportMap } from "{{ url_for('static', filename='js/globe.js') }}";
|
||||
import { addCountry, removeCountry, init, exportMap, shareMap } from "{{ url_for('static', filename='js/globe.js') }}";
|
||||
window.addCountry = addCountry;
|
||||
window.removeCountry = removeCountry;
|
||||
window.exportMap = exportMap;
|
||||
window.shareMap = shareMap;
|
||||
|
||||
document.getElementById('add-country-btn').addEventListener('click', addCountry);
|
||||
document.getElementById('export-btn').addEventListener('click', exportMap);
|
||||
document.getElementById('share-btn').addEventListener('click', shareMap);
|
||||
|
||||
// Initialize the map when the page loads
|
||||
window.addEventListener('load', init);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" class="h-full bg-gray-100">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>共享地图</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"three": "https://unpkg.com/three@0.138.0/build/three.module.js",
|
||||
"three/examples/jsm/controls/OrbitControls": "https://unpkg.com/three@0.138.0/examples/jsm/controls/OrbitControls.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="h-full" x-data="{ sidebarOpen: true }">
|
||||
<div class="flex h-full">
|
||||
<!-- Sidebar -->
|
||||
<div :class="{'w-80': sidebarOpen, 'w-16': !sidebarOpen}" class="bg-gray-900 shadow-lg transition-all duration-300 ease-in-out flex flex-col">
|
||||
<!-- Toggle button -->
|
||||
<button @click="sidebarOpen = !sidebarOpen" class="p-4 text-gray-400 hover:text-gray-200 focus:outline-none">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div :class="{'hidden': !sidebarOpen}" class="p-6 flex-grow overflow-y-auto">
|
||||
<h2 class="text-2xl font-bold mb-4 text-gray-200">国家列表</h2>
|
||||
<ul id="countries" class="mb-4 space-y-2"></ul>
|
||||
<div class="mb-4">
|
||||
<input type="text" id="new-country" placeholder="输入国家名称" list="country-suggestions" class="w-full px-3 py-2 bg-gray-800 text-gray-200 border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<datalist id="country-suggestions"></datalist>
|
||||
</div>
|
||||
<button id="add-country-btn" class="w-full bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition duration-200 mb-2">添加国家</button>
|
||||
<p id="error-message" class="text-red-400 mt-2 mb-4"></p>
|
||||
<div class="mb-4">
|
||||
<h3 class="text-lg font-semibold mb-2 text-gray-300">高亮颜色</h3>
|
||||
<div id="color-picker" class="flex justify-around"></div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<h3 class="text-lg font-semibold mb-2 text-gray-300">地图背景</h3>
|
||||
<div id="map-type-selector" class="flex flex-wrap gap-2">
|
||||
<div class="map-type-option w-16 h-16 cursor-pointer border-2 border-gray-600 rounded-md transition duration-200 hover:border-blue-500" data-map-type="standard"></div>
|
||||
<div class="map-type-option w-16 h-16 cursor-pointer border-2 border-gray-600 rounded-md transition duration-200 hover:border-blue-500" data-map-type="night"></div>
|
||||
<div class="map-type-option w-16 h-16 cursor-pointer border-2 border-gray-600 rounded-md transition duration-200 hover:border-blue-500" data-map-type="gray"></div>
|
||||
<div class="map-type-option w-16 h-16 cursor-pointer border-2 border-gray-600 rounded-md transition duration-200 hover:border-blue-500" data-map-type="satellite"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="share-btn" class="w-full bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition duration-200">创建新的共享地图</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main content -->
|
||||
<div class="flex-grow relative">
|
||||
<div id="map" class="w-full h-full"></div>
|
||||
</div>
|
||||
|
||||
<!-- Map controls -->
|
||||
<div id="map-controls" class="fixed top-4 right-4 z-50 flex flex-col space-y-2">
|
||||
<button id="zoom-in" class="map-control-button bg-white text-gray-800 p-2 rounded-full shadow-md hover:bg-gray-200">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="zoom-out" class="map-control-button bg-white text-gray-800 p-2 rounded-full shadow-md hover:bg-gray-200">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import { init, loadSharedMap, addCountry, removeCountry, shareMap } from "{{ url_for('static', filename='js/shared_map.js') }}";
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
init();
|
||||
loadSharedMap({{ map_data|tojson }});
|
||||
});
|
||||
|
||||
window.addCountry = addCountry;
|
||||
window.removeCountry = removeCountry;
|
||||
window.shareMap = shareMap;
|
||||
|
||||
document.getElementById('add-country-btn').addEventListener('click', addCountry);
|
||||
document.getElementById('share-btn').addEventListener('click', shareMap);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,247 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Activate a Python virtual environment for the current PowerShell session.
|
||||
|
||||
.Description
|
||||
Pushes the python executable for a virtual environment to the front of the
|
||||
$Env:PATH environment variable and sets the prompt to signify that you are
|
||||
in a Python virtual environment. Makes use of the command line switches as
|
||||
well as the `pyvenv.cfg` file values present in the virtual environment.
|
||||
|
||||
.Parameter VenvDir
|
||||
Path to the directory that contains the virtual environment to activate. The
|
||||
default value for this is the parent of the directory that the Activate.ps1
|
||||
script is located within.
|
||||
|
||||
.Parameter Prompt
|
||||
The prompt prefix to display when this virtual environment is activated. By
|
||||
default, this prompt is the name of the virtual environment folder (VenvDir)
|
||||
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
||||
|
||||
.Example
|
||||
Activate.ps1
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Verbose
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and shows extra information about the activation as it executes.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
||||
Activates the Python virtual environment located in the specified location.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Prompt "MyPython"
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and prefixes the current prompt with the specified string (surrounded in
|
||||
parentheses) while the virtual environment is active.
|
||||
|
||||
.Notes
|
||||
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
||||
execution policy for the user. You can do this by issuing the following PowerShell
|
||||
command:
|
||||
|
||||
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
For more information on Execution Policies:
|
||||
https://go.microsoft.com/fwlink/?LinkID=135170
|
||||
|
||||
#>
|
||||
Param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$VenvDir,
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$Prompt
|
||||
)
|
||||
|
||||
<# Function declarations --------------------------------------------------- #>
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Remove all shell session elements added by the Activate script, including the
|
||||
addition of the virtual environment's Python executable from the beginning of
|
||||
the PATH variable.
|
||||
|
||||
.Parameter NonDestructive
|
||||
If present, do not remove this function from the global namespace for the
|
||||
session.
|
||||
|
||||
#>
|
||||
function global:deactivate ([switch]$NonDestructive) {
|
||||
# Revert to original values
|
||||
|
||||
# The prior prompt:
|
||||
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
||||
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
||||
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
# The prior PYTHONHOME:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
}
|
||||
|
||||
# The prior PATH:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
||||
}
|
||||
|
||||
# Just remove the VIRTUAL_ENV altogether:
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV
|
||||
}
|
||||
|
||||
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
||||
}
|
||||
|
||||
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
||||
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
||||
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
||||
}
|
||||
|
||||
# Leave deactivate function in the global namespace if requested:
|
||||
if (-not $NonDestructive) {
|
||||
Remove-Item -Path function:deactivate
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Description
|
||||
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
||||
given folder, and returns them in a map.
|
||||
|
||||
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
||||
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
||||
then it is considered a `key = value` line. The left hand string is the key,
|
||||
the right hand is the value.
|
||||
|
||||
If the value starts with a `'` or a `"` then the first and last character is
|
||||
stripped from the value before being captured.
|
||||
|
||||
.Parameter ConfigDir
|
||||
Path to the directory that contains the `pyvenv.cfg` file.
|
||||
#>
|
||||
function Get-PyVenvConfig(
|
||||
[String]
|
||||
$ConfigDir
|
||||
) {
|
||||
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
||||
|
||||
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
||||
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
||||
|
||||
# An empty map will be returned if no config file is found.
|
||||
$pyvenvConfig = @{ }
|
||||
|
||||
if ($pyvenvConfigPath) {
|
||||
|
||||
Write-Verbose "File exists, parse `key = value` lines"
|
||||
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
||||
|
||||
$pyvenvConfigContent | ForEach-Object {
|
||||
$keyval = $PSItem -split "\s*=\s*", 2
|
||||
if ($keyval[0] -and $keyval[1]) {
|
||||
$val = $keyval[1]
|
||||
|
||||
# Remove extraneous quotations around a string value.
|
||||
if ("'""".Contains($val.Substring(0, 1))) {
|
||||
$val = $val.Substring(1, $val.Length - 2)
|
||||
}
|
||||
|
||||
$pyvenvConfig[$keyval[0]] = $val
|
||||
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pyvenvConfig
|
||||
}
|
||||
|
||||
|
||||
<# Begin Activate script --------------------------------------------------- #>
|
||||
|
||||
# Determine the containing directory of this script
|
||||
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$VenvExecDir = Get-Item -Path $VenvExecPath
|
||||
|
||||
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
||||
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
||||
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
||||
|
||||
# Set values required in priority: CmdLine, ConfigFile, Default
|
||||
# First, get the location of the virtual environment, it might not be
|
||||
# VenvExecDir if specified on the command line.
|
||||
if ($VenvDir) {
|
||||
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
||||
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
||||
Write-Verbose "VenvDir=$VenvDir"
|
||||
}
|
||||
|
||||
# Next, read the `pyvenv.cfg` file to determine any required value such
|
||||
# as `prompt`.
|
||||
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
||||
|
||||
# Next, set the prompt from the command line, or the config file, or
|
||||
# just use the name of the virtual environment folder.
|
||||
if ($Prompt) {
|
||||
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
||||
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
||||
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
||||
$Prompt = $pyvenvCfg['prompt'];
|
||||
}
|
||||
else {
|
||||
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
||||
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
||||
$Prompt = Split-Path -Path $venvDir -Leaf
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose "Prompt = '$Prompt'"
|
||||
Write-Verbose "VenvDir='$VenvDir'"
|
||||
|
||||
# Deactivate any currently active virtual environment, but leave the
|
||||
# deactivate function in place.
|
||||
deactivate -nondestructive
|
||||
|
||||
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
||||
# that there is an activated venv.
|
||||
$env:VIRTUAL_ENV = $VenvDir
|
||||
|
||||
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
|
||||
Write-Verbose "Setting prompt to '$Prompt'"
|
||||
|
||||
# Set the prompt to include the env name
|
||||
# Make sure _OLD_VIRTUAL_PROMPT is global
|
||||
function global:_OLD_VIRTUAL_PROMPT { "" }
|
||||
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
||||
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
||||
|
||||
function global:prompt {
|
||||
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
||||
_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||
}
|
||||
|
||||
# Clear PYTHONHOME
|
||||
if (Test-Path -Path Env:PYTHONHOME) {
|
||||
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
Remove-Item -Path Env:PYTHONHOME
|
||||
}
|
||||
|
||||
# Add the venv to the PATH
|
||||
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
||||
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
||||
@@ -0,0 +1,70 @@
|
||||
# This file must be used with "source bin/activate" *from bash*
|
||||
# You cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
# reset old environment variables
|
||||
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
||||
PATH="${_OLD_VIRTUAL_PATH:-}"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
||||
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# Call hash to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
hash -r 2> /dev/null
|
||||
|
||||
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
||||
PS1="${_OLD_VIRTUAL_PS1:-}"
|
||||
export PS1
|
||||
unset _OLD_VIRTUAL_PS1
|
||||
fi
|
||||
|
||||
unset VIRTUAL_ENV
|
||||
unset VIRTUAL_ENV_PROMPT
|
||||
if [ ! "${1:-}" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
deactivate nondestructive
|
||||
|
||||
# on Windows, a path can contain colons and backslashes and has to be converted:
|
||||
if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
|
||||
# transform D:\path\to\venv to /d/path/to/venv on MSYS
|
||||
# and to /cygdrive/d/path/to/venv on Cygwin
|
||||
export VIRTUAL_ENV=$(cygpath "/Users/junv/code/ai-projects/venv")
|
||||
else
|
||||
# use the path as-is
|
||||
export VIRTUAL_ENV="/Users/junv/code/ai-projects/venv"
|
||||
fi
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
if [ -n "${PYTHONHOME:-}" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||
PS1="(venv) ${PS1:-}"
|
||||
export PS1
|
||||
VIRTUAL_ENV_PROMPT="(venv) "
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
fi
|
||||
|
||||
# Call hash to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
hash -r 2> /dev/null
|
||||
@@ -0,0 +1,27 @@
|
||||
# This file must be used with "source bin/activate.csh" *from csh*.
|
||||
# You cannot run it directly.
|
||||
|
||||
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
||||
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
|
||||
|
||||
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
setenv VIRTUAL_ENV "/Users/junv/code/ai-projects/venv"
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH"
|
||||
setenv PATH "$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
|
||||
set _OLD_VIRTUAL_PROMPT="$prompt"
|
||||
|
||||
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
|
||||
set prompt = "(venv) $prompt"
|
||||
setenv VIRTUAL_ENV_PROMPT "(venv) "
|
||||
endif
|
||||
|
||||
alias pydoc python -m pydoc
|
||||
|
||||
rehash
|
||||
@@ -0,0 +1,69 @@
|
||||
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
|
||||
# (https://fishshell.com/). You cannot run it directly.
|
||||
|
||||
function deactivate -d "Exit virtual environment and return to normal shell environment"
|
||||
# reset old environment variables
|
||||
if test -n "$_OLD_VIRTUAL_PATH"
|
||||
set -gx PATH $_OLD_VIRTUAL_PATH
|
||||
set -e _OLD_VIRTUAL_PATH
|
||||
end
|
||||
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
|
||||
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
|
||||
set -e _OLD_VIRTUAL_PYTHONHOME
|
||||
end
|
||||
|
||||
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
|
||||
set -e _OLD_FISH_PROMPT_OVERRIDE
|
||||
# prevents error when using nested fish instances (Issue #93858)
|
||||
if functions -q _old_fish_prompt
|
||||
functions -e fish_prompt
|
||||
functions -c _old_fish_prompt fish_prompt
|
||||
functions -e _old_fish_prompt
|
||||
end
|
||||
end
|
||||
|
||||
set -e VIRTUAL_ENV
|
||||
set -e VIRTUAL_ENV_PROMPT
|
||||
if test "$argv[1]" != "nondestructive"
|
||||
# Self-destruct!
|
||||
functions -e deactivate
|
||||
end
|
||||
end
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
set -gx VIRTUAL_ENV "/Users/junv/code/ai-projects/venv"
|
||||
|
||||
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||
set -gx PATH "$VIRTUAL_ENV/bin" $PATH
|
||||
|
||||
# Unset PYTHONHOME if set.
|
||||
if set -q PYTHONHOME
|
||||
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
|
||||
set -e PYTHONHOME
|
||||
end
|
||||
|
||||
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||
# fish uses a function instead of an env var to generate the prompt.
|
||||
|
||||
# Save the current fish_prompt function as the function _old_fish_prompt.
|
||||
functions -c fish_prompt _old_fish_prompt
|
||||
|
||||
# With the original prompt function renamed, we can override with our own.
|
||||
function fish_prompt
|
||||
# Save the return status of the last command.
|
||||
set -l old_status $status
|
||||
|
||||
# Output the venv prompt; color taken from the blue of the Python logo.
|
||||
printf "%s%s%s" (set_color 4B8BBE) "(venv) " (set_color normal)
|
||||
|
||||
# Restore the return status of the previous command.
|
||||
echo "exit $old_status" | .
|
||||
# Output the original/"old" prompt.
|
||||
_old_fish_prompt
|
||||
end
|
||||
|
||||
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
||||
set -gx VIRTUAL_ENV_PROMPT "(venv) "
|
||||
end
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/Users/junv/code/ai-projects/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/Users/junv/code/ai-projects/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/Users/junv/code/ai-projects/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
python3.12
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
python3.12
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
/usr/local/opt/python@3.12/bin/python3.12
|
||||
@@ -0,0 +1,5 @@
|
||||
home = /usr/local/opt/python@3.12/bin
|
||||
include-system-site-packages = false
|
||||
version = 3.12.6
|
||||
executable = /usr/local/Cellar/python@3.12/3.12.6/Frameworks/Python.framework/Versions/3.12/bin/python3.12
|
||||
command = /usr/local/opt/python@3.12/bin/python3.12 -m venv /Users/junv/code/ai-projects/venv
|
||||
Reference in New Issue
Block a user