You can use Python only (versions 2.7.x and 3.x) without QGIS:

1) With Fiona, shapely and itertools
import fiona
from shapely.geometry import MuliPolygon, LineString, mapping
Multi = MultiPolygon([shape(poly['geometry']) for poly in fiona.open("polygons.shp")])
# creation of the dual graph shapefile
import itertools
# schema of the dual graph shapefile
schema = {'geometry': 'LineString','properties': {'test': 'int'}}
with fiona.open('dual_graph.shp','w','ESRI Shapefile', schema) as e:
for poly1,poly2 in itertools.combinations(Multi, 2):
if poly1.touches(poly2):
e.write({'geometry':mapping(LineString([poly1.centroid, poly2.centroid])), 'properties':{'test':1}})

2) with GeoPandas,shapely and itertools
import geopandas as gpd
polygon = gpd.GeoDataFrame.from_file("polygons.shp")
geoms = polygon['geometry'].tolist()
dual = gpd.GeoDataFrame(gpd.GeoSeries([LineString([poly1.centroid, poly2.centroid]) for poly1,poly2 in itertools.combinations(geoms, 2) if poly1.touches(poly2)]),columns=['geometry'])
dual.to_file("dual_graph2.shp")
3) I suppose you can do the same thing with PyQgis and itertools (Python 2.7 for 2.x versions and 3.x for 3.x versions)