Skip to content

Commit 68af4e2

Browse files
committed
Add 03-7-03
Image Viewer Example 图像查看器案例
1 parent 5e47d54 commit 68af4e2

File tree

1 file changed

+322
-0
lines changed

1 file changed

+322
-0
lines changed
Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
import sys
2+
from typing import List
3+
4+
from PySide6.QtCore import QDir, QMimeData, QStandardPaths
5+
from PySide6.QtGui import *
6+
from PySide6.QtWidgets import *
7+
8+
"""
9+
Image Viewer Example 图像查看器案例
10+
本案例为Qt官方同名案例的PySide6移植版,结合QLabel与QScrollArea实现图像显示功能
11+
https://doc.qt.io/qt-6/qtwidgets-widgets-imageviewer-example.html
12+
13+
注意本脚本仍有部分函数未能完全完成改写
14+
15+
"""
16+
17+
18+
def clipboard_image() -> QImage:
19+
mime_data: QMimeData = QMimeData()
20+
if mime_data == QGuiApplication.clipboard().mimeData():
21+
if mime_data.hasImage():
22+
pass
23+
# TODO 写完此函数
24+
25+
26+
class ImageViewer(QMainWindow):
27+
def __init__(self, *args, **kwargs):
28+
super().__init__(*args, **kwargs)
29+
30+
self.image: QImage = QImage()
31+
self.scale_factor: float = 1.0
32+
self.setWindowTitle("Image Viewer")
33+
self.resize(QGuiApplication.primaryScreen().availableSize() * 3 / 5)
34+
35+
self.image_label = QLabel(self)
36+
self.image_label.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
37+
self.image_label.setScaledContents(True)
38+
39+
self.scroll_area = QScrollArea(self)
40+
self.scroll_area.setBackgroundRole(QPalette.Dark)
41+
self.scroll_area.setWidget(self.image_label)
42+
self.scroll_area.setVisible(False)
43+
44+
self.setCentralWidget(self.scroll_area)
45+
46+
self.save_as_act = QAction()
47+
self.print_act = QAction()
48+
self.copy_act = QAction()
49+
self.zoom_in_act = QAction()
50+
self.zoom_out_act = QAction()
51+
self.normal_size_act = QAction()
52+
self.fit_to_window_act = QAction()
53+
self.create_actions()
54+
55+
def load_file(self, file_name: str) -> bool:
56+
"""
57+
打开文件,加载图片 \n
58+
:param file_name: 文件名
59+
:return: 是否成功加载文件
60+
"""
61+
62+
reader = QImageReader(file_name)
63+
reader.setAutoTransform(True)
64+
new_image = reader.read()
65+
if new_image.isNull():
66+
QMessageBox.information(
67+
self,
68+
QGuiApplication.applicationDisplayName(),
69+
f"Cannot load {QDir.toNativeSeparators(file_name)}: {reader.errorString()}",
70+
)
71+
return False
72+
73+
self.set_image(new_image)
74+
75+
self.setWindowFilePath(file_name)
76+
77+
message = (
78+
f"Opened '{QDir.toNativeSeparators(file_name)}', "
79+
f"{self.image.width()}x{self.image.height()}, Depth: {self.image.depth()}"
80+
)
81+
self.statusBar().showMessage(message)
82+
return True
83+
84+
def set_image(self, new_image: QImage) -> None:
85+
"""
86+
将图像设置到滚动区域中,并展示 \n
87+
:param new_image: QImage图像
88+
:return: None
89+
"""
90+
91+
self.image = new_image
92+
if self.image.colorSpace().isValid():
93+
self.image.convertToColorSpace(QColorSpace.SRgb)
94+
self.image_label.setPixmap(QPixmap.fromImage(self.image))
95+
96+
self.scale_factor = 1.0
97+
98+
self.scroll_area.setVisible(True)
99+
self.print_act.setEnabled(True)
100+
self.fit_to_window_act.setEnabled(True)
101+
self.update_actions()
102+
103+
if not self.fit_to_window_act.isChecked():
104+
self.image_label.adjustSize()
105+
106+
def save_file(self, file_name: str) -> bool:
107+
writer = QImageWriter(file_name)
108+
109+
if not writer.write(self.image):
110+
QMessageBox.information(
111+
self,
112+
QGuiApplication.applicationDisplayName(),
113+
f"Cannot write {QDir.toNativeSeparators(file_name)}: {writer.errorString()}",
114+
)
115+
return False
116+
message = f"Wrote '{QDir.toNativeSeparators(file_name)}'"
117+
self.statusBar().showMessage(message)
118+
return True
119+
120+
def initialize_image_file_dialog(
121+
self, dialog: QFileDialog, accept_mode: QFileDialog.AcceptMode
122+
) -> None:
123+
"""
124+
配置文件对话框为只接受图像模式 \n
125+
:param dialog: 待配置的对话框
126+
:param accept_mode: 接受模式
127+
:return: None
128+
"""
129+
130+
first_dialog = True
131+
132+
if first_dialog:
133+
first_dialog = False
134+
pictures_locations = QStandardPaths.standardLocations(QStandardPaths.PicturesLocation)
135+
if not pictures_locations:
136+
dialog_dir = QDir.currentPath()
137+
else:
138+
dialog_dir = pictures_locations[-1]
139+
dialog.setDirectory(dialog_dir)
140+
141+
mime_type_filters: List[str] = []
142+
if accept_mode == QFileDialog.AcceptOpen:
143+
supported_mime_types = QImageReader.supportedMimeTypes()
144+
else:
145+
supported_mime_types = QImageWriter.supportedMimeTypes()
146+
147+
for mime_type_name in supported_mime_types:
148+
mime_type_filters.append(str(mime_type_name))
149+
mime_type_filters.sort()
150+
151+
dialog.setMimeTypeFilters(mime_type_filters)
152+
dialog.selectMimeTypeFilter("image/jpeg")
153+
dialog.setAcceptMode(accept_mode)
154+
if accept_mode == QFileDialog.AcceptSave:
155+
dialog.setDefaultSuffix("jpg")
156+
157+
def open(self) -> None:
158+
dialog = QFileDialog(self, "Open File")
159+
self.initialize_image_file_dialog(dialog, QFileDialog.AcceptOpen)
160+
while dialog.exec() == QDialog.Accepted and not self.load_file(dialog.selectedFiles()[0]):
161+
...
162+
163+
def save_as(self) -> None:
164+
dialog = QFileDialog(self, "Save File As")
165+
self.initialize_image_file_dialog(dialog, QFileDialog.AcceptSave)
166+
167+
while dialog.exec() == QDialog.Accepted and not self.save_file(dialog.selectedFiles()[0]):
168+
...
169+
170+
def print(self) -> None:
171+
pass
172+
173+
def copy(self) -> None:
174+
QGuiApplication.clipboard().setImage(self.image)
175+
176+
def paste(self) -> None:
177+
new_image = clipboard_image()
178+
179+
def zoom_in(self) -> None:
180+
self.scale_image(1.25)
181+
182+
def zoom_out(self) -> None:
183+
self.scale_image(0.8)
184+
185+
def normal_size(self) -> None:
186+
self.image_label.adjustSize()
187+
self.scale_factor = 1.0
188+
189+
def fit_to_window(self) -> None:
190+
fit_to_window_: bool = self.fit_to_window_act.isChecked()
191+
self.scroll_area.setWidgetResizable(fit_to_window_)
192+
if not fit_to_window_:
193+
self.normal_size()
194+
self.update_actions()
195+
196+
def about(self) -> None:
197+
"""
198+
展示本应用程序的关于信息 \n
199+
:return: None
200+
"""
201+
202+
QMessageBox.about(
203+
self,
204+
"About Image Viewer",
205+
"<p>The <b>Image Viewer</b> example shows how to combine QLabel "
206+
"and QScrollArea to display an image. QLabel is typically used "
207+
"for displaying a text, but it can also display an image. "
208+
"QScrollArea provides a scrolling view around another widget. "
209+
"If the child widget exceeds the size of the frame, QScrollArea "
210+
"automatically provides scroll bars. </p><p>The example "
211+
"demonstrates how QLabel's ability to scale its contents "
212+
"(QLabel::scaledContents), and QScrollArea's ability to "
213+
"automatically resize its contents "
214+
"(QScrollArea::widgetResizable), can be used to implement "
215+
"zooming and scaling features. </p><p>In addition the example "
216+
"shows how to use QPainter to print an image.</p>",
217+
)
218+
219+
def create_actions(self) -> None:
220+
"""
221+
创建菜单并添加actions \n
222+
:return: None
223+
"""
224+
225+
file_menu: QMenu = self.menuBar().addMenu("&File")
226+
227+
open_act = file_menu.addAction("&Open...", self.open)
228+
open_act.setShortcut(QKeySequence.Open)
229+
230+
# TODO 解决无法通过Mypy的问题
231+
save_as_act = file_menu.addAction("&Save As...", self.save_as)
232+
save_as_act.setEnabled(False)
233+
234+
print_act = file_menu.addAction("&Print...", self.print)
235+
print_act.setShortcut(QKeySequence.Print)
236+
print_act.setEnabled(False)
237+
238+
file_menu.addSeparator()
239+
240+
exit_act = file_menu.addAction("E&xit", self.close)
241+
exit_act.setShortcut("Ctrl+Q")
242+
243+
edit_menu = self.menuBar().addMenu("&Edit")
244+
245+
self.copy_act = edit_menu.addAction("&Copy", self.copy)
246+
self.copy_act.setShortcut(QKeySequence.Copy)
247+
self.copy_act.setEnabled(False)
248+
249+
paste_act: QAction = edit_menu.addAction("&Paste", self.paste)
250+
paste_act.setShortcut(QKeySequence.Paste)
251+
252+
view_menu = self.menuBar().addMenu("&View")
253+
254+
self.zoom_in_act = view_menu.addAction("Zoom &In (25%)", self.zoom_in)
255+
self.zoom_in_act.setShortcut(QKeySequence.ZoomIn)
256+
self.zoom_in_act.setEnabled(False)
257+
258+
self.zoom_out_act = view_menu.addAction("Zoom &Out (25%)", self.zoom_out)
259+
self.zoom_out_act.setShortcut(QKeySequence.ZoomOut)
260+
self.zoom_out_act.setEnabled(False)
261+
262+
self.normal_size_act = view_menu.addAction("&Normal Size", self.normal_size)
263+
self.normal_size_act.setShortcut("Ctrl+S")
264+
self.normal_size_act.setEnabled(False)
265+
266+
view_menu.addSeparator()
267+
268+
self.fit_to_window_act = view_menu.addAction("&Fit to Window", self.fit_to_window)
269+
self.fit_to_window_act.setEnabled(True)
270+
self.fit_to_window_act.setCheckable(True)
271+
self.fit_to_window_act.setShortcut("Ctrl+F")
272+
273+
help_menu = self.menuBar().addMenu("&Help")
274+
275+
help_menu.addAction("&About", self.about)
276+
help_menu.addAction("About &Qt", QApplication.aboutQt)
277+
278+
def update_actions(self) -> None:
279+
"""
280+
更新actions可用状态 \n
281+
:return: None
282+
"""
283+
284+
self.save_as_act.setEnabled(self.image.isNull())
285+
self.copy_act.setEnabled(self.image.isNull())
286+
self.zoom_in_act.setEnabled(not self.fit_to_window_act.isChecked())
287+
self.zoom_out_act.setEnabled(not self.fit_to_window_act.isChecked())
288+
self.normal_size_act.setEnabled(not self.fit_to_window_act.isChecked())
289+
290+
def scale_image(self, factor: float) -> None:
291+
"""
292+
缩放图像并调整相关控件 \n
293+
:param factor: 缩放比例系数
294+
:return: None
295+
"""
296+
297+
self.scale_factor *= factor
298+
self.image_label.resize(self.scale_factor * self.image_label.pixmap().size())
299+
self.adjust_scroll_bar(self.scroll_area.horizontalScrollBar(), factor)
300+
self.adjust_scroll_bar(self.scroll_area.verticalScrollBar(), factor)
301+
302+
self.zoom_in_act.setEnabled(self.scale_factor < 3.0)
303+
self.zoom_out_act.setEnabled(self.scale_factor > 0.333)
304+
305+
def adjust_scroll_bar(self, scroll_bar: QScrollBar, factor: float):
306+
"""
307+
调整滚动条位置 \n
308+
:param scroll_bar: 滚动条实例
309+
:param factor: 缩放因数
310+
:return: None
311+
"""
312+
313+
scroll_bar.setValue(
314+
int(factor * scroll_bar.value() + ((factor - 1) * scroll_bar.pageStep() / 2))
315+
)
316+
317+
318+
if __name__ == "__main__":
319+
app = QApplication(sys.argv)
320+
window = ImageViewer()
321+
window.show()
322+
sys.exit(app.exec())

0 commit comments

Comments
 (0)