Python GUIの進捗ウインドウ
呼び出し例
show_progress_wx("キャプチャ中...")
try:
update_progress_wx("キャプチャ中...")
update_progress_wx("リサイズ中...")
except Exception as e:
close_progress_wx()
進捗ウインドウ
import multiprocessing
import wx
import sys
import time
import os
import threading
LOG_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'log')
PROGRESS_FILE = os.path.abspath(os.path.join(LOG_DIR, 'progress.txt'))
CLOSE_FLAG_FILE = os.path.abspath(os.path.join(LOG_DIR, 'progress_close.flag'))
# サブプロセスで動く進捗ウィンドウ
class ProgressFrame(wx.Frame):
def __init__(self, file_path, close_flag_path):
super().__init__(None, title="進捗", style=wx.DEFAULT_FRAME_STYLE & ~(wx.RESIZE_BORDER | wx.MAXIMIZE_BOX))
self.panel = wx.Panel(self)
self.label = wx.StaticText(self.panel, label="処理中...", pos=(20, 20), size=(260, 30))
self.SetSize((300, 100))
self.Centre()
self.file_path = file_path
self.close_flag_path = close_flag_path
self.last_msg = None
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
self.timer.Start(200)
self.Bind(wx.EVT_CLOSE, self.on_close)
self.Show()
def on_timer(self, event):
try:
# 終了フラグファイルがあればウィンドウを閉じる
if os.path.exists(self.close_flag_path):
self.timer.Stop()
self.Destroy()
return
if not os.path.exists(self.file_path):
return
with open(self.file_path, "r", encoding="utf-8") as f:
msg = f.read().strip()
if msg != self.last_msg:
self.label.SetLabel(msg)
self.last_msg = msg
except Exception:
pass
def on_close(self, event):
self.timer.Stop()
self.Destroy()
# サブプロセス本体
def progress_window_proc(file_path, close_flag_path):
app = wx.App(False)
frame = ProgressFrame(file_path, close_flag_path)
app.MainLoop()
# 親プロセス側API
_progress_proc = None
_progress_file = None
_close_flag_file = None
def show_progress_wx(message="処理中...", file_path=None, close_flag_path=None):
global _progress_proc, _progress_file, _close_flag_file
if file_path is None:
# log/progress.txtをデフォルトに
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR, exist_ok=True)
file_path = PROGRESS_FILE
if close_flag_path is None:
close_flag_path = CLOSE_FLAG_FILE
# 終了フラグを消しておく
if os.path.exists(close_flag_path):
try:
os.remove(close_flag_path)
except Exception:
pass
if _progress_proc is not None and _progress_proc.is_alive():
update_progress_wx(message, file_path)
return file_path
with open(file_path, "w", encoding="utf-8") as f:
f.write(message)
_progress_proc = multiprocessing.get_context("spawn").Process(target=progress_window_proc, args=(file_path, close_flag_path))
_progress_proc.start()
_progress_file = file_path
_close_flag_file = close_flag_path
time.sleep(0.2) # ウィンドウ起動待ち
update_progress_wx(message, file_path)
return file_path
def update_progress_wx(message, file_path=None):
global _progress_file
if file_path is None:
file_path = _progress_file
if file_path:
try:
with open(file_path, "w", encoding="utf-8") as f:
f.write(message)
except Exception:
pass
def close_progress_wx(file_path=None, close_flag_path=None):
global _progress_proc, _progress_file, _close_flag_file
if close_flag_path is None:
close_flag_path = _close_flag_file
# 終了フラグファイルを作成
if close_flag_path:
try:
with open(close_flag_path, "w", encoding="utf-8") as f:
f.write("close")
except Exception:
pass
if _progress_proc is not None:
_progress_proc.join(timeout=1)
# ファイルは削除しない
_progress_proc = None
_progress_file = None
_close_flag_file = None
if __name__ == "__main__":
import time
fpath = show_progress_wx("最初のメッセージ")
time.sleep(2)
update_progress_wx("進捗を更新中...", fpath)
time.sleep(2)
close_progress_wx(fpath)
python fastapi ログ出力(汎用メモ)
fastapiで下記を行う
main.py
from contextvars import ContextVar
from asgi_correlation_id import CorrelationIdMiddleware
import logging.config
from fastapi import FastAPI, Request
from pydantic import BaseModel
from schema import Item
from my_logger import setup_logger
# from my_logger_middleware import add_request_id, log_requests
from my_logger_middleware import log_requests
# # Context variable for request ID
# request_id_context: ContextVar[str] = ContextVar("request_id", default="")
# # Helper function to get the current request ID
# def get_request_id() -> str:
# return request_id_context.get()
# Logger setup
setup_logger()
logger = logging.getLogger("appLogger")
# FastAPI application
app = FastAPI()
# app.middleware("http")(add_request_id)
app.add_middleware(CorrelationIdMiddleware, header_name="X-Request-ID")
app.middleware("http")(log_requests)
logger = logging.getLogger("appLogger")
@app.get("/example")
def example_endpoint(name: str):
logger.info("Processing request - message: Starting processing")
# print(f'item: {item}')
# response_data = item
response_data = {"message": "This is response body!", "code": "dummy"}
logger.info("Processing request - message: Finished processing")
return response_data
class RequestBodyModel(BaseModel):
key: str
value: str
@app.post("/log-request-body")
async def log_request_body(request: Request, body: RequestBodyModel):
body_dict = body.dict()
# logger.info(f"Request received - correlation_id: {current_correlation_id}, body: {body_dict}")
logger.info('ログ確認')
response_data = {"message": "Request body logged successfully!", "received": body_dict}
# logger.info(f"Response sent - correlation_id: {current_correlation_id}, response: {response_data}")
return response_data
@app.get("/")
def read_root():
logger.info("GET ルートメソッド")
return {"Hello": "World"}
my_logger_middleware.py
# from contextvars import ContextVar
# import uuid
from fastapi import Request
from fastapi.responses import JSONResponse
import logging
logger = logging.getLogger("appLogger")
# Context variable for request ID
# print('ContextVar')
# print(ContextVar)
# request_id_context: ContextVar[str] = ContextVar("request_id", default="")
# async def add_request_id(request: Request, call_next):
# request_id = str(uuid.uuid4())
# request_id_context.set(request_id)
# response = await call_next(request)
# return response
# リクエスト・レスポンス ロギング
async def log_requests(request: Request, call_next):
# print(f'request: {request}')
# リクエストボディロギング
request_body = await request.body()
request_message = request_body.decode("utf-8").replace("\\n", " ").replace("\\r", " ")
logger.info(f"Request received - body: {request_message}")
# OpenAPI関連の場合、処理終了
if request.url.path.startswith(("/docs", "/redoc", "/openapi.json")):
response = await call_next(request)
return response
# 各固有処理
response = await call_next(request)
# レスポンスロギング
response_body = b""
async for chunk in response.body_iterator:
response_body += chunk
response_message = response_body.decode("utf-8").replace("\\n", " ").replace("\\r", " ")
logger.info(f"Response sent - body: {response_message}")
return JSONResponse(content=response_body.decode("utf-8"))
my_logger.py
import os
import logging.config
import yaml
from datetime import datetime
# from logging import Filter
# from my_logger_middleware import request_id_context
# # add_request_id
# class AddRequestIdFilter(Filter):
# def filter(self, record):
# try:
# # リクエストIDを取得しレコードに設定
# record.request_id = request_id_context.get()
# except LookupError:
# # フォールバック値を設定
# record.request_id = "N/A"
# return True
def setup_logger():
LOG_DIR = "..\logs"
CONFIG_DIR = "..\config"
os.makedirs(LOG_DIR, exist_ok=True)
log_config_path = os.path.join(CONFIG_DIR, "logconf.yaml")
with open(log_config_path, "r") as f:
log_config = yaml.safe_load(f)
# ログファイル名に日付設定
log_filename = os.path.join(LOG_DIR, f"applog_{datetime.now().strftime('%Y%m%d')}.log")
log_config['handlers']['file']['filename'] = log_filename
logging.config.dictConfig(log_config)
logconf.yaml
version: 1
formatters:
detailed:
# format: "%(asctime)s - %(levelname)s - [request_id=%(request_id)s] [correlation_id=%(correlation_id)s] %(message)s"
format: "%(asctime)s - %(levelname)s - [request_id=%(correlation_id)s] %(message)s"
filters:
correlation_id:
(): asgi_correlation_id.CorrelationIdFilter
# add_request_id:
# (): my_logger.AddRequestIdFilter
handlers:
file:
class: logging.FileHandler
level: DEBUG
formatter: detailed
filters: [correlation_id]
# filters: [add_request_id]
filename: applog_default.log
encoding: utf-8
loggers:
appLogger:
level: DEBUG
handlers: [file]
propagate: no
root:
level: DEBUG
handlers: [file]
環境作成
uv init fastapi-app cd .\fastapi-app\ uv add fastapi --extra standard uv add asgi-correlation-id
【GAS】Googleドキュメントのファイル一覧をスプレッドシートに書き出す
参考
qiita.com
/** @OnlyCurrentDoc */ function outputFileListLink() { //フォルダIDを指定する var folder_id = "1K-Zig9uIKt9iH2XWQYe3nrngyzkyB3eA"; //URLの「~~folders/{この部分}」 //フォルダ情報を取得する var folder = DriveApp.getFolderById(folder_id); //フォルダ情報からファイル一覧を取得する var files = folder.getFiles(); var list = []; //この変数のファイル名・URLが入っていきます //フォルダ内のファイル一覧を1件ずつ読み込む while(files.hasNext()) { //ファイル情報を1件読む var buff = files.next(); //ファイル名取得 var xlsName = buff.getName(); //ファイル名の1文字目から"_"が最初に出てきた位置までの文字列を取得 var nameIndex = xlsName.indexOf("_"); //何文字目に"_"が入っているか取得 xlsName = xlsName.substring(0, nameIndex); //1文字目から指定文字目までの文字列を取得 //ファイルのURLを取得 var xlsLink = buff.getUrl(); //URLからダウンロード用のリンク文字列を作成 xlsLink = xlsLink.replace("file/d/", "uc?export=download&id="); //文字を置換 xlsLink = xlsLink.replace("/view?usp=drivesdk","") //文字を置換 xlsLink = "=HYPERLINK(\"" + xlsLink + "\")" //ハイパーリンクの関数を設定 //ファイル名、URLを格納 list.push([xlsName, xlsLink]); }; // ファイルが0件なら処理終了 if(list.length == 0) { return; } // 出力先シートを取得する var ss = SpreadsheetApp.getActive(); //アクティブになっているスプレッドシートを取得 var sheet = ss.getSheetByName("シート2"); //シート2を取得 // シートの内容をクリア sheet.clearContents(); // 出力範囲を取得 var rowIndex = 1; // The starting row of a range. var colIndex = 1; // The starting row of a column. range = sheet.getRange( rowIndex, // 行始点位置 colIndex, // 列始点位置 list.length, // 行終点位置(ファイルの数を指定) list[0].length // 列終点位置(2(ファイルとURL)を指定) ); // 対象の範囲にまとめて書き出します range.setValues(list); }
【DBDictionary】カスタムテキストボックス
カスタムテキストボックス コード
続きを読む【DBDictionary】文字フォーマット変換
文字列操作ユーティリティ コード
続きを読む