GetSaveFileName弹出文件选择框居中显示

Win32 API:实现文件选择框对话框居中
本文介绍了如何通过设置Win32 API的OPENFILENAME结构体及Hook函数,使得GetSaveFileName弹出的文件选择框在指定窗口居中显示。主要步骤包括设置OFN_ENABLEHOOK和OFN_EXPLORER标志,以及编写Hook回调函数WM_INITDIALOG,计算并调整对话框位置。
部署运行你感兴趣的模型镜像

传入的结构体参数:

OPENFILENAME ofn;

ZeroMemory(&ofn, sizeof(ofn));

ofn.lpstrFile = 初始文件名;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = _T("Text Files(*.txt)|*.txt|All Files(*.*)|*.*||");
ofn.lpstrDefExt = _T("txt");
ofn.lpstrTitle = _T("保存为");
ofn.hInstance = GetModuleHandle(NULL);
ofn.Flags = OFN_HIDEREADONLY | OFN_ENABLEHOOK | OFN_EXPLORER;
ofn.hwndOwner = 父窗口句柄;
ofn.FlagsEx = OFN_EX_NOPLACESBAR;
ofn.lpfnHook = OFNHookProc;
ofn.lpstrInitialDir = prtMainFrame->InitPaht.c_str();

ofn.lStructSize = sizeof(OPENFILENAME); 

// 让窗口居中显示,主要是设置Hook函数,在Flags标志中必须设置OFN_ENABLEHOOK | OFN_EXPLORER,具体的作用请看msdn

下面看一下Hook函数的编写方法:

UINT_PTR CALLBACK OFNHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if (uiMsg == WM_INITDIALOG)
{
RECT rcParent;
HWND hWndParent = GetParent(hdlg);
GetClientRect(hWndParent, &rcParent);
RECT rcHaotiMainFrame;
GetClientRect(按哪个窗口剧中的窗口句柄, &rcHaotiMainFrame);
POINT ptParentInScreen;
ptParentInScreen.x = rcParent.left;
ptParentInScreen.y = rcParent.top;
::ClientToScreen(hWndParent, (LPPOINT)&ptParentInScreen);
SetWindowPos(hWndParent, NULL,
ptParentInScreen.x + (rcHaotiMainFrame.right - rcHaotiMainFrame.left - (rcParent.right - rcParent.left)) / 2,
ptParentInScreen.y + (rcHaotiMainFrame.bottom - rcHaotiMainFrame.top - (rcParent.bottom - rcParent.top)) / 2,
0, 0, SWP_NOZORDER | SWP_NOSIZE);
}
UNREFERENCED_PARAMETER(wParam);
return 1;
}

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

# main_window.py(主窗口逻辑) import os import numpy as np from PySide6.QtWidgets import QMainWindow, QFileDialog, QGraphicsScene, QGraphicsView, QMessageBox, QGraphicsPathItem from PySide6.QtGui import QPainterPath, QPen, QBrush, QAction, QTransform, QImage, QPixmap, QColor from PySide6.QtCore import Qt, QRectF, QPointF from osgeo import ogr, gdal from PySide6.QtWidgets import QInputDialog # 新增输入对话 # 新增自定义图形项类(用于存储属性) class FeatureItem(QGraphicsPathItem): def __init__(self, path, attributes): super().__init__(path) self.attributes = attributes # 存储属性字典 class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("GIS软件") self.setGeometry(100, 100, 800, 600) ogr.UseExceptions() self.init_ui() self.scene = QGraphicsScene(self) self.graphicsView.setScene(self.scene) # 新增:存储所有几何边界 self.total_bounds = QRectF() def init_ui(self): self.toolBar = self.addToolBar("工具") self.actionOpen_Vector_Data = QAction("打开矢量数据", self) self.toolBar.addAction(self.actionOpen_Vector_Data) # 新增栅格动作 self.actionOpen_Raster_Data = QAction("打开栅格数据", self) self.toolBar.addAction(self.actionOpen_Raster_Data) # 添加到工具栏 # 新增缓冲区分析按钮 self.actionBuffer_Analysis = QAction("缓冲区分析", self) self.toolBar.addAction(self.actionBuffer_Analysis) self.graphicsView = QGraphicsView() self.setCentralWidget(self.graphicsView) # 新增属性查询按钮 self.actionQuery_Attribute = QAction("属性查询", self) self.toolBar.addAction(self.actionQuery_Attribute) self.actionOpen_Vector_Data.triggered.connect(self.open_vector_data) self.actionOpen_Raster_Data.triggered.connect(self.open_raster_data) # 新增连接 self.actionBuffer_Analysis.triggered.connect(self.buffer_analysis) self.actionQuery_Attribute.triggered.connect(self.enable_query_mode) # 新增鼠标点击事件 self.graphicsView.setMouseTracking(True) self.is_query_mode = False # 新增波段组合按钮 self.actionBand_Combination = QAction("波段组合", self) self.toolBar.addAction(self.actionBand_Combination) self.actionBand_Combination.triggered.connect(self.open_band_combination) # 新增栅格裁剪按钮(在init_ui方法末尾添加) self.actionClip_Raster = QAction("栅格裁剪", self) self.toolBar.addAction(self.actionClip_Raster) self.actionClip_Raster.triggered.connect(self.clip_raster) # 新增连接 self.actionBand_Calculation = QAction("波段运算", self) self.toolBar.addAction(self.actionBand_Calculation) self.actionBand_Calculation.triggered.connect(self.band_calculation) # 新增质心绘制按钮(放在init_ui方法中) self.actionDraw_Centroids = QAction("绘制质心", self) self.toolBar.addAction(self.actionDraw_Centroids) self.actionDraw_Centroids.triggered.connect(self.draw_centroids) self.centroid_items = [] # 新增:存储质心图形项 # 新增空间查询按钮(放在init_ui方法中) self.actionSpatial_Query = QAction("空间查询", self) self.toolBar.addAction(self.actionSpatial_Query) self.actionSpatial_Query.triggered.connect(self.enable_spatial_query_mode) self.is_spatial_query_mode = False self.spatial_query_results = [] # 存储查询结果 # 新增保存按钮(放在init_ui方法中) self.actionSave_Vector = QAction("保存矢量数据", self) self.toolBar.addAction(self.actionSave_Vector) self.actionSave_Vector.triggered.connect(self.save_vector_data) # 新增栅格保存按钮(放在init_ui方法中) self.actionSave_Raster = QAction("保存栅格数据", self) self.toolBar.addAction(self.actionSave_Raster) self.actionSave_Raster.triggered.connect(self.save_raster_data) def open_vector_data(self): file_path, _ = QFileDialog.getOpenFileName( self, "打开矢量文件", "", "Shapefile (*.shp);;GeoJSON (*.geojson);;All Files (*)" ) if file_path: self.load_vector_data(file_path) # 新增:自动缩放视图 self.auto_zoom() def load_vector_data(self, file_path): self.scene.clear() self.total_bounds = QRectF() # 重置边界 try: data_source = ogr.Open(file_path, 0) layer = data_source.GetLayer(0) for feature in layer: geom = feature.GetGeometryRef() path = self.geometry_to_qpainterpath(geom) # 更新总边界 if path.boundingRect().isValid(): self.total_bounds = self.total_bounds.united(path.boundingRect()) pen = QPen(Qt.blue, 1) brush = QBrush(Qt.cyan) self.scene.addPath(path, pen, brush) data_source = None except Exception as e: print(f"加载失败: {str(e)}") self.current_vector_path = file_path # 新增这一行 data_source = None def geometry_to_qpainterpath(self, geom): path = QPainterPath() if geom.GetGeometryType() == ogr.wkbPolygon: for ring in range(geom.GetGeometryCount()): linear_ring = geom.GetGeometryRef(ring) points = linear_ring.GetPoints() if points: path.moveTo(points[0][0], points[0][1]) for p in points[1:]: path.lineTo(p[0], p[1]) path.closeSubpath() elif geom.GetGeometryType() == ogr.wkbLineString: points = geom.GetPoints() if points: path.moveTo(points[0][0], points[0][1]) for p in points[1:]: path.lineTo(p[0], p[1]) elif geom.GetGeometryType() == ogr.wkbPoint: x, y = geom.GetX(), geom.GetY() path.addEllipse(x - 2, y - 2, 4, 4) return path def auto_zoom(self): """自动缩放视图到数据范围并放大2倍""" if not self.total_bounds.isValid(): return # 设置场景边界 self.scene.setSceneRect(self.total_bounds) # 获取视图可视区域 view_rect = self.graphicsView.viewport().rect() # 计算缩放比例(自动适应 + 2倍放大) transform = QTransform() transform.scale(2, 2) # 先放大2倍 # 应用缩放并居中 self.graphicsView.setTransform(transform) self.graphicsView.fitInView(self.total_bounds, Qt.KeepAspectRatio) # 新增缓冲区分析方法 def buffer_analysis(self): """执行缓冲区分析""" if not hasattr(self, 'current_vector_path'): QMessageBox.warning(self, "警告", "请先打开矢量数据文件!") return # 获取缓冲距离 distance, ok = QInputDialog.getDouble( self, "缓冲区分析", "输入缓冲距离(单位与数据坐标系一致):", 0.0, 0 ) if not ok: return try: # 重新打开数据源获取几何 data_source = ogr.Open(self.current_vector_path, 0) layer = data_source.GetLayer(0) # 创建缓冲区路径 buffer_path = QPainterPath() pen = QPen(Qt.red, 2, Qt.DashLine) brush = QBrush(QColor(255, 0, 0, 50)) # 半透明红色填充 for feature in layer: geom = feature.GetGeometryRef() buffer_geom = geom.Buffer(distance) path = self.geometry_to_qpainterpath(buffer_geom) buffer_path.addPath(path) # 添加到场景 self.scene.addPath(buffer_path, pen, brush) # 更新视图边界 if buffer_path.boundingRect().isValid(): self.total_bounds = self.total_bounds.united(buffer_path.boundingRect()) self.auto_zoom() data_source = None except Exception as e: QMessageBox.critical(self, "错误", f"缓冲区分析失败: {str(e)}") def load_vector_data(self, file_path): self.scene.clear() self.total_bounds = QRectF() try: data_source = ogr.Open(file_path, 0) layer = data_source.GetLayer(0) # 获取字段定义 layer_defn = layer.GetLayerDefn() field_names = [layer_defn.GetFieldDefn(i).GetName() for i in range(layer_defn.GetFieldCount())] for feature in layer: geom = feature.GetGeometryRef() path = self.geometry_to_qpainterpath(geom) # 创建属性字典 attributes = { "FID": feature.GetFID(), **{name: feature.GetField(name) for name in field_names} } # 使用自定义图形项 item = FeatureItem(path, attributes) item.setPen(QPen(Qt.blue, 1)) item.setBrush(QBrush(Qt.cyan)) self.scene.addItem(item) if path.boundingRect().isValid(): self.total_bounds = self.total_bounds.united(path.boundingRect()) data_source = None except Exception as e: print(f"加载失败: {str(e)}") self.current_vector_path = file_path data_source = None # 新增属性查询方法 def enable_query_mode(self): """启用属性查询模式""" self.is_query_mode = not self.is_query_mode self.actionQuery_Attribute.setText("退出查询" if self.is_query_mode else "属性查询") self.graphicsView.setCursor(Qt.CrossCursor if self.is_query_mode else Qt.ArrowCursor) # 新增鼠标事件处理 def mousePressEvent(self, event): if self.is_query_mode and event.button() == Qt.LeftButton: scene_pos = self.graphicsView.mapToScene(event.pos()) items = self.scene.items(scene_pos, Qt.IntersectsItemShape, Qt.DescendingOrder) for item in items: if isinstance(item, FeatureItem): # 构建属性信息字符串 info = "\n".join([f"{k}: {v}" for k, v in item.attributes.items()]) QMessageBox.information(self, "要素属性", info) return super().mousePressEvent(event) def draw_centroids(self): """独立质心绘制功能""" if not hasattr(self, 'current_vector_path'): QMessageBox.warning(self, "警告", "请先打开矢量数据文件!") return # 清除已有质心 for item in self.centroid_items: self.scene.removeItem(item) self.centroid_items.clear() try: data_source = ogr.Open(self.current_vector_path, 0) layer = data_source.GetLayer(0) for feature in layer: geom = feature.GetGeometryRef() centroid = geom.Centroid() if centroid: # 创建质心图形项 path = QPainterPath() path.addEllipse( QRectF( centroid.GetX() - 0.3, # 修改为0.3像素半径 centroid.GetY() - 0.3, 0.6, 0.6 # 直径0.6像素 ) ) item = self.scene.addPath( path, QPen(Qt.red, 0.1), QBrush(Qt.red) ) self.centroid_items.append(item) data_source = None self.auto_zoom() except Exception as e: QMessageBox.critical(self, "错误", f"质心绘制失败: {str(e)}") # 新增空间查询模式切换方法 def enable_spatial_query_mode(self): """启用空间查询模式""" self.is_spatial_query_mode = not self.is_spatial_query_mode self.actionSpatial_Query.setText("退出空间查询" if self.is_spatial_query_mode else "空间查询") self.graphicsView.setCursor(Qt.CrossCursor if self.is_spatial_query_mode else Qt.ArrowCursor) if not self.is_spatial_query_mode: self.clear_spatial_query_results() # 新增空间查询处理方法 def mousePressEvent(self, event): if self.is_spatial_query_mode and event.button() == Qt.LeftButton: scene_pos = self.graphicsView.mapToScene(event.pos()) items = self.scene.items(scene_pos, Qt.IntersectsItemShape, Qt.DescendingOrder) for item in items: if isinstance(item, FeatureItem): # 获取空间关系选择 relations = ["相交", "包含", "被包含", "接触", "重叠"] relation, ok = QInputDialog.getItem( self, "空间关系选择", "请选择空间关系:", relations, 0, False ) if not ok: return # 执行空间查询 self.perform_spatial_query(item, relation) return super().mousePressEvent(event) # 新增空间查询核心方法 def perform_spatial_query(self, source_item, relation): """执行空间查询并高亮结果""" self.clear_spatial_query_results() try: # 获取源要素几何 source_geom = self.item_to_geometry(source_item) if not source_geom: return # 获取所有要素 all_items = [item for item in self.scene.items() if isinstance(item, FeatureItem)] # 遍历检查空间关系 for target_item in all_items: target_geom = self.item_to_geometry(target_item) if not target_geom: continue # 执行空间关系判断 if relation == "相交" and source_geom.Intersects(target_geom): self.highlight_item(target_item) elif relation == "包含" and source_geom.Contains(target_geom): self.highlight_item(target_item) elif relation == "被包含" and target_geom.Contains(source_geom): self.highlight_item(target_item) elif relation == "接触" and source_geom.Touches(target_geom): self.highlight_item(target_item) elif relation == "重叠" and source_geom.Overlaps(target_geom): self.highlight_item(target_item) except Exception as e: QMessageBox.critical(self, "错误", f"空间查询失败: {str(e)}") # 新增辅助方法 def item_to_geometry(self, item): """将图形项转换为OGR几何对象""" path = item.path() elements = path.toSubpathPolygons(QTransform()) if not elements: return None # 创建多边形几何 geom = ogr.Geometry(ogr.wkbPolygon) ring = ogr.Geometry(ogr.wkbLinearRing) for point in elements[0]: ring.AddPoint(point.x(), point.y()) ring.CloseRings() geom.AddGeometry(ring) return geom def highlight_item(self, item): """高亮显示查询结果""" original_pen = item.pen() highlight_pen = QPen(Qt.yellow, 3) item.setPen(highlight_pen) self.spatial_query_results.append((item, original_pen)) def clear_spatial_query_results(self): """清除查询结果高亮""" for item, original_pen in self.spatial_query_results: item.setPen(original_pen) self.spatial_query_results.clear() def open_raster_data(self): """打开栅格数据文件""" file_path, _ = QFileDialog.getOpenFileName( self, "打开栅格文件", "", "GeoTIFF (*.tif);;JPEG (*.jpg *.jpeg);;PNG (*.png);;All Files (*)" ) if file_path: try: self.load_raster_data(file_path) self.auto_zoom() except Exception as e: QMessageBox.critical(self, "错误", f"加载栅格失败: {str(e)}") def load_raster_data(self, file_path): """加载栅格数据到视图""" # 打开栅格文件(需要用户修改路径的部分) dataset = gdal.Open(file_path) # 相对路径示例:"./data/raster.tif" # 读取第一个波段 band = dataset.GetRasterBand(1) width = dataset.RasterXSize height = dataset.RasterYSize # 转换为numpy数组 data = band.ReadAsArray() # 创建QImage(注意数据类型转换) if data.dtype == np.uint8: format = QImage.Format.Format_Grayscale8 else: format = QImage.Format.Format_ARGB32 q_img = QImage(data.tobytes(), width, height, format) # 创建像素图项 pixmap = QPixmap.fromImage(q_img) raster_item = self.scene.addPixmap(pixmap) # 处理地理坐标(如果存在) geotransform = dataset.GetGeoTransform() if geotransform: # 计算四个角的坐标 x_origin = geotransform[0] y_origin = geotransform[3] pixel_width = geotransform[1] pixel_height = geotransform[5] # 更新场景边界 x_min = x_origin x_max = x_origin + pixel_width * width y_min = y_origin + pixel_height * height y_max = y_origin self.total_bounds = QRectF( QPointF(x_min, y_min), QPointF(x_max, y_max) ) dataset = None # 关闭数据集 def open_band_combination(self): if not hasattr(self, 'current_raster_path'): QMessageBox.warning(self, "警告", "请先打开栅格数据文件!") return # 复用open_raster_data的逻辑 self.open_raster_data() def open_raster_data(self): file_path, _ = QFileDialog.getOpenFileName( self, "打开栅格文件", "", "GeoTIFF (*.tif);;JPEG (*.jpg *.jpeg);;PNG (*.png);;All Files (*)" ) if file_path: try: dataset = gdal.Open(file_path) num_bands = dataset.RasterCount # 获取用户输入的波段组合 red_band, ok1 = QInputDialog.getInt( self, "波段选择", f"红通道波段号 (1-{num_bands}):", 1, 1, num_bands ) green_band, ok2 = QInputDialog.getInt( self, "波段选择", f"绿通道波段号 (1-{num_bands}):", min(2, num_bands), 1, num_bands ) blue_band, ok3 = QInputDialog.getInt( self, "波段选择", f"蓝通道波段号 (1-{num_bands}):", min(3, num_bands), 1, num_bands ) if not (ok1 and ok2 and ok3): return self.load_raster_data(file_path, red_band, green_band, blue_band) self.auto_zoom() self.current_raster_path = file_path # 新增存储当前路径 except Exception as e: QMessageBox.critical(self, "错误", f"加载栅格失败: {str(e)}") def load_raster_data(self, file_path, red_band=1, green_band=2, blue_band=3): """加载栅格数据到视图(支持波段组合)""" dataset = gdal.Open(file_path) width = dataset.RasterXSize height = dataset.RasterYSize # 读取三个波段数据 def read_band(band_num): band = dataset.GetRasterBand(band_num) data = band.ReadAsArray() # 自动拉伸到0-255范围 data_min = data.min() data_max = data.max() return np.clip(((data - data_min) / (data_max - data_min) * 255), 0, 255).astype(np.uint8) # 合并波段 rgb_array = np.dstack([ read_band(red_band), read_band(green_band), read_band(blue_band) ]) # 创建QImage q_img = QImage( rgb_array.data, width, height, 3 * width, # 每像素3字节(RGB) QImage.Format.Format_RGB888 ) # 创建像素图项 pixmap = QPixmap.fromImage(q_img) self.scene.addPixmap(pixmap) # 处理地理坐标(保持原有逻辑) geotransform = dataset.GetGeoTransform() if geotransform: x_origin = geotransform[0] y_origin = geotransform[3] pixel_width = geotransform[1] pixel_height = geotransform[5] x_min = x_origin x_max = x_origin + pixel_width * width y_min = y_origin + pixel_height * height # 计算下边界 y_max = y_origin # 上边界 # 确保坐标顺序正确 if x_min > x_max: x_min, x_max = x_max, x_min if y_min > y_max: y_min, y_max = y_max, y_min self.total_bounds = QRectF(QPointF(x_min, y_min), QPointF(x_max, y_max)) dataset = None # 新增栅格裁剪方法(必须缩进在类内部) def clip_raster(self): """执行栅格裁剪功能""" if not hasattr(self, 'current_raster_path'): QMessageBox.warning(self, "警告", "请先打开栅格数据文件!") return # 选择裁剪矢量文件 vector_path, _ = QFileDialog.getOpenFileName( self, "选择裁剪区域文件", "", "Shapefile (*.shp);;GeoJSON (*.geojson);;All Files (*)" ) if not vector_path: return try: # 获取原始栅格信息 src_ds = gdal.Open(self.current_raster_path) geotransform = src_ds.GetGeoTransform() proj = src_ds.GetProjection() # 获取矢量范围 vector_ds = ogr.Open(vector_path) layer = vector_ds.GetLayer() feature = layer.GetNextFeature() geom = feature.GetGeometryRef() x_min, x_max, y_min, y_max = geom.GetEnvelope() # 创建临时裁剪结果文件 import os # 确保导入os模块 output_path = os.path.splitext(self.current_raster_path)[0] + "_clipped.tif" # 执行裁剪操作 options = gdal.WarpOptions( format='GTiff', outputBounds=[x_min, y_min, x_max, y_max], dstSRS=proj ) gdal.Warp(output_path, src_ds, options=options) # 加载裁剪结果 self.load_raster_data(output_path) self.auto_zoom() # 清理资源 src_ds = None vector_ds = None except Exception as e: QMessageBox.critical(self, "错误", f"栅格裁剪失败: {str(e)}") # 新增波段运算方法 def band_calculation(self): """执行波段运算(示例为NDVI计算)""" if not hasattr(self, 'current_raster_path'): QMessageBox.warning(self, "警告", "请先打开栅格数据文件!") return try: # 获取用户输入参数 red_band, ok1 = QInputDialog.getInt( self, "波段选择", "输入红波段编号 (1-based):", 1, 1, 100 ) nir_band, ok2 = QInputDialog.getInt( self, "波段选择", "输入近红外波段编号 (1-based):", 4, 1, 100 ) if not (ok1 and ok2): return # 读取栅格数据 dataset = gdal.Open(self.current_raster_path) red = dataset.GetRasterBand(red_band).ReadAsArray() nir = dataset.GetRasterBand(nir_band).ReadAsArray() # 执行NDVI计算 ndvi = np.where( (nir + red) == 0, 0, (nir - red) / (nir + red) ).astype(np.float32) # 创建输出文件 output_path, _ = QFileDialog.getSaveFileName( self, "保存结果", "", "GeoTIFF (*.tif)" ) if not output_path: return # 写入结果 driver = gdal.GetDriverByName('GTiff') out_ds = driver.Create( output_path, dataset.RasterXSize, dataset.RasterYSize, 1, gdal.GDT_Float32 ) out_ds.SetGeoTransform(dataset.GetGeoTransform()) out_ds.SetProjection(dataset.GetProjection()) out_ds.GetRasterBand(1).WriteArray(ndvi) out_ds.FlushCache() # 清理资源 dataset = None out_ds = None QMessageBox.information(self, "成功", f"NDVI计算结果已保存至:\n{output_path}") except Exception as e: QMessageBox.critical(self, "错误", f"波段运算失败: {str(e)}") # 新增保存方法 def save_vector_data(self): """保存当前场景中的矢量数据""" if not hasattr(self, 'current_vector_path'): QMessageBox.warning(self, "警告", "没有可保存的矢量数据!") return # 获取保存路径 file_path, _ = QFileDialog.getSaveFileName( self, "保存矢量文件", "", "Shapefile (*.shp);;GeoJSON (*.geojson);;All Files (*)" ) if not file_path: return try: # 获取原始数据源信息 src_ds = ogr.Open(self.current_vector_path) src_layer = src_ds.GetLayer(0) src_defn = src_layer.GetLayerDefn() # 创建目标数据源 driver = ogr.GetDriverByName("ESRI Shapefile" if file_path.endswith(".shp") else "GeoJSON") if os.path.exists(file_path): driver.DeleteDataSource(file_path) dst_ds = driver.CreateDataSource(file_path) # 创建图层(保持与原始数据相同的坐标系) dst_layer = dst_ds.CreateLayer( "saved_features", srs=src_layer.GetSpatialRef(), geom_type=ogr.wkbPolygon ) # 复制字段定义 for i in range(src_defn.GetFieldCount()): field_defn = src_defn.GetFieldDefn(i) dst_layer.CreateField(field_defn) # 遍历场景中的所有要素项 for item in self.scene.items(): if isinstance(item, FeatureItem): # 创建新要素 feature = ogr.Feature(dst_layer.GetLayerDefn()) # 复制属性 for key, value in item.attributes.items(): if key == "FID": continue # FID通常自动生成 if feature.GetFieldIndex(key) != -1: feature.SetField(key, str(value)) # 转换几何 geom = self.item_to_geometry(item) if geom: feature.SetGeometry(geom) dst_layer.CreateFeature(feature) feature = None # 添加缓冲区要素(如果存在) self.save_additional_features(dst_layer, "buffer") # 添加质心要素(如果存在) self.save_additional_features(dst_layer, "centroid") dst_ds = None src_ds = None QMessageBox.information(self, "成功", f"数据已保存至:\n{file_path}") except Exception as e: QMessageBox.critical(self, "错误", f"保存失败: {str(e)}") # 新增辅助保存方法 def save_additional_features(self, layer, feature_type): """保存附加要素(缓冲区/质心)""" items = [] if feature_type == "buffer": items = [item for item in self.scene.items() if item.pen().style() == Qt.DashLine and item.pen().color() == Qt.red] elif feature_type == "centroid": items = self.centroid_items for item in items: geom = self.item_to_geometry(item) if geom: feature = ogr.Feature(layer.GetLayerDefn()) feature.SetGeometry(geom) # 添加类型标识字段 feature.SetField("FEATURE_TYPE", feature_type.upper()) layer.CreateFeature(feature) feature = None # 在item_to_geometry方法中添加点要素支持 def item_to_geometry(self, item): """增强版几何转换(支持点要素)""" path = item.path() elements = path.toSubpathPolygons(QTransform()) if not elements: # 处理点要素 if isinstance(item, QGraphicsPathItem): path = item.path() if path.elementCount() == 1 and path.elementAt(0).isMoveTo(): pt = path.elementAt(0) geom = ogr.Geometry(ogr.wkbPoint) geom.AddPoint(pt.x, pt.y) return geom return None # 原有多边形处理逻辑 geom = ogr.Geometry(ogr.wkbPolygon) ring = ogr.Geometry(ogr.wkbLinearRing) for point in elements[0]: ring.AddPoint(point.x(), point.y()) ring.CloseRings() geom.AddGeometry(ring) return geom # 新增栅格保存方法 def save_raster_data(self): """保存当前显示的栅格数据""" if not hasattr(self, 'current_raster_path'): QMessageBox.warning(self, "警告", "没有可保存的栅格数据!") return # 获取保存路径和格式 file_path, selected_filter = QFileDialog.getSaveFileName( self, "保存栅格文件", "", "GeoTIFF (*.tif);;JPEG (*.jpg *.jpeg);;PNG (*.png);;All Files (*)" ) if not file_path: return try: # 获取当前显示的栅格数据 dataset = gdal.Open(self.current_raster_path) band_count = dataset.RasterCount width = dataset.RasterXSize height = dataset.RasterYSize # 创建输出数据集 driver = gdal.GetDriverByName(self.get_driver_name(file_path)) out_ds = driver.Create( file_path, width, height, band_count, self.get_gdal_datatype(dataset) ) # 复制地理参考信息 out_ds.SetGeoTransform(dataset.GetGeoTransform()) out_ds.SetProjection(dataset.GetProjection()) # 复制所有波段数据 for band_num in range(1, band_count + 1): in_band = dataset.GetRasterBand(band_num) out_band = out_ds.GetRasterBand(band_num) out_band.WriteArray(in_band.ReadAsArray()) out_band.FlushCache() # 清理资源 out_ds = None dataset = None QMessageBox.information(self, "成功", f"栅格数据已保存至:\n{file_path}") except Exception as e: QMessageBox.critical(self, "错误", f"栅格保存失败: {str(e)}") # 新增辅助方法 def get_driver_name(self, file_path): """根据文件扩展名获取GDAL驱动名称""" ext = os.path.splitext(file_path)[1].lower() return { '.tif': 'GTiff', '.jpg': 'JPEG', '.jpeg': 'JPEG', '.png': 'PNG' }.get(ext, 'GTiff') def get_gdal_datatype(self, dataset): """获取与原始数据集匹配的GDAL数据类型""" sample_band = dataset.GetRasterBand(1) return { gdal.GDT_Byte: gdal.GDT_Byte, gdal.GDT_UInt16: gdal.GDT_UInt16, gdal.GDT_Int16: gdal.GDT_Int16, gdal.GDT_UInt32: gdal.GDT_UInt32, gdal.GDT_Int32: gdal.GDT_Int32, gdal.GDT_Float32: gdal.GDT_Float32, gdal.GDT_Float64: gdal.GDT_Float64 }.get(sample_band.DataType, gdal.GDT_Float32) 你看看我的程序哪里出问题了,为什么属性查询我点了之后什么反应都没有啊,也没有弹窗什么的,之前还有弹窗显示我选中的地方的属性呢
07-18
import sys from PyQt5.QtWidgets import ( QApplication, QMainWindow, QTextEdit, QToolBar, QAction, QFileDialog, QFontComboBox, QComboBox, QInputDialog ) from PyQt5.QtGui import ( QTextCharFormat, QFont, QTextCursor, QTextBlockFormat, QImage, QKeySequence ) from PyQt5.QtCore import Qt, QMimeData, QByteArray, QRegularExpression from PyQt5.QtGui import QFontDatabase class WordLikeEditor(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Word 风格富文本编辑器") self.setGeometry(100, 100, 1000, 700) self.updating_font = False # 创建文本编辑区域 self.text_edit = QTextEdit(self) self.setCentralWidget(self.text_edit) # 重写 keyPressEvent 以支持粘贴 self.text_edit.keyPressEvent = self.handle_key_press # 初始化工具栏和菜单 self.init_toolbar() self.init_menu() # 监听光标变化以同步工具栏状态 self.text_edit.cursorPositionChanged.connect(self.update_toolbar_font) self.text_edit.cursorPositionChanged.connect(self.update_toolbar_font_size) self.text_edit.cursorPositionChanged.connect(self.update_toolbar_font_style) self.text_edit.cursorPositionChanged.connect(self.update_toolbar_font_color) self.text_edit.cursorPositionChanged.connect(self.update_toolbar_paragraph_style) def init_toolbar(self): toolbar = QToolBar("工具栏") self.addToolBar(toolbar) # ==== 字体设置区域 ==== self.font_combo = QFontComboBox() self.font_combo.setWritingSystem(QFontDatabase.SimplifiedChinese) self.font_combo.setEditable(False) self.font_combo.setToolTip("字体") self.font_combo.setFixedWidth(150) self.font_combo.currentFontChanged.connect(self.change_font) toolbar.addWidget(self.font_combo) self.size_combo = QComboBox() self.size_combo.setEditable(False) self.size_combo.setToolTip("字号") self.size_combo.setFixedWidth(60) for size in range(8, 72, 2): self.size_combo.addItem(str(size)) self.size_combo.setCurrentText("12") self.size_combo.currentTextChanged.connect(self.change_font_size) toolbar.addWidget(self.size_combo) self.style_combo = QComboBox() self.style_combo.setEditable(False) self.style_combo.setToolTip("字体样式") self.style_combo.setFixedWidth(80) self.style_combo.addItems(["普通", "粗体", "斜体", "粗斜体", "下划线"]) self.style_combo.currentTextChanged.connect(self.change_font_style) toolbar.addWidget(self.style_combo) self.color_combo = QComboBox() self.color_combo.setEditable(False) self.color_combo.setToolTip("字体颜色") self.color_combo.setFixedWidth(80) self.color_combo.addItems(["黑色", "红色", "蓝色", "绿色", "紫色", "橙色"]) self.color_combo.currentTextChanged.connect(self.change_font_color) toolbar.addWidget(self.color_combo) toolbar.addSeparator() # ==== 段落样式 ==== self.paragraph_combo = QComboBox() self.paragraph_combo.setEditable(False) self.paragraph_combo.setToolTip("段落样式") self.paragraph_combo.setFixedWidth(100) self.paragraph_combo.addItems(["正文", "标题1", "标题2", "标题3", "引用", "列表项"]) self.paragraph_combo.currentTextChanged.connect(self.change_paragraph_style) toolbar.addWidget(self.paragraph_combo) # ==== 行间距设置按钮 ==== line_spacing_action = QAction("行间距", self) line_spacing_action.setToolTip("设置段落行间距") line_spacing_action.triggered.connect(self.set_custom_line_spacing) toolbar.addAction(line_spacing_action) toolbar.addSeparator() # ==== 对齐方式 ==== left_align = QAction("左对齐", self) left_align.triggered.connect(lambda: self.text_edit.setAlignment(Qt.AlignLeft)) toolbar.addAction(left_align) center_align = QAction("居中", self) center_align.triggered.connect(lambda: self.text_edit.setAlignment(Qt.AlignCenter)) toolbar.addAction(center_align) right_align = QAction("右对齐", self) right_align.triggered.connect(lambda: self.text_edit.setAlignment(Qt.AlignRight)) toolbar.addAction(right_align) toolbar.addSeparator() # ==== 图片操作 ==== insert_image = QAction("插入图片", self) insert_image.triggered.connect(self.insert_image) toolbar.addAction(insert_image) paste_image = QAction("粘贴图片", self) paste_image.triggered.connect(self.paste_image) toolbar.addAction(paste_image) def init_menu(self): menu_bar = self.menuBar() file_menu = menu_bar.addMenu("文件") open_action = QAction("打开", self) open_action.setShortcut(QKeySequence.Open) open_action.triggered.connect(self.open_file) file_menu.addAction(open_action) save_action = QAction("保存", self) save_action.setShortcut(QKeySequence.Save) save_action.triggered.connect(self.save_file) file_menu.addAction(save_action) exit_action = QAction("退出", self) exit_action.triggered.connect(self.close) file_menu.addAction(exit_action) def set_custom_line_spacing(self): spacing, ok = QInputDialog.getDouble( self, "设置行间距", "请输入行间距倍数(如 1.0、1.5、2.0)", value=1.0, min=0.5, max=5.0, decimals=1 ) if ok and spacing > 0: cursor = self.text_edit.textCursor() block_fmt = cursor.blockFormat() block_fmt.setLineHeight(spacing * 100, QTextBlockFormat.FixedHeight) cursor.setBlockFormat(block_fmt) # 下面是已有方法,不再重复列出,仅展示新增部分 # 原有方法:change_font, change_font_size, change_font_style 等保持不变 # 请保留你原代码中这些方法的定义 def change_font(self, font): if self.updating_font: return self.updating_font = True fmt = self.text_edit.currentCharFormat() fmt.setFont(font) self.text_edit.mergeCurrentCharFormat(fmt) self.updating_font = False def change_font_size(self, size): if self.updating_font: return try: size = int(size) if size > 0: self.updating_font = True fmt = self.text_edit.currentCharFormat() fmt.setFontPointSize(size) self.text_edit.mergeCurrentCharFormat(fmt) self.updating_font = False except Exception as e: print("设置字号失败:", e) def change_font_style(self, style): if self.updating_font: return self.updating_font = True fmt = self.text_edit.currentCharFormat() if style == "普通": fmt.setFontWeight(QFont.Normal) fmt.setFontItalic(False) fmt.setFontUnderline(False) elif style == "粗体": fmt.setFontWeight(QFont.Bold) fmt.setFontItalic(False) fmt.setFontUnderline(False) elif style == "斜体": fmt.setFontWeight(QFont.Normal) fmt.setFontItalic(True) fmt.setFontUnderline(False) elif style == "粗斜体": fmt.setFontWeight(QFont.Bold) fmt.setFontItalic(True) fmt.setFontUnderline(False) elif style == "下划线": fmt.setFontWeight(QFont.Normal) fmt.setFontItalic(False) fmt.setFontUnderline(True) self.text_edit.mergeCurrentCharFormat(fmt) self.updating_font = False def change_font_color(self, color_name): if self.updating_font: return self.updating_font = True color_map = { "黑色": Qt.black, "红色": Qt.red, "蓝色": Qt.blue, "绿色": Qt.green, "紫色": Qt.magenta, "橙色": Qt.darkYellow, } color = color_map.get(color_name, Qt.black) fmt = self.text_edit.currentCharFormat() fmt.setForeground(color) self.text_edit.mergeCurrentCharFormat(fmt) self.updating_font = False def change_paragraph_style(self, style): if self.updating_font: return self.updating_font = True cursor = self.text_edit.textCursor() char_fmt = cursor.charFormat() block_fmt = cursor.blockFormat() if style == "正文": block_fmt.setHeadingLevel(0) block_fmt.setLineHeight(100, QTextBlockFormat.ProportionalHeight) block_fmt.setTopMargin(5) block_fmt.setBottomMargin(5) char_fmt.setFontPointSize(12) char_fmt.setFontWeight(QFont.Normal) elif style == "标题1": block_fmt.setHeadingLevel(1) block_fmt.setLineHeight(150, QTextBlockFormat.FixedHeight) block_fmt.setTopMargin(10) block_fmt.setBottomMargin(10) char_fmt.setFontPointSize(16) char_fmt.setFontWeight(QFont.Bold) elif style == "标题2": block_fmt.setHeadingLevel(2) block_fmt.setLineHeight(140, QTextBlockFormat.FixedHeight) block_fmt.setTopMargin(8) block_fmt.setBottomMargin(8) char_fmt.setFontPointSize(14) char_fmt.setFontWeight(QFont.Bold) elif style == "标题3": block_fmt.setHeadingLevel(3) block_fmt.setLineHeight(130, QTextBlockFormat.FixedHeight) block_fmt.setTopMargin(6) block_fmt.setBottomMargin(6) char_fmt.setFontPointSize(13) char_fmt.setFontWeight(QFont.Bold) elif style == "引用": block_fmt.setHeadingLevel(0) block_fmt.setLineHeight(100, QTextBlockFormat.ProportionalHeight) block_fmt.setLeftMargin(20) block_fmt.setBackground(Qt.lightGray) elif style == "列表项": block_fmt.setHeadingLevel(0) block_fmt.setLineHeight(100, QTextBlockFormat.ProportionalHeight) block_fmt.setLeftMargin(15) cursor.select(QTextCursor.BlockUnderCursor) cursor.setBlockFormat(block_fmt) cursor.setCharFormat(char_fmt) self.updating_font = False def insert_image(self): filename, _ = QFileDialog.getOpenFileName(self, "插入图片", "", "图像文件 (*.png *.jpg *.bmp)") if filename: cursor = self.text_edit.textCursor() cursor.insertImage(filename) self.restore_font_format(cursor) def open_file(self): filename, _ = QFileDialog.getOpenFileName(self, "打开文件", "", "文本文件 (*.txt);;所有文件 (*)") if filename: with open(filename, 'r', encoding='utf-8') as f: self.text_edit.setText(f.read()) def save_file(self): filename, _ = QFileDialog.getSaveFileName(self, "保存文件", "", "文本文件 (*.txt);;所有文件 (*)") if filename: with open(filename, 'w', encoding='utf-8') as f: f.write(self.text_edit.toPlainText()) def handle_key_press(self, event): if event.key() == Qt.Key_V and (event.modifiers() & Qt.ControlModifier): self.paste_image() else: QTextEdit.keyPressEvent(self.text_edit, event) def paste_image(self): clipboard = QApplication.clipboard() mime_data = clipboard.mimeData() if mime_data.hasImage(): image = clipboard.image() if not image.isNull(): cursor = self.text_edit.textCursor() cursor.insertImage(image) self.restore_font_format(cursor) else: print("剪贴板中的图片为空") elif mime_data.hasUrls(): url = mime_data.urls()[0] image = QImage(url.toLocalFile()) if not image.isNull(): cursor = self.text_edit.textCursor() cursor.insertImage(image) self.restore_font_format(cursor) else: print("无法加载图片文件:", url.toLocalFile()) elif mime_data.hasHtml(): html = mime_data.html() if 'data:image/' in html: regex = QRegularExpression(r"src=\"data:image/([^;\"]+);base64,([^\"\)]+)") match = regex.match(html) if match.hasMatch(): image_format = match.captured(1) base64_data = match.captured(2) byte_array = QByteArray.fromBase64(base64_data.encode()) image = QImage() image.loadFromData(byte_array) if not image.isNull(): cursor = self.text_edit.textCursor() cursor.insertImage(image) self.restore_font_format(cursor) else: print("无法加载 base64 图片") else: print("base64 匹配失败") else: print("HTML 中没有 base64 图片数据") else: print("剪贴板中没有支持的图片格式") def restore_font_format(self, cursor): fmt = self.text_edit.currentCharFormat() cursor.setCharFormat(fmt) self.text_edit.setTextCursor(cursor) self.text_edit.setFocus() def update_toolbar_font(self): fmt = self.text_edit.currentCharFormat() font = fmt.font() self.updating_font = True self.font_combo.setCurrentFont(font) self.updating_font = False def update_toolbar_font_size(self): fmt = self.text_edit.currentCharFormat() size = fmt.fontPointSize() if size > 0: self.updating_font = True self.size_combo.setCurrentText(str(int(size))) self.updating_font = False def update_toolbar_font_style(self): fmt = self.text_edit.currentCharFormat() weight = fmt.fontWeight() italic = fmt.fontItalic() underline = fmt.fontUnderline() if underline: style = "下划线" elif weight == QFont.Bold and italic: style = "粗斜体" elif weight == QFont.Bold: style = "粗体" elif italic: style = "斜体" else: style = "普通" self.updating_font = True self.style_combo.setCurrentText(style) self.updating_font = False def update_toolbar_font_color(self): fmt = self.text_edit.currentCharFormat() color = fmt.foreground().color() if color == Qt.red: style = "红色" elif color == Qt.blue: style = "蓝色" elif color == Qt.green: style = "绿色" elif color == Qt.magenta: style = "紫色" elif color == Qt.darkYellow: style = "橙色" else: style = "黑色" self.updating_font = True self.color_combo.setCurrentText(style) self.updating_font = False def update_toolbar_paragraph_style(self): cursor = self.text_edit.textCursor() block = cursor.block() fmt = block.blockFormat() level = fmt.headingLevel() if level == 1: style = "标题1" elif level == 2: style = "标题2" elif level == 3: style = "标题3" else: if fmt.leftMargin() > 10 and fmt.background().color() == Qt.lightGray: style = "引用" elif fmt.leftMargin() > 10: style = "列表项" else: style = "正文" self.updating_font = True self.paragraph_combo.setCurrentText(style) self.updating_font = False if __name__ == '__main__': # 把窗口实例化 from PyQt5 import QtCore from PyQt5 import QtGui QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling,True) QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps,True) QtGui.QGuiApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) app = QApplication(sys.argv) # 初始化qt应用架 # 获取屏幕 DPI screen = app.screens()[0] dpi = screen.logicalDotsPerInch() # 基准 DPI 为 96,按比例调整字体大小 base_dpi = 96.0 scale_factor = dpi / base_dpi font_size = max(12, int(12 * scale_factor)) # 最小字体大小为 10 font = app.font() font.setPointSize(font_size) app.setFont(font) window = WordLikeEditor() # 给予的第一个实例 window.show() sys.exit(app.exec()) 设置成即使点击了按钮,文本的编辑焦点一直保持,而不是点了按钮后又要点击文本才能继续编辑
最新发布
07-31
要实现WindTerm中rz命令弹出文件选择的功能,需要使用go语言编写一个WinAPI程序,并在WindTerm中调用它。以下是基本的代码示例: ```go package main import ( "fmt" "syscall" "unsafe" ) var ( comdlg32 = syscall.NewLazyDLL("comdlg32.dll") getOpenFileName = comdlg32.NewProc("GetOpenFileNameW") getSaveFileName = comdlg32.NewProc("GetSaveFileNameW") ole32 = syscall.NewLazyDLL("ole32.dll") coTaskMemFree = ole32.NewProc("CoTaskMemFree") shell32 = syscall.NewLazyDLL("shell32.dll") dragQueryFileW = shell32.NewProc("DragQueryFileW") dragFinish = shell32.NewProc("DragFinish") shellExecuteExW = shell32.NewProc("ShellExecuteExW") shellNotifyIconW = shell32.NewProc("Shell_NotifyIconW") shellGetImageLists = shell32.NewProc("SHGetImageList") user32 = syscall.NewLazyDLL("user32.dll") sendMessageW = user32.NewProc("SendMessageW") postMessageW = user32.NewProc("PostMessageW") ) type openfilename struct { structSize uint32 hwndOwner uintptr hInstance uintptr filter *uint16 customFilter *uint16 maxCustomFilter uint32 filterIndex uint32 file *uint16 maxFile uint32 fileTitle *uint16 maxFileTitle uint32 initialDir *uint16 title *uint16 flags uint32 fileOffset uint16 fileExtension uint16 defExt *uint16 custData uintptr hook uintptr templateName *uint16 reserved uintptr flagsEx uint32 } func rzFileDialog() (string, error) { var ofn openfilename ofn.structSize = uint32(unsafe.Sizeof(ofn)) ofn.flags |= 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 // OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_ALLOWMULTISELECT | OFN_PATHMUSTEXIST ofn.filter = syscall.StringToUTF16Ptr("All Files (*.*)\x00*.*\x00\x00") ofn.file = syscall.StringToUTF16Ptr(string(make([]uint16, 8192))) ofn.maxFile = uint32(len([]uint16(*ofn.file))) ofn.fileTitle = syscall.StringToUTF16Ptr(string(make([]uint16, 8192))) ofn.maxFileTitle = uint32(len([]uint16(*ofn.fileTitle))) ret, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&ofn))) if ret == 0 { return "", syscall.GetLastError() } defer coTaskMemFree.Call(uintptr(unsafe.Pointer(ofn.file))) defer coTaskMemFree.Call(uintptr(unsafe.Pointer(ofn.fileTitle))) return syscall.UTF16ToString((*[8192]uint16)(unsafe.Pointer(ofn.file))[:]), nil } func main() { file, err := rzFileDialog() if err != nil { fmt.Println("Error:", err) return } fmt.Println("Selected file:", file) } ``` 在WindTerm中,可以使用以下命令来调用此程序: ``` rz | go run filedialog.go ``` 这将在WindTerm中启动rz命令,并弹出文件选择。选择文件后,程序将在WindTerm中输出所选文件的路径。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值