如何在PyQt5的QTabWidget中无布局嵌入Folium地图?
问题:在PyQt5的QTabWidget中嵌入Folium地图(无需使用布局)
我是PyQt5与Python新手,希望在名为tabContainer1的QTabWidget组件中嵌入Folium地图,且完全不使用任何布局(需移除代码中标记为avoid1、avoid2的QVBoxLayout相关代码)。我已拥有mapTezt.ui界面设计文件,经多方搜索未找到可行方案,特此求助。当前代码如下:
class WebEnginePage(QWebEnginePage): def __init__(self, parent): super().__init__(parent) self.parent = parent def javaScriptConsoleMessage(self, level, msg, line, sourceID): print(msg) # Check js errors if 'coordinates' in msg: self.parent.handleConsoleMessage(msg) class FoliumDisplay(QDialog): def __init__(self,parent=None): super().__init__() super(FoliumDisplay, self).__init__() uic.loadUi("mapTezt.ui", self) self.setWindowTitle('B7_Map_view') self.setWindowFlags(QtCore.Qt.Window) self.window_width, self.window_height = 1200, 800 self.setMinimumSize(self.window_width, self.window_height) layout = QtWidgets.QVBoxLayout() # avoid 1 self.setLayout(layout) # avoid 2 self.tabContainer1=self.findChild(QTabWidget,"tabContainer1") #I want to add folium map inside the tab widget coordinate = (8.575589503988725,436.98120117187506) m = folium.Map( tiles="OpenStreetMap", zoom_start=8, location=coordinate) folium.Marker(location=[8.558954404198643,436.87803268432623], popup='One', icon=folium.Icon(color='purple',prefix='fa',icon='Anchor')).add_to(m) folium.Marker(location=[8.908136736259276,436.90841674804693],tooltip="Two" , name="Two").add_to(m) A=[8.558954404198643,436.87803268432623] B=[8.908136736259276,436.90841674804693] folium.PolyLine(locations=[A, B], color='red').add_to(m) c1=(A[0]+B[0])/2 c2 = (A[1] + B[1]) / 2 angle=90 folium.RegularPolygonMarker(location=(c1,c2), fill_color='red',color="red", number_of_sides=3, radius=5, rotation=angle, fill=True).add_to(m) # Add Custom JS to folium map m = self.add_customjs(m) # save map data to data object data = io.BytesIO() m.save(data, close_file=False) webView = QWebEngineView() # start web engine page = WebEnginePage(self) webView.setPage(page) webView.setHtml(data.getvalue().decode()) # give html of folium map to webengine layout.addWidget(webView) # Here, i need map view in tabWidget def add_customjs(self, map_object): my_js = f"""{map_object.get_name()}.on("click", function (e) {{ var data = `{{"coordinates": ${{JSON.stringify(e.latlng)}}}}`; console.log(data)}});""" e = Element(my_js) html = map_object.get_root() html.script.get_root().render() # Insert new element or custom JS html.script._children[e.get_name()] = e return map_object def handleConsoleMessage(self, msg): data = json.loads(msg) lat = data['coordinates']['lat'] lng = data['coordinates']['lng'] coords = f"latitude: {lat} longitude: {lng}" if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) UIWindow = FoliumDisplay() UIWindow.show() sys.exit(app.exec_())
解决方案
以下是修改后的代码,核心是移除布局代码,并将WebEngineView直接绑定到QTabWidget的目标tab页,通过手动控制大小实现无布局嵌入:
import sys import io import json from PyQt5 import QtWidgets, QtCore, QtWebEngineWidgets, uic from folium import Map, Marker, PolyLine, RegularPolygonMarker, Icon from branca.element import Element class WebEnginePage(QtWebEngineWidgets.QWebEnginePage): def __init__(self, parent): super().__init__(parent) self.parent = parent def javaScriptConsoleMessage(self, level, msg, line, sourceID): print(msg) # 打印JS错误信息 if 'coordinates' in msg: self.parent.handleConsoleMessage(msg) class FoliumDisplay(QtWidgets.QDialog): def __init__(self, parent=None): super().__init__(parent) uic.loadUi("mapTezt.ui", self) self.setWindowTitle('B7_Map_view') self.setWindowFlags(QtCore.Qt.Window) self.window_width, self.window_height = 1200, 800 self.setMinimumSize(self.window_width, self.window_height) # 移除原布局代码 # layout = QtWidgets.QVBoxLayout() # avoid 1 # self.setLayout(layout) # avoid 2 self.tabContainer1 = self.findChild(QtWidgets.QTabWidget, "tabContainer1") # 假设要把地图放到第一个tab页(索引0),如果是其他tab页修改索引即可 self.target_tab = self.tabContainer1.widget(0) if not self.target_tab: # 如果没有现有tab页,新建一个 self.target_tab = QtWidgets.QWidget() self.tabContainer1.addTab(self.target_tab, "地图") # 生成Folium地图 coordinate = (8.575589503988725, 436.98120117187506) m = Map( tiles="OpenStreetMap", zoom_start=8, location=coordinate) Marker(location=[8.558954404198643, 436.87803268432623], popup='One', icon=Icon(color='purple', prefix='fa', icon='Anchor')).add_to(m) Marker(location=[8.908136736259276, 436.90841674804693], tooltip="Two", name="Two").add_to(m) A = [8.558954404198643, 436.87803268432623] B = [8.908136736259276, 436.90841674804693] PolyLine(locations=[A, B], color='red').add_to(m) c1 = (A[0] + B[0]) / 2 c2 = (A[1] + B[1]) / 2 angle = 90 RegularPolygonMarker(location=(c1, c2), fill_color='red', color="red", number_of_sides=3, radius=5, rotation=angle, fill=True).add_to(m) # 添加自定义JS m = self.add_customjs(m) # 保存地图数据 data = io.BytesIO() m.save(data, close_file=False) # 创建WebEngineView并设置为实例变量,方便后续调整大小 self.webView = QtWebEngineWidgets.QWebEngineView() page = WebEnginePage(self) self.webView.setPage(page) self.webView.setHtml(data.getvalue().decode()) # 将WebEngineView绑定到目标tab页,不使用布局 self.webView.setParent(self.target_tab) # 初始设置大小为tab页的大小 self.webView.resize(self.target_tab.size()) def add_customjs(self, map_object): my_js = f"""{map_object.get_name()}.on("click", function (e) {{ var data = `{{"coordinates": ${{JSON.stringify(e.latlng)}}}}`; console.log(data)}});""" e = Element(my_js) html = map_object.get_root() html.script.get_root().render() html.script._children[e.get_name()] = e return map_object def handleConsoleMessage(self, msg): data = json.loads(msg) lat = data['coordinates']['lat'] lng = data['coordinates']['lng'] coords = f"latitude: {lat} longitude: {lng}" print(coords) # 可以在这里处理点击坐标 def resizeEvent(self, event): """重写resize事件,确保WebEngineView随tab页大小变化""" super().resizeEvent(event) if self.target_tab and hasattr(self, 'webView'): self.webView.resize(self.target_tab.size()) if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) UIWindow = FoliumDisplay() UIWindow.show() sys.exit(app.exec_())
关键修改说明
- 移除布局代码:直接删掉原代码中标记为
avoid1和avoid2的两行布局相关代码,不再设置全局布局。 - 绑定WebEngineView到目标tab:
- 获取QTabWidget中的目标tab页(如果没有则新建),将WebEngineView的父控件设为该tab页。
- 初始设置WebEngineView的大小与tab页一致。
- 重写resizeEvent:监听窗口大小变化事件,实时调整WebEngineView的大小,确保地图始终铺满整个tab页。
- WebEngineView改为实例变量:将
webView从局部变量改为self.webView,方便在resizeEvent中访问调整。
内容的提问来源于stack exchange,提问作者user6543831
相关产品推荐
相关产品推荐

