Загрузка данных
# -*- coding: utf-8 -*-
import sys
import subprocess
import threading
import os
import glob
import shutil
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QLineEdit, QPushButton, QLabel, QFileDialog, QMessageBox,
QProgressBar, QRadioButton, QButtonGroup, QGroupBox,
QComboBox, QCheckBox)
from PyQt5.QtCore import Qt, pyqtSignal, QObject
class ScanWorker(QObject):
finished = pyqtSignal(bool, str)
status_update = pyqtSignal(str, str)
def __init__(self, device, save_dir, resolution, filename, save_as_pdf, parent=None):
super().__init__(parent)
self.device = device
self.save_dir = save_dir
self.resolution = resolution
self.filename = filename
self.save_as_pdf = save_as_pdf
def run(self):
batch_pattern = os.path.join(self.save_dir, "scan_%04d.tiff")
command = ('scanimage -d "' + self.device + '" --ScanMode Duplex --batchMode 0 '
'--resolution ' + str(self.resolution) + ' --mode Color --format=tiff '
'--batch="' + batch_pattern + '"')
try:
subprocess.run(command, shell=True, check=True,
stderr=subprocess.PIPE, text=True, timeout=600)
tiff_files = sorted(glob.glob(os.path.join(self.save_dir, "scan_*.tiff")))
if tiff_files:
if self.save_as_pdf:
self.status_update.emit("Создаю PDF...", "blue")
pdf_path = os.path.join(self.save_dir, self.filename + ".pdf")
try:
from PIL import Image
images = []
for f in tiff_files:
img = Image.open(f)
if img.mode != 'RGB':
img = img.convert('RGB')
images.append(img)
if images:
images[0].save(pdf_path, save_all=True, append_images=images[1:])
for f in tiff_files:
try:
os.remove(f)
except Exception:
pass
self.finished.emit(True, "Файл сохранён:\n" + pdf_path)
else:
self.finished.emit(False, "Нет изображений для PDF")
except ImportError:
self.finished.emit(False, "Не установлен PIL. Установите: sudo apt install python3-pil")
except Exception as e:
self.finished.emit(False, "Ошибка создания PDF:\n" + str(e))
else:
self.finished.emit(True, "Сохранено TIFF файлов: " + str(len(tiff_files)) + "\nПапка: " + self.save_dir)
else:
self.finished.emit(False, "Не создано ни одного файла")
except subprocess.TimeoutExpired:
self.finished.emit(False, "Превышено время ожидания")
except subprocess.CalledProcessError as e:
err = e.stderr if e.stderr else "Проверьте подключение сканера"
self.finished.emit(False, "Ошибка сканирования:\n" + err)
except Exception as e:
self.finished.emit(False, "Ошибка:\n" + str(e))
class ScannerApp(QWidget):
def __init__(self):
super().__init__()
self.scanning = False
self.save_dir = os.path.expanduser("~/Изображения/Сканы")
self.scanners = []
self.worker = None
self.thread = None
self.initUI()
self.refresh_scanners()
def initUI(self):
self.setWindowTitle('Сканер Canon DR-G1100')
self.resize(520, 520)
vbox = QVBoxLayout()
self.label_scanner = QLabel('Выберите сканер:')
self.combo_scanner = QComboBox()
self.btn_refresh = QPushButton('Обновить список')
self.btn_refresh.clicked.connect(self.refresh_scanners)
hbox_scanner = QHBoxLayout()
hbox_scanner.addWidget(self.combo_scanner)
hbox_scanner.addWidget(self.btn_refresh)
vbox.addWidget(self.label_scanner)
vbox.addLayout(hbox_scanner)
self.label_path = QLabel('Папка для сохранения:')
self.entry_path = QLineEdit(self.save_dir)
self.btn_browse = QPushButton('Выбрать папку...')
self.btn_browse.clicked.connect(self.choose_path)
hbox_path = QHBoxLayout()
hbox_path.addWidget(self.entry_path)
hbox_path.addWidget(self.btn_browse)
vbox.addWidget(self.label_path)
vbox.addLayout(hbox_path)
self.label_filename = QLabel('Имя документа:')
self.entry_filename = QLineEdit('Документ')
vbox.addWidget(self.label_filename)
vbox.addWidget(self.entry_filename)
format_group = QGroupBox('Формат сохранения:')
format_layout = QVBoxLayout()
self.chk_pdf = QCheckBox('Сохранить как PDF')
self.chk_pdf.setChecked(True)
self.chk_tiff = QCheckBox('Сохранить как TIFF-картинки (без создания PDF)')
format_layout.addWidget(self.chk_pdf)
format_layout.addWidget(self.chk_tiff)
format_group.setLayout(format_layout)
vbox.addWidget(format_group)
res_group = QGroupBox('Разрешение (dpi):')
res_layout = QHBoxLayout()
self.res_group_btn = QButtonGroup(self)
self.res_200 = QRadioButton('200 (быстро)')
self.res_300 = QRadioButton('300 (стандарт)')
self.res_400 = QRadioButton('400 (хорошо)')
self.res_600 = QRadioButton('600 (отлично)')
self.res_300.setChecked(True)
self.res_group_btn.addButton(self.res_200, 200)
self.res_group_btn.addButton(self.res_300, 300)
self.res_group_btn.addButton(self.res_400, 400)
self.res_group_btn.addButton(self.res_600, 600)
res_layout.addWidget(self.res_200)
res_layout.addWidget(self.res_300)
res_layout.addWidget(self.res_400)
res_layout.addWidget(self.res_600)
res_group.setLayout(res_layout)
vbox.addWidget(res_group)
self.btn_scan = QPushButton('Начать сканирование')
self.btn_scan.setStyleSheet("font-weight: bold; height: 40px; background-color: #4CAF50; color: white;")
self.btn_scan.clicked.connect(self.start_scan)
vbox.addWidget(self.btn_scan)
self.progress = QProgressBar()
self.progress.setRange(0, 0)
self.progress.setVisible(False)
vbox.addWidget(self.progress)
self.label_status = QLabel('Готов к работе')
self.label_status.setAlignment(Qt.AlignCenter)
self.label_status.setStyleSheet("color: blue; font-size: 12px;")
vbox.addWidget(self.label_status)
self.btn_open = QPushButton('Открыть папку со сканами')
self.btn_open.clicked.connect(self.open_folder)
vbox.addWidget(self.btn_open)
self.setLayout(vbox)
def refresh_scanners(self):
self.combo_scanner.clear()
self.scanners = []
try:
result = subprocess.run(["scanimage", "-L"],
capture_output=True, text=True, timeout=30)
for line in result.stdout.splitlines():
if "`" in line and "'" in line:
start = line.index("`") + 1
end = line.index("'", start)
device_name = line[start:end]
description = ""
if "is a" in line:
description = line[line.index("is a") + 5:].strip()
display_text = device_name
if description:
display_text = device_name + " (" + description + ")"
self.combo_scanner.addItem(display_text, device_name)
self.scanners.append(device_name)
if len(self.scanners) == 0:
self.combo_scanner.addItem("Сканеры не найдены")
except Exception as e:
self.combo_scanner.addItem("Ошибка: " + str(e))
def choose_path(self):
folder = QFileDialog.getExistingDirectory(
self, "Выберите папку для сохранения", self.save_dir)
if folder:
self.save_dir = folder
self.entry_path.setText(folder)
def start_scan(self):
if self.scanning:
return
if len(self.scanners) == 0:
QMessageBox.warning(self, 'Внимание', 'Сканер не найден. Нажмите "Обновить список".')
return
device = self.combo_scanner.currentData()
if not device:
QMessageBox.warning(self, 'Внимание', 'Выберите сканер из списка.')
return
os.makedirs(self.save_dir, exist_ok=True)
self.scanning = True
self.btn_scan.setEnabled(False)
self.btn_scan.setText('Сканирование...')
self.btn_browse.setEnabled(False)
self.btn_refresh.setEnabled(False)
self.combo_scanner.setEnabled(False)
self.progress.setVisible(True)
self.label_status.setText('Идёт сканирование... Не закрывайте окно!')
self.label_status.setStyleSheet("color: red; font-size: 12px;")
resolution = self.res_group_btn.checkedId()
filename = self.entry_filename.text().strip() or "Документ"
save_as_pdf = self.chk_pdf.isChecked()
self.thread = threading.Thread(target=self._run_worker, args=(device, resolution, filename, save_as_pdf))
self.thread.daemon = True
self.thread.start()
def _run_worker(self, device, resolution, filename, save_as_pdf):
self.worker = ScanWorker(device, self.save_dir, resolution, filename, save_as_pdf)
self.worker.finished.connect(self.finish_scan)
self.worker.status_update.connect(self.update_status)
self.worker.run()
def update_status(self, text, color):
self.label_status.setText(text)
self.label_status.setStyleSheet("color: " + color + "; font-size: 12px;")
def finish_scan(self, success, message):
self.scanning = False
self.btn_scan.setEnabled(True)
self.btn_scan.setText('Начать сканирование')
self.btn_browse.setEnabled(True)
self.btn_refresh.setEnabled(True)
self.combo_scanner.setEnabled(True)
self.progress.setVisible(False)
if success:
self.label_status.setText('Готово!')
self.label_status.setStyleSheet("color: green; font-size: 12px;")
QMessageBox.information(self, 'Успех', message)
else:
self.label_status.setText('Ошибка')
self.label_status.setStyleSheet("color: red; font-size: 12px;")
QMessageBox.critical(self, 'Ошибка', message)
def open_folder(self):
os.makedirs(self.save_dir, exist_ok=True)
subprocess.Popen(["xdg-open", self.save_dir])
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = ScannerApp()
ex.show()
sys.exit(app.exec_())