最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python+PySide6構(gòu)建一個響應(yīng)式視頻剪輯工具

 更新時間:2025年10月27日 09:40:02   作者:mortimer  
從長視頻中批量剪輯出精彩片段,傳統(tǒng)的做法是打開笨重的剪輯軟件手動操作,效率低下,下面我們就來講講如何使用Python構(gòu)建一個響應(yīng)式視頻剪輯工具吧

從長視頻中批量剪輯出精彩片段。傳統(tǒng)的做法是打開笨重的剪輯軟件手動操作,效率低下。作為一個開發(fā)者,我們自然會想:能不能寫個腳本來自動化這個過程?

當(dāng)然可以。但如果想讓這個工具更易用,一個圖形用戶界面是必不可少的。而這恰恰引入了 GUI 編程中最經(jīng)典的挑戰(zhàn):如何在執(zhí)行耗時任務(wù)(如視頻處理)的同時,保持界面的流暢響應(yīng)?

這篇文章將分享我寫的一個app.py單文件小工具,它能根據(jù)字幕文件批量剪輯視頻。并聊聊如何使用 PySide6框架,通過 QThreadPool 和信號/槽機(jī)制,構(gòu)建一個高性能、不卡頓的桌面應(yīng)用。

UI線程的“生命不可承受之重”

任何 GUI 框架,無論是前端的瀏覽器、Android 的 View 系統(tǒng)還是 Qt,都有一個主線程,也叫 UI 線程。它負(fù)責(zé)處理用戶輸入、繪制界面、響應(yīng)事件。如果在這個線程里執(zhí)行一個耗時任務(wù),比如調(diào)用 ffmpeg 命令來剪輯一個 10 秒的視頻,那么在這 10 秒內(nèi),整個應(yīng)用界面將完全凍結(jié)——無法點(diǎn)擊按鈕,無法拖動窗口,在用戶看來就是“程序卡死了”。

這就是我們首先要解決的問題。解決方案很明確:將所有耗時操作都放到后臺工作線程中去執(zhí)行。

架構(gòu)設(shè)計(jì):QThreadPool與QRunnable

在 Qt 中,處理并發(fā)任務(wù)的首選方式之一就是 QThreadPoolQRunnable

  • QThreadPool: 一個托管的線程池,負(fù)責(zé)管理和復(fù)用工作線程,避免了手動創(chuàng)建和銷毀線程的開銷。
  • QRunnable: 一個輕量級的工作任務(wù)抽象類。我們只需要繼承它并實(shí)現(xiàn) run() 方法,就可以將具體的業(yè)務(wù)邏輯(如調(diào)用 ffmpeg)封裝起來。

在我的代碼中,定義了兩個這樣的任務(wù)類:

  • LoadSubtitlesTask: 負(fù)責(zé)讀取和解析字幕文件。雖然這通常很快,但對于非常大的字幕文件,也可能造成瞬間卡頓,因此也將其放入后臺。
  • ClipTask: 核心的剪輯任務(wù),封裝了對 subprocess.run() 的調(diào)用來執(zhí)行 ffmpeg 命令。
class ClipTask(QRunnable):
    def __init__(self, video_path, sub, line_num, ...):
        super().__init__()
        # ... 初始化

    def run(self):
        # ... 構(gòu)建 ffmpeg 命令
        cmd = ["ffmpeg", "-ss", start_time, "-i", video_path, ...]
        try:
            # 這是耗時操作
            subprocess.run(cmd, check=True, capture_output=True)
            # 任務(wù)成功了
        except Exception as e:
            # 任務(wù)失敗了
            pass

現(xiàn)在問題來了:任務(wù)在后臺線程執(zhí)行,但執(zhí)行的結(jié)果(成功、失敗、進(jìn)度)需要更新到主線程的 UI 控件上。直接在工作線程中操作 UI 控件是絕對禁止的,這會導(dǎo)致線程不安全,輕則界面錯亂,重則程序崩潰。

這就是 Qt 的精髓——信號與槽(Signal/Slot)機(jī)制——登場的時候了。

用信號與槽實(shí)現(xiàn)線程安全通信

信號與槽是 Qt 框架的核心特性,它是一種聽起來有些復(fù)雜但實(shí)際還算簡單的觀察者模式實(shí)現(xiàn),完美地解決了跨線程通信的難題。

  • Signal(信號): 當(dāng)某個特定事件發(fā)生時,一個對象可以“發(fā)射(emit)”一個信號。信號可以攜帶參數(shù)。
  • Slot(槽): 一個可以接收并處理信號的函數(shù)或方法。

當(dāng)一個對象的信號連接(connect)到另一個對象的槽時,一旦信號被發(fā)射,與之關(guān)聯(lián)的槽函數(shù)就會被自動調(diào)用。最關(guān)鍵的是,如果信號是從工作線程發(fā)射,而接收槽位于主線程的對象上,Qt 會自動、安全地將這個調(diào)用安排到主線程的事件循環(huán)中執(zhí)行。

在我的代碼中,創(chuàng)建了一個專門的 Signals 類來統(tǒng)一定義所有需要用到的信號:

# 必須繼承自 QObject 才能定義信號
class Signals(QObject):

    # 進(jìn)度更新信號,攜帶一個字符串參數(shù)
    progress = Signal(str)
    
    # 所有任務(wù)完成的信號,不帶參數(shù)
    finished = Signal()
    
    # 字幕加載完成的信號,攜帶解析后的數(shù)據(jù)和文件名
    subtitles_loaded = Signal(list, str)
    
    # 加載出錯的信號
    load_error = Signal(str)

工作流程:

初始化與連接:在主窗口 MainWindow__init__ 方法中,實(shí)例化 Signals 類,并將信號連接到對應(yīng)的槽函數(shù)。

class MainWindow(QWidget):
    def __init__(self):
        # ...
        self.signals = Signals()
        # 將 progress 信號連接到 update_progress 槽函數(shù)
        self.signals.progress.connect(self.update_progress)
        self.signals.finished.connect(self.clipping_finished)
        # ...

任務(wù)分發(fā)與信號發(fā)射:當(dāng)用戶點(diǎn)擊“開始剪輯”按鈕時,start_clipping 方法會為每個選中的字幕行創(chuàng)建一個 ClipTask 實(shí)例,并將我們的 signals 對象傳遞給它。

# 在 start_clipping 中
for line_num in self.selected_lines:
    sub = self.subtitles[line_num - 1]
    # 將 signals 對象傳入任務(wù)
    task = ClipTask(..., self.signals)
    # 提交到線程池執(zhí)行
    self.thread_pool.start(task)

后臺發(fā)射信號ClipTask 在其 run 方法中執(zhí)行完 ffmpeg 命令后,根據(jù)結(jié)果發(fā)射不同的信號。

# 在 ClipTask.run 中
class ClipTask(QRunnable):
    def run(self):
        try:
            subprocess.run(...)
            # 任務(wù)成功,從工作線程發(fā)射 progress 信號
            self.signals.progress.emit(f"Completed clip {self.line_num}")
        except subprocess.CalledProcessError as e:
            # 任務(wù)失敗,同樣發(fā)射 progress 信號,但內(nèi)容是錯誤信息
            self.signals.progress.emit(f"Failed clip {self.line_num}: {e}")

主線程安全接收并更新 UI:由于我們在第一步中建立了連接,主線程的 update_progress 方法會被自動調(diào)用。我們可以在這個方法里安全地更新 QLabel 等 UI 控件。

# 在 MainWindow 中定義的槽函數(shù)
def update_progress(self, message: str):
    if "Completed" in message:
        self.completed_clips += 1
    elif "Failed" in message:
        self.failed_clips.append(message)

    self.progress_label.setText(
        f"共: {self.total_clips}, 完成: {self.completed_clips}, 失敗: {len(self.failed_clips)}"
    )

    # 檢查是否所有任務(wù)都結(jié)束了
    if self.completed_clips + len(self.failed_clips) >= self.total_clips:
        self.signals.finished.emit()

通過這個機(jī)制,我們成功地將耗時的業(yè)務(wù)邏輯與 UI 更新解耦,實(shí)現(xiàn)了應(yīng)用的流暢響應(yīng)。即使用戶一次性剪輯上百個片段,界面也依然能自如操作。

用uv實(shí)現(xiàn)零依賴運(yùn)行

對于 Python 開發(fā)者來說,環(huán)境和依賴管理常常是個頭疼的問題。為了讓這個工具對技術(shù)小白也足夠友好,我采用了最近大火的 uv 工具。

通過在腳本文件頭部添加特定的注釋,uv 能夠讀取這些元數(shù)據(jù),自動創(chuàng)建一個虛擬環(huán)境并安裝所需的依賴,然后執(zhí)行腳本。

# /// script
# requires-python = ">=3.10,<=3.12"
# dependencies = [
#   "pyside6",
#   "pysubs2"
# ]
# ///

這意味著用戶只需要下載 uv.exeapp.py,無需手動 pip install 任何東西,只需在命令行運(yùn)行 uv run app.py 即可啟動程序。這極大地降低了使用門檻,也為代碼分發(fā)提供了一種輕量級的解決方案。

完整代碼

下面是完整的項(xiàng)目代碼,涵蓋了從 UI 布局、事件處理到并發(fā)編程和線程安全通信的各個方面。

# /// script
# requires-python = ">=3.10,<=3.12"
# dependencies = [
#   "pyside6",
#   "pysubs2"
# ]
# 
# [[tool.uv.index]]
# url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
# 	
# ///

import sys
import os,shutil,platform
from pathlib import Path
import subprocess
from PySide6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
    QLabel, QFileDialog, QListWidget, QListWidgetItem, QCheckBox,
    QComboBox
)
from PySide6.QtCore import Qt, QThreadPool, QRunnable, Signal, QObject, QUrl, Slot,QSize
from PySide6.QtGui import QDesktopServices
from datetime import timedelta
import pysubs2

# 全局輸出文件夾
ROOT_DIR=Path(os.getcwd()).as_posix()
output_folder = f"{ROOT_DIR}/output"


class Signals(QObject):
    progress = Signal(str)
    finished = Signal()
    subtitles_loaded = Signal(list, str)  # list of (i, text), subtitle_name
    load_error = Signal(str)

class ClipTask(QRunnable):
    def __init__(self, video_path, sub, line_num, subtitle_name, signals, mode):
        super().__init__()
        self.video_path = video_path
        self.sub = sub
        self.line_num = line_num
        self.subtitle_name = subtitle_name
        self.signals = signals
        self.mode = mode

    def run(self):
        try:
            start_time = self.sub.start / 1000.0 
            duration = (self.sub.end - self.sub.start) / 1000.0
            output_dir = f'{output_folder}/{self.subtitle_name}'
            os.makedirs(output_dir, exist_ok=True)

            if self.mode == 0:  # 默認(rèn)
                output_path = os.path.join(output_dir, f"{self.line_num}.mp4")
                cmd = [
                    "ffmpeg","-y", "-ss", str(start_time), "-t", str(duration),
                    "-i", self.video_path, "-c:v", "copy", "-c:a", "copy",
                    output_path
                ]
                subprocess.run(cmd, check=True, capture_output=True)
            elif self.mode == 1:  # 僅視頻
                output_path = os.path.join(output_dir, f"{self.line_num}.mp4")
                cmd = [
                    "ffmpeg","-y", "-ss", str(start_time), "-t", str(duration),
                    "-i", self.video_path, "-c:v", "copy", "-an",
                    output_path
                ]
                subprocess.run(cmd, check=True, capture_output=True)
            elif self.mode == 2:  # 僅音頻
                output_path = os.path.join(output_dir, f"{self.line_num}.wav")
                cmd = [
                    "ffmpeg", "-y","-ss", str(start_time), "-t", str(duration),
                    "-i", self.video_path, "-vn", "-c:a", "pcm_s16le",
                    output_path
                ]
                subprocess.run(cmd, check=True, capture_output=True)
            elif self.mode == 3:  # 分離
                # 無聲視頻
                video_path_out = os.path.join(output_dir, f"{self.line_num}.mp4")
                cmd_video = [
                    "ffmpeg","-y", "-ss", str(start_time), "-t", str(duration),
                    "-i", self.video_path, "-c:v", "copy", "-an",
                    video_path_out
                ]
                subprocess.run(cmd_video, check=True, capture_output=True)

                # 音頻
                audio_path_out = os.path.join(output_dir, f"{self.line_num}.wav")
                cmd_audio = [
                    "ffmpeg","-y", "-ss", str(start_time), "-t", str(duration),
                    "-i", self.video_path, "-vn", "-c:a", "pcm_s16le",
                    audio_path_out
                ]
                subprocess.run(cmd_audio, check=True, capture_output=True)
            # 注意:Completed 和 Failed 不可修改,UI線程據(jù)此判斷成功與失敗,丑陋但簡單
            self.signals.progress.emit(f"Completed clip {self.line_num}")
        except subprocess.CalledProcessError as e:
            error_msg = e.stderr.decode() if e.stderr else str(e)
            self.signals.progress.emit(f"Failed clip {self.line_num}: {error_msg}")
        except Exception as e:
            self.signals.progress.emit(f"Failed clip {self.line_num}: {str(e)}")

class LoadSubtitlesTask(QRunnable):
    def __init__(self, subtitle_path, signals):
        super().__init__()
        self.subtitle_path = subtitle_path
        self.signals = signals

    
    def _format_time(self,ms):
        hours = ms // (1000 * 3600)
        minutes = (ms // (1000 * 60)) % 60
        seconds = (ms // 1000) % 60
        milliseconds = ms % 1000
        return f"{hours:02d}:{minutes:02d}:{seconds:02d},{milliseconds:03d}"

    def run(self):
        try:
            subtitles = pysubs2.load(self.subtitle_path)
            subtitle_name = os.path.splitext(os.path.basename(self.subtitle_path))[0]
            data = []
            for i, sub in enumerate(subtitles, 1):
                start = self._format_time(sub.start)
                end = self._format_time(sub.end)
                duration = (sub.end - sub.start) / 1000.0
                text = f"{start}->{end}({duration:.2f}s) {sub.text}"
                data.append((i, text))
            self.signals.subtitles_loaded.emit(data, subtitle_name)
        except Exception as e:
            self.signals.load_error.emit(str(e))

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("按字幕剪輯視頻")
        self.resize(1000, 600)
        self.setWindowFlags(self.windowFlags() | Qt.WindowMaximizeButtonHint | Qt.WindowMinimizeButtonHint)

        self.video_path = None
        self.subtitle_path = None
        self.subtitles = None
        self.subtitle_name = None
        self.selected_lines = []
        self.thread_pool = QThreadPool()
        self.is_clipping = False
        self.signals = Signals()
        self.signals.progress.connect(self.update_progress)
        self.signals.finished.connect(self.clipping_finished)
        self.signals.subtitles_loaded.connect(self.on_subtitles_loaded)
        self.signals.load_error.connect(self.on_load_error)
        self.total_clips = 0
        self.completed_clips = 0
        self.failed_clips = []
        self.open_button = None
        self.active_tasks = 0

        self.setup_ui()

    def setup_ui(self):
        layout = QVBoxLayout()

        # 文件選擇
        file_layout = QHBoxLayout()
        self.video_label = QLabel("未選視頻")
        video_btn = QPushButton("選擇視頻")
        video_btn.clicked.connect(self.select_video)
        video_btn.setMinimumSize(QSize(200, 35))
        video_btn.setCursor(Qt.PointingHandCursor)
        file_layout.addWidget(video_btn)
        file_layout.addWidget(self.video_label)

        self.subtitle_label = QLabel("未選字幕")
        subtitle_btn = QPushButton("選擇字幕")
        subtitle_btn.setMinimumSize(QSize(200, 35))
        subtitle_btn.clicked.connect(self.select_subtitle)
        subtitle_btn.setCursor(Qt.PointingHandCursor)
        file_layout.addWidget(subtitle_btn)
        file_layout.addWidget(self.subtitle_label)

        # 輸出模式下拉列表
        self.output_mode = QComboBox()
        self.output_mode.addItems([
            "默認(rèn)(mp4片段有聲)",
            "僅保留視頻(mp4片段無聲)",
            "僅保留音頻(wav音頻片段)",
            "聲畫分離(mp4片段無聲和wav音頻片段)"
        ])
        file_layout.addWidget(self.output_mode)
        file_layout.addStretch() 
        layout.addLayout(file_layout)



        # 批量選擇按鈕
        batch_layout = QHBoxLayout()
        select_all_btn = QPushButton("全選")
        select_all_btn.clicked.connect(self.select_all)
        select_all_btn.setCursor(Qt.PointingHandCursor)
        batch_layout.addWidget(select_all_btn)

        deselect_all_btn = QPushButton("全不選")
        deselect_all_btn.clicked.connect(self.deselect_all)
        deselect_all_btn.setCursor(Qt.PointingHandCursor)
        batch_layout.addWidget(deselect_all_btn)

        invert_btn = QPushButton("反選")
        invert_btn.clicked.connect(self.invert_selection)
        invert_btn.setCursor(Qt.PointingHandCursor)
        batch_layout.addWidget(invert_btn)
        batch_layout.addStretch()
        layout.addLayout(batch_layout)

        # 字幕列表
        self.subtitle_list = QListWidget()
        layout.addWidget(self.subtitle_list)

        # 底部按鈕
        btn_layout = QHBoxLayout()

        self.clip_btn = QPushButton("開始剪輯")
        self.clip_btn.setCursor(Qt.PointingHandCursor)
        self.clip_btn.setMinimumSize(QSize(200, 35))
        self.clip_btn.clicked.connect(self.start_clipping)
        btn_layout.addWidget(self.clip_btn)

        self.clear_btn = QPushButton("清除已選")
        self.clear_btn.setCursor(Qt.PointingHandCursor)
        self.clear_btn.setMaximumWidth(150)
        self.clear_btn.clicked.connect(self.clear_all)
        btn_layout.addWidget(self.clear_btn)
        
        self.open_button = QPushButton("打開輸出目錄")
        self.open_button.setMaximumWidth(200)
        self.open_button.setCursor(Qt.PointingHandCursor)
        self.open_button.clicked.connect(self.open_output_folder)
        self.open_button.hide()
        btn_layout.addWidget(self.open_button)
        

        layout.addLayout(btn_layout)


        self.progress_label = QLabel("")
        self.progress_label.setStyleSheet('color:#2196f3;font-size:14px')
        layout.addWidget(self.progress_label)

        self.setLayout(layout)

    def select_video(self):
        path, _ = QFileDialog.getOpenFileName(self, "選擇視頻", "", "Video Files (*.mp4 *.avi *.mkv)")
        if path:
            self.video_path = path
            self.video_label.setText(os.path.basename(path))

    def select_subtitle(self):
        global output_folder
        path, _ = QFileDialog.getOpenFileName(self, "選擇對應(yīng)字幕", "", "Subtitle Files (*.srt *.ass *.vtt)")
        if path:
            output_folder=Path(path).parent.as_posix()
            self.subtitle_path = path
            self.subtitle_label.setText(os.path.basename(path))
            self.subtitle_list.clear()
            self.progress_label.setText("正在渲染字幕...")
            task = LoadSubtitlesTask(self.subtitle_path, self.signals)
            self.thread_pool.start(task)

    @Slot(list, str)
    def on_subtitles_loaded(self, data, subtitle_name):
        self.subtitles = pysubs2.load(self.subtitle_path)  # Reload if needed
        self.subtitle_name = subtitle_name
        self.subtitle_list.clear()
        for i, text in data:
            item = QListWidgetItem()
            check = QCheckBox(f"{i}行: {text}")
            self.subtitle_list.addItem(item)
            self.subtitle_list.setItemWidget(item, check)
        self.progress_label.setText(f"字幕渲染完成.剪輯后輸出到:{output_folder}/{subtitle_name}")

    @Slot(str)
    def on_load_error(self, error):
        self.progress_label.setText(f"字幕渲染出錯: {error}")

    def select_all(self):
        for i in range(self.subtitle_list.count()):
            item = self.subtitle_list.item(i)
            check = self.subtitle_list.itemWidget(item)
            check.setChecked(True)

    def deselect_all(self):
        for i in range(self.subtitle_list.count()):
            item = self.subtitle_list.item(i)
            check = self.subtitle_list.itemWidget(item)
            check.setChecked(False)

    def invert_selection(self):
        for i in range(self.subtitle_list.count()):
            item = self.subtitle_list.item(i)
            check = self.subtitle_list.itemWidget(item)
            check.setChecked(not check.isChecked())

    def clear_all(self):
        self.video_path = None
        self.subtitle_path = None
        self.subtitles = None
        self.subtitle_name = None
        self.selected_lines = []
        self.video_label.setText("未選視頻")
        self.subtitle_label.setText("未選字幕")
        self.subtitle_list.clear()
        self.progress_label.setText("")
        self.clip_btn.setText("開始剪輯")
        self.is_clipping = False
        self.total_clips = 0
        self.completed_clips = 0
        self.failed_clips = []
        self.output_mode.setCurrentIndex(0)
        self.active_tasks = 0
        self.open_button.hide()

    def start_clipping(self):
        if self.is_clipping:
            self.stop_clipping()
            return

        if not self.video_path or not self.subtitle_name:
            self.progress_label.setText("請選擇待剪輯視頻及對應(yīng)字幕文件.")
            return

        self.selected_lines = []
        for i in range(self.subtitle_list.count()):
            item = self.subtitle_list.item(i)
            check = self.subtitle_list.itemWidget(item)
            if check.isChecked():
                self.selected_lines.append(i + 1)  # 1-based

        if not self.selected_lines:
            self.progress_label.setText("至少請選中一行字幕.")
            return

        mode = self.output_mode.currentIndex()

        self.is_clipping = True
        self.clip_btn.setText("立即停止")
        self.total_clips = len(self.selected_lines)
        self.completed_clips = 0
        self.failed_clips = []
        self.open_button.show()
        self.active_tasks = self.total_clips
        self.progress_label.setText(f"共: {self.total_clips}, 完成: 0, 失敗: 0")

        for line_num in self.selected_lines:
            sub = self.subtitles[line_num - 1]
            task = ClipTask(self.video_path, sub, line_num, self.subtitle_name, self.signals, mode)
            self.thread_pool.start(task)


    def stop_clipping(self):
        self.thread_pool.clear()
        self.is_clipping = False
        self.clip_btn.setText("開始剪輯")
        self.progress_label.setText("立即停止.")
        self.active_tasks = 0

    def update_progress(self, message):
        if "Completed" in message:
            self.completed_clips += 1
        elif "Failed" in message:
            self.failed_clips.append(message)
        self.active_tasks -= 1
        self.progress_label.setText(
            f"共: {self.total_clips}, 完成: {self.completed_clips}, "
            f"失敗: {len(self.failed_clips)}\n" + "\n".join(self.failed_clips)
        )
        if self.active_tasks <= 0 and self.is_clipping:
            self.signals.finished.emit()

    def clipping_finished(self):
        self.is_clipping = False
        self.clip_btn.setText("開始剪輯")
        self.active_tasks = 0


    def open_output_folder(self):
        output_dir = f'{output_folder}/{self.subtitle_name}'
        QDesktopServices.openUrl(QUrl.fromLocalFile(output_dir))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

希望這個實(shí)例能為你下次構(gòu)建桌面應(yīng)用時,在處理并發(fā)和線程通信方面提供一些有用的參考。

到此這篇關(guān)于Python+PySide6構(gòu)建一個響應(yīng)式視頻剪輯工具的文章就介紹到這了,更多相關(guān)Python視頻剪輯內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python:slice與indices的用法

    Python:slice與indices的用法

    今天小編就為大家分享一篇Python:slice與indices的用法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • python實(shí)現(xiàn)郵件發(fā)送功能

    python實(shí)現(xiàn)郵件發(fā)送功能

    這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)郵件發(fā)送功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Python找出9個連續(xù)的空閑端口

    Python找出9個連續(xù)的空閑端口

    這篇文章主要介紹了Python找出9個連續(xù)的空閑端口的方法,感興趣的小伙伴們可以參考一下
    2016-02-02
  • Python實(shí)現(xiàn)實(shí)現(xiàn)gltf預(yù)覽圖的示例詳解

    Python實(shí)現(xiàn)實(shí)現(xiàn)gltf預(yù)覽圖的示例詳解

    隨著3D技術(shù)的不斷發(fā)展,GLTF逐漸成為了Web和移動應(yīng)用程序中最流行的3D文件格式之一,本文將介紹如何使用Python生成GLTF模型的預(yù)覽圖,需要的可以了解下
    2025-02-02
  • python config文件的讀寫操作示例

    python config文件的讀寫操作示例

    這篇文章主要介紹了python config文件的讀寫操作,結(jié)合簡單示例形式分析了Python針對config文件的設(shè)置、讀取、寫入相關(guān)操作技巧,需要的朋友可以參考下
    2019-09-09
  • python性能測試手機(jī)號驗(yàn)證碼登錄壓測示例詳解

    python性能測試手機(jī)號驗(yàn)證碼登錄壓測示例詳解

    這篇文章主要為大家介紹了python性能測試手機(jī)號驗(yàn)證碼登錄壓測的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • python GUI庫圖形界面開發(fā)之PyQt5瀏覽器控件QWebEngineView詳細(xì)使用方法

    python GUI庫圖形界面開發(fā)之PyQt5瀏覽器控件QWebEngineView詳細(xì)使用方法

    這篇文章主要介紹了python GUI庫圖形界面開發(fā)之PyQt5瀏覽器控件QWebEngineView詳細(xì)使用方法,需要的朋友可以參考下
    2020-02-02
  • python爬蟲 urllib模塊反爬蟲機(jī)制UA詳解

    python爬蟲 urllib模塊反爬蟲機(jī)制UA詳解

    這篇文章主要介紹了python爬蟲 urllib模塊反爬蟲機(jī)制UA詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • 用Python寫的圖片蜘蛛人代碼

    用Python寫的圖片蜘蛛人代碼

    寫了個圖片蜘蛛人玩玩,抓了幾個網(wǎng)頁試試,感覺不不錯。核心的代碼可能20行也不到,簡潔明了,嘻嘻。廢話少說,翠花,上代碼
    2012-08-08
  • np.meshgrid中的indexing參數(shù)問題解決

    np.meshgrid中的indexing參數(shù)問題解決

    本文主要介紹了np.meshgrid中的indexing參數(shù)問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03

最新評論

乳山市| 兰溪市| 海城市| 吴桥县| 乳源| 明溪县| 双柏县| 天台县| 临泽县| 凤翔县| 布拖县| 巴马| 云龙县| 澄江县| 宽甸| 辛集市| 柏乡县| 博白县| 增城市| 荃湾区| 滦平县| 博爱县| 北辰区| 杨浦区| 日照市| 监利县| 井陉县| 娄烦县| 临城县| 中宁县| 乌拉特前旗| 深圳市| 资溪县| 济南市| 道孚县| 辉县市| 闽侯县| 商洛市| 达尔| 越西县| 靖西县|