🎃

memo

2025/02/20に公開
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 5 モーダル</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<!-- モーダルを開くボタン -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
  モーダルを開く
</button>

<!-- モーダルのHTML -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">モーダルタイトル</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        ここに内容を書きます。
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">閉じる</button>
        <button type="button" class="btn btn-primary">保存する</button>
      </div>
    </div>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

function hasModeAbc(url) {
  try {
    const parsedUrl = new URL(url);
    // searchParamsからmodeの値を取得して判定
    return parsedUrl.searchParams.get('mode') === 'abc';
  } catch (error) {
    // URLのパースに失敗した場合はfalseを返す
    return false;
  }
}

// 使用例
console.log(hasModeAbc("https://example.com/?mode=abc"));                   // true
console.log(hasModeAbc("https://example.com/?foo=bar&mode=abc"));             // true
console.log(hasModeAbc("https://example.com/?mode=abc&foo=bar"));             // true
console.log(hasModeAbc("https://example.com/?mode=xyz&foo=bar"));             // false
console.log(hasModeAbc("https://example.com/?foo=bar&mode=abc&baz=qux"));      // true

その2

from openpyxl import load_workbook
from openpyxl.worksheet.datavalidation import DataValidation

def copy_data_validation(ws, fromCell, toCell):
    """
    指定したセルのデータバリデーション(入力規則)をコピーする関数

    :param ws: 対象のワークシート
    :param fromCell: コピー元のセル(例: "A1")
    :param toCell: コピー先のセル(例: "B1")
    """
    # コピー元セルのデータバリデーションを取得
    for dv in ws.data_validations.dataValidation:
        if fromCell in dv.cells:
            original_dv = dv
            break
    else:
        raise ValueError(f"{fromCell} にデータバリデーションが見つかりません")

    # プルダウンリスト(リスト制約)の場合
    if original_dv.type == "list":
        new_dv = DataValidation(
            type="list",
            formula1=original_dv.formula1,  # リストの定義をコピー
            allowBlank=original_dv.allowBlank,
            showDropDown=original_dv.showDropDown,
            showErrorMessage=original_dv.showErrorMessage,
            showInputMessage=original_dv.showInputMessage,
            prompt=original_dv.prompt,
            promptTitle=original_dv.promptTitle,
            error=original_dv.error,
            errorTitle=original_dv.errorTitle,
            errorStyle=original_dv.errorStyle
        )

        # コピー先セルに適用
        new_dv.add(toCell)

        # ワークシートにデータバリデーションを追加
        ws.add_data_validation(new_dv)
    else:
        raise ValueError(f"{fromCell} のデータバリデーションはリスト型(プルダウン)ではありません")

# 使用例
wb = load_workbook("sample.xlsx")
ws = wb.active

# A1 から B1 に入力規則(プルダウン)をコピー
copy_data_validation(ws, "A1", "B1")

# 保存
wb.save("sample_copy.xlsx")
from openpyxl import load_workbook
from openpyxl.worksheet.datavalidation import DataValidation

# グローバル変数としてデータバリデーションを保持
original_dv = None  

def copy_data_validation(ws, fromCell, toCell):
    """
    指定したセルのデータバリデーション(入力規則)をコピーする関数
    1回目は `original_dv` を取得して保持し、2回目以降は再利用する。

    :param ws: 対象のワークシート
    :param fromCell: コピー元のセル(例: "A1")
    :param toCell: コピー先のセル(例: "B1")
    """
    global original_dv  # グローバル変数を使用

    # 1回目の実行時はコピー元のデータバリデーションを取得
    if original_dv is None:
        for dv in ws.data_validations.dataValidation:
            if fromCell in dv.cells:
                original_dv = dv
                break
        else:
            raise ValueError(f"{fromCell} にデータバリデーションが見つかりません")

    # 直接コピー先セルに追加
    original_dv.add(toCell)

    # 再度ワークシートにデータバリデーションを適用(必須)
    ws.add_data_validation(original_dv)

# 使用例
wb = load_workbook("sample.xlsx")
ws = wb.active

# 1回目のコピー(グローバル変数に保持)
copy_data_validation(ws, "A1", "B1")

# 2回目以降のコピー(`original_dv` を再利用)
copy_data_validation(ws, "A1", "C1")
copy_data_validation(ws, "A1", "D1")

# 保存
wb.save("sample_copy.xlsx")
from openpyxl import load_workbook
from openpyxl.worksheet.datavalidation import DataValidation

# グローバル変数としてデータバリデーションを保持
original_dv = None  

def copy_data_validation(ws, fromCell, toCell):
    """
    指定したセルのデータバリデーション(入力規則)をコピーする関数
    `fromCell` と `toCell` は `ws.cell(row=y, column=x)` で取得されたセルオブジェクト

    :param ws: 対象のワークシート
    :param fromCell: コピー元のセルオブジェクト(例: ws.cell(row=1, column=1))
    :param toCell: コピー先のセルオブジェクト(例: ws.cell(row=1, column=2))
    """
    global original_dv  # グローバル変数を使用

    # 1回目の実行時はコピー元のデータバリデーションを取得
    if original_dv is None:
        cell_ref = fromCell.coordinate  # A1 形式のセル参照
        for dv in ws.data_validations.dataValidation:
            if cell_ref in dv.cells:
                original_dv = dv
                break
        else:
            raise ValueError(f"{cell_ref} にデータバリデーションが見つかりません")

    # 直接コピー先セルに追加
    original_dv.add(toCell.coordinate)

    # 再度ワークシートにデータバリデーションを適用(必須)
    ws.add_data_validation(original_dv)

# 使用例
wb = load_workbook("sample.xlsx")
ws = wb.active

# A1 から B1 に入力規則をコピー
copy_data_validation(ws, ws.cell(row=1, column=1), ws.cell(row=1, column=2))

# C1, D1 へもコピー
copy_data_validation(ws, ws.cell(row=1, column=1), ws.cell(row=1, column=3))
copy_data_validation(ws, ws.cell(row=1, column=1), ws.cell(row=1, column=4))

# 保存
wb.save("sample_copy.xlsx")

ExcelFitToPageSetter

from openpyxl import load_workbook

def set_fit_to_page(ws):
    """
    Excelの「次のページ数に合わせて印刷」を有効にし、
    縦のページ数の制限を解除する(空白にする)。
    
    :param ws: ワークシートオブジェクト
    """
    # 次のページ数に合わせて印刷を有効化
    ws.page_setup.fitToPage = True

    # 横を1ページに収める
    ws.page_setup.fitToWidth = 1  

    # 縦のページ数を「空白」にする(無制限)
    ws.page_setup.fitToHeight = None

# 使用例
wb = load_workbook("sample.xlsx")
ws = wb.active

# 設定を適用
set_fit_to_page(ws)

# 保存
wb.save("sample_modified.xlsx")
from openpyxl import load_workbook

# ワークブックを読み込む
wb = load_workbook("sample.xlsx")

# ワークシートを取得する
ws = wb["Sheet1"]

# ページ設定を行う
ws.page_setup.fitToWidth = 1  # 横方向に1ページに収める
ws.page_setup.fitToHeight = 0  # 縦方向のページ数を無制限にする

# ワークブックを保存する
wb.save("sample_modified.xlsx")
from openpyxl import load_workbook

def copy_sheet(file_path, source_sheet_name, target_sheet_name):
    # Excelブックを読み込み
    wb = load_workbook(file_path)

    # コピー元のシートを取得
    if source_sheet_name not in wb.sheetnames:
        raise ValueError(f"シート '{source_sheet_name}' が存在しません。")
    source_sheet = wb[source_sheet_name]

    # シートをコピー
    copied_sheet = wb.copy_worksheet(source_sheet)

    # コピーしたシートの名称を設定
    copied_sheet.title = target_sheet_name

    # ファイルを上書き保存
    wb.save(file_path)
    wb.close()

# 使用例
copy_sheet("sample.xlsx", "Sheet1", "Sheet1_Copy")

from openpyxl import load_workbook
from openpyxl.worksheet.worksheet import Worksheet

def copy_sheet_with_settings(file_path, source_sheet_name, target_sheet_name):
    # Excelブックを読み込み
    wb = load_workbook(file_path)

    # コピー元のシートを取得
    if source_sheet_name not in wb.sheetnames:
        raise ValueError(f"シート '{source_sheet_name}' が存在しません。")
    source_sheet = wb[source_sheet_name]

    # シートをコピー
    copied_sheet = wb.copy_worksheet(source_sheet)
    copied_sheet.title = target_sheet_name

    # 印刷範囲のコピー
    if source_sheet.print_area:
        copied_sheet.print_area = source_sheet.print_area

    # 印刷オプションのコピー(ヘッダー、フッター、グリッド線、拡大縮小など)
    copied_sheet.print_options.horizontalCentered = source_sheet.print_options.horizontalCentered
    copied_sheet.print_options.verticalCentered = source_sheet.print_options.verticalCentered
    copied_sheet.print_options.gridLines = source_sheet.print_options.gridLines
    copied_sheet.print_options.headings = source_sheet.print_options.headings

    # シートビュー(標準ビューや改ページプレビューの状態)をコピー
    if hasattr(source_sheet, 'views') and source_sheet.views:
        copied_sheet.views = source_sheet.views

    # ファイルを上書き保存
    wb.save(file_path)
    wb.close()

# 使用例
copy_sheet_with_settings("sample.xlsx", "Sheet1", "Sheet1_Copy")

from openpyxl import load_workbook

def select_all_sheets_and_set_first_active(file_path):
    # Excelブックを開く
    wb = load_workbook(file_path)

    # 全シートの選択を解除
    for ws in wb.worksheets:
        ws.sheet_view.tabSelected = False

    # すべてのシートを選択状態にする
    for ws in wb.worksheets:
        ws.sheet_view.tabSelected = True

    # 一番左のシートをアクティブにする
    wb.active = 0  # 最初のシートをアクティブに設定

    # ファイルを保存
    wb.save(file_path)
    wb.close()

# 使用例
select_all_sheets_and_set_first_active("sample.xlsx")


Excelの仕様

Excelでは「選択済みのシート は tabSelected=True のシートが 1つ以上ある状態」
ただし、「アクティブなシートは必ず1つだけ」 でなければならない
もし、すでに tabSelected=True のシートがあって、別のシートを tabSelected=True にすると どのシートがアクティブなのか不明確になる
openpyxlの内部動作

openpyxl は「アクティブなシートが必ず1つである」ことを期待している
tabSelected=True を複数のシートに設定するとき、一度 すべて False にしないと、どのシートがアクティブかの整合性が取れなくなる可能性がある
Excel のUIでは、 手動で「すべてのシートを選択」すると、自動的に1つのシートがアクティブになる 仕様になっているため、これを openpyxl でも再現するには、 一度すべて False にしてから True にする方が安全

            /*
                画面リロードから再表示完了までスピナーを表示するようにしたため
                途中でjavascriptエラーが発生時に、スピナーが表示されっぱなしになることを防ぎたい。
                ( 処理に時間がかかっているのか、エラーがあるのか判断できなくなるから )
                そのため、catchしてないjavascriptの例外を捕獲して
                スピナーを消す。
                その際、エラーの通知、および、詳細なデバッグ情報をconsole.error()をしておく
            */
            window.addEventListener('error', function (event) {
                let errorMessage = getErrorMessageOnGlobalError(event);

                let isAlert = true;
                /*
                    videoタグをカルーセルに入れてから、
                    カルーセルでページ切り替え中に、時々、
                    message : ResizeObserver loop completed with undelivered notifications.
                    のようなエラーメッセージで、ここに飛んでくる。原因不明である。

                    ただ、ここにエラーが飛んでくるだけで画面表示がおかしくなったりもしない
                    特に実害がないため、
                    ブラウザのデバッガーで見れるコンソールには出力しておくが、alert表示をしないことにした。
                */
                let specialError00100 = errorMessage.includes('ResizeObserver loop completed');
                if(specialError00100) {
                    isAlert = false;
                }

                console.error('エラーが発生しました:', errorMessage);

                if(isAlert) {
                    alert('エラーが発生しました');
                }
                
                // スピナーを消す
                hideSpinner();
            });




/**
 *  「window.addEventListener('error', function (event) {」
 *  グローバルにcatchしてないjavascript例外を捕獲した時のeventオブジェクトより
 *  詳細なデバッグ情報のあるerrorMessageを取得する。
 */
function getErrorMessageOnGlobalError(event) {
    let errorMessage = `message : ${event.message}  source : ${event.filename}:${event.lineno}:${event.colno}`;
    if (event.error) {
        if (event.error.stack) {
            errorMessage += `stacktrace : ${event.error.stack}`;
        }
    }
    return errorMessage;
}

Discussion